Skip to content

Commit

Permalink
add spinless givens ansatz gate
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinsung committed May 14, 2024
1 parent 2264f0b commit f15f85e
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 0 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,
GivensAnsatzOperatorSpinlessJW,
OrbitalRotationJW,
OrbitalRotationSpinlessJW,
PrepareHartreeFockJW,
Expand All @@ -37,6 +38,7 @@
__all__ = [
"DiagCoulombEvolutionJW",
"DropNegligible",
"GivensAnsatzOperatorSpinlessJW",
"MergeOrbitalRotations",
"OrbitalRotationJW",
"OrbitalRotationSpinlessJW",
Expand Down
2 changes: 2 additions & 0 deletions python/ffsim/qiskit/gates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"""Qiskit fermionic quantum gates."""

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

__all__ = [
"DiagCoulombEvolutionJW",
"GivensAnsatzOperatorSpinlessJW",
"OrbitalRotationJW",
"OrbitalRotationSpinlessJW",
"PrepareHartreeFockJW",
Expand Down
68 changes: 68 additions & 0 deletions python/ffsim/qiskit/gates/givens_ansatz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# (C) Copyright IBM 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Givens rotation ansatz gate."""

from __future__ import annotations

import itertools
import math
from collections.abc import Iterator, Sequence

from qiskit.circuit import (
CircuitInstruction,
Gate,
QuantumCircuit,
QuantumRegister,
Qubit,
)
from qiskit.circuit.library import XXPlusYYGate

from ffsim.variational import GivensAnsatzOperator


class GivensAnsatzOperatorSpinlessJW(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", givens_ansatz_operator.norb, [], label=label
)

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
)


def _givens_ansatz_jw(
qubits: Sequence[Qubit], givens_ansatz_operator: GivensAnsatzOperator
) -> Iterator[CircuitInstruction]:
for (i, j), theta in zip(
itertools.cycle(givens_ansatz_operator.interaction_pairs),
givens_ansatz_operator.thetas,
):
yield CircuitInstruction(
XXPlusYYGate(2 * theta, -0.5 * math.pi), (qubits[i], qubits[j])
)
1 change: 1 addition & 0 deletions python/ffsim/variational/givens.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class GivensAnsatzOperator:
norb: int
interaction_pairs: list[tuple[int, int]]
thetas: np.ndarray
# TODO add phis for complex phases

def _apply_unitary_(
self, vec: np.ndarray, norb: int, nelec: tuple[int, int], copy: bool
Expand Down
55 changes: 55 additions & 0 deletions tests/python/qiskit/givens_ansatz_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# (C) Copyright IBM 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Tests for Givens rotation ansatz gate."""

from __future__ import annotations

import numpy as np
import pytest
from qiskit.quantum_info import Statevector

import ffsim


def brickwork(norb: int, n_layers: int):
for i in range(n_layers):
for j in range(i % 2, norb - 1, 2):
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):
"""Test random 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
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.GivensAnsatzOperatorSpinlessJW(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)

0 comments on commit f15f85e

Please sign in to comment.