-
Notifications
You must be signed in to change notification settings - Fork 657
Add ROWCOL CNOT routing algorithm #7394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
dwierichs
wants to merge
36
commits into
phase-polynomials
Choose a base branch
from
rowcol
base: phase-polynomials
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
**Context:** As part of the sphinx upgrade, sphinx-action has been upgrade from python 3.9 to 3.10 and sphinx 2.2 to min 7.0. The upgrade has caused issues with the docs workflow as it has introduced new warnings that cause the workflow to fail. **Description of the Change:** This change will change sphinx-action to use a version fixed to python 3.9 until the [permanent sphinx](#7212) changes are merged. **Benefits:** Sphinx build will not give any more unnecessary warnings. **Possible Drawbacks:** None **Related GitHub Issues:**
**Context:** The repr for empty `Sum` and `Prod` was just an empty string. This did not communicate the existance of an operator instance, leading to confusion. **Description of the Change:** Adds a special repr for empty sums and prods. **Benefits:** Tracebacks and logging now communicate what actually exists. **Possible Drawbacks:** **Related GitHub Issues:** --------- Co-authored-by: Mudit Pandey <[email protected]>
**Context:** **Description of the Change:** ``` dev = qml.device("default.qubit", wires=2) @qml.qnode(dev, interface=None) def circuit(): qml.Snapshot("sample", measurement=qml.sample(), shots=5) return qml.expval(qml.X(0)) qml.snapshots(circuit)() ``` ``` {'sample': array([[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]), 'execution_results': 0.0} ``` **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** [sc-89771] --------- Co-authored-by: Isaac De Vlugt <[email protected]>
**Context:** `Snapshot` wasn't working with defer measurements handling of mcms. ``` dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def func(): qml.Hadamard(wires=0) m_0 = qml.measure(0) # without this mcm everything's fine qml.Snapshot("label") return qml.probs() print(qml.snapshots(func)()) ``` **Description of the Change:** Switch the order of applying defer measurements and adding the device wires. **Benefits:** Now it works. **Possible Drawbacks:** We still need to figure out dynamic one shot and tree-traversal. But those are harder problems. **Related GitHub Issues:** Partially solves #7334 . Only for defer measurements though. [sc-89863] --------- Co-authored-by: Isaac De Vlugt <[email protected]>
) Follow up to #5448, applying the same solution to the 3-cnot case. Fixes: #7339 [sc-90052] --------- Co-authored-by: Yushao Chen (Jerry) <[email protected]>
**Context:** The `process_counts` functions were added to `CountsMP` and `SamplesMP` a while back, to expand support in cases with `shots` where the initial device results are `counts` instead of `samples`. Some cases where these measurement processes included observables (i.e. `qml.counts(qml.Z(0))`, `qml.sample(qml.X("a")`) returned incorrect results, specifically: ``` >>> counts_mp = qml.counts(qml.Z(0)) >>> counts_mp.process_counts({"00": 2, "10": 3}, wire_order=[0, 1]) {"0": 2, "1": 3} ``` instead of `{-1.0: 3, 1.0: 2}`, and ``` >>> sample_mp = qml.sample(qml.Z(0) @ qml.Z(1)) >>> sample_mp.process_counts({"00": 2, "10": 3}, wire_order=[0, 1]) [[ 1. 1.] [ 1. 1.] [-1. 1.] [-1. 1.] [-1. 1.]] ``` instead of `[1., 1., -1., -1., -1.]`. **Description of the Change:** Added/modified handling of samples/counts when `CountsMP` or `SamplesMP` have eigenvalues. **Related GitHub Issues:** #7344 #7343 [sc-90071]
… be on any number of wires (#7312) **Context:** This PR is separated from #7311 to allow us to update catalyst easily. `AllWires` and `AnyWires` are overkill that don't really need to exist. Setting `Operator.num_wires = None` can sufficiently communicate "I can accept any number of wires". We haven't validated `AllWires` or done anything special about it for many releases. All these serve to do it tell the default `Operator.__init__` to not validate how many wires the user passes in. We can do that with `None`. All they really do is make the interface more complicated and cluttered. **Description of the Change:** Default to using `Operator.num_wires = None` to indicate that the operator can be on any number of wires. **Benefits:** Cleaner Operator interface. Sets us up for deprecating `WiresEnum`. **Possible Drawbacks:** Deprecations and removals always run the risk of breaking something for someone, but I think this a harmless enough change. [sc-89159]
PR #7312 just does the num_wires = None part of this change and will allow us to update catalyst before merging this PR. Context: AllWires and AnyWires are overkill that don't really need to exist. Setting Operator.num_wires = None can sufficiently communicate "I can accept any number of wires". We haven't validated AllWires or done anything special about it for many releases. All these serve to do it tell the default Operator.__init__ to not validate how many wires the user passes in. We can do that with None. All they really do is make the interface more complicated and cluttered. Description of the Change: Deprecate AllWires, AnyWires, and WiresEnum. Benefits: Cleaner Operator interface. Possible Drawbacks: Deprecations and removals always run the risk of breaking something for someone, but I think this a harmless enough change. Related GitHub Issues: [[sc-89159](https://app.shortcut.com/xanaduai/story/89159)] --------- Co-authored-by: Yushao Chen (Jerry) <[email protected]>
**Context:** **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** Fixes #7359 [sc-90233] --------- Co-authored-by: Yushao Chen (Jerry) <[email protected]>
**Context:** The `operation.py` module currently serves to provide both the core interface for the operator abstraction, and a bunch of minor utility functions. Some of those minor utility functions have dependencies on downstream components, like `MeasurementProcess` and `QuantumScript`. Others are examples where the interface is more complicated than the code they hide. They just end up complicating the interface and don't really even simplify anything. **Description of the Change:** Deprecate all the `BooleanFn`'s present in `operation.py` in favour of people just using the relevant code. **Benefits:** `operation.py` can be more focused on setting up the abstractions and interfaces for operators. `operation.py` looses dependencies on downstream structures. The interface is less cluttered with unnecessary helpers. **Possible Drawbacks:** As it is still a deprecation, someone out there might still be depending on these things. **Related GitHub Issues:** [sc-89158] --------- Co-authored-by: Yushao Chen (Jerry) <[email protected]> Co-authored-by: Isaac De Vlugt <[email protected]> Co-authored-by: Pietropaolo Frisoni <[email protected]>
Current implementation summary: - We leave `ftqc.measure_z` completely equivalent to `qml.measure`, so it captures the standard `measure` primitive for PennyLane ``` qml.capture.enable() @qml.qnode(dev) def circ1(): qml.measure(0) return qml.expval(Z(0)) @qml.qnode(dev) def circ2(): measure_z(0) return qml.expval(Z(0)) ``` ``` >>> jaxpr = jax.make_jaxpr(circ1)() >>> jaxpr.eqns[0].params["qfunc_jaxpr"].eqns [_:i64[] = measure[postselect=None reset=False] 0, a:AbstractOperator() = PauliZ[n_wires=1] 0, a:AbstractMeasurement(n_wires=None) = expval_obs b] ``` ``` >>> jaxpr = jax.make_jaxpr(circ2)() >>> jaxpr.eqns[0].params["qfunc_jaxpr"].eqns [_:i64[] = measure[postselect=None reset=False] 0, a:AbstractOperator() = PauliZ[n_wires=1] 0, a:AbstractMeasurement(n_wires=None) = expval_obs b] ``` - We capture `measure_x`, `measure_y` and `measure_arbitrary_basis` all with the same parametrized measurement primitive, `p_measure`. This is under the assumption that the distinction is primarily a user-facing one for reading code and drawing circuits, as well as more specific diagonalization, and none of those things are relevant at the point where we are capturing for catalyst, so they may as well all just be one thing that is handled in a uniform manner. So when captured, these circuits are identical: ``` @qml.qnode(dev) def circ1(): measure_x(0) return qml.expval(Z(0)) @qml.qnode(dev) def circ2(): measure_arbitrary_basis(0, angle=0, plane="XY") return qml.expval(Z(0)) ``` ``` >>> jaxpr = jax.make_jaxpr(circ1)() >>> jaxpr.eqns[0].params["qfunc_jaxpr"].eqns [_:i64[] = p_measure[angle=0 plane=XY postselect=None reset=False] 0, a:AbstractOperator() = PauliZ[n_wires=1] 0, a:AbstractMeasurement(n_wires=None) = expval_obs b] ``` ``` >>> jaxpr = jax.make_jaxpr(circ2)() >>> jaxpr.eqns[0].params["qfunc_jaxpr"].eqns [_:i64[] = p_measure[angle=0 plane=XY postselect=None reset=False] 0, a:AbstractOperator() = PauliZ[n_wires=1] 0, a:AbstractMeasurement(n_wires=None) = expval_obs b] ``` - We capture `cond_measure` by putting it inside a `cond` primitive using `qml.cond`. This wouldn't work without program capture on, but my understanding is that this will be more flexible with plxpr. We can make a new primitive if that's not the case. The essential thing here is that we need to be able to use the value returned by whichever function is called going forward, so `cond` needs to have the option to return something, i.e. in the following we need `a:i64[]` to be the value returned by whichever of the measurement functions we end up executing inside cond: ``` @qml.qnode(dev) def circ(): m1 = measure_x(0) m2 = cond_measure(m1, measure_x, measure_y)(2) cond_measure(m2, measure_x, measure_y)(3) return qml.expval(Z(0)) ``` ``` >>> jaxpr = jax.make_jaxpr(circ)() >>> jaxpr.eqns[0].params["qfunc_jaxpr"].eqns[1] a:i64[] = cond[ args_slice=slice(2, None, None) consts_slices=[slice(2, 2, None), slice(2, 2, None)] jaxpr_branches=[{ lambda ; a:i64[]. let b:i64[] = p_measure[angle=0 plane=XY postselect=None reset=False] a in (b,) }, { lambda ; a:i64[]. let b:i64[] = p_measure[ angle=1.5707963267948966 plane=XY postselect=None reset=False ] a in (b,) }] ] b True 2 ``` - We make a new function, `make_graph_state`, that either creates a GraphStatePrep operation (if `capture` is off), or decomposes it and captures the operations generated in the decomposition (if `capture` is on). This circumvents JAX's inability to handle a networkx graph. Maybe we want to maintain some more efficient way of storing this in a single primitive in the future, but for now this seems simple and acceptable. ``` import networkx as nx from pennylane.ftqc import make_graph_state @qml.qnode(dev) def circ(): make_graph_state(nx.grid_graph((4,)), wires=[1, 2, 3, 4]) return qml.expval(X(4)) ``` ``` >>> jax.make_jaxpr(circ.func)() { lambda ; . let _:AbstractOperator() = Hadamard[n_wires=1] 1 _:AbstractOperator() = Hadamard[n_wires=1] 2 _:AbstractOperator() = Hadamard[n_wires=1] 3 _:AbstractOperator() = Hadamard[n_wires=1] 4 _:AbstractOperator() = CZ[n_wires=2] 1 2 _:AbstractOperator() = CZ[n_wires=2] 2 3 _:AbstractOperator() = CZ[n_wires=2] 3 4 a:AbstractOperator() = PauliX[n_wires=1] 4 b:AbstractMeasurement(n_wires=None) = expval_obs a in (b,) } ``` ``` >>> qml.capture.disable() >>> qml.workflow.construct_tape(circ)().operations [GraphStatePrep(Hadamard, CZ)] ``` --------------------------------- **Context:** **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** [sc-88520] --------- Co-authored-by: Joey Carter <[email protected]>
**Context:** For benchmarking, we need a method of precise resource tracking. This method has to be compatible with code `@qjit` compiled using Catalyst and should be able to track precise resources after various transformation passes have already occurred. See also: PennyLaneAI/pennylane-benchmarks#76 **Description of the Change:** Adds a new attribute to `null.qubit`, as well as an optional argument to its `__init__` function. When this option is toggled on, `null.qubit` will track precise gate counts when "executing" a circuit. **Benefits:** It is now possible to track precise gate counts (with or without gate decomposition or other transformation passes) for measuring circuit complexity. **Possible Drawbacks:** To be mutually compatible with the catalyst implementation, this currently just saves the resources JSON to a file as opposed to neatly returning an object. In the future, a better solution may be desired. This implementation causes `null.qubit` to have a different capability that other devices do not have. If this feature is useful elsewhere, it might be wise to move resource tracking to the base Device class. **Related GitHub Issues:** [sc-88213] --------- Co-authored-by: Christina Lee <[email protected]>
**Context:** When fixing up the tests for jax v0.5.3, I realized we were capturing QSVT wrong. **Description of the Change:** Treat the projectors as traceable objects, rather than metadata. **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:**
**Context:** Docs.yml uses python3.9 and sphinx version 3.5. It is currently a blocker to dropping python 3.10 in pennylane. Upgrading sphinx to version 8.1 allows for higher versions that are compatible with >Python 3.11 to be used later. **Description of the Change:** Sphinx is upgraded to version 8.1 and uses Python 3.10. References to `<demos>` are updated to remove the `:doc:` prefix which is incompatible with sphinx 8.1 and was broken previously. **Benefits:** Python 3.9 will be dropped. **Possible Drawbacks:** **Related GitHub Issues:** --------- Co-authored-by: Rashid N H M <[email protected]>
…om int to bool (#7368) **Context:** The JAX primitive representing arbitrary-basis measurements currently returns a `jax.core.ShapedArray` of type `int64` (or `int32` if `jax.config.jax_enable_x64` is False). However, the corresponding operation in Catalyst returns a `bool`. Having different return types is generally fine, except in certain circumstances when qjit compiling a circuit containing bitwise operations on a measurement result and boolean literals. For instance, the following workload would generate the following Catalyst jaxpr: ```python dev = qml.device("null.qubit", wires=1) qml.capture.enable() @qjit(target="jaxpr") @qml.qnode(dev) def foo(): m0 = qml.ftqc.measure_x(0) x = m0 ^ True return qml.expval(qml.Z(0)) qml.capture.disable() ``` ```pycon >>> print(foo.jaxpr) # Output abbreviated for clarity ... d:bool[] e:AbstractQbit() = measure_in_basis[ plane=MeasurementPlane.XY postselect=None ] 0.0 c _:bool[] = xor d 1 # <-- Literal `True` implicitly converted to `1` ... ``` Lowering this jaxpr to MLIR results in a compiler error because the `xor` operation applied to variable `d` (a `bool`) and a literal `1` (interpreted as an `int64`) is not permitted. **Description of the Change:** Changes the return type of `measure_in_basis_p` abstract eval from `int` to `bool` and updates the affected tests appropriately. Running the same example above generates the following Catalyst jaxpr, as expected: ``` d:bool[] e:AbstractQbit() = measure_in_basis[ plane=MeasurementPlane.XY postselect=None ] 0.0 c _:bool[] = xor d True # <-- Literal `True` unchanged ``` **Benefits:** This change will enable us to qjit compile a CNOT gate expressed in the MBQC formalism, which contains a by-product correction of the form `m0 ^ m1 ^ ... ^ 1`, where `m*` are measurement outcomes. **Possible Drawbacks:** The computation-basis mid-circuit measurement operation `qml.measure()` continues to return 32-/64-bit `ints`, putting the MCMs in the `ftqc` module out of sync with their core PennyLane counterpart. We may be able to change the abstract eval return type of the computation-basis measurement primitives as well, although such a change could have broader implications elsewhere.
**Context:** `Observable` no longer really serves a purpose, as operator arithmetic is now generic for all operators. It only really serves to communicate an implicit `is_hermitian`, and stops the object from getting processed into the tape via `_queue_category=None`. So we can now get rid of it in favor of using the simpler `Operator` abstraction. **Description of the Change:** Deprecates `Observable`. **Benefits:** Simpler abstractions. **Possible Drawbacks:** **Related GitHub Issues:** [sc-89295] --------- Co-authored-by: Yushao Chen (Jerry) <[email protected]> Co-authored-by: Isaac De Vlugt <[email protected]> Co-authored-by: Pietropaolo Frisoni <[email protected]>
This PR applies some changes to master coming from [this PR](#7369), which has been merged in the release candidate branch for the bugfix release of PennyLane 0.41.1. More details can be found in the original PR description. This PR aims to update the master branch with the changes currently present in the stable version of PennyLane (0.41.1, no longer 0.41.0)
Automatic update of stable requirement files to snapshot valid python environments. Because bots are not able to trigger CI on their own, please do so by pushing an empty commit to this branch using the following command: ``` git commit --allow-empty -m 'trigger ci' ``` --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Pietropaolo Frisoni <[email protected]>
**Context:** PR #7226 added a resource-tracking feature to PennyLane. The same feature is desired in Catalyst: PennyLaneAI/catalyst#1619 **Description of the Change:** Adds a class attribute to `null.qubit` which allows the `resource_tracking` argument to get passed through to Catalyst. **Benefits:** `track_resources` can more cleanly be passed through to Catalyst. **Possible Drawbacks:** **Related GitHub Issues:** #7226 PennyLaneAI/catalyst#1619 PennyLaneAI/pennylane-benchmarks#76 [sc-88213](https://app.shortcut.com/xanaduai/story/88213) --------- Co-authored-by: Ali Asadi <[email protected]>
**Context:** This is one of the first PRs implementing the new quantum compiler in PennyLane. This PR was originally opened in Catalyst: PennyLaneAI/catalyst#1691 **Description of the Change:** The changes introduced in this PR can be divided at a high level into 3 different steps: *Step 1* Generated the Python dialect using `xdsl_tblgen.py`, using the JSON file generated by `llvm-tblgen` starting from [this Catalyst file](https://github.com/PennyLaneAI/catalyst/blob/main/mlir/include/Quantum/IR/QuantumOps.td). *Step 2* At the current stage, I commented the `assembly_format` attributes, which generated an error, and added the `AttrSizedOperandSegments` and `AttrSizedResultSegments` metadata In fact, in the generated dialect, there is a class of errors that would be good to fix in Catalyst. Erick is writing them as issues in Catalyst. [Here](PennyLaneAI/catalyst#1692) and [here](PennyLaneAI/catalyst#1690). Once these issues are solved, we believe the whole quantum dialect would be easy to generate and work relatively well to proceed to either other dialects or the cancel-inverses transform. *Step 3* Writing some minimal tests for the dialect. These will be improved and expanded (we will have multiple chats about this point). **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** None. **Related Shortcut Story:** [sc-89798] --------- Co-authored-by: Isaac De Vlugt <[email protected]>
**Context:** The PyPI release process requires more strict adherence to the https://packaging.python.org/en/latest/specifications/binary-distribution-format/ guidelines. `PennyLane` the package name is not compliant with this naming, and requires the released wheels to be named `pennylane`. Older versions of setuptools were less strict about this naming, and forwarded what was requested in the package metadata. Upgrade setuptools and wheel build infrastructure ensures that we adhere with the wheel naming expectations of PyPI. Additionally, this PR also removes the deprecated license classifier, removing a warning of `SetuptoolsDeprecationWarning: License classifiers are deprecated`. **Description of the Change:** Replaces the direct call to setup.py for wheel building with a call to https://github.com/pypa/build **Benefits:** Ensures adherence to Python packaging expectations coming into enforcement from PyPI. Fixes future failure expected during release. **Possible Drawbacks:** **Related GitHub Issues:**
**Context:** When we diagonalize the parametric mid-circuit measurements to get analytic results to validate our protocols, we swap them out with new, updated measurements. This is currently updated in the conditionals, but not on the measurements. That means that returning parametric mcms when using the `diagonalize_mcms` transform currently fails. **Description of the Change:** We also update the measurements in the transform - if any MeasurementProcess contains MCMs, we update them based on the existing dictionary that's tracking updated measurement values, and create a copy of the MP with the new MeasurementValue(s) instead of the original ones. **Benefits:** We can do this: ``` @diagonalize_mcms @qml.qnode(dev) def circ(): qml.H(0) m = measure_x(0, reset=True) return qml.expval(m) ``` [sc-90609]
**Context:** See #7215 for the bug here. Separately recorded as a standalone bug: #7395 **Description of the Change:** change the `np.` in executable source code to `qml.math.` for agnosticity. added several tests for `default.mixed` to make sure the trainable parameters are treated well **Benefits:** Users would be able to use `default.mixed` on other interfaces e.g. torch cuda **Possible Drawbacks:** We don't have the GPU test suites for `default.mixed`! **Related GitHub Issues:** [sc-90500] --------- Co-authored-by: Isaac De Vlugt <[email protected]>
…ottonenStatePreparation`) (#7377) **Context:** In `MottonenStatePreparation`, there is a step where the desired phases need to be transformed into rotation angles for the Gray code-based rotation sequence that implements the phases. The sequence is shown in Fig. 2, the angle transformation in Eq. (3) of [Möttönen et al.](https://arxiv.org/pdf/quant-ph/0407010). Currently, the transformation is done by explicitly constructing the matrix $M$ from Eq.(3) and multiplying it to the angles. This is not only painstakingly slow but also requires quadratic memory size compared to the input and output. If we care about preparing larger state vectors, this limits the computation to about 15 qubits, which it does not have to do. Instead, one can compute the angles with a fast Walsh-Hadamard transform (FWHT) and subsequent permutations. These permutations act as if we would apply a ladder of CNOT gates to the angles, when interpreting them as a quantum state. **Description of the Change:** Change `compute_theta`, which implements the transform, from the matrix-based method to the FWHT-based one. **Benefits:** Roughly reduces memory requirement from `O(d^2)` to `O(d)`, where `d` is the input (and output) dimension. Reduces compute time _heavily_:  **Possible Drawbacks:** N/A **Related GitHub Issues:** #7378 [sc-90387] --------- Co-authored-by: Korbinian Kottmann <[email protected]> Co-authored-by: Isaac De Vlugt <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Context:
A pure CNOT circuit can be described entirely via its parity matrix. In #7229, a function is added to
qml.labs
to compute the parity matrix for a CNOT circuit. The answer there is unique: A CNOT circuit maps to one unique parity matrix.The reverse task of extracting, or synthesizing, a CNOT circuit that implements a given parity matrix is accomplished by a series of algorithms in the literature, a better-known one being ROWCOL introduced here and described in the compilation hub here.
Note that there are infinitely many CNOT circuits that produce the same parity matrix, so that the answer to this task is never unique, and different algorithms will return CNOT circuits with different gate counts and depths.
Description of the Change:$P$ and a qubit connectivity graph as inputs and returns a CNOT circuit that has the parity matrix $P$ and that respects the connectivity.
Add a
rowcol
function toqml.labs
that takes a parity matrixBenefits:
CNOT routing and circuit optimization capabilities.
Possible Drawbacks:
N/A
Related GitHub Issues: