How to disable qubit-wise commuting?

Hello, everyone!

Can I disable cubit-wise commuting option when using expval() function?

Hello Roux,

I would like to help you with this, as I was recently implementing the qubit-wise commuting evaluation. Can you please elaborate on what the problem is and what you are trying to achieve?

Currently, when you pass a list of observables to be evaluated, that contains non-commuting terms, then internally, the expectation values are computed separately in commuting groups. This, however, has no effect on the output!

So for example, when you compute return the expvals of [X_1, Y_1, X_1@X_2, Y_1@Y_2] - then internally it computes [X_1, X_1@X_2] and [Y_1, Y_1@Y_2] - since these are the commuting subgroups, but the output is unaffected by this and again the list of expectation values as expected: [X_1, Y_1, X_1@X_2, Y_1@Y_2]

(I am using short notation X_1 = qml.PauliX(wires=1))

What currently does not work is mixing expectation values of non-commuting Hamiltonians and operators. I.e. you currently cannot return
[qml.expval(1.*qml.PauliX(0)), qml.expval(qml.PauliY(0))]
as the first term 1.*qml.PauliX(0)) is interpreted as a Hamiltonian object in pennylane.

2 Likes

Hello Qottmann!

Sorry for the late reply. And thank you for your answer!
I’m trying to compare the number of executions of a device with qwc and without qwc. So I tried to use ExpValCost() instead of expval() with dev.num_executions and optimize option (True or False). I found out that num_executions with expval is lesser than num_executions with ExpValCost() with optimize=True. Is there any additional optimization inside this expval function?
As an operator inside expval() I use the molecular Hamiltonian of H2 chain.

symbols = ["H", "H"]
r = 2.969280527
geometry = np.array([0.0, 0.0, 0.0, 0.0, 0.0, r])
H, qubits = qchem.molecular_hamiltonian(symbols,
geometry)

Hi Roux,

in the new release of PennyLane v0.24 we actually deprecated ExpvalCost, which is generally much less efficient than expval. Users are now advised to use expval exclusively. In case you have old code that was doing qml.ExpvalCost(H, qfunc, dev) you can just use qml.QNode(qfunc, dev) where you change qfunc to return qml.expval(H) instead, for minimal maintanance :slight_smile: - or define the qnode directly as

@qml.qnode(dev)
def qfunc(*args, **kwargs):
    <operations>
    return qml.expval(H)

qml.expval is not doing any further optimization in terms of commuting groups, I think the case here is rather that qml.ExpvalCost was just doing some unnecessary operations (dont quote me on this though, I know too little about ExpvalCost).

So if you want to compare optimized vs. non-optimized measurement you can pass a Hamiltonian with grouping_type set to "qwc" and None, respectively.
Since you are using a molecular Hamiltonian, the one way to do that is

H_opt = qml.Hamiltonian(H.coeffs, H.ops, grouping_type="qwc")

where H is the molecular Hamiltonian from your code snippet.

Hope that helps! Let me know if you have further questions :slight_smile:

1 Like

Here is a short example highlighting the difference in efficiency:

symbols = ["H", "H"]
r = 2.969280527
geometry = np.array([0.0, 0.0, 0.0, 0.0, 0.0, r])
H, qubits = qml.qchem.molecular_hamiltonian(symbols,
geometry)

weights_shape = template.shape(n_layers, n_wires)
w1, w2 = [2 * np.pi * np.random.random(s) for s in weights_shape]

dev = qml.device("default.mixed", wires=n_wires)

@qml.qnode(dev)
def circuit(w1, w2, H):
    qml.SimplifiedTwoDesign(w1, w2, wires=range(n_wires))
    return qml.expval(H)

H_opt = qml.Hamiltonian(H.coeffs, H.ops, grouping_type="qwc")
H_std = qml.Hamiltonian(H.coeffs, H.ops, grouping_type=None)
>>> %timeit circuit(w1, w2, H_opt)
64 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit circuit(w1, w2, H_std)
150 ms ± 4.06 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1 Like

Hi Qottmann!

Thank you for detailed response, somehow I didn’t notice the “grouping_type” option in the Hamiltonian function before :slight_smile:

1 Like