* Bump pyo3 and rust numpy version to 0.19.0
PyO3 0.19.0 and rust-numpy 0.19.0 were just released. This commit
updates the version used in qiskit to these latest releases. At the same
time this updates usage of text signature for classes that was
deprecated in the PyO3 0.19.0 release. While not fatal for normal builds
this would have failed clippy in CI because we treat warnings as errors.
* Apply suggestions from code review
* Fix interaction graph vf2 scoring to include 1q component
In #9148 a bug was introduced into the vf2 scoring code. In that PR the
vf2 passes were changed to treat standalone qubits as a special case to
improve the algorithmic efficiency of the pass. However, that PR
broke the scoring algorithm so that it was no longer factoring in the 1q
component of the interaction graph when scoring a layout. This meant
that when scoring a potential layout only the 2q error rates were been
factored into the score and the pass was potentially selecting worse
performing qubits. This commit fixes this error and ensures the scoring
is looking at all the error rates.
* Update qiskit/transpiler/passes/layout/vf2_utils.py
Now that the first release candidate for the 0.24.0 release has been
tagged, the stable branch for the 0.24 series has been created and we
can start developing the 0.25.0 release on main. This commit bumps all
the version strings from 0.24.0 to 0.25.0 (and the backport branch for
mergify to 0.24) accordingly to differentiate the main branch from
0.24.*.
Previously, if Python thought the target file for `qasm2.load` existed,
but Rust was unable to open it, the result `io:Error` would be
propagated up to Python space verbatim, and these situations usually
have confusing error messages. This could happen, for example, if the
target was a directory on Windows.
* Add Rust-based OpenQASM 2 converter
This is a vendored version of qiskit-qasm2
(https://pypi.org/project/qiskit-qasm2), with this initial commit being
equivalent (barring some naming / documentation / testing conversions to
match Qiskit's style) to version 0.5.3 of that package.
This adds a new translation layer from OpenQASM 2 to Qiskit, which is
around an order of magnitude faster than the existing version in Python,
while being more type safe (in terms of disallowing invalid OpenQASM 2
programs rather than attempting to construction `QuantumCircuit`s that
are not correct) and more extensible.
The core logic is a hand-written lexer and parser combination written in
Rust, which emits a bytecode stream across the PyO3 boundary to a small
Python interpreter loop. The main bulk of the parsing logic is a simple
LL(1) recursive-descent algorithm, which delegates to more specific
recursive Pratt-based algorithm for handling classical expressions.
Many of the design decisions made (including why the lexer is written by
hand) are because the project originally started life as a way for me to
learn about implementations of the different parts of a parser stack;
this is the principal reason there are very few external crates used.
There are a few inefficiencies in this implementation, for example:
- the string interner in the lexer allocates twice for each stored
string (but zero times for a lookup). It may be possible to
completely eliminate allocations when parsing a string (or a file if
it's read into memory as a whole), but realistically there's only a
fairly small number of different tokens seen in most OpenQASM 2
programs, so it shouldn't be too big a deal.
- the hand-off from Rust to Python transfers small objects frequently.
It might be more efficient to have a secondary buffered iterator in
Python space, transferring more bytecode instructions at a time and
letting Python resolve them. This form could also be made
asynchronous, since for the most part, the Rust components only need
to acquire the CPython GIL at the API boundary.
- there are too many points within the lexer that can return a failure
result that needs unwrapping at every site. Since there are no tokens
that can span multiple lines, it should be possible to refactor so
that almost all of the byte-getter and -peeker routines cannot return
error statuses, at the cost of the main lexer loop becoming
responsible for advancing the line buffer, and moving the non-ASCII
error handling into each token constructor.
I'll probably keep playing with some of those in the `qiskit-qasm2`
package itself when I have free time, but at some point I needed to draw
the line and vendor the package. It's still ~10x faster than the
existing one:
In [1]: import qiskit.qasm2
...: prog = """
...: OPENQASM 2.0;
...: include "qelib1.inc";
...: qreg q[2];
...: """
...: prog += "rz(pi * 2) q[0];\ncx q[0], q[1];\n"*100_000
...: %timeit qiskit.qasm2.loads(prog)
...: %timeit qiskit.QuantumCircuit.from_qasm_str(prog)
2.26 s ± 39.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
22.5 s ± 106 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
`cx`-heavy programs like this one are actually the ones that the new
parser is (comparatively) slowest on, because the construction time of
`CXGate` is higher than most gates, and this dominates the execution
time for the Rust-based parser.
* Work around docs failure on Sphinx 5.3, Python 3.9
The version of Sphinx that we're constrained to use in the docs build
can't handle the `Unpack` operator, so as a temporary measure we can
just relax the type hint a little.
* Remove unused import
* Tweak documentation
* More specific PyO3 usage
* Use PathBuf directly for paths
* Format
* Freeze dataclass
* Use type-safe id types
This should have no impact on runtime or on memory usage, since each of
the new types has the same bit width and alignment as the `usize` values
they replace.
* Documentation tweaks
* Fix comments in lexer
* Fix lexing version number with separating comments
* Add test of pathological formatting
* Fixup release note
* Fix handling of u0 gate
* Credit reviewers
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Co-authored-by: Kevin Hartman <kevin@hart.mn>
Co-authored-by: Eric Arellano <14852634+Eric-Arellano@users.noreply.github.com>
* Add test of invalid gate-body statements
* Refactor custom built-in gate definitions
The previous system was quite confusing, and required all accesses to
the global symbol table to know that the `Gate` symbol could be present
but overridable. This led to confusing logic, various bugs and
unnecessary constraints, such as it previously being (erroneously)
possible to provide re-definitions for any "built-in" gate.
Instead, we keep a separate store of instructions that may be redefined.
This allows the logic to be centralised to only to the place responsible
for performing those overrides, and remains accessible for error-message
builders to query in order to provide better diagnostics.
* Credit Sasha
Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
* Credit Matthew
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
* Remove dependency on `lazy_static`
For a hashset of only 6 elements that is only checked once, there's not
really any point to pull in an extra dependency or use a hash set at
all.
* Update PyO3 version
---------
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Co-authored-by: Kevin Hartman <kevin@hart.mn>
Co-authored-by: Eric Arellano <14852634+Eric-Arellano@users.noreply.github.com>
Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
* Leverage Rust circuit sequence construction for OneQubitEulerDecomposer
This commit is a follow-up to #9578 which added a rust implementation
of the second half of the single qubit euler decomposition routines and
leveraged them for the Optimize1qGatesDecomposition transpiler pass.
With that PR the Optimize1qGatesDecomposition no longer was dependent on
the OneQubitEulerDecomposer class. This commit continues from that PR
and updates the OneQubitEulerDecomposer to leverage the same Rust
implementation internally. Calling a decomposer object will internally
call the rust function to generate a circuit sequence and then return
either a QuantumCircuit or DAGCircuit (depending on the options).
Similarly all the angle computation is done directly in Rust.
* Add missing atol clamping to mod_2pi
The python version of the OneQubitEulerDecomposer class had atol
clamping on it's output from mod_2pi, when this function was ported to
rust this was not included. At the time it was because nothing set the
atol parameter when calling mod_2pi (the angle calculation in #9185 did
not have atol and the expansion to construct circuits for
Optimize1qGatesDecomposition always used the default value). However,
now that we're expanding OneQubitEulerDecomposer to internally do all
the calculations in rust we need to support an adjustable atol which
includes the missing endpoint clamping in mod_2pi. This commit adds this
missing functionality to the function.
* Add docstring to mod_2pi rust function
* Remove mod_2pi python function
* Add structure for multiplie Rust extension crates
This is a precursor to adding an entirely separate and self-contained
crate to handle parsing of OpenQASM 2 (not 3 - this is kind of like a
trial run for that). It could conceivably still live within
`qiskit._accelerate`, but having separate crates helps us enforce more
sane API barriers, and has improvements in the incremental build time
when working on only one of the Rust extensions.
The intent after this commit is still to have `qiskit._accelerate` as an
easy catch-all for accelerators for Python. Developers should not need
to be concerned with defining a new crate for every new feature, and for
the most part `_accelerate` is still logically interoperative within
itself (for example, `NLayout` is used all over). This is just laying
out the groundwork so more complex crate additions _can_ also be made.
Some of the niceties to do with Cargo workspaces only became stabilised
in Rust 1.64. In particular, inheritance from the workspace root for
things like the package version, Rust version, and dependencies only
came in then. For now, the `workspace.packages` key in the root
`Cargo.toml` is ignored with a small warning during the build, but I've
put it in place mostly to keep track of what we can change once the MSRV
becomes 1.64 or greater (likely not til at least Terra 0.26).
* Add README.md to crates/accelerate
* Correct licence metadata