Trotterization using qml.templates.ApproxTimeEvolution

Hi all,
I was dealing with a simple circuit for my project. But there is an issue.
The code:

import pennylane as qml

H = qml.Hamiltonian(
    [1, 1, 0.5],
    [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(0) @ qml.PauliX(1)]
)
print(H)

(1) [X0]+ (1) [Z1]+ (0.5) [X0 X1]


t = 1
n = 2

@qml.qnode(dev)
def circuit():
    
    qml.templates.ApproxTimeEvolution(H, t, n)
    return [qml.expval(qml.PauliZ(i)) for i in range(2)]

circuit()
print(circuit.draw())

0: ──H──────RZ(1)──H──H──╭RZ(0.5)──H──H──────RZ(1)──H──H──╭RZ(0.5)──H──┤ ⟨Z⟩
1: ──RZ(1)──H────────────╰RZ(0.5)──H──RZ(1)──H────────────╰RZ(0.5)──H──┤ ⟨Z⟩

That code works properly, but when i modify the circuit:


t = 1
n = 2
@qml.qnode(dev)
def circuit(par):
    H=qml.Hamiltonian(par,[qml.PauliX(0), qml.PauliZ(1),qml.PauliZ(0)@qml.PauliZ(1)])
    qml.templates.ApproxTimeEvolution(H, t, n)
    return [qml.expval(qml.PauliZ(i)) for i in range(2)]

circuit([1,0.3,.5])
print(circuit.draw())

Error: Observable PauliZ(wires=[0]) @ PauliZ(wires=[1]) does not have a measurement type specified.

It shows this error. So basically when I want to call the circuit with a random set of parameters correspond to the interaction strength of the local hamiltonians, it doesn’t work. And note that the issue is only with the two-qubit term Z(0) \otimes Z(1). With only single qubit terms the code runs perfectly.
It will be great if anyone can have a look at that issue. Thanks in advance.

Hi @Arun,

Any Operation/Observable that is defined within a QNode will be “queued”, and if an Observable (like PauliZ(wires=[0]) @ PauliZ(wires=[1])) is encountered without some kind of measurement (like qml.expval), the QNode gets confused.

The reason why it works for the single qubit terms is likely because we have introduced some queuing logic for terms of Hamiltonians, but transferring over the same logic to Tensor objects is more difficult, and is something we still have to add in the future.

If you want to have a parametrized Hamiltonian, one option is to use the invisible transform, so it doesn’t get queued when it is instantiated:

@qml.transforms.invisible
def build_ham(par):
    H = qml.Hamiltonian(par,[qml.PauliX(0), qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(1)])
    return H

@qml.qnode(dev)
def circuit(par):
    H = build_ham(par)
    qml.templates.ApproxTimeEvolution(H, t, n)
    return [qml.expval(qml.PauliZ(i)) for i in range(2)]

par = [1, 1, 1]

print(circuit(par))
print(qml.draw(circuit)(par))
[-0.41614684  1.        ]
 0: ──H──────RZ(2)──H──╭RZ(2)──┤ ⟨Z⟩ 
 1: ──RZ(2)────────────╰RZ(2)──┤ ⟨Z⟩ 

Unfortunately, these parameters will not be differentiable: this is not yet supported for Hamiltonian objects.

Hope this helps!

1 Like

Thanks, @Lucaman99, for a quick response. That will work for me and hope the features, you mentioned, that are missing will be introduced soon. That will be really great! And again thanks for your help :slight_smile:

1 Like

Awesome, glad to help :+1:

1 Like