From 10f399fb2d69ba31fc62d1403eba85d3d7705a84 Mon Sep 17 00:00:00 2001 From: Thierry Martinez Date: Thu, 5 Mar 2026 18:26:19 +0100 Subject: [PATCH 1/2] Allow `None` in simulator's `input_state` type The `run` method handles `None` and doesn't add input nodes to the backend in this case, assuming that the backend has already been prepared by the caller. However, the type annotation prevented using `None` as an `input_state` value. ```python if input_state is not None: self.backend.add_nodes(self.pattern.input_nodes, input_state) ``` --- graphix/simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphix/simulator.py b/graphix/simulator.py index 19e58eff..d3b14a2d 100644 --- a/graphix/simulator.py +++ b/graphix/simulator.py @@ -355,7 +355,7 @@ def measure_method(self) -> MeasureMethod: """Return the measure method.""" return self.__measure_method - def run(self, input_state: Data = BasicStates.PLUS, rng: Generator | None = None) -> None: + def run(self, input_state: Data | None = BasicStates.PLUS, rng: Generator | None = None) -> None: """Perform the simulation. Returns From 4addf276a1ec042247b9d3d5bb9cd130210b51d3 Mon Sep 17 00:00:00 2001 From: Thierry Martinez Date: Fri, 6 Mar 2026 12:57:27 +0100 Subject: [PATCH 2/2] Allow `Matrix` in `is_close` and `None` in `simulate_pattern` --- CHANGELOG.md | 2 ++ graphix/pattern.py | 13 ++++++++----- graphix/sim/statevec.py | 8 ++++---- tests/test_statevec.py | 8 +++----- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf672471..e34882fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #455: Causal-flow finding algorithm (`graphix.flow._find_cflow.py`) does not raise `RecursionError` now. +- #458: The type for the `input_state` parameter in the simulator now allows `None` to indicate that the input qubits have already been specified in the backend. + ### Changed - #181, #423: Structural separation of Pauli measurements diff --git a/graphix/pattern.py b/graphix/pattern.py index 5e26933d..ec82f9c0 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1332,7 +1332,8 @@ def simulate_pattern( | Statevec | Iterable[State] | Iterable[ExpressionOrSupportsComplex] - | Iterable[Iterable[ExpressionOrSupportsComplex]] = ..., + | Iterable[Iterable[ExpressionOrSupportsComplex]] + | None = ..., rng: Generator | None = ..., **kwargs: Any, ) -> Statevec: ... @@ -1345,7 +1346,8 @@ def simulate_pattern( | DensityMatrix | Iterable[State] | Iterable[ExpressionOrSupportsComplex] - | Iterable[Iterable[ExpressionOrSupportsComplex]] = ..., + | Iterable[Iterable[ExpressionOrSupportsComplex]] + | None = ..., rng: Generator | None = ..., **kwargs: Any, ) -> DensityMatrix: ... @@ -1357,7 +1359,8 @@ def simulate_pattern( input_state: State | Iterable[State] | Iterable[ExpressionOrSupportsComplex] - | Iterable[Iterable[ExpressionOrSupportsComplex]] = ..., + | Iterable[Iterable[ExpressionOrSupportsComplex]] + | None = ..., rng: Generator | None = ..., **kwargs: Any, ) -> MBQCTensorNet: ... @@ -1366,7 +1369,7 @@ def simulate_pattern( def simulate_pattern( self, backend: Backend[_StateT_co], - input_state: Data = ..., + input_state: Data | None = ..., rng: Generator | None = ..., **kwargs: Any, ) -> _StateT_co: ... @@ -1374,7 +1377,7 @@ def simulate_pattern( def simulate_pattern( self, backend: Backend[_StateT_co] | _BackendLiteral = "statevector", - input_state: Data = BasicStates.PLUS, + input_state: Data | None = BasicStates.PLUS, rng: Generator | None = None, **kwargs: Any, ) -> _StateT_co | _BuiltinBackendState: diff --git a/graphix/sim/statevec.py b/graphix/sim/statevec.py index ea7abe1a..d106031b 100644 --- a/graphix/sim/statevec.py +++ b/graphix/sim/statevec.py @@ -388,14 +388,14 @@ def expectation_value(self, op: Matrix, qargs: Sequence[int]) -> complex: st1.evolve(op, qargs) return complex(np.dot(st2.psi.flatten().conjugate(), st1.psi.flatten())) - def fidelity(self, other: Statevec) -> float: + def fidelity(self, other: Matrix | Statevec) -> float: r"""Calculate the fidelity against another statevector. The fidelity is defined as :math:`|\langle\psi_1|\psi_2\rangle|^2`. Parameters ---------- - other : :class:`Statevec` + other : :class:`Matrix` | :class:`Statevec` statevector to compare with Returns @@ -406,14 +406,14 @@ def fidelity(self, other: Statevec) -> float: inner = np.dot(self.flatten().conjugate(), other.flatten()) return float(np.abs(inner) ** 2) - def isclose(self, other: Statevec, *, rtol: float = 1e-09, atol: float = 0.0) -> bool: + def isclose(self, other: Matrix | Statevec, *, rtol: float = 1e-09, atol: float = 0.0) -> bool: """Check if two quantum states are equal up to global phase. Two states are considered close if their fidelity is close to 1. Parameters ---------- - other : :class:`Statevec` + other : :class:`Matrix` | :class:`Statevec` statevector to compare with rtol : float relative tolerance for :func:`math.isclose` diff --git a/tests/test_statevec.py b/tests/test_statevec.py index 6593c8dd..9b064a49 100644 --- a/tests/test_statevec.py +++ b/tests/test_statevec.py @@ -193,17 +193,15 @@ def test_isclose_same_state(self) -> None: def test_isclose_orthogonal(self) -> None: zero = Statevec(data=BasicStates.ZERO) - one = Statevec(data=BasicStates.ONE) - assert not zero.isclose(one) + assert not zero.isclose(BasicStates.ONE.to_statevector()) def test_isclose_global_phase(self) -> None: plus = Statevec(data=BasicStates.PLUS) - rotated = Statevec(data=np.array([1, 1]) / np.sqrt(2) * np.exp(1j * 0.7)) - assert plus.isclose(rotated) + assert plus.isclose(np.array([1, 1]) / np.sqrt(2) * np.exp(1j * 0.7)) def test_isclose_tolerance(self) -> None: zero = Statevec(data=BasicStates.ZERO) - almost = Statevec(data=np.array([np.sqrt(1 - 1e-8), np.sqrt(1e-8)])) + almost = np.array([np.sqrt(1 - 1e-8), np.sqrt(1e-8)]) assert not zero.isclose(almost) assert zero.isclose(almost, atol=1e-6)