-
Notifications
You must be signed in to change notification settings - Fork 575
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
Add functions to find correct sector for qubit tapering #2041
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2041 +/- ##
=======================================
Coverage 99.18% 99.18%
=======================================
Files 226 226
Lines 17354 17368 +14
=======================================
+ Hits 17212 17226 +14
Misses 142 142
Continue to review full report at Codecov.
|
pennylane/hf/tapering.py
Outdated
return energy | ||
|
||
|
||
def find_optimal_sector(qubit_op, generators, paulix_ops, brute_force=True, num_electrons=0): |
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.
I think this function should only return the correct sector and not the energy. This means that we should not perform the brute force method here since the user can always call the transform_hamiltonian
function with any desired sector. Also, if the user can already obtain the correct sector, then no need to do a brute force at all. Therefore, the args and return might be:
Args: generators, reference HF state
Return: list of eigenvalues
Instead of the reference HF state, we might have mol, core=None, active=None
or something similar.
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.
Agree with Soran 💯 Why use an inefficient brute force method when we know a better algorithm?
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.
Agreed. My original intention to include brute force was for the sake of completeness. But now that transform_hamiltonian
can already allow users to do it already, I also believe that we can skip doing it here.
pennylane/hf/tapering.py
Outdated
To obtain the optimal sector one can brute force through all the permutation or by utilize the following | ||
relation between the Pauli-Z qubit operator and the occupation number under Jordan-Wigner transform. | ||
|
||
.. math:: | ||
|
||
\sigma_z^{i} = a_{i}^{\dagger}a_{i} - 1 | ||
|
||
This relation allows us to figure out whether the orbital is occupied or unoccupied in a given symmetry sector, | ||
to build the correct eigensector. |
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.
The theory can be explained a bit better.
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.
Agree. How exactly does knowing if an orbital is occupied or not help us build the correct eigensector?
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.
Thanks @obliviateandsurrender. I have two major suggestions:
- Focus this PR (and the function) to obtain the sector, no need to compute the energy here which means that we do not need to perform the brute force method.
- It will be great if we have a slightly more transparent explanation of how the sector is found..
pennylane/hf/tapering.py
Outdated
|
||
.. math:: | ||
|
||
\sigma_z^{i} = a_{i}^{\dagger}a_{i} - 1 |
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.
Shouldn't this be \sigma_z^{i} = 1 - 2 a_{i}^{\dagger}a_{i} or maybe I am missing something?
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.
Number operator is [[0, 0], [0, 1]]
so indeed the formula Soran is quoting should be the correct one. Also, I think it's better to express the number operator in terms of the Pauli operators. I suggest changing to
n_i=a_{i}^{\dagger}a_{i}=I-2\sigma_z^i
But please check that we are using labels on the Pauli operators consistently. I recall in other PRs we were using \sigma_i^z
, though I may be wrong
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.
Updated. Even I'd gotten the same result when I was calculating it by myself. But I wonder why Setia's paper that we are following used this definition (Eq 17).
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.
The code performing the main calculation looks good, but the rest of the PR needs additional work.
pennylane/hf/tapering.py
Outdated
"""Computes energy of the given tapered Hamiltonian. | ||
|
||
Args: | ||
sector (pennylane.Hamiltonian): tapered Hamiltonian whose energy has to be calculated |
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.
I'm still very confused by what we mean by "sector". I thought it was a list of eigenvalues? But here it is a Hamiltonian?
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.
I am sorry for the confusion. I somehow used "sector" here to represent the tapered Hamiltonian. I guess while implementing the function I more or less remembered quite a literal definition of the word sector (as in part of the whole). Didn't realize the conflict.
pennylane/hf/tapering.py
Outdated
""" | ||
|
||
if len(sector.wires) < 2: | ||
energy = min(sp.linalg.eigvals(qml.utils.sparse_hamiltonian(sector).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.
Won't this be very expensive for more complicated Hamiltonians? Thinking long term, if there is ever an experiment attempting to simulate a large system, it may be prohibitive to directly diagonalize the Hamiltonian. Isn't that a problem here?
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.
Yes. The energy calculation was being done only to support the brute force method. Now that I have removed it, this is not needed either.
pennylane/hf/tapering.py
Outdated
return energy | ||
|
||
|
||
def find_optimal_sector(qubit_op, generators, paulix_ops, brute_force=True, num_electrons=0): |
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.
Agree with Soran 💯 Why use an inefficient brute force method when we know a better algorithm?
pennylane/hf/tapering.py
Outdated
f" got 'orbitals'={num_orbitals} < 'electrons'={num_electrons}." | ||
) | ||
|
||
hf_str = np.where(np.arange(num_orbitals) < num_electrons, 1, 0) |
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.
Clever implementation!
@@ -349,10 +303,13 @@ def test_generate_paulis(generators, num_qubits, result): | |||
), | |||
], | |||
) | |||
def test_generate_symmetries(hamiltonian, num_qubits, res_generators, res_pauli_ops): | |||
def test_generate_symmetries(symbols, geometry, num_qubits, res_generators, res_pauli_ops): |
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.
Why is this change being made?
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.
To avoid hardcoding the input Hamiltonian which could be huge for any molecule other than H2.
pennylane/hf/tapering.py
Outdated
def optimal_sector(qubit_op, generators, active_electrons): | ||
r"""Get the optimal sector which contains the ground state. | ||
|
||
To obtain the optimal sector, we need to choose the right eigenvalue for the symmetries :math:`\vec{\tau}`. We can do |
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.
It seems to me that the optimal eigenvalue sector is obtained by applying each \tau
to the referee HF state. Assuming that \tau
s are composed of only I
and Pauli-Z
, then finding the eigenvalue is trivial by just looking at the wires of the Pauli-Z
ops and the occupation state of the qubits.
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.
Thanks for the confirmation on this! I have updated the docstring to make it more concise.
@@ -464,3 +465,65 @@ def transform_hamiltonian(h, generators, paulix_ops, paulix_sector): | |||
c = qml.math.stack(c) | |||
|
|||
return _simplify(qml.Hamiltonian(c, o)) | |||
|
|||
|
|||
def optimal_sector(qubit_op, generators, active_electrons): |
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.
Why not just use h
instead of qubit_op
?
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.
I am just a bit more inclined towards using descriptive variable names.
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.
Looks good to me, thanks for the changes.
perm = [] | ||
for tau in generators: | ||
symmstr = np.array([1 if wire in tau.ops[0].wires else 0 for wire in qubit_op.wires]) | ||
coeff = -1 if numpy.logical_xor.reduce(numpy.logical_and(symmstr, hf_str)) else 1 |
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.
I like that this is a 1-liner
Looks good to me. Sorry for the delay! |
Context:
This PR adds functions to find the correct sector for which the tapered Hamiltonian will correspond to the ground state of the system.
Description of the Change:
_sector_energy
function returns the energy of the Hamiltonian in a given sector.find_optimal_sector
function returns the correct sector containing the ground state. It offers two strategies to do so - (i) by performing a brute force through all the possible permutations (exponential), and (ii) by building the sector using a relation between Pauli-Z operator and occupation number under JW transform (linear).Benefits:
Possible Drawbacks:
Related GitHub Issues: