Source code for qplex.algorithms.qaoa

import numpy as np

from qplex.algorithms.base_algorithm import Algorithm
from qplex.algorithms.mixers import QuantumMixer
import qplex


[docs] class QAOA(Algorithm): """ Quantum Approximate Optimization Algorithm (QAOA). This class implements the QAOA algorithm for solving combinatorial optimization problems using quantum resources. It creates a quantum circuit, updates parameters, and provides a starting point for the optimization process. Attributes ---------- p : int The number of repetitions of the two parameterized unitary operations. n : int The number of qubits in the quantum circuit, which corresponds to the number of binary variables in the optimization problem. circuit : str The OpenQASM3 representation of the quantum circuit. num_params : int The number of parameters for the QAOA variational circuit, which is equal to 2 times the number of repetitions (p). """ def __init__(self, model, p: int, seed: int, penalty: float, mixer: QuantumMixer = None): """ Initializes the QAOA algorithm with the given parameters. Parameters ---------- model : QModel The optimization model to be solved, represented as a Quadratic Unconstrained Binary Optimization (QUBO) problem. p : int The number of repetitions of the two parameterized unitary operations (often referred to as the "depth" of the QAOA circuit). seed : int The seed for the random number generator, ensuring reproducibility. penalty : float The penalty factor for the QUBO conversion, used to penalize constraint violations in the QUBO formulation. mixer : QuantumMixer The mixer or driver of the QAOA variational circuit """ super().__init__(model) self.p: int = p self.n: int = 0 self.num_params = 2 * self.p if mixer is None: raise ValueError( f"Expected mixer to be provided, got {mixer}") self.mixer = mixer self.circuit: str = self.create_circuit(penalty=penalty) np.random.seed(seed)
[docs] def create_circuit(self, *args, **kwargs) -> str: """ Creates a quantum circuit in the form of an OpenQASM3 string for QAOA. The circuit is constructed based on the QUBO encoding of the model, including all the necessary gates for the QAOA ansatz. The ansatz includes mixing and problem unitary operations with parameterized rotation angles. Parameters ---------- kwargs : dict Optional parameters, including 'penalty' for the QUBO conversion. Returns ------- str An OpenQASM3 string representing the quantum circuit for QAOA. """ self.qubo = self.model.get_qubo(penalty=kwargs['penalty']) self.n = self.qubo.get_num_binary_vars() circuit_lines = [f"input float[64] theta{i};" for i in range(self.num_params)] circuit_lines.extend([f"qreg q[{self.n}];", f"creg c[{self.n}];"]) circuit_lines.extend([f"h q[{i}];" for i in range(self.n)]) linear_terms = self.qubo.objective.linear.to_array() quadratic_terms = self.qubo.objective.quadratic.to_array() for idx in range(self.p): theta_2idx = f"theta{2 * idx}" theta_2idx_plus_1 = f"theta{2 * idx + 1}" for i, w in enumerate(linear_terms): h_sum = sum(quadratic_terms[i]) circuit_lines.append( f"rz({theta_2idx} * {(w + h_sum)}) q[{i}];") for i in range(self.n): for j in range(i + 1, self.n): w = quadratic_terms[i, j] if w != 0: circuit_lines.append(f"cx q[{i}], q[{j}];") circuit_lines.append( f"rz({theta_2idx} * {w / 2}) q[{j}];") circuit_lines.append(f"cx q[{i}], q[{j}];") circuit_lines.extend( self.mixer.generate_circuit(self.n, theta_2idx_plus_1)) circuit_lines.extend( [f"measure q[{i}] -> c[{i}];" for i in range(self.n)]) return "\n".join(circuit_lines)
[docs] def update_params(self, params: np.ndarray) -> str: """ Updates the parameters of the QAOA circuit. The circuit is updated by replacing the placeholder angles ('thetaX') with the values provided in the parameter array. This allows the quantum circuit to be re-parameterized as part of a variational optimization process. Parameters ---------- params : np.ndarray The new set of parameters for the QAOA circuit, typically representing rotation angles in the circuit's gates. Returns ------- str The updated OpenQASM3 string for the QAOA circuit with the new parameter values. """ if len(params) != self.num_params: raise ValueError( f"Expected {self.num_params} parameters, got {len(params)}") return qplex.utils.circuit_utils.replace_params(self.circuit, params)
[docs] def get_starting_point(self) -> np.ndarray: """ Defines the starting point for the QAOA optimization. The starting point consists of a set of randomly initialized parameters (rotation angles) for the variational QAOA circuit. Returns ------- np.ndarray An array representing the starting point for QAOA, initialized with random values between 0 and 1. """ return np.random.rand(2 * self.p)