Catalyst v0.6.0
New features
-
Catalyst now supports externally hosted callbacks with parameters and return values within qjit-compiled code. This provides the ability to insert native Python code into any qjit-compiled function, allowing for the capability to include subroutines that do not yet support qjit-compilation and enhancing the debugging experience. (#540) (#596) (#610) (#650) (#649) (#661) (#686) (#689)
The following two callback functions are available:
-
catalyst.pure_callback
supports callbacks of pure functions. That is, functions with no side-effects that accept parameters and return values. However, the return type and shape of the function must be known in advance, and is provided as a type signature.@pure_callback def callback_fn(x) -> float: # here we call non-JAX compatible code, such # as standard NumPy return np.sin(x) @qjit def fn(x): return jnp.cos(callback_fn(x ** 2))
>>> fn(0.654) array(0.9151995)
-
catalyst.debug.callback
supports callbacks of functions with no return values. This makes it an easy entry point for debugging, for example via printing or logging at runtime.@catalyst.debug.callback def callback_fn(y): print("Value of y =", y) @qjit def fn(x): y = jnp.sin(x) callback_fn(y) return y ** 2
>>> fn(0.54) Value of y = 0.5141359916531132 array(0.26433582) >>> fn(1.52) Value of y = 0.998710143975583 array(0.99742195)
Note that callbacks do not currently support differentiation, and cannot be used inside functions that
catalyst.grad
is applied to. -
-
More flexible runtime printing through support for format strings. (#621)
The
catalyst.debug.print
function has been updated to support Python-like format strings:@qjit def cir(a, b, c): debug.print("{c} {b} {a}", a=a, b=b, c=c)
>>> cir(1, 2, 3) 3 2 1
Note that previous functionality of the print function to print out memory reference information of variables has been moved to
catalyst.debug.print_memref
. -
Catalyst now supports QNodes that execute on Oxford Quantum Circuits (OQC) superconducting hardware, via OQC Cloud. (#578) (#579) (#691)
To use OQC Cloud with Catalyst, simply ensure your credentials are set as environment variables, and load the
oqc.cloud
device to be used within your qjit-compiled workflows.import os os.environ["OQC_EMAIL"] = "your_email" os.environ["OQC_PASSWORD"] = "your_password" os.environ["OQC_URL"] = "oqc_url" dev = qml.device("oqc.cloud", backend="lucy", shots=2012, wires=2) @qjit @qml.qnode(dev) def circuit(a: float): qml.Hadamard(0) qml.CNOT(wires=[0, 1]) qml.RX(wires=0) return qml.counts(wires=[0, 1]) print(circuit(0.2))
-
Catalyst now ships with an instrumentation feature allowing to explore what steps are run during compilation and execution, and for how long. (#528) (#597)
Instrumentation can be enabled from the frontend with the
catalyst.debug.instrumentation
context manager:>>> @qjit ... def expensive_function(a, b): ... return a + b >>> with debug.instrumentation("session_name", detailed=False): ... expensive_function(1, 2) [DIAGNOSTICS] Running capture walltime: 3.299 ms cputime: 3.294 ms programsize: 0 lines [DIAGNOSTICS] Running generate_ir walltime: 4.228 ms cputime: 4.225 ms programsize: 14 lines [DIAGNOSTICS] Running compile walltime: 57.182 ms cputime: 12.109 ms programsize: 121 lines [DIAGNOSTICS] Running run walltime: 1.075 ms cputime: 1.072 ms
The results will be appended to the provided file if the
filename
attribute is set, and printed to the console otherwise. The flagdetailed
determines whether individual steps in the compiler and runtime are instrumented, or whether only high-level steps like "program capture" and "compilation" are reported.Measurements currently include wall time, CPU time, and (intermediate) program size.
Improvements
-
AutoGraph now supports return statements inside conditionals in qjit-compiled functions. (#583)
For example, the following pattern is now supported, as long as all return values have the same type:
@qjit(autograph=True) def fn(x): if x > 0: return jnp.sin(x) return jnp.cos(x)
>>> fn(0.1) array(0.09983342) >>> fn(-0.1) array(0.99500417)
This support extends to quantum circuits:
dev = qml.device("lightning.qubit", wires=1) @qjit(autograph=True) @qml.qnode(dev) def f(x: float): qml.RX(x, wires=0) m = catalyst.measure(0) if not m: return m, qml.expval(qml.PauliZ(0)) qml.RX(x ** 2, wires=0) return m, qml.expval(qml.PauliZ(0))
>>> f(1.4) (array(False), array(1.)) >>> f(1.4) (array(True), array(0.37945176))
Note that returning results with different types or shapes within the same function, such as different observables or differently shaped arrays, is not possible.
-
Errors are now raised at compile time if the gradient of an unsupported function is requested. (#204)
At the moment,
CompileError
exceptions will be raised if at compile time it is found that code reachable from the gradient operation contains either a mid-circuit measurement, a callback, or a JAX-style custom call (which happens through the mitigation operation as well as certain JAX operations). -
Catalyst now supports devices built from the new PennyLane device API. (#565) (#598) (#599) (#636) (#638) (#664) (#687)
When using the new device API, Catalyst will discard the preprocessing from the original device, replacing it with Catalyst-specific preprocessing based on the TOML file provided by the device. Catalyst also requires that provided devices specify their wires upfront.
-
A new compiler optimization that removes redundant chains of self inverse operations has been added. This is done within a new MLIR pass called
remove-chained-self-inverse
. Currently we only match redundant Hadamard operations, but the list of supported operations can be expanded. (#630) -
The
catalyst.measure
operation is now more lenient in the accepted type for thewires
parameter. In addition to a scalar, a 1D array is also accepted as long as it only contains one element. (#623)For example, the following is now supported:
catalyst.measure(wires=jnp.array([0]))
-
The compilation & execution of
@qjit
compiled functions can now be aborted using an interrupt signal (SIGINT). This includes usingCTRL-C
from a command line and theInterrupt
button in a Jupyter Notebook. (#642) -
The Catalyst Amazon Braket support has been updated to work with the latest version of the Amazon Braket PennyLane plugin (v1.25.0) and Amazon Braket Python SDK (v1.73.3) (#620) (#672) (#673)
Note that with this update, all declared qubits in a submitted program will always be measured, even if specific qubits were never used.
-
An updated quantum device specification format, TOML schema v2, is now supported by Catalyst. This allows device authors to specify properties such as native quantum control support, gate invertibility, and differentiability on a per-operation level. (#554)
For more details on the new TOML schema, please refer to the custom devices documentation.
-
An exception is now raised when OpenBLAS cannot be found by Catalyst during compilation. (#643)
Breaking changes
-
qml.sample
andqml.counts
now produce integer arrays for the sample array and basis state array when used without observables. (#648) -
The endianness of counts in Catalyst now matches the convention of PennyLane. (#601)
-
catalyst.debug.print
no longer supports thememref
keyword argument. Please usecatalyst.debug.print_memref
instead. (#621)
Bug fixes
-
The QNode argument
diff_method=None
is now supported for QNodes within a qjit-compiled function. (#658) -
A bug has been fixed where the C++ compiler driver was incorrectly being triggered twice. (#594)
-
Programs with
jnp.reshape
no longer fail. (#592) -
A bug in the quantum adjoint routine in the compiler has been fixed, which didn't take into account control wires on operations in all instances. (#591)
-
A bug in the test suite causing stochastic autograph test failures has been fixed. (#652)
-
Running Catalyst tests should no longer raise
ResourceWarning
from the use oftempfile.TemporaryDirectory
. (#676) -
Raises an exception if the user has an incompatible CUDA Quantum version installed. (#707)
Internal changes
-
The deprecated
@qfunc
decorator, in use mainly by the LIT test suite, has been removed. (#679) -
Catalyst now publishes a revision string under
catalyst.__revision__
, in addition to the existingcatalyst.__version__
string. The revision contains the Git commit hash of the repository at the time of packaging, or for editable installations the active commit hash at the time of package import. (#560) -
The Python interpreter is now a shared resource across the runtime. (#615)
This change allows any part of the runtime to start executing Python code through pybind.
Contributors
This release contains contributions from (in alphabetical order):
Ali Asadi,
David Ittah,
Romain Moyard,
Sergei Mironov,
Erick Ochoa Lopez,
Lee James O'Riordan,
Muzammiluddin Syed.