-
Notifications
You must be signed in to change notification settings - Fork 23
Add from_fermion_operator method to MoelcularHamiltonian class #386
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -10,6 +10,8 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from __future__ import annotations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from typing import Dict, Tuple | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import dataclasses | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import itertools | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -156,6 +158,88 @@ def _fermion_operator_(self) -> FermionOperator: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return op | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@staticmethod | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def from_fermion_operator(op: FermionOperator) -> MolecularHamiltonian: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Convert a FermionOperator to a MolecularHamiltonian.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# extract number of spatial orbitals | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
norb = 1 + max(orb for term in op for _, _, orb in term) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# initialize constant, one‑ and two‑body tensors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
constant: float = 0.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
one_body_tensor = np.zeros((norb, norb), dtype=complex) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
two_body_tensor = np.zeros((norb, norb, norb, norb), dtype=complex) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# track which (p,q,r,s) we've already processed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
seen_2b = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for term, coeff in op.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# constant term: empty tuple | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if len(term) == 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if coeff.imag: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError(f"Constant term must be real. Instead, got {coeff}.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
constant = coeff.real | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kevinsung marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# one‑body term: a†_σ,p a_σ,q (σ = α or β) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
elif len(term) == 2: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(_, _, p), (_, _, q) = term | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
valid_1b = [(cre_a(p), des_a(q)), (cre_b(p), des_b(q))] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if term in valid_1b: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
one_body_tensor[p, q] += 0.5 * coeff | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"FermionOperator cannot be converted to " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
f"MolecularHamiltonian. The one-body term {term} is not " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"of the form a^\\dagger_{\\sigma, p} a_{\\sigma, q}." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# two‑body term: a†_σ,p a†_τ,r a_τ,s a_σ,q | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
elif len(term) == 4: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(_, _, p), (_, _, r), (_, _, s), (_, _, q) = term | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
valid_2b = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(cre_a(p), cre_a(r), des_a(s), des_a(q)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(cre_a(p), cre_b(r), des_b(s), des_a(q)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(cre_b(p), cre_a(r), des_a(s), des_b(q)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(cre_b(p), cre_b(r), des_b(s), des_b(q)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if term not in valid_2b: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"FermionOperator cannot be converted to " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
f"MolecularHamiltonian. The two-body term {term} is not " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"of the form a^{\\dagger}_{\\sigma, p} a^{\\dagger}_{\\sigma, r} a_{\\sigma, s} a_{\\sigma, q}, or " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"a^{\\dagger}_{\\sigma, p} a^{\\dagger}_{\\tau, r} a_{\\tau, s} a_{\\sigma, q}." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
key = (p, q, r, s) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if key not in seen_2b: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
h_pqrs = 2.0 * coeff | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# fill (p,q,r,s) and its 7 symmetric equivalents | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general, there is only four-fold symmetry. See ffsim/tests/python/random_test.py Lines 59 to 85 in 92a23c3
Actually, I don't think we should try to symmetrize the tensor. Just put in the single term, as is. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for a, b, c, d in [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(p, q, r, s), (q, p, r, s), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(p, q, s, r), (q, p, s, r), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(r, s, p, q), (s, r, p, q), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(r, s, q, p), (s, r, q, p), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
two_body_tensor[a, b, c, d] = h_pqrs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# more terms | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"FermionOperator cannot be converted to MolecularHamiltonian." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
f" The term {term} is neither a constant, one-body, or two-body " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"term." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return MolecularHamiltonian( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
one_body_tensor=one_body_tensor, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
two_body_tensor=two_body_tensor, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
constant=constant, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def _approx_eq_(self, other, rtol: float, atol: float) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if isinstance(other, MolecularHamiltonian): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -162,3 +162,25 @@ def test_rotated(): | |
original_expectation = np.vdot(vec, linop @ vec) | ||
rotated_expectation = np.vdot(rotated_vec, linop_rotated @ rotated_vec) | ||
np.testing.assert_allclose(original_expectation, rotated_expectation) | ||
|
||
|
||
def test_from_fermion_operator(): | ||
norb = 5 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make the the test parametrized (see other tests for examples) and test a range of |
||
|
||
rng = np.random.default_rng() | ||
|
||
# generate a random molecular hamiltonian | ||
mol_hamiltonian = ffsim.random.random_molecular_hamiltonian(norb=norb, seed=rng) | ||
|
||
# convert to fermion op | ||
mol_hamiltonian_fops = ffsim.fermion_operator(mol_hamiltonian) | ||
|
||
# convert back from fermion op to ham | ||
mol_hamiltonian_from_ferm = ffsim.MolecularHamiltonian.from_fermion_operator(mol_hamiltonian_fops) | ||
|
||
|
||
# check they are matching | ||
np.testing.assert_allclose(mol_hamiltonian.constant, mol_hamiltonian_from_ferm.constant) | ||
np.testing.assert_allclose(mol_hamiltonian.one_body_tensor, mol_hamiltonian_from_ferm.one_body_tensor) | ||
np.testing.assert_allclose(mol_hamiltonian.two_body_tensor, mol_hamiltonian_from_ferm.two_body_tensor) | ||
|
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 looks like this wasn't actually used.