qoro-divi 0.3.4__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qoro-divi might be problematic. Click here for more details.
- divi/backends/__init__.py +1 -1
- divi/backends/_circuit_runner.py +42 -0
- divi/backends/_parallel_simulator.py +145 -49
- divi/backends/_qoro_service.py +451 -182
- divi/backends/_qpu_system.py +77 -3
- divi/circuits/_core.py +124 -4
- divi/circuits/qasm.py +20 -3
- divi/extern/cirq/_validator.py +12 -3
- divi/qprog/__init__.py +1 -0
- divi/qprog/algorithms/_ansatze.py +112 -12
- divi/qprog/algorithms/_qaoa.py +179 -110
- divi/qprog/algorithms/_vqe.py +192 -58
- divi/qprog/batch.py +270 -51
- divi/qprog/exceptions.py +9 -0
- divi/qprog/optimizers.py +336 -51
- divi/qprog/quantum_program.py +162 -339
- divi/qprog/variational_quantum_algorithm.py +786 -0
- divi/qprog/workflows/_graph_partitioning.py +43 -38
- divi/qprog/workflows/_qubo_partitioning.py +41 -24
- divi/qprog/workflows/_vqe_sweep.py +67 -39
- divi/reporting/_pbar.py +51 -9
- divi/reporting/_qlogger.py +35 -1
- divi/reporting/_reporter.py +11 -20
- divi/utils.py +100 -4
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.4.0.dist-info}/METADATA +16 -1
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.4.0.dist-info}/RECORD +30 -28
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.4.0.dist-info}/LICENSE +0 -0
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.4.0.dist-info}/LICENSES/.license-header +0 -0
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.4.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
- {qoro_divi-0.3.4.dist-info → qoro_divi-0.4.0.dist-info}/WHEEL +0 -0
divi/backends/_qpu_system.py
CHANGED
|
@@ -2,11 +2,26 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
"""Data models for Quantum Processing Units (QPUs) and QPUSystems."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field, replace
|
|
10
|
+
|
|
11
|
+
_AVAILABLE_QPU_SYSTEMS: dict[str, QPUSystem] = {}
|
|
6
12
|
|
|
7
13
|
|
|
8
14
|
@dataclass(frozen=True, repr=True)
|
|
9
15
|
class QPU:
|
|
16
|
+
"""Represents a single Quantum Processing Unit (QPU).
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
nickname: The unique name or identifier for the QPU.
|
|
20
|
+
q_bits: The number of qubits in the QPU.
|
|
21
|
+
status: The current operational status of the QPU.
|
|
22
|
+
system_kind: The type of technology the QPU uses.
|
|
23
|
+
"""
|
|
24
|
+
|
|
10
25
|
nickname: str
|
|
11
26
|
q_bits: int
|
|
12
27
|
status: str
|
|
@@ -15,6 +30,65 @@ class QPU:
|
|
|
15
30
|
|
|
16
31
|
@dataclass(frozen=True, repr=True)
|
|
17
32
|
class QPUSystem:
|
|
33
|
+
"""Represents a collection of QPUs that form a quantum computing system.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
name: The name of the QPU system.
|
|
37
|
+
qpus: A list of QPU objects that are part of this system.
|
|
38
|
+
access_level: The access level granted to the user for this system (e.g., 'PUBLIC').
|
|
39
|
+
supports_expval: Whether the system supports expectation value jobs.
|
|
40
|
+
"""
|
|
41
|
+
|
|
18
42
|
name: str
|
|
19
|
-
qpus: list[QPU]
|
|
20
|
-
access_level: str
|
|
43
|
+
qpus: list[QPU] = field(default_factory=list)
|
|
44
|
+
access_level: str = "PUBLIC"
|
|
45
|
+
supports_expval: bool = False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_qpu_systems(json_data: list) -> list[QPUSystem]:
|
|
49
|
+
"""Parses a list of QPU system data from JSON into QPUSystem objects."""
|
|
50
|
+
return [
|
|
51
|
+
QPUSystem(
|
|
52
|
+
name=system_data["name"],
|
|
53
|
+
qpus=[QPU(**qpu) for qpu in system_data.get("qpus", [])],
|
|
54
|
+
access_level=system_data["access_level"],
|
|
55
|
+
)
|
|
56
|
+
for system_data in json_data
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def update_qpu_systems_cache(systems: list[QPUSystem]):
|
|
61
|
+
"""Updates the cache of available QPU systems."""
|
|
62
|
+
_AVAILABLE_QPU_SYSTEMS.clear()
|
|
63
|
+
for system in systems:
|
|
64
|
+
if system.name == "qoro_maestro":
|
|
65
|
+
system = replace(system, supports_expval=True)
|
|
66
|
+
_AVAILABLE_QPU_SYSTEMS[system.name] = system
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_qpu_system(name: str) -> QPUSystem:
|
|
70
|
+
"""
|
|
71
|
+
Get a QPUSystem object by its name from the cache.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
name: The name of the QPU system to retrieve.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
The QPUSystem object with the matching name.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValueError: If the cache is empty or the system is not found.
|
|
81
|
+
"""
|
|
82
|
+
if not _AVAILABLE_QPU_SYSTEMS:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"QPU systems cache is empty. Call `QoroService.fetch_qpu_systems()` to populate it."
|
|
85
|
+
)
|
|
86
|
+
try:
|
|
87
|
+
return _AVAILABLE_QPU_SYSTEMS[name]
|
|
88
|
+
except KeyError:
|
|
89
|
+
raise ValueError(f"QPUSystem with name '{name}' not found in cache.") from None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_available_qpu_systems() -> list[QPUSystem]:
|
|
93
|
+
"""Returns a list of all available QPU systems from the cache."""
|
|
94
|
+
return list(_AVAILABLE_QPU_SYSTEMS.values())
|
divi/circuits/_core.py
CHANGED
|
@@ -20,6 +20,20 @@ TRANSFORM_PROGRAM.add_transform(qml.transforms.split_non_commuting)
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class Circuit:
|
|
23
|
+
"""
|
|
24
|
+
Represents a quantum circuit with its QASM representation and metadata.
|
|
25
|
+
|
|
26
|
+
This class encapsulates a PennyLane quantum circuit along with its OpenQASM
|
|
27
|
+
serialization and associated tags for identification. Each circuit instance
|
|
28
|
+
is assigned a unique ID for tracking purposes.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
main_circuit: The PennyLane quantum circuit/tape object.
|
|
32
|
+
tags (list[str]): List of string tags for circuit identification.
|
|
33
|
+
qasm_circuits (list[str]): List of OpenQASM string representations.
|
|
34
|
+
circuit_id (int): Unique identifier for this circuit instance.
|
|
35
|
+
"""
|
|
36
|
+
|
|
23
37
|
_id_counter = 0
|
|
24
38
|
|
|
25
39
|
def __init__(
|
|
@@ -28,6 +42,16 @@ class Circuit:
|
|
|
28
42
|
tags: list[str],
|
|
29
43
|
qasm_circuits: list[str] = None,
|
|
30
44
|
):
|
|
45
|
+
"""
|
|
46
|
+
Initialize a Circuit instance.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
main_circuit: A PennyLane quantum circuit or tape object to be wrapped.
|
|
50
|
+
tags (list[str]): List of string tags for identifying this circuit.
|
|
51
|
+
qasm_circuits (list[str], optional): Pre-computed OpenQASM string
|
|
52
|
+
representations. If None, they will be generated from main_circuit.
|
|
53
|
+
Defaults to None.
|
|
54
|
+
"""
|
|
31
55
|
self.main_circuit = main_circuit
|
|
32
56
|
self.tags = tags
|
|
33
57
|
|
|
@@ -44,29 +68,83 @@ class Circuit:
|
|
|
44
68
|
Circuit._id_counter += 1
|
|
45
69
|
|
|
46
70
|
def __str__(self):
|
|
71
|
+
"""
|
|
72
|
+
Return a string representation of the circuit.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
str: String in format "Circuit: {circuit_id}".
|
|
76
|
+
"""
|
|
47
77
|
return f"Circuit: {self.circuit_id}"
|
|
48
78
|
|
|
49
79
|
|
|
50
80
|
class MetaCircuit:
|
|
81
|
+
"""
|
|
82
|
+
A parameterized quantum circuit template for batch circuit generation.
|
|
83
|
+
|
|
84
|
+
MetaCircuit represents a symbolic quantum circuit that can be instantiated
|
|
85
|
+
multiple times with different parameter values. It handles circuit compilation,
|
|
86
|
+
observable grouping, and measurement decomposition for efficient execution.
|
|
87
|
+
|
|
88
|
+
Attributes:
|
|
89
|
+
main_circuit: The PennyLane quantum circuit with symbolic parameters.
|
|
90
|
+
symbols: Array of sympy symbols used as circuit parameters.
|
|
91
|
+
qem_protocol (QEMProtocol): Quantum error mitigation protocol to apply.
|
|
92
|
+
compiled_circuits_bodies (list[str]): QASM bodies without measurements.
|
|
93
|
+
measurements (list[str]): QASM measurement strings.
|
|
94
|
+
measurement_groups (list[list]): Grouped observables for each circuit variant.
|
|
95
|
+
postprocessing_fn: Function to combine measurement results.
|
|
96
|
+
"""
|
|
97
|
+
|
|
51
98
|
def __init__(
|
|
52
99
|
self,
|
|
53
100
|
main_circuit,
|
|
54
101
|
symbols,
|
|
55
|
-
grouping_strategy:
|
|
102
|
+
grouping_strategy: (
|
|
103
|
+
Literal["wires", "default", "qwc", "_backend_expval"] | None
|
|
104
|
+
) = None,
|
|
56
105
|
qem_protocol: QEMProtocol | None = None,
|
|
57
106
|
):
|
|
107
|
+
"""
|
|
108
|
+
Initialize a MetaCircuit with symbolic parameters.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
main_circuit: A PennyLane quantum circuit/tape with symbolic parameters.
|
|
112
|
+
symbols: Array of sympy Symbol objects representing circuit parameters.
|
|
113
|
+
grouping_strategy (str, optional): Strategy for grouping commuting
|
|
114
|
+
observables. Options are "wires", "default", or "qwc" (qubit-wise
|
|
115
|
+
commuting). If the backend supports expectation value measurements,
|
|
116
|
+
"_backend_expval" to place all observables in the same measurement group.
|
|
117
|
+
Defaults to None.
|
|
118
|
+
qem_protocol (QEMProtocol, optional): Quantum error mitigation protocol
|
|
119
|
+
to apply to the circuits. Defaults to None.
|
|
120
|
+
"""
|
|
58
121
|
self.main_circuit = main_circuit
|
|
59
122
|
self.symbols = symbols
|
|
60
123
|
self.qem_protocol = qem_protocol
|
|
124
|
+
self.grouping_strategy = grouping_strategy
|
|
125
|
+
|
|
126
|
+
# --- VQE Optimization ---
|
|
127
|
+
# Create a lightweight tape with only measurements to avoid deep-copying
|
|
128
|
+
# the circuit's operations inside the split_non_commuting transform.
|
|
129
|
+
measurements_only_tape = qml.tape.QuantumScript(
|
|
130
|
+
measurements=self.main_circuit.measurements
|
|
131
|
+
)
|
|
61
132
|
|
|
62
133
|
transform_program = deepcopy(TRANSFORM_PROGRAM)
|
|
63
|
-
transform_program[1].kwargs["grouping_strategy"] =
|
|
134
|
+
transform_program[1].kwargs["grouping_strategy"] = (
|
|
135
|
+
None if grouping_strategy == "_backend_expval" else grouping_strategy
|
|
136
|
+
)
|
|
137
|
+
# Run the transform on the lightweight tape
|
|
138
|
+
qscripts, self.postprocessing_fn = transform_program((measurements_only_tape,))
|
|
64
139
|
|
|
65
|
-
|
|
140
|
+
if grouping_strategy == "_backend_expval":
|
|
141
|
+
wrapped_measurements_groups = [[]]
|
|
142
|
+
else:
|
|
143
|
+
wrapped_measurements_groups = [qsc.measurements for qsc in qscripts]
|
|
66
144
|
|
|
67
145
|
self.compiled_circuits_bodies, self.measurements = to_openqasm(
|
|
68
146
|
main_circuit,
|
|
69
|
-
measurement_groups=
|
|
147
|
+
measurement_groups=wrapped_measurements_groups,
|
|
70
148
|
return_measurements_separately=True,
|
|
71
149
|
# TODO: optimize later
|
|
72
150
|
measure_all=True,
|
|
@@ -81,11 +159,30 @@ class MetaCircuit:
|
|
|
81
159
|
]
|
|
82
160
|
|
|
83
161
|
def __getstate__(self):
|
|
162
|
+
"""
|
|
163
|
+
Prepare the MetaCircuit for pickling.
|
|
164
|
+
|
|
165
|
+
Serializes the postprocessing function using dill since regular pickle
|
|
166
|
+
cannot handle certain PennyLane function objects.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
dict: State dictionary with serialized postprocessing function.
|
|
170
|
+
"""
|
|
84
171
|
state = self.__dict__.copy()
|
|
85
172
|
state["postprocessing_fn"] = dill.dumps(self.postprocessing_fn)
|
|
86
173
|
return state
|
|
87
174
|
|
|
88
175
|
def __setstate__(self, state):
|
|
176
|
+
"""
|
|
177
|
+
Restore the MetaCircuit from a pickled state.
|
|
178
|
+
|
|
179
|
+
Deserializes the postprocessing function that was serialized with dill
|
|
180
|
+
during pickling.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
state (dict): State dictionary from pickling with serialized
|
|
184
|
+
postprocessing function.
|
|
185
|
+
"""
|
|
89
186
|
state["postprocessing_fn"] = dill.loads(state["postprocessing_fn"])
|
|
90
187
|
|
|
91
188
|
self.__dict__.update(state)
|
|
@@ -93,6 +190,29 @@ class MetaCircuit:
|
|
|
93
190
|
def initialize_circuit_from_params(
|
|
94
191
|
self, param_list, tag_prefix: str = "", precision: int = 8
|
|
95
192
|
) -> Circuit:
|
|
193
|
+
"""
|
|
194
|
+
Instantiate a concrete Circuit by substituting symbolic parameters with values.
|
|
195
|
+
|
|
196
|
+
Takes a list of parameter values and creates a fully instantiated Circuit
|
|
197
|
+
by replacing all symbolic parameters in the QASM representations with their
|
|
198
|
+
concrete numerical values.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
param_list: Array of numerical parameter values to substitute for symbols.
|
|
202
|
+
Must match the length and order of self.symbols.
|
|
203
|
+
tag_prefix (str, optional): Prefix to prepend to circuit tags for
|
|
204
|
+
identification. Defaults to "".
|
|
205
|
+
precision (int, optional): Number of decimal places for parameter values
|
|
206
|
+
in the QASM output. Defaults to 8.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Circuit: A new Circuit instance with parameters substituted and proper
|
|
210
|
+
tags for identification.
|
|
211
|
+
|
|
212
|
+
Note:
|
|
213
|
+
The main_circuit attribute in the returned Circuit still contains
|
|
214
|
+
symbolic parameters. Only the QASM representations have concrete values.
|
|
215
|
+
"""
|
|
96
216
|
mapping = dict(
|
|
97
217
|
zip(
|
|
98
218
|
map(lambda x: re.escape(str(x)), self.symbols),
|
divi/circuits/qasm.py
CHANGED
|
@@ -46,6 +46,25 @@ OPENQASM_GATES = {
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def _ops_to_qasm(operations, precision, wires):
|
|
49
|
+
"""
|
|
50
|
+
Convert PennyLane operations to OpenQASM instruction strings.
|
|
51
|
+
|
|
52
|
+
Translates a sequence of PennyLane quantum operations into their OpenQASM
|
|
53
|
+
2.0 equivalent representations. Each operation is mapped to its corresponding
|
|
54
|
+
QASM gate with appropriate parameters and wire labels.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
operations: Sequence of PennyLane operation objects to convert.
|
|
58
|
+
precision (int | None): Number of decimal places for parameter values.
|
|
59
|
+
If None, uses default Python string formatting.
|
|
60
|
+
wires: Wire labels used in the circuit for indexing.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
str: OpenQASM instruction string with each operation on a new line.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
ValueError: If an operation is not supported by the QASM serializer.
|
|
67
|
+
"""
|
|
49
68
|
# create the QASM code representing the operations
|
|
50
69
|
qasm_str = ""
|
|
51
70
|
|
|
@@ -196,9 +215,7 @@ def to_openqasm(
|
|
|
196
215
|
for wire in range(len(wires)):
|
|
197
216
|
measure_qasm_str += f"measure q[{wire}] -> c[{wire}];\n"
|
|
198
217
|
else:
|
|
199
|
-
measured_wires = Wires.all_wires(
|
|
200
|
-
[m.wires for m in main_qscript.measurements]
|
|
201
|
-
)
|
|
218
|
+
measured_wires = Wires.all_wires([m.wires for m in meas_group])
|
|
202
219
|
|
|
203
220
|
for w in measured_wires:
|
|
204
221
|
wire_indx = main_qscript.wires.index(w)
|
divi/extern/cirq/_validator.py
CHANGED
|
@@ -639,9 +639,18 @@ def validate_qasm_raise(src: str) -> None:
|
|
|
639
639
|
Parser(toks).parse()
|
|
640
640
|
|
|
641
641
|
|
|
642
|
-
def
|
|
642
|
+
def validate_qasm_count_qubits(src: str) -> int:
|
|
643
|
+
"""Validate QASM and return the total number of qubits."""
|
|
644
|
+
toks = _lex(src)
|
|
645
|
+
parser = Parser(toks)
|
|
646
|
+
parser.parse()
|
|
647
|
+
# Sum all qubit register sizes to get total qubit count
|
|
648
|
+
return sum(parser.qregs.values())
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def is_valid_qasm(src: str) -> int | str:
|
|
652
|
+
"""Validate QASM and return the number of qubits if valid, or error message if invalid."""
|
|
643
653
|
try:
|
|
644
|
-
|
|
645
|
-
return True
|
|
654
|
+
return validate_qasm_count_qubits(src)
|
|
646
655
|
except SyntaxError as e:
|
|
647
656
|
return str(e)
|
divi/qprog/__init__.py
CHANGED
|
@@ -11,7 +11,7 @@ import pennylane as qml
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class Ansatz(ABC):
|
|
14
|
-
"""Abstract base class for all VQE
|
|
14
|
+
"""Abstract base class for all VQE ansätze."""
|
|
15
15
|
|
|
16
16
|
@property
|
|
17
17
|
def name(self) -> str:
|
|
@@ -25,15 +25,17 @@ class Ansatz(ABC):
|
|
|
25
25
|
raise NotImplementedError
|
|
26
26
|
|
|
27
27
|
@abstractmethod
|
|
28
|
-
def build(self, params, n_qubits: int, n_layers: int, **kwargs):
|
|
28
|
+
def build(self, params, n_qubits: int, n_layers: int, **kwargs) -> None:
|
|
29
29
|
"""
|
|
30
|
-
Builds the ansatz circuit
|
|
30
|
+
Builds the ansatz circuit by adding operations to the active PennyLane
|
|
31
|
+
quantum function context.
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
Note: This method is called within a PennyLane quantum function context
|
|
34
|
+
(qml.qnode or qml.tape.make_qscript). Operations are added via side effects
|
|
35
|
+
to the active quantum tape/script.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
None: Operations are added to the active PennyLane context
|
|
37
39
|
"""
|
|
38
40
|
raise NotImplementedError
|
|
39
41
|
|
|
@@ -118,7 +120,7 @@ class GenericLayerAnsatz(Ansatz):
|
|
|
118
120
|
per_qubit = sum(getattr(g, "num_params", 1) for g in self.gate_sequence)
|
|
119
121
|
return per_qubit * n_qubits
|
|
120
122
|
|
|
121
|
-
def build(self, params, n_qubits: int, n_layers: int, **kwargs):
|
|
123
|
+
def build(self, params, n_qubits: int, n_layers: int, **kwargs) -> None:
|
|
122
124
|
# calculate how many params each gate needs per qubit
|
|
123
125
|
gate_param_counts = [getattr(g, "num_params", 1) for g in self.gate_sequence]
|
|
124
126
|
per_qubit = sum(gate_param_counts)
|
|
@@ -143,11 +145,37 @@ class GenericLayerAnsatz(Ansatz):
|
|
|
143
145
|
|
|
144
146
|
|
|
145
147
|
class QAOAAnsatz(Ansatz):
|
|
148
|
+
"""
|
|
149
|
+
QAOA-style ansatz using PennyLane's QAOAEmbedding.
|
|
150
|
+
|
|
151
|
+
Implements a parameterized ansatz based on the Quantum Approximate Optimization
|
|
152
|
+
Algorithm structure, alternating between problem and mixer Hamiltonians.
|
|
153
|
+
"""
|
|
154
|
+
|
|
146
155
|
@staticmethod
|
|
147
156
|
def n_params_per_layer(n_qubits: int, **kwargs) -> int:
|
|
157
|
+
"""
|
|
158
|
+
Calculate the number of parameters per layer for QAOA ansatz.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
n_qubits (int): Number of qubits in the circuit.
|
|
162
|
+
**kwargs: Additional unused arguments.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
int: Number of parameters needed per layer.
|
|
166
|
+
"""
|
|
148
167
|
return qml.QAOAEmbedding.shape(n_layers=1, n_wires=n_qubits)[1]
|
|
149
168
|
|
|
150
|
-
def build(self, params, n_qubits: int, n_layers: int, **kwargs):
|
|
169
|
+
def build(self, params, n_qubits: int, n_layers: int, **kwargs) -> None:
|
|
170
|
+
"""
|
|
171
|
+
Build the QAOA ansatz circuit.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
params: Parameter array to use for the ansatz.
|
|
175
|
+
n_qubits (int): Number of qubits.
|
|
176
|
+
n_layers (int): Number of QAOA layers.
|
|
177
|
+
**kwargs: Additional unused arguments.
|
|
178
|
+
"""
|
|
151
179
|
qml.QAOAEmbedding(
|
|
152
180
|
features=[],
|
|
153
181
|
weights=params.reshape(n_layers, -1),
|
|
@@ -156,11 +184,23 @@ class QAOAAnsatz(Ansatz):
|
|
|
156
184
|
|
|
157
185
|
|
|
158
186
|
class HardwareEfficientAnsatz(Ansatz):
|
|
187
|
+
"""
|
|
188
|
+
Hardware-efficient ansatz (not yet implemented).
|
|
189
|
+
|
|
190
|
+
This ansatz is designed to be easily implementable on near-term quantum hardware,
|
|
191
|
+
typically using native gate sets and connectivity patterns.
|
|
192
|
+
|
|
193
|
+
Note:
|
|
194
|
+
This class is a placeholder for future implementation.
|
|
195
|
+
"""
|
|
196
|
+
|
|
159
197
|
@staticmethod
|
|
160
198
|
def n_params_per_layer(n_qubits: int, **kwargs) -> int:
|
|
199
|
+
"""Not yet implemented."""
|
|
161
200
|
raise NotImplementedError("HardwareEfficientAnsatz is not yet implemented.")
|
|
162
201
|
|
|
163
202
|
def build(self, params, n_qubits: int, n_layers: int, **kwargs) -> None:
|
|
203
|
+
"""Not yet implemented."""
|
|
164
204
|
raise NotImplementedError("HardwareEfficientAnsatz is not yet implemented.")
|
|
165
205
|
|
|
166
206
|
|
|
@@ -168,13 +208,43 @@ class HardwareEfficientAnsatz(Ansatz):
|
|
|
168
208
|
|
|
169
209
|
|
|
170
210
|
class UCCSDAnsatz(Ansatz):
|
|
211
|
+
"""
|
|
212
|
+
Unitary Coupled Cluster Singles and Doubles (UCCSD) ansatz.
|
|
213
|
+
|
|
214
|
+
This ansatz is specifically designed for quantum chemistry calculations,
|
|
215
|
+
implementing the UCCSD approximation which includes all single and double
|
|
216
|
+
electron excitations from a reference state.
|
|
217
|
+
"""
|
|
218
|
+
|
|
171
219
|
@staticmethod
|
|
172
220
|
def n_params_per_layer(n_qubits: int, n_electrons: int, **kwargs) -> int:
|
|
221
|
+
"""
|
|
222
|
+
Calculate the number of parameters per layer for UCCSD ansatz.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
n_qubits (int): Number of qubits in the circuit.
|
|
226
|
+
n_electrons (int): Number of electrons in the system.
|
|
227
|
+
**kwargs: Additional unused arguments.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
int: Number of parameters (number of single + double excitations).
|
|
231
|
+
"""
|
|
173
232
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
174
233
|
s_wires, d_wires = qml.qchem.excitations_to_wires(singles, doubles)
|
|
175
234
|
return len(s_wires) + len(d_wires)
|
|
176
235
|
|
|
177
|
-
def build(self, params, n_qubits: int, n_layers: int,
|
|
236
|
+
def build(self, params, n_qubits: int, n_layers: int, **kwargs) -> None:
|
|
237
|
+
"""
|
|
238
|
+
Build the UCCSD ansatz circuit.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
params: Parameter array for excitation amplitudes.
|
|
242
|
+
n_qubits (int): Number of qubits.
|
|
243
|
+
n_layers (int): Number of UCCSD layers (repeats).
|
|
244
|
+
**kwargs: Additional arguments:
|
|
245
|
+
n_electrons (int): Number of electrons in the system (required).
|
|
246
|
+
"""
|
|
247
|
+
n_electrons = kwargs.pop("n_electrons")
|
|
178
248
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
179
249
|
s_wires, d_wires = qml.qchem.excitations_to_wires(singles, doubles)
|
|
180
250
|
hf_state = qml.qchem.hf_state(n_electrons, n_qubits)
|
|
@@ -190,12 +260,42 @@ class UCCSDAnsatz(Ansatz):
|
|
|
190
260
|
|
|
191
261
|
|
|
192
262
|
class HartreeFockAnsatz(Ansatz):
|
|
263
|
+
"""
|
|
264
|
+
Hartree-Fock-based ansatz for quantum chemistry.
|
|
265
|
+
|
|
266
|
+
This ansatz prepares the Hartree-Fock reference state and applies
|
|
267
|
+
parameterized single and double excitation gates. It's a simplified
|
|
268
|
+
alternative to UCCSD, often used as a starting point for VQE calculations.
|
|
269
|
+
"""
|
|
270
|
+
|
|
193
271
|
@staticmethod
|
|
194
272
|
def n_params_per_layer(n_qubits: int, n_electrons: int, **kwargs) -> int:
|
|
273
|
+
"""
|
|
274
|
+
Calculate the number of parameters per layer for Hartree-Fock ansatz.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
n_qubits (int): Number of qubits in the circuit.
|
|
278
|
+
n_electrons (int): Number of electrons in the system.
|
|
279
|
+
**kwargs: Additional unused arguments.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
int: Number of parameters (number of single + double excitations).
|
|
283
|
+
"""
|
|
195
284
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
196
285
|
return len(singles) + len(doubles)
|
|
197
286
|
|
|
198
|
-
def build(self, params, n_qubits: int, n_layers: int,
|
|
287
|
+
def build(self, params, n_qubits: int, n_layers: int, **kwargs) -> None:
|
|
288
|
+
"""
|
|
289
|
+
Build the Hartree-Fock ansatz circuit.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
params: Parameter array for excitation amplitudes.
|
|
293
|
+
n_qubits (int): Number of qubits.
|
|
294
|
+
n_layers (int): Number of ansatz layers.
|
|
295
|
+
**kwargs: Additional arguments:
|
|
296
|
+
n_electrons (int): Number of electrons in the system (required).
|
|
297
|
+
"""
|
|
298
|
+
n_electrons = kwargs.pop("n_electrons")
|
|
199
299
|
singles, doubles = qml.qchem.excitations(n_electrons, n_qubits)
|
|
200
300
|
hf_state = qml.qchem.hf_state(n_electrons, n_qubits)
|
|
201
301
|
|