diff --git a/src/all_qas.py b/src/all_qas.py index 7001c66..ff11721 100755 --- a/src/all_qas.py +++ b/src/all_qas.py @@ -1,13 +1,7 @@ #!/usr/bin/env python from argparse import ArgumentParser -from dataclasses import dataclass from enum import Enum -from ga_qas import GeneticAlgorithmSettings -from qd_qas import QualityDiversitySettings -from settings import QuantumArchitectureSearchSettings -from tf_qas import TrainingFreeSettings - class SearchStrategy(Enum): TFQAS = 1 @@ -15,7 +9,8 @@ class SearchStrategy(Enum): QDQAS = 3 -def main(search_settings: QualityDiversitySettings | TrainingFreeSettings | GeneticAlgorithmSettings): +def main(search_strategy: SearchStrategy): + pass diff --git a/src/ga_qas/__init__.py b/src/ga_qas/__init__.py deleted file mode 100644 index 8d947ca..0000000 --- a/src/ga_qas/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from dataclasses import dataclass - -from settings import QuantumArchitectureSearchSettings - - -@dataclass -class QualityDiversitySettings(QuantumArchitectureSearchSettings): - initial_population_size: int diff --git a/src/qd_qas/__init__.py b/src/qd_qas/__init__.py deleted file mode 100644 index 59d9311..0000000 --- a/src/qd_qas/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -from dataclasses import dataclass -from typing import Callable - -from settings import QuantumArchitectureSearchSettings - - -@dataclass -class QualityDiversitySettings(QuantumArchitectureSearchSettings): - """ - Hyperparameters for Quality Diversity based Quantum Architecture Search - """ - - initial_population_size: int - """How many (random) circuits should be generated at the beginning""" - - offspring_size: int - """How many offspring should be generated from the previous generation""" - - generation_size: int - """How many of the previous generation offspring become a parent for the next generation""" - - generation_count: int - """How many generations to optimize for""" - - mutation_rate: float - """for each gate in a circuit from the offspring perform a mutation if random [0, 1) < mutation_rate""" - - cost_function: Callable[[ParametrizedQuantumCircuit], float] diff --git a/src/quantum_circuit/__init__.py b/src/quantum_circuit/__init__.py deleted file mode 100644 index f9b6c15..0000000 --- a/src/quantum_circuit/__init__.py +++ /dev/null @@ -1,146 +0,0 @@ -import enum -from dataclasses import dataclass - -from qiskit import QuantumCircuit -from qiskit.circuit.parametervector import ParameterVector - -from quantum_circuit.proxy_config import ProxyConfig -from quantum_circuit.qiskit_helpers import build_qiskit_circ - -from .proxies import (calculate_entanglement, calculate_expressivity, - calculate_fidelity) - - -class QuantumType(enum.Enum): - Identity = enum.auto() - Hadamard = enum.auto() - X = enum.auto() - RX = enum.auto() - RXX = enum.auto() - Y = enum.auto() - RY = enum.auto() - RYY = enum.auto() - Z = enum.auto() - RZ = enum.auto() - RZZ = enum.auto() - CX = enum.auto() - CRX = enum.auto() - CZ = enum.auto() - - def is_single_qubit(self): - return self in { - QuantumType.Identity, - QuantumType.Hadamard, - QuantumType.X, - QuantumType.RX, - QuantumType.Y, - QuantumType.RY, - QuantumType.Z, - QuantumType.RZ, - } - - def is_parameterized(self): - return self in { - QuantumType.RX, - QuantumType.RXX, - QuantumType.RY, - QuantumType.RYY, - QuantumType.RZ, - QuantumType.RZZ, - QuantumType.CRX, - } - - -@dataclass(frozen=True) -class QuantumGate: - type: QuantumType - qubits: tuple[int] | tuple[int, int] - - -class ParametrizedQuantumCircuit: - """ - A Quantum circuit representation to use in the Quantum Architecture Searches. - It has methods for various proxies and stores the results as well, to have everything close. - Allows easy access to the gates inside so it can be used by functions that use it. - """ - - qubits: int - """How many qubits does the circuit contain""" - - gates: list[list[QuantumGate]] - """Gates in the quantum circuit, organised as a list of layers of gates.""" - - parameters: int - """How many parameters are used the circuit""" - - _expressivity: float | None = None - """ - Expressivity of the circuit, following the definition from {THE PAPER} - Use `expressivity` to get the value - """ # TODO: link paper - - _entanglement: float | None = None - """ - Entanglement of the circuit, following the definition from {THE PAPER} - Use `entanglement` to get the value - """ # TODO: link paper - - _fidelity: float | None = None - """ - Approximated fidelity of the circuit - Use `fidelity` to get the value - """ - - _qiskit_circ: tuple[QuantumCircuit, ParameterVector] | None = None - - def __init__(self, qubits: int): - """ - Initialise an empty circuit for the passed amount of qubits - """ - self.qubits = qubits - self.gates = [] - - def append_layer(self, layer: list[QuantumGate]): - """ - Add a layer to the end of the existing circuit in-place - """ - for gate in layer: - if gate.type.is_parameterized(): - self.parameters += 1 - self.gates.append(layer) - - @property - def circ(self) -> tuple[QuantumCircuit, ParameterVector]: - """ - Qiskit circuit version to be used for simulations etc. - """ - if self._qiskit_circ is None: - self._qiskit_circ = build_qiskit_circ(self) - - return self._qiskit_circ - - def expressivity(self, config: ProxyConfig) -> float: - """ - Expressivity of the circuit, following the definition from {THE PAPER} - """ # TODO: Link Paper - if self._expressivity is None: - self._expressivity = float(calculate_expressivity(self, config)[0]) - return self._expressivity - - def entanglement(self, config: ProxyConfig) -> float: - """ - Entanglement of the circuit, following the definition from {THE PAPER} - """ # TODO: Link Paper - if self._entanglement is None: - self._entanglement = float(calculate_entanglement(self, config)[0]) - - return self._entanglement - - def fidelity(self, config: ProxyConfig) -> float: - """ - Approximated fidelity of the circuit - """ - if self._fidelity is None: - self._fidelity = float(calculate_fidelity(self, config)[0]) - - return self._fidelity diff --git a/src/quantum_circuit/proxies.py b/src/quantum_circuit/proxies.py deleted file mode 100644 index c385fe0..0000000 --- a/src/quantum_circuit/proxies.py +++ /dev/null @@ -1,94 +0,0 @@ -from itertools import pairwise -from typing import TYPE_CHECKING - -import numpy as np -from numpy.typing import NDArray -from qiskit import transpile -from qiskit_aer.backends.aer_simulator import AerSimulator -from qiskit_aer.backends.aerbackend import AerBackend -from scipy import stats -from tqdm import tqdm - -if TYPE_CHECKING: - from quantum_circuit import ParametrizedQuantumCircuit - from quantum_circuit.proxy_config import ProxyConfig - - -def single_circuit_param_fidelity(circ: ParametrizedQuantumCircuit, samples: int, backend: AerBackend) -> float: - qc, thetas = circ.circ - qc.save_statevector() - - tqc = transpile(qc, backend) - - number_of_initial_circuits = 2 * samples - - params: NDArray[np.float64] = np.random.uniform(-np.pi, np.pi, (len(thetas), number_of_initial_circuits)) - - binds = [{param: params[idx] for idx, param in enumerate(thetas.params)}] - - job = backend.run([tqc], parameter_binds=binds) - result = job.result() - - sv = np.array( - [np.asarray(result.get_statevector(i), dtype=np.complex128) for i in range(number_of_initial_circuits)] - ) - left = sv[:samples] - right = sv[samples:] - - return np.power(np.absolute((left * right.conjugate()).sum(-1)), 2) - - -def calculate_fidelity( - circs: list[ParametrizedQuantumCircuit] | ParametrizedQuantumCircuit, config: ProxyConfig -) -> NDArray[np.float64]: - if isinstance(circs, ParametrizedQuantumCircuit): - circs = [circs] - raise NotImplementedError - - -def calculate_expressivity( - circs: list[ParametrizedQuantumCircuit] | ParametrizedQuantumCircuit, config: ProxyConfig -) -> NDArray[np.float64]: - tqdm_depth = 0 - if isinstance(circs, ParametrizedQuantumCircuit): - circs = [circs] - - qubits = config.qubits - samples = config.expressivity_samples - bin_count = config.expressivity_bins - force_recalculate = config.force_recalculate - bins: np.ndarray[tuple[int], np.dtype[np.float64]] = np.linspace(0.0, 1.0, bin_count + 1) - - haar_power = (1 << qubits) - 1 - lower_edges: np.ndarray[tuple[int], np.dtype[np.float64]] = -1.0 * np.power(1.0 - bins[:-1], haar_power) - higher_edges: np.ndarray[tuple[int], np.dtype[np.float64]] = -1.0 * np.power(1.0 - bins[1:], haar_power) - haar_values: np.ndarray[tuple[int], np.dtype[np.float64]] = higher_edges - lower_edges - - backend = AerSimulator(method="statevector") - - fidelities = np.zeros((len(circs))) - for idx, circuit in tqdm(enumerate(circs), position=tqdm_depth, desc="Computing Fidelities", leave=False): - if not force_recalculate and circs[idx]._expressivity is not None: - continue - fidelities[idx] = single_circuit_param_fidelity(circuit, samples, backend) - - expressivity = np.zeros((len(circs))) - for idx, fid in tqdm(enumerate(fidelities), position=tqdm_depth, desc="Computing Expressibility"): - if not force_recalculate and circs[idx]._expressivity is not None: - continue - bin_idx = np.floor(fid * bin_count).astype(int) - num = np.array([len(bin_idx[bin_idx == i]) for i in range(bin_count)]) - - expressivity[idx] = -stats.entropy(num, haar_values) - # NOTE: I'm setting the expressivity here directly, for caching - circs[idx]._expressivity = float(expressivity[idx]) - - return expressivity - - -def calculate_entanglement( - circs: list[ParametrizedQuantumCircuit] | ParametrizedQuantumCircuit, config: ProxyConfig -) -> NDArray[np.float64]: - if isinstance(circs, ParametrizedQuantumCircuit): - circs = [circs] - raise NotImplementedError diff --git a/src/quantum_circuit/proxy_config.py b/src/quantum_circuit/proxy_config.py deleted file mode 100644 index a16e9e6..0000000 --- a/src/quantum_circuit/proxy_config.py +++ /dev/null @@ -1,17 +0,0 @@ -from dataclasses import dataclass - - -@dataclass(frozen=True) -class ProxyConfig: - """ - Configuration for various proxies, defaults are included for every setting so only the - non-default values need to be changed when creating one - """ - - qubits: int - force_recalculate: bool = False - - entanglement_samples: int = 1000 - - expressivity_samples: int = 1000 - expressivity_bins: int = 100 diff --git a/src/quantum_circuit/qiskit_helpers.py b/src/quantum_circuit/qiskit_helpers.py deleted file mode 100644 index a9476bc..0000000 --- a/src/quantum_circuit/qiskit_helpers.py +++ /dev/null @@ -1,65 +0,0 @@ -from typing import TYPE_CHECKING - -from qiskit import QuantumCircuit -from qiskit.circuit.parameter import Parameter -from qiskit.circuit.parametervector import ParameterVector - -if TYPE_CHECKING: - from quantum_circuit import (ParametrizedQuantumCircuit, QuantumGate, - QuantumType) - - -def add_qubit_gate(circ: QuantumCircuit, gate: QuantumGate, theta: Parameter) -> int: - match gate.type: - case QuantumType.Identity: - return 0 - case QuantumType.Hadamard: - circ.h(gate.qubits[0]) - return 0 - case QuantumType.X: - circ.x(gate.qubits[0]) - return 0 - case QuantumType.RX: - circ.rx(theta, gate.qubits[0]) - return 1 - case QuantumType.RXX: - circ.rxx(theta, gate.qubits[0], gate.qubits[1]) # ty:ignore[index-out-of-bounds] - return 1 - case QuantumType.Y: - circ.y(gate.qubits[0]) - return 0 - case QuantumType.RY: - circ.ry(theta, gate.qubits[0]) - return 1 - case QuantumType.RYY: - circ.ryy(theta, gate.qubits[0], gate.qubits[1]) # ty:ignore[index-out-of-bounds] - return 1 - case QuantumType.Z: - circ.z(gate.qubits[0]) - return 0 - case QuantumType.RZ: - circ.rz(theta, gate.qubits[0]) - return 1 - case QuantumType.RZZ: - circ.rzz(theta, gate.qubits[0], gate.qubits[1]) # ty:ignore[index-out-of-bounds] - return 1 - case QuantumType.CRX: - circ.crx(theta, gate.qubits[0], gate.qubits[1]) # ty:ignore[index-out-of-bounds] - return 1 - case QuantumType.CX: - circ.cx(gate.qubits[0], gate.qubits[1]) # ty:ignore[index-out-of-bounds] - return 0 - raise NotImplementedError - - -def build_qiskit_circ(pqc: "ParametrizedQuantumCircuit") -> tuple[QuantumCircuit, ParameterVector]: - - circ = QuantumCircuit(pqc.qubits) - thetas = ParameterVector("thetas", circ.parameters) - - current_theta_index = 0 - for layer in pqc.gates: - for gate in layer: - current_theta_index += add_qubit_gate(circ, gate, thetas[current_theta_index]) - - return circ, thetas diff --git a/src/settings.py b/src/settings.py deleted file mode 100644 index 6ef7827..0000000 --- a/src/settings.py +++ /dev/null @@ -1,6 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class QuantumArchitectureSearchSettings: - qubits: int diff --git a/src/tf_qas/__init__.py b/src/tf_qas/__init__.py deleted file mode 100644 index cf2f346..0000000 --- a/src/tf_qas/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from dataclasses import dataclass - -from settings import QuantumArchitectureSearchSettings - - -@dataclass -class TrainingFreeSettings(QuantumArchitectureSearchSettings): - sample_size: int