Source code for ffsim.qiskit.gates.ucj

# (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.

"""(Local) unitary cluster Jastrow ansatz gate."""

from __future__ import annotations

from collections.abc import Iterator, Sequence

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

from ffsim import variational
from ffsim.qiskit.gates.diag_coulomb import (
    DiagCoulombEvolutionJW,
    DiagCoulombEvolutionSpinlessJW,
)
from ffsim.qiskit.gates.orbital_rotation import (
    OrbitalRotationJW,
    OrbitalRotationSpinlessJW,
)


[docs] class UCJOpSpinBalancedJW(Gate): """Spin-balanced UCJ operator under the Jordan-Wigner transformation. See :class:`ffsim.UCJOpSpinBalanced` for a description of this gate's unitary. This gate assumes that qubits are ordered such that the first ``norb`` qubits correspond to the alpha orbitals and the last ``norb`` qubits correspond to the beta orbitals. """ def __init__( self, ucj_op: variational.UCJOpSpinBalanced, *, tol: float = 1e-12, label: str | None = None, ): """Create a new spin-balanced unitary cluster Jastrow (UCJ) gate. Args: ucj_op: The UCJ operator. tol: Tolerance for the Givens decomposition of the orbital rotations. Matrix entries smaller than this value will be treated as equal to zero. label: The label of the gate. """ self.ucj_op = ucj_op self.tol = tol super().__init__("ucj_balanced_jw", 2 * ucj_op.norb, [], label=label) def _define(self): """Gate decomposition.""" qubits = QuantumRegister(self.num_qubits) self.definition = QuantumCircuit.from_instructions( _ucj_op_spin_balanced_jw(qubits, self.ucj_op, tol=self.tol), qubits=qubits, name=self.name, )
def _ucj_op_spin_balanced_jw( qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinBalanced, tol: float = 1e-12 ) -> Iterator[CircuitInstruction]: for (diag_coulomb_mat_aa, diag_coulomb_mat_ab), orbital_rotation in zip( ucj_op.diag_coulomb_mats, ucj_op.orbital_rotations ): yield CircuitInstruction( OrbitalRotationJW(ucj_op.norb, orbital_rotation.T.conj(), tol=tol), qubits, ) yield CircuitInstruction( DiagCoulombEvolutionJW( ucj_op.norb, (diag_coulomb_mat_aa, diag_coulomb_mat_ab, diag_coulomb_mat_aa), -1.0, ), qubits, ) yield CircuitInstruction( OrbitalRotationJW(ucj_op.norb, orbital_rotation, tol=tol), qubits ) if ucj_op.final_orbital_rotation is not None: yield CircuitInstruction( OrbitalRotationJW(ucj_op.norb, ucj_op.final_orbital_rotation, tol=tol), qubits, )
[docs] class UCJOpSpinUnbalancedJW(Gate): """Spin-unbalanced UCJ operator under the Jordan-Wigner transformation. See :class:`ffsim.UCJOpSpinUnbalanced` for a description of this gate's unitary. This gate assumes that qubits are ordered such that the first ``norb`` qubits correspond to the alpha orbitals and the last ``norb`` qubits correspond to the beta orbitals. """ def __init__( self, ucj_op: variational.UCJOpSpinUnbalanced, *, tol: float = 1e-12, label: str | None = None, ): """Create a new spin-unbalanced unitary cluster Jastrow (UCJ) gate. Args: ucj_op: The UCJ operator. tol: Tolerance for the Givens decomposition of the orbital rotations. Matrix entries smaller than this value will be treated as equal to zero. label: The label of the gate. """ self.ucj_op = ucj_op self.tol = tol super().__init__("ucj_unbalanced_jw", 2 * ucj_op.norb, [], label=label) def _define(self): """Gate decomposition.""" qubits = QuantumRegister(self.num_qubits) self.definition = QuantumCircuit.from_instructions( _ucj_op_spin_unbalanced_jw(qubits, self.ucj_op, tol=self.tol), qubits=qubits, name=self.name, )
def _ucj_op_spin_unbalanced_jw( qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinUnbalanced, tol: float = 1e-12 ) -> Iterator[CircuitInstruction]: for diag_colomb_mat, orbital_rotation in zip( ucj_op.diag_coulomb_mats, ucj_op.orbital_rotations ): yield CircuitInstruction( OrbitalRotationJW( ucj_op.norb, orbital_rotation.transpose(0, 2, 1).conj(), tol=tol ), qubits, ) yield CircuitInstruction( DiagCoulombEvolutionJW(ucj_op.norb, diag_colomb_mat, -1.0), qubits, ) yield CircuitInstruction( OrbitalRotationJW(ucj_op.norb, orbital_rotation, tol=tol), qubits ) if ucj_op.final_orbital_rotation is not None: yield CircuitInstruction( OrbitalRotationJW(ucj_op.norb, ucj_op.final_orbital_rotation, tol=tol), qubits, )
[docs] class UCJOpSpinlessJW(Gate): """Spinless UCJ operator under the Jordan-Wigner transformation. See :class:`ffsim.UCJOpSpinless` for a description of this gate's unitary. """ def __init__( self, ucj_op: variational.UCJOpSpinless, *, tol: float = 1e-12, label: str | None = None, ): """Create a new spinless unitary cluster Jastrow (UCJ) gate. Args: ucj_op: The UCJ operator. tol: Tolerance for the Givens decomposition of the orbital rotations. Matrix entries smaller than this value will be treated as equal to zero. label: The label of the gate. """ self.ucj_op = ucj_op self.tol = tol super().__init__("ucj_spinless_jw", ucj_op.norb, [], label=label) def _define(self): """Gate decomposition.""" qubits = QuantumRegister(self.num_qubits) self.definition = QuantumCircuit.from_instructions( _ucj_op_spinless_jw(qubits, self.ucj_op, tol=self.tol), qubits=qubits, name=self.name, )
def _ucj_op_spinless_jw( qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinless, tol: float = 1e-12 ) -> Iterator[CircuitInstruction]: for diag_coulomb_mat, orbital_rotation in zip( ucj_op.diag_coulomb_mats, ucj_op.orbital_rotations ): yield CircuitInstruction( OrbitalRotationSpinlessJW(ucj_op.norb, orbital_rotation.T.conj(), tol=tol), qubits, ) yield CircuitInstruction( DiagCoulombEvolutionSpinlessJW(ucj_op.norb, diag_coulomb_mat, -1.0), qubits, ) yield CircuitInstruction( OrbitalRotationSpinlessJW(ucj_op.norb, orbital_rotation, tol=tol), qubits ) if ucj_op.final_orbital_rotation is not None: yield CircuitInstruction( OrbitalRotationSpinlessJW( ucj_op.norb, ucj_op.final_orbital_rotation, tol=tol ), qubits, )