Skip to content

QRE Update#3090

Open
msoeken wants to merge 50 commits intomainfrom
feature/qre
Open

QRE Update#3090
msoeken wants to merge 50 commits intomainfrom
feature/qre

Conversation

@msoeken
Copy link
Copy Markdown
Member

@msoeken msoeken commented Apr 2, 2026

No description provided.

msoeken and others added 30 commits January 15, 2026 20:45
This pull request sets up the integration of QRE into the code base. In
the first PR it defines classes and functions to create ISAs and ISA
requirements.
This implements ISA transforms that can provide new ISAs from existing
ones. It also builds data structures to define ISA queries that can
combine transformations and their parameters to define the ISA
exploration space.
Co-authored-by: Mathias Soeken <mathias.soeken@outlook.com>
These are data structures for traces, transform on traces, and Pareto
frontiers to store estimation results. The `Trace` struct also
implements an estimate function to estimate a trace with respect to an
ISA. The two current trace transformations are PSSPC and LatticeSurgery.

The trace API is considered final for now, whereas the implementation
might still change.

Instruction IDs are now provided by the Rust crate, the updates for the
corresponding IDs in the Python package happen in an upcoming PR to keep
this PR in a reasonable size.
#2912)

Updated the Hypergraph class to support edge partitioning (coloring)
* Added a function to perform (multiple trials) of greedy edge coloring
   * Added two simple 1d lattices: a open string (chain) and a ring

Added/updated tests.

---------

Co-authored-by: Mathias Soeken <mathias.soeken@outlook.com>
This is the skeleton implementations for Trotter-Suzuki expansions
    * TrotterStep class wraps the basic functionality of a Trotter step
* StrangStep class subclasses TrotterStep for the (second-order)
symmetric Trotter step
* TrotterExpansion wraps the basic functionality of a full Trotter
expansion
    * Test file included.

This PR is disjoint from #2912.

---------

Co-authored-by: Mathias Soeken <mathias.soeken@outlook.com>
This is the base class for a magnet model.
* Draws on the geometry from the Hypergraph class (base class for
geometries)
* Only implements simple modification of the Hamiltonian
* Test file included
This PR is disjoint from #2912  and #2913

---------

Co-authored-by: Mathias Soeken <mathias.soeken@outlook.com>
Added more basic hypergraphs to the geometies library.
   * Patch2D and Torus2D: two dimensional analogues of chains and rings
* Compete graphs: the base geometry for the Sherrington–Kirkpatrick
model
* Complete bipartite graphs: useful to create models with hidden
frustration

The complete graph does not have edge partitioning implemented (this is
a bit tricky)
   * All others have edge partitioning
* In the case of complete bipartite graphs it still needs to be tested,
and comments added

Basic tests added
This introduces ISA queries and trace queries in the Python API. With
that one can implement a preliminary implementation for the central
`estimate` function which can enumerate traces and ISA using these
queries and perform estimation. It also moves some of the preliminary
models from the test code to proper places in the API
(`qsharp.qre.models`) with `AQREGateBased` as first architecture and
`SurfaceCode` as first QEC model. Further it defines the application
base class with a `QSharpApplication` as first implementation of it.
Changed how edge coloring is implemented:

- Old: self.part was a list of lists indicating the partitions of the
edges.
- New: self.color is a dictionary keyed on the vertices of the edge with
values indicating the "color" or which part the edge belongs.
- Property ncolors gives the number of parts (colors) in the partition 

Convention: self loops are all called "color" -1. Remaining colors are
0, 1, ....

- Automatic indexing (creating lists of parts) then places the self
loops at the end (index -1).

Updated all the examples of graphs to using this coloring implementation

- Chain1D, Ring1D, Patch2D, Torus2D, and CompleteBipartiteGraph have
only minor changes.
- CompleteGraph has a new implementation of edge coloring.

All test files updated.
… recursion (#2926)

Changed some basic functionality:

- The TrotterStep class is now just a wrapper class with some
information functions and an iterator
- Factory functions instantiate TrotterStep classes at desired orders
(trotter_decomposition, strang_splitting)

Added recursion

- Both the Suzuki fractal recursion and Yoshide triple recursion are
implemented (suzuki_recursion, yoshide_recursion)
- fourth_order_trotter_suzuki is a convenience function for
strang_splitting composed with suzuki_recursion

Tests added.

This PR is independent from #2925.

---------

Co-authored-by: Mathias Soeken <mathias.soeken@outlook.com>
This PR provides some support for upcoming models by providing new
features to ISAs and the Rust bindings in general
- ISA instructions can have properties (e.g., the `LATTICE_SURGERY`
instruction can have a distance property if it was generated by a
surface code)
- ISA requirement constraints can check if some properties are set,
e.g., one can require that the `LATTICE_SURGERY` instruction also
provides a `distance` property.
- Variable arity instructions can provide space, time, and error_rate
functions as generic Python functions. This can be slower than the
built-in functions `const`, `linear`, and `block_linear`, but useful for
cases that do not have an easy structure. The new function is called
`generic` (`generic_function` in the API)
- We expose a `binom_ppf` functions that works like `scipy.binom.ppf`
and uses the `probability` crate that is already a dependency. This is
not only faster but also does not require an additional dependency. It
will be used to model round-based distillation factories.

I missed to address some comments in the last PR. This is now done here.
The main change of this PR is the introduction of a provenance graph
that keeps track of how instructions in ISAs are build from other
instructions from other ISAs down to the architecture's ISA. We can then
build an `InstructionSource` (also a graph) that describes the
dependencies with respect to optimal resource estimation results such
that we can track which instructions were used to estimate the trace and
what properties they have.

Other changes include:
- A function `instruction_name` to turn an instruction ID into a name,
e.g. `instruction_name(CNOT) == "CNOT"`, as well as using them in
`__str__` (`Display`) implementations for ISAs and traces.
- Prefixing some Rust bindings with an underscore when exported to the
Python package to emphasize that they are private and are not exported
by the `qsharp.qre` module.
This PR cleans up the way properties are handled for traces and results.
Trace properties can be assigned by application generators and trace
transforms and are stored in optimal resource estimation results. The
Python API automatically detects whether the property value is `bool`,
`int`, `float`, or `str` and does not require a dedicated property class
exposed from Rust.

Some other changes include
- Fixes a deadlock due to the Python GIL
- Allows parallel trace generation from application generators
- Reorganizes code around Q# application generators
- Sets up resource estimation for memory qubits
Major modifications to the internal structure of the Model class.

- Coefficients of the model are now a dictionary keyed by the vertex
tuple of the (hyper)edges.
- API more closely matches QREv3 (still needs some work).
- Standard translation-invariant Ising model is implemented as a factory
function that builds Model instances.
- Tests included.
Added a utilities sublibrary.
   * Moved Hypergraph and Hyperedge to utilities
* Added Pauli and PauliString classes (primitives around Cirq classes)
   * Modified Model class to use PauliStrings
   * Modified tests
This PR adds various models:
- Qubits
  - Majorana
- Error correcting codes
  - ThreeAux
  - Yoked surface code
- Factories
  - Round-based factory
  - Litinski19 factory

It also makes some updates to the surface code model and improves
documentation. Further, it adds a large test suite for all the models.
Some of these tests are very detailed but they will help us to spot if
we introduce inconsistencies with future code changes.

Besides that there are some fixes to the Python API:
- Adds caching to Q# to Trace transformation
- InstructionFrontier can be 2D or 3D (without and with error rates)
- Instance enumeration can also consider nested types and union types
now
- EstimationResult can be used from the Python API to create new results
Issue: different terms in a Trotter expansion may need different
coloring of the edges. So the coloring is not an intrinsic property of
the graph. We remove (edge) coloring of a graph to be a seperate class,
and shift all the coloring functionality to this class. Hence a single
graph can now support multiple different colorings.

- Major revisions to the Hypergraph class and introduction of the
HypergraphEdgeColoring class
- Introduction of the the edge_coloring() function that wraps
greedy_edge_coloring() for the base class
- Revisions of each of the graph subclasses to remove coloring and
create an overloaded version of edge_coloring()
- Revisions of the docstrings across all these classes
- Minor changes to some string representations
- Update to all the unit tests
#2975)

A small PR around the change in functionality of terms in the Model
class.

- Minor revisions on how edge coloring is presented
- Terms are now dict[int, dict[int, int]], where the first index is the
term and the second is the color, the value is the index of the
interaction operator
- Fixed IsingModel to use colorings properly
- Added HeisenbergModel
- Added tests
This fixes some tests that caused CI errors. The faulty tests were
checked in with a previous PR.

Fixes #2997
The main feature from this PR is a resource estimation result table that
is returned from the `estimate` call. It can be configured to have
additional columns which are considered in the creation of a pandas
DataFrame.

Besides that, the PR
- refactors how instructions are build: they are now all build inside
the provenance graph that is part of the architecture context
- enhances enumeration capabilities: one can now enumerate over nested
attributes and union attributes, and also restrict domains, further one
can restrict the domain of the application inside the trace query
- adds post processing to applications: applications can post-process
estimation results _before_ they are inserted in the estimation table.
This post processing step comes currently at a cost in runtime, because
estimation is parallelized in Python and not in Rust
…ovements (#3013)

This PR replaces string-keyed properties with integer keys defined via a
Rust macro, and includes several estimation performance and API
improvements.

  **Property keys**

- New `define_properties!` macro in isa/property_keys.rs auto-assigns
u64 values and generates `property_name_to_key`
- Properties on `Trace`, `EstimationResult`, and `Instruction` now use
`u64` keys instead of `String`
- Python `property_keys` submodule exposes all keys as constants;
`PropertyKey` enum is removed

  **Estimation improvements**

- `LockedISA` holds a single read lock for the duration of `estimate()`,
avoiding repeated lock acquisitions
- ISA cloning is deferred to Pareto-surviving results only, reducing
allocation in `estimate_parallel`
- `EstimationTableStats` tracks job counts (total, successful,
Pareto-surviving)
- `add_qubit_partition_column()` breaks down physical qubits into
compute, factory, and memory

  **Model updates**

- `SurfaceCode`: configurable `one_qubit_gate_depth` /
`two_qubit_gate_depth` with per-instruction time factor overrides
- `RoundBasedFactory`: deterministic cache key using full instruction
serialization

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This adds QIR support to QRE. It uses existing code to walk the QIR
tree. The transformation will fail in programs with branches until we
have better heuristics for branch prediction in place.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Splits Yoked SC into two models, also adds more property IDs.
When performing post processing we need to know the updated results
after the post_process function on the application is called (in
Python). As a result, so far a Python-based parallelism was used for
estimation when post processing is in play. This is changed in this PR,
by first computing all estimates in parallel (without filtering to
Pareto optimal results) and then performing the filtering in Python.
This reduces runtime by about 30% on some QRE tests. Further, it allows
to use the same Rust-based estimation function which improves code
sharing.
msoeken and others added 19 commits March 23, 2026 08:02
#3028)

The previous implementation hit a corner case (the one in the test)
which is now fixed with a new bucketing logic.
Adds an alternative estimation path that builds a **provenance graph**
of ISA instructions and prunes suboptimal candidates before forming the
Cartesian product, significantly reducing the combinatorial search
space.

   ### Changes

   **Rust (`source/qre/src/`)**
- `isa.rs`: Add `build_pareto_index()` to compute per-instruction-ID
Pareto-optimal node sets over (space, time, error). Add
`query_satisfying()` to enumerate ISAs from pruned graph nodes. Extract
`InstructionConstraint::is_satisfied_by()` from inline logic.
- `trace.rs`: Add `estimate_with_graph()` — a new parallel estimator
that uses the provenance graph with per-slot dominance pruning to skip
combinations dominated by previously successful estimates. Add
`Trace::required_instruction_ids()` helper. Add `post_process` flag to
`estimate_parallel()` to control summary collection.
   - `result.rs` / `lib.rs`: Expose new types and re-exports.

   **Python (`source/pip/`)**
- `_estimation.py`: Add `use_graph` parameter to `estimate()` (default
`True`). When enabled, populates the provenance graph and calls the
graph-based estimator instead of the flat enumerator.
- `_isa_enumeration.py`: Add `populate()` method to `ISAQuery` and its
subclasses to fill the provenance graph without yielding ISA objects.
   - `_instruction.py`: Add `InstructionSource` utility.
- `qre.rs`: Expose `_estimate_with_graph`, `ProvenanceGraph` bindings,
and related Python-facing APIs.
   - `_qre.pyi`: Update type stubs.

   **Tests**
- Add tests verifying graph-based estimation produces Pareto-optimal
results consistent with the exhaustive path.

   ### Trade-offs

The graph-based pruning filters ISA instructions by comparing
per-instruction space, time, and error independently. Because total
qubit counts depend on the interaction between factory space and runtime
(copies × factory_space), an instruction dominated on per-instruction
metrics can still contribute to a globally Pareto-optimal result.
`use_graph=False`
  can be used when completeness of the Pareto frontier is required.
…3040)

New features for Trotter expansions.
   * Main class is now TrotterExpansion.
   * TrotterStep represents a single Trotter step.
* TrotterExpansion accepts a function the creates the target type of
Trotter step.
* Minor changes to the Model class and PauliString class to support
changes.
   * Added cirq.Circuit output to TrotterStep instances.
   * Added cirq.CircuitOperation output to TrotterExpansion instances.
   * Updated unit tests.
- Remove unused import
- Propagate source and transform information in cached T states
Adding a plotting function for estimation results. We may change this
later to use Q# widgets like other parts in the repo. But adding this
functionality now to support samples of higher priority.
We always get logical compute and memory qubits (memory is 0, if there
are no memory qubits). These are the qubits that are in the trace before
estimation, and therefore might already contain auxiliary qubits.
Application generators may add algorithm logical qubits (for compute and
memory) to the trace properties as well.
Some refactoring of the trace code for graph estimation, and a way to
expose the ISA requirements of a trace through the Python API.
This improves the function to retrieve ISA requirements from a trace.
This can then be used both as a trace profile to prune estimation jobs
based on the minimum required error rate for an instruction, but also to
report the provided ISA for a trace through the Python API. A function
to display it as a pandas data frame facilitates using it in a Jupyter
notebook.
This is still an intermediate way to get plots into Jupyter notebooks
and should be replaced by qdk widgets in a future PR.
This adds support to generate traces from cirq circuits. These traces
represent the original circuit and do not simply represent the gate
counts. They monkey patch a `_to_trace` method to cirq gates and users
of QRE can add a `_to_trace` method to their custom operations and
gates. If no such method is found, the operation's or gate's
decomposition method will be called to walk the circuit tree.

The PR has some other small fixes:
- extending the gate-based architecture gate set
- minor fixes to `Trace` Python API
- minor improvements on how to create _estimation table results_ (with
extended info such as source ISA tree) from unannotated _estimation
results_
Some files became too large, so they have been split up. Also some
Python names are used in the public API and the `_` prefix was removed.
The list of commits gives a good overview of what happened in this PR.
There is no changes to the functionality or no added functionality in
this PR.
Makes sure that Python docs for QRE are complete and are consistent with
the style used in the other Python files.
Using matplotlib in tests is causing CI problems. This fixes #3076
Graph-based estimation missed some estimates when using ISA transforms
with a pass-through (e.g., those extending instruction sets and keeping
the previous ones).
instances = _enumerate_instances(t, **transformer_kwargs)
transformer_instances.append(instances)

# TODO: make parallel

Check notice

Code scanning / devskim

A "TODO" or similar was left in source code, possibly indicating incomplete functionality Note

Suspicious comment
Returns:
Trace: The resource estimation trace.
"""
# TODO: make caching work for `Callable` as well

Check notice

Code scanning / devskim

A "TODO" or similar was left in source code, possibly indicating incomplete functionality Note

Suspicious comment
self.output_error_rate,
)
)
# TODO: handle case when output_error_rate is larger than input_error_rate

Check notice

Code scanning / devskim

A "TODO" or similar was left in source code, possibly indicating incomplete functionality Note

Suspicious comment
}
}

// TODO: Assign default value to offset?

Check notice

Code scanning / devskim

A "TODO" or similar was left in source code, possibly indicating incomplete functionality Note

Suspicious comment
Comment on lines 1 to +5
pytest
expecttest==0.3.0
pyqir>=0.11.1,<0.12
cirq==1.6.1
pandas>=2.1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We have a few different test_requirements files in the Python code at this point. Should we consider adding a "test" extra so that we can pip install ".[test]" and consolidate test deps and versions? I bring this up as the cirq version requirement here is compatible but different than the wheel cirq extra version requirements. /CC @minestarks @swernli


/// A resourc estimation error.
#[derive(Clone, Debug, Error, PartialEq)]
pub enum Error {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For custom errors we should be using miette diagnostics with code/help where applicable for better errors and consistency with the codebase.

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.

4 participants