Source code for ffsim.qiskit.gates.givens_ansatz

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

import numpy as np
from qiskit.circuit import (
    CircuitInstruction,
    Gate,
    QuantumCircuit,
    QuantumRegister,
    Qubit,
)
from qiskit.circuit.library import PhaseGate, XXPlusYYGate
from typing_extensions import deprecated

from ffsim.variational import GivensAnsatzOp, GivensAnsatzOperator


[docs] @deprecated("GivensAnsatzOperatorJW is deprecated. Use GivensAnsatzOpJW instead.") class GivensAnsatzOperatorJW(Gate): """Givens rotation ansatz operator under the Jordan-Wigner transformation. .. warning:: This class is deprecated. Use :class:`ffsim.qiskit.GivensAnsatzOpJW` instead. See :class:`ffsim.GivensAnsatzOperator` for a description of this gate's unitary. """
[docs] 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_operator_jw( alpha_qubits, self.givens_ansatz_operator ): circuit.append(instruction) for instruction in _givens_ansatz_operator_jw( beta_qubits, self.givens_ansatz_operator ): circuit.append(instruction) self.definition = circuit
[docs] @deprecated( "GivensAnsatzOperatorSpinlessJW is deprecated. " "Use GivensAnsatzOpSpinlessJW instead." ) class GivensAnsatzOperatorSpinlessJW(Gate): """Givens rotation ansatz operator under the Jordan-Wigner transformation, spinless. Like :class:`GivensAnsatzOperatorJW` but only acts on a single spin species. """
[docs] 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_operator_jw(qubits, self.givens_ansatz_operator), qubits=qubits, name=self.name, )
def _givens_ansatz_operator_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]) )
[docs] class GivensAnsatzOpJW(Gate): """Givens rotation ansatz operator under the Jordan-Wigner transformation. See :class:`ffsim.GivensAnsatzOp` for a description of this gate's unitary. """
[docs] def __init__(self, givens_ansatz_op: GivensAnsatzOp, *, label: str | None = None): """Create a new Givens ansatz operator gate. Args: givens_ansatz_op: The Givens rotation ansatz operator. label: The label of the gate. """ self.givens_ansatz_op = givens_ansatz_op super().__init__("givens_ansatz_jw", 2 * givens_ansatz_op.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_op): circuit.append(instruction) for instruction in _givens_ansatz_jw(beta_qubits, self.givens_ansatz_op): circuit.append(instruction) self.definition = circuit
[docs] class GivensAnsatzOpSpinlessJW(Gate): """Spinless Givens rotation ansatz operator under the Jordan-Wigner transformation. Like :class:`GivensAnsatzOpJW` but only acts on a single spin species. """
[docs] def __init__(self, givens_ansatz_op: GivensAnsatzOp, *, label: str | None = None): """Create a new Givens ansatz operator gate. Args: givens_ansatz_op: The Givens rotation ansatz operator. label: The label of the gate. """ self.givens_ansatz_op = givens_ansatz_op super().__init__( "givens_ansatz_spinless_jw", givens_ansatz_op.norb, [], label=label )
def _define(self): """Gate decomposition.""" qubits = QuantumRegister(self.num_qubits) circuit = QuantumCircuit(qubits, name=self.name) for instruction in _givens_ansatz_jw(qubits, self.givens_ansatz_op): circuit.append(instruction) self.definition = circuit
def _givens_ansatz_jw( qubits: Sequence[Qubit], givens_ansatz_op: GivensAnsatzOp ) -> Iterator[CircuitInstruction]: phis = givens_ansatz_op.phis if phis is None: phis = np.zeros(len(givens_ansatz_op.interaction_pairs)) for (i, j), theta, phi in zip( givens_ansatz_op.interaction_pairs, givens_ansatz_op.thetas, phis ): yield CircuitInstruction( XXPlusYYGate(2 * theta, phi - 0.5 * math.pi), (qubits[i], qubits[j]) ) if givens_ansatz_op.phase_angles is not None: for i, phase_angle in enumerate(givens_ansatz_op.phase_angles): yield CircuitInstruction(PhaseGate(phase_angle), (qubits[i],))