Skip to content
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

Adding a user-friendly qml.transform.decompose function #6334

Merged
merged 25 commits into from
Oct 11, 2024

Conversation

andrijapau
Copy link
Contributor

@andrijapau andrijapau commented Oct 3, 2024

Context:

Users often want to inspect and transform quantum circuits at different stages of decomposition. This feature will allow them to gain a better understanding for the decomposition and to think about algorithms at different layers of abstraction.

Prior to this feature, users only had access to qml.devices.preprocess.decompose- a function focused on device preprocessing. Over time, it became apparent that this function is developer focused, making use of hard-coded error messages / exceptions as well as non-intuitive stopping conditions. This led to some frustrations where users were trying to explore abstract decompositions (such as counting the number of CNOTs in a multi-controlled gate). For now, this function will serve to call the qml.devices.preprocess.decompose function and prune any developer-focused features. This feature should accomplish the following,

  1. Decomposition of circuits into a desired gate set.
  2. Decomposition of circuits into gate sets defined by a Callable[Operator, bool] (e.g. lambda op: len(op.wires) <= 2).
  3. Decomposition of circuits in stages to understand the process. This can be done by adjusting the max_expansion kwarg.

Description of the Change:

The goal of this feature is to create a user-facing function that enables intuitive decomposition in a less restrictive manner.

Examples:

>>> @partial(decompose, gate_set={qml.Toffoli, "RX", "RZ"})
>>> @qml.qnode(dev)
>>> def circuit():
>>>     qml.Hadamard(wires=[0])
>>>     qml.Toffoli(wires=[0,1,2])
>>>     return qml.expval(qml.Z(0))
>>>
>>> print(qml.draw(circuit)())
0: ──RZ(1.57)──RX(1.57)──RZ(1.57)─╭●─┤  <Z>
1: ───────────────────────────────├●─┤
2: ───────────────────────────────╰X─┤

>>> @partial(decompose, gate_set=lambda op: len(op.wires) <= 2)
>>> @qml.qnode(dev)
>>> def circuit():
>>>     qml.Hadamard(wires=[0])
>>>     qml.Toffoli(wires=[0,1,2])
>>>     return qml.expval(qml.Z(0))
>>>
>>> print(qml.draw(circuit)())
0: ──H────────╭●───────────╭●────╭●──T──╭●─┤  <Z>
1: ────╭●─────│─────╭●─────│───T─╰X──T†─╰X─┤
2: ──H─╰X──T†─╰X──T─╰X──T†─╰X──T──H────────┤

>>> tape = qml.tape.QuantumScript([qml.IsingXX(1.2, wires=(0,1))], [qml.expval(qml.Z(0))])
>>> batch, fn = qml.transforms.decompose(tape, gate_set={"CNOT", "RX", "RZ"})
>>> batch[0].circuit
[CNOT(wires=[0, 1]), RX(1.2, wires=[0]), CNOT(wires=[0, 1]), expval(Z(0))]

Benefits:

Users can explore quantum circuit decomposition in a more abstract sense without having to rely on specific device architectures.

Possible Drawbacks:

None that I am aware of (yet 😄).

Related Shortcut Story:

[sc-51282]

@andrijapau andrijapau self-assigned this Oct 3, 2024
@andrijapau
Copy link
Contributor Author

Hi @lillian542 & @albi3ro,

Would appreciate any comments on the implementation and whether or not I have the right idea for what's required.

I'm working on tests now but I figured I would get it out to you ASAP 😄.

Copy link
Contributor

@lillian542 lillian542 left a comment

Choose a reason for hiding this comment

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

Looks like great progress, @andrijapau 🚀 I left a few thoughts on the implementation so far, let me know if you have any questions!

Wrt names, one suggestion could be to rename the other function to device_decompose (that would probably require a deprecation cycle), and then call this something like apply_decomposition. I think that would make it fairly clear that one is intended for the preprocess of a device, and the other is just a user applying a decomposition they want to specify.

@andrijapau
Copy link
Contributor Author

andrijapau commented Oct 4, 2024

Hi @lillian542 & @albi3ro,

Thank you for your input, I've implemented the suggestions into the newest commit. 😄

Notable changes:

  • The user can use max_expansion to "step through" decomposition stages of the circuit.
  • Warnings are raised if operators can no longer be decomposed and were not a part of their desired gate set.

Open to feedback!

Copy link
Contributor

@lillian542 lillian542 left a comment

Choose a reason for hiding this comment

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

Looking good! I left a few more comments/questions :)

@JerryChen97 JerryChen97 marked this pull request as ready for review October 7, 2024 20:35
Copy link
Contributor

@lillian542 lillian542 left a comment

Choose a reason for hiding this comment

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

Looking good! If you feel it's ready, you could switch this from a draft to ready-for-review now. Then CI will run and you will see any CodeCov warnings if there are any lines missing test coverage 😅

@andrijapau andrijapau marked this pull request as draft October 7, 2024 21:24
Copy link

codecov bot commented Oct 9, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 99.39%. Comparing base (dbb64fb) to head (be09ee9).
Report is 324 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6334      +/-   ##
==========================================
- Coverage   99.71%   99.39%   -0.32%     
==========================================
  Files         447      448       +1     
  Lines       42365    42464      +99     
==========================================
- Hits        42244    42209      -35     
- Misses        121      255     +134     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@andrijapau andrijapau requested a review from albi3ro October 9, 2024 15:55
Copy link
Contributor

@lillian542 lillian542 left a comment

Choose a reason for hiding this comment

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

Left some comments about documentation, this one is very user-facing, so being extra careful 🚀

Copy link
Contributor

@albi3ro albi3ro left a comment

Choose a reason for hiding this comment

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

❤️ No more concerns from my side. So happy to be getting this in :)

Copy link
Contributor

@lillian542 lillian542 left a comment

Choose a reason for hiding this comment

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

Looks great, thanks @andrijapau!

@albi3ro albi3ro merged commit 38a5140 into master Oct 11, 2024
37 of 38 checks passed
@albi3ro albi3ro deleted the add-user-friendly-decomposition branch October 11, 2024 18:22
austingmhuang pushed a commit that referenced this pull request Oct 23, 2024
**Context:**

Users often want to inspect and transform quantum circuits at different
stages of decomposition. This feature will allow them to gain a better
understanding for the decomposition and to think about algorithms at
different layers of abstraction.

Prior to this feature, users only had access to
`qml.devices.preprocess.decompose`- a function focused on device
preprocessing. Over time, it became apparent that this function is
developer focused, making use of hard-coded error messages / exceptions
as well as non-intuitive stopping conditions. This led to some
frustrations where users were trying to explore abstract decompositions
(such as counting the number of CNOTs in a multi-controlled gate). For
now, this function will serve to call the
`qml.devices.preprocess.decompose` function and prune any
developer-focused features. This feature should accomplish the
following,

1. Decomposition of circuits into a desired gate set.
2. Decomposition of circuits into gate sets defined by a
`Callable[Operator, bool]` (e.g. `lambda op: len(op.wires) <= 2`).
3. Decomposition of circuits in stages to understand the process. This
can be done by adjusting the `max_expansion` kwarg.

**Description of the Change:**

The goal of this feature is to create a user-facing function that
enables intuitive decomposition in a less restrictive manner.

Examples:

```python
>>> @partial(decompose, gate_set={qml.Toffoli, "RX", "RZ"})
>>> @qml.qnode(dev)
>>> def circuit():
>>>     qml.Hadamard(wires=[0])
>>>     qml.Toffoli(wires=[0,1,2])
>>>     return qml.expval(qml.Z(0))
>>>
>>> print(qml.draw(circuit)())
0: ──RZ(1.57)──RX(1.57)──RZ(1.57)─╭●─┤  <Z>
1: ───────────────────────────────├●─┤
2: ───────────────────────────────╰X─┤

>>> @partial(decompose, gate_set=lambda op: len(op.wires) <= 2)
>>> @qml.qnode(dev)
>>> def circuit():
>>>     qml.Hadamard(wires=[0])
>>>     qml.Toffoli(wires=[0,1,2])
>>>     return qml.expval(qml.Z(0))
>>>
>>> print(qml.draw(circuit)())
0: ──H────────╭●───────────╭●────╭●──T──╭●─┤  <Z>
1: ────╭●─────│─────╭●─────│───T─╰X──T†─╰X─┤
2: ──H─╰X──T†─╰X──T─╰X──T†─╰X──T──H────────┤

>>> tape = qml.tape.QuantumScript([qml.IsingXX(1.2, wires=(0,1))], [qml.expval(qml.Z(0))])
>>> batch, fn = qml.transforms.decompose(tape, gate_set={"CNOT", "RX", "RZ"})
>>> batch[0].circuit
[CNOT(wires=[0, 1]), RX(1.2, wires=[0]), CNOT(wires=[0, 1]), expval(Z(0))]
```

**Benefits:**

Users can explore quantum circuit decomposition in a more abstract sense
without having to rely on specific device architectures.

**Possible Drawbacks:**

None that I am aware of (yet 😄).

**Related Shortcut Story:**


[sc-51282]
mudit2812 pushed a commit that referenced this pull request Nov 11, 2024
**Context:**

Users often want to inspect and transform quantum circuits at different
stages of decomposition. This feature will allow them to gain a better
understanding for the decomposition and to think about algorithms at
different layers of abstraction.

Prior to this feature, users only had access to
`qml.devices.preprocess.decompose`- a function focused on device
preprocessing. Over time, it became apparent that this function is
developer focused, making use of hard-coded error messages / exceptions
as well as non-intuitive stopping conditions. This led to some
frustrations where users were trying to explore abstract decompositions
(such as counting the number of CNOTs in a multi-controlled gate). For
now, this function will serve to call the
`qml.devices.preprocess.decompose` function and prune any
developer-focused features. This feature should accomplish the
following,

1. Decomposition of circuits into a desired gate set.
2. Decomposition of circuits into gate sets defined by a
`Callable[Operator, bool]` (e.g. `lambda op: len(op.wires) <= 2`).
3. Decomposition of circuits in stages to understand the process. This
can be done by adjusting the `max_expansion` kwarg.

**Description of the Change:**

The goal of this feature is to create a user-facing function that
enables intuitive decomposition in a less restrictive manner.

Examples:

```python
>>> @partial(decompose, gate_set={qml.Toffoli, "RX", "RZ"})
>>> @qml.qnode(dev)
>>> def circuit():
>>>     qml.Hadamard(wires=[0])
>>>     qml.Toffoli(wires=[0,1,2])
>>>     return qml.expval(qml.Z(0))
>>>
>>> print(qml.draw(circuit)())
0: ──RZ(1.57)──RX(1.57)──RZ(1.57)─╭●─┤  <Z>
1: ───────────────────────────────├●─┤
2: ───────────────────────────────╰X─┤

>>> @partial(decompose, gate_set=lambda op: len(op.wires) <= 2)
>>> @qml.qnode(dev)
>>> def circuit():
>>>     qml.Hadamard(wires=[0])
>>>     qml.Toffoli(wires=[0,1,2])
>>>     return qml.expval(qml.Z(0))
>>>
>>> print(qml.draw(circuit)())
0: ──H────────╭●───────────╭●────╭●──T──╭●─┤  <Z>
1: ────╭●─────│─────╭●─────│───T─╰X──T†─╰X─┤
2: ──H─╰X──T†─╰X──T─╰X──T†─╰X──T──H────────┤

>>> tape = qml.tape.QuantumScript([qml.IsingXX(1.2, wires=(0,1))], [qml.expval(qml.Z(0))])
>>> batch, fn = qml.transforms.decompose(tape, gate_set={"CNOT", "RX", "RZ"})
>>> batch[0].circuit
[CNOT(wires=[0, 1]), RX(1.2, wires=[0]), CNOT(wires=[0, 1]), expval(Z(0))]
```

**Benefits:**

Users can explore quantum circuit decomposition in a more abstract sense
without having to rely on specific device architectures.

**Possible Drawbacks:**

None that I am aware of (yet 😄).

**Related Shortcut Story:**


[sc-51282]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants