qml.transforms.pattern_matching_optimization¶
-
pattern_matching_optimization
(tape, pattern_tapes, custom_quantum_cost=None)[source]¶ Quantum function transform to optimize a circuit given a list of patterns (templates).
- Parameters
tape (QNode or QuantumTape or Callable) – A quantum circuit to be optimized.
pattern_tapes (list(QuantumTape)) – List of quantum tapes that implement the identity.
custom_quantum_cost (dict) – Optional, quantum cost that overrides the default cost dictionary.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]
- Raises
QuantumFunctionError – The pattern provided is not a valid QuantumTape or the pattern contains measurements or the pattern does not implement identity or the circuit has less qubits than the pattern.
Example
>>> dev = qml.device('default.qubit', wires=5)
You can apply the transform directly on a
QNode
. For that, you need first to define a pattern that is to be found in the circuit. We use the following pattern that implements the identity:ops = [qml.S(0), qml.S(0), qml.Z(0)] pattern = qml.tape.QuantumTape(ops)
Let’s consider the following circuit where we want to replace a sequence of two
pennylane.S
gates with apennylane.PauliZ
gate.@partial(pattern_matching_optimization, pattern_tapes = [pattern]) @qml.qnode(device=dev) def circuit(): qml.S(wires=0) qml.Z(0) qml.S(wires=1) qml.CZ(wires=[0, 1]) qml.S(wires=1) qml.S(wires=2) qml.CZ(wires=[1, 2]) qml.S(wires=2) return qml.expval(qml.X(0))
During the call of the circuit, it is first optimized (if possible) and then executed.
>>> circuit()
Usage Details
def circuit(): qml.S(wires=0) qml.Z(0) qml.S(wires=1) qml.CZ(wires=[0, 1]) qml.S(wires=1) qml.S(wires=2) qml.CZ(wires=[1, 2]) qml.S(wires=2) return qml.expval(qml.X(0))
For optimizing the circuit given the following template of CNOTs we apply the
pattern_matching
transform.>>> qnode = qml.QNode(circuit, dev) >>> optimized_qfunc = pattern_matching_optimization(pattern_tapes=[pattern])(circuit) >>> optimized_qnode = qml.QNode(optimized_qfunc, dev)
>>> print(qml.draw(qnode)()) 0: ──S──Z─╭●──────────┤ <X> 1: ──S────╰Z──S─╭●────┤ 2: ──S──────────╰Z──S─┤
>>> print(qml.draw(optimized_qnode)()) 0: ──S†─╭●────┤ <X> 1: ──Z──╰Z─╭●─┤ 2: ──Z─────╰Z─┤
Note that with this pattern we also replace a
pennylane.S
,pennylane.PauliZ
sequence byAdjoint(S)
. If one would like avoiding this, it possible to give a custom quantum cost dictionary with a negative cost forpennylane.PauliZ
.>>> my_cost = {"PauliZ": -1 , "S": 1, "Adjoint(S)": 1} >>> optimized_qfunc = pattern_matching_optimization(circuit, pattern_tapes=[pattern], custom_quantum_cost=my_cost) >>> optimized_qnode = qml.QNode(optimized_qfunc, dev)
>>> print(qml.draw(optimized_qnode)()) 0: ──S──Z─╭●────┤ <X> 1: ──Z────╰Z─╭●─┤ 2: ──Z───────╰Z─┤
Now we can consider a more complicated example with the following quantum circuit to be optimized
def circuit(): qml.Toffoli(wires=[3, 4, 0]) qml.CNOT(wires=[1, 4]) qml.CNOT(wires=[2, 1]) qml.Hadamard(wires=3) qml.Z(1) qml.CNOT(wires=[2, 3]) qml.Toffoli(wires=[2, 3, 0]) qml.CNOT(wires=[1, 4]) return qml.expval(qml.X(0))
We define a pattern that implement the identity:
ops = [ qml.CNOT(wires=[1, 2]), qml.CNOT(wires=[0, 1]), qml.CNOT(wires=[1, 2]), qml.CNOT(wires=[0, 1]), qml.CNOT(wires=[0, 2]), ] tape = qml.tape.QuantumTape(ops)
For optimizing the circuit given the following pattern of CNOTs we apply the
pattern_matching
transform.>>> dev = qml.device('default.qubit', wires=5) >>> qnode = qml.QNode(circuit, dev) >>> optimized_qfunc = pattern_matching_optimization(circuit, pattern_tapes=[pattern]) >>> optimized_qnode = qml.QNode(optimized_qfunc, dev)
In our case, it is possible to find three CNOTs and replace this pattern with only two CNOTs and therefore optimizing the circuit. The number of CNOTs in the circuit is reduced by one.
>>> qml.specs(qnode)()["resources"].gate_types["CNOT"] 4
>>> qml.specs(optimized_qnode)()["resources"].gate_types["CNOT"] 3
>>> print(qml.draw(qnode)()) 0: ─╭X──────────╭X────┤ <X> 1: ─│──╭●─╭X──Z─│──╭●─┤ 2: ─│──│──╰●─╭●─├●─│──┤ 3: ─├●─│───H─╰X─╰●─│──┤ 4: ─╰●─╰X──────────╰X─┤
>>> print(qml.draw(optimized_qnode)()) 0: ─╭X──────────╭X─┤ <X> 1: ─│─────╭X──Z─│──┤ 2: ─│──╭●─╰●─╭●─├●─┤ 3: ─├●─│───H─╰X─╰●─┤ 4: ─╰●─╰X──────────┤
See also
References
[1] Iten, R., Moyard, R., Metger, T., Sutter, D. and Woerner, S., 2022. Exact and practical pattern matching for quantum circuit optimization. doi.org/10.1145/3498325