Combinatorial classes¶
Four Quadratic Unconstrained Binary Optimisation (QUBO) example problems are listed here: the Travelling Salesman Problem, the Quadratic Assignment Problem, the Minimal Vertex Cover and the Maximum Independent Set.
Travelling Salesman Problem¶
The Travelling Salesman Problem (sometimes referred to as the Travelling Salesperson Problem), commonly abbreviated as TSP, is a NP-hard problem in combinatorial optimisation.
Briefly, the problem revolves around finding the shortest possible route for a salesman to visit some cities before returning to the origin. TSP is usually formulated as a graph problem with nodes specifying the cities and edges denoting the distances between each city.
The idea behind TSP can be mapped to similar-type problems. For instance, what is the optimal route for the salesman to take in order to minimise something.
In this module, the TSP class follows Hadfield’s 2017 paper.
- class qiboopt.combinatorial.combinatorial.TSP(distance_matrix, two_to_one=None, backend=None)[source]¶
Class representing the Travelling Salesman Problem (TSP). The implementation is based on the work by Hadfield.
- Parameters:
distance_matrix – a numpy matrix encoding the distance matrix.
two_to_one – Mapping from two coordinates to one coordinate
backend – Backend to use for calculations. If not given the global backend will be used.
Example
from qiboopt.combinatorial.combinatorial import TSP import numpy as np from collections import defaultdict from qibo import Circuit, gates from qibo.models import QAOA from qibo.result import CircuitResult def convert_to_standard_Cauchy(config): m = int(np.sqrt(len(config))) cauchy = [-1] * m # Cauchy's notation for permutation, e.g. (1,2,0) or (2,0,1) for i in range(m): for j in range(m): if config[m * i + j] == '1': cauchy[j] = i # citi i is in slot j for i in range(m): if cauchy[i] == 0: cauchy = cauchy[i:] + cauchy[:i] return tuple(cauchy) # now, the cauchy notation for permutation begins with 0 def evaluate_dist(cauchy): ''' Given a permutation of 0 to n-1, we compute the distance of the tour ''' m = len(cauchy) return sum(distance_matrix[cauchy[i]][cauchy[(i+1)%m]] for i in range(m)) def qaoa_function_of_layer(layer, distance_matrix): ''' This is a function to study the impact of the number of layers on QAOA, it takes in the number of layers and compute the distance of the mode of the histogram obtained from QAOA ''' small_tsp = TSP(distance_matrix) obj_hamil, mixer = small_tsp.hamiltonians() qaoa = QAOA(obj_hamil, mixer=mixer) best_energy, final_parameters, extra = qaoa.minimize(initial_p=[0.1] * layer, initial_state=initial_state, method='BFGS') qaoa.set_parameters(final_parameters) quantum_state = qaoa.execute(initial_state) circuit = Circuit(9) circuit.add(gates.M(*range(9))) result = CircuitResult(quantum_state, circuit.measurements, small_tsp.backend, nshots=1000) freq_counter = result.frequencies() # let's combine freq_counter here, first convert each key and sum up the frequency cauchy_dict = defaultdict(int) for freq_key in freq_counter: standard_cauchy_key = convert_to_standard_Cauchy(freq_key) cauchy_dict[standard_cauchy_key] += freq_counter[freq_key] max_key = max(cauchy_dict, key=cauchy_dict.get) return evaluate_dist(max_key) np.random.seed(42) num_cities = 3 distance_matrix = np.array([[0, 0.9, 0.8], [0.4, 0, 0.1],[0, 0.7, 0]]) distance_matrix = distance_matrix.round(1) small_tsp = TSP(distance_matrix) initial_parameters = np.random.uniform(0, 1, 2) initial_state = small_tsp.prepare_initial_state([i for i in range(num_cities)]) qaoa_function_of_layer(2, distance_matrix)
- Reference:
1. S. Hadfield, Z. Wang, B. O’Gorman, E. G. Rieffel, D. Venturelli, R. Biswas, From the Quantum Approximate Optimization Algorithm to a Quantum Alternating Operator Ansatz. (arxiv:1709.03489)
- hamiltonians()[source]¶
Constructs the phaser and mixer Hamiltonians for the TSP.
- Returns:
A tuple containing the phaser and mixer Hamiltonians.
- Return type:
- prepare_initial_state(ordering)[source]¶
Prepares a valid initial state for TSP QAOA based on the given city ordering.
- Parameters:
ordering (list) – A list representing the permutation of cities.
- Returns:
The quantum state representing the initial solution.
- Return type:
array
- penalty_method(penalty)[source]¶
Constructs the TSP QUBO object using a penalty method for feasibility.
The TSP is formulated as:
\[\min \sum_{u,v,j} d_{u,v} \, x_{u,j} \, x_{v,j+1}\]Subject to constraints:
\[\sum_j x_{v,j} = 1 \quad \forall v \ \sum_v x_{v,j} = 1 \quad \forall j\]The penalty method converts this to an unconstrained QUBO:
\[f(x) = \text{objective}(x) + \lambda \left[ \sum_v \left(\sum_j x_{v,j} - 1\right)^2 + \sum_j \left(\sum_v x_{v,j} - 1\right)^2 \right]\]- Parameters:
penalty (float) – The penalty parameter for constraint violations. It should be large enough to enforce constraints but not so large as to cause numerical issues.
- Returns:
A QUBO object for the TSP with penalties applied.
- Return type:
- Raises:
ValueError – If penalty is negative.
Quadratic Assignment Problem¶
The Quadratic Assignment Problem, commonly abbreviated as QAP, is a NP-hard problem in combinatorial optimisation, first introduced by Koopmans and Beckmann.
Briefly, the problem concerns assigning a set of facilities to a set of locations in a way that minimises the total assignment cost. The assignment cost is typically determined by the flow between facilities and the distance between their assigned locations. QAP is usually formulated using two matrices: one representing flows between facilities and another representing distances between locations.
To map QAO to a QUBO, we define binary variables \(x \in \{0, 1\}\), where 1 denotes if the \(i\)-th facility is assigned to the \(j\)-th location and 0 otherwise.
The objective function is
subject to the assignment constraints
and
where \(f_{ik}\) are the elements of the flow matrix \(F \in \mathbb{R}^{n \times n}\), representing the interaction between facilities \(i\) and \(k\), and \(d_{jl}\) are the elements of the distance matrix \(D \in \mathbb{R}^{n \times n}\), representing the distance between locations \(j\) and \(l\).
- class qiboopt.combinatorial.combinatorial.QAP(flow_matrix, distance_matrix, two_to_one=None)[source]¶
Class for representing the Quadratic Assignment Problem (QAP).
The QAP assigns a set of facilities to a set of locations in a way that minimises the total assignment cost.
- Parameters:
flow_matrix – An \((n,n)\) numpy array describing the flows between the facilities.
distance_matrix – A \((n,n)\) numpy array describing the distance between the locations
two_to_one (optional) – Mapping from 2-index to a single index.
Example
from qiboopt.combinatorial.combinatorial import QAP flow_matrix = np.array([[1, 2],[3, 4]]) distance_matrix = np.array([[4, 3],[2, 1]]) qap = QAP(flow_matrix, distance_matrix) penalty = 10 qp = qap.penalty_method(penalty)
Minimum Weighted Vertex Cover¶
The Minimum Weighted Vertex Cover Problem, commonly abbreviated as MWVC, is a NP-hard problem in combinatorial optimisation.
Briefly, the problem involves selecting a subset of vertices in a graph such that every edge in the graph is incident to at least one selected vertex. Each vertex is assigned a weight and the objective is to minimise the total weight of the selected vertices (rather than their count). MWVC is usually formulated as a graph problem where nodes represent entities and edges represent relationships between them.
Given a graph \(G = (V, E)\), the goal of MWVC is to select a subset of vertices in such a way that every edge is covered while minimising total vertex weight.
Define binary variables \(x_i \in \{0, 1\}\) for each vertex \(i \in V\), where \(x_i = 1\) indicates that vertex \(i\) is selected and 0 otherwise.
To map MWVC to a QUBO, constraints are enforced via penalty terms:
where \(w_i\) is the weight of vertex \(i\) and \(P\) is a sufficiently large penalty coefficient enforcing coverage.
- class qiboopt.combinatorial.combinatorial.MWVC(graph)[source]¶
Class for representing the Minimum Weighted Vertex Cover (MWVC) problem.
The MWVC problem selects a subset of vertices in a graph such that every edge in the graph is incident to at least one selected vertex.
- Parameters:
graph – a networkx graph where the weight is encoded with keyword “weight”.
Example
import networkx as nx from qiboopt.combinatorial.combinatorial import MWVC g = nx.Graph() g.add_nodes_from([(0, {"weight":2}), (1, {"weight":3}), (2, {"weight":4})]) g.add_edges_from([(0,1), (1,2), (2,1)]) mwvc = MWVC(g) penalty = 10 qp = mwvc.penalty_method(penalty)
Maximum Independent Set¶
The MIS problem involves selecting the largest subset of non-adjacent vertices in a graph.
- class qiboopt.combinatorial.combinatorial.MIS(g)[source]¶
Class for representing the Maximal Independent Set (MIS) problem.
The MIS problem involves selecting the largest subset of non-adjacent vertices in a graph.
- Parameters:
g (networkx.Graph) – A graph object representing the problem.
Example
import networkx as nx from qiboopt.combinatorial.combinatorial import MIS g = nx.Graph() g.add_edges_from([(0, 1), (1, 2), (2, 0)]) mis = MIS(g) penalty = 10 qp = mis.penalty_method(penalty)