Skip to content

Commit

Permalink
add spinful givens ansatz operator gate
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinsung committed May 14, 2024
1 parent 049602d commit 2cd1c55
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 6 deletions.
2 changes: 2 additions & 0 deletions python/ffsim/qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from ffsim.qiskit.gates import (
DiagCoulombEvolutionJW,
GivensAnsatzOperatorJW,
GivensAnsatzOperatorSpinlessJW,
OrbitalRotationJW,
OrbitalRotationSpinlessJW,
Expand All @@ -38,6 +39,7 @@
__all__ = [
"DiagCoulombEvolutionJW",
"DropNegligible",
"GivensAnsatzOperatorJW",
"GivensAnsatzOperatorSpinlessJW",
"MergeOrbitalRotations",
"OrbitalRotationJW",
Expand Down
6 changes: 5 additions & 1 deletion python/ffsim/qiskit/gates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
"""Qiskit fermionic quantum gates."""

from ffsim.qiskit.gates.diag_coulomb import DiagCoulombEvolutionJW
from ffsim.qiskit.gates.givens_ansatz import GivensAnsatzOperatorSpinlessJW
from ffsim.qiskit.gates.givens_ansatz import (
GivensAnsatzOperatorJW,
GivensAnsatzOperatorSpinlessJW,
)
from ffsim.qiskit.gates.orbital_rotation import (
OrbitalRotationJW,
OrbitalRotationSpinlessJW,
Expand All @@ -26,6 +29,7 @@

__all__ = [
"DiagCoulombEvolutionJW",
"GivensAnsatzOperatorJW",
"GivensAnsatzOperatorSpinlessJW",
"OrbitalRotationJW",
"OrbitalRotationSpinlessJW",
Expand Down
40 changes: 38 additions & 2 deletions python/ffsim/qiskit/gates/givens_ansatz.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,46 @@
from ffsim.variational import GivensAnsatzOperator


class GivensAnsatzOperatorSpinlessJW(Gate):
class GivensAnsatzOperatorJW(Gate):
"""Givens rotation ansatz operator under the Jordan-Wigner transformation.
See :class:`ffsim.GivensAnsatzOperator` for a description of this gate's unitary.
"""

def __init__(
self, givens_ansatz_operator: GivensAnsatzOperator, *, label: str | None = None
):
"""Create a new Givens ansatz operator gate.
Args:
givens_ansatz_operator: The Givens rotation ansatz operator.
label: The label of the gate.
"""
self.givens_ansatz_operator = givens_ansatz_operator
super().__init__(
"givens_ansatz_jw", 2 * givens_ansatz_operator.norb, [], label=label
)

def _define(self):
"""Gate decomposition."""
qubits = QuantumRegister(self.num_qubits)
circuit = QuantumCircuit(qubits, name=self.name)
norb = len(qubits) // 2
alpha_qubits = qubits[:norb]
beta_qubits = qubits[norb:]
for instruction in _givens_ansatz_jw(alpha_qubits, self.givens_ansatz_operator):
circuit.append(instruction)
for instruction in _givens_ansatz_jw(beta_qubits, self.givens_ansatz_operator):
circuit.append(instruction)
self.definition = circuit


class GivensAnsatzOperatorSpinlessJW(Gate):
"""Givens rotation ansatz operator under the Jordan-Wigner transformation, spinless.
Like :class:`GivensAnsatzOperatorJW` but only acts on a single spin species.
"""

def __init__(
self, givens_ansatz_operator: GivensAnsatzOperator, *, label: str | None = None
):
Expand All @@ -52,7 +86,9 @@ def _define(self):
"""Gate decomposition."""
qubits = QuantumRegister(self.num_qubits)
self.definition = QuantumCircuit.from_instructions(
_givens_ansatz_jw(qubits, self.givens_ansatz_operator), qubits=qubits
_givens_ansatz_jw(qubits, self.givens_ansatz_operator),
qubits=qubits,
name=self.name,
)


Expand Down
35 changes: 32 additions & 3 deletions tests/python/qiskit/givens_ansatz_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,43 @@ def brickwork(norb: int, n_layers: int):
yield (j, j + 1)


@pytest.mark.parametrize("norb, nocc", ffsim.testing.generate_norb_nocc(range(5)))
def test_random_givens_ansatz_operator(norb: int, nocc: int):
@pytest.mark.parametrize("norb, nelec", ffsim.testing.generate_norb_nelec(range(5)))
def test_random_givens_ansatz_operator_spinful(norb: int, nelec: tuple[int, int]):
"""Test random Givens rotation ansatz gives correct output state."""
rng = np.random.default_rng()
dim = ffsim.dim(norb, nelec)
for _ in range(3):
n_layers = 2 * norb
interaction_pairs = list(brickwork(norb, n_layers))
thetas = rng.uniform(-np.pi, np.pi, size=len(interaction_pairs))
givens_ansatz_op = ffsim.GivensAnsatzOperator(norb, interaction_pairs, thetas)
gate = ffsim.qiskit.GivensAnsatzOperatorJW(givens_ansatz_op)

small_vec = ffsim.random.random_statevector(dim, seed=rng)
big_vec = ffsim.qiskit.ffsim_vec_to_qiskit_vec(
small_vec, norb=norb, nelec=nelec
)

statevec = Statevector(big_vec).evolve(gate)
result = ffsim.qiskit.qiskit_vec_to_ffsim_vec(
np.array(statevec), norb=norb, nelec=nelec
)

expected = ffsim.apply_unitary(
small_vec, givens_ansatz_op, norb=norb, nelec=nelec
)

np.testing.assert_allclose(result, expected)


@pytest.mark.parametrize("norb, nocc", ffsim.testing.generate_norb_nocc(range(5)))
def test_random_givens_ansatz_operator_spinless(norb: int, nocc: int):
"""Test random spinless Givens rotation ansatz gives correct output state."""
rng = np.random.default_rng()
nelec = (nocc, 0)
dim = ffsim.dim(norb, nelec)
for _ in range(3):
n_layers = norb
n_layers = 2 * norb
interaction_pairs = list(brickwork(norb, n_layers))
thetas = rng.uniform(-np.pi, np.pi, size=len(interaction_pairs))
givens_ansatz_op = ffsim.GivensAnsatzOperator(norb, interaction_pairs, thetas)
Expand Down

0 comments on commit 2cd1c55

Please sign in to comment.