Skip to content

feat: Stim improvements (more ops + state printing) - #115

Merged
jake-arkinstall merged 10 commits into
mainfrom
feat/stim-fix--theta-pi--phi-quarter-pi
Feb 12, 2026
Merged

jake-arkinstall merged 10 commits into
mainfrom
feat/stim-fix--theta-pi--phi-quarter-pi

Conversation

@jake-arkinstall

@jake-arkinstall jake-arkinstall commented Jan 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Background

On more ops

Previously we assumed that all clifford operations have angles in multiples of pi/2. There are some cases where angles can be pi/4. To take account of this, we now check angles are approximately pi/4, and use lookups to determine the gates. This is derived from the now-provided gate_definitions.py, which we will use in future as more gates are added.

On state printing

With the QuEST plugin, we support state dumping. This involves writing the internal state of the simulator to a file that can be read by the python frontend. Users can extract reduced density matrices in the space of qubits they have listed, and can also extract statevector distributions and (in the case of pure states) single statevectors.

This PR adds this equivalent functionality for the Stim simulator. Stabilizers are printed to a file as strings, and can then be pulled in and interpreted through the Stim selene plugin. One can read the full generator list directly (not recommended), extract a traced-out generator list, and fetch the density matrix and statevectors with the same interface as in QuEST.

@PabloAndresCQ PabloAndresCQ left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jake, it all looks good. There's a few minor comments that would be good to address, and other than that a few nit-picks that you are welcome to ignore.

I have not tested this yet for my use case, but I'm starting on it now. Feel free to merge though, I'm satisfied with the tests.

// | 0 | [any] | | identity regardless of phi
// +-------+-------+-----------+
// | pi/2 | 0 | V | pi/2 theta section
// | pi/2 | pi/2 | H X |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please indicate in all of these rows that you are using gate application order, rather than multiplication order; i.e. it is H then X, rather than H*X. I suggest writing it as H; X.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good. I've double checked all of the Clifford gates are implemented correctly.

return Phase((self._value - other._value) % 4)

def __neg__(self) -> "Phase":
return self + Phase.REAL_NEGATIVE

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very bugprone:

  • Doesn't do modulo addition
  • It would cause a self==REAL_NEGATIVE to become IMAGINARY_POSITIVE

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK you are not using negation anywhere... maybe just remove it?

raise ValueError(f"Unhandled Pauli multiplication: {self}, {other}")


class StabilizerGenerator:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit-pick: I would just call this a Stabilizer. Any set of stabilizers is a generator set for some stabilizer group.



class StabilizerList:
entries: list[StabilizerGenerator] # le n: specified_qubits

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit-pick: since you asked for suggestions on naming... I think the most appropriate thing to do here would be to define this as:
generators: list[Stabilizer]

An entry of the tableau, in my mind, is an element on a chosen (stabilizer, qubit) coordinate, similarly to entries (row, column) in matrices.

And as mentioned in a previous comment, any stabilizer is a generator of some group. What we care is that this particular list is our current choice of generators for the group stabilising the state. This is why I think it's more appropriate to call the variable generators rather than the type.

entries: list[StabilizerGenerator] # le n: specified_qubits

def __init__(self, stabilizer_strings: list[str]):
n_qubits_known = len(stabilizer_strings)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit-pick: this is a bit puzzling to me.

  • If you are assuming the input is a pure stabiliser state, then len(stabilizer_strings) must match with all len(stab.paulis) for each stabiliser stab. In this case, you can add that extra check and I'd just call this variable n_qubits .
  • Otherwise, if you are accepting mixed state tableaus, this is variable name is confusing... You could have stabilizer_strings = ["+ZZ"]. This is a valid mixed state 1/2 (|00><00| + |11><11|) on two qubits.

Ofc, it doesn't really matter since this variable is only used in this scope.

)
return result

def get_single_state(self) -> np.ndarray:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've dropped zero_threshold from get_single_state, get_dirac_notation and get_single_dirac_notation. Was this intentional?

],
),
ReductionExample(
# |+++++.....+++> (N_QUBITS_LARGE_EXAMPLE qubits)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# |+++++.....+++> (N_QUBITS_LARGE_EXAMPLE qubits)
# |00...0> + |11...1> (N_QUBITS_LARGE_EXAMPLE qubits)

This comment is wrong. The full state is a GHZ state, not a tensor product of |+> states.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest in this test is correct

…C api for selene's usage. Avoid usage of TableauSimulator and just drive the inner inverse tableau directly. Generate definitions using pytket (gate definitions provided in a script). Fix conformance testing framework as the cnot definition was incorrect (although observed measurements were okay, statevector prints were not)
@jake-arkinstall
jake-arkinstall force-pushed the feat/stim-fix--theta-pi--phi-quarter-pi branch from 8f2c769 to dec800f Compare February 12, 2026 13:31
@jake-arkinstall
jake-arkinstall merged commit 867d5e5 into main Feb 12, 2026
10 checks passed
@jake-arkinstall
jake-arkinstall deleted the feat/stim-fix--theta-pi--phi-quarter-pi branch February 12, 2026 14:34
jake-arkinstall pushed a commit that referenced this pull request Feb 24, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.2.5](selene-core-v0.2.4...selene-core-v0.2.5)
(2026-02-24)


### Features

* Add __version__ attributes
([#137](#137))
([379ae01](379ae01))
* Interactive use of Selene from python
([#135](#135))
([db3028d](db3028d))
* Stim improvements (more ops + state printing)
([#115](#115))
([867d5e5](867d5e5))


### Bug Fixes

* add `___barrier` to Helios QIS for QIR emulation
([#136](#136))
([dfbc4c3](dfbc4c3))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
jake-arkinstall pushed a commit that referenced this pull request Feb 24, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.2.11](selene-sim-v0.2.10...selene-sim-v0.2.11)
(2026-02-24)


### Features

* Add __version__ attributes
([#137](#137))
([379ae01](379ae01))
* Interactive use of Selene from python
([#135](#135))
([db3028d](db3028d))
* Quest performance improvements
([#133](#133))
([af640e9](af640e9))
* Stim improvements (more ops + state printing)
([#115](#115))
([867d5e5](867d5e5))
* Test on QIS snapshots
([#134](#134))
([c4d58ab](c4d58ab))


### Bug Fixes

* add `___barrier` to Helios QIS for QIR emulation
([#136](#136))
([dfbc4c3](dfbc4c3))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
jake-arkinstall pushed a commit that referenced this pull request Jun 12, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.0-alpha.1](selene-core-v0.3.0-alpha.0...selene-core-v0.3.0-alpha.1)
(2026-06-12)


### ⚠ BREAKING CHANGES

* Use struct exports for all plugin types, separate error model and
simulator ([#169](#169))
* Add handling for an additional gateset
([#119](#119))

### Features

* Add __version__ attributes
([#137](#137))
([379ae01](379ae01))
* Add handling for an additional gateset
([#119](#119))
([5180b80](5180b80))
* Add simulate_delay functionality
([#139](#139))
([cca97fa](cca97fa))
* Add support for object files provided as bytes
([#94](#94))
([c4cfac6](c4cfac6))
* add timing to builtin runtimes and batching options to softrz runtime
([#158](#158))
([049e123](049e123))
* Build improvements
([#142](#142))
([12f399b](12f399b))
* Interactive use of Selene from python
([#135](#135))
([db3028d](db3028d))
* Make interfaces shared, add ArgReader
([#171](#171))
([143e742](143e742))
* QIR support using QIR-QIS
([#114](#114))
([70ab294](70ab294))
* Result stream handling refactor
([#93](#93))
([607a55e](607a55e))
* Stim improvements (more ops + state printing)
([#115](#115))
([867d5e5](867d5e5))
* test on QIS instead of relying on the upper stack
([#150](#150))
([b80a9c4](b80a9c4))
* Traces for analytics
([#160](#160))
([24b9978](24b9978))
* Use mingw instead of msvc for windows wheels
([#143](#143))
([3d91514](3d91514))
* Use struct exports for all plugin types, separate error model and
simulator ([#169](#169))
([c44a5c9](c44a5c9))


### Bug Fixes

* add `___barrier` to Helios QIS for QIR emulation
([#136](#136))
([dfbc4c3](dfbc4c3))
* avoid using qir_major_version string for QIR detection
([#123](#123))
([deaa0dc](deaa0dc))
* classify lowered qir-qis bitcode as helios
([#157](#157))
([67fca60](67fca60))
* make qir-qis optional and stabilize CI
([#164](#164))
([e52c0b2](e52c0b2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
jake-arkinstall added a commit that referenced this pull request Jun 12, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.0-alpha.1](selene-sim-v0.3.0-alpha.0...selene-sim-v0.3.0-alpha.1)
(2026-06-12)


### ⚠ BREAKING CHANGES

* Use struct exports for all plugin types, separate error model and
simulator ([#169](#169))
* Add handling for an additional gateset
([#119](#119))

### Features

* Add __version__ attributes
([#137](#137))
([379ae01](379ae01))
* Add event hook which records measurement results
([#104](#104))
([01300ee](01300ee)),
closes [#103](#103)
* Add handling for an additional gateset
([#119](#119))
([5180b80](5180b80))
* Add simulate_delay functionality
([#139](#139))
([cca97fa](cca97fa))
* Add support for object files provided as bytes
([#94](#94))
([c4cfac6](c4cfac6))
* add timing to builtin runtimes and batching options to softrz runtime
([#158](#158))
([049e123](049e123))
* Better exception handling for parse_shots=False
([#70](#70))
([3caf530](3caf530))
* Build improvements
([#142](#142))
([12f399b](12f399b))
* Bump selene-core dependency and relax guppylang testing dependency
([#120](#120))
([6375791](6375791))
* Cleanup error'd processes before log collection
([#98](#98))
([77e698e](77e698e))
* **compiler:** Bump tket version; add wasm + gpu to the hugr-qis
registry
([c69155d](c69155d))
* correct shot end strategy and error processing
([#91](#91))
([93eaeb0](93eaeb0))
* Emit a nicer error when trying to emulate unsupported pytket ops
([#72](#72))
([d88a28a](d88a28a))
* Fine-grained timeout configuration
([#69](#69))
([072842e](072842e))
* Interactive use of Selene from python
([#135](#135))
([db3028d](db3028d))
* Make interfaces shared, add ArgReader
([#171](#171))
([143e742](143e742))
* QIR support using QIR-QIS
([#114](#114))
([70ab294](70ab294))
* Quest performance improvements
([#133](#133))
([af640e9](af640e9))
* random_advance ([#55](#55))
([974b496](974b496))
* Record simulated delays in event hooks
([#154](#154))
([608884a](608884a))
* Result stream handling refactor
([#93](#93))
([607a55e](607a55e))
* Stim improvements (more ops + state printing)
([#115](#115))
([867d5e5](867d5e5))
* Support state-dump passthrough on quantum replay simulator
([#108](#108))
([1b01a01](1b01a01))
* test on QIS instead of relying on the upper stack
([#150](#150))
([b80a9c4](b80a9c4))
* Test on QIS snapshots
([#134](#134))
([c4d58ab](c4d58ab))
* Traces for analytics
([#160](#160))
([24b9978](24b9978))
* update to tket-qsystem 0.20
([#66](#66))
([7191b07](7191b07))
* Use mingw instead of msvc for windows wheels
([#143](#143))
([3d91514](3d91514))
* Use SeleneStartupError for errors before shot_start
([#170](#170))
([85c2907](85c2907))
* Use struct exports for all plugin types, separate error model and
simulator ([#169](#169))
([c44a5c9](c44a5c9))


### Bug Fixes

* add `___barrier` to Helios QIS for QIR emulation
([#136](#136))
([dfbc4c3](dfbc4c3))
* Add ENDING to allowed shot state on receiving meta information
([#100](#100))
([fc4d673](fc4d673))
* Add numpy dependency back
([#127](#127))
([c9a01eb](c9a01eb))
* avoid using qir_major_version string for QIR detection
([#123](#123))
([deaa0dc](deaa0dc))
* classify lowered qir-qis bitcode as helios
([#157](#157))
([67fca60](67fca60))
* **compiler:** error when entrypoint has arguments
([#84](#84))
([604b131](604b131))
* **compiler:** update tket-qystem to fix CZ bug
([#78](#78))
([3991f11](3991f11))
* correct post_runtime duration metric
([#74](#74))
([0bef66a](0bef66a))
* make qir-qis optional and stabilize CI
([#164](#164))
([e52c0b2](e52c0b2))


### Documentation

* add repository citation guidance and metadata
([#146](#146))
([880a58b](880a58b)),
closes [#145](#145)
* Correct URL to guppy repository
([#121](#121))
([27ded00](27ded00))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: Jake Arkinstall <65358059+jake-arkinstall@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add state_result support for Stim

2 participants