Skip to content
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
280 changes: 143 additions & 137 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ decoder-cache-clean: ## Clean decoder download cache
pytest: ## Run tests on the Python package (not including optional dependencies). ASSUMES: previous build command
uv run pytest ./python/tests/ --doctest-modules -m "not optional_dependency"
uv run pytest ./python/pecos-rslib/tests/
uv run pytest ./python/slr-tests/ -m "not optional_dependency"

.PHONY: pytest-dep
pytest-dep: ## Run tests on the Python package only for optional dependencies. ASSUMES: previous build command
Expand Down
6 changes: 5 additions & 1 deletion crates/pecos-qasm/tests/general_noise_builder_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ fn test_general_noise_builder_with_pauli_models() {

let noise_model = NoiseModelType::General(Box::new(noise_builder));

let results = qasm_sim(qasm).noise(noise_model).run(1000).unwrap();
let results = qasm_sim(qasm)
.seed(42)
.noise(noise_model)
.run(1000)
.unwrap();

let shot_map = results.try_as_shot_map().unwrap();
let values = shot_map.try_bits_as_u64("c").unwrap();
Expand Down
220 changes: 220 additions & 0 deletions examples/Steane code to guppy.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "df9aa642-5320-4134-a41d-b6f45f626245",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"from __future__ import annotations\n",
"\n",
"from typing import no_type_check\n",
"\n",
"from guppylang.decorator import guppy\n",
"from guppylang.std import quantum\n",
"from guppylang.std.quantum import qubit\n",
"from guppylang.std.quantum.functional import (reset, h, x, y, z, s, t, sdg, tdg, cx, cy, cz)\n",
"from guppylang.std.builtins import array, owned, result, py\n",
"\n",
"@guppy.struct\n",
"@no_type_check\n",
"class steane_struct:\n",
" c: array[bool, 32]\n",
" d: array[qubit, 7]\n",
" flag_x: array[bool, 3]\n",
" flags: array[bool, 3]\n",
" flags_z: array[bool, 3]\n",
" last_raw_syn_x: array[bool, 32]\n",
" last_raw_syn_z: array[bool, 32]\n",
" raw_meas: array[bool, 7]\n",
" scratch: array[bool, 32]\n",
" syn_meas: array[bool, 32]\n",
" syn_x: array[bool, 3]\n",
" syn_z: array[bool, 3]\n",
" syndromes: array[bool, 3]\n",
" verify_prep: array[bool, 32]\n",
"\n",
"\n",
"# === Qubit Allocation Optimization Report ===\n",
"# Array: c_d (size: 7)\n",
"# Strategy: pre_allocate\n",
"# Array: c_a (size: 3)\n",
"# Strategy: pre_allocate\n",
"# - Element 1 allocated but not used in operations\n",
"# - Element 2 allocated but not used in operations\n",
"# - Some elements are reused after consumption\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def main() -> None:\n",
" # Pre-allocate ancilla array c_a (used as function parameter)\n",
" c_a = array(quantum.qubit() for _ in range(3))\n",
" c_d = array(quantum.qubit() for _ in range(7))\n",
" c_c = array(False for _ in range(32))\n",
" c_syn_meas = array(False for _ in range(32))\n",
" c_last_raw_syn_x = array(False for _ in range(32))\n",
" c_last_raw_syn_z = array(False for _ in range(32))\n",
" c_scratch = array(False for _ in range(32))\n",
" c_flag_x = array(False for _ in range(3))\n",
" c_flags_z = array(False for _ in range(3))\n",
" c_flags = array(False for _ in range(3))\n",
" c_raw_meas = array(False for _ in range(7))\n",
" c_syn_x = array(False for _ in range(3))\n",
" c_syn_z = array(False for _ in range(3))\n",
" c_syndromes = array(False for _ in range(3))\n",
" c_verify_prep = array(False for _ in range(32))\n",
" c = steane_struct(c_c, c_d, c_flag_x, c_flags, c_flags_z, c_last_raw_syn_x, c_last_raw_syn_z, c_raw_meas, c_scratch, c_syn_meas, c_syn_x, c_syn_z, c_syndromes, c_verify_prep)\n",
" # Skip unpacking c_d - consumed by struct constructor\n",
" c, c_a = steane_prep_rus(c, c_a)\n",
" # Decompose struct c for cleanup\n",
" c_c_final, c_d_final, c_flag_x_final, c_flags_final, c_flags_z_final, c_last_raw_syn_x_final, c_last_raw_syn_z_final, c_raw_meas_final, c_scratch_final, c_syn_meas_final, c_syn_x_final, c_syn_z_final, c_syndromes_final, c_verify_prep_final = steane_decompose(c)\n",
" result(\"c_c\", c_c_final)\n",
" result(\"c_syn_meas\", c_syn_meas_final)\n",
" result(\"c_last_raw_syn_x\", c_last_raw_syn_x_final)\n",
" result(\"c_last_raw_syn_z\", c_last_raw_syn_z_final)\n",
" result(\"c_scratch\", c_scratch_final)\n",
" result(\"c_flag_x\", c_flag_x_final)\n",
" result(\"c_flags_z\", c_flags_z_final)\n",
" result(\"c_flags\", c_flags_final)\n",
" result(\"c_raw_meas\", c_raw_meas_final)\n",
" result(\"c_syn_x\", c_syn_x_final)\n",
" result(\"c_syn_z\", c_syn_z_final)\n",
" result(\"c_syndromes\", c_syndromes_final)\n",
" result(\"c_verify_prep\", c_verify_prep_final)\n",
" # Discard quantum fields from c\n",
" quantum.discard_array(c_d_final)\n",
" # Note: struct c contains unconsumed quantum arrays\n",
" # Discard c_a\n",
" quantum.discard_array(c_a)\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def steane_decompose(c: steane_struct @owned) -> tuple[array[bool, 32], array[qubit, 7], array[bool, 3], array[bool, 3], array[bool, 3], array[bool, 32], array[bool, 32], array[bool, 7], array[bool, 32], array[bool, 32], array[bool, 3], array[bool, 3], array[bool, 3], array[bool, 32]]:\n",
" return c.c, c.d, c.flag_x, c.flags, c.flags_z, c.last_raw_syn_x, c.last_raw_syn_z, c.raw_meas, c.scratch, c.syn_meas, c.syn_x, c.syn_z, c.syndromes, c.verify_prep\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def steane_discard(c: steane_struct @owned) -> None:\n",
" quantum.discard_array(c.d)\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def steane_prep_rus(c: steane_struct @owned, c_a: array[quantum.qubit, 3] @owned) -> tuple[steane_struct, array[quantum.qubit, 3]]:\n",
" # Unpack ancilla array c_a to avoid MoveOutOfSubscriptError with @owned\n",
" c_a_0, c_a_1, c_a_2 = c_a\n",
" c_a = array(c_a_0, c_a_1, c_a_2)\n",
" c, c_a = steane_prep_encoding_ft_zero(c, c_a)\n",
" for _ in range(2):\n",
" # Extract condition variable to avoid @owned struct field access in loop\n",
" verify_prep_0_extracted = c.verify_prep[0]\n",
" if verify_prep_0_extracted:\n",
" c, c_a = steane_prep_encoding_ft_zero(c, c_a)\n",
" log_zero_rot()\n",
" return c, c_a\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def steane_prep_encoding_ft_zero(c: steane_struct @owned, c_a: array[quantum.qubit, 3] @owned) -> tuple[steane_struct, array[quantum.qubit, 3]]:\n",
" # Unpack ancilla array c_a to avoid MoveOutOfSubscriptError with @owned\n",
" c_a_0, c_a_1, c_a_2 = c_a\n",
" for i in range(0, 7):\n",
" quantum.reset(c.d[i])\n",
" quantum.reset(c_a_0)\n",
" c = steane_prep_encoding_non_ft_zero(c)\n",
" c_a = array(c_a_0, c_a_1, c_a_2)\n",
" c, c_a = steane_prep_zero_verify(c, c_a)\n",
" return c, c_a\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def log_zero_rot() -> None:\n",
" pass\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def steane_prep_encoding_non_ft_zero(c: steane_struct @owned) -> steane_struct:\n",
" quantum.h(c.d[0])\n",
" quantum.h(c.d[4])\n",
" quantum.h(c.d[6])\n",
" quantum.cx(c.d[4], c.d[5])\n",
" quantum.cx(c.d[0], c.d[1])\n",
" quantum.cx(c.d[6], c.d[3])\n",
" quantum.cx(c.d[4], c.d[2])\n",
" quantum.cx(c.d[6], c.d[5])\n",
" quantum.cx(c.d[0], c.d[3])\n",
" quantum.cx(c.d[4], c.d[1])\n",
" quantum.cx(c.d[3], c.d[2])\n",
" return c\n",
"\n",
"@guppy\n",
"@no_type_check\n",
"def steane_prep_zero_verify(c: steane_struct @owned, c_a: array[quantum.qubit, 3] @owned) -> tuple[steane_struct, array[quantum.qubit, 3]]:\n",
" # Unpack ancilla array c_a to avoid MoveOutOfSubscriptError with @owned\n",
" c_a_0, c_a_1, c_a_2 = c_a\n",
" # verification step\n",
" quantum.cx(c.d[5], c_a_0)\n",
" quantum.cx(c.d[1], c_a_0)\n",
" quantum.cx(c.d[3], c_a_0)\n",
" c.verify_prep[0] = quantum.measure(c_a_0)\n",
" c_a_0 = quantum.qubit()\n",
" return c, array(c_a_0, c_a_1, c_a_2)\n"
]
}
],
"source": [
"from pecos.qeclib.steane.steane_class import Steane\n",
"from pecos.slr import Main, SlrConverter\n",
"\n",
"prog = Main(\n",
" c := Steane(\"c\"),\n",
" c.pz(),\n",
")\n",
"\n",
"print(SlrConverter(prog).guppy())"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "4d68f6f5-4688-4d3b-bcd5-326088981c6c",
"metadata": {},
"outputs": [],
"source": [
"hugr = SlrConverter(prog).hugr()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "96b4e356-c240-407f-8df9-8a49045710cd",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
29 changes: 19 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
name = "pecos-workspace"
version = "0.7.0.dev4"
dependencies = [
"wasmer~=1.1.0",
"wasmer-compiler-cranelift~=1.1.0",
# Note: guppylang is an optional dependency in quantum-pecos
# Don't include it here as a direct dependency
]

[tool.uv.workspace]
Expand All @@ -12,17 +12,20 @@ members = [
"python/quantum-pecos"
]

[tool.uv.sources]
# guppylang = { path = "../guppylang" } # Commented out to use packaged version

[dependency-groups]
dev = [
"maturin>=1.2,<2.0", # For building (should match build requirements)
"setuptools>=62.6", # Build system
"pre-commit", # Git hooks
"black", # Code formatting
"ruff", # Fast Python linting
"mkdocs", # Documentation framework
"mkdocs-material", # Material theme for MkDocs
"maturin>=1.2,<2.0", # For building (should match build requirements)
"setuptools>=62.6", # Build system
"pre-commit", # Git hooks
"black", # Code formatting
"ruff", # Fast Python linting
"mkdocs", # Documentation framework
"mkdocs-material", # Material theme for MkDocs
"mkdocstrings[python]", # Code documentation extraction
"markdown-exec[ansi]", # Executable markdown blocks
"markdown-exec[ansi]", # Executable markdown blocks
# Runtime dependencies for development
"numpy>=1.15.0",
"scipy>=1.1.0",
Expand All @@ -31,6 +34,7 @@ dev = [
"phir>=0.3.3",
# WebAssembly runtimes for testing
"wasmtime>=13.0",
"jupyter>=1.1.1",
]
test = [ # pinning testing environment
"pytest==8.3.3", # 8.3.4 seems to be causing errors
Expand All @@ -41,6 +45,11 @@ test = [ # pinning testing environment
[tool.uv]
default-groups = ["dev", "test"]

# Override dependencies to ensure correct versions
override-dependencies = [
"hugr==0.13.0",
]

[tool.pytest.ini_options]
markers = [
"optional_dependency: mark a test as using one or more optional dependencies",
Expand Down
Binary file not shown.
6 changes: 6 additions & 0 deletions python/quantum-pecos/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ repository = "https://github.com/PECOS-packages/PECOS"
qir = [
"llvmlite==0.43.0; python_version < '3.13'"
]
guppy = [
"guppylang>=0.21.0", # Install guppylang first
"selene-sim~=0.2.0", # Then selene-sim (dependency of guppylang)
"hugr>=0.13.0,<0.14", # Use stable version compatible with guppylang
]
projectq = [ # State-vector sims using ProjectQ
"pybind11>=2.2.3",
"projectq>=0.5",
Expand All @@ -82,6 +87,7 @@ all = [
"quantum-pecos[wasm-all]",
"quantum-pecos[visualization]",
"quantum-pecos[qir]",
"quantum-pecos[guppy]",
]
# The following only work for some environments/Python versions:
qulacs = [ # State-vector sims using Qulacs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def __init__(
ValueError: If provided ancilla register has fewer than 3 qubits.
"""
super().__init__()
# Set the source class for code generation
self.source_class = self.__class__.__name__
self.check_indices = [[2, 1, 3, 0], [5, 2, 1, 4], [6, 5, 2, 3]]

self.d = QReg(f"{name}_d", 7)
Expand Down
3 changes: 3 additions & 0 deletions python/quantum-pecos/src/pecos/slr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from pecos.slr.block import Block
from pecos.slr.cond_block import If, Repeat
from pecos.slr.loop_block import For, While
from pecos.slr.main import Main
from pecos.slr.main import Main as SLR # noqa: N814
from pecos.slr.misc import Barrier, Comment, Parallel, Permute
Expand All @@ -24,6 +25,7 @@
"Block",
"CReg",
"Comment",
"For",
"If",
"Main",
"Parallel",
Expand All @@ -33,4 +35,5 @@
"Repeat",
"SlrConverter",
"Vars",
"While",
]
12 changes: 11 additions & 1 deletion python/quantum-pecos/src/pecos/slr/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@
class Block(Node):
"""A collection of other operations and blocks."""

def __init__(self, *args, ops=None, vargs=None, allow_no_ops=True) -> None:
def __init__(
self,
*args,
ops=None,
vargs=None,
allow_no_ops=True,
block_name=None,
) -> None:
self.ops = []
self.vars = Vars()
# Preserve the original block type name for code generation
self.block_name = block_name or self.__class__.__name__
self.block_module = self.__class__.__module__

if args and ops:
msg = "Can not use both *args for ops and the ops keyword argument."
Expand Down
9 changes: 9 additions & 0 deletions python/quantum-pecos/src/pecos/slr/gen_codes/gen_guppy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Guppy code generation for SLR programs.

This module provides the entry point for Guppy code generation.
The actual implementation is in the guppy/ subdirectory.
"""

from pecos.slr.gen_codes.guppy import GuppyGenerator

__all__ = ["GuppyGenerator"]
Loading
Loading