Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,188 changes: 1,188 additions & 0 deletions example/qubo_mixed.ipynb

Large diffs are not rendered by default.

378 changes: 284 additions & 94 deletions example/qubols.ipynb → example/qubo_mixed_floats.ipynb

Large diffs are not rendered by default.

1,150 changes: 1,150 additions & 0 deletions example/qubo_poly.ipynb

Large diffs are not rendered by default.

1,294 changes: 1,294 additions & 0 deletions example/qubo_poly_landscape.ipynb

Large diffs are not rendered by default.

1,238 changes: 1,238 additions & 0 deletions example/qubo_poly_mixed.ipynb

Large diffs are not rendered by default.

1,455 changes: 1,455 additions & 0 deletions example/qubo_poly_mixed_inverse.ipynb

Large diffs are not rendered by default.

125 changes: 116 additions & 9 deletions qubols/encodings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from sympy import Symbol
import itertools
import numpy as np


class BaseQbitEncoding(object):
Expand All @@ -15,6 +17,14 @@ def __init__(self, nqbit, var_base_name):
self.var_base_name = var_base_name
self.variables = self.create_variable()

def set_var_base_name(self, var_base_name):
"""set the variable base name

Args:
var_base_name (_type_): _description_
"""
self.var_base_name = var_base_name

def create_variable(self):
"""Create all the variabes/qbits required for the expansion

Expand All @@ -26,6 +36,57 @@ def create_variable(self):
variables.append(Symbol(self.var_base_name + "_%03d" % (i + 1)))
return variables

def create_polynom(self):
raise NotImplementedError("Implement create_polynom")

def decode_polynom(self, data):
raise NotImplementedError("Implement decode_polynom")

def get_max_value(self):
"""Get the maximum value of the encoding

Returns:
float: max value
"""
return self.decode_polynom([1] * self.nqbit)

def get_possible_values(self):
"""get all the posible values encoded

Returns:
_type_: _description_
"""

values = []
for data in itertools.product([0, 1], repeat=self.nqbit):
values.append(self.decode_polynom(list(data)[::-1]))
return values

def find_closest(self, float):
"""finds the closest possible encoded number to float

Args:
float (_type_): _description_
"""

min_diff = 1e12
closest_value = None
binary_encoding = None
for data in itertools.product([0, 1], repeat=self.nqbit):
val = self.decode_polynom(list(data)[::-1])
if np.abs(val - float) < min_diff:
min_diff = np.abs(val - float)
closest_value = val
binary_encoding = list(data)[::-1]

return closest_value, binary_encoding

def get_average_precision(self):
"""get the mean precision on the encoded variables"""
vals = self.get_possible_values()
z = vals - np.roll(vals, 1)
return np.mean(z[1:])


class RangedEfficientEncoding(BaseQbitEncoding):

Expand All @@ -47,7 +108,10 @@ def create_polynom(self):
Returns:
sympy expression
"""
out = -(2 ** (self.nqbit - 1)) * self.variables[0] * self.max_absval[0]
out = (
self.offset
- (2 ** (self.nqbit - 1)) * self.variables[0] * self.max_absval[0]
)
for i in range(self.nqbit - 1):
out += 2 ** (i) * self.variables[i + 1] * self.max_absval[i]
return out
Expand All @@ -59,7 +123,7 @@ def decode_polynom(self, data):
Returns:
sympy expression
"""
out = -(2 ** (self.nqbit - 1)) * data[0] * self.max_absval[0]
out = self.offset - (2 ** (self.nqbit - 1)) * data[0] * self.max_absval[0]
for i in range(self.nqbit - 1):
out += 2 ** (i) * data[i + 1] * self.max_absval[i]
return out
Expand Down Expand Up @@ -185,8 +249,10 @@ def decode_polynom(self, data):

class PositiveQbitEncoding(BaseQbitEncoding):

def __init__(self, nqbit, var_base_name):
def __init__(self, nqbit, var_base_name, offset=0, step=1):
super().__init__(nqbit, var_base_name)
self.offset = offset
self.step = step

def create_polynom(self):
"""
Expand All @@ -195,14 +261,55 @@ def create_polynom(self):
Returns:
sympy expression
"""
out = 0.0
out = self.offset
for i in range(self.nqbit):
out += 2**i * self.variables[i]
out += self.step * 2**i * self.variables[i]
return out

def decode_polynom(self, data):
out = 0.0
for i in range(self.nqbit // 2):
out += 2**i * data[i]
out -= 2**i * data[self.nqbit // 2 + i]
out = self.offset
for i in range(self.nqbit):
out += self.step * 2**i * data[i]
return out


class DiscreteValuesEncoding(BaseQbitEncoding):

def __init__(self, values, nqbit, var_base_name):
super().__init__(nqbit, var_base_name)
self.discrete_values = values
self.coefs = self.get_coefficients()
self.offset = 0

def get_coefficients(self):
"""get the lstqst coefficients"""
nvalues = len(self.discrete_values)
nqbit = self.nqbit
A = np.zeros((nvalues, nqbit + 1))
c = [1] + [2**i for i in range(nqbit)]
for idx in range(nvalues):
row = [1] + [float(i) for i in np.binary_repr(idx, width=nqbit)][::-1]
A[idx, :] = row
A = A * c

coefs, res, rank, s = np.linalg.lstsq(A, self.discrete_values)

return coefs

def create_polynom(self):
"""
Create the polynoms of the expansion

Returns:
sympy expression
"""
out = self.coefs[0]
for i in range(self.nqbit):
out += self.coefs[i + 1] * 2**i * self.variables[i]
return out

def decode_polynom(self, data):
out = self.coefs[0]
for i in range(self.nqbit):
out += self.coefs[i + 1] * 2**i * data[i]
return out
126 changes: 126 additions & 0 deletions qubols/mixed_solution_vector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from sympy.matrices import Matrix
import numpy as np
from copy import deepcopy
from .encodings import RangedEfficientEncoding


class MixedSolutionVector(object):

def __init__(self, solution_vectors):
"""init the mixed solution vector

Args:
solution_vectors (List): A list of SolutionVector instances
"""
self.solution_vectors = solution_vectors
self.nqbit = []
self.idx_start_data = self.get_indexes_data()
self.encoded_reals = self.create_encoding()

def create_encoding(self):
"""Create the enconding for all the unknowns


Returns:
list[RealEncoded]:
"""
encoded_reals = []

idx_vars = 0
for sol_vec in self.solution_vectors:
is_ranged_encoding = sol_vec.encoding == RangedEfficientEncoding
for i in range(sol_vec.size):
var_base_name = sol_vec.base_name + "_%03d" % (idx_vars + 1)
idx_vars += 1

if is_ranged_encoding:
args = (
sol_vec.nqbit,
sol_vec.range[i],
sol_vec.offset[i],
var_base_name,
)
else:
args = (sol_vec.nqbit, var_base_name)
encoded_reals.append(sol_vec.encoding(*args))
self.nqbit.append(sol_vec.nqbit)
return encoded_reals

def get_indexes_data(self):
"""Get the indices of the start/end of the data for each encoding

Returns:
np.array: lis of start/end index
"""
idx_start_data = [0]
for sv in self.solution_vectors:
idx_start_data.append(sv.nqbit * sv.size)
idx_start_data[-1] += 1
return np.cumsum(idx_start_data)

def create_polynom_vector(self):
"""Create the list of polynom epxressions

Returns:
sympy.Matrix: matrix of polynomial expressions
"""
pl = []
for real in self.encoded_reals:
pl.append(real.create_polynom())
return Matrix(pl)

def decode_solution(self, data):
"""Decode the solution

Args:
data (list): data from the annealer

Returns:
list: decoded numbers
"""
sol = []
for iv, sv in enumerate(self.solution_vectors):
idx_start = self.idx_start_data[iv]
idx_end = self.idx_start_data[iv + 1]
sv_data = data[idx_start:idx_end]
sol.append(list(sv.decode_solution(sv_data)))
idx_start += sv.size
return sol


class MixedSolutionVector_V2(MixedSolutionVector):

def __init__(self, solution_vectors):
"""init the mixed solution vector

Args:
solution_vectors (List): A list of SolutionVector instances
"""
self.solution_vectors = solution_vectors
self.nqbit = []
self.idx_start_data = self.get_indexes_data()
self.encoded_reals = self.create_encoding()

def create_encoding(self):
"""Create the enconding for all the unknowns


Returns:
list[RealEncoded]:
"""
encoded_reals = []

idx_vars = 0
for sol_vec in self.solution_vectors:

for i in range(sol_vec.size):
var_base_name = sol_vec.base_name + "_%03d" % (idx_vars + 1)
idx_vars += 1

x = deepcopy(sol_vec.encoding)
x.set_var_base_name(var_base_name)
x.variables = x.create_variable()
encoded_reals.append(x)
self.nqbit.append(sol_vec.nqbit)

return encoded_reals
Loading