Skip to content

Commit 555c783

Browse files
committed
Streamline down to minimal product and turn into a package
1 parent 9e9ba9a commit 555c783

12 files changed

Lines changed: 216 additions & 317 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.idea/*
22
.venv/*
33
**/__pycache__/**
4+
dist/**
5+
*.egg-info/**

README.md

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,25 @@ Specifically, it uses `O(q^2*m + q*c)` time and `O(q^2)` space where
88
`m` is the number of measurements,
99
and `c` is the number of Hadamard/CNOT/Phase gates.
1010

11+
# Installation
12+
13+
The `chp_sim` package is available on pypi and can be installed using `pip`:
14+
15+
```bash
16+
python -m pip install chp_sim
17+
```
18+
19+
Alternatively, you can just copy paste the `chp_sim` directory of this
20+
repository into your project.
21+
The only runtime dependency is `numpy`.
22+
1123
# Usage
1224

1325
Here is an example of simulating [a circuit](https://algassert.com/quirk#circuit=%7B%22cols%22%3A%5B%5B1%2C1%2C%22H%22%5D%2C%5B%22X%22%2C1%2C%22%E2%80%A2%22%5D%2C%5B1%2C%22X%22%2C%22%E2%80%A2%22%5D%2C%5B%22Z%5E%C2%BD%22%2C%22Z%5E%C2%BD%22%5D%2C%5B%22H%22%2C%22H%22%2C%22H%22%5D%2C%5B%22Measure%22%2C%22Measure%22%2C%22Measure%22%5D%2C%5B%22Chance3%22%5D%5D%7D):
1426

1527
```python
16-
from stabilizer_sim import StabilizerSim
17-
sim = StabilizerSim(num_qubits=3)
28+
import chp_sim
29+
sim = chp_sim.ChpSimulator(num_qubits=3)
1830

1931
# Desired circuit:
2032
# 0: -------X-------S---H---M---
@@ -62,8 +74,33 @@ assert v2.determined
6274
assert bool(v0) ^ bool(v1) ^ bool(v2)
6375
```
6476

65-
# Installation
6677

67-
At the moment there is no special installation method such as a pypi package.
68-
Just copy-paste the `stabilizer_sim` folder of this repository into your project.
69-
The only runtime dependency is `numpy`.
78+
# Packaging
79+
80+
(Notes to self on how to release a new version.)
81+
82+
1. Edit the source code as needed and run tests.
83+
84+
```bash
85+
pytest
86+
```
87+
88+
2. Build the wheel.
89+
90+
```bash
91+
python3 setup.py -q bdist_wheel
92+
```
93+
94+
3. Verify the wheel works.
95+
96+
```bash
97+
python -m pip install dist/[INSERT FILE NAME HERE].whl
98+
python -c "import chp_sim; print(chp_sim.__version__); print(chp_sim.ChpSimulator(4))"
99+
python -m pip uninstall chp_sim --yes
100+
```
101+
102+
4. Upload to pypi.
103+
104+
```bash
105+
twine upload dist/*.whl --username="${TWINE_USERNAME}" --password="${TWINE_PASSWORD}"
106+
```

chp_sim/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from chp_sim.version import (
2+
__version__
3+
)
4+
5+
from chp_sim.chp_simulator import (
6+
ChpSimulator,
7+
MeasureResult,
8+
)
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import random
2-
from typing import Union
2+
from typing import Union, Any
33

44
import numpy as np
55

66

7-
class StabilizerSimCore:
7+
class ChpSimulator:
88
"""The bare minimum needed for the CHP simulation.
99
1010
Reference:
@@ -144,6 +144,9 @@ def _row(row: int) -> str:
144144
x_obs = [_row(row) for row in range(self._n, 2 * self._n)]
145145
return '\n'.join(z_obs + sep + x_obs)
146146

147+
def _repr_pretty_(self, p: Any, cycle: bool) -> None:
148+
p.text(str(self))
149+
147150

148151
def pauli_product_phase(x1: bool, z1: bool, x2: bool, z2: bool) -> int:
149152
"""Determines the power of i in the product of two Paulis.

stabilizer_sim/stabilizer_sim_core_test.py renamed to chp_sim/chp_simulator_test.py

Lines changed: 114 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
1-
import cirq
1+
import collections
22

3-
from .stabilizer_sim_core import pauli_product_phase, StabilizerSimCore
4-
from . import MeasureResult
3+
from typing import Set
4+
import itertools
5+
6+
from chp_sim.chp_simulator import (
7+
pauli_product_phase, ChpSimulator, MeasureResult,
8+
)
59

610

711
def test_pauli_product_phase():
8-
q = cirq.LineQubit(0)
9-
mapping = {
10-
0: cirq.I(q),
11-
1: cirq.X(q),
12-
2: cirq.Z(q),
13-
3: cirq.Y(q)
14-
}
15-
for k1, v1 in mapping.items():
16-
for k2, v2 in mapping.items():
17-
x1 = bool(k1 & 1)
18-
z1 = bool(k1 & 2)
19-
x2 = bool(k2 & 1)
20-
z2 = bool(k2 & 2)
21-
p = pauli_product_phase(x1, z1, x2, z2)
22-
if k1 == 0 or k2 == 0:
23-
assert p == 0
24-
else:
25-
assert [1, 1j, None, -1j][p] == (v1 * v2).coefficient
12+
paulis = [
13+
[False, False], # I
14+
[True, False], # X
15+
[True, True], # Y
16+
[False, True], # Z
17+
]
18+
expected = [
19+
[0, 0, 0, 0],
20+
[0, 0, 1, -1],
21+
[0, -1, 0, 1],
22+
[0, 1, -1, 0],
23+
]
24+
assert pauli_product_phase(x1=True, z1=False, x2=True, z2=True) == 1;
25+
for i in range(4):
26+
for j in range(4):
27+
assert pauli_product_phase(*paulis[i], *paulis[j]) == expected[i][j]
2628

2729

2830
def test_identity():
29-
s = StabilizerSimCore(num_qubits=1)
31+
s = ChpSimulator(num_qubits=1)
3032
assert s.measure(0) == MeasureResult(value=False, determined=True)
3133

3234

3335
def test_bit_flip():
34-
s = StabilizerSimCore(num_qubits=1)
36+
s = ChpSimulator(num_qubits=1)
3537
s.hadamard(0)
3638
s.phase(0)
3739
s.phase(0)
@@ -40,13 +42,13 @@ def test_bit_flip():
4042

4143

4244
def test_identity_2():
43-
s = StabilizerSimCore(num_qubits=2)
45+
s = ChpSimulator(num_qubits=2)
4446
assert s.measure(0) == MeasureResult(value=False, determined=True)
4547
assert s.measure(1) == MeasureResult(value=False, determined=True)
4648

4749

4850
def test_bit_flip_2():
49-
s = StabilizerSimCore(num_qubits=2)
51+
s = ChpSimulator(num_qubits=2)
5052
s.hadamard(0)
5153
s.phase(0)
5254
s.phase(0)
@@ -56,7 +58,7 @@ def test_bit_flip_2():
5658

5759

5860
def test_epr():
59-
s = StabilizerSimCore(num_qubits=2)
61+
s = ChpSimulator(num_qubits=2)
6062
s.hadamard(0)
6163
s.cnot(0, 1)
6264
v1 = s.measure(0)
@@ -67,7 +69,7 @@ def test_epr():
6769

6870

6971
def test_phase_kickback_consume_s_state():
70-
s = StabilizerSimCore(num_qubits=2)
72+
s = ChpSimulator(num_qubits=2)
7173
s.hadamard(1)
7274
s.phase(1)
7375
s.hadamard(0)
@@ -83,7 +85,7 @@ def test_phase_kickback_consume_s_state():
8385

8486

8587
def test_phase_kickback_preserve_s_state():
86-
s = StabilizerSimCore(num_qubits=2)
88+
s = ChpSimulator(num_qubits=2)
8789

8890
# Prepare S state.
8991
s.hadamard(1)
@@ -108,7 +110,7 @@ def test_phase_kickback_preserve_s_state():
108110

109111

110112
def test_kickback_vs_stabilizer():
111-
sim = StabilizerSimCore(num_qubits=3)
113+
sim = ChpSimulator(num_qubits=3)
112114
sim.hadamard(2)
113115
sim.cnot(2, 0)
114116
sim.cnot(2, 1)
@@ -163,7 +165,7 @@ def test_kickback_vs_stabilizer():
163165

164166
def test_s_state_distillation_low_depth():
165167
for _ in range(100):
166-
sim = StabilizerSimCore(num_qubits=9)
168+
sim = ChpSimulator(num_qubits=9)
167169

168170
stabilizers = [
169171
(0, 1, 2, 3),
@@ -217,7 +219,7 @@ def test_s_state_distillation_low_depth():
217219

218220
def test_s_state_distillation_low_space():
219221
for _ in range(100):
220-
sim = StabilizerSimCore(num_qubits=5)
222+
sim = ChpSimulator(num_qubits=5)
221223

222224
phasors = [
223225
(0,),
@@ -251,3 +253,84 @@ def test_s_state_distillation_low_space():
251253
sim.phase(3)
252254
sim.hadamard(3)
253255
assert sim.measure(3) == MeasureResult(value=True, determined=True)
256+
257+
258+
def test_count_s_state_distillation_failure_cases():
259+
def distill(errors: Set[int]) -> str:
260+
sim = ChpSimulator(num_qubits=5)
261+
262+
phasors = [
263+
(0,),
264+
(1,),
265+
(2,),
266+
(0, 1, 2),
267+
(0, 1, 3),
268+
(0, 2, 3),
269+
(1, 2, 3),
270+
]
271+
272+
anc = 4
273+
for e, phasor in enumerate(phasors):
274+
for k in phasor:
275+
sim.hadamard(anc)
276+
sim.cnot(anc, k)
277+
sim.hadamard(anc)
278+
sim.phase(anc)
279+
280+
if e in errors:
281+
sim.phase(anc)
282+
sim.phase(anc)
283+
284+
sim.hadamard(anc)
285+
v = sim.measure(anc)
286+
if v.value:
287+
sim.hadamard(anc)
288+
sim.phase(anc)
289+
sim.phase(anc)
290+
sim.hadamard(anc)
291+
assert not v.determined
292+
if v.value:
293+
for k in phasor:
294+
sim.hadamard(k)
295+
sim.phase(k)
296+
sim.phase(k)
297+
sim.hadamard(k)
298+
299+
sim.phase(3)
300+
sim.phase(3)
301+
sim.phase(3)
302+
sim.hadamard(3)
303+
result = sim.measure(3)
304+
sim.hadamard(3)
305+
sim.phase(3)
306+
checks = [sim.measure(k) for k in range(3)]
307+
assert result.determined
308+
assert all(e.determined for e in checks)
309+
good_result = result.value is False
310+
checks_passed = not any(e.value for e in checks)
311+
if checks_passed:
312+
if good_result:
313+
return 'good'
314+
else:
315+
return 'ERROR'
316+
else:
317+
if good_result:
318+
return 'victim'
319+
else:
320+
return 'caught'
321+
322+
def classify(errs) -> collections.Counter:
323+
result = collections.Counter()
324+
for err in errs:
325+
result[distill(err)] += 1
326+
return result
327+
328+
nones = list(itertools.combinations(range(7), 0))
329+
singles = list(itertools.combinations(range(7), 1))
330+
doubles = list(itertools.combinations(range(7), 2))
331+
triples = list(itertools.combinations(range(7), 3))
332+
333+
assert classify(nones) == {'good': 1}
334+
assert classify(singles) == {'caught': 3, 'victim': 4}
335+
assert classify(doubles) == {'caught': 12, 'victim': 9}
336+
assert classify(triples) == {'caught': 12, 'victim': 16, 'ERROR': 7}

chp_sim/version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = '0.1.0'

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
numpy
2-
pytest

setup.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2018 The Cirq Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import io
16+
from setuptools import setup
17+
18+
# This reads the __version__ variable from version.py
19+
__version__ = ''
20+
exec(open('chp_sim/version.py').read())
21+
22+
description = ("Reference implementation of Aaronson et al's CHP simulator "
23+
"for efficiently simulating quantum stabilizer circuits.")
24+
25+
# README file as long_description.
26+
long_description = io.open('README.md', encoding='utf-8').read()
27+
28+
# Read in requirements
29+
requirements = open('requirements.txt').readlines()
30+
requirements = [r.strip() for r in requirements]
31+
32+
setup(name='chp_sim',
33+
version=__version__,
34+
url='https://github.com/Strilanc/python-chp-stabilizer-simulator',
35+
author='Craig Gidney',
36+
author_email='craig.gidney@gmail.com',
37+
python_requires='>=3.6.0',
38+
install_requires=requirements,
39+
license='Apache 2',
40+
description='',
41+
long_description=long_description,
42+
packages=['chp_sim'])

stabilizer_sim/__init__.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)