Optimisation class¶
This module contains classes for formulating and solving QUBO and linear problems.
QUBO¶
QUBO, short for Quadratic Unconstrained Binary Optimisation, are a class of problems which are NP-complete.
When formulated carefully, QUBO problems can be mapped to solve a host of optimisation problems such as Travelling Salesman Problem, Maximum Independent Set, Quadratic Assignment Problem, Maximum Clique problem, Maximum Cut problem, etc.
- class qiboopt.opt_class.opt_class.QUBO(offset, *args)[source]¶
- qubo_to_ising(constant=0.0)[source]¶
Convert a QUBO problem to an Ising problem.
Maps a quadratic unconstrained binary optimisation (QUBO) problem defined over binary variables (0 or 1 values), where the linear term is contained along x’ Qx the diagonal of Q, to an Ising model defined on spins (variables with {-1, +1} values). Returns h and J that define the Ising model as well as constant representing the offset in energy between the two problem formulations.
\[x' Q x = constant + s' J s + h' s\]- Parameters:
Q (dict[(variable, variable), coefficient]) – QUBO coefficients in a dict of form {(u, v): coefficient, …}, where keys are 2-tuples of variables of the model and values are biases associated with the pair of variables. Tuples (u, v) represent interactions and (v, v) linear biases.
constant (float) – Constant offset to be applied to the energy. Defaults to 0.
- Returns:
A 3-tuple containing: h (dict): Linear coefficients of the Ising problem. J (dict): Quadratic coefficients of the Ising problem. constant (float): The new energy offset.
- Return type:
Example
This example converts a QUBO problem of two variables that have positive biases of value 1 and are positively coupled with an interaction of value 1 to an Ising problem, and shows the new energy offset.
- construct_symbolic_Hamiltonian_from_QUBO()[source]¶
Constructs a symbolic Hamiltonian from the QUBO problem by converting it to an Ising model.
The method calls the qubo_to_ising function to convert the QUBO formulation into an Ising Hamiltonian with linear and quadratic terms. Then, it creates a symbolic Hamiltonian using the qibo library.
- Returns:
- A symbolic
Hamiltonian that corresponds to the QUBO problem.
- Return type:
ham (qibo.hamiltonians.hamiltonians.SymbolicHamiltonian)
- evaluate_f(x)[source]¶
Evaluates the quadratic function for a given binary vector.
- Parameters:
x (list) – A list representing the binary vector for which to evaluate the function.
- Returns:
The evaluated function value.
- Return type:
f_value (float)
Example
Qdict = {(0, 0): 1.0, (0, 1): 0.5, (1, 1): -1.0} qp = QUBO(0, Qdict) x = [1, 1] print(qp.evaluate_f(x)) # >>> 0.5
- evaluate_grad_f(x)[source]¶
Evaluates the gradient of the quadratic function at a given binary vector.
- Parameters:
x (List[int]) – A list representing the binary vector for which to evaluate the gradient.
- Returns:
List of float representing the gradient vector.
- Return type:
grad (List)
Example
Qdict = {(0, 0): 1.0, (0, 1): 0.5, (1, 1): -1.0} qp = QUBO(0, Qdict) x = [1, 1] print(qp.evaluate_grad_f(x)) # >>> [1.5, -0.5]
- tabu_search(max_iterations=100, tabu_size=10)[source]¶
Solves the QUBO problem using the Tabu search algorithm.
- Parameters:
- Returns:
List of ints representing the best binary vector found. best_obj_value (float): The corresponding objective value.
- Return type:
best_solution (list)
Example
Qdict = {(0, 0): 1.0, (0, 1): 0.5, (1, 1): -1.0} qp = QUBO(0, Qdict) best_solution, best_obj_value = qp.tabu_search(50, 5) print(best_solution) # >>> [0, 1] print(best_obj_value) # >>> 0.5
- brute_force()[source]¶
- Solves the QUBO problem by evaluating all possible binary vectors.
Note that this approach is very slow.
- Returns:
List of ints representing the optimal binary vector. min_value (float): The minimum value of the objective function.
- Return type:
opt_vector (list)
Example
Qdict = {(0, 0): 1.0, (0, 1): 0.5, (1, 1): -1.0} qp = QUBO(0, Qdict) opt_vector, min_value = qp.brute_force() print(opt_vector) # >>> [0, 1] print(min_value) # >>> -1.0
- canonical_q()[source]¶
Converts the QUBO matrix to canonical form where only terms with i < j are retained.
- Returns:
A dictionary and also update Qdict
- Return type:
self.Qdict (dict)
- qubo_to_qaoa_circuit(gammas, betas, alphas=None, custom_mixer=None)[source]¶
Constructs a QAOA or XQAOA circuit for the given QUBO problem.
- Parameters:
gammas (List[float]) – parameters for phasers
betas (List[float]) – parameters for X mixers
alphas (List[float], optional) – parameters for Y mixers for XQAOA
custom_mixer (List[
qibo.models.Circuit
]) – optional argument that takes as input custom mixers. If len(custom_mixer) == 1, then use this one circuit as mixer for all layers. If len(custom_mixer) == len(gammas), then use each circuit as mixer for each layer. If len(custom_mixer) != 1 and != len(gammas), raise an error.
- Returns:
The QAOA or XQAOA circuit corresponding to the QUBO problem.
- Return type:
circuit (
qibo.models.Circuit
)
- train_QAOA(gammas=None, betas=None, alphas=None, p=None, nshots=1000, regular_loss=True, maxiter=10, method='cobyla', cvar_delta=0.25, custom_mixer=None, backend=None, noise_model=None)[source]¶
Constructs the QAOA or XQAOA circuit with optional parameters for the mixers or phases before using a classical optimiser to search for the optimal parameters which minimise the cost function (either expected value or Conditional Variance at Risk (CVaR).
- Parameters:
gammas (List[float], optional) – parameters for phasers.
betas (List[float], optional) – parameters for X mixers.
alphas (List[float], optional) – parameters for Y mixers for XQAOA. Defaults to None.
p (int, optional) – number of layers.
nshots (int, optional) – number of shots
regular_loss (Bool, optional) – If False, Conditional Variance at Risk (CVaR) is used as cost function. Defaults to True, where expected value is used as cost function.
maxiter (int, optional) – Maximum number of iterations used in the minimiser. Defaults to 10.
cvar_delta (float, optional) – Represents the quantile threshold used for calculating the CVaR. Defaults to 0.25.
custom_mixer (List[
qibo.models.Circuit
]) – optional argument that takes as input custom mixers. If len(custom_mixer) == 1, then use this one circuit as mixer for all layers. If len(custom_mixer) == len(gammas), then use each circuit as mixer for each layer. If len(custom_mixer) != 1 and != len(gammas), raise an error.backend (
qibo.backends.abstract.Backend
, optional) – backend to be used in the execution. IfNone
, it uses the current backend. Defaults toNone
.noise_model (
qibo.noise.NoiseModel
, optional) – noise model applied to simulate noisy computations. Defaults to None.
- Returns:
- A tuple containing:
best (float): The lowest cost value achieved.
params (List[float]): Optimised QAOA parameters.
extra (dict): Additional metadata (e.g., convergence info).
circuit (
qibo.models.Circuit
): Final circuit used for evaluation.frequencies (dict): Bitstring outcome frequencies from measurement.
- Return type:
Tuple[float, List[float], dict,
qibo.models.Circuit
, dict]
Example
Qdict = {(0, 0): 1.0, (0, 1): 0.5, (1, 1): -1.0} qp = QUBO(0, Qdict) opt_vector, min_value = qp.brute_force() # Train regular QAOA output = QUBO(0, Qdict).train_QAOA(p=10)
- qubo_to_qaoa_object(params: list = None)[source]¶
Generates a QAOA object for the QUBO problem.
- Parameters:
params (List[float]) – Parameters of the QAOA given in block format: e.g. [all_gammas, all_betas, all_alphas] (if alphas is not None)
- Returns:
A QAOA circuit for the QUBO problem.
- Return type:
qaoa (qibo.models.QAOA)
Linear Problems¶
Linear problem write up goes here.
- class qiboopt.opt_class.opt_class.linear_problem(A, b)[source]¶
A class used to represent a linear problem of the form Ax + b.
- Parameters:
A (np.ndarray) – Coefficient matrix.
b (np.ndarray) – Constant vector.
n (int) – Dimension of the problem, inferred from the size of b.
Example
A1 = np.array([[1, 2], [3, 4]]) b1 = np.array([5, 6]) lp1 = linear_problem(A1, b1) A2 = np.array([[1, 1], [1, 1]]) b2 = np.array([1, 1]) lp2 = linear_problem(A2, b2) lp3 = lp1 + lp2 print(lp3.A) # >>> [[2 3] # [4 5]] print(lp3.b) # >>> [6 7] print(lp1.A) # Original lp1 unchanged # >>> [[1 2] # [3 4]]
- evaluate_f(x)[source]¶
Evaluates the linear function Ax + b at a given point x.
- Parameters:
x (np.ndarray) – Input vector at which to evaluate the linear function.
- Returns:
The value of the linear function Ax + b at the given x.
- Return type:
numpy.ndarray
Example
A = np.array([[1, 2], [3, 4]]) b = np.array([5, 6]) lp = linear_problem(A, b) x = np.array([1, 1]) result = lp.evaluate_f(x) print(result) # [ 8 13]
- square()[source]¶
Squares the linear problem to obtain a quadratic problem.
- Returns:
A quadratic problem corresponding to squaring the linear function.
- Return type:
Example
A = np.array([[1, 2], [3, 4]]) b = np.array([5, 6]) lp = linear_problem(A, b) Quadratic = lp.square() print(Quadratic.Qdict) # >>> {(0, 0): 56, (0, 1): 14, (1, 0): 14, (1, 1): 88} print(Quadratic.offset) # >>> 61