Quantum Machine Learning
Overview
The unitarylab_algorithms.quantum_machine_learning package provides variational and generative quantum algorithms suitable for near-term quantum devices (the NISQ era). These algorithms use classical-quantum hybrid optimization loops to train parameterized quantum circuits.
| Algorithm | Class | Task |
|---|---|---|
| VQE | VQEAlgorithm | Ground-state energy estimation |
| VQC | VQCAlgorithm | Quantum classifier (Iris dataset) |
| QAOA | QAOAAlgorithm | Combinatorial optimization (Max-Cut problem) |
| QCBM | QCBMAlgorithm | Generative modeling of probability distributions (Bars-and-Stripes) |
| CVQNN | CVQNNAlgorithm | Continuous-variable quantum neural network classification |
| Fermi-Hubbard VQE | FermiHubbardVQEAlgorithm | Ground-state solver for the one-dimensional open Fermi-Hubbard model |
Return Values and Output
All algorithms return status, circuit_path, plot, and circuit, plus their own result fields. The content of plot varies by algorithm: VQE/VQC/CVQNN each have 1 entry, QAOA has 2, QCBM has 3, and Fermi-Hubbard VQE includes a convergence plot and a parameter .npy file. Optimization quality should be judged by combining result fields such as energy, loss, accuracy, and optimizer status.
Variational Quantum Eigensolver (VQE)
Background
VQE estimates the ground-state energy of a Hermitian Hamiltonian by minimizing the expectation value over a parameterized ansatz state. This algorithm is widely used in quantum chemistry and materials simulation.
The algorithm alternates between:
- Evaluating on the quantum circuit
- Updating the parameters using COBYLA (
scipy.optimize.minimize)
The ansatz is a Ry-Rz ring-entangling circuit: each layer first applies Ry+Rz rotations to every qubit, then entangles adjacent qubits (including the first-last wraparound) with a ring of CNOT gates.
Import
from unitarylab_algorithms import VQEAlgorithm.run() Parameters
def run(self, n=2, layers=2, max_iter=150, seed=7,
hamiltonian=None, normalize=True,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
n | int | 2 | Number of qubits (only effective when hamiltonian=None) |
layers | int | 2 | Number of variational layers in the ansatz |
max_iter | int | 150 | Maximum number of COBYLA iterations |
seed | int | 7 | Random seed for random Hamiltonian generation and initial parameter sampling |
hamiltonian | np.ndarray | None | None | Custom Hermitian Hamiltonian; when None, a random Hermitian matrix is generated internally |
normalize | bool | True | Whether to normalize by the spectral norm when generating a random Hamiltonian (only affects the random-Hamiltonian branch) |
_validate_hamiltonian() checks that the matrix is square, has a dimension that is a power of 2, and satisfies ; otherwise it raises ValueError.
Return Value
{
"status": "ok",
"circuit_path": "<path to VQE_Circuit.svg.svg>",
"plot": [{"format": "svg", "filename": "<path to VQE_Convergence.svg>"}],
"circuit": <Circuit object>,
"Exact Energy": ...,
"VQE Energy": ...,
"Absolute Error": ...,
"Optimizer Message": "...",
"Quantum Comp Time": ...,
}Example
import numpy as np
from unitarylab_algorithms import VQEAlgorithm
# Use a custom Hamiltonian
H = np.array([[1, 0.5], [0.5, -1]])
algo = VQEAlgorithm()
result = algo.run(n=2, layers=2, max_iter=150, hamiltonian=H)
print(result['status'], result['VQE Energy'], result['Absolute Error'])Quick Demo
from unitarylab_algorithms.quantum_machine_learning.vqe.algorithm import test
test(n=2, layers=2, max_iter=150)Notes
- When a custom Hamiltonian is supplied,
nis silently overridden: ifhamiltonianis notNone, the code first validates it and derives the true number of qubits from the matrix dimension, then executesn = num_qubits; thenargument passed by the user is ignored without any error or warning— - The
normalizeparameter is only used whenhamiltonian=None(the internal random-generation branch); if a custom Hamiltonian is supplied,normalizehas no effect whatsoever. - The
descriptionfield inparameters.jsonstill states “find the ground-state energy of the 2-qubit Ising Hamiltonian H = Z0Z1 - 0.5X0 - 0.5X1,” but the actual default behavior ofrun()is to generate a random Hermitian matrix (determined byseed), not this fixed Ising Hamiltonian—theparameters.jsondescription is out of date and should not be relied on as a reference for the default behavior. - UI default value does not match the Python default value: the default value of
layersinparameters.jsonis3, while the default in therun()signature islayers=2; submitting an empty form via the Web UI and callingVQEAlgorithm().run()directly will not produce the same number of layers. statusis always'ok'(see the package-level note), so you should useOptimizer Messageinstead to judge whether COBYLA actually converged.
Variational Quantum Classifier (VQC)
Background
VQC maps classical features into a quantum state via angle encoding, then performs 3-class classification using a trainable ansatz plus Pauli-Z expectation-value observables. This implementation is fixed to the Iris dataset (4 features, 3 classes); gradients are computed parameter-by-parameter via a manually implemented Parameter Shift Rule (not automatic differentiation), and training uses CrossEntropyLoss (applied to expectation-value logits scaled by 10).
Import
from unitarylab_algorithms import VQCAlgorithm.run() Parameters
def run(self, layers=3, epochs=20, lr=0.05, batch_size=16,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
layers | int | 3 | Number of variational layers |
epochs | int | 20 | Number of training epochs (number of passes over the full training set) |
lr | float | 0.05 | Adam optimizer learning rate |
batch_size | int | 16 | Mini-batch size |
The number of qubits is fixed at 4 (corresponding to the 4 features of the Iris dataset) and is not exposed as a parameter.
Return Value
{
"status": "ok",
"circuit_path": "<path to vqc_algorithm_circuit.svg>",
"plot": [{"format": "svg", "filename": "<path to VQC_Metrics.svg>"}],
"circuit": <Circuit object>,
"Final Loss": ...,
"Final Accuracy": ...,
"Quantal Computation Time (s)": ...,
}Example
from unitarylab_algorithms import VQCAlgorithm
algo = VQCAlgorithm()
result = algo.run(layers=3, epochs=20, lr=0.05, batch_size=16)
print(result['status'], result['Final Accuracy'])Quick Demo
from unitarylab_algorithms.quantum_machine_learning.vqc.algorithm import test
test(layers=3, epochs=20, lr=0.05, batch_size=16)Notes
- The output key in the returned dictionary contains a spelling issue: the actual output key is named
"Quantal Computation Time (s)"(it should be “Quantum”), and this is the exact spelling as it appears in the source code. When retrieving this field by name, callers must use this exact (misspelled) string, not the semantically “correct”"Quantum Computation Time (s)". - The number of qubits is fixed at 4 (determined by the number of features in the Iris dataset) and cannot be adjusted for other datasets via parameters.
_load_iris_data()prefers to usesklearn.datasets.load_iris; ifscikit-learnis not installed in the environment, it falls back to a fixed copy of the Iris data embedded in the source file (120 training / 30 test samples), so the algorithm can still run even without thescikit-learndependency—but in the fallback path, the train/test split ratio cannot be customized (the embedded data uses a fixed 80/20 split).- The parameter-shift gradient is computed via a nested
forloop that performs forward/backward shifted evaluations for each individual component oftheta(a total of circuit executions per parameter update), and the computational cost grows linearly as the number of layers and batch size increase. __init__unconditionally callstorch.manual_seed(42)andnp.random.seed(42), and does not expose aseedparameter. Along the normal path of “instantiate, then immediately callrun(),” both the initial parameters and the dataset split are reproducible—althoughtrain_test_splitis not explicitly passed arandom_state, it uses the NumPy global random state that was just reset to 42; if other code consumes that global random state after instantiation but before callingrun(), the dataset split may still change. Thus the current implementation relies on the global random state—reproducible, but not isolated—and users have no way to adjust the seed externally.- The default value of
layersinparameters.jsonis5, while the default in therun()signature islayers=3—again a case of the UI default and the Python default not matching. statusis always'ok'(see the package-level note).
Quantum Approximate Optimization Algorithm (QAOA)
Background
QAOA is a variational algorithm for combinatorial optimization problems. It alternates between applying a problem Hamiltonian (composed of a term for each edge of the graph) that encodes the objective function, and a mixing layer implemented via rotations, executing layers rounds in total. This implementation solves the Max-Cut problem on a graph defined by an edge list, optimizing via COBYLA.
Import
from unitarylab_algorithms import QAOAAlgorithm.run() Parameters
def run(self, edges=None, n=6, layers=4, max_iter=100,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
edges | List[Tuple[int,int]] | None | None | Edge list of the graph; when None, the built-in default graph [(0,1),(1,2),(2,3),(3,0),(0,4),(1,5)] is used |
n | int | 6 | Number of qubits (number of graph vertices) |
layers | int | 4 | Number of QAOA evolution layers |
max_iter | int | 100 | Maximum number of COBYLA iterations |
Return Value
{
"status": "ok",
"circuit_path": "<path to QAOA_Circuit.svg>",
"plot": [
{"format": "svg", "filename": "<path to QAOA_Convergence.svg>"},
{"format": "svg", "filename": "<path to MaxCut_Solution.svg>"},
],
"circuit": <Circuit object>,
"Optimal bitstring": "...",
"Max-Cut Value": ...,
"Optimized Energy": ...,
"Quantum Computation Time": ...,
}Example
from unitarylab_algorithms import QAOAAlgorithm
edges = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 4), (1, 5)]
algo = QAOAAlgorithm()
result = algo.run(edges=edges, n=6, layers=4, max_iter=100)
print(result['status'], result['Optimal bitstring'], result['Max-Cut Value'])Quick Demo
from unitarylab_algorithms.quantum_machine_learning.qaoa.algorithm import test
test()Notes
QAOAAlgorithm.__init__unconditionally callsnp.random.seed(42)andtorch.manual_seed(42), and does not expose aseedparameter; the initial parametersinitial_params = np.random.uniform(0, np.pi, 2*layers)depend on this global seed.- The
plotfield contains 2 entries (the convergence curve plot + the Max-Cut result plot); callers iterating overplotshould not take only the first element. - The module-level
test()function itself defaults tomax_iter=60, which is inconsistent with the.run()method signature’s default ofmax_iter=100; while theif __name__ == "__main__":block at the bottom of the file explicitly usesmax_iter=100. The three default values are not unified, so check the default actually used based on which entry point you call (.run()ortest()). statusis always'ok'(see the package-level note), so quality should be judged by comparingOptimized Energyagainst the known exact Max-Cut value of the graph.
Quantum Circuit Born Machine (QCBM)
Background
QCBM uses a parameterized quantum circuit as a generative model, training the Born probability distribution to fit a target distribution. The target distribution in this implementation is fixed to be the Bars-and-Stripes (BAS) distribution, computed internally by _get_bas_dist(n) (which derives the rows × cols grid closest to square based on , enumerates binary patterns where all rows are identical or all columns are identical as “valid” states, and assigns them uniform nonzero probability); arbitrary target distributions supplied by the user are not supported. The loss function is the KL divergence (torch.sum(target * log((target+eps)/(curr+eps)))), and gradients are computed via the parameter-shift rule.
Import
from unitarylab_algorithms import QCBMAlgorithm.run() Parameters
def run(self, n=4, layers=4, epochs=40, lr=0.1,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
n | int | 4 | Number of qubits |
layers | int | 4 | Variational circuit depth |
epochs | int | 40 | Number of training iterations |
lr | float | 0.1 | Adam optimizer learning rate |
_validate_run_params() explicitly validates that n/layers/epochs are positive integers and lr is positive, raising ValueError otherwise—this is one of the few algorithms in this package that performs genuine input validation at the Python level.
Return Value
{
"status": "ok",
"circuit_path": "<path to qcbm_algorithm_circuit.svg>",
"plot": [
{"format": "svg", "filename": "<path to QCBM_Loss.svg>"},
{"format": "svg", "filename": "<path to QCBM_Distribution.svg>"},
{"format": "svg", "filename": "<path to QCBM_Samples.svg>"},
],
"circuit": <Circuit object>,
"Final KL Loss": ...,
"Quantum Computation Time": ...,
}Example
from unitarylab_algorithms import QCBMAlgorithm
algo = QCBMAlgorithm()
result = algo.run(n=4, layers=4, epochs=40, lr=0.1)
print(result['status'], result['Final KL Loss'])Quick Demo
from unitarylab_algorithms.quantum_machine_learning.qcbm.algorithm import test
test(n=4, layers=4, epochs=40, lr=0.1)Notes
- The target distribution is fixed to the BAS (Bars-and-Stripes) distribution, generated from
nvia_get_bas_dist(n); supplying a custom target distribution is not supported. - The loss function uses the KL divergence (see the formula above).
- The
plotfield contains 3 entries (the loss curve, the distribution comparison plot, and the sample grid plot). QCBMAlgorithm.__init__unconditionally callstorch.manual_seed(42)/np.random.seed(42), and does not expose aseedparameter.statusis always'ok'(see the package-level note), but this algorithm is one of the few in this package that performs genuine validation of its parameters at the entry point (_validate_run_params)—invalid inputs will directly raiseValueErrorrather than silently producing an erroneous result.
Continuous-Variable Quantum Neural Network (CVQNN)
Background
CVQNN operates under the continuous-variable (CV) quantum computing paradigm, using optical modes rather than qubits. Within a truncated Fock space (of dimension cutoff), it constructs a variational circuit using displacement, squeezing, beamsplitter, rotation, and Kerr nonlinearity operators (all implemented via torch.matrix_exp), trained for binary classification (MSELoss, with labels mapped to ).
Import
from unitarylab_algorithms import CVQNNAlgorithm.run() Parameters
def run(self, x_train: np.ndarray, y_train: np.ndarray,
n_layers: int = 2, cutoff: int = 6, epochs: int = 40,
lr: float = 0.05) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
x_train | np.ndarray | — (required) | Input feature array, shape (N, 2) |
y_train | np.ndarray | — (required) | Label array (0/1) |
n_layers | int | 2 | Number of variational CV layers |
cutoff | int | 6 | Fock space truncation dimension |
epochs | int | 40 | Number of training epochs |
lr | float | 0.05 | Adam optimizer learning rate |
Note: unlike the other algorithms in this package, CVQNNAlgorithm.run() has no backend/device/dtype parameters—the CV simulation is entirely implemented by a custom CVSimulator (a dense matrix exponential based on torch.matrix_exp) and does not go through the Circuit.execute() backend dispatch.
Return Value
{
"status": "ok",
"circuit_path": "<path to cvqnn_algorithm_circuit.svg>",
"plot": [{"format": "svg", "filename": "<path to CVQNN_Metrics.svg>"}],
"circuit": <Circuit object>,
"Final Loss": ...,
"Final Accuracy": ...,
"Total Computation Time (s)": ...,
}Example
import numpy as np
from unitarylab_algorithms import CVQNNAlgorithm
# Generate a simple 2D dataset
x_train = np.random.randn(50, 2)
y_train = (x_train[:, 0] + x_train[:, 1] > 0).astype(int)
algo = CVQNNAlgorithm()
result = algo.run(x_train=x_train, y_train=y_train, n_layers=2, cutoff=6, epochs=30)
print(result['status'], result['Final Accuracy'])Quick Demo
from unitarylab_algorithms.quantum_machine_learning.cvqnn.algorithm import test
test(n_layers=2, cutoff=6, epochs=30, lr=0.05)Notes
- The saved circuit diagram does not reflect the trained gate parameters: the
Circuitdrawn bysave_circuit()comes from_build_circuit(), which builds the topology only using gates such asqc.ry(0.0, ...)/qc.rx(0.0, ...)/qc.rz(0.0, ...)whose placeholder angles are always 0, purely to display the structure. The continuous-variable parameters actually involved in training and inference (sq_r,disp_r,rot_theta,kerr_k,bs_theta) exist in a separateCVClassifier/CVSimulator(based on rawtorchtensor operations), and are never written back to the exported circuit diagram. Therefore the SVG corresponding tocircuit_pathcan only convey the circuit structure, not the actual trained parameter values. - Increasing
cutoffimproves approximation accuracy, but the memory footprint (acutoff × cutoffdense matrix exponential) grows accordingly; going beyond12is not recommended on standard hardware (the UI upper limit inparameters.jsonis also12). - The CV framework models optical quantum systems and is fundamentally different from qubit-based circuits; operators such as
x_opandn_opare defined in the truncated Fock basis, and too small acutoffwill introduce truncation error. CVQNNAlgorithm.__init__unconditionally callstorch.manual_seed(42),np.random.seed(42), and setstorch.set_default_dtype(torch.float64), and does not expose aseedparameter.- The module-level
test()function itself defaults toepochs=30, inconsistent with the.run()method signature’s default ofepochs=40; the dataset insidetest()is generated preferentially viasklearn.datasets.make_moons, falling back to a fixed copy embedded in the source code (40 samples) if unavailable. statusis always'ok'(see the package-level note).
Fermi-Hubbard Model VQE (Fermi-Hubbard VQE)
FermiHubbardVQEAlgorithm is located in fermi_hubbard_vqe/algorithm.py.
Background
This algorithm targets the one-dimensional open-boundary Fermi-Hubbard model (L sites, nearest-neighbor hopping coefficient t, on-site interaction strength U, Zeeman magnetic field coefficient B):
- Constructs the fermionic Hamiltonian and its Pauli-string expression after Jordan-Wigner transformation via the
unitarylab.library.fermi_hubbard.fermi_hubbard_paulimodule; - Uses
pauli_ground_state()to perform dense exact diagonalization, obtaining the exact ground-state energy as a reference; - Internally reuses
VQEAlgorithm(see the “Variational Quantum Eigensolver” section above) to run VQE optimization, obtaining an approximate ground-state energy for this Pauli Hamiltonian; - Optionally measures the total spin magnetic moment on the optimized circuit.
Endianness Adaptation
The qubit numbering convention used by the Pauli expressions generated by fermi_hubbard_pauli is the opposite of the UnitaryLab circuit simulator’s convention, in which the lowest-index qubit (q0) is the least significant bit. Therefore, before handing the Pauli Hamiltonian matrix to VQEAlgorithm, the source code calls an internal _bit_reverse_hamiltonian() to apply a bit-reversal permutation to the matrix’s rows and columns, and performs the following self-checks (raising an exception, rather than silently producing an incorrect result, if any check fails):
- Round-trip consistency: applying the reversal twice should restore the original matrix, with an error of
< 1e-12(otherwise raisesValueError); - Spectral consistency: the eigenvalue spectrum of the matrix before and after reversal should match, with an error of
< 1e-10(otherwise raisesValueError); - Environment self-check
_check_unitarylab_environment(): on the first call within a process, executesCircuit(2).x(0)and checks that the final state’s peak indeed falls at index1(i.e., verifying that the assumption that q0 is the least significant bit actually holds), raisingRuntimeErrorif the bit-ordering convention of the current UnitaryLab version has changed.
Import
from unitarylab_algorithms import FermiHubbardVQEAlgorithm.run() Parameters
def run(self, params: Dict[str, Any] | str | None = None, *,
L: int = 2, t: float = 1.0, U: float = 4.0, B: float = 1.5,
layers: int = 5, max_iter: int = 1000, seed: int = 7,
measure_shots: int = 10000,
backend: str = "torch", device: str = "cpu",
dtype = np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
params | dict | str | None | None | Optional: pass the keyword parameters below all at once as a dict or JSON string (see the description below), overriding the corresponding keyword defaults |
L | int | 2 | Number of one-dimensional sites (open boundary) |
t | float | 1.0 | Nearest-neighbor hopping coefficient |
U | float | 4.0 | On-site interaction strength |
B | float | 1.5 | Zeeman magnetic field coefficient |
layers | int | 5 | Number of variational layers in the VQE ansatz |
max_iter | int | 1000 | Maximum number of COBYLA iterations |
seed | int | 7 | Random seed for VQE initial parameters |
measure_shots | int | 10000 | Number of shots used to measure the total spin magnetic moment on the optimized circuit; set to 0 to skip this measurement stage |
Other than params, all remaining parameters are keyword-only (after the * in the signature). If params is supplied, its keys override the corresponding keyword defaults; layers/max_iter/measure_shots also each accept the alias keys vqe_layers/vqe_max_iter/measurement_shots.
Return Value
{
"status": "ok",
"circuit_path": "<path to Fermi_Hubbard_VQE_Circuit.svg>",
"plot": [
{"format": "svg", "filename": "<path to Fermi_Hubbard_VQE_Convergence.svg>"},
{"format": "npy", "filename": "<path to Fermi_Hubbard_VQE_Parameters.npy>"},
],
"circuit": <Circuit object>,
"Exact Energy": ...,
"VQE Energy": ...,
"Absolute Error": ...,
"Circuit Energy": ...,
"Number of Qubits": ...,
"Optimizer Evaluations": ...,
"Optimizer Converged": ...,
"Optimizer Message": "...",
"VQE Runtime": ...,
"Total Runtime": ...,
"Qubit Mapping": ...,
"Fermionic Hamiltonian": "...",
"Pauli Hamiltonian": "...",
# Appended only when measure_shots > 0:
"Measured Magnetic Moment": ...,
"Magnetic Moment Standard Errors": ...,
"Measurement Total Shots": ...,
}Example
from unitarylab_algorithms import FermiHubbardVQEAlgorithm
algo = FermiHubbardVQEAlgorithm()
result = algo.run(L=2, t=1.0, U=4.0, B=1.5, layers=5, max_iter=1000, measure_shots=10000)
print(result['status'], result['VQE Energy'], result['Exact Energy'])The parameters can also be passed as a single dictionary as a whole (equivalent to passing them individually as keywords when params=None):
algo = FermiHubbardVQEAlgorithm()
result = algo.run(params={"L": 3, "U": 6.0, "vqe_layers": 6})Quick Demo
from unitarylab_algorithms.quantum_machine_learning.fermi_hubbard_vqe.algorithm import test
test(L=2, t=1.0, U=4.0, B=1.5, layers=5, max_iter=1000, seed=7, measurement_shots=10000)Notes
- Best-observed parameter tracking: internally, the source code uses
_TrackingVQEAlgorithm(a subclass ofVQEAlgorithm) that overrides_expectation(), recording the energy history and the parameter combination with the lowest energy so far (best_energy/best_parameters) on every COBYLA objective-function evaluation, because the final iteration point at which COBYLA converges is not necessarily the historical optimum—the returnedVQE Energy/Circuit Energycome from this “historical best,” not from the optimizer’s last iteration. - Multiple correctness self-checks, raising an exception on failure: besides the endianness self-check,
run_pauli_vqe()also checks: whether the internal VQE call reportsstatus == 'ok'(otherwiseRuntimeError); whether zero objective-function evaluations occurred (otherwiseRuntimeError); whetherbest_parametersexists and its energy is finite (otherwiseRuntimeError); whether the exact energy reported by VQE matches the spectrum of the Pauli Hamiltonian (error< 1e-10, otherwiseValueError); whether the energy recomputed on the optimized circuit matches the recorded best energy (error< 1e-8, otherwiseRuntimeError); and whether the best energy violates the variational lower bound (< exact energy - 1e-8triggersValueError, suggesting a possible endianness issue). Therefore, once this algorithm returns normally (without raising an exception), its numerical consistency is better guaranteed than that of the other algorithms in this package; but this also means it is more prone than the other algorithms to directly raising an exception and terminating on abnormal input or environment conditions, rather than returning a result dict withstatus: 'failed'. paramsand individual keyword arguments can be mixed, but keys inparamsalways take priority in overriding the corresponding keyword defaults (they will not override other keywords you explicitly passed under a different name than the one inparams).- The second entry of the
plotfield is a.npyfile (a NumPy array of the optimized VQE parameters), not an image; frontend rendering or download logic needs to branch on theformatfield, and should not assume that everything in theplotlist is a directly displayable image. - When
measure_shots=0, the magnetic-moment measurement stage is skipped, and the returned dictionary will not include the three measurement-related keys such asMeasured Magnetic Moment—callers should check whether these keys exist rather than assuming they always appear.
Version Reference
from unitarylab_algorithms import (
VQEAlgorithm,
VQCAlgorithm,
QAOAAlgorithm,
QCBMAlgorithm,
CVQNNAlgorithm,
FermiHubbardVQEAlgorithm,
)