Skip to content

Commit af839c0

Browse files
authored
Migrate over pyqrack interpreter. (#8)
* adding pyqrack impl into circuits repo * updating workflows to work with pyqrack * updating ci * removing dependencies from main list
1 parent 58b65c3 commit af839c0

File tree

26 files changed

+4454
-5
lines changed

26 files changed

+4454
-5
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- name: Set up Python ${{ matrix.python-version }}
3131
run: uv python install ${{ matrix.python-version }}
3232
- name: Install the project
33-
run: uv sync --all-extras --dev
33+
run: uv sync --upgrade --dev --group dev-linux
3434
- name: Run tests
3535
# For example, using `pytest`
3636
run: uv run just coverage

.github/workflows/devdoc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
enable-cache: true
2525
cache-dependency-glob: "uv.lock"
2626
- name: Install Documentation dependencies
27-
run: uv sync --group doc
27+
run: uv sync --upgrade --dev --group doc --group dev-linux
2828
- name: Set up build cache
2929
uses: actions/cache@v4
3030
id: cache

.github/workflows/doc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
enable-cache: true
2525
cache-dependency-glob: "uv.lock"
2626
- name: Install Documentation dependencies
27-
run: uv sync --group doc
27+
run: uv sync --upgrade --dev --group doc --group dev-linux
2828
- name: Set up build cache
2929
uses: actions/cache@v4
3030
id: cache

.github/workflows/pub_doc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
enable-cache: true
2525
cache-dependency-glob: "uv.lock"
2626
- name: Install Documentation dependencies
27-
run: uv sync --group doc
27+
run: uv sync --upgrade --dev --group doc --group dev-linux
2828
- name: Set up build cache
2929
uses: actions/cache@v4
3030
id: cache

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
# Install a specific version of uv.
1616
version: "0.5.5"
1717
- name: Install the project
18-
run: uv sync --all-extras --dev
18+
run: uv sync --all-extras
1919
- name: Build distribution 📦
2020
run: uv build
2121
- name: Store the distribution packages

_typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ AttributeIDSupressMenu = "AttributeIDSupressMenu"
1212
Braket = "Braket"
1313
mch = "mch"
1414
IY = "IY"
15+
ket = "ket"

pyproject.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ doc = [
7676
examples = [
7777
"networkx>=3.4.2",
7878
]
79+
dev-linux = [
80+
"cirq-core[contrib]>=1.4.1",
81+
"lark>=1.2.2",
82+
"pyqrack-cpu>=1.38.2",
83+
"qbraid>=0.9.5",
84+
"ffmpeg>=1.4",
85+
"matplotlib>=3.10.0",
86+
"pyqt5>=5.15.11",
87+
"tqdm>=4.67.1",
88+
]
89+
dev-mac-arm = [
90+
"cirq-core[contrib]>=1.4.1",
91+
"ffmpeg>=1.4",
92+
"lark>=1.2.2",
93+
"matplotlib>=3.10.0",
94+
"pyqrack>=1.38.2",
95+
"pyqt5>=5.15.11",
96+
"qbraid>=0.9.5",
97+
"tqdm>=4.67.1",
98+
]
7999

80100
[tool.isort]
81101
profile = "black"

src/bloqade/pyqrack/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from .reg import (
2+
CBitRef as CBitRef,
3+
CRegister as CRegister,
4+
PyQrackReg as PyQrackReg,
5+
QubitState as QubitState,
6+
Measurement as Measurement,
7+
PyQrackQubit as PyQrackQubit,
8+
)
9+
from .base import (
10+
StackMemory as StackMemory,
11+
DynamicMemory as DynamicMemory,
12+
PyQrackInterpreter as PyQrackInterpreter,
13+
)
14+
15+
# NOTE: The following import is for registering the method tables
16+
from .noise import native as native
17+
from .qasm2 import uop as uop, core as core, parallel as parallel
18+
from .target import PyQrack as PyQrack

src/bloqade/pyqrack/base.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import abc
2+
import typing
3+
from dataclasses import field, dataclass
4+
from unittest.mock import Mock
5+
6+
import numpy as np
7+
from kirin.interp import Interpreter
8+
from typing_extensions import Self
9+
from kirin.interp.exceptions import InterpreterError
10+
11+
from bloqade.pyqrack.reg import Measurement
12+
13+
if typing.TYPE_CHECKING:
14+
from pyqrack import QrackSimulator
15+
16+
17+
class PyQrackOptions(typing.TypedDict):
18+
qubitCount: int
19+
isTensorNetwork: bool
20+
isSchmidtDecomposeMulti: bool
21+
isSchmidtDecompose: bool
22+
isStabilizerHybrid: bool
23+
isBinaryDecisionTree: bool
24+
isPaged: bool
25+
isCpuGpuHybrid: bool
26+
isOpenCL: bool
27+
28+
29+
def _default_pyqrack_args() -> PyQrackOptions:
30+
return PyQrackOptions(
31+
qubitCount=-1,
32+
isTensorNetwork=False,
33+
isSchmidtDecomposeMulti=True,
34+
isSchmidtDecompose=True,
35+
isStabilizerHybrid=True,
36+
isBinaryDecisionTree=True,
37+
isPaged=True,
38+
isCpuGpuHybrid=True,
39+
isOpenCL=True,
40+
)
41+
42+
43+
@dataclass
44+
class MemoryABC(abc.ABC):
45+
pyqrack_options: PyQrackOptions = field(default_factory=_default_pyqrack_args)
46+
sim_reg: "QrackSimulator" = field(init=False)
47+
48+
@abc.abstractmethod
49+
def allocate(self, n_qubits: int) -> tuple[int, ...]:
50+
"""Allocate `n_qubits` qubits and return their ids."""
51+
...
52+
53+
def reset(self):
54+
"""Reset the memory, releasing all qubits."""
55+
from pyqrack import QrackSimulator
56+
57+
# do not reset the simulator it might be used by
58+
# results of the simulation
59+
self.sim_reg = QrackSimulator(**self.pyqrack_options)
60+
61+
62+
@dataclass
63+
class MockMemory(MemoryABC):
64+
"""Mock memory for testing purposes."""
65+
66+
allocated: int = field(init=False, default=0)
67+
68+
def allocate(self, n_qubits: int):
69+
allocated = self.allocated + n_qubits
70+
result = tuple(range(self.allocated, allocated))
71+
self.allocated = allocated
72+
return result
73+
74+
def reset(self):
75+
self.allocated = 0
76+
self.sim_reg = Mock()
77+
78+
79+
@dataclass
80+
class StackMemory(MemoryABC):
81+
total: int = field(kw_only=True)
82+
allocated: int = field(init=False, default=0)
83+
84+
def allocate(self, n_qubits: int):
85+
curr_allocated = self.allocated
86+
self.allocated += n_qubits
87+
88+
if self.allocated > self.total:
89+
raise InterpreterError(
90+
f"qubit allocation exceeds memory, "
91+
f"{self.total} qubits, "
92+
f"{self.allocated} allocated"
93+
)
94+
95+
return tuple(range(curr_allocated, self.allocated))
96+
97+
def reset(self):
98+
super().reset()
99+
self.allocated = 0
100+
101+
102+
@dataclass
103+
class DynamicMemory(MemoryABC):
104+
def __post_init__(self):
105+
self.reset()
106+
107+
if self.sim_reg.is_tensor_network:
108+
raise ValueError("DynamicMemory does not support tensor networks")
109+
110+
def allocate(self, n_qubits: int):
111+
start = self.sim_reg.num_qubits()
112+
for i in range(start, start + n_qubits):
113+
self.sim_reg.allocate_qubit(i)
114+
115+
return tuple(range(start, start + n_qubits))
116+
117+
118+
@dataclass
119+
class PyQrackInterpreter(Interpreter):
120+
keys = ["pyqrack", "main"]
121+
memory: MemoryABC = field(kw_only=True)
122+
rng_state: np.random.Generator = field(
123+
default_factory=np.random.default_rng, kw_only=True
124+
)
125+
loss_m_result: Measurement = field(default=Measurement.One, kw_only=True)
126+
"""The value of a measurement result when a qubit is lost."""
127+
128+
def initialize(self) -> Self:
129+
super().initialize()
130+
self.memory.reset() # reset allocated qubits
131+
return self

src/bloqade/pyqrack/noise/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)