pennylane icon indicating copy to clipboard operation
pennylane copied to clipboard

Additional useful standard transforms

Open cvjjm opened this issue 1 year ago • 6 comments

Feature details

PennyLane ships a few useful transform, but some obvious one that would be nice to have are missing. These include for example transforms that:

  • Combine all all GlobalPhase gates into a single one in the end
  • Remove all GlobalPhase gates
  • Remove adjoints whenever possible, e.g., by changing the gate parameter, i.e., qml.adjoint(qml.RY)(0.2, 0)) -> qml.RY(-0.2, 0))
  • Combine powers of T gates into S gates as much as possible, i.e., TTT -> ST
  • Turn all fixed gates into some equivalent parametrized gate

While I understand that it will never be possible to cover all transforms a user could ever want, an added benefit of a slightly larger library of transforms would that there are more templates for users to start implementing their own transforms from.

Implementation

No response

How important would you say this feature is?

1: Not important. Would be nice to have.

Additional information

No response

cvjjm avatar Aug 22 '24 14:08 cvjjm

Thanks @cvjjm, definitely something we want to improve down the line!

Are there any particular ones in the list that have come up which you would find useful?

josh146 avatar Aug 23 '24 00:08 josh146

They all would have been useful at some point to me and some of them I had also implemented at some point...

For the merging/deleting of the global phase I still have this code lying around:

@transform
def merge_global_phase(tape: QuantumTape, remove=False) -> (Sequence[QuantumTape], Callable):
    filtered_operations = list()

    global_phase = 0
    for operation in tape.operations:
        if isinstance(operation, qml.GlobalPhase):
            global_phase += operation.parameters[0]
        else:
            filtered_operations.append(operation)

    if remove is False and global_phase != 0:
        filtered_operations.append(qml.GlobalPhase(global_phase))

    if compatibility_mode:
        new_tape = type(tape)(filtered_operations, tape.measurements)
    else:
        new_tape = type(tape)(filtered_operations, tape.measurements, shots=tape.shots)

    def postprocessing(results):
        return np.array(results)[0]

    return [new_tape], postprocessing

cvjjm avatar Aug 23 '24 07:08 cvjjm

Note that qml.simplify does simplify adjoints where possible:

@qml.simplify
@qml.qnode(qml.device('default.qubit'))
def circuit(x):
    qml.adjoint(qml.RX(x,0))
    qml.adjoint(qml.X(0))
    return qml.state()

qml.draw(circuit, level=1)(0.5)
0: ──RX(12.07)──X─┤  State

Though it will also perform other simplications.

albi3ro avatar Aug 23 '24 13:08 albi3ro

Note that qml.simplify does simplify adjoints where possible:

@qml.simplify
@qml.qnode(qml.device('default.qubit'))
def circuit(x):
    qml.adjoint(qml.RX(x,0))
    qml.adjoint(qml.X(0))
    return qml.state()

qml.draw(circuit, level=1)(0.5)
0: ──RX(12.07)──X─┤  State

Though it will also perform other simplications.

To avoid oversimplifying, you can also set the lazy keyword argument of qml.adjoint to False, which will cause the operators to eagerly replace Adjoint with updated parameters.

@qml.qnode(qml.device('default.qubit'))
def circuit(x):
    qml.adjoint(qml.RX(x,0), lazy=False)
    qml.adjoint(qml.X(0), lazy=False)
    return qml.state()
print(qml.draw(circuit, level=1)(0.5))
0: ──RX(-0.50)──X─┤  State

mudit2812 avatar Aug 23 '24 14:08 mudit2812

I must say that a transform with an "obscure" name such as simplify that does not really tell me what it does and potentially does things I do not want, which I then have to switch off with another option with an obscure name such as "lazy" is less useful than a collection of transforms with descriptive names (so that they can be found easily via search or by glancing over the list) that do exactly one thing and do it well, and which I can then chain together in exactly the order I want.

Here are a two more for the wish list:

  • A transform that expands all (multi) controlled operations out into with CNOT ladders
  • A transform that tries to permute commuting gates such that the same types of gates and gathered in groups so that they can be parallelized, i.e., turning
0: ─╭●─────────────────────┤  
1: ─│─────────╭●─╭●────────┤  
2: ─╰RY(0.10)─╰X─│─────────┤  
3: ──────────────╰RY(0.50)─┤ 
```
into 
```
0: ─╭●─────────────────────┤  
1: ─│─────────╭●────────╭●─┤  
2: ─╰RY(0.10)─│─────────╰X─┤  
3: ───────────╰RY(0.50)────┤  ```

Further it would be nice if the transforms would get a dedicated page in the documentation. Currently they are a bit hidden as a subsection of the API part.

cvjjm avatar Sep 05 '24 18:09 cvjjm

Hi @cvjjm , you raise some very good points. Thank you very much for these suggestions. Would any of the things in the wish list have higher priority for you than the others?

CatalinaAlbornoz avatar Sep 06 '24 21:09 CatalinaAlbornoz