PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
Two new user-facing classes enable calculating and propagating gate errors in PennyLane:
qml.resource.SpectralNormError
: the spectral norm error is defined as the distance, in spectral norm, between the true unitary we intend to apply and the approximate unitary that is actually applied.
qml.resource.ErrorOperation
: a base class that inherits from qml.operation.Operation
and represents quantum operations which carry some form of algorithmic error.
SpectralNormError
can be used for back-of-the-envelope type calculations like obtaining the spectral norm error between two unitaries via get_error
:
import pennylane as qml
from pennylane.resource import ErrorOperation, SpectralNormError
intended_op = qml.RY(0.40, 0)
actual_op = qml.RY(0.41, 0) # angle of rotation is slightly off
>>> SpectralNormError.get_error(intended_op, actual_op)
0.004999994791668309
SpectralNormError
is also a key tool to specify errors in larger quantum circuits:
For operations representing a major building block of an algorithm, we can create a custom operation that inherits from ErrorOperation
. This child class must override the error
method and should return a SpectralNormError
instance:
class MyErrorOperation(ErrorOperation):
def __init__(self, error_val, wires):
self.error_val = error_val
super().__init__(wires=wires)
def error(self):
return SpectralNormError(self.error_val)
In this toy example, MyErrorOperation
introduces an arbitrary SpectralNormError
when called in a QNode. It does not require a decomposition or matrix representation when used with null.qubit
(suggested for use with resource and error estimation since circuit executions are not required to calculate resources or errors).
dev = qml.device("null.qubit")
@qml.qnode(dev)
def circuit():
MyErrorOperation(0.1, wires=0)
MyErrorOperation(0.2, wires=1)
return qml.state()
The total spectral norm error of the circuit can be calculated using qml.specs
:
>>> qml.specs(circuit)()['errors']
{'SpectralNormError': SpectralNormError(0.30000000000000004)}
PennyLane already includes a number of built-in building blocks for algorithms like QuantumPhaseEstimation
and TrotterProduct
. TrotterProduct
now propagates errors based on the number of steps performed in the Trotter product. QuantumPhaseEstimation
now propagates errors based on the error of its input unitary.
dev = qml.device('null.qubit')
hamiltonian = qml.dot([1.0, 0.5, -0.25], [qml.X(0), qml.Y(0), qml.Z(0)])
@qml.qnode(dev)
def circuit():
qml.TrotterProduct(hamiltonian, time=0.1, order=2)
qml.QuantumPhaseEstimation(MyErrorOperation(0.01, wires=0), estimation_wires=[1, 2, 3])
return qml.state()
Again, the total spectral norm error of the circuit can be calculated using qml.specs
:
>>> qml.specs(circuit)()["errors"]
{'SpectralNormError': SpectralNormError(0.07616666666666666)}
Check out our error propagation demo to see how to use these new features in a real-world example!
The Fast Approximate BLock-Encodings (FABLE) algorithm for embedding a matrix into a quantum circuit as outlined in arXiv:2205.00081 is now accessible via the qml.FABLE
template. (#5107)
The usage of qml.FABLE
is similar to qml.BlockEncode
but provides a more efficient circuit construction at the cost of a user-defined approximation level, tol
. The number of wires that qml.FABLE
operates on is 2*n + 1
, where n
defines the dimension of the $2^n \times 2^n$ matrix that we want to block-encode.
import numpy as np
A = np.array([[0.1, 0.2], [0.3, 0.4]])
dev = qml.device('default.qubit', wires=3)
@qml.qnode(dev)
def circuit():
qml.FABLE(A, tol = 0.001, wires=range(3))
return qml.state()
>>> mat = qml.matrix(circuit)()
>>> 2 * mat[0:2, 0:2]
array([[0.1+0.j, 0.2+0.j], [0.3+0.j, 0.4+0.j]])
A high-level interface for amplitude amplification and its variants is now available via the new qml.AmplitudeAmplification
template. (#5160)
Based on arXiv:quant-ph/0005055, given a state $\vert \Psi \rangle = \alpha \vert \phi \rangle + \beta \vert \phi^{\perp} \rangle$, qml.AmplitudeAmplification
amplifies the amplitude of $\vert \phi \rangle$.
Here's an example with a target state $\vert \phi \rangle = \vert 2 \rangle = \vert 010 \rangle$, an input state $\vert \Psi \rangle = H^{\otimes 3} \vert 000 \rangle$, as well as an oracle that flips the sign of $\vert \phi \rangle$ and does nothing to $\vert \phi^{\perp} \rangle$, which can be achieved in this case through qml.FlipSign
.
@qml.prod
def generator(wires):
for wire in wires:
qml.Hadamard(wires=wire)
U = generator(wires=range(3))
O = qml.FlipSign(2, wires=range(3))
Here, U
is a quantum operation that is created by decorating a quantum function with @qml.prod
. This could alternatively be done by creating a user-defined custom operation with a decomposition. Amplitude amplification can then be set up within a circuit:
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circuit():
generator(wires=range(3)) # prepares |Psi> = U|0>
qml.AmplitudeAmplification(U, O, iters=10)
return qml.probs(wires=range(3))
>>> print(np.round(circuit(), 3))
[0.01 0.01 0.931 0.01 0.01 0.01 0.01 0.01 ]
As expected, we amplify the $\vert 2 \rangle$ state.
Reflecting about a given quantum state is now available via qml.Reflection
. This operation is very useful in the amplitude amplification algorithm and offers a generalization of qml.FlipSign
, which operates on basis states. (#5159)
qml.Reflection
works by providing an operation, $U$, that prepares the desired state, $\vert \psi \rangle$, that we want to reflect about. In other words, $U$ is such that $U \vert 0 \rangle = \vert \psi \rangle$. In PennyLane, $U$ must be an Operator
. For example, if we want to reflect about $\vert \psi \rangle = \vert + \rangle$, then $U = H$:
U = qml.Hadamard(wires=0)
dev = qml.device('default.qubit')
@qml.qnode(dev)
def circuit():
qml.Reflection(U)
return qml.state()
>>> circuit()
tensor([0.-6.123234e-17j, 1.+6.123234e-17j], requires_grad=True)
Performing qubitization is now easily accessible with the new qml.Qubitization
operator. (#5500)
qml.Qubitization
encodes a Hamiltonian into a suitable unitary operator. When applied in conjunction with quantum phase estimation (QPE), it allows for computing the eigenvalue of an eigenvector of the given Hamiltonian.
H = qml.dot([0.1, 0.3, -0.3], [qml.Z(0), qml.Z(1), qml.Z(0) @ qml.Z(2)])
@qml.qnode(qml.device("default.qubit"))
def circuit():
# initialize the eigenvector
qml.PauliX(2)
# apply QPE
measurements = qml.iterative_qpe(
qml.Qubitization(H, control = [3,4]), ancilla = 5, iters = 3
)
return qml.probs(op = measurements)
A new function called qml.bravyi_kitaev
has been added to perform the Bravyi-Kitaev mapping of fermionic Hamiltonians to qubit Hamiltonians. (#5390)
This function presents an alternative mapping to qml.jordan_wigner
or qml.parity_transform
which can help us measure expectation values more efficiently on hardware. Simply provide a fermionic Hamiltonian (created from from_string
, FermiA
, FermiC
, FermiSentence
, or FermiWord
) and the number of qubits / spin orbitals in the system, n
:
>>> fermi_ham = qml.fermi.from_string('0+ 1+ 1- 0-')
>>> qubit_ham = qml.bravyi_kitaev(fermi_ham, n=6, tol=0.0)
>>> print(qubit_ham)
0.25 * I(0) + -0.25 * Z(0) + -0.25 * (Z(0) @ Z(1)) + 0.25 * Z(1)
The qml.qchem.hf_state
function has been upgraded to be compatible with qml.parity_transform
and the new Bravyi-Kitaev mapping (qml.bravyi_kitaev
). (#5472) (#5472)
>>> state_bk = qml.qchem.hf_state(2, 6, basis="bravyi_kitaev")
>>> print(state_bk)
[1 0 0 0 0 0]
>>> state_parity = qml.qchem.hf_state(2, 6, basis="parity")
>>> print(state_parity)
[1 0 0 0 0 0]
The dynamical Lie algebra (DLA) of a set of operators captures the range of unitary evolutions that the operators can generate. In v0.36 of PennyLane, we have added support for calculating important DLA concepts including:
A new qml.lie_closure
function to compute the Lie closure of a list of operators, providing one way to obtain the DLA. (#5161) (#5169) (#5627)
For a list of operators ops = [op1, op2, op3, ..]
, one computes all nested commutators between ops
until no new operators are generated from commutation. All these operators together form the DLA, see e.g. section IIB of arXiv:2308.01432.
Take for example the following operators:
from pennylane import X, Y, Z
ops = [X(0) @ X(1), Z(0), Z(1)]
A first round of commutators between all elements yields the new operators Y(0) @ X(1)
and X(0) @ Y(1)
(omitting scalar prefactors).
>>> qml.commutator(X(0) @ X(1), Z(0))
-2j * (Y(0) @ X(1))
>>> qml.commutator(X(0) @ X(1), Z(1))
-2j * (X(0) @ Y(1))
A next round of commutators between all elements further yields the new operator Y(0) @ Y(1)
.
>>> qml.commutator(X(0) @ Y(1), Z(0))
-2j * (Y(0) @ Y(1))
After that, no new operators emerge from taking nested commutators and we have the resulting DLA. This can now be done in short via qml.lie_closure
as follows.
>>> ops = [X(0) @ X(1), Z(0), Z(1)]
>>> dla = qml.lie_closure(ops)
>>> dla
[X(0) @ X(1), Z(0), Z(1), -1.0 * (Y(0) @ X(1)), -1.0 * (X(0) @ Y(1)), -1.0 * (Y(0) @ Y(1))]
Computing the structure constants (the adjoint representation) of a dynamical Lie algebra. (5406)
For example, we can compute the adjoint representation of the transverse field Ising model DLA.
>>> dla = [X(0) @ X(1), Z(0), Z(1), Y(0) @ X(1), X(0) @ Y(1), Y(0) @ Y(1)]
>>> structure_const = qml.structure_constants(dla)
>>> structure_const.shape
(6, 6, 6)
Visit the documentation of qml.structure_constants to understand how structure constants are a useful way to represent a DLA.
Computing the center of a dynamical Lie algebra. (#5477)
Given a DLA g
, we can now compute its centre. The center
is the collection of operators that commute with all other operators in the DLA.
>>> g = [X(0), X(1) @ X(0), Y(1), Z(1) @ X(0)]
>>> qml.center(g)
[X(0)]
To help explain these concepts, check out the dynamical Lie algebras demo.
Mixed qutrit states can now be simulated with the default.qutrit.mixed
device. (#5495) (#5451) (#5186) (#5082) (#5213)
Thanks to contributors from the University of British Columbia, a mixed-state qutrit device is now available for simulation, providing a noise-capable equivalent to default.qutrit
.
dev = qml.device("default.qutrit.mixed")
def circuit():
qml.TRY(0.1, wires=0)
@qml.qnode(dev)
def shots_circuit():
circuit()
return qml.sample(), qml.expval(qml.GellMann(wires=0, index=1))
@qml.qnode(dev)
def density_matrix_circuit():
circuit()
return qml.state()
>>> shots_circuit(shots=5)
(array([0, 0, 0, 0, 0]), 0.19999999999999996)
>>> density_matrix_circuit()
tensor([[0.99750208+0.j, 0.04991671+0.j, 0. +0.j], [0.04991671+0.j, 0.00249792+0.j, 0. +0.j], [0. +0.j, 0. +0.j, 0. +0.j]], requires_grad=True)
However, there's one crucial ingredient that we still need to add: support for qutrit noise operations. Keep your eyes peeled for this to arrive in the coming releases!
This release completes the main phase of PennyLane's switchover to an updated approach for handling arithmetic operations between operators. The new approach is now enabled by default and is intended to realize a few objectives: 1. To make it as easy to work with PennyLane operators as it would be with pen and paper. 2. To improve the efficiency of operator arithmetic.
In many cases, this update should not break code. If issues do arise, check out the updated operator troubleshooting page and don't hesitate to reach out to us on the PennyLane discussion forum. As a last resort the old behaviour can be enabled by calling qml.operation.disable_new_opmath()
, but this is not recommended because support will not continue in future PennyLane versions (v0.36 and higher). (#5269)
A new class called qml.ops.LinearCombination
has been introduced. In essence, this class is an updated equivalent of the now-deprecated qml.ops.Hamiltonian
but for usage with the new operator arithmetic. (#5216)
qml.ops.Sum
now supports storing grouping information. Grouping type and method can be specified during construction using the grouping_type
and method
keyword arguments of qml.dot
, qml.sum
, or qml.ops.Sum
. The grouping indices are stored in Sum.grouping_indices
. (#5179)
a = qml.X(0)
b = qml.prod(qml.X(0), qml.X(1))
c = qml.Z(0)
obs = [a, b, c]
coeffs = [1.0, 2.0, 3.0]
op = qml.dot(coeffs, obs, grouping_type="qwc")
>>> op.grouping_indices
((2,), (0, 1))
Additionally, grouping_type
and method
can be set or changed after construction using Sum.compute_grouping()
:
a = qml.X(0)
b = qml.prod(qml.X(0), qml.X(1))
c = qml.Z(0)
obs = [a, b, c]
coeffs = [1.0, 2.0, 3.0]
op = qml.dot(coeffs, obs)
>>> op.grouping_indices is None
True
>>> op.compute_grouping(grouping_type="qwc")
>>> op.grouping_indices
((2,), (0, 1))
Note that the grouping indices refer to the lists returned by Sum.terms()
, not Sum.operands
.
A new function called qml.operation.convert_to_legacy_H
that converts Sum
, SProd
, and Prod
to Hamiltonian
instances has been added. This function is intended for developers and will be removed in a future release without a deprecation cycle. (#5309)
The qml.is_commuting
function now accepts Sum
, SProd
, and Prod
instances. (#5351)
Operators can now be left-multiplied by NumPy arrays (i.e., arr * op
). (#5361)
op.generator()
, where op
is an Operator
instance, now returns operators consistent with the global setting for qml.operator.active_new_opmath()
wherever possible. Sum
, SProd
and Prod
instances will be returned even after disabling the new operator arithmetic in cases where they offer additional functionality not available using legacy operators. (#5253) (#5410) (#5411) (#5421)
Prod
instances temporarily have a new obs
property, which helps smoothen the transition of the new operator arithmetic system. In particular, this is aimed at preventing breaking code that uses Tensor.obs
. The property has been immediately deprecated. Moving forward, we recommend using op.operands
. (#5539)
qml.ApproxTimeEvolution
is now compatible with any operator that has a defined pauli_rep
. (#5362)
Hamiltonian.pauli_rep
is now defined if the Hamiltonian is a linear combination of Pauli operators. (#5377)
Prod
instances created with qutrit operators now have a defined eigvals()
method. (#5400)
qml.transforms.hamiltonian_expand
and qml.transforms.sum_expand
can now handle multi-term observables with a constant offset (i.e., terms like qml.I()
). (#5414) (#5543)
qml.qchem.taper_operation
is now compatible with the new operator arithmetic. (#5326)
The warning for an observable that might not be hermitian in QNode executions has been removed. This enables jit-compilation. (#5506)
qml.transforms.split_non_commuting
will now work with single-term operator arithmetic. (#5314)
LinearCombination
and Sum
now accept _grouping_indices
on initialization. This addition is relevant to developers only. (#5524)
Calculating the dense, differentiable matrix for PauliSentence
and operators with Pauli sentences is now faster. (#5578)
ExpectationMP
, VarianceMP
, CountsMP
, and SampleMP
now have a process_counts
method (similar to process_samples
). This allows for calculating measurements given a counts
dictionary. (#5256) (#5395)
Type-hinting has been added in the Operator
class for better interpretability. (#5490)
An alternate strategy for sampling with multiple different shots
values has been implemented via the shots.bins()
method, which samples all shots at once and then processes each separately. (#5476)
A new module called qml.capture
that will contain PennyLane's own capturing mechanism for hybrid quantum-classical programs has been added. (#5509)
The dynamic_one_shot
transform has been introduced, enabling dynamic circuit execution on circuits with finite shots
and devices that natively support mid-circuit measurements. (#5266)
The QubitDevice
class and children classes support the dynamic_one_shot
transform provided that they support mid-circuit measurement operations natively. (#5317)
default.qubit
can now be provided a random seed for sampling mid-circuit measurements with finite shots. This (1) ensures that random behaviour is more consistent with dynamic_one_shot
and defer_measurements
and (2) makes our continuous-integration (CI) have less failures due to stochasticity. (#5337)
Gradient transforms may now be applied to batched/broadcasted QNodes as long as the broadcasting is in non-trainable parameters. (#5452)
The performance of computing the matrix of qml.QFT
has been improved. (#5351)
qml.transforms.broadcast_expand
now supports shot vectors when returning qml.sample()
. (#5473)
LightningVJPs
is now compatible with Lightning devices using the new device API. (#5469)
Obtaining classical shadows using the default.clifford
device is now compatible with stim v1.13.0
. (#5409)
default.mixed
has improved support for sampling-based measurements with non-NumPy interfaces. (#5514) (#5530)
default.mixed
now supports arbitrary state-based measurements with qml.Snapshot
. (#5552)
null.qubit
has been upgraded to the new device API and has support for all measurements and various modes of differentiation. (#5211)
Entanglement entropy can now be calculated with qml.math.vn_entanglement_entropy
, which computes the von Neumann entanglement entropy from a density matrix. A corresponding QNode transform, qml.qinfo.vn_entanglement_entropy
, has also been added. (#5306)
qml.draw
and qml.draw_mpl
will now attempt to sort the wires if no wire order is provided by the user or the device. (#5576)
A clear error message is added in KerasLayer
when using the newest version of TensorFlow with Keras 3 (which is not currently compatible with KerasLayer
), linking to instructions to enable Keras 2. (#5488)
qml.ops.Conditional
now stores the data
, num_params
, and ndim_param
attributes of the operator it wraps. (#5473)
The molecular_hamiltonian
function calls PySCF
directly when method='pyscf'
is selected. (#5118)
cache_execute
has been replaced with an alternate implementation based on @transform
. (#5318)
QNodes now defer diff_method
validation to the device under the new device API. (#5176)
The device test suite has been extended to cover gradient methods, templates and arithmetic observables. (#5273) (#5518)
A typo and string formatting mistake have been fixed in the error message for ClassicalShadow._convert_to_pauli_words
when the input is not a valid pauli_rep
. (#5572)
Circuits running on lightning.qubit
and that return qml.state()
now preserve the dtype
when specified. (#5547)
qml.matrix()
called on the following will now raise an error if wire_order
is not specified: * tapes with more than one wire * quantum functions * Operator
classes where num_wires
does not equal to 1 * QNodes if the device does not have wires specified. * PauliWord
s and PauliSentence
s with more than one wire. (#5328) (#5359)
single_tape_transform
, batch_transform
, qfunc_transform
, op_transform
, gradient_transform
and hessian_transform
have been removed. Instead, switch to using the new qml.transform
function. Please refer to the transform docs <https://docs.pennylane.ai/en/stable/code/qml_transforms.html#custom-transforms>
_ to see how this can be done. (#5339)
Attempting to multiply PauliWord
and PauliSentence
with *
will raise an error. Instead, use @
to conform with the PennyLane convention. (#5341)
DefaultQubit
now uses a pre-emptive key-splitting strategy to avoid reusing JAX PRNG keys throughout a single execute
call. (#5515)
qml.pauli.pauli_mult
and qml.pauli.pauli_mult_with_phase
have been removed. Instead, use qml.simplify(qml.prod(pauli_1, pauli_2))
to get the reduced operator. (#5324)
>>> op = qml.simplify(qml.prod(qml.PauliX(0), qml.PauliZ(0)))
>>> op -1j*(PauliY(wires=[0]))
>>> [phase], [base] = op.terms()
>>> phase, base
(-1j, PauliY(wires=[0]))
The dynamic_one_shot
transform now uses sampling (SampleMP
) to get back the values of the mid-circuit measurements. (#5486)
Operator
dunder methods now combine like-operator arithmetic classes via lazy=False
. This reduces the chances of getting a RecursionError
and makes nested operators easier to work with. (#5478)
The private functions _pauli_mult
, _binary_matrix
and _get_pauli_map
from the pauli
module have been removed. The same functionality can be achieved using newer features in the pauli
module. (#5323)
MeasurementProcess.name
and MeasurementProcess.data
have been removed. Use MeasurementProcess.obs.name
and MeasurementProcess.obs.data
instead. (#5321)
Operator.validate_subspace(subspace)
has been removed. Instead, use qml.ops.qutrit.validate_subspace(subspace)
. (#5311)
The contents of qml.interfaces
has been moved inside qml.workflow
. The old import path no longer exists. (#5329)
Since default.mixed
does not support snapshots with measurements, attempting to do so will result in a DeviceError
instead of getting the density matrix. (#5416)
LinearCombination._obs_data
has been removed. You can still use LinearCombination.compare
to check mathematical equivalence between a LinearCombination
and another operator. (#5504)
Accessing qml.ops.Hamiltonian
is deprecated because it points to the old version of the class that may not be compatible with the new approach to operator arithmetic. Instead, using qml.Hamiltonian
is recommended because it dispatches to the LinearCombination
class when the new approach to operator arithmetic is enabled. This will allow you to continue to use qml.Hamiltonian
with existing code without needing to make any changes. (#5393)
qml.load
has been deprecated. Instead, please use the functions outlined in the Importing workflows quickstart guide. (#5312)
Specifying control_values
with a bit string in qml.MultiControlledX
has been deprecated. Instead, use a list of booleans or 1s and 0s. (#5352)
qml.from_qasm_file
has been deprecated. Instead, please open the file and then load its content using qml.from_qasm
. (#5331)
>>> with open("test.qasm", "r") as f:
... circuit = qml.from_qasm(f.read())
A new page explaining the shapes and nesting of return types has been added. (#5418)
Redundant documentation for the evolve
function has been removed. (#5347)
The final example in the compile
docstring has been updated to use transforms correctly. (#5348)
A link to the demos for using qml.SpecialUnitary
and qml.QNGOptimizer
has been added to their respective docstrings. (#5376)
A code example in the qml.measure
docstring has been added that showcases returning mid-circuit measurement statistics from QNodes. (#5441)
The computational basis convention used for qml.measure
โ 0 and 1 rather than ยฑ1 โ has been clarified in its docstring. (#5474)
A new Release news section has been added to the table of contents, containing release notes, deprecations, and other pages focusing on recent changes. (#5548)
A summary of all changes has been added in the "Updated Operators" page in the new "Release news" section in the docs. (#5483) (#5636)
Patches the QNode so that parameter-shift will be considered best with lightning if qml.metric_tensor
is in the transform program. (#5624)
Stopped printing the ID of qcut.MeasureNode
and qcut.PrepareNode
in tape drawing. (#5613)
Improves the error message for setting shots on the new device interface, or trying to access a property that no longer exists. (#5616)
Fixed a bug where qml.draw
and qml.draw_mpl
incorrectly raised errors for circuits collecting statistics on mid-circuit measurements while using qml.defer_measurements
. (#5610)
Using shot vectors with param_shift(... broadcast=True)
caused a bug. This combination is no longer supported and will be added again in the next release. Fixed a bug with custom gradient recipes that only consist of unshifted terms. (#5612) (#5623)
qml.counts
now returns the same keys with dynamic_one_shot
and defer_measurements
. (#5587)
null.qubit
now automatically supports any operation without a decomposition. (#5582)
Fixed a bug where the shape and type of derivatives obtained by applying a gradient transform to a QNode differed based on whether the QNode uses classical coprocessing. (#4945)
ApproxTimeEvolution
, CommutingEvolution
, QDrift
, and TrotterProduct
now de-queue their input observable. (#5524)
(In)equality of qml.HilbertSchmidt
instances is now reported correctly by qml.equal
. (#5538)
qml.ParticleConservingU1
and qml.ParticleConservingU2
no longer raise an error when the initial state is not specified but default to the all-zeros state. (#5535)
qml.counts
no longer returns negative samples when measuring 8 or more wires. (#5544) (#5556)
The dynamic_one_shot
transform now works with broadcasting. (#5473)
Diagonalizing gates are now applied when measuring qml.probs
on non-computational basis states on a Lightning device. (#5529)
two_qubit_decomposition
no longer diverges at a special case of a unitary matrix. (#5448)
The qml.QNSPSAOptimizer
now correctly handles optimization for legacy devices that do not follow the new device API. (#5497)
Operators applied to all wires are now drawn correctly in a circuit with mid-circuit measurements. (#5501)
Fixed a bug where certain unary mid-circuit measurement expressions would raise an uncaught error. (#5480)
Probabilities now sum to 1 when using the torch
interface with default_dtype
set to torch.float32
. (#5462)
Tensorflow can now handle devices with float32
results but float64
input parameters. (#5446)
Fixed a bug where the argnum
keyword argument of qml.gradients.stoch_pulse_grad
references the wrong parameters in a tape, creating an inconsistency with other differentiation methods and preventing some use cases. (#5458)
Bounded value failures due to numerical noise with calls to np.random.binomial
is now avoided. (#5447)
Using @
with legacy Hamiltonian instances now properly de-queues the previously existing operations. (#5455)
The QNSPSAOptimizer
now properly handles differentiable parameters, resulting in being able to use it for more than one optimization step. (#5439)
The QNode interface now resets if an error occurs during execution. (#5449)
Failing tests due to changes with Lightning's adjoint diff pipeline have been fixed. (#5450)
Failures occurring when making autoray-dispatched calls to Torch with paired CPU data have been fixed. (#5438)
jax.jit
now works with qml.sample
with a multi-wire observable. (#5422)
qml.qinfo.quantum_fisher
now works with non-default.qubit
devices. (#5423)
We no longer perform unwanted dtype
promotion in the pauli_rep
of SProd
instances when using Tensorflow. (#5246)
Fixed TestQubitIntegration.test_counts
in tests/interfaces/test_jax_qnode.py
to always produce counts for all outcomes. (#5336)
Fixed PauliSentence.to_mat(wire_order)
to support identities with wires. (#5407)
CompositeOp.map_wires
now correctly maps the overlapping_ops
property. (#5430)
DefaultQubit.supports_derivatives
has been updated to correctly handle circuits containing mid-circuit measurements and adjoint differentiation. (#5434)
SampleMP
, ExpectationMP
, CountsMP
, and VarianceMP
constructed with eigvals
can now properly process samples. (#5463)
Fixed a bug in hamiltonian_expand
that produces incorrect output dimensions when shot vectors are combined with parameter broadcasting. (#5494)
default.qubit
now allows measuring Identity
on no wires and observables containing Identity
on no wires. (#5570)
Fixed a bug where TorchLayer
does not work with shot vectors. (#5492)
Fixed a bug where the output shape of a QNode returning a list containing a single measurement is incorrect when combined with shot vectors. (#5492)
Fixed a bug in qml.math.kron
that makes Torch incompatible with NumPy. (#5540)
Fixed a bug in _group_measurements
that fails to group measurements with commuting observables when they are operands of Prod
. (#5525)
qml.equal
can now be used with sums and products that contain operators on no wires like I
and GlobalPhase
. (#5562)
CompositeOp.has_diagonalizing_gates
now does a more complete check of the base operators to ensure consistency between op.has_diagonalzing_gates
and op.diagonalizing_gates()
(#5603)
Updated the method
kwarg of qml.TrotterProduct().error()
to be more clear that we are computing upper-bounds. (#5637)
This release contains contributions from (in alphabetical order):
Tarun Kumar Allamsetty, Guillermo Alonso, Mikhail Andrenkov, Utkarsh Azad, Gabriel Bottrill, Thomas Bromley, Astral Cai, Diksha Dhawan, Isaac De Vlugt, Amintor Dusko, Pietropaolo Frisoni, Lillian M. A. Frederiksen, Diego Guala, Austin Huang, Soran Jahangiri, Korbinian Kottmann, Christina Lee, Vincent Michaud-Rioux, Mudit Pandey, Kenya Sakka, Jay Soni, Matthew Silverman, David Wierichs.
Lightning simulators need special handling of diagonalizing gates when performing sampling measurements. (#5343)
Updated the lower bound on the required Catalyst version to v0.5.0
. (#5320)
This release contains contributions from (in alphabetical order):
Vincent Michaud-Rioux, Erick Ochoa Lopez.
This version of PennyLane makes it easier to import circuits from Qiskit. (#5218) (#5168)
The qml.from_qiskit
function converts a Qiskit QuantumCircuit into a PennyLane quantum function. Although qml.from_qiskit
already exists in PennyLane, we have made a number of improvements to make importing from Qiskit easier. And yes โ qml.from_qiskit
functionality is compatible with both Qiskit 1.0 and earlier versions! Here's a comprehensive list of the improvements:
You can now append PennyLane measurements onto the quantum function returned by qml.from_qiskit
. Consider this simple Qiskit circuit:
import pennylane as qml
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.rx(0.785, 0)
qc.ry(1.57, 1)
We can convert it into a PennyLane QNode in just a few lines, with PennyLane measurements
easily included:
>>> dev = qml.device("default.qubit")
>>> measurements = qml.expval(qml.Z(0) @ qml.Z(1))
>>> qfunc = qml.from_qiskit(qc, measurements=measurements)
>>> qnode = qml.QNode(qfunc, dev)
>>> qnode()
tensor(0.00056331, requires_grad=True)
Quantum circuits that already contain Qiskit-side measurements can be faithfully converted with qml.from_qiskit
. Consider this example Qiskit circuit:
qc = QuantumCircuit(3, 2) # Teleportation
qc.rx(0.9, 0) # Prepare input state on qubit 0
qc.h(1) # Prepare Bell state on qubits 1 and 2 qc.cx(1, 2)
qc.cx(0, 1) # Perform teleportation
qc.h(0)
qc.measure(0, 0)
qc.measure(1, 1)
with qc.if_test((1, 1)): # Perform first conditional
qc.x(2)
This circuit can be converted into PennyLane with the Qiskit measurements still accessible. For example, we can use those results as inputs to a mid-circuit measurement in PennyLane:
@qml.qnode(dev)
def teleport():
m0, m1 = qml.from_qiskit(qc)()
qml.cond(m0, qml.Z)(2)
return qml.density_matrix(2)
>>> teleport()
tensor([[0.81080498+0.j , 0. +0.39166345j],
[0. -0.39166345j, 0.18919502+0.j ]], requires_grad=True)
It is now more intuitive to handle and differentiate parametrized Qiskit circuits. Consider the following circuit:
from qiskit.circuit import Parameter
from pennylane import numpy as np
angle0 = Parameter("x") angle1 = Parameter("y")
qc = QuantumCircuit(2, 2)
qc.rx(angle0, 0)
qc.ry(angle1, 1)
qc.cx(1, 0)
We can convert this circuit into a QNode with two arguments, corresponding to x
and y
:
measurements = qml.expval(qml.PauliZ(0))
qfunc = qml.from_qiskit(qc, measurements)
qnode = qml.QNode(qfunc, dev)
The QNode can be evaluated and differentiated:
>>> x, y = np.array([0.4, 0.5], requires_grad=True)
>>> qnode(x, y)
tensor(0.80830707, requires_grad=True)
>>> qml.grad(qnode)(x, y)
(tensor(-0.34174675, requires_grad=True), tensor(-0.44158016, requires_grad=True))
This shows how easy it is to make a Qiskit circuit differentiable with PennyLane.
In addition to circuits, it is also possible to convert operators from Qiskit to PennyLane with a new function called qml.from_qiskit_op
. (#5251)
A Qiskit SparsePauliOp can be converted to a PennyLane operator using qml.from_qiskit_op
:
>>> from qiskit.quantum_info import SparsePauliOp
>>> qiskit_op = SparsePauliOp(["II", "XY"])
>>> qiskit_op SparsePauliOp(['II', 'XY'], coeffs=[1.+0.j, 1.+0.j])
>>> pl_op = qml.from_qiskit_op(qiskit_op)
>>> pl_op I(0) + X(1) @ Y(0)
Combined with qml.from_qiskit
, it becomes easy to quickly calculate quantities like expectation values by converting the whole workflow to PennyLane:
qc = QuantumCircuit(2) # Create circuit
qc.rx(0.785, 0)
qc.ry(1.57, 1)
measurements = qml.expval(pl_op) # Create QNode
qfunc = qml.from_qiskit(qc, measurements)
qnode = qml.QNode(qfunc, dev)
>>> qnode() # Evaluate!
tensor(0.29317504, requires_grad=True)
Mid-circuit measurements can now be more scalable and efficient in finite-shots mode with default.qubit
by simulating them in a similar way to what happens on quantum hardware. (#5088) (#5120)
Previously, mid-circuit measurements (MCMs) would be automatically replaced with an additional qubit using the @qml.defer_measurements
transform. The circuit below would have required thousands of qubits to simulate.
Now, MCMs are performed in a similar way to quantum hardware with finite shots on default.qubit
. For each shot and each time an MCM is encountered, the device evaluates the probability of projecting onto |0>
or |1>
and makes a random choice to collapse the circuit state. This approach works well when there are a lot of MCMs and the number of shots is not too high.
import pennylane as qml
dev = qml.device("default.qubit", shots=10)
@qml.qnode(dev)
def f():
for i in range(1967):
qml.Hadamard(0)
qml.measure(0)
return qml.sample(qml.PauliX(0))
>>> f()
tensor([-1, -1, -1, 1, 1, -1, 1, -1, 1, -1], requires_grad=True)
Over the past few releases, PennyLane's approach to operator arithmetic has been in the process of being overhauled. We have a few objectives:
The updated operator arithmetic functionality is still being finalized, but can be activated using qml.operation.enable_new_opmath()
. In the next release, the new behaviour will become the default, so we recommend enabling now to become familiar with the new system!
The following updates have been made in this version of PennyLane:
You can now easily access Pauli operators via I
, X
, Y
, and Z
: (#5116)
>>> from pennylane import I, X, Y, Z
>>> X(0) X(0) ```
The original long-form names `Identity`, `PauliX`, `PauliY`, and `PauliZ` remain available, but use of the short-form names is now recommended.
A new qml.commutator
function is now available that allows you to compute commutators between PennyLane operators. (#5051) (#5052) (#5098)
>>> qml.commutator(X(0), Y(0))
2j * Z(0)
Operators in PennyLane can have a backend Pauli representation, which can be used to perform faster operator arithmetic. Now, the Pauli representation will be automatically used for calculations when available. (#4989) (#5001) (#5003) (#5017) (#5027)
The Pauli representation can be optionally accessed via op.pauli_rep
:
>>> qml.operation.enable_new_opmath()
>>> op = X(0) + Y(0)
>>> op.pauli_rep
1.0 * X(0) + 1.0 * Y(0)
Extensive improvements have been made to the string representations of PennyLane operators, making them shorter and possible to copy-paste as valid PennyLane code. (#5116) (#5138)
>>> 0.5 * X(0)
0.5 * X(0)
>>> 0.5 * (X(0) + Y(1))
0.5 * (X(0) + Y(1))
Sums with many terms are broken up into multiple lines, but can still be copied back as valid code:
>>> 0.5 * (X(0) @ X(1)) + 0.7 * (X(1) @ X(2)) + 0.8 * (X(2) @ X(3))
(
0.5 * (X(0) @ X(1))
+ 0.7 * (X(1) @ X(2))
+ 0.8 * (X(2) @ X(3))
)
Linear combinations of operators and operator multiplication via Sum
and Prod
, respectively, have been updated to reach feature parity with Hamiltonian
and Tensor
, respectively. This should minimize the effort to port over any existing code. (#5070) (#5132) (#5133)
Updates include support for grouping via the pauli
module:
>>> obs = [X(0) @ Y(1), Z(0), Y(0) @ Z(1), Y(1)]
>>> qml.pauli.group_observables(obs)
[[Y(0) @ Z(1)], [X(0) @ Y(1), Y(1)], [Z(0)]]
A new default.clifford
device enables efficient simulation of large-scale Clifford circuits defined in PennyLane through the use of stim as a backend. (#4936) (#4954) (#5144)
Given a circuit with only Clifford gates, one can use this device to obtain the usual range of PennyLane measurements as well as the state represented in the Tableau form of Aaronson & Gottesman (2004):
import pennylane as qml
dev = qml.device("default.clifford", tableau=True)
@qml.qnode(dev)
def circuit():
qml.CNOT(wires=[0, 1])
qml.PauliX(wires=[1])
qml.ISWAP(wires=[0, 1])
qml.Hadamard(wires=[0])
return qml.state()
>>> circuit()
array([[0, 1, 1, 0, 0],
[1, 0, 1, 1, 1],
[0, 0, 0, 1, 0],
[1, 0, 0, 1, 1]])
The default.clifford
device also supports the PauliError
, DepolarizingChannel
, BitFlip
and PhaseFlip
noise channels when operating in finite-shot mode.
Vector-Jacobian products (VJPs) can result in faster computations when the output of your quantum Node has a low dimension. They can be enabled by setting device_vjp=True
when loading a QNode. In the next release of PennyLane, VJPs are planned to be used by default, when available.
In this release, we have unlocked:
Adjoint device VJPs can be used with jax.jacobian
, meaning that device_vjp=True
is always faster when using JAX with default.qubit
. (#4963)
PennyLane can now use lightning-provided VJPs. (#4914)
VJPs can be used with TensorFlow, though support has not yet been added for tf.Function
and Tensorflow Autograph. (#4676)
Measuring qml.probs
is now faster due to an optimization in converting samples to counts. (#5145)
The performance of circuit-cutting workloads with large numbers of generated tapes has been improved. (#5005)
Queueing (AnnotatedQueue
) has been removed from qml.cut_circuit
and qml.cut_circuit_mc
to improve performance for large workflows. (#5108)
A new function called qml.fermi.parity_transform
has been added for parity mapping of a fermionic Hamiltonian. (#4928)
It is now possible to transform a fermionic Hamiltonian to a qubit Hamiltonian with parity mapping.
import pennylane as qml
fermi_ham = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
qubit_ham = qml.fermi.parity_transform(fermi_ham, n=6)
>>> print(qubit_ham)
-0.25j * Y(0) + (-0.25+0j) * (X(0) @ Z(1)) + (0.25+0j) * X(0) + 0.25j * (Y(0) @ Z(1))
The transform split_non_commuting
now accepts measurements of type probs
, sample
, and counts
, which accept both wires and observables. (#4972)
The efficiency of matrix calculations when an operator is symmetric over a given set of wires has been improved. (#3601)
The pennylane/math/quantum.py
module now has support for computing the minimum entropy of a density matrix. (#3959)
>>> x = [1, 0, 0, 1] / np.sqrt(2)
>>> x = qml.math.dm_from_state_vector(x)
>>> qml.math.min_entropy(x, indices=[0])
0.6931471805599455
A function called apply_operation
that applies operations to device-compatible states has been added to the new qutrit_mixed
module found in qml.devices
. (#5032)
A function called measure
has been added to the new qutrit_mixed
module found in qml.devices
that measures device-compatible states for a collection of measurement processes. (#5049)
A partial_trace
function has been added to qml.math
for taking the partial trace of matrices. (#5152)
The following capabilities have been added for Pauli arithmetic: (#4989) (#5001) (#5003) (#5017) (#5027) (#5018)
You can now multiply PauliWord
and PauliSentence
instances by scalars (e.g., 0.5 * PauliWord({0: "X"})
or 0.5 * PauliSentence({PauliWord({0: "X"}): 1.})
).
You can now intuitively add and subtract PauliWord
and PauliSentence
instances and scalars together (scalars are treated implicitly as multiples of the identity, I
). For example, ps1 + pw1 + 1.
for some Pauli word pw1 = PauliWord({0: "X", 1: "Y"})
and Pauli sentence ps1 = PauliSentence({pw1: 3.})
.
You can now element-wise multiply PauliWord
, PauliSentence
, and operators together with qml.dot
(e.g., qml.dot([0.5, -1.5, 2], [pw1, ps1, id_word])
with id_word = PauliWord({})
).
qml.matrix
now accepts PauliWord
and PauliSentence
instances (e.g., qml.matrix(PauliWord({0: "X"}))
).
It is now possible to compute commutators with Pauli operators natively with the new commutator
method.
>>> op1 = PauliWord({0: "X", 1: "X"})
>>> op2 = PauliWord({0: "Y"}) + PauliWord({1: "Y"})
>>> op1.commutator(op2) 2j * Z(0) @ X(1) + 2j * X(0) @ Z(1)
Composite operations (e.g., those made with qml.prod
and qml.sum
) and scalar-product operations convert Hamiltonian
and Tensor
operands to Sum
and Prod
types, respectively. This helps avoid the mixing of incompatible operator types. (#5031) (#5063)
qml.Identity()
can be initialized without wires. Measuring it is currently not possible, though. (#5106)
qml.dot
now returns a Sum
class even when all the coefficients match. (#5143)
qml.pauli.group_observables
now supports grouping Prod
and SProd
operators. (#5070)
The performance of converting a PauliSentence
to a Sum
has been improved. (#5141) (#5150)
Akin to qml.Hamiltonian
features, the coefficients and operators that make up composite operators formed via Sum
or Prod
can now be accessed with the terms()
method. (#5132) (#5133) (#5164)
>>> qml.operation.enable_new_opmath()
>>> op = X(0) @ (0.5 * X(1) + X(2))
>>> op.terms()
([0.5, 1.0],
[X(1) @ X(0),
X(2) @ X(0)])
String representations of ParametrizedHamiltonian
have been updated to match the style of other PL operators. (#5215)
The pl-device-test
suite is now compatible with the qml.devices.Device
interface. (#5229)
The QSVT
operation now determines its data
from the block encoding and projector operator data. (#5226) (#5248)
The BlockEncode
operator is now JIT-compatible with JAX. (#5110)
The qml.qsvt
function uses qml.GlobalPhase
instead of qml.exp
to define a global phase. (#5105)
The tests/ops/functions/conftest.py
test has been updated to ensure that all operator types are tested for validity. (#4978)
A new pennylane.workflow
module has been added. This module now contains qnode.py
, execution.py
, set_shots.py
, jacobian_products.py
, and the submodule interfaces
. (#5023)
A more informative error is now raised when calling adjoint_jacobian
with trainable state-prep operations. (#5026)
qml.workflow.get_transform_program
and qml.workflow.construct_batch
have been added to inspect the transform program and batch of tapes at different stages. (#5084)
All custom controlled operations such as CRX
, CZ
, CNOT
, ControlledPhaseShift
now inherit from ControlledOp
, giving them additional properties such as control_wire
and control_values
. Calling qml.ctrl
on RX
, RY
, RZ
, Rot
, and PhaseShift
with a single control wire will return gates of types CRX
, CRY
, etc. as opposed to a general Controlled
operator. (#5069) (#5199)
The CI will now fail if coverage data fails to upload to codecov. Previously, it would silently pass and the codecov check itself would never execute. (#5101)
qml.ctrl
called on operators with custom controlled versions will now return instances of the custom class, and it will flatten nested controlled operators to a single multi-controlled operation. For PauliX
, CNOT
, Toffoli
, and MultiControlledX
, calling qml.ctrl
will always resolve to the best option in CNOT
, Toffoli
, or MultiControlledX
depending on the number of control wires and control values. (#5125)
Unwanted warning filters have been removed from tests and no PennyLaneDeprecationWarning
s are being raised unexpectedly. (#5122)
New error tracking and propagation functionality has been added (#5115) (#5121)
The method map_batch_transform
has been replaced with the method _batch_transform
implemented in TransformDispatcher
. (#5212)
TransformDispatcher
can now dispatch onto a batch of tapes, making it easier to compose transforms when working in the tape paradigm. (#5163)
qml.ctrl
is now a simple wrapper that either calls PennyLane's built in create_controlled_op
or uses the Catalyst implementation. (#5247)
Controlled composite operations can now be decomposed using ZYZ rotations. (#5242)
New functions called qml.devices.modifiers.simulator_tracking
and qml.devices.modifiers.single_tape_support
have been added to add basic default behavior onto a device class. (#5200)
Passing additional arguments to a transform that decorates a QNode must now be done through the use of functools.partial
. (#5046)
qml.ExpvalCost
has been removed. Users should use qml.expval()
moving forward. (#5097)
Caching of executions is now turned off by default when max_diff == 1
, as the classical overhead cost outweighs the probability that duplicate circuits exists. (#5243)
The entry point convention registering compilers with PennyLane has changed. (#5140)
To allow for packages to register multiple compilers with PennyLane, the entry_points
convention under the designated group name pennylane.compilers
has been modified.
Previously, compilers would register qjit
(JIT decorator), ops
(compiler-specific operations), and context
(for tracing and program capture).
Now, compilers must register compiler_name.qjit
, compiler_name.ops
, and compiler_name.context
, where compiler_name
is replaced by the name of the provided compiler.
For more information, please see the documentation on adding compilers.
PennyLane source code is now compatible with the latest version of black
. (#5112) (#5119)
gradient_analysis_and_validation
has been renamed to find_and_validate_gradient_methods
. Instead of returning a list, it now returns a dictionary of gradient methods for each parameter index, and no longer mutates the tape. (#5035)
Multiplying two PauliWord
instances no longer returns a tuple (new_word, coeff)
but instead PauliSentence({new_word: coeff})
. The old behavior is still available with the private method PauliWord._matmul(other)
for faster processing. (#5045)
Observable.return_type
has been removed. Instead, you should inspect the type of the surrounding measurement process. (#5044)
ClassicalShadow.entropy()
no longer needs an atol
keyword as a better method to estimate entropies from approximate density matrix reconstructions (with potentially negative eigenvalues). (#5048)
Controlled operators with a custom controlled version decompose like how their controlled counterpart decomposes as opposed to decomposing into their controlled version. (#5069) (#5125)
For example:
>>> qml.ctrl(qml.RX(0.123, wires=1), control=0).decomposition()
[
RZ(1.5707963267948966, wires=[1]),
RY(0.0615, wires=[1]),
CNOT(wires=[0, 1]),
RY(-0.0615, wires=[1]),
CNOT(wires=[0, 1]),
RZ(-1.5707963267948966, wires=[1])
]
QuantumScript.is_sampled
and QuantumScript.all_sampled
have been removed. Users should now validate these properties manually. (#5072)
qml.transforms.one_qubit_decomposition
and qml.transforms.two_qubit_decomposition
have been removed. Instead, you should use qml.ops.one_qubit_decomposition
and qml.ops.two_qubit_decomposition
. (#5091)
Calling qml.matrix
without providing a wire_order
on objects where the wire order could be ambiguous now raises a warning. In the future, the wire_order
argument will be required in these cases. (#5039)
Operator.validate_subspace(subspace)
has been relocated to the qml.ops.qutrit.parametric_ops
module and will be removed from the Operator class in an upcoming release. (#5067)
Matrix and tensor products between PauliWord
and PauliSentence
instances are done using the @
operator, *
will be used only for scalar multiplication. Note also the breaking change that the product of two PauliWord
instances now returns a PauliSentence
instead of a tuple (new_word, coeff)
. (#4989) (#5054)
MeasurementProcess.name
and MeasurementProcess.data
are now deprecated, as they contain dummy values that are no longer needed. (#5047) (#5071) (#5076) (#5122)
qml.pauli.pauli_mult
and qml.pauli.pauli_mult_with_phase
are now deprecated. Instead, you should use qml.simplify(qml.prod(pauli_1, pauli_2))
to get the reduced operator. (#5057)
The private functions _pauli_mult
, _binary_matrix
and _get_pauli_map
from the pauli
module have been deprecated, as they are no longer used anywhere and the same functionality can be achieved using newer features in the pauli
module. (#5057)
Sum.ops
, Sum.coeffs
, Prod.ops
and Prod.coeffs
will be deprecated in the future. (#5164)
The module documentation for pennylane.tape
now explains the difference between QuantumTape
and QuantumScript
. (#5065)
A typo in a code example in the qml.transforms
API has been fixed. (#5014)
Documentation for qml.data
has been updated and now mentions a way to access the same dataset simultaneously from multiple environments. (#5029)
A clarification for the definition of argnum
added to gradient methods has been made. (#5035)
A typo in the code example for qml.qchem.dipole_of
has been fixed. (#5036)
A development guide on deprecations and removals has been added. (#5083)
A note about the eigenspectrum of second-quantized Hamiltonians has been added to qml.eigvals
. (#5095)
A warning about two mathematically equivalent Hamiltonians undergoing different time evolutions has been added to qml.TrotterProduct
and qml.ApproxTimeEvolution
. (#5137)
A reference to the paper that provides the image of the qml.QAOAEmbedding
template has been added. (#5130)
The docstring of qml.sample
has been updated to advise the use of single-shot expectations instead when differentiating a circuit. (#5237)
A quick start page has been added called "Importing Circuits". This explains how to import quantum circuits and operations defined outside of PennyLane. (#5281)
QubitChannel
can now be used with jitting. (#5288)
Fixed a bug in the matplotlib drawer where the colour of Barrier
did not match the requested style. (#5276)
qml.draw
and qml.draw_mpl
now apply all applied transforms before drawing. (#5277)
ctrl_decomp_zyz
is now differentiable. (#5198)
qml.ops.Pow.matrix()
is now differentiable with TensorFlow with integer exponents. (#5178)
The qml.MottonenStatePreparation
template has been updated to include a global phase operation. (#5166)
Fixed a queuing bug when using qml.prod
with a quantum function that queues a single operator. (#5170)
The qml.TrotterProduct
template has been updated to accept scalar products of operators as an input Hamiltonian. (#5073)
Fixed a bug where caching together with JIT compilation and broadcasted tapes yielded wrong results Operator.hash
now depends on the memory location, id
, of a JAX tracer instead of its string representation. (#3917)
qml.transforms.undo_swaps
can now work with operators with hyperparameters or nesting. (#5081)
qml.transforms.split_non_commuting
will now pass the original shots along. (#5081)
If argnum
is provided to a gradient transform, only the parameters specified in argnum
will have their gradient methods validated. (#5035)
StatePrep
operations expanded onto more wires are now compatible with backprop. (#5028)
qml.equal
works well with qml.Sum
operators when wire labels are a mix of integers and strings. (#5037)
The return value of Controlled.generator
now contains a projector that projects onto the correct subspace based on the control value specified. (#5068)
CosineWindow
no longer raises an unexpected error when used on a subset of wires at the beginning of a circuit. (#5080)
tf.function
now works with TensorSpec(shape=None)
by skipping batch size computation. (#5089)
PauliSentence.wires
no longer imposes a false order. (#5041)
qml.qchem.import_state
now applies the chemist-to-physicist sign convention when initializing a PennyLane state vector from classically pre-computed wavefunctions. That is, it interleaves spin-up/spin-down operators for the same spatial orbital index, as standard in PennyLane (instead of commuting all spin-up operators to the left, as is standard in quantum chemistry). (#5114)
Multi-wire controlled CNOT
and PhaseShift
are now be decomposed correctly. (#5125) (#5148)
draw_mpl
no longer raises an error when drawing a circuit containing an adjoint of a controlled operation. (#5149)
default.mixed
no longer throws ValueError
when applying a state vector that is not of type complex128
when used with tensorflow. (#5155)
ctrl_decomp_zyz
no longer raises a TypeError
if the rotation parameters are of type torch.Tensor
(#5183)
Comparing Prod
and Sum
objects now works regardless of nested structure with qml.equal
if the operators have a valid pauli_rep
property. (#5177)
Controlled GlobalPhase
with non-zero control wires no longer throws an error. (#5194)
A QNode
transformed with mitigate_with_zne
now accepts batch parameters. (#5195)
The matrix of an empty PauliSentence
instance is now correct (all-zeros). Further, matrices of empty PauliWord
and PauliSentence
instances can now be turned into matrices. (#5188)
PauliSentence
instances can handle matrix multiplication with PauliWord
instances. (#5208)
CompositeOp.eigendecomposition
is now JIT-compatible. (#5207)
QubitDensityMatrix
now works with JAX-JIT on the default.mixed
device. (#5203) (#5236)
When a QNode specifies diff_method="adjoint"
, default.qubit
no longer tries to decompose non-trainable operations with non-scalar parameters such as QubitUnitary
. (#5233)
The overwriting of the class names of I
, X
, Y
, and Z
no longer happens in the initialization after causing problems with datasets. This now happens globally. (#5252)
The adjoint_metric_tensor
transform now works with jax
. (#5271)
This release contains contributions from (in alphabetical order):
Abhishek Abhishek, Mikhail Andrenkov, Utkarsh Azad, Trenten Babcock, Gabriel Bottrill, Thomas Bromley, Astral Cai, Skylar Chan, Isaac De Vlugt, Diksha Dhawan, Lillian Frederiksen, Pietropaolo Frisoni, Eugenio Gigante, Diego Guala, David Ittah, Soran Jahangiri, Jacky Jiang, Korbinian Kottmann, Christina Lee, Xiaoran Li, Vincent Michaud-Rioux, Romain Moyard, Pablo Antonio Moreno Casares, Erick Ochoa Lopez, Lee J. O'Riordan, Mudit Pandey, Alex Preciado, Matthew Silverman, Jay Soni.
This postfix release pins additional requirements in doc/requirements.txt
for building the website documentation. This allows the website to be rebuilt to show the "Getting involved" section.
It is now possible to return statistics of composite mid-circuit measurements. (#4888)
Mid-circuit measurement results can be composed using basic arithmetic operations and then statistics can be calculated by putting the result within a PennyLane measurement like qml.expval()
. For example:
import pennylane as qml
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circuit(phi, theta):
qml.RX(phi, wires=0)
m0 = qml.measure(wires=0)
qml.RY(theta, wires=1)
m1 = qml.measure(wires=1)
return qml.expval(~m0 + m1)
print(circuit(1.23, 4.56))
1.2430187928114291
Another option, for ease-of-use when using qml.sample()
, qml.probs()
, or qml.counts()
, is to provide a simple list of mid-circuit measurement results:
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circuit(phi, theta):
qml.RX(phi, wires=0)
m0 = qml.measure(wires=0)
qml.RY(theta, wires=1)
m1 = qml.measure(wires=1)
return qml.sample(op=[m0, m1])
print(circuit(1.23, 4.56, shots=5))
[[0 1]
[0 1]
[0 0]
[1 0]
[0 1]]
Composite mid-circuit measurement statistics are supported on default.qubit
and default.mixed
. To learn more about which measurements and arithmetic operators are supported, refer to the measurements page and the documentation for qml.measure.
Mid-circuit measurements can now be visualized with the text-based qml.draw()
and the graphical qml.draw_mpl()
methods. (#4775) (#4803) (#4832) (#4901) (#4850) (#4917) (#4930) (#4957)
Drawing of mid-circuit measurement capabilities including qubit reuse and reset, postselection, conditioning, and collecting statistics is now supported. Here is an all-encompassing example:
def circuit():
m0 = qml.measure(0, reset=True)
m1 = qml.measure(1, postselect=1)
qml.cond(m0 - m1 == 0, qml.S)(0)
m2 = qml.measure(1)
qml.cond(m0 + m1 == 2, qml.T)(0)
qml.cond(m2, qml.PauliX)(1)
The text-based drawer outputs:
>>> print(qml.draw(circuit)())
0: โโโคโโ โ0โฉโโโโโโโโSโโโโโโโTโโโโโค
1: โโโโโโโโโโโโโคโโโโโโโโโคโโโโโโโXโโค
โโโโโโโโโโโโโโโโฌโโโโโโโโฃ โ
โโโโโโฉโโโโโโโโ โ
โโโโโโโโ
The graphical drawer outputs:
>>> print(qml.draw_mpl(circuit)())
Catalyst, our next-generation compilation framework, is now accessible within PennyLane, allowing you to more easily benefit from hybrid just-in-time (JIT) compilation.
To access these features, simply install pennylane-catalyst
:
pip install pennylane-catalyst
The qml.compiler module provides support for hybrid quantum-classical compilation. (#4692) (#4979)
Through the use of the qml.qjit
decorator, entire workflows can be JIT compiled โ including both quantum and classical processing โ down to a machine binary on first-function execution. Subsequent calls to the compiled function will execute the previously-compiled binary, resulting in significant performance improvements.
import pennylane as qml
dev = qml.device("lightning.qubit", wires=2)
@qml.qjit
@qml.qnode(dev)
def circuit(theta):
qml.Hadamard(wires=0)
qml.RX(theta, wires=1)
qml.CNOT(wires=[0,1])
return qml.expval(qml.PauliZ(wires=1))
>>> circuit(0.5) # the first call, compilation occurs here array(0.)
>>> circuit(0.5) # the precompiled quantum function is called
array(0.)
Currently, PennyLane supports the Catalyst hybrid compiler with the qml.qjit
decorator. A significant benefit of Catalyst is the ability to preserve complex control flow around quantum operations โ such as if
statements and for
loops, and including measurement feedback โ during compilation, while continuing to support end-to-end autodifferentiation.
The following functions can now be used with the qml.qjit
decorator: qml.grad
, qml.jacobian
, qml.vjp
, qml.jvp
, and qml.adjoint
. (#4709) (#4724) (#4725) (#4726)
When qml.grad
or qml.jacobian
are used with @qml.qjit
, they are patched to catalyst.grad and catalyst.jacobian, respectively.
dev = qml.device("lightning.qubit", wires=1)
@qml.qjit
def workflow(x):
@qml.qnode(dev)
def circuit(x):
qml.RX(np.pi * x[0], wires=0)
qml.RY(x[1], wires=0)
return qml.probs()
g = qml.jacobian(circuit)
return g(x)
>>> workflow(np.array([2.0, 1.0]))
array([[ 3.48786850e-16, -4.20735492e-01],
[-8.71967125e-17, 4.20735492e-01]])
JIT-compatible functionality for control flow has been added via qml.for_loop
, qml.while_loop
, and qml.cond
. (#4698)
qml.for_loop
and qml.while_loop
can be deployed as decorators on functions that are the body of the loop. The arguments to both follow typical conventions:
@qml.for_loop(lower_bound, upper_bound, step)
@qml.while_loop(cond_function)
Here is a concrete example with qml.for_loop
:
dev = qml.device("lightning.qubit", wires=1)
@qml.qjit
@qml.qnode(dev)
def circuit(n: int, x: float):
@qml.for_loop(0, n, 1)
def loop_rx(i, x):
# perform some work and update (some of) the arguments
qml.RX(x, wires=0)
# update the value of x for the next iteration
return jnp.sin(x)
# apply the for loop
final_x = loop_rx(x)
return qml.expval(qml.PauliZ(0)), final_x
>>> circuit(7, 1.6)
(array(0.97926626), array(0.55395718))
The new qml.clifford_t_decomposition()
transform provides an approximate breakdown of an input circuit into the Clifford+T gateset. Behind the scenes, this decomposition is enacted via the sk_decomposition()
function using the Solovay-Kitaev algorithm. (#4801) (#4802)
The Solovay-Kitaev algorithm approximately decomposes a quantum circuit into the Clifford+T gateset. To account for this, a desired total circuit decomposition error, epsilon
, must be specified when using qml.clifford_t_decomposition
:
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circuit():
qml.RX(1.1, 0)
return qml.state()
circuit = qml.clifford_t_decomposition(circuit, epsilon=0.1)
>>> print(qml.draw(circuit)())
0: โโTโ โโHโโTโ โโHโโTโโHโโTโโHโโTโโHโโTโโHโโTโ โโHโโTโ โโTโ โโHโโTโ โโHโโTโโHโโTโโHโโTโโHโโTโโHโโTโ โโH
โโโTโ โโHโโTโโHโโGlobalPhase(0.39)โโค
The resource requirements of this circuit can also be evaluated:
>>> with qml.Tracker(dev) as tracker:
... circuit()
>>> resources_lst = tracker.history["resources"]
>>> resources_lst[0]
wires: 1
gates: 34
depth: 34
shots: Shots(total=None)
gate_types: {'Adjoint(T)': 8, 'Hadamard': 16, 'T': 9, 'GlobalPhase': 1}
gate_sizes: {1: 33, 0: 1}
Iterative Quantum Phase Estimation is now available with qml.iterative_qpe
. (#4804)
The subroutine can be used similarly to mid-circuit measurements:
import pennylane as qml
dev = qml.device("default.qubit", shots=5)
@qml.qnode(dev)
def circuit():
# Initial state
qml.PauliX(wires=[0])
# Iterative QPE
measurements = qml.iterative_qpe(qml.RZ(2., wires=[0]), ancilla=[1], iters=3)
return [qml.sample(op=meas) for meas in measurements]
>>> print(circuit())
[array([0, 0, 0, 0, 0]), array([1, 0, 0, 0, 0]), array([0, 1, 1, 1, 1])]
The $i$-th element in the list refers to the 5 samples generated by the $i$-th measurement of the algorithm.
The +=
operand can now be used with a PauliSentence
, which has also provides a performance boost. (#4662)
The Approximate Quantum Fourier Transform (AQFT) is now available with qml.AQFT
. (#4715)
qml.draw
and qml.draw_mpl
now render operator IDs. (#4749)
The ID can be specified as a keyword argument when instantiating an operator:
>>> def circuit():
... qml.RX(0.123, id="data", wires=0)
>>> print(qml.draw(circuit)())
0: โโRX(0.12,"data")โโค
Non-parametric operators such as Barrier
, Snapshot
, and Wirecut
have been grouped together and moved to pennylane/ops/meta.py
. Additionally, the relevant tests have been organized and placed in a new file, tests/ops/test_meta.py
. (#4789)
The TRX
, TRY
, and TRZ
operators are now differentiable via backpropagation on default.qutrit
. (#4790)
The function qml.equal
now supports ControlledSequence
operators. (#4829)
XZX decomposition has been added to the list of supported single-qubit unitary decompositions. (#4862)
==
and !=
operands can now be used with TransformProgram
and TransformContainers
instances. (#4858)
A qutrit_mixed
module has been added to qml.devices
to store helper functions for a future qutrit mixed-state device. A function called create_initial_state
has been added to this module that creates device-compatible initial states. (#4861)
The function qml.Snapshot
now supports arbitrary state-based measurements (i.e., measurements of type StateMeasurement
). (#4876)
qml.equal
now supports the comparison of QuantumScript
and BasisRotation
objects. (#4902) (#4919)
The function qml.draw_mpl
now accept a keyword argument fig
to specify the output figure window. (#4956)
qml.AmplitudeEmbedding
now supports batching when used with Tensorflow. (#4818)
default.qubit
can now evolve already batched states with qml.pulse.ParametrizedEvolution
. (#4863)
qml.ArbitraryUnitary
now supports batching. (#4745)
Operator and tape batch sizes are evaluated lazily, helping run expensive computations less frequently and an issue with Tensorflow pre-computing batch sizes. (#4911)
Autograd, PyTorch, and JAX can now use vector-Jacobian products (VJPs) provided by the device from the new device API. If a device provides a VJP, this can be selected by providing device_vjp=True
to a QNode or qml.execute
. (#4935) (#4557) (#4654) (#4878) (#4841)
>>> dev = qml.device('default.qubit')
>>> @qml.qnode(dev, diff_method="adjoint", device_vjp=True)
>>> def circuit(x):
... qml.RX(x, wires=0)
... return qml.expval(qml.PauliZ(0))
>>> with dev.tracker:
... g = qml.grad(circuit)(qml.numpy.array(0.1))
>>> dev.tracker.totals
{'batches': 1, 'simulations': 1, 'executions': 1, 'vjp_batches': 1, 'vjps': 1}
>>> g
-0.09983341664682815
qml.expval
with large Hamiltonian
objects is now faster and has a significantly lower memory footprint (and constant with respect to the number of Hamiltonian
terms) when the Hamiltonian
is a PauliSentence
. This is due to the introduction of a specialized dot
method in the PauliSentence
class which performs PauliSentence
-state
products. (#4839)
default.qubit
no longer uses a dense matrix for MultiControlledX
for more than 8 operation wires. (#4673)
Some relevant Pytests have been updated to enable its use as a suite of benchmarks. (#4703)
default.qubit
now applies GroverOperator
faster by not using its matrix representation but a custom rule for apply_operation
. Also, the matrix representation of GroverOperator
now runs faster. (#4666)
A new pipeline to run benchmarks and plot graphs comparing with a fixed reference has been added. This pipeline will run on a schedule and can be activated on a PR with the label ci:run_benchmarks
. (#4741)
default.qubit
now supports adjoint differentiation for arbitrary diagonal state-based measurements. (#4865)
The benchmarks pipeline has been expanded to export all benchmark data to a single JSON file and a CSV file with runtimes. This includes all references and local benchmarks. (#4873)
qml.quantum_monte_carlo
and qml.simplify
now use the new transform system. (#4708) (#4949)
The formal requirement that type hinting be provided when using the qml.transform
decorator has been removed. Type hinting can still be used, but is now optional. Please use a type checker such as mypy if you wish to ensure types are being passed correctly. (#4942)
PennyLane now supports Python 3.12. (#4985)
SampleMeasurement
now has an optional method process_counts
for computing the measurement results from a counts dictionary. (#4941)
A new function called ops.functions.assert_valid
has been added for checking if an Operator
class is defined correctly. (#4764)
Shots
objects can now be multiplied by scalar values. (#4913)
GlobalPhase
now decomposes to nothing in case devices do not support global phases. (#4855)
Custom operations can now provide their matrix directly through the Operator.matrix()
method without needing to update the has_matrix
property. has_matrix
will now automatically be True
if Operator.matrix
is overridden, even if Operator.compute_matrix
is not. (#4844)
The logic for re-arranging states before returning them has been improved. (#4817)
When multiplying SparseHamiltonian
s by a scalar value, the result now stays as a SparseHamiltonian
. (#4828)
trainable_params
can now be set upon initialization of a QuantumScript
instead of having to set the parameter after initialization. (#4877)
default.qubit
now calculates the expectation value of Hermitian
operators in a differentiable manner. (#4866)
The rot
decomposition now has support for returning a global phase. (#4869)
The "pennylane_sketch"
MPL-drawer style has been added. This is the same as the "pennylane"
style, but with sketch-style lines. (#4880)
Operators now define a pauli_rep
property, an instance of PauliSentence
, defaulting to None
if the operator has not defined it (or has no definition in the Pauli basis). (#4915)
qml.ShotAdaptiveOptimizer
can now use a multinomial distribution for spreading shots across the terms of a Hamiltonian measured in a QNode. Note that this is equivalent to what can be done with qml.ExpvalCost
, but this is the preferred method because ExpvalCost
is deprecated. (#4896)
Decomposition of qml.PhaseShift
now uses qml.GlobalPhase
for retaining the global phase information. (#4657) (#4947)
qml.equal
for Controlled
operators no longer returns False
when equivalent but differently-ordered sets of control wires and control values are compared. (#4944)
All PennyLane Operator
subclasses are automatically tested by ops.functions.assert_valid
to ensure that they follow PennyLane Operator
standards. (#4922)
Probability measurements can now be calculated from a counts
dictionary with the addition of a process_counts
method in the ProbabilityMP
class. (#4952)
ClassicalShadow.entropy
now uses the algorithm outlined in 1106.5458 to project the approximate density matrix (with potentially negative eigenvalues) onto the closest valid density matrix. (#4959)
The ControlledSequence.compute_decomposition
default now decomposes the Pow
operators, improving compatibility with machine learning interfaces. (#4995)
The functions qml.transforms.one_qubit_decomposition
, qml.transforms.two_qubit_decomposition
, and qml.transforms.sk_decomposition
were moved to qml.ops.one_qubit_decomposition
, qml.ops.two_qubit_decomposition
, and qml.ops.sk_decomposition
, respectively. (#4906)
The function qml.transforms.classical_jacobian
has been moved to the gradients module and is now accessible as qml.gradients.classical_jacobian
. (#4900)
The transforms submodule qml.transforms.qcut
is now its own module: qml.qcut
. (#4819)
The decomposition of GroverOperator
now has an additional global phase operation. (#4666)
qml.cond
and the Conditional
operation have been moved from the transforms
folder to the ops/op_math
folder. qml.transforms.Conditional
will now be available as qml.ops.Conditional
. (#4860)
The prep
keyword argument has been removed from QuantumScript
and QuantumTape
. StatePrepBase
operations should be placed at the beginning of the ops
list instead. (#4756)
qml.gradients.pulse_generator
is now named qml.gradients.pulse_odegen
to adhere to paper naming conventions. (#4769)
Specifying control_values
passed to qml.ctrl
as a string is no longer supported. (#4816)
The rot
decomposition will now normalize its rotation angles to the range [0, 4pi]
for consistency (#4869)
QuantumScript.graph
is now built using tape.measurements
instead of tape.observables
because it depended on the now-deprecated Observable.return_type
property. (#4762)
The "pennylane"
MPL-drawer style now draws straight lines instead of sketch-style lines. (#4880)
The default value for the term_sampling
argument of ShotAdaptiveOptimizer
is now None
instead of "weighted_random_sampling"
. (#4896)
single_tape_transform
, batch_transform
, qfunc_transform
, and op_transform
are deprecated. Use the new qml.transform
function instead. (#4774)
Observable.return_type
is deprecated. Instead, you should inspect the type of the surrounding measurement process. (#4762) (#4798)
All deprecations now raise a qml.PennyLaneDeprecationWarning
instead of a UserWarning
. (#4814)
QuantumScript.is_sampled
and QuantumScript.all_sampled
are deprecated. Users should now validate these properties manually. (#4773)
With an algorithmic improvement to ClassicalShadow.entropy
, the keyword atol
becomes obsolete and will be removed in v0.35. (#4959)
Documentation for unitaries and operations' decompositions has been moved from qml.transforms
to qml.ops.ops_math
. (#4906)
Documentation for qml.metric_tensor
and qml.adjoint_metric_tensor
and qml.transforms.classical_jacobian
is now accessible via the gradients API page qml.gradients
in the documentation. (#4900)
Documentation for qml.specs
has been moved to the resource
module. (#4904)
Documentation for QCut has been moved to its own API page: qml.qcut
. (#4819)
The documentation page for qml.measurements
now links top-level accessible functions (e.g., qml.expval
) to their top-level pages rather than their module-level pages (e.g., qml.measurements.expval
). (#4750)
Information for the documentation of qml.matrix
about wire ordering has been added for using qml.matrix
on a QNode which uses a device with device.wires=None
. (#4874)
TransformDispatcher
now stops queuing when performing the transform when applying it to a qfunc. Only the output of the transform will be queued. (#4983)
qml.map_wires
now works properly with qml.cond
and qml.measure
. (#4884)
Pow
operators are now picklable. (#4966)
Finite differences and SPSA can now be used with tensorflow-autograph on setups that were seeing a bus error. (#4961)
qml.cond
no longer incorrectly queues operators used arguments. (#4948)
Attribute
objects now return False
instead of raising a TypeError
when checking if an object is inside the set. (#4933)
Fixed a bug where the parameter-shift rule of qml.ctrl(op)
was wrong if op
had a generator that has two or more eigenvalues and is stored as a SparseHamiltonian
. (#4899)
Fixed a bug where trainable parameters in the post-processing of finite-differences were incorrect for JAX when applying the transform directly on a QNode. (#4879)
qml.grad
and qml.jacobian
now explicitly raise errors if trainable parameters are integers. (#4836)
JAX-JIT now works with shot vectors. (#4772)
JAX can now differentiate a batch of circuits where one tape does not have trainable parameters. (#4837)
The decomposition of GroverOperator
now has the same global phase as its matrix. (#4666)
The tape.to_openqasm
method no longer mistakenly includes interface information in the parameter string when converting tapes using non-NumPy interfaces. (#4849)
qml.defer_measurements
now correctly transforms circuits when terminal measurements include wires used in mid-circuit measurements. (#4787)
Fixed a bug where the adjoint differentiation method would fail if an operation that has a parameter with grad_method=None
is present. (#4820)
MottonenStatePreparation
and BasisStatePreparation
now raise an error when decomposing a broadcasted state vector. (#4767)
Gradient transforms now work with overridden shot vectors and default.qubit
. (#4795)
Any ScalarSymbolicOp
, like Evolution
, now states that it has a matrix if the target is a Hamiltonian
. (#4768)
In default.qubit
, initial states are now initialized with the simulator's wire order, not the circuit's wire order. (#4781)
qml.compile
will now always decompose to expand_depth
, even if a target basis set is not specified. (#4800)
qml.transforms.transpile
can now handle measurements that are broadcasted onto all wires. (#4793)
Parametrized circuits whose operators do not act on all wires return PennyLane tensors instead of NumPy arrays, as expected. (#4811) (#4817)
qml.transforms.merge_amplitude_embedding
no longer depends on queuing, allowing it to work as expected with QNodes. (#4831)
qml.pow(op)
and qml.QubitUnitary.pow()
now also work with Tensorflow data raised to an integer power. (#4827)
The text drawer has been fixed to correctly label qml.qinfo
measurements, as well as qml.classical_shadow
qml.shadow_expval
. (#4803)
Removed an implicit assumption that an empty PauliSentence
gets treated as identity under multiplication. (#4887)
Using a CNOT
or PauliZ
operation with large batched states and the Tensorflow interface no longer raises an unexpected error. (#4889)
qml.map_wires
no longer fails when mapping nested quantum tapes. (#4901)
Conversion of circuits to openqasm now decomposes to a depth of 10, allowing support for operators requiring more than 2 iterations of decomposition, such as the ApproxTimeEvolution
gate. (#4951)
MPLDrawer
does not add the bonus space for classical wires when no classical wires are present. (#4987)
Projector
now works with parameter-broadcasting. (#4993) * The jax-jit interface can now be used with float32 mode. (#4990)
Keras models with a qnn.KerasLayer
no longer fail to save and load weights properly when they are named "weights". (#5008)
This release contains contributions from (in alphabetical order):
Guillermo Alonso, Ali Asadi, Utkarsh Azad, Gabriel Bottrill, Thomas Bromley, Astral Cai, Minh Chau, Isaac De Vlugt, Amintor Dusko, Pieter Eendebak, Lillian Frederiksen, Pietropaolo Frisoni, Josh Izaac, Juan Giraldo, Emiliano Godinez Ramirez, Ankit Khandelwal, Korbinian Kottmann, Christina Lee, Vincent Michaud-Rioux, Anurav Modak, Romain Moyard, Mudit Pandey, Matthew Silverman, Jay Soni, David Wierichs, Justin Woodring.
Fix gradient performance regression due to expansion of VJP products. (#4806)
qml.defer_measurements
now correctly transforms circuits when terminal measurements include wires used in mid-circuit measurements. (#4787)
Any ScalarSymbolicOp
, like Evolution
, now states that it has a matrix if the target is a Hamiltonian
. (#4768)
In default.qubit
, initial states are now initialized with the simulator's wire order, not the circuit's wire order. (#4781)
This release contains contributions from (in alphabetical order):
Christina Lee, Lee James O'Riordan, Mudit Pandey
It is now possible to request postselection on a mid-circuit measurement. (#4604)
This can be achieved by specifying the postselect
keyword argument in qml.measure
as either 0
or 1
, corresponding to the basis states.
import pennylane as qml
dev = qml.device("default.qubit")
@qml.qnode(dev, interface=None)
def circuit():
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
qml.measure(0, postselect=1)
return qml.expval(qml.PauliZ(1)), qml.sample(wires=1)
This circuit prepares the $| \Phi^{+} \rangle$ Bell state and postselects on measuring $|1\rangle$ in wire 0
. The output of wire 1
is then also $|1\rangle$ at all times:
>>> circuit(shots=10)
(-1.0, array([1, 1, 1, 1, 1, 1]))
Note that the number of shots is less than the requested amount because we have thrown away the samples where $|0\rangle$ was measured in wire 0
.
Measurement statistics can now be collected for mid-circuit measurements. (#4544)
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circ(x, y):
qml.RX(x, wires=0)
qml.RY(y, wires=1)
m0 = qml.measure(1)
return qml.expval(qml.PauliZ(0)), qml.expval(m0), qml.sample(m0)
>>> circ(1.0, 2.0, shots=10000)
(0.5606, 0.7089, array([0, 1, 1, ..., 1, 1, 1]))
Support is provided for both finite-shot and analytic modes and devices default to using the deferred measurement principle to enact the mid-circuit measurements.
Higher-order Trotter-Suzuki methods are now easily accessible through a new operation called TrotterProduct
. (#4661)
Trotterization techniques are an affective route towards accurate and efficient Hamiltonian simulation. The Suzuki-Trotter product formula allows for the ability to express higher-order approximations to the matrix exponential of a Hamiltonian, and it is now available to use in PennyLane via the TrotterProduct
operation. Simply specify the order
of the approximation and the evolution time
.
coeffs = [0.25, 0.75]
ops = [qml.PauliX(0), qml.PauliZ(0)]
H = qml.dot(coeffs, ops)
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def circuit():
qml.Hadamard(0)
qml.TrotterProduct(H, time=2.4, order=2)
return qml.state()
>>> circuit()
[-0.13259524+0.59790098j 0. +0.j -0.13259524-0.77932754j 0. +0.j ]
Approximating matrix exponentiation with random product formulas, qDrift, is now available with the new QDrift
operation. (#4671)
As shown in 1811.08017, qDrift is a Markovian process that can provide a speedup in Hamiltonian simulation. At a high level, qDrift works by randomly sampling from the Hamiltonian terms with a probability that depends on the Hamiltonian coefficients. This method for Hamiltonian simulation is now ready to use in PennyLane with the QDrift
operator. Simply specify the evolution time
and the number of samples drawn from the Hamiltonian, n
:
coeffs = [0.25, 0.75]
ops = [qml.PauliX(0), qml.PauliZ(0)]
H = qml.dot(coeffs, ops)
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def circuit():
qml.Hadamard(0)
qml.QDrift(H, time=1.2, n = 10)
return qml.probs()
>>> circuit()
array([0.61814334, 0. , 0.38185666, 0. ])
A new operator called CosineWindow
has been added to prepare an initial state based on a cosine wave function. (#4683)
As outlined in 2110.09590, the cosine tapering window is part of a modification to quantum phase estimation that can provide a cubic improvement to the algorithm's error rate. Using CosineWindow
will prepare a state whose amplitudes follow a cosinusoidal distribution over the computational basis.
import matplotlib.pyplot as plt
dev = qml.device('default.qubit', wires=4)
@qml.qnode(dev)
def example_circuit():
qml.CosineWindow(wires=range(4))
return qml.state()
output = example_circuit()
plt.style.use("pennylane.drawer.plot")
plt.bar(range(len(output)), output)
plt.show()
Controlled gate sequences raised to decreasing powers, a sub-block in quantum phase estimation, can now be created with the new ControlledSequence
operator. (#4707)
To use ControlledSequence
, specify the controlled unitary operator and the control wires, control
:
dev = qml.device("default.qubit", wires = 4)
@qml.qnode(dev)
def circuit():
for i in range(3):
qml.Hadamard(wires = i)
qml.ControlledSequence(qml.RX(0.25, wires = 3), control = [0, 1, 2])
qml.adjoint(qml.QFT)(wires = range(3))
return qml.probs(wires = range(3))
>>> print(circuit())
[0.92059345 0.02637178 0.00729619 0.00423258 0.00360545 0.00423258 0.00729619 0.02637178]
default.qubit
now uses the new qml.devices.Device
API and functionality in qml.devices.qubit
. If you experience any issues with the updated default.qubit
, please let us know by posting an issue. The old version of the device is still accessible by the short name default.qubit.legacy
, or directly via qml.devices.DefaultQubitLegacy
. (#4594) (#4436) (#4620) (#4632)
This changeover has a number of benefits for default.qubit
, including:
The number of wires is now optional โ simply having qml.device("default.qubit")
is valid! If wires are not provided at instantiation, the device automatically infers the required number of wires for each circuit provided for execution.
dev = qml.device("default.qubit")
@qml.qnode(dev)
def circuit():
qml.PauliZ(0)
qml.RZ(0.1, wires=1)
qml.Hadamard(2)
return qml.state()
>>> print(qml.draw(circuit)())
0: โโZโโโโโโโโโค State
1: โโRZ(0.10)โโค State
2: โโHโโโโโโโโโค State
default.qubit
is no longer silently swapped out with an interface-appropriate device when the backpropagation differentiation method is used. For example, consider:
import jax
dev = qml.device("default.qubit", wires=1)
@qml.qnode(dev, diff_method="backprop")
def f(x):
qml.RX(x, wires=0)
return qml.expval(qml.PauliZ(0))
f(jax.numpy.array(0.2))
In previous versions of PennyLane, the device will be swapped for the JAX equivalent:
>>> f.device
<DefaultQubitJax device (wires=1, shots=None) at 0x7f8c8bff50a0>
>>> f.device == dev
False
Now, default.qubit
can itself dispatch to all the interfaces in a backprop-compatible way and hence does not need to be swapped:
>>> f.device
<default.qubit device (wires=1) at 0x7f20d043b040>
>>> f.device == dev
True
A QNode that has been decorated with qjit
from PennyLane's Catalyst library for just-in-time hybrid compilation is now compatible with qml.draw
. (#4609)
import catalyst
@catalyst.qjit
@qml.qnode(qml.device("lightning.qubit", wires=3))
def circuit(x, y, z, c):
"""A quantum circuit on three wires."""
@catalyst.for_loop(0, c, 1)
def loop(i):
qml.Hadamard(wires=i)
qml.RX(x, wires=0)
loop()
qml.RY(y, wires=1)
qml.RZ(z, wires=2)
return qml.expval(qml.PauliZ(0))
draw = qml.draw(circuit, decimals=None)(1.234, 2.345, 3.456, 1)
>>> print(draw)
0: โโRXโโHโโโค <Z>
1: โโHโโโRYโโค
2: โโRZโโโโโโค
MeasurementProcess
and QuantumScript
objects are now registered as JAX PyTrees. (#4607) (#4608)
It is now possible to JIT-compile functions with arguments that are a MeasurementProcess
or a QuantumScript
:
tape0 = qml.tape.QuantumTape([qml.RX(1.0, 0), qml.RY(0.5, 0)], [qml.expval(qml.PauliZ(0))])
dev = qml.device('lightning.qubit', wires=5)
execute_kwargs = {"device": dev, "gradient_fn": qml.gradients.param_shift, "interface":"jax"}
jitted_execute = jax.jit(qml.execute, static_argnames=execute_kwargs.keys())
jitted_execute((tape0, ), **execute_kwargs)
Computationally expensive functions in integrals.py
, electron_repulsion
and _hermite_coulomb
, have been modified to replace indexing with slicing for better compatibility with JAX. (#4685)
qml.qchem.import_state
has been extended to import more quantum chemistry wavefunctions, from MPS, DMRG and SHCI classical calculations performed with the Block2 and Dice libraries. #4523 #4524 #4626 #4634
Check out our how-to guide to learn more about how PennyLane integrates with your favourite quantum chemistry libraries.
The qchem fermionic_dipole
and particle_number
functions have been updated to use a FermiSentence
. The deprecated features for using tuples to represent fermionic operations are removed. (#4546) (#4556)
The tensor-network template qml.MPS
now supports changing the offset
between subsequent blocks for more flexibility. (#4531)
Builtin types support with qml.pauli_decompose
have been improved. (#4577)
AmplitudeEmbedding
now inherits from StatePrep
, allowing for it to not be decomposed when at the beginning of a circuit, thus behaving like StatePrep
. (#4583)
qml.cut_circuit
is now compatible with circuits that compute the expectation values of Hamiltonians with two or more terms. (#4642)
default.qubit
now tracks the number of equivalent qpu executions and total shots when the device is sampling. Note that "simulations"
denotes the number of simulation passes, whereas "executions"
denotes how many different computational bases need to be sampled in. Additionally, the new default.qubit
tracks the results of device.execute
. (#4628) (#4649)
DefaultQubit
can now accept a jax.random.PRNGKey
as a seed
to set the key for the JAX pseudo random number generator when using the JAX interface. This corresponds to the prng_key
on default.qubit.jax
in the old API. (#4596)
The JacobianProductCalculator
abstract base class and implementations TransformJacobianProducts
DeviceDerivatives
, and DeviceJacobianProducts
have been added to pennylane.interfaces.jacobian_products
. (#4435) (#4527) (#4637)
DefaultQubit
dispatches to a faster implementation for applying ParametrizedEvolution
to a state when it is more efficient to evolve the state than the operation matrix. (#4598) (#4620)
Wires can be provided to the new device API. (#4538) (#4562)
qml.sample()
in the new device API now returns a np.int64
array instead of np.bool8
. (#4539)
The new device API now has a repr()
method. (#4562)
DefaultQubit
now works as expected with measurement processes that don't specify wires. (#4580)
Various improvements to measurements have been made for feature parity between default.qubit.legacy
and the new DefaultQubit
. This includes not trying to squeeze batched CountsMP
results and implementing MutualInfoMP.map_wires
. (#4574)
devices.qubit.simulate
now accepts an interface keyword argument. If a QNode with DefaultQubit
specifies an interface, the result will be computed with that interface. (#4582)
ShotAdaptiveOptimizer
has been updated to pass shots to QNode executions instead of overriding device shots before execution. This makes it compatible with the new device API. (#4599)
pennylane.devices.preprocess
now offers the transforms decompose
, validate_observables
, validate_measurements
, validate_device_wires
, validate_multiprocessing_workers
, warn_about_trainable_observables
, and no_sampling
to assist in constructing devices under the new device API. (#4659)
Updated qml.device
, devices.preprocessing
and the tape_expand.set_decomposition
context manager to bring DefaultQubit
to feature parity with default.qubit.legacy
with regards to using custom decompositions. The DefaultQubit
device can now be included in a set_decomposition
context or initialized with a custom_decomps
dictionary, as well as a custom max_depth
for decomposition. (#4675)
The StateMP
measurement now accepts a wire order (e.g., a device wire order). The process_state
method will re-order the given state to go from the inputted wire-order to the process's wire-order. If the process's wire-order contains extra wires, it will assume those are in the zero-state. (#4570) (#4602)
Methods called add_transform
and insert_front_transform
have been added to TransformProgram
. (#4559)
Instances of the TransformProgram
class can now be added together. (#4549)
Transforms can now be applied to devices following the new device API. (#4667)
All gradient transforms have been updated to the new transform program system. (#4595)
Multi-controlled operations with a single-qubit special unitary target can now automatically decompose. (#4697)
pennylane.defer_measurements
will now exit early if the input does not contain mid circuit measurements. (#4659)
The density matrix aspects of StateMP
have been split into their own measurement process called DensityMatrixMP
. (#4558)
StateMeasurement.process_state
now assumes that the input is flat. ProbabilityMP.process_state
has been updated to reflect this assumption and avoid redundant reshaping. (#4602)
qml.exp
returns a more informative error message when decomposition is unavailable for non-unitary operators. (#4571)
Added qml.math.get_deep_interface
to get the interface of a scalar hidden deep in lists or tuples. (#4603)
Updated qml.math.ndim
and qml.math.shape
to work with built-in lists or tuples that contain interface-specific scalar dat (e.g., [(tf.Variable(1.1), tf.Variable(2.2))]
). (#4603)
When decomposing a unitary matrix with one_qubit_decomposition
and opting to include the GlobalPhase
in the decomposition, the phase is no longer cast to dtype=complex
. (#4653)
_qfunc_output
has been removed from QuantumScript
, as it is no longer necessary. There is still a _qfunc_output
property on QNode
instances. (#4651)
qml.data.load
properly handles parameters that come after 'full'
(#4663)
The qml.jordan_wigner
function has been modified to optionally remove the imaginary components of the computed qubit operator, if imaginary components are smaller than a threshold. (#4639)
qml.data.load
correctly performs a full download of the dataset after a partial download of the same dataset has already been performed. (#4681) * The performance of qml.data.load()
has been improved when partially loading a dataset (#4674)
Plots generated with the pennylane.drawer.plot
style of matplotlib.pyplot
now have black axis labels and are generated at a default DPI of 300. (#4690)
Shallow copies of the QNode
now also copy the execute_kwargs
and transform program. When applying a transform to a QNode
, the new qnode is only a shallow copy of the original and thus keeps the same device. (#4736)
QubitDevice
and CountsMP
are updated to disregard samples containing failed hardware measurements (record as np.NaN
) when tallying samples, rather than counting failed measurements as ground-state measurements, and to display qml.counts
coming from these hardware devices correctly. (#4739)
qml.defer_measurements
now raises an error if a transformed circuit measures qml.probs
, qml.sample
, or qml.counts
without any wires or observable, or if it measures qml.state
. (#4701)
The device test suite now converts device keyword arguments to integers or floats if possible. (#4640)
MeasurementProcess.eigvals()
now raises an EigvalsUndefinedError
if the measurement observable does not have eigenvalues. (#4544)
The __eq__
and __hash__
methods of Operator
and MeasurementProcess
no longer rely on the object's address in memory. Using ==
with operators and measurement processes will now behave the same as qml.equal
, and objects of the same type with the same data and hyperparameters will have the same hash. (#4536)
In the following scenario, the second and third code blocks show the previous and current behaviour of operator and measurement process equality, determined by ==
:
op1 = qml.PauliX(0)
op2 = qml.PauliX(0)
op3 = op1
Old behaviour:
>>> op1 == op2
False
>>> op1 == op3
True
New behaviour:
>>> op1 == op2
True
>>> op1 == op3
True
The __hash__
dunder method defines the hash of an object. The default hash of an object is determined by the objects memory address. However, the new hash is determined by the properties and attributes of operators and measurement processes. Consider the scenario below. The second and third code blocks show the previous and current behaviour.
op1 = qml.PauliX(0)
op2 = qml.PauliX(0)
Old behaviour:
>>> print({op1, op2})
{PauliX(wires=[0]), PauliX(wires=[0])}
New behaviour:
>>> print({op1, op2})
{PauliX(wires=[0])}
The old return type and associated functions qml.enable_return
and qml.disable_return
have been removed. (#4503)
The mode
keyword argument in QNode
has been removed. Please use grad_on_execution
instead. (#4503)
The CV observables qml.X
and qml.P
have been removed. Please use qml.QuadX
and qml.QuadP
instead. (#4533)
The sampler_seed
argument of qml.gradients.spsa_grad
has been removed. Instead, the sampler_rng
argument should be set, either to an integer value, which will be used to create a PRNG internally, or to a NumPy pseudo-random number generator (PRNG) created via np.random.default_rng(seed)
. (#4550)
The QuantumScript.set_parameters
method and the QuantumScript.data
setter have been removed. Please use QuantumScript.bind_new_parameters
instead. (#4548)
The method tape.unwrap()
and corresponding UnwrapTape
and Unwrap
classes have been removed. Instead of tape.unwrap()
, use qml.transforms.convert_to_numpy_parameters
. (#4535)
The RandomLayers.compute_decomposition
keyword argument ratio_imprivitive
has been changed to ratio_imprim
to match the call signature of the operation. (#4552)
The private TmpPauliRot
operator used for SpecialUnitary
no longer decomposes to nothing when the theta value is trainable. (#4585)
ProbabilityMP.marginal_prob
has been removed. Its contents have been moved into process_state
, which effectively just called marginal_prob
with np.abs(state) ** 2
. (#4602)
The following decorator syntax for transforms has been deprecated and will raise a warning: (#4457)
@transform_fn(**transform_kwargs)
@qml.qnode(dev)
def circuit():
...
If you are using a transform that has supporting transform_kwargs
, please call the transform directly using circuit = transform_fn(circuit, **transform_kwargs)
, or use functools.partial
:
@functools.partial(transform_fn, **transform_kwargs)
@qml.qnode(dev)
def circuit():
...
The prep
keyword argument in QuantumScript
has been deprecated and will be removed from QuantumScript
. StatePrepBase
operations should be placed at the beginning of the ops
list instead. (#4554)
qml.gradients.pulse_generator
has been renamed to qml.gradients.pulse_odegen
to adhere to paper naming conventions. During v0.33, pulse_generator
is still available but raises a warning. (#4633)
A warning section in the docstring for DefaultQubit
regarding the start method used in multiprocessing has been added. This may help users circumvent issues arising in Jupyter notebooks on macOS for example. (#4622)
Documentation improvements to the new device API have been made. The documentation now correctly states that interface-specific parameters are only passed to the device for backpropagation derivatives. (#4542)
Functions for qubit-simulation to the qml.devices
sub-page of the "Internal" section have been added. Note that these functions are unstable while device upgrades are underway. (#4555)
A documentation improvement to the usage example in the qml.QuantumMonteCarlo
page has been made. An integral was missing the differential $dx$. (#4593)
A documentation improvement for the use of the pennylane
style of qml.drawer
and the pennylane.drawer.plot
style of matplotlib.pyplot
has been made by clarifying the use of the default font. (#4690)
Jax jit now works when a probability measurement is broadcasted onto all wires. (#4742)
Fixed LocalHilbertSchmidt.compute_decomposition
so that the template can be used in a QNode. (#4719)
Fixes transforms.transpile
with arbitrary measurement processes. (#4732)
Providing work_wires=None
to qml.GroverOperator
no longer interprets None
as a wire. (#4668)
Fixed an issue where the __copy__
method of the qml.Select()
operator attempted to access un-initialized data. (#4551)
Fixed the skip_first
option in expand_tape_state_prep
. (#4564)
convert_to_numpy_parameters
now uses qml.ops.functions.bind_new_parameters
. This reinitializes the operation and makes sure everything references the new NumPy parameters. (#4540)
tf.function
no longer breaks ProbabilityMP.process_state
, which is needed by new devices. (#4470)
Fixed unit tests for qml.qchem.mol_data
. (#4591)
Fixed ProbabilityMP.process_state
so that it allows for proper Autograph compilation. Without this, decorating a QNode that returns an expval
with tf.function
would fail when computing the expectation. (#4590)
The torch.nn.Module
properties are now accessible on a pennylane.qnn.TorchLayer
. (#4611)
qml.math.take
with Pytorch now returns tensor[..., indices]
when the user requests the last axis (axis=-1
). Without the fix, it would wrongly return tensor[indices]
. (#4605)
Ensured the logging TRACE
level works with gradient-free execution. (#4669)
This release contains contributions from (in alphabetical order):
Guillermo Alonso, Utkarsh Azad, Thomas Bromley, Isaac De Vlugt, Jack Brown, Stepan Fomichev, Joana Fraxanet, Diego Guala, Soran Jahangiri, Edward Jiang, Korbinian Kottmann, Ivana Kureฤiฤ Christina Lee, Lillian M. A. Frederiksen, Vincent Michaud-Rioux, Romain Moyard, Daniel F. Nino, Lee James O'Riordan, Mudit Pandey, Matthew Silverman, Jay Soni.
This release changes doc/requirements.txt
to upgrade jax
, jaxlib
, and pin ml-dtypes
.
It is now possible to encode an operator A
into a quantum circuit by decomposing it into a linear combination of unitaries using PREP (qml.StatePrep) and SELECT (qml.Select) routines. (#4431) (#4437) (#4444) (#4450) (#4506) (#4526)
Consider an operator A
composed of a linear combination of Pauli terms:
>>> A = qml.PauliX(2) + 2 * qml.PauliY(2) + 3 * qml.PauliZ(2)
A decomposable block-encoding circuit can be created:
def block_encode(A, control_wires):
probs = A.coeffs / np.sum(A.coeffs)
state = np.pad(np.sqrt(probs, dtype=complex), (0, 1))
unitaries = A.ops
qml.StatePrep(state, wires=control_wires)
qml.Select(unitaries, control=control_wires)
qml.adjoint(qml.StatePrep)(state, wires=control_wires)
>>> print(qml.draw(block_encode, show_matrices=False)(A, control_wires=[0, 1]))
0: โโญ|ฮจโฉโโญSelectโโญ|ฮจโฉโ โโค
1: โโฐ|ฮจโฉโโSelectโโฐ|ฮจโฉโ โโค
2: โโโโโโโฐSelectโโโโโโโโค
This circuit can be used as a building block within a larger QNode to perform algorithms such as QSVT and Hamiltonian simulation.
Decomposing a Hermitian matrix into a linear combination of Pauli words via qml.pauli_decompose
is now faster and differentiable. (#4395) (#4479) (#4493)
def find_coeffs(p):
mat = np.array([[3, p], [p, 3]])
A = qml.pauli_decompose(mat)
return A.coeffs
>>> import jax
>>> from jax import numpy as np
>>> jax.jacobian(find_coeffs)(np.array(2.))
Array([0., 1.], dtype=float32, weak_type=True)
Python-native logging can now be enabled with qml.logging.enable_logging()
. (#4377) (#4383)
Consider the following code that is contained in my_code.py
:
import pennylane as qml
qml.logging.enable_logging() # enables logging
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def f(x):
qml.RX(x, wires=0)
return qml.state()
f(0.5)
Executing my_code.py
with logging enabled will detail every step in PennyLane's pipeline that gets used to run your code.
$ python my_code.py
[1967-02-13 15:18:38,591][DEBUG][<PID 8881:MainProcess>] - pennylane.qnode.__init__()::"Creating QNode(func=<function f at 0x7faf2a6fbaf0>, device=<DefaultQubit device (wires=2, shots=None) at 0x7faf2a689b50>, interface=auto, diff_method=best, expansion_strategy=gradient, max_expansion=10, grad_on_execution=best, mode=None, cache=True, cachesize=10000, max_diff=1, gradient_kwargs={}"
...
Additional logging configuration settings can be specified by modifying the contents of the logging configuration file, which can be located by running qml.logging.config_path()
. Follow our logging docs page for more details!
Input states obtained from advanced quantum chemistry calculations can be used in a circuit. (#4427) (#4433) (#4461) (#4476) (#4505)
Quantum chemistry calculations rely on an initial state that is typically selected to be the trivial Hartree-Fock state. For molecules with a complicated electronic structure, using initial states obtained from affordable post-Hartree-Fock calculations helps to improve the efficiency of the quantum simulations. These calculations can be done with external quantum chemistry libraries such as PySCF.
It is now possible to import a PySCF solver object in PennyLane and extract the corresponding wave function in the form of a state vector that can be directly used in a circuit. First, perform your classical quantum chemistry calculations and then use the qml.qchem.import_state function to import the solver object and return a state vector.
>>> from pyscf import gto, scf, ci
>>> mol = gto.M(atom=[['H', (0, 0, 0)], ['H', (0,0,0.71)]], basis='sto6g')
>>> myhf = scf.UHF(mol).run()
>>> myci = ci.UCISD(myhf).run()
>>> wf_cisd = qml.qchem.import_state(myci, tol=1e-1)
>>> print(wf_cisd)
[ 0. +0.j 0. +0.j 0. +0.j 0.1066467 +0.j
1. +0.j 0. +0.j 0. +0.j 0. +0.j
2. +0.j 0. +0.j 0. +0.j 0. +0.j
-0.99429698+0.j 0. +0.j 0. +0.j 0. +0.j]
The state vector can be implemented in a circuit using qml.StatePrep
.
>>> dev = qml.device('default.qubit', wires=4)
>>> @qml.qnode(dev)
... def circuit():
... qml.StatePrep(wf_cisd, wires=range(4))
... return qml.state()
>>> print(circuit())
[ 0. +0.j 0. +0.j 0. +0.j 0.1066467 +0.j
1. +0.j 0. +0.j 0. +0.j 0. +0.j
2. +0.j 0. +0.j 0. +0.j 0. +0.j
-0.99429698+0.j 0. +0.j 0. +0.j 0. +0.j]
The currently supported post-Hartree-Fock methods are RCISD, UCISD, RCCSD, and UCCSD which denote restricted (R) and unrestricted (U) configuration interaction (CI) and coupled cluster (CC) calculations with single and double (SD) excitations.
PennyLane now allows you to define circuits that reuse a qubit after a mid-circuit measurement has taken place. Optionally, the wire can also be reset to the $|0\rangle$ state. (#4402) (#4432)
Post-measurement reset can be activated by setting reset=True
when calling qml.measure. In this version of PennyLane, executing circuits with qubit reuse will result in the defer_measurements transform being applied. This transform replaces each reused wire with an additional qubit. However, future releases of PennyLane will explore device-level support for qubit reuse without consuming additional qubits.
Qubit reuse and reset is also fully differentiable:
dev = qml.device("default.qubit", wires=4)
@qml.qnode(dev)
def circuit(p):
qml.RX(p, wires=0)
m = qml.measure(0, reset=True)
qml.cond(m, qml.Hadamard)(1)
qml.RX(p, wires=0)
m = qml.measure(0)
qml.cond(m, qml.Hadamard)(1)
return qml.expval(qml.PauliZ(1))
>>> jax.grad(circuit)(0.4)
Array(-0.35867804, dtype=float32, weak_type=True)
You can read more about mid-circuit measurements in the documentation, and stay tuned for more mid-circuit measurement features in the next few releases!
Circuit drawings and plots can now be created following a PennyLane style. (#3950)
The qml.draw_mpl
function accepts a style='pennylane'
argument to create PennyLane themed circuit diagrams:
def circuit(x, z):
qml.QFT(wires=(0,1,2,3))
qml.Toffoli(wires=(0,1,2))
qml.CSWAP(wires=(0,2,3))
qml.RX(x, wires=0)
qml.CRZ(z, wires=(3,0))
return qml.expval(qml.PauliZ(0))
qml.draw_mpl(circuit, style="pennylane")(1, 1)
PennyLane-styled plots can also be drawn by passing "pennylane.drawer.plot"
to Matplotlib's plt.style.use
function:
import matplotlib.pyplot as plt
plt.style.use("pennylane.drawer.plot")
for i in range(3):
plt.plot(np.random.rand(10))
If the font Quicksand Bold isn't available, an available default font is used instead.
Any class inheriting from Operator
is now automatically registered as a pytree with JAX. This unlocks the ability to jit functions of Operator
. (#4458)
>>> op = qml.adjoint(qml.RX(1.0, wires=0))
>>> jax.jit(qml.matrix)(op)
Array([[0.87758255-0.j , 0. +0.47942555j],
[0. +0.47942555j, 0.87758255-0.j ]], dtype=complex64, weak_type=True)
>>> jax.tree_util.tree_map(lambda x: x+1, op)
Adjoint(RX(2.0, wires=[0]))
All Operator
objects now define Operator._flatten
and Operator._unflatten
methods that separate trainable from untrainable components. These methods will be used in serialization and pytree registration. Custom operations may need an update to ensure compatibility with new PennyLane features. (#4483) (#4314)
The QuantumScript
class now has a bind_new_parameters
method that allows creation of new QuantumScript
objects with the provided parameters. (#4345)
The qml.gradients
module no longer mutates operators in-place for any gradient transforms. Instead, operators that need to be mutated are copied with new parameters. (#4220)
PennyLane no longer directly relies on Operator.__eq__
. (#4398)
qml.equal
no longer raises errors when operators or measurements of different types are compared. Instead, it returns False
. (#4315)
Transform programs are now integrated with the QNode. (#4404)
def null_postprocessing(results: qml.typing.ResultBatch) -> qml.typing.Result:
return results[0]
@qml.transforms.core.transform
def scale_shots(tape: qml.tape.QuantumTape, shot_scaling) -> (Tuple[qml.tape.QuantumTape], Callable):
new_shots = tape.shots.total_shots * shot_scaling
new_tape = qml.tape.QuantumScript(tape.operations, tape.measurements, shots=new_shots)
return (new_tape, ), null_postprocessing
dev = qml.devices.experimental.DefaultQubit2()
@partial(scale_shots, shot_scaling=2)
@qml.qnode(dev, interface=None)
def circuit():
return qml.sample(wires=0)
>>> circuit(shots=1)
array([False, False])
Transform Programs, qml.transforms.core.TransformProgram
, can now be called on a batch of circuits and return a new batch of circuits and a single post processing function. (#4364)
TransformDispatcher
now allows registration of custom QNode transforms. (#4466)
QNode transforms in qml.qinfo
now support custom wire labels. #4331
qml.transforms.adjoint_metric_tensor
now uses the simulation tools in qml.devices.qubit
instead of private methods of qml.devices.DefaultQubit
. (#4456)
Auxiliary wires and device wires are now treated the same way in qml.transforms.metric_tensor
as in qml.gradients.hadamard_grad
. All valid wire input formats for aux_wire
are supported. (#4328)
The experimental device interface has been integrated with the QNode for JAX, JAX-JIT, TensorFlow and PyTorch. (#4323) (#4352) (#4392) (#4393)
The experimental DefaultQubit2
device now supports computing VJPs and JVPs using the adjoint method. (#4374)
New functions called adjoint_jvp
and adjoint_vjp
that compute the JVP and VJP of a tape using the adjoint method have been added to qml.devices.qubit.adjoint_jacobian
(#4358)
DefaultQubit2
now accepts a max_workers
argument which controls multiprocessing. A ProcessPoolExecutor
executes tapes asynchronously using a pool of at most max_workers
processes. If max_workers
is None
or not given, only the current process executes tapes. If you experience any issue, say using JAX, TensorFlow, Torch, try setting max_workers
to None
. (#4319) (#4425)
qml.devices.experimental.Device
now accepts a shots keyword argument and has a shots
property. This property is only used to set defaults for a workflow, and does not directly influence the number of shots used in executions or derivatives. (#4388)
expand_fn()
for DefaultQubit2
has been updated to decompose StatePrep
operations present in the middle of a circuit. (#4444)
If no seed is specified on initialization with DefaultQubit2
, the local random number generator will be seeded from NumPy's global random number generator. (#4394)
pennylane/interfaces
has been refactored. The execute_fn
passed to the machine learning framework boundaries is now responsible for converting parameters to NumPy. The gradients module can now handle TensorFlow parameters, but gradient tapes now retain the original dtype
instead of converting to float64
. This may cause instability with finite-difference differentiation and float32
parameters. The machine learning boundary functions are now uncoupled from their legacy counterparts. (#4415)
qml.interfaces.set_shots
now accepts a Shots
object as well as int
's and tuples of int
's. (#4388)
Readability improvements and stylistic changes have been made to pennylane/interfaces/jax_jit_tuple.py
(#4379)
A HardwareHamiltonian
can now be summed with int
or float
objects. A sequence of HardwareHamiltonian
s can now be summed via the builtin sum
. (#4343)
qml.pulse.transmon_drive
has been updated in accordance with 1904.06560. In particular, the functional form has been changed from $\Omega(t)(\cos(\omega_d t + \phi) X - \sin(\omega_d t + \phi) Y)$ to $\Omega(t) \sin(\omega_d t + \phi) Y$. (#4418) (#4465) (#4478) (#4418)
The qchem
module has been upgraded to use the fermionic operators of the fermi
module. #4336 #4521
The calculation of Sum
, Prod
, SProd
, PauliWord
, and PauliSentence
sparse matrices are orders of magnitude faster. (#4475) (#4272) (#4411)
A function called qml.math.fidelity_statevector
that computes the fidelity between two state vectors has been added. (#4322)
qml.ctrl(qml.PauliX)
returns a CNOT
, Toffoli
, or MultiControlledX
operation instead of Controlled(PauliX)
. (#4339)
When given a callable, qml.ctrl
now does its custom pre-processing on all queued operators from the callable. (#4370)
The qchem
functions primitive_norm
and contracted_norm
have been modified to be compatible with higher versions of SciPy. The private function _fac2
for computing double factorials has also been added. #4321
tape_expand
now uses Operator.decomposition
instead of Operator.expand
in order to make more performant choices. (#4355)
CI now runs tests with TensorFlow 2.13.0 (#4472)
All tests in CI and pre-commit hooks now enable linting. (#4335)
The default label for a StatePrepBase
operator is now |ฮจโฉ
. (#4340)
Device.default_expand_fn()
has been updated to decompose qml.StatePrep
operations present in the middle of a provided circuit. (#4437)
QNode.construct
has been updated to only apply the qml.defer_measurements
transform if the device does not natively support mid-circuit measurements. (#4516)
The application of the qml.defer_measurements
transform has been moved from QNode.construct
to qml.Device.batch_transform
to allow more fine-grain control over when defer_measurements
should be used. (#4432)
The label for ParametrizedEvolution
can display parameters with the requested format as set by the kwarg decimals
. Array-like parameters are displayed in the same format as matrices and stored in the cache. (#4151)
Applying gradient transforms to broadcasted/batched tapes has been deactivated until it is consistently supported for QNodes as well. (#4480)
Gradient transforms no longer implicitly cast float32
parameters to float64
. Finite difference differentiation with float32
parameters may no longer give accurate results. (#4415)
The do_queue
keyword argument in qml.operation.Operator
has been removed. Instead of setting do_queue=False
, use the qml.QueuingManager.stop_recording()
context. (#4317)
Operator.expand
now uses the output of Operator.decomposition
instead of what it queues. (#4355)
The gradients module no longer needs shot information passed to it explicitly, as the shots are on the tapes. (#4448)
qml.StatePrep
has been renamed to qml.StatePrepBase
and qml.QubitStateVector
has been renamed to qml.StatePrep
. qml.operation.StatePrep
and qml.QubitStateVector
are still accessible. (#4450)
Support for Python 3.8 has been dropped. (#4453)
MeasurementValue
's signature has been updated to accept a list of MidMeasureMP
's rather than a list of their IDs. (#4446)
The grouping_type
and grouping_method
keyword arguments have been removed from qchem.molecular_hamiltonian
. (#4301)
zyz_decomposition
and xyx_decomposition
have been removed. Use one_qubit_decomposition
instead. (#4301)
LieAlgebraOptimizer
has been removed. Use RiemannianGradientOptimizer
instead. (#4301)
Operation.base_name
has been removed. (#4301)
QuantumScript.name
has been removed. (#4301)
qml.math.reduced_dm
has been removed. Use qml.math.reduce_dm
or qml.math.reduce_statevector
instead. (#4301)
The qml.specs
dictionary no longer supports direct key access to certain keys. (#4301)
Instead, these quantities can be accessed as fields of the new Resources
object saved under specs_dict["resources"]
:
num_operations
is no longer supported, use specs_dict["resources"].num_gates
num_used_wires
is no longer supported, use specs_dict["resources"].num_wires
gate_types
is no longer supported, use specs_dict["resources"].gate_types
gate_sizes
is no longer supported, use specs_dict["resources"].gate_sizes
depth
is no longer supported, use specs_dict["resources"].depth
qml.math.purity
, qml.math.vn_entropy
, qml.math.mutual_info
, qml.math.fidelity
, qml.math.relative_entropy
, and qml.math.max_entropy
no longer support state vectors as input. (#4322)
The private QuantumScript._prep
list has been removed, and prep operations now go into the _ops
list. (#4485)
qml.enable_return
and qml.disable_return
have been deprecated. Please avoid calling disable_return
, as the old return system has been deprecated along with these switch functions. (#4316)
qml.qchem.jordan_wigner
has been deprecated. Use qml.jordan_wigner
instead. List input to define the fermionic operator has also been deprecated; the fermionic operators in the qml.fermi
module should be used instead. (#4332)
The qml.RandomLayers.compute_decomposition
keyword argument ratio_imprimitive
will be changed to ratio_imprim
to match the call signature of the operation. (#4314)
The CV observables qml.X
and qml.P
have been deprecated. Use qml.QuadX
and qml.QuadP
instead. (#4330)
The method tape.unwrap()
and corresponding UnwrapTape
and Unwrap
classes have been deprecated. Use convert_to_numpy_parameters
instead. (#4344)
The mode
keyword argument in QNode has been deprecated, as it was only used in the old return system (which has also been deprecated). Please use grad_on_execution
instead. (#4316)
The QuantumScript.set_parameters
method and the QuantumScript.data
setter have been deprecated. Please use QuantumScript.bind_new_parameters
instead. (#4346)
The __eq__
and __hash__
dunder methods of Operator
and MeasurementProcess
will now raise warnings to reflect upcoming changes to operator and measurement process equality and hashing. (#4144) (#4454) (#4489) (#4498)
The sampler_seed
argument of qml.gradients.spsa_grad
has been deprecated, along with a bug fix of the seed-setting behaviour. Instead, the sampler_rng
argument should be set, either to an integer value, which will be used to create a PRNG internally or to a NumPy pseudo-random number generator created via np.random.default_rng(seed)
. (#4165)
The qml.pulse.transmon_interaction
and qml.pulse.transmon_drive
documentation has been updated. (#4327)
qml.ApproxTimeEvolution.compute_decomposition()
now has a code example. (#4354)
The documentation for qml.devices.experimental.Device
has been improved to clarify some aspects of its use. (#4391)
Input types and sources for operators in qml.import_operator
are specified. (#4476)
qml.Projector
is pickle-able again. (#4452)
_copy_and_shift_params
does not cast or convert integral types, just relying on +
and *
's casting rules in this case. (#4477)
Sparse matrix calculations of SProd
s containing a Tensor
are now allowed. When using Tensor.sparse_matrix()
, it is recommended to use the wire_order
keyword argument over wires
. (#4424)
op.adjoint
has been replaced with qml.adjoint
in QNSPSAOptimizer
. (#4421)
jax.ad
(deprecated) has been replaced by jax.interpreters.ad
. (#4403)
metric_tensor
stops accidentally catching errors that stem from flawed wires assignments in the original circuit, leading to recursion errors. (#4328)
A warning is now raised if control indicators are hidden when calling qml.draw_mpl
(#4295)
qml.qinfo.purity
now produces correct results with custom wire labels. (#4331)
default.qutrit
now supports all qutrit operations used with qml.adjoint
. (#4348)
The observable data of qml.GellMann
now includes its index, allowing correct comparison between instances of qml.GellMann
, as well as Hamiltonians and Tensors containing qml.GellMann
. (#4366)
qml.transforms.merge_amplitude_embedding
now works correctly when the AmplitudeEmbedding
s have a batch dimension. (#4353)
The jordan_wigner
function has been modified to work with Hamiltonians built with an active space. (#4372)
When a style
option is not provided, qml.draw_mpl
uses the current style set from qml.drawer.use_style
instead of black_white
. (#4357)
qml.devices.qubit.preprocess.validate_and_expand_adjoint
no longer sets the trainable parameters of the expanded tape. (#4365)
qml.default_expand_fn
now selectively expands operations or measurements allowing more operations to be executed in circuits when measuring non-qwc Hamiltonians. (#4401)
qml.ControlledQubitUnitary
no longer reports has_decomposition
as True
when it does not really have a decomposition. (#4407)
qml.transforms.split_non_commuting
now correctly works on tapes containing both expval
and var
measurements. (#4426)
Subtracting a Prod
from another operator now works as expected. (#4441)
The sampler_seed
argument of qml.gradients.spsa_grad
has been changed to sampler_rng
. One can either provide an integer, which will be used to create a PRNG internally. Previously, this lead to the same direction being sampled, when num_directions
is greater than 1. Alternatively, one can provide a NumPy PRNG, which allows reproducibly calling spsa_grad
without getting the same results every time. (#4165) (#4482)
qml.math.get_dtype_name
now works with autograd array boxes. (#4494)
The backprop gradient of qml.math.fidelity
is now correct. (#4380)
This release contains contributions from (in alphabetical order):
Utkarsh Azad, Thomas Bromley, Isaac De Vlugt, Amintor Dusko, Stepan Fomichev, Lillian M. A. Frederiksen, Soran Jahangiri, Edward Jiang, Korbinian Kottmann, Ivana Kureฤiฤ, Christina Lee, Vincent Michaud-Rioux, Romain Moyard, Lee James O'Riordan, Mudit Pandey, Borja Requena, Matthew Silverman, Jay Soni, David Wierichs, Frederik Wilde.
data.Dataset
now uses HDF5 instead of dill for serialization. (#4097)
The qchem
functions primitive_norm
and contracted_norm
are modified to be compatible with higher versions of scipy. (#4321)
This release contains contributions from (in alphabetical order):
Utkarsh Azad, Jack Brown, Diego Guala, Soran Jahangiri, Matthew Silverman