Source code for qiskit_cold_atom.applications.fermionic_evolution_problem

# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# 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.

"""Class that holds a fermionic time-evolution problem."""

from typing import List, Union

from qiskit import QuantumCircuit
from qiskit_nature.operators.second_quantization import FermionicOp

from qiskit_cold_atom.fermions.fermionic_state import FermionicState
from qiskit_cold_atom.fermions.fermionic_basis import FermionicBasis
from qiskit_cold_atom.fermions.fermion_gate_library import FermionicGate
from qiskit_cold_atom.exceptions import QiskitColdAtomError
from qiskit_cold_atom.applications.fermi_hubbard import FermionicLattice


[docs]class FermionicEvolutionProblem: """ Problem class corresponding to evaluating an observable of a fermionic system after a time evolution under a hamiltonian from an initial state in an occupation number representation. """ def __init__( self, system: FermionicLattice, initial_state: FermionicState, evolution_times: Union[float, List[float]], observable: FermionicOp, ): """ Initialize a fermionic time evolution problem. Args: system: The fermionic system under which the initial state will evolve. initial_state: The fermionic state at time t=0. evolution_times: List of times (or single time) after which the observable is measured. observable: The observable to measure after the time evolution, given as a FermionicOp. The observable must be diagonal in the fermionic occupation number basis. Raises: QiskitColdAtomError: - If the sizes of the system, initial state and the observable do not match. - If the observables is not diagonal in the fermionic occupation number basis """ if system.size != initial_state.sites: raise QiskitColdAtomError( f"The size of the system {system.size} does not match " f"the size of the initial state {initial_state.sites}." ) if 2 * system.size != observable.register_length: raise QiskitColdAtomError( f"The fermionic modes of the system {2*system.size} do not match " f"the size of the observable {observable.register_length}." ) # check if matrix is diagonal # can later be replaced when the FermionicOp from qiskit-nature has its own .to_matrix() method basis = FermionicBasis.from_fermionic_op(observable) observable_mat = FermionicGate.operator_to_mat(observable, num_species=1, basis=basis) if list(observable_mat.nonzero()[0]) != list(observable_mat.nonzero()[1]): raise QiskitColdAtomError( "The fermionic observable needs to be diagonal in the computational basis, " "as measuring general, non-diagonal observables is not yet implemented for " "fermionic backends. This requires non-trivial basis transformations that " "are in general difficult to find and depend on the backend's native gate set." ) self._system = system self._initial_state = initial_state self._evolution_times = evolution_times self._observable = observable @property def system(self) -> FermionicLattice: """Return the system of the problem.""" return self._system @property def initial_state(self) -> FermionicState: """Return the initial state of the system.""" return self._initial_state @property def evolution_times(self) -> List[float]: """Return the evolution times to simulate.""" return self._evolution_times @property def observable(self) -> FermionicOp: """Return the observable as a FermionicOp.""" return self._observable
[docs] def circuits(self, initial_state: QuantumCircuit) -> List[QuantumCircuit]: """ The problem embedded in a quantum circuit. Args: initial_state: A quantum circuit which corresponds to the initial state for the time-evolution problem. Return: A list of quantum circuits. Circuit :math:`i` is a single instruction which corresponds to :math:`exp(-i*H*t_i)` where :math:`t_i` is the time of the the ith evolution time. """ circuits = [] for time in self.evolution_times: circ = QuantumCircuit(initial_state.num_qubits) circ.compose(initial_state, inplace=True) circ.compose(self.system.to_circuit(time), inplace=True) circuits.append(circ) return circuits