Skip to content

Commit dc7b443

Browse files
[feature] Adding support for QuEST and Qulacs statevector simulators (#68)
* Added Qulacs simulator * Added wrapper for QuEST * Updated readme with simulator installation instructions * Adding tests for HybridEngine on statevector simulators * Supporting new statevector sims on HybridEngine * Fixed a bug in CuStateVec relating to HybridEngine
1 parent 87e86f3 commit dc7b443

23 files changed

+1595
-20
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ pytest tests
9797

9898
Certain simulators from `pecos.simulators` require external packages that are not installed by `pip install .[all]`.
9999

100-
- `CuStateVec` requires a Linux machine with an NVIDIA GPU (see requirements [here](https://docs.nvidia.com/cuda/cuquantum/latest/getting_started/getting_started.html#dependencies-custatevec-label)). PECOS' dependencies are specified in the `[cuda]` section of `pyproject.toml` and can be installed via `pip install .[cuda]`. If installation is not successful, see alternative instructions [here](https://docs.nvidia.com/cuda/cuquantum/latest/getting_started/getting_started.html#installing-cuquantum).
100+
- `QuEST` is installed along with the python package `pyquest` when calling `pip install .[all]`. However, it uses 64-bit float point precision by default, and if you wish to make use of 32-bit float point precision you will need to follow the installation instructions provided by the developers [here](https://github.com/rrmeister/pyQuEST/tree/develop).
101+
- `CuStateVec` requires a Linux machine with an NVIDIA GPU (see requirements [here](https://docs.nvidia.com/cuda/cuquantum/latest/getting_started/getting_started.html#dependencies-custatevec-label)). PECOS' dependencies are specified in the `[cuda]` section of `pyproject.toml`, however, installation via `pip` is not reliable. The recommended method of installation is via `conda`, as discussed [here](https://docs.nvidia.com/cuda/cuquantum/latest/getting_started/getting_started.html#installing-cuquantum). Note that there might be conflicts between `conda` and `venv`; if you intend to use `CuStateVec`, you may follow the installation instructions for PECOS within a `conda` environment without involving the `venv` commands.
101102

102103
## Uninstall
103104

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ simulators = [
5757
"cython",
5858
"pybind11>=2.2.3,<3.0",
5959
"projectq>=0.5.0,<0.9.0",
60+
"qulacs>=0.6.4",
61+
"pyquest>=0.0.1",
6062
]
6163
cuda = [
6264
"cuquantum-python>=24.03.0",

python/pecos/simulators/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@
3232
except ImportError:
3333
pass
3434

35+
# Attempt to import optional Qulacs package
36+
try: # noqa: SIM105
37+
from pecos.simulators.qulacs.state import Qulacs # wrapper for Qulacs sim
38+
except ImportError:
39+
pass
40+
41+
# Attempt to import optional QuEST package
42+
try: # noqa: SIM105
43+
from pecos.simulators.quest.state import QuEST # wrapper for QuEST sim
44+
except ImportError:
45+
pass
46+
3547
# Attempt to import optional cuquantum and cupy packages
3648
try:
3749
import cupy

python/pecos/simulators/basic_sv/state.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212

1313
import numpy as np
14+
from numpy.typing import ArrayLike
1415

1516
from pecos.simulators.basic_sv import bindings
1617
from pecos.simulators.sim_class_types import StateVector
@@ -109,5 +110,5 @@ def reset(self):
109110
return self
110111

111112
@property
112-
def vector(self) -> np.ndarray:
113+
def vector(self) -> ArrayLike:
113114
return np.reshape(self.internal_vector, newshape=2**self.num_qubits)

python/pecos/simulators/custatevec/bindings.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@
1919

2020
gate_dict = {
2121
"Init": init_zero,
22+
"Init +Z": init_zero,
23+
"Init -Z": init_one,
2224
"init |0>": init_zero,
2325
"init |1>": init_one,
24-
"Measure": meas_z,
25-
"measure Z": meas_z,
2626
"leak": init_zero,
2727
"leak |0>": init_zero,
2828
"leak |1>": init_one,
2929
"unleak |0>": init_zero,
3030
"unleak |1>": init_one,
31+
"Measure": meas_z,
32+
"measure Z": meas_z,
3133
"I": one_q.identity,
3234
"X": one_q.X,
3335
"Y": one_q.Y,

python/pecos/simulators/custatevec/state.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
import random
1313

1414
import cupy as cp
15-
import numpy as np
1615
from cuquantum import ComputeType, cudaDataType
1716
from cuquantum import custatevec as cusv
17+
from numpy.typing import ArrayLike
1818

1919
from pecos.simulators.custatevec import bindings
2020
from pecos.simulators.sim_class_types import StateVector
@@ -53,8 +53,7 @@ def __init__(self, num_qubits, seed=None) -> None:
5353
self.compute_type = ComputeType.COMPUTE_64F
5454

5555
# Allocate the statevector in GPU and initialize it to |0>
56-
self.cupy_vector = cp.zeros(shape=2**num_qubits, dtype=self.cp_type)
57-
self.cupy_vector[0] = 1
56+
self.reset()
5857

5958
####################################################
6059
# Set up cuStateVec library and GPU memory handles #
@@ -101,8 +100,8 @@ def free(ptr, size, stream):
101100
def reset(self):
102101
"""Reset the quantum state for another run without reinitializing."""
103102
# Initialize all qubits in the zero state
104-
self.vector = cp.zeros(shape=2**self.num_qubits, dtype=self.cp_type)
105-
self.vector[0] = 1
103+
self.cupy_vector = cp.zeros(shape=2**self.num_qubits, dtype=self.cp_type)
104+
self.cupy_vector[0] = 1
106105
return self
107106

108107
def __del__(self) -> None:
@@ -111,5 +110,5 @@ def __del__(self) -> None:
111110
cusv.destroy(self.libhandle)
112111

113112
@property
114-
def vector(self) -> np.ndarray:
113+
def vector(self) -> ArrayLike:
115114
return self.cupy_vector.get()

python/pecos/simulators/parent_sim_classes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ def run_gate(self, symbol: str, locations: set[int] | set[tuple[int, ...]], **pa
3737
for location in locations:
3838
if params.get("angles") and len(params["angles"]) == 1:
3939
params.update({"angle": params["angles"][0]})
40+
elif "angle" in params and "angles" not in params:
41+
params["angles"] = (params["angle"],)
42+
4043
results = self.bindings[symbol](self, location, **params)
4144

4245
# TODO: get params var value ... -> result = {'sym':, 'index':, 'result': result, 'qubit': location}

python/pecos/simulators/quantum_simulator.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,21 @@
1818
except ImportError:
1919
ProjectQSim = None
2020

21+
try:
22+
from pecos.simulators import Qulacs
23+
except ImportError:
24+
Qulacs = None
25+
26+
try:
27+
from pecos.simulators import QuEST
28+
except ImportError:
29+
QuEST = None
30+
31+
try:
32+
from pecos.simulators import CuStateVec
33+
except ImportError:
34+
CuStateVec = None
35+
2136
from pecos.reps.pypmir.op_types import QOp
2237

2338

@@ -38,7 +53,15 @@ def init(self, num_qubits: int):
3853
if self.backend == "stabilizer":
3954
self.state = SparseSim
4055
elif self.backend == "state-vector":
56+
self.state = Qulacs # Seems to be the better default choice for now
57+
elif self.backend == "ProjectQSim":
4158
self.state = ProjectQSim
59+
elif self.backend == "Qulacs":
60+
self.state = Qulacs
61+
elif self.backend == "QuEST":
62+
self.state = QuEST
63+
elif self.backend == "CuStateVec":
64+
self.state = CuStateVec
4265
else:
4366
msg = f"simulator `{self.state}` not currently implemented!"
4467
raise NotImplementedError(msg)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2024 The PECOS Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4+
# the License.You may obtain a copy of the License at
5+
#
6+
# https://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
9+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
# specific language governing permissions and limitations under the License.
11+
12+
from pecos.simulators.quest import bindings
13+
from pecos.simulators.quest.state import QuEST
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright 2024 The PECOS Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4+
# the License.You may obtain a copy of the License at
5+
#
6+
# https://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
9+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
# specific language governing permissions and limitations under the License.
11+
12+
import pecos.simulators.quest.gates_one_qubit as one_q
13+
import pecos.simulators.quest.gates_two_qubit as two_q
14+
from pecos.simulators.quest.gates_init import init_one, init_zero
15+
from pecos.simulators.quest.gates_meas import meas_z
16+
17+
# Supporting gates from table:
18+
# https://github.com/CQCL/phir/blob/main/spec.md#table-ii---quantum-operations
19+
20+
gate_dict = {
21+
"Init": init_zero,
22+
"Init +Z": init_zero,
23+
"Init -Z": init_one,
24+
"init |0>": init_zero,
25+
"init |1>": init_one,
26+
"leak": init_zero,
27+
"leak |0>": init_zero,
28+
"leak |1>": init_one,
29+
"unleak |0>": init_zero,
30+
"unleak |1>": init_one,
31+
"Measure": meas_z,
32+
"measure Z": meas_z,
33+
"I": one_q.identity,
34+
"X": one_q.X,
35+
"Y": one_q.Y,
36+
"Z": one_q.Z,
37+
"RX": one_q.RX,
38+
"RY": one_q.RY,
39+
"RZ": one_q.RZ,
40+
"R1XY": one_q.R1XY,
41+
"RXY1Q": one_q.R1XY,
42+
"SX": one_q.SX,
43+
"SXdg": one_q.SXdg,
44+
"SqrtX": one_q.SX,
45+
"SqrtXd": one_q.SXdg,
46+
"SY": one_q.SY,
47+
"SYdg": one_q.SYdg,
48+
"SqrtY": one_q.SY,
49+
"SqrtYd": one_q.SYdg,
50+
"SZ": one_q.SZ,
51+
"SZdg": one_q.SZdg,
52+
"SqrtZ": one_q.SZ,
53+
"SqrtZd": one_q.SZdg,
54+
"H": one_q.H,
55+
"F": one_q.F,
56+
"Fdg": one_q.Fdg,
57+
"T": one_q.T,
58+
"Tdg": one_q.Tdg,
59+
"CX": two_q.CX,
60+
"CY": two_q.CY,
61+
"CZ": two_q.CZ,
62+
"RXX": two_q.RXX,
63+
"RYY": two_q.RYY,
64+
"RZZ": two_q.RZZ,
65+
"R2XXYYZZ": two_q.R2XXYYZZ,
66+
"SXX": two_q.SXX,
67+
"SXXdg": two_q.SXXdg,
68+
"SYY": two_q.SYY,
69+
"SYYdg": two_q.SYYdg,
70+
"SZZ": two_q.SZZ,
71+
"SqrtZZ": two_q.SZZ,
72+
"SZZdg": two_q.SZZdg,
73+
"SWAP": two_q.SWAP,
74+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2024 The PECOS Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4+
# the License.You may obtain a copy of the License at
5+
#
6+
# https://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
9+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
# specific language governing permissions and limitations under the License.
11+
12+
from typing import Any
13+
14+
from pecos.simulators.quest.gates_meas import meas_z
15+
from pecos.simulators.quest.gates_one_qubit import X
16+
17+
18+
def init_zero(state, qubit: int, **params: Any) -> None:
19+
"""Initialise or reset the qubit to state |0>
20+
21+
Args:
22+
state: An instance of QuEST
23+
qubit: The index of the qubit to be initialised
24+
"""
25+
result = meas_z(state, qubit)
26+
27+
if result:
28+
X(state, qubit)
29+
30+
31+
def init_one(state, qubit: int, **params: Any) -> None:
32+
"""Initialise or reset the qubit to state |1>
33+
Args:
34+
state: An instance of QuEST
35+
qubit: The index of the qubit to be initialised
36+
"""
37+
result = meas_z(state, qubit)
38+
39+
if not result:
40+
X(state, qubit)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2024 The PECOS Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4+
# the License.You may obtain a copy of the License at
5+
#
6+
# https://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
9+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
# specific language governing permissions and limitations under the License.
11+
12+
from typing import Any
13+
14+
from pyquest.gates import M
15+
16+
17+
def meas_z(state, qubit: int, **params: Any) -> int:
18+
"""Measure in the Z-basis, collapse and normalise.
19+
20+
Notes:
21+
The number of qubits in the state remains the same.
22+
23+
Args:
24+
state: An instance of QuEST
25+
qubit: The index of the qubit to be measured
26+
27+
Returns:
28+
The outcome of the measurement, either 0 or 1.
29+
"""
30+
# QuEST uses qubit index 0 as the least significant bit
31+
idx = state.num_qubits - qubit - 1
32+
return state.quest_state.apply_operator(M(idx))[0]

0 commit comments

Comments
 (0)