graphqomb 0.1.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.
- graphqomb/__init__.py +1 -0
- graphqomb/circuit.py +271 -0
- graphqomb/command.py +110 -0
- graphqomb/common.py +399 -0
- graphqomb/euler.py +318 -0
- graphqomb/feedforward.py +149 -0
- graphqomb/focus_flow.py +201 -0
- graphqomb/gates.py +1272 -0
- graphqomb/graphstate.py +805 -0
- graphqomb/matrix.py +52 -0
- graphqomb/pattern.py +263 -0
- graphqomb/pauli_frame.py +129 -0
- graphqomb/qompiler.py +131 -0
- graphqomb/random_objects.py +82 -0
- graphqomb/rng.py +40 -0
- graphqomb/schedule_solver.py +230 -0
- graphqomb/scheduler.py +323 -0
- graphqomb/simulator.py +230 -0
- graphqomb/simulator_backend.py +275 -0
- graphqomb/statevec.py +296 -0
- graphqomb/visualizer.py +567 -0
- graphqomb-0.1.0.dist-info/METADATA +258 -0
- graphqomb-0.1.0.dist-info/RECORD +26 -0
- graphqomb-0.1.0.dist-info/WHEEL +5 -0
- graphqomb-0.1.0.dist-info/licenses/LICENSE +21 -0
- graphqomb-0.1.0.dist-info/top_level.txt +1 -0
graphqomb/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""GraphQOMB library."""
|
graphqomb/circuit.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Circuit classes for encoding quantum operations.
|
|
2
|
+
|
|
3
|
+
This module provides:
|
|
4
|
+
|
|
5
|
+
- `BaseCircuit`: An abstract base class for quantum circuits.
|
|
6
|
+
- `MBQCCircuit`: A circuit class composed solely of a unit gate set.
|
|
7
|
+
- `Circuit`: A class for circuits that include macro instructions.
|
|
8
|
+
- `circuit2graph`: A function that converts a circuit to a graph state and gflow.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import copy
|
|
14
|
+
import itertools
|
|
15
|
+
from abc import ABC, abstractmethod
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
import typing_extensions
|
|
19
|
+
|
|
20
|
+
from graphqomb.common import Plane, PlannerMeasBasis
|
|
21
|
+
from graphqomb.gates import CZ, Gate, J, PhaseGadget, UnitGate
|
|
22
|
+
from graphqomb.graphstate import GraphState
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from collections.abc import Sequence
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BaseCircuit(ABC):
|
|
29
|
+
"""
|
|
30
|
+
Abstract base class for quantum circuits.
|
|
31
|
+
|
|
32
|
+
This class defines the interface for quantum circuit objects.
|
|
33
|
+
It enforces implementation of core methods that must be present
|
|
34
|
+
in any subclass representing a specific type of quantum circuit.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def num_qubits(self) -> int:
|
|
40
|
+
"""Get the number of qubits in the circuit.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
`int`
|
|
45
|
+
The number of qubits in the circuit
|
|
46
|
+
"""
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def instructions(self) -> list[Gate]:
|
|
51
|
+
r"""Get the list of gate instructions in the circuit.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
`list`\[`Gate`\]
|
|
56
|
+
List of gate instructions in the circuit.
|
|
57
|
+
"""
|
|
58
|
+
raise NotImplementedError
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def unit_instructions(self) -> list[UnitGate]:
|
|
62
|
+
r"""Get the list of unit gate instructions in the circuit.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
`list`\[`UnitGate`\]
|
|
67
|
+
List of unit gate instructions in the circuit.
|
|
68
|
+
"""
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class MBQCCircuit(BaseCircuit):
|
|
73
|
+
"""A circuit class composed solely of a unit gate set."""
|
|
74
|
+
|
|
75
|
+
__num_qubits: int
|
|
76
|
+
__gate_instructions: list[UnitGate]
|
|
77
|
+
|
|
78
|
+
def __init__(self, num_qubits: int) -> None:
|
|
79
|
+
self.__num_qubits = num_qubits
|
|
80
|
+
self.__gate_instructions = []
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
@typing_extensions.override
|
|
84
|
+
def num_qubits(self) -> int:
|
|
85
|
+
"""Get the number of qubits in the circuit.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
`int`
|
|
90
|
+
The number of qubits in the circuit.
|
|
91
|
+
"""
|
|
92
|
+
return self.__num_qubits
|
|
93
|
+
|
|
94
|
+
@typing_extensions.override
|
|
95
|
+
def instructions(self) -> list[Gate]:
|
|
96
|
+
r"""Get the list of gate instructions in the circuit.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
`list`\[`Gate`\]
|
|
101
|
+
List of gate instructions in the circuit.
|
|
102
|
+
"""
|
|
103
|
+
# For MBQCCircuit, Gate and UnitGate are the same
|
|
104
|
+
return [copy.deepcopy(gate) for gate in self.__gate_instructions]
|
|
105
|
+
|
|
106
|
+
@typing_extensions.override
|
|
107
|
+
def unit_instructions(self) -> list[UnitGate]:
|
|
108
|
+
r"""Get the list of unit gate instructions in the circuit.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
`list`\[`UnitGate`\]
|
|
113
|
+
List of unit gate instructions in the circuit.
|
|
114
|
+
"""
|
|
115
|
+
return [copy.deepcopy(gate) for gate in self.__gate_instructions]
|
|
116
|
+
|
|
117
|
+
def j(self, qubit: int, angle: float) -> None:
|
|
118
|
+
"""Add a J gate to the circuit.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
qubit : `int`
|
|
123
|
+
The qubit index.
|
|
124
|
+
angle : `float`
|
|
125
|
+
The angle of the J gate.
|
|
126
|
+
"""
|
|
127
|
+
self.__gate_instructions.append(J(qubit=qubit, angle=angle))
|
|
128
|
+
|
|
129
|
+
def cz(self, qubit1: int, qubit2: int) -> None:
|
|
130
|
+
"""Add a CZ gate to the circuit.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
qubit1 : `int`
|
|
135
|
+
The first qubit index.
|
|
136
|
+
qubit2 : `int`
|
|
137
|
+
The second qubit index.
|
|
138
|
+
"""
|
|
139
|
+
self.__gate_instructions.append(CZ(qubits=(qubit1, qubit2)))
|
|
140
|
+
|
|
141
|
+
def phase_gadget(self, qubits: Sequence[int], angle: float) -> None:
|
|
142
|
+
r"""Add a phase gadget to the circuit.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
qubits : `collections.abc.Sequence`\[`int`\]
|
|
147
|
+
The qubit indices.
|
|
148
|
+
angle : `float`
|
|
149
|
+
The angle of the phase gadget
|
|
150
|
+
"""
|
|
151
|
+
self.__gate_instructions.append(PhaseGadget(qubits=list(qubits), angle=angle))
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Circuit(BaseCircuit):
|
|
155
|
+
"""A class for circuits that include macro instructions."""
|
|
156
|
+
|
|
157
|
+
__num_qubits: int
|
|
158
|
+
__macro_gate_instructions: list[Gate]
|
|
159
|
+
|
|
160
|
+
def __init__(self, num_qubits: int) -> None:
|
|
161
|
+
self.__num_qubits = num_qubits
|
|
162
|
+
self.__macro_gate_instructions = []
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
@typing_extensions.override
|
|
166
|
+
def num_qubits(self) -> int:
|
|
167
|
+
"""Get the number of qubits in the circuit.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
`int`
|
|
172
|
+
The number of qubits in the circuit.
|
|
173
|
+
"""
|
|
174
|
+
return self.__num_qubits
|
|
175
|
+
|
|
176
|
+
@typing_extensions.override
|
|
177
|
+
def instructions(self) -> list[Gate]:
|
|
178
|
+
r"""Get the list of gate instructions in the circuit.
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
`list`\[`Gate`\]
|
|
183
|
+
List of gate instructions in the circuit.
|
|
184
|
+
"""
|
|
185
|
+
return [copy.deepcopy(gate) for gate in self.__macro_gate_instructions]
|
|
186
|
+
|
|
187
|
+
@typing_extensions.override
|
|
188
|
+
def unit_instructions(self) -> list[UnitGate]:
|
|
189
|
+
r"""Get the list of unit gate instructions in the circuit.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
`list`\[`UnitGate`\]
|
|
194
|
+
The list of unit gate instructions in the circuit.
|
|
195
|
+
"""
|
|
196
|
+
return list(
|
|
197
|
+
itertools.chain.from_iterable(macro_gate.unit_gates() for macro_gate in self.__macro_gate_instructions)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def apply_macro_gate(self, gate: Gate) -> None:
|
|
201
|
+
"""Apply a macro gate to the circuit.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
gate : `Gate`
|
|
206
|
+
The macro gate to apply.
|
|
207
|
+
"""
|
|
208
|
+
self.__macro_gate_instructions.append(gate)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def circuit2graph(circuit: BaseCircuit) -> tuple[GraphState, dict[int, set[int]]]:
|
|
212
|
+
r"""Convert a circuit to a graph state and gflow.
|
|
213
|
+
|
|
214
|
+
Parameters
|
|
215
|
+
----------
|
|
216
|
+
circuit : `BaseCircuit`
|
|
217
|
+
The quantum circuit to convert.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
`tuple`\[`GraphState`, `dict`\[`int`, `set`\[`int`\]\]\]
|
|
222
|
+
The graph state and gflow converted from the circuit.
|
|
223
|
+
|
|
224
|
+
Raises
|
|
225
|
+
------
|
|
226
|
+
TypeError
|
|
227
|
+
If the circuit contains an invalid instruction.
|
|
228
|
+
"""
|
|
229
|
+
graph = GraphState()
|
|
230
|
+
gflow: dict[int, set[int]] = {}
|
|
231
|
+
|
|
232
|
+
qindex2front_nodes: dict[int, int] = {}
|
|
233
|
+
|
|
234
|
+
# input nodes
|
|
235
|
+
for i in range(circuit.num_qubits):
|
|
236
|
+
node = graph.add_physical_node()
|
|
237
|
+
graph.register_input(node, i)
|
|
238
|
+
qindex2front_nodes[i] = node
|
|
239
|
+
|
|
240
|
+
for instruction in circuit.unit_instructions():
|
|
241
|
+
if isinstance(instruction, J):
|
|
242
|
+
new_node = graph.add_physical_node()
|
|
243
|
+
graph.add_physical_edge(qindex2front_nodes[instruction.qubit], new_node)
|
|
244
|
+
graph.assign_meas_basis(
|
|
245
|
+
qindex2front_nodes[instruction.qubit],
|
|
246
|
+
PlannerMeasBasis(Plane.XY, -instruction.angle),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
gflow[qindex2front_nodes[instruction.qubit]] = {new_node}
|
|
250
|
+
qindex2front_nodes[instruction.qubit] = new_node
|
|
251
|
+
|
|
252
|
+
elif isinstance(instruction, CZ):
|
|
253
|
+
graph.add_physical_edge(
|
|
254
|
+
qindex2front_nodes[instruction.qubits[0]],
|
|
255
|
+
qindex2front_nodes[instruction.qubits[1]],
|
|
256
|
+
)
|
|
257
|
+
elif isinstance(instruction, PhaseGadget):
|
|
258
|
+
new_node = graph.add_physical_node()
|
|
259
|
+
graph.assign_meas_basis(new_node, PlannerMeasBasis(Plane.YZ, instruction.angle))
|
|
260
|
+
for qubit in instruction.qubits:
|
|
261
|
+
graph.add_physical_edge(qindex2front_nodes[qubit], new_node)
|
|
262
|
+
|
|
263
|
+
gflow[new_node] = {new_node}
|
|
264
|
+
else:
|
|
265
|
+
msg = f"Invalid instruction: {instruction}"
|
|
266
|
+
raise TypeError(msg)
|
|
267
|
+
|
|
268
|
+
for qindex, node in qindex2front_nodes.items():
|
|
269
|
+
graph.register_output(node, qindex)
|
|
270
|
+
|
|
271
|
+
return graph, gflow
|
graphqomb/command.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Command module for measurement pattern.
|
|
2
|
+
|
|
3
|
+
This module provides:
|
|
4
|
+
|
|
5
|
+
- `N`: Preparation command.
|
|
6
|
+
- `E`: Entanglement command.
|
|
7
|
+
- `M`: Measurement command.
|
|
8
|
+
- `X`: X correction command.
|
|
9
|
+
- `Z`: Z correction command.
|
|
10
|
+
- `Command`: Type alias of all commands.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import dataclasses
|
|
16
|
+
import sys
|
|
17
|
+
from typing import TYPE_CHECKING, Union
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from graphqomb.common import MeasBasis
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclasses.dataclass
|
|
24
|
+
class N:
|
|
25
|
+
"""Preparation command.
|
|
26
|
+
|
|
27
|
+
Attributes
|
|
28
|
+
----------
|
|
29
|
+
node : `int`
|
|
30
|
+
The node index to be prepared.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
node: int
|
|
34
|
+
|
|
35
|
+
def __str__(self) -> str:
|
|
36
|
+
return f"N: node={self.node}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclasses.dataclass
|
|
40
|
+
class E:
|
|
41
|
+
r"""Entanglement command.
|
|
42
|
+
|
|
43
|
+
Attributes
|
|
44
|
+
----------
|
|
45
|
+
nodes : `tuple`\[`int`, `int`\]
|
|
46
|
+
The node indices to be entangled.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
nodes: tuple[int, int]
|
|
50
|
+
|
|
51
|
+
def __str__(self) -> str:
|
|
52
|
+
return f"E: nodes={self.nodes}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclasses.dataclass
|
|
56
|
+
class M:
|
|
57
|
+
"""Measurement command.
|
|
58
|
+
|
|
59
|
+
Attributes
|
|
60
|
+
----------
|
|
61
|
+
node : `int`
|
|
62
|
+
The node index to be measured.
|
|
63
|
+
meas_basis : MeasBasis
|
|
64
|
+
The measurement basis.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
node: int
|
|
68
|
+
meas_basis: MeasBasis
|
|
69
|
+
|
|
70
|
+
def __str__(self) -> str:
|
|
71
|
+
return f"M: node={self.node}, plane={self.meas_basis.plane}, angle={self.meas_basis.angle}"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclasses.dataclass
|
|
75
|
+
class _Correction:
|
|
76
|
+
node: int
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclasses.dataclass
|
|
80
|
+
class X(_Correction):
|
|
81
|
+
"""X correction command.
|
|
82
|
+
|
|
83
|
+
Attributes
|
|
84
|
+
----------
|
|
85
|
+
node : `int`
|
|
86
|
+
The node index to apply the correction.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __str__(self) -> str:
|
|
90
|
+
return f"X: node={self.node}"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclasses.dataclass
|
|
94
|
+
class Z(_Correction):
|
|
95
|
+
"""Z correction command.
|
|
96
|
+
|
|
97
|
+
Attributes
|
|
98
|
+
----------
|
|
99
|
+
node : `int`
|
|
100
|
+
The node index to apply the correction.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __str__(self) -> str:
|
|
104
|
+
return f"Z: node={self.node}"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if sys.version_info >= (3, 10):
|
|
108
|
+
Command = N | E | M | X | Z
|
|
109
|
+
else:
|
|
110
|
+
Command = Union[N, E, M, X, Z]
|