-
Notifications
You must be signed in to change notification settings - Fork 615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use pauli_rep
to compute qml.matrix() whenever possible
#5392
Conversation
Do we have any data on how much faster this is? Maybe we can use @obliviateandsurrender's benchmark. |
Hello. You may have forgotten to update the changelog!
|
pauli_rep
to compute Sum.matrix()
more efficientlypauli_rep
to compute Sum.matrix()
and Prod.matrix()
more efficiently
Some numbers, the speedup is actually not really significant 🤔
|
op = qml.sum(*(0.5 * (X(i) @ X(i+1)) for i in range(10)))
op += qml.sum(*(0.7 * (Z(i) @ Y(i+1)) for i in range(10)))
op += qml.sum(*(1.5 * (Z(i) @ Y(i+3)) for i in range(10)))
%timeit a = qml.matrix(op) # this branch
109 ms ± 8.92 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit a = qml.matrix(op) # master branch
3.65 s ± 176 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) import pyscf
import numpy as np
h3_molecule = [["H", (0, 0, 0)], ["H", (0, 1.2, 1.2)], ["H", (0, 0, 2.4)]]
molecule, basis, symm, charge, spin, tol = h3_molecule, "631g", None, 0, 1, 1e-6
mol = pyscf.gto.M(atom=molecule, basis=basis, symmetry=symm, charge=charge, spin=spin)
myhf = pyscf.scf.ROHF(mol).run(verbose=0)
core_constant = np.array([mol.energy_nuc()])
# molecular integrals in AO basis
one_ao = mol.intor_symmetric("int1e_kin") + mol.intor_symmetric("int1e_nuc")
two_ao = mol.intor("int2e_sph")
# rotate to MO basis
one_mo = np.einsum("pi,pq,qj->ij", myhf.mo_coeff, one_ao, myhf.mo_coeff)
two_mo = pyscf.ao2mo.incore.full(two_ao, myhf.mo_coeff)
# physicist ordering convention
two_mo = np.swapaxes(two_mo, 1, 3)
h_ferm = qml.qchem.fermionic_observable(core_constant, one_mo, two_mo)
with qml.operation.disable_new_opmath_cm():
H = qml.qchem.qubit_observable(h_ferm)
print(len(H.terms()[1]), type(H))
%timeit qml.matrix(H)
with qml.operation.enable_new_opmath_cm():
H = qml.qchem.qubit_observable(h_ferm)
print(len(H.terms()[1]), type(H))
%timeit qml.matrix(H)
|
…o pauli-swap-order
The issue is that I do think we could leverage |
I am using the Edit: The failures seem to come from #5301 This PR is currently blocked by this |
There's currently a CI check failing, but bringing in @maliasadi @vincentmr here as this is a performance issue related to op arithmetic. Thanks! |
Maybe we should bring in @mudit2812 too, since he's the instigator of #5301 . |
Would it be possible to update I'd like to keep the dense matrix differentiable if at all possible. |
I fixed all failing CI in my PR. I will update branch and let's see if CI passes. Once CI passes, I will run lightning CI against this branch as well to validate that tests pass. I'll update this PR accordingly, but if lightning tests break because of this PR, we should have a PR on the lightning side ready to be merged right after this. |
Also keeping @mlxd in the loop 👍 |
pauli_rep
to compute Sum.matrix()
and Prod.matrix()
more efficientlypauli_rep
to compute qml.matrix() whenever possible
In the current implementation that uses I.e. def f(x):
mat = qml.matrix(x * X(0))
return jnp.sum(mat)
x = jnp.array(0.5, dtype=complex)
f(x), jax.grad(f, holomorphic=True)(x) (Array(1.+0.j, dtype=complex128), Array(0.+0.j, dtype=complex128)) This is why the pulse tests fail (the "true" results are computed with explicit matrices evaluated via qml.matrix(), using @albi3ro I didnt fully grasp your suggestion, could you elaborate which specific steps you would take? |
@@ -211,8 +211,10 @@ def circuit(): | |||
if isinstance(op, qml.operation.Tensor) and wire_order is not None: | |||
op = 1.0 * op # convert to a Hamiltonian | |||
|
|||
if isinstance(op, qml.ops.Hamiltonian): | |||
if (pr := op.pauli_rep) is not None: | |||
return pr.to_mat(wire_order=wire_order or op.wires, format="csr").toarray() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is CSR what we want to use for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For context, this came up in switching to new opmath because some generators and qchem operators where now slower in computing qml.matrix()
, as they were no longer ops.Hamiltonian
instances and therefore didnt hit the op.sparse_matrix(..).toarray()
method below anymore. The idea was to just allow still doing that via the pauli rep in this PR; but as @albi3ro pointed out this breaks differentiability. Afaik christina has been cooking up an alternate solution 👩🍳
Closing in favor of #5578 that ensures differentiability |
Use
pauli_rep
whenever possible to compute the matrix of aSum
operator more efficiently.Builds on / is blocked by #5301
[sc-58850]