Skip to content

Commit bf2b900

Browse files
authored
Merge pull request #25 from quantum-programming/feature/mixed-synthesis
Feature/mixed synthesis
2 parents 927b52d + 2d03933 commit bf2b900

34 files changed

Lines changed: 3326 additions & 308 deletions

.pre-commit-config.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ repos:
1616
args:
1717
# compatibility with black
1818
# E203 whitespace before ':'
19+
# E704 multiple statements on one line (def)
1920
# E741 ambiguous variable name 'I'
2021
# W503 line break before binary operator
2122
- "--max-line-length=88"
22-
- "--ignore=E203, E741, W503"
23+
- "--ignore=E203, E704, E741, W503"
2324
- repo: https://github.com/pre-commit/mirrors-mypy
2425
rev: 'v1.15.0'
2526
hooks:
@@ -31,7 +32,8 @@ repos:
3132
name: pytest
3233
entry: bash -c 'PYTHONPATH=./ .venv/bin/pytest tests'
3334
language: system
34-
types: [python]
35+
pass_filenames: false
36+
always_run: true
3537
- repo: https://github.com/pre-commit/pre-commit-hooks
3638
rev: v4.4.0
3739
hooks:

.vscode/settings.json

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,92 @@
44
},
55
"editor.formatOnSave": true,
66
"cSpell.words": [
7+
"addopts",
8+
"allclose",
79
"Amano",
10+
"capsys",
11+
"choi",
812
"CINV",
9-
"Kazuyuki",
10-
"Kliuchnikov",
11-
"Maslov",
12-
"Mosca",
13-
"Matsumoto",
14-
"Nobuyuki",
15-
"ODGP",
16-
"PYTHONPATH",
17-
"Selinger",
18-
"Shuntaro",
19-
"TCONJ",
20-
"TDGP",
21-
"Vadym",
22-
"Yamamoto",
23-
"Yoshioka",
24-
"addopts",
2513
"cnot",
14+
"cnots",
15+
"cvxpy",
2616
"decomp",
2717
"denomexp",
2818
"dloop",
2919
"domega",
3020
"droottwo",
3121
"dtimeout",
22+
"ECOS",
23+
"eigh",
3224
"eigsy",
25+
"eigval",
3326
"facs",
3427
"figsize",
3528
"floop",
3629
"floorlog",
3730
"floorsqrt",
3831
"ftimeout",
32+
"FWHT",
33+
"gptm",
3934
"gridsynth",
35+
"GUROBI",
4036
"isort",
37+
"Kazuyuki",
38+
"Kliuchnikov",
39+
"kron",
40+
"kwargs",
4141
"limegreen",
42+
"linalg",
43+
"Maslov",
4244
"matplotlib",
45+
"Matsumoto",
46+
"Mosca",
4347
"mpmath",
4448
"mymath",
4549
"mypy",
50+
"ndarray",
4651
"newsynth",
52+
"njit",
53+
"Nobuyuki",
54+
"nonneg",
55+
"numba",
4756
"numpy",
57+
"ODGP",
4858
"orangered",
4959
"prec",
60+
"prefactors",
61+
"probs",
5062
"pygridsynth",
5163
"pypi",
5264
"pytest",
65+
"PYTHONPATH",
5366
"qubit",
5467
"qubits",
68+
"qulacs",
69+
"randn",
5570
"rounddiv",
71+
"scipy",
5672
"selfassociate",
5773
"selfcoprime",
74+
"Selinger",
75+
"semidefinite",
5876
"setuptools",
5977
"showgraph",
78+
"Shuntaro",
79+
"TCONJ",
80+
"TDGP",
6081
"testpaths",
82+
"triu",
6183
"unitaries",
84+
"Vadym",
85+
"vecs",
6286
"venv",
87+
"Weyl",
6388
"workdps",
6489
"xlabel",
90+
"Yamamoto",
6591
"ylabel",
92+
"Yoshioka",
6693
"zomega",
6794
"zroottwo",
6895
],

README.md

Lines changed: 164 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pygridsynth <theta> <epsilon> [options]
6969
- `--dloop`, `-dl`: Maximum number of failed integer factoring attempts allowed during Diophantine equation solving (default: `10`).
7070
- `--floop`, `-fl`: Maximum number of failed integer factoring attempts allowed during the factoring process (default: `10`).
7171
- `--seed`: Random seed for deterministic results.(default: `0`)
72-
- `--verbose`, `-v`: Enables detailed output.
72+
- `--verbose`, `-v`: Verbosity level (0=silent, 1=basic, 2=detailed, 3=debug).(default: `0`)
7373
- `--time`, `-t`: Measures the execution time.
7474
- `--showgraph`, `-g`: Displays the decomposition result as a graph.
7575
- `--up-to-phase`, `-ph`: Approximates up to a phase.
@@ -138,6 +138,157 @@ gates = gridsynth_gates(theta=theta, epsilon=epsilon)
138138
print(gates)
139139
```
140140

141+
### Multi-Qubit Unitary Approximation
142+
143+
`pygridsynth` provides functionality for approximating multi-qubit unitary matrices using the Clifford+T gate set. This is useful for synthesizing quantum circuits that implement arbitrary multi-qubit unitaries.
144+
145+
**Basic usage:**
146+
<!-- multi_qubit_basic -->
147+
```python
148+
import mpmath
149+
150+
from pygridsynth.multi_qubit_unitary_approximation import (
151+
approximate_multi_qubit_unitary,
152+
)
153+
154+
# Define a target unitary matrix (example: 2-qubit identity)
155+
num_qubits = 2
156+
U = mpmath.eye(2**num_qubits) # 4x4 identity matrix
157+
epsilon = "1e-10"
158+
159+
# Approximate the unitary
160+
circuit, U_approx = approximate_multi_qubit_unitary(U, num_qubits, epsilon)
161+
162+
print(f"Circuit length: {len(circuit)}")
163+
print(f"Circuit: {str(circuit)}")
164+
```
165+
166+
**Using with random unitary:**
167+
<!-- multi_qubit_random -->
168+
```python
169+
from pygridsynth.multi_qubit_unitary_approximation import (
170+
approximate_multi_qubit_unitary,
171+
)
172+
from pygridsynth.mymath import random_su
173+
174+
# Generate a random SU(2^n) unitary
175+
num_qubits = 2
176+
U = random_su(num_qubits)
177+
epsilon = "1e-10"
178+
179+
# Approximate with high precision
180+
circuit, U_approx = approximate_multi_qubit_unitary(U, num_qubits, epsilon)
181+
```
182+
183+
**Returning DOmegaMatrix:**
184+
<!-- multi_qubit_domega -->
185+
```python
186+
from pygridsynth.multi_qubit_unitary_approximation import (
187+
approximate_multi_qubit_unitary,
188+
)
189+
from pygridsynth.mymath import random_su
190+
191+
# Generate a random SU(2^n) unitary
192+
num_qubits = 2
193+
U = random_su(num_qubits)
194+
epsilon = "1e-10"
195+
196+
# Return DOmegaMatrix instead of mpmath.matrix for more efficient representation
197+
circuit, U_domega = approximate_multi_qubit_unitary(
198+
U, num_qubits, epsilon, return_domega_matrix=True
199+
)
200+
201+
# Convert to complex matrix if needed
202+
U_complex = U_domega.to_complex_matrix
203+
```
204+
205+
**Parameters:**
206+
207+
- `U`: Target unitary matrix (`mpmath.matrix`)
208+
- `num_qubits`: Number of qubits
209+
- `epsilon`: Error tolerance (can be `str`, `float`, or `mpmath.mpf`)
210+
- `return_domega_matrix`: If `True`, returns `DOmegaMatrix`; if `False`, returns `mpmath.matrix` (default: `False`)
211+
- `scale_epsilon`: Whether to scale epsilon based on the number of qubits (default: `True`)
212+
- `cfg`: Optional `GridsynthConfig` object for advanced configuration
213+
- `**kwargs`: Additional configuration options (ignored if `cfg` is provided)
214+
215+
**Returns:**
216+
217+
A tuple of `(circuit, U_approx)`:
218+
- `circuit`: `QuantumCircuit` object representing the Clifford+T decomposition
219+
- `U_approx`: Approximated unitary matrix (`mpmath.matrix` or `DOmegaMatrix` depending on `return_domega_matrix`)
220+
221+
### Mixed Unitary Synthesis
222+
223+
`pygridsynth` also provides functionality for mixed unitary synthesis, which approximates a target unitary by mixing multiple perturbed unitaries. This is useful for reducing the number of T-gates in quantum circuits.
224+
225+
The library provides two versions: `mixed_synthesis_parallel` (for parallel execution) and `mixed_synthesis_sequential` (for sequential execution).
226+
227+
**Basic usage with mpmath.matrix:**
228+
<!-- mixed_synthesis_sequential -->
229+
```python
230+
from pygridsynth.mixed_synthesis import mixed_synthesis_sequential
231+
from pygridsynth.mymath import diamond_norm_error_from_choi, random_su
232+
233+
# Generate a random SU(2^n) unitary matrix
234+
num_qubits = 2
235+
unitary = random_su(num_qubits)
236+
237+
# Parameters
238+
eps = 1e-4 # Error tolerance
239+
M = 64 # Number of Hermitian operators for perturbation
240+
seed = 123 # Random seed for reproducibility
241+
242+
# Compute mixed synthesis (sequential version)
243+
result = mixed_synthesis_sequential(unitary, num_qubits, eps, M, seed=seed)
244+
245+
if result is not None:
246+
circuit_list, eu_np_list, probs_gptm, u_choi, u_choi_opt = result
247+
print(f"Number of circuits: {len(circuit_list)}")
248+
print(f"Mixing probabilities: {probs_gptm}")
249+
error = diamond_norm_error_from_choi(u_choi, u_choi_opt, eps, mixed_synthesis=True)
250+
print(f"error: {error}")
251+
```
252+
253+
**Using parallel version:**
254+
<!-- mixed_synthesis_parallel -->
255+
```python
256+
import mpmath
257+
258+
from pygridsynth.mixed_synthesis import mixed_synthesis_parallel
259+
260+
# Generate a random SU(2^n) unitary matrix
261+
num_qubits = 2
262+
unitary = mpmath.eye(2**num_qubits)
263+
264+
# Parameters
265+
eps = 1e-4 # Error tolerance
266+
M = 64 # Number of Hermitian operators for perturbation
267+
seed = 123 # Random seed for reproducibility
268+
269+
# For faster computation with multiple cores
270+
result = mixed_synthesis_parallel(unitary, num_qubits, eps, M, seed=seed)
271+
```
272+
273+
**Parameters:**
274+
275+
- `unitary`: Target unitary matrix (`mpmath.matrix` or `numpy.ndarray`)
276+
- `num_qubits`: Number of qubits
277+
- `eps`: Error tolerance parameter
278+
- `M`: Number of Hermitian operators for perturbation
279+
- `seed`: Random seed for reproducibility (default: `123`)
280+
- `dps`: Decimal precision (default: `-1` for auto-calculation)
281+
282+
**Returns:**
283+
284+
A tuple of `(circuit_list, eu_np_list, probs_gptm, u_choi_opt)` or `None` on failure:
285+
- `circuit_list`: List of `QuantumCircuit` objects for perturbed unitaries
286+
- `eu_np_list`: List of approximated unitary matrices (numpy arrays)
287+
- `probs_gptm`: Array of mixing probabilities
288+
- `u_choi_opt`: Optimal mixed Choi matrix
289+
290+
**Note:** The parallel version (`mixed_synthesis_parallel`) uses multiprocessing and may be faster for large `M` values, while the sequential version (`mixed_synthesis_sequential`) is more suitable for debugging or when parallel execution is not desired.
291+
141292

142293
## Contributing
143294

@@ -149,9 +300,15 @@ This project is licensed under the MIT License.
149300

150301
## References
151302

152-
- Brett Giles and Peter Selinger. Remarks on Matsumoto and Amano's normal form for single-qubit Clifford+T operators, 2019.
153-
- Ken Matsumoto and Kazuyuki Amano. Representation of Quantum Circuits with Clifford and π/8 Gates, 2008.
154-
- Neil J. Ross and Peter Selinger. Optimal ancilla-free Clifford+T approximation of z-rotations, 2016.
155-
- Peter Selinger. Efficient Clifford+T approximation of single-qubit operators, 2014.
156-
- Peter Selinger and Neil J. Ross. Exact and approximate synthesis of quantum circuits. https://www.mathstat.dal.ca/~selinger/newsynth/, 2018.
157-
- Vadym Kliuchnikov, Dmitri Maslov, and Michele Mosca. Fast and efficient exact synthesis of single qubit unitaries generated by Clifford and T gates, 2013.
303+
- Vadym Kliuchnikov, Kristin Lauter, Romy Minko, Adam Paetznick, Christophe Petit. "Shorter quantum circuits via single-qubit gate approximation." Quantum 7 (2023): 1208. DOI: 10.22331/q-2023-12-18-1208.
304+
- Peter Selinger. "Efficient Clifford+T approximation of single-qubit operators." Quantum Info. Comput. 15, no. 1-2 (2015): 159-180.
305+
- Neil J. Ross and Peter Selinger. "Optimal ancilla-free Clifford+T approximation of z-rotations." Quantum Info. Comput. 16, no. 11-12 (2016): 901-953.
306+
- Vadym Kliuchnikov, Dmitri Maslov, and Michele Mosca. "Fast and efficient exact synthesis of single-qubit unitaries generated by Clifford and T gates." Quantum Info. Comput. 13, no. 7-8 (2013): 607-630.
307+
- Ken Matsumoto and Kazuyuki Amano. "Representation of Quantum Circuits with Clifford and π/8 Gates." arXiv: Quantum Physics (2008). URL: https://api.semanticscholar.org/CorpusID:17327793.
308+
- Brett Gordon Giles and Peter Selinger. "Remarks on Matsumoto and Amano's normal form for single-qubit Clifford+T operators." ArXiv abs/1312.6584 (2013). URL: https://api.semanticscholar.org/CorpusID:10077777.
309+
- Vivek V. Shende, Igor L. Markov, and Stephen S. Bullock. "Minimal universal two-qubit controlled-NOT-based circuits." Phys. Rev. A 69, no. 6 (2004): 062321. DOI: 10.1103/PhysRevA.69.062321.
310+
- Vivek V. Shende, Igor L. Markov, and Stephen S. Bullock. "Finding Small Two-Qubit Circuits." Proceedings of SPIE - The International Society for Optical Engineering (2004). DOI: 10.1117/12.542381.
311+
- Vivek V. Shende, Igor L. Markov, and Stephen S. Bullock. "Smaller Two-Qubit Circuits for Quantum Communication and Computation." In Proceedings of the Conference on Design, Automation and Test in Europe - Volume 2, p. 20980. IEEE Computer Society, 2004.
312+
- Korbinian Kottmann. "Two-qubit Synthesis." (2025). URL: https://pennylane.ai/compilation/two-qubit-synthesis.
313+
- Anna M. Krol and Zaid Al-Ars. "Beyond quantum Shannon decomposition: Circuit construction for n-qubit gates based on block-ZXZ decomposition." Physical Review Applied 22, no. 3 (2024): 034019. DOI: 10.1103/PhysRevApplied.22.034019.
314+
- Peter Selinger and Neil J. Ross. "Exact and approximate synthesis of quantum circuits." (2018). URL: https://www.mathstat.dal.ca/~selinger/newsynth/.
-31.6 KB
Binary file not shown.

dist/pygridsynth-1.2.0.tar.gz

-31.3 KB
Binary file not shown.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import mpmath
2+
3+
from pygridsynth.mixed_synthesis import mixed_synthesis_parallel
4+
5+
# Generate a random SU(2^n) unitary matrix
6+
num_qubits = 2
7+
unitary = mpmath.eye(2**num_qubits)
8+
9+
# Parameters
10+
eps = 1e-4 # Error tolerance
11+
M = 64 # Number of Hermitian operators for perturbation
12+
seed = 123 # Random seed for reproducibility
13+
14+
# For faster computation with multiple cores
15+
result = mixed_synthesis_parallel(unitary, num_qubits, eps, M, seed=seed)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from pygridsynth.mixed_synthesis import mixed_synthesis_sequential
2+
from pygridsynth.mymath import diamond_norm_error_from_choi, random_su
3+
4+
# Generate a random SU(2^n) unitary matrix
5+
num_qubits = 2
6+
unitary = random_su(num_qubits)
7+
8+
# Parameters
9+
eps = 1e-4 # Error tolerance
10+
M = 64 # Number of Hermitian operators for perturbation
11+
seed = 123 # Random seed for reproducibility
12+
13+
# Compute mixed synthesis (sequential version)
14+
result = mixed_synthesis_sequential(unitary, num_qubits, eps, M, seed=seed)
15+
16+
if result is not None:
17+
circuit_list, eu_np_list, probs_gptm, u_choi, u_choi_opt = result
18+
print(f"Number of circuits: {len(circuit_list)}")
19+
print(f"Mixing probabilities: {probs_gptm}")
20+
error = diamond_norm_error_from_choi(u_choi, u_choi_opt, eps, mixed_synthesis=True)
21+
print(f"error: {error}")

examples/multi_qubit_basic.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import mpmath
2+
3+
from pygridsynth.multi_qubit_unitary_approximation import (
4+
approximate_multi_qubit_unitary,
5+
)
6+
7+
# Define a target unitary matrix (example: 2-qubit identity)
8+
num_qubits = 2
9+
U = mpmath.eye(2**num_qubits) # 4x4 identity matrix
10+
epsilon = "1e-10"
11+
12+
# Approximate the unitary
13+
circuit, U_approx = approximate_multi_qubit_unitary(U, num_qubits, epsilon)
14+
15+
print(f"Circuit length: {len(circuit)}")
16+
print(f"Circuit: {str(circuit)}")

examples/multi_qubit_domega.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from pygridsynth.multi_qubit_unitary_approximation import (
2+
approximate_multi_qubit_unitary,
3+
)
4+
from pygridsynth.mymath import random_su
5+
6+
# Generate a random SU(2^n) unitary
7+
num_qubits = 2
8+
U = random_su(num_qubits)
9+
epsilon = "1e-10"
10+
11+
# Return DOmegaMatrix instead of mpmath.matrix for more efficient representation
12+
circuit, U_domega = approximate_multi_qubit_unitary(
13+
U, num_qubits, epsilon, return_domega_matrix=True
14+
)
15+
16+
# Convert to complex matrix if needed
17+
U_complex = U_domega.to_complex_matrix

0 commit comments

Comments
 (0)