Skip to content

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
@codecov
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
36 of 37 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

5 participants