Skip to content

Commit 7054c9b

Browse files
committed
add seeding to the Python wrappers of the stabilizer simulators
1 parent e23cecd commit 7054c9b

5 files changed

Lines changed: 105 additions & 6 deletions

File tree

python/pecos-rslib/pecos_rslib.pyi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -771,8 +771,9 @@ class GateBindingsDict:
771771
class SparseSim:
772772
"""Sparse stabilizer simulator."""
773773

774-
def __init__(self, num_qubits: int) -> None: ...
774+
def __init__(self, num_qubits: int, seed: int | None = None) -> None: ...
775775
def reset(self) -> SparseSim: ...
776+
def set_seed(self, seed: int) -> None: ...
776777
@property
777778
def num_qubits(self) -> int: ...
778779
@property
@@ -788,8 +789,9 @@ class SparseSim:
788789
class Stab:
789790
"""Generic stabilizer simulator (recommended)."""
790791

791-
def __init__(self, num_qubits: int) -> None: ...
792+
def __init__(self, num_qubits: int, seed: int | None = None) -> None: ...
792793
def reset(self) -> Stab: ...
794+
def set_seed(self, seed: int) -> None: ...
793795
@property
794796
def num_qubits(self) -> int: ...
795797
def stab_tableau(self) -> str: ...

python/pecos-rslib/src/sparse_stab_bindings.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ pub struct PySparseSim {
2424
#[pymethods]
2525
impl PySparseSim {
2626
#[new]
27-
fn new(num_qubits: usize) -> Self {
27+
#[pyo3(signature = (num_qubits, seed=None))]
28+
fn new(num_qubits: usize, seed: Option<u64>) -> Self {
2829
PySparseSim {
29-
inner: SparseStab::new(num_qubits),
30+
inner: match seed {
31+
Some(s) => SparseStab::with_seed(num_qubits, s),
32+
None => SparseStab::new(num_qubits),
33+
},
3034
}
3135
}
3236

@@ -40,6 +44,10 @@ impl PySparseSim {
4044
self.inner.num_qubits()
4145
}
4246

47+
fn set_seed(&mut self, seed: u64) {
48+
self.inner.set_seed(seed);
49+
}
50+
4351
#[allow(clippy::too_many_lines)]
4452
#[pyo3(signature = (symbol, location, params=None))]
4553
fn run_1q_gate(

python/pecos-rslib/src/stab_bindings.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ pub struct PyStab {
2424
#[pymethods]
2525
impl PyStab {
2626
#[new]
27-
fn new(num_qubits: usize) -> Self {
27+
#[pyo3(signature = (num_qubits, seed=None))]
28+
fn new(num_qubits: usize, seed: Option<u64>) -> Self {
2829
PyStab {
29-
inner: Stab::new(num_qubits),
30+
inner: match seed {
31+
Some(s) => Stab::with_seed(num_qubits, s),
32+
None => Stab::new(num_qubits),
33+
},
3034
}
3135
}
3236

@@ -40,6 +44,10 @@ impl PyStab {
4044
self.inner.num_qubits()
4145
}
4246

47+
fn set_seed(&mut self, seed: u64) {
48+
self.inner.set_seed(seed);
49+
}
50+
4351
#[allow(clippy::too_many_lines)]
4452
#[pyo3(signature = (symbol, location, params=None))]
4553
fn run_1q_gate(
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright 2026 The PECOS Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4+
# in compliance with 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
9+
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10+
# or implied. See the License for the specific language governing permissions and limitations under
11+
# the License.
12+
13+
"""Tests for exposing simulator seeding on stabilizer backends."""
14+
15+
import pytest
16+
17+
from pecos_rslib import SparseSim, Stab
18+
19+
20+
def _measurement_sequence(sim_cls, *, seed=None, reseed=None, rounds=32):
21+
sim = sim_cls(1, seed=seed) if seed is not None else sim_cls(1)
22+
if reseed is not None:
23+
sim.set_seed(reseed)
24+
25+
outcomes = []
26+
for _ in range(rounds):
27+
sim.reset()
28+
sim.run_1q_gate("H", 0)
29+
outcomes.append(sim.run_1q_gate("MZ", 0))
30+
return outcomes
31+
32+
33+
@pytest.mark.parametrize("sim_cls", [SparseSim, Stab])
34+
def test_seeded_constructor_repeats_measurement_sequence(sim_cls) -> None:
35+
assert _measurement_sequence(sim_cls, seed=42) == _measurement_sequence(sim_cls, seed=42)
36+
37+
38+
@pytest.mark.parametrize("sim_cls", [SparseSim, Stab])
39+
def test_set_seed_repeats_measurement_sequence(sim_cls) -> None:
40+
assert _measurement_sequence(sim_cls, reseed=42) == _measurement_sequence(sim_cls, reseed=42)
41+
42+
43+
@pytest.mark.parametrize("sim_cls", [SparseSim, Stab])
44+
def test_different_seeds_change_measurement_sequence(sim_cls) -> None:
45+
assert _measurement_sequence(sim_cls, seed=42) != _measurement_sequence(sim_cls, seed=43)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2026 The PECOS Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4+
# in compliance with 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
9+
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10+
# or implied. See the License for the specific language governing permissions and limitations under
11+
# the License.
12+
13+
"""High-level tests for seeded stabilizer simulator re-exports."""
14+
15+
import pytest
16+
17+
from pecos.simulators import SparseSim, Stab
18+
19+
20+
def _measurement_sequence(sim_cls, *, seed=None, reseed=None, rounds=16):
21+
sim = sim_cls(1, seed=seed) if seed is not None else sim_cls(1)
22+
if reseed is not None:
23+
sim.set_seed(reseed)
24+
25+
outcomes = []
26+
for _ in range(rounds):
27+
sim.reset()
28+
sim.run_1q_gate("H", 0)
29+
outcomes.append(sim.run_1q_gate("MZ", 0))
30+
return outcomes
31+
32+
33+
@pytest.mark.parametrize("sim_cls", [SparseSim, Stab])
34+
def test_high_level_simulators_accept_seed_and_set_seed(sim_cls) -> None:
35+
assert _measurement_sequence(sim_cls, seed=42) == _measurement_sequence(sim_cls, seed=42)
36+
assert _measurement_sequence(sim_cls, reseed=42) == _measurement_sequence(sim_cls, reseed=42)

0 commit comments

Comments
 (0)