Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

64 bit signed arithmetic support #57

Merged
merged 13 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ updatereqs: upgrade-pip ## Autogenerate requirements.txt
$(VENV_BIN)/pip-compile --extra=tests --no-annotate --no-emit-index-url --output-file=requirements.txt --strip-extras pyproject.toml

metadeps: upgrade-pip ## Install extra dependencies used to develop/build this package
$(VENV_BIN)/pip install -U build pip-tools pre-commit wheel pytest
$(VENV_BIN)/pip install -U build pip-tools pre-commit wheel pytest hypothesis

# Installation
# ------------
Expand Down
7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "quantum-pecos"
version = "0.6.0.dev1"
version = "0.6.0.dev2"
authors = [
{name = "The PECOS Developers"},
]
Expand All @@ -28,7 +28,7 @@ requires-python = ">=3.10"
license = { file = "LICENSE"}
keywords = ["quantum", "QEC", "simulation", "PECOS"]
dependencies = [
"phir~=0.3.0",
"phir>=0.3.3,<0.4",
"numpy>=1.15.0,<2.0",
"scipy>=1.1.0,<2.0",
"networkx>=2.1.0,<3.0",
Expand Down Expand Up @@ -73,7 +73,8 @@ visualization = [
"plotly~=5.9.0",
]
tests = [
"pytest>=5.0.0"
"pytest>=5.0.0",
"hypothesis"
]
all = [
"quantum-pecos[simulators]",
Expand Down
2 changes: 1 addition & 1 deletion python/pecos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from pecos import circuit_converters, circuits, decoders, engines, error_models, misc, qeccs, simulators, tools
from pecos.circuits.quantum_circuit import QuantumCircuit
from pecos.engines import circuit_runners
from pecos.engines.cvm.binarray import BinArray
from pecos.engines.cvm.binarray2 import BinArray2 as BinArray
from pecos.engines.hybrid_engine_old import HybridEngine

__all__ = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
import warnings
from typing import TYPE_CHECKING, Any

import numpy as np
from phir.model import PHIRModel

from pecos.classical_interpreters.classical_interpreter_abc import ClassicalInterpreter
from pecos.reps.pypmir import PyPMIR
from pecos.reps.pypmir import PyPMIR, signed_data_types, unsigned_data_types
from pecos.reps.pypmir import types as pt

if TYPE_CHECKING:
Expand All @@ -34,16 +33,7 @@ def version2tuple(v):
return tuple(map(int, (v.split("."))))


data_type_map = {
"i8": np.int8,
"i16": np.int16,
"i32": np.int32,
"i64": np.int64,
"u8": np.uint8,
"u16": np.uint16,
"u32": np.uint32,
"u64": np.uint64,
}
data_type_map = signed_data_types | unsigned_data_types

data_type_map_rev = {v: k for k, v in data_type_map.items()}

Expand Down Expand Up @@ -276,7 +266,6 @@ def assign_int(self, cvar, val: int):

cid = self.csym2id[cvar]
dtype = self.cid2dtype[cid]
size = self.cvar_meta[cid].size

cval = self.cenv[cid]
val = dtype(val)
Expand All @@ -286,8 +275,11 @@ def assign_int(self, cvar, val: int):
cval &= ~(1 << i)
cval |= (val & 1) << i

# mask off bits give the size of the register
cval &= (1 << size) - 1
if type(cval) not in signed_data_types.values():
# mask off bits given the size of the register
# (only valid for unsigned data types)
size = self.cvar_meta[cid].size
cval &= (1 << size) - 1
self.cenv[cid] = cval

def handle_cops(self, op):
Expand Down
11 changes: 9 additions & 2 deletions python/pecos/engines/cvm/binarray2.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@

import numpy as np

from pecos.reps.pypmir import unsigned_data_types


class BinArray2:
def __init__(self, size, value=0, dtype=np.int32) -> None:
"""As opposed to the original unsigned 32-bit BinArray, this class defaults to signed 64-bit type."""

def __init__(self, size, value=0, dtype=np.int64) -> None:
self.size = size
self.value = None
self.dtype = dtype
Expand All @@ -33,6 +37,8 @@ def __init__(self, size, value=0, dtype=np.int32) -> None:
def set(self, value):
if isinstance(value, self.dtype):
self.value = value
elif isinstance(value, BinArray2):
self.value = value.value
else:
if isinstance(value, str):
value = int(value, 2)
Expand All @@ -41,7 +47,8 @@ def set(self, value):

def new_val(self, value):
b = BinArray2(self.size, value, self.dtype)
b.clamp(self.size)
if self.dtype in unsigned_data_types.values():
b.clamp(self.size)
return b

def num_bits(self):
Expand Down
2 changes: 1 addition & 1 deletion python/pecos/engines/cvm/classical.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from __future__ import annotations

from pecos.engines.cvm.binarray import BinArray
from pecos.engines.cvm.binarray2 import BinArray2 as BinArray


def set_output(state, circuit, output_spec, output):
Expand Down
2 changes: 1 addition & 1 deletion python/pecos/engines/cvm/wasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import pickle
from pathlib import Path

from pecos.engines.cvm.binarray import BinArray
from pecos.engines.cvm.binarray2 import BinArray2 as BinArray
from pecos.engines.cvm.sim_func import sim_exec
from pecos.engines.cvm.wasm_vms.pywasm import read_pywasm
from pecos.engines.cvm.wasm_vms.pywasm3 import read_pywasm3
Expand Down
3 changes: 2 additions & 1 deletion python/pecos/engines/hybrid_engine_old.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@

import numpy as np

from pecos.engines.cvm.classical import BinArray, eval_condition, eval_cop, set_output
from pecos.engines.cvm.binarray2 import BinArray2 as BinArray
from pecos.engines.cvm.classical import eval_condition, eval_cop, set_output
from pecos.engines.cvm.wasm import eval_cfunc, get_ccop
from pecos.error_models.fake_error_model import FakeErrorModel
from pecos.errors import NotSupportedGateError
Expand Down
2 changes: 1 addition & 1 deletion python/pecos/reps/pypmir/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

from pecos.reps.pypmir.pypmir import PyPMIR
from pecos.reps.pypmir.pypmir import PyPMIR, signed_data_types, unsigned_data_types
24 changes: 22 additions & 2 deletions python/pecos/reps/pypmir/pypmir.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from math import pi
from typing import TYPE_CHECKING, Callable, TypeVar

import numpy as np

from pecos.reps.pypmir import block_types as blk
from pecos.reps.pypmir import data_types as d
from pecos.reps.pypmir import op_types as op
Expand All @@ -25,6 +27,20 @@

TypeOp = TypeVar("TypeOp", bound=op.Op)

signed_data_types = {
"i8": np.int8,
"i16": np.int16,
"i32": np.int32,
"i64": np.int64,
}

unsigned_data_types = {
"u8": np.uint8,
"u16": np.uint16,
"u32": np.uint32,
"u64": np.uint64,
}

qartik marked this conversation as resolved.
Show resolved Hide resolved

class PyPMIR:
"""Pythonic PECOS Middle-level IR. Used to convert PHIR into an object and optimize the data structure for
Expand Down Expand Up @@ -229,10 +245,14 @@ def from_phir(cls, phir: dict, name_resolver=None) -> PyPMIR:
name = o["data"]

if name == "cvar_define":
data_type = o["data_type"]
size = int(data_type[1:])
if "size" in o:
size = o["size"]
qciaran marked this conversation as resolved.
Show resolved Hide resolved
data = d.CVarDefine(
data_type=o["data_type"],
data_type=data_type,
variable=o["variable"],
size=o["size"],
size=size,
cvar_id=len(p.cvar_meta),
metadata=o.get("metadata"),
)
Expand Down
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
# pip-compile --extra=tests --no-annotate --no-emit-index-url --output-file=requirements.txt --strip-extras pyproject.toml
#
annotated-types==0.6.0
attrs==23.2.0
contourpy==1.2.0
cycler==0.12.1
fonttools==4.50.0
hypothesis==6.100.1
iniconfig==2.0.0
kiwisolver==1.4.5
markdown-it-py==3.0.0
Expand All @@ -16,7 +18,7 @@ mdurl==0.1.2
networkx==2.8.8
numpy==1.26.4
packaging==24.0
phir==0.3.2
phir==0.3.3
pillow==10.2.0
pluggy==1.4.0
pydantic==2.6.4
Expand All @@ -28,4 +30,5 @@ python-dateutil==2.9.0.post0
rich==13.7.1
scipy==1.12.0
six==1.16.0
sortedcontainers==2.4.0
typing-extensions==4.10.0
35 changes: 35 additions & 0 deletions tests/integration/test_phir_64_bit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pecos.engines.hybrid_engine import HybridEngine


def bin2int(result: list[str]) -> int:
return int(result[0], base=2)


def test_setting_cvar():
phir = {
"format": "PHIR/JSON",
"version": "0.1.0",
"ops": [
{"data": "cvar_define", "data_type": "i32", "variable": "var_i32"},
{"data": "cvar_define", "data_type": "u32", "variable": "var_u32", "size": 32},
{"data": "cvar_define", "data_type": "i64", "variable": "var_i64"},
{"data": "cvar_define", "data_type": "u64", "variable": "var_u64", "size": 64},
{"data": "cvar_define", "data_type": "i32", "variable": "var_i32neg"},
{"data": "cvar_define", "data_type": "i64", "variable": "var_i64neg"},
qartik marked this conversation as resolved.
Show resolved Hide resolved
{"cop": "=", "returns": ["var_i32"], "args": [2**31 - 1]},
{"cop": "=", "returns": ["var_u32"], "args": [2**32 - 1]},
{"cop": "=", "returns": ["var_i64"], "args": [2**63 - 1]},
{"cop": "=", "returns": ["var_u64"], "args": [2**64 - 1]},
{"cop": "=", "returns": ["var_i32neg"], "args": [-(2**31)]},
{"cop": "=", "returns": ["var_i64neg"], "args": [-(2**63)]},
],
}

results = HybridEngine(qsim="stabilizer").run(program=phir, shots=5)

assert bin2int(results["var_i32"]) == 2**31 - 1
assert bin2int(results["var_u32"]) == 2**32 - 1
assert bin2int(results["var_i64"]) == 2**63 - 1
assert bin2int(results["var_u64"]) == 2**64 - 1
assert bin2int(results["var_i32neg"]) == -(2**31)
assert bin2int(results["var_i64neg"]) == -(2**63)
104 changes: 104 additions & 0 deletions tests/unit/test_binarray.py
qciaran marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from typing import Final

import numpy as np
from hypothesis import assume, given
from hypothesis import strategies as st
from pecos.engines.cvm.binarray2 import BinArray2 as BinArray

DEFAULT_SIZE: Final = 63
MIN: Final = -(2**DEFAULT_SIZE)
MAX: Final = 2**DEFAULT_SIZE - 1
int_range = st.integers(min_value=MIN, max_value=MAX)


@given(st.text(alphabet=["0", "1"], min_size=1))
def test_init(x):
ba = BinArray(x)
assert ba == f"0b{x}"


def test_set_bit():
ba = BinArray("0000")
ba[2] = 1
assert ba == 0b0100


def test_get_bit():
ba = BinArray("1010")
assert ba[2] == 0
assert ba[3] == 1


def test_to_int():
ba = BinArray("1010")
assert int(ba) == 10


@given(int_range, int_range)
def test_addition(x, y):
assume(MIN <= x + y <= MAX)
ba1 = BinArray(DEFAULT_SIZE, x)
ba2 = BinArray(DEFAULT_SIZE, y)
result = ba1 + ba2
assert int(result) == x + y


def test_subtraction():
ba1 = BinArray("1101") # 13
ba2 = BinArray("1010") # 10
result = ba1 - ba2
assert int(result) == 3


@given(int_range, int_range)
def test_multiplication(x, y):
assume(MIN <= x * y <= MAX)
ba1 = BinArray(DEFAULT_SIZE, x)
ba2 = BinArray(DEFAULT_SIZE, y)
result = ba1 * ba2
assert int(result) == x * y


def test_comparison():
ba1 = BinArray("1010") # 10
ba2 = BinArray("1010") # 10
ba3 = BinArray("1101") # 13
assert ba1 == ba2
assert ba1 != ba3
assert ba1 != ba3
assert ba1 < ba3
assert ba3 > ba1


def test_bitwise_and():
ba1 = BinArray("1010") # 10
ba2 = BinArray("1101") # 13
result = ba1 & ba2
assert result == 0b1000


def test_bitwise_or():
ba1 = BinArray("1010") # 10
ba2 = BinArray("1101") # 13
result = ba1 | ba2
assert result == 0b1111


def test_bitwise_xor():
ba1 = BinArray("1010") # 10
ba2 = BinArray("1101") # 13
result = ba1 ^ ba2
assert result == 0b0111


def test_unsigned_bitwise_not():
ba = BinArray("1010", dtype=np.uint64) # 10
result = ~ba
assert result == 0b0101


@given(int_range)
def test_signed_bitwise_not(x):
ba = BinArray(DEFAULT_SIZE, x)
result = ~ba
assert int(result) == -x - 1 # (two's complement)