quantic-rust 0.1.0__cp313-cp313-macosx_10_12_x86_64.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.
- quantic_rust/__init__.py +218 -0
- quantic_rust/quantic_rust.cpython-313-darwin.so +0 -0
- quantic_rust/utils.py +72 -0
- quantic_rust-0.1.0.dist-info/METADATA +1252 -0
- quantic_rust-0.1.0.dist-info/RECORD +7 -0
- quantic_rust-0.1.0.dist-info/WHEEL +4 -0
- quantic_rust-0.1.0.dist-info/licenses/LICENSE.txt +21 -0
quantic_rust/__init__.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantic-Rust: High-Performance Quantum Computing Library
|
|
3
|
+
|
|
4
|
+
A Rust-powered quantum circuit synthesis, optimization, and error correction library.
|
|
5
|
+
|
|
6
|
+
Algorithms are based on papers:
|
|
7
|
+
- "A graph-state based synthesis framework for Clifford isometries"
|
|
8
|
+
by T. Goubault de Brugière, S. Martiel, and C. Vuillot
|
|
9
|
+
- "Faster and shorter synthesis of Hamiltonian simulation circuits"
|
|
10
|
+
by T. Goubault de Brugère, and S. Martiel
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import List, Tuple, Union, Any
|
|
14
|
+
|
|
15
|
+
import networkx as nx
|
|
16
|
+
|
|
17
|
+
from .quantic_rust import (
|
|
18
|
+
Metric,
|
|
19
|
+
graph_state_synthesis as rust_gs,
|
|
20
|
+
stabilizer_state_synthesis as rust_ss,
|
|
21
|
+
pauli_network_synthesis as rust_pauli_network,
|
|
22
|
+
codiagonalization as rust_codiag,
|
|
23
|
+
codiagonalization_sswise as rust_sswise,
|
|
24
|
+
isometry_synthesis as rust_isometry,
|
|
25
|
+
extract_rotations as rust_extract_rotations,
|
|
26
|
+
zhang_rotation_optimization,
|
|
27
|
+
)
|
|
28
|
+
from .utils import convert_circuit
|
|
29
|
+
|
|
30
|
+
__version__ = "0.1.0"
|
|
31
|
+
__all__ = [
|
|
32
|
+
"Metric",
|
|
33
|
+
"pauli_network_synthesis",
|
|
34
|
+
"graph_state_synthesis",
|
|
35
|
+
"stabilizer_state_synthesis",
|
|
36
|
+
"codiagonalization",
|
|
37
|
+
"clifford_isometry_synthesis",
|
|
38
|
+
"clifford_synthesis",
|
|
39
|
+
"extract_rotations",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
_Circuit = List[Tuple[str, List[int]]]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def pauli_network_synthesis(
|
|
46
|
+
paulis: list[str],
|
|
47
|
+
metric: Metric,
|
|
48
|
+
preserve_order: bool = True,
|
|
49
|
+
nshuffles: int = 0,
|
|
50
|
+
skip_sort: bool = False,
|
|
51
|
+
fix_clifford: bool = False,
|
|
52
|
+
check: bool = False,
|
|
53
|
+
) -> _Circuit:
|
|
54
|
+
"""
|
|
55
|
+
Synthesize a circuit implementing a Pauli network for a given set of Pauli operators.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
paulis (list): List of Pauli operators (as strings).
|
|
59
|
+
metric (:class:`.Metric`): The metric to minimize.
|
|
60
|
+
preserve_order (optional, bool): Whether to preserve the order of the Pauli operators.
|
|
61
|
+
Default is True.
|
|
62
|
+
nshuffles (optional, int): Number of qbit ordering shuffles to perform. Default is 0.
|
|
63
|
+
skip_sort (optional, bool): Whether to skip sorting Pauli operators by hamming weight.
|
|
64
|
+
If set to True, the algorithm might fail to converge (use with caution). Default is False.
|
|
65
|
+
fix_clifford (optional, bool): Whether to fix the final Clifford operator. Default is False.
|
|
66
|
+
check (optional, bool): Whether to check the validity of the circuit. Default is False.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
list: The synthesized circuit.
|
|
70
|
+
"""
|
|
71
|
+
if paulis:
|
|
72
|
+
return rust_pauli_network(
|
|
73
|
+
paulis, metric, preserve_order, check, nshuffles, skip_sort, fix_clifford
|
|
74
|
+
)
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def graph_state_synthesis(
|
|
79
|
+
graph: nx.Graph, metric: Metric, syndrome_iter: int = 1
|
|
80
|
+
) -> _Circuit:
|
|
81
|
+
"""
|
|
82
|
+
Synthesize a circuit preparing a given graph state specified by a networkx graph.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
graph (:class:`network.Graph`): A graph (networkx object).
|
|
86
|
+
metric (:class:`.Metric`): The metric to minimize.
|
|
87
|
+
syndrome_iter (optional, int): The number of syndrome decoding iteration used
|
|
88
|
+
in the ISD solver (for `Metric.COUNT` only). Default is 1.
|
|
89
|
+
Returns:
|
|
90
|
+
list: The synthesized circuit.
|
|
91
|
+
"""
|
|
92
|
+
syndrome_iter = syndrome_iter or 1
|
|
93
|
+
adj = nx.to_numpy_array(graph) > 0.5
|
|
94
|
+
return rust_gs(adj.tolist(), metric, syndrome_iter)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def stabilizer_state_synthesis(
|
|
98
|
+
stabilizers: list[str], metric: Metric, syndrome_iter: int = 1
|
|
99
|
+
) -> _Circuit:
|
|
100
|
+
"""
|
|
101
|
+
Synthesize a circuit preparing a given graph state specified by a networkx graph.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
stabilizers (list): A list of strings representing the stabilizers.
|
|
105
|
+
metric (:class:`.Metric`): The metric to minimize.
|
|
106
|
+
syndrome_iter (optional, int): The number of syndrome decoding iteration used
|
|
107
|
+
in the ISD solver (for `Metric.COUNT` only). Default is 1.
|
|
108
|
+
Returns:
|
|
109
|
+
list: The synthesized circuit.
|
|
110
|
+
"""
|
|
111
|
+
return rust_ss(stabilizers, metric, syndrome_iter)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def codiagonalization(
|
|
115
|
+
paulis: list[str], metric: Metric, syndrome_iter: int = 1
|
|
116
|
+
) -> _Circuit:
|
|
117
|
+
"""
|
|
118
|
+
Synthesize a circuit preparing a codiagonalization Clifford circuit for a given set of
|
|
119
|
+
pairwise commuting Pauli operators.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
paulis (list): List of the Pauli operators to co-diagonalize (as strings).
|
|
123
|
+
metric (:class:`.Metric`): The metric to minimize.
|
|
124
|
+
syndrome_iter (optional, int): The number of syndrome decoding iteration used
|
|
125
|
+
in the ISD solver (for `Metric.COUNT` only). Default is 1.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
list: The synthesized circuit.
|
|
129
|
+
"""
|
|
130
|
+
syndrome_iter = syndrome_iter or 1
|
|
131
|
+
return rust_codiag(paulis, metric, syndrome_iter)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def codiagonalization_sswise(paulis: list[str]) -> _Circuit:
|
|
135
|
+
"""
|
|
136
|
+
Synthesize a circuit preparing a codiagonalization Clifford circuit for a given set of
|
|
137
|
+
pairwise commuting Pauli operators.
|
|
138
|
+
|
|
139
|
+
This is the heuristic of the paper "A Generic Compilation Strategy for the Unitary
|
|
140
|
+
Coupled Cluster Ansatz".
|
|
141
|
+
It is here for comparison purposes with the `codiagonalization` method .
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
paulis (list): List of the Pauli operators to co-diagonalize (as strings).
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
list: The synthesized circuit.
|
|
148
|
+
"""
|
|
149
|
+
return rust_sswise(paulis, 2)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def clifford_isometry_synthesis(
|
|
153
|
+
logicals: list[str], stabilisers: list[str], metric: Metric, syndrome_iter: int = 1
|
|
154
|
+
) -> _Circuit:
|
|
155
|
+
"""
|
|
156
|
+
Synthesize a Clifford circuit implementing a target Clifford isometry specified by
|
|
157
|
+
its logical operators and its stabilizers.
|
|
158
|
+
The tableau is specified by a list of operators (as strings). The first (resp. second) half
|
|
159
|
+
of the list corresponding to the images of `Zi` (resp. `Xi`) operators.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
logicals (list): The logical operators.
|
|
163
|
+
stabilisers (list): The stabilizers.
|
|
164
|
+
metric (:class:`.Metric`): The metric to minimize.
|
|
165
|
+
syndrome_iter (optional, int): The number of syndrome decoding iteration used in the
|
|
166
|
+
ISD solver in the metric=`Metric.COUNT` case only. Default is 1.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
list: The synthesized circuit.
|
|
170
|
+
"""
|
|
171
|
+
syndrome_iter = syndrome_iter or 1
|
|
172
|
+
return rust_isometry(logicals, stabilisers, metric, syndrome_iter)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def clifford_synthesis(
|
|
176
|
+
logicals: list[str], metric: Metric, syndrome_iter: int = 1
|
|
177
|
+
) -> _Circuit:
|
|
178
|
+
"""
|
|
179
|
+
Synthesize a Clifford circuit implementing a target Clifford operator specified by its
|
|
180
|
+
tableau.
|
|
181
|
+
The tableau is specified by a list of operators (as strings). The first (resp. second) half
|
|
182
|
+
of the list corresponding to the images of `Zi` (resp. `Xi`) operators.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
logicals (list): The tableau.
|
|
186
|
+
metric (:class:`.Metric`): The metric to minimize.
|
|
187
|
+
syndrome_iter (optional, int): The number of syndrome decoding iteration used in
|
|
188
|
+
the ISD solver in the metric=`Metric.COUNT` case only. Default is 1.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
list: The synthesized circuit.
|
|
192
|
+
"""
|
|
193
|
+
syndrome_iter = syndrome_iter or 1
|
|
194
|
+
return rust_isometry(logicals, [], metric, syndrome_iter)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def extract_rotations(
|
|
198
|
+
circuit: List[
|
|
199
|
+
Union[Tuple[str, List[int]], Tuple[str, List[int], Any]]
|
|
200
|
+
],
|
|
201
|
+
optimize: bool = True,
|
|
202
|
+
):
|
|
203
|
+
"""
|
|
204
|
+
Converts a circuit into a sequence of Pauli rotations and a final Clifford operator.
|
|
205
|
+
The circuit is specified as a sequence of tuples:
|
|
206
|
+
- ("CX", [q0, q1]) for a CNOT gate
|
|
207
|
+
- ("H", [q0]) for a Hadamard gate
|
|
208
|
+
- ("S", [q0]) for a S gate
|
|
209
|
+
- ("RZ", [q0], angle) for a RZ gate with angle in radian
|
|
210
|
+
|
|
211
|
+
This gate set is exactly universal for QC.
|
|
212
|
+
"""
|
|
213
|
+
circuit_no_angles, angles = convert_circuit(circuit)
|
|
214
|
+
nqubits = max(max(g[1]) for g in circuit) + 1
|
|
215
|
+
rotations, final_clifford_tableau = rust_extract_rotations(
|
|
216
|
+
circuit_no_angles, [str(a) for a in angles], nqubits, optimize
|
|
217
|
+
)
|
|
218
|
+
return rotations, final_clifford_tableau
|
|
Binary file
|
quantic_rust/utils.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for quantic_rust's circuits
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Tuple
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
_Circuit = List[Tuple[str, List[int]]]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def entangling_depth(circ: _Circuit) -> int:
|
|
13
|
+
"""
|
|
14
|
+
Computes the entangling depth of a circuit.
|
|
15
|
+
Only checks the number of qubits of the gate to decide if it is entangling or not.
|
|
16
|
+
|
|
17
|
+
Arguments:
|
|
18
|
+
circ: A circuit.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
int: the entangling depth
|
|
22
|
+
"""
|
|
23
|
+
depths = {}
|
|
24
|
+
for _, qbits in circ:
|
|
25
|
+
if len(qbits) > 1:
|
|
26
|
+
gate_depth = max(depths.get(qbits[0], 0), depths.get(qbits[1], 0)) + 1
|
|
27
|
+
for qbit in qbits:
|
|
28
|
+
depths[qbit] = gate_depth
|
|
29
|
+
return max(depths.values()) if depths else 0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def entangling_count(circ: _Circuit) -> int:
|
|
33
|
+
"""
|
|
34
|
+
Count the number of entangling gates in a circuit.
|
|
35
|
+
Only checks the number of qubits of the gate to decide if it is entangling or not.
|
|
36
|
+
|
|
37
|
+
Arguments:
|
|
38
|
+
circ: A circuit.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
int: the entangling count
|
|
42
|
+
"""
|
|
43
|
+
return sum(len(qbits) == 2 for _, qbits in circ)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def convert_circuit(circuit: list) -> (list, list):
|
|
47
|
+
"""
|
|
48
|
+
Converts a circuit into a CNOT + H + S + RZ(theta) circuit with theta non Clifford (i.e. != kpi/2)
|
|
49
|
+
"""
|
|
50
|
+
new_circuit = []
|
|
51
|
+
angles = []
|
|
52
|
+
for gate in circuit:
|
|
53
|
+
if gate[0] in ["CX", "CZ", "H", "S", "Sd", "X", "Z", "SqrtX", "SqrtXd"]:
|
|
54
|
+
new_circuit.append(gate)
|
|
55
|
+
continue
|
|
56
|
+
assert gate[0] == "RZ"
|
|
57
|
+
if isinstance(gate[2], str):
|
|
58
|
+
new_circuit.append(gate[:2])
|
|
59
|
+
angles.append(gate[2])
|
|
60
|
+
continue
|
|
61
|
+
if np.isclose(0.0, gate[2] % (np.pi / 2)) or np.isclose(
|
|
62
|
+
np.pi / 2, gate[2] % (np.pi / 2)
|
|
63
|
+
):
|
|
64
|
+
mult, offset = np.divmod(gate[2], np.pi / 2)
|
|
65
|
+
if np.isclose(offset, np.pi / 2):
|
|
66
|
+
mult += 1
|
|
67
|
+
for _ in mult:
|
|
68
|
+
new_circuit.append(("S", gate[1]))
|
|
69
|
+
continue
|
|
70
|
+
new_circuit.append(gate[:2])
|
|
71
|
+
angles.append(gate[2])
|
|
72
|
+
return new_circuit, angles
|