From `qml.Hamiltonian` to numpy representation

Hi,

I have a question regarding the qml.Hamiltonian function. Given that I have a Hamiltonian written as a qml.Hamiltonian, can I see it’s matrix representation as a np.array? The point of this is because I want to use a circuit that does the following:

def circuit(params):
        # performs some operations
        return qml.expval(qml.Hermitian(array), wires = [...])

I know that a possibility is to use qml.ExpvalCost() instead of doing this. However, I want to use JAX here so that instead of working with circuit I work with vcircuit = jax.vmap(circuit) so I can run the same circuit for multiple parameters simultaneously.

Also, the point of this is because I know how to write my Hamiltonian efficiently in terms of the parameters needed for qml.Hamiltonian (it’s basically a sum of local terms so I can use qml.PauliX operations together with the Y and Z).

On the other hand, in the qml.Hamiltonian documentation I see that it is possible to go from qml.Hermitian to qml.Hamiltonian, so I wonder whether it is possible to do it the other way around, i.e., given that I have a qml.Hamiltonian, could I generate the corresponding qml.Hermitian?

Thank you very much for your attention.

Cheers,
Javier.

Hi Javier,

Thanks for the question! The qml.Hamilonian class does not currently have a matrix attribute however the qml.Operation class does which returns a numpy (nd)array as you’re looking for. Since a Hamiltonian is built up of operators and coefficients we can find work around e.g

import pennylane as qml

coeffs = [0.2, -0.543]
ops = [qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.Hadamard(2)]
H = qml.Hamiltonian(coeffs, ops)

matrix = 0
for coeff, op in zip(H.coeffs, H.ops):
    matrix += coeff * op.matrix

where the variable matrix gives the array

 array([[-0.38395898, -0.38395898,  0.2       ,  0.        ],
       [-0.38395898,  0.38395898,  0.        , -0.2       ],
       [ 0.2       ,  0.        ,  0.38395898,  0.38395898],
       [ 0.        , -0.2       ,  0.38395898, -0.38395898]])

Let us know if you have any more questions!

2 Likes

I’m a bit late to the party, but wanted to point out that in addition to @anthayes92 excellent answer above, we also have a utility in PennyLane to convert Hamiltonian’s to sparse matrices:

>>> coeffs = [0.2, -0.543]
>>> ops = [
...   qml.PauliX(0) @ qml.PauliZ(1),
...   qml.PauliZ(0) @ qml.Hadamard(1)
... ]
>>> H = qml.Hamiltonian(coeffs, ops)
>>> Hmat = qml.utils.sparse_hamiltonian(H).real
>>> Hmat
<4x4 sparse matrix of type '<class 'numpy.float64'>'
        with 12 stored elements in COOrdinate format>

You can directly manipulate this sparse matrix using scipy.sparse, or you can convert it to a NumPy array:

>>> Hmat.toarray()
array([[-0.38395898, -0.38395898,  0.2       ,  0.        ],
       [-0.38395898,  0.38395898,  0.        , -0.2       ],
       [ 0.2       ,  0.        ,  0.38395898,  0.38395898],
       [ 0.        , -0.2       ,  0.38395898, -0.38395898]])
2 Likes

Hi,

thank you very much for your answers! They are both super useful and exactly what I was looking for!

Cheers,
Javier.

No worries, glad they helped @Javier!

Hi,
I think this doesn’t really work - if you chance the wires for the Paulis in the ops, the final Hamiltonian in numpy representation does not change! It seems like the whole method doesn’t care about what wires the gates are acting on, which means the final numpy representation is almost always wrong!

Hi @eigentejas, welcome to the Forum!

Please notice that this thread was from about 1.5 years ago and PennyLane has evolved a lot since then! :grinning: Would you like to describe the problem you’re having? I might be able to help you. Please if possible also post:

  • Your code so that I can try to replicate your issue
  • The output of qml.about()
  • Your full error traceback

I look forward to hearing back from you!

Along these lines, is compute_matrix() in qml.Hamiltonian supported? I tried to use this to get the matrix and I got a pennylane.operation.MatrixUndefinedError. Because none of the options presented here seem to still work, at least for me.

Hey @Lucas_Tecot,

Indeed it’s not supported at the moment, but there is a way around this by looking at the terms() attribute that a Hamiltonian has in PEnnyLane:

>>> import pennylane as qml
>>> H = 3*qml.PauliY(0) + 0.5  * qml.PauliZ(0)
>>> H
<Hamiltonian: terms=2, wires=[0]>
>>> H.terms()
([3.0, 0.5], [PauliY(wires=[0]), PauliZ(wires=[0])])
>>> coeffs, ops = H.terms()[0], H.terms()[1]
>>> coeffs, ops
([3.0, 0.5], [PauliY(wires=[0]), PauliZ(wires=[0])])
>>> mat = [coeffs[i] * ops[i].compute_matrix() for i in range(len(coeffs))]
>>> mat
[array([[0.+0.j, 0.-3.j],
       [0.+3.j, 0.+0.j]]), array([[ 0.5,  0. ],
       [ 0. , -0.5]])]
>>> from pennylane import numpy as np
>>> mat = np.dot(mat[0], mat[1])
>>> mat
tensor([[0.+0.j , 0.+1.5j],
        [0.+1.5j, 0.+0.j ]], requires_grad=True)
>>>

Let me know if that helps!

I actually had a friend recommend I just use qml.matrix(H), which I think works? Should that function work when inputing a Hamiltonian?

Oh! Yeah that does work :sweat_smile:

>>> qml.matrix(H)
array([[ 0.5+0.j,  0. -3.j],
       [ 0. +3.j, -0.5+0.j]])

Use that then! :grin:

matrix += coeff * op.matrix
          ~~~~~~^~~~~~~~~~~

TypeError: unsupported operand type(s) for *: ‘float’ and ‘method’

Hey @jeet_sharma,

As the error suggests, matrix is a method, so you need to add parentheses after to call it.

matrix += coeff * op.matrix()

That should do it :slight_smile: