Ejemplo n.º 1
0
    def trotter_step(self,
                     qubits: Sequence[cirq.Qid],
                     time: float,
                     control_qubit: Optional[cirq.Qid] = None) -> cirq.OP_TREE:

        n_qubits = len(qubits)

        if not isinstance(control_qubit, cirq.Qid):
            raise TypeError('Control qudit must be specified.')

        # Simulate the two-body terms for the full time
        def two_body_interaction(p, q, a, b) -> cirq.OP_TREE:
            yield rot111(-2 * self.hamiltonian.two_body[p, q] * time).on(
                cast(cirq.Qid, control_qubit), a, b)

        yield swap_network(qubits, two_body_interaction)
        # The qubit ordering has been reversed
        qubits = qubits[::-1]

        # Rotate to the basis in which the one-body term is diagonal
        yield cirq.inverse(
            bogoliubov_transform(qubits, self.basis_change_matrix))

        # Simulate the one-body terms for the full time
        yield (rot11(rads=-self.orbital_energies[i] * time).on(
            control_qubit, qubits[i]) for i in range(n_qubits))

        # Rotate back to the computational basis
        yield bogoliubov_transform(qubits, self.basis_change_matrix)

        # Apply phase from constant term
        yield cirq.rz(rads=-self.hamiltonian.constant * time).on(control_qubit)
Ejemplo n.º 2
0
def test_bogoliubov_transform_quadratic_hamiltonian_inverse_is_dagger(
        n_qubits, real, particle_conserving, atol):
    quad_ham = random_quadratic_hamiltonian(
        n_qubits,
        real=real,
        conserves_particle_number=particle_conserving,
        seed=46533)
    _, transformation_matrix, _ = quad_ham.diagonalizing_bogoliubov_transform()

    qubits = cirq.LineQubit.range(n_qubits)

    if transformation_matrix.shape == (n_qubits, n_qubits):
        daggered_transformation_matrix = transformation_matrix.T.conj()
    else:
        left_block = transformation_matrix[:, :n_qubits]
        right_block = transformation_matrix[:, n_qubits:]
        daggered_transformation_matrix = numpy.block(
            [left_block.T.conj(), right_block.T])

    circuit1 = cirq.Circuit(
        cirq.inverse(bogoliubov_transform(qubits, transformation_matrix)))

    circuit2 = cirq.Circuit(
        bogoliubov_transform(qubits, daggered_transformation_matrix))

    cirq.testing.assert_allclose_up_to_global_phase(circuit1.unitary(),
                                                    circuit2.unitary(),
                                                    atol=atol)
Ejemplo n.º 3
0
    def trotter_step(
            self,
            qubits: Sequence[cirq.QubitId],
            time: float,
            control_qubit: Optional[cirq.QubitId] = None) -> cirq.OP_TREE:

        n_qubits = len(qubits)

        # Simulate the one-body terms for half of the full time
        yield (cirq.Rz(rads=-0.5 * self.orbital_energies[i] * time).on(
            qubits[i]) for i in range(n_qubits))

        # Rotate to the computational basis
        yield bogoliubov_transform(qubits, self.basis_change_matrix)

        # Simulate the two-body terms for the full time
        def two_body_interaction(p, q, a, b) -> cirq.OP_TREE:
            yield rot11(rads=-2 * self.hamiltonian.two_body[p, q] * time).on(
                a, b)

        yield swap_network(qubits, two_body_interaction)
        # The qubit ordering has been reversed
        qubits = qubits[::-1]

        # Rotate back to the basis in which the one-body term is diagonal
        yield cirq.inverse(
            bogoliubov_transform(qubits, self.basis_change_matrix))

        # Simulate the one-body terms for half of the full time
        yield (cirq.Rz(rads=-0.5 * self.orbital_energies[i] * time).on(
            qubits[i]) for i in range(n_qubits))
Ejemplo n.º 4
0
    def trotter_step(
            self,
            qubits: Sequence[cirq.Qid],
            time: float,
            control_qubit: Optional[cirq.Qid]=None
            ) -> cirq.OP_TREE:

        if not isinstance(control_qubit, cirq.Qid):
            raise NotImplementedError('Control qudit must be specified.')
        n_qubits = len(qubits)

        # Change to the basis in which the one-body term is diagonal
        yield bogoliubov_transform(
                qubits, self.one_body_basis_change_matrix.T.conj())

        # Simulate the one-body terms.
        for p in range(n_qubits):
            yield rot11(rads=
                    -self.one_body_energies[p] * time
                    ).on(control_qubit, qubits[p])

        # Simulate each singular vector of the two-body terms.
        prior_basis_matrix = self.one_body_basis_change_matrix

        for j in range(len(self.eigenvalues)):

            # Get the two-body coefficients and basis change matrix.
            two_body_coefficients = self.scaled_density_density_matrices[j]
            basis_change_matrix = self.basis_change_matrices[j]

            # Merge previous basis change matrix with the inverse of the
            # current one
            merged_basis_change_matrix = numpy.dot(prior_basis_matrix,
                                                   basis_change_matrix.T.conj())
            yield bogoliubov_transform(qubits, merged_basis_change_matrix)

            # Simulate the off-diagonal two-body terms.
            yield swap_network(
                    qubits,
                    lambda p, q, a, b: rot111(
                        -2 * two_body_coefficients[p, q] * time).on(
                            control_qubit, a, b))
            qubits = qubits[::-1]

            # Simulate the diagonal two-body terms.
            yield (rot11(rads=
                       -two_body_coefficients[k, k] * time).on(
                           control_qubit, qubits[k])
                   for k in range(n_qubits))

            # Update prior basis change matrix.
            prior_basis_matrix = basis_change_matrix

        # Undo final basis transformation.
        yield bogoliubov_transform(qubits, prior_basis_matrix)

        # Apply phase from constant term
        yield cirq.rz(rads=
                -self.hamiltonian.constant * time).on(control_qubit)
Ejemplo n.º 5
0
def test_bogoliubov_transform_quadratic_hamiltonian(n_qubits,
                                                    conserves_particle_number,
                                                    atol=5e-5):
    qubits = LineQubit.range(n_qubits)

    # Initialize a random quadratic Hamiltonian
    quad_ham = random_quadratic_hamiltonian(n_qubits,
                                            conserves_particle_number,
                                            real=False)
    quad_ham_sparse = get_sparse_operator(quad_ham)

    # Compute the orbital energies and circuit
    orbital_energies, constant = quad_ham.orbital_energies()
    transformation_matrix = quad_ham.diagonalizing_bogoliubov_transform()
    circuit = cirq.Circuit.from_ops(
        bogoliubov_transform(qubits, transformation_matrix))

    # Pick some random eigenstates to prepare, which correspond to random
    # subsets of [0 ... n_qubits - 1]
    n_eigenstates = min(2**n_qubits, 5)
    subsets = [
        numpy.random.choice(range(n_qubits),
                            numpy.random.randint(1, n_qubits + 1), False)
        for _ in range(n_eigenstates)
    ]
    # Also test empty subset
    subsets += [()]

    for occupied_orbitals in subsets:
        # Compute the energy of this eigenstate
        energy = (sum(orbital_energies[i]
                      for i in occupied_orbitals) + constant)

        # Construct initial state
        initial_state = sum(2**(n_qubits - 1 - int(i))
                            for i in occupied_orbitals)

        # Get the state using a circuit simulation
        state1 = circuit.apply_unitary_effect_to_state(initial_state)

        # Also test the option to start with a computational basis state
        special_circuit = cirq.Circuit.from_ops(
            bogoliubov_transform(qubits,
                                 transformation_matrix,
                                 initial_state=initial_state))
        state2 = special_circuit.apply_unitary_effect_to_state(
            initial_state, qubits_that_should_be_present=qubits)

        # Check that the result is an eigenstate with the correct eigenvalue
        numpy.testing.assert_allclose(quad_ham_sparse.dot(state1),
                                      energy * state1,
                                      atol=atol)
        numpy.testing.assert_allclose(quad_ham_sparse.dot(state2),
                                      energy * state2,
                                      atol=atol)
Ejemplo n.º 6
0
def test_bogoliubov_transform_fourier_transform_inverse_is_dagger(
        n_qubits, atol):
    u = fourier_transform_matrix(n_qubits)

    qubits = cirq.LineQubit.range(n_qubits)

    circuit1 = cirq.Circuit(cirq.inverse(bogoliubov_transform(qubits, u)))

    circuit2 = cirq.Circuit(bogoliubov_transform(qubits, u.T.conj()))

    cirq.testing.assert_allclose_up_to_global_phase(
        circuit1.to_unitary_matrix(), circuit2.to_unitary_matrix(), atol=atol)
Ejemplo n.º 7
0
    def operations(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE:
        """Produce the operations of the ansatz circuit."""

        n_qubits = len(qubits)
        param_set = set(self.params())

        for i in range(self.iterations):

            # Change to the basis in which the one-body term is diagonal
            yield bogoliubov_transform(
                    qubits, self.one_body_basis_change_matrix.T.conj())

            # Simulate the one-body terms.
            for p in range(n_qubits):
                u_symbol = LetterWithSubscripts('U', p, i)
                if u_symbol in param_set:
                    yield cirq.ZPowGate(exponent=u_symbol).on(qubits[p])

            # Simulate each singular vector of the two-body terms.
            prior_basis_matrix = self.one_body_basis_change_matrix

            for j in range(len(self.eigenvalues)):

                # Get the basis change matrix.
                basis_change_matrix = self.basis_change_matrices[j]

                # Merge previous basis change matrix with the inverse of the
                # current one
                merged_basis_change_matrix = numpy.dot(
                        prior_basis_matrix,
                        basis_change_matrix.T.conj())
                yield bogoliubov_transform(qubits, merged_basis_change_matrix)

                # Simulate the off-diagonal two-body terms.
                def two_body_interaction(p, q, a, b) -> cirq.OP_TREE:
                    v_symbol = LetterWithSubscripts('V', p, q, j, i)
                    if v_symbol in param_set:
                        yield cirq.CZPowGate(exponent=v_symbol).on(a, b)
                yield swap_network(qubits, two_body_interaction)
                qubits = qubits[::-1]

                # Simulate the diagonal two-body terms.
                for p in range(n_qubits):
                    u_symbol = LetterWithSubscripts('U', p, j, i)
                    if u_symbol in param_set:
                        yield cirq.ZPowGate(exponent=u_symbol).on(qubits[p])

                # Update prior basis change matrix.
                prior_basis_matrix = basis_change_matrix

            # Undo final basis transformation.
            yield bogoliubov_transform(qubits, prior_basis_matrix)
Ejemplo n.º 8
0
    def trotter_step(
            self,
            qubits: Sequence[cirq.Qid],
            time: float,
            control_qubit: Optional[cirq.Qid]=None
            ) -> cirq.OP_TREE:

        n_qubits = len(qubits)

        # Change to the basis in which the one-body term is diagonal
        yield bogoliubov_transform(
                qubits, self.one_body_basis_change_matrix.T.conj())

        # Simulate the one-body terms.
        for p in range(n_qubits):
            yield cirq.Rz(rads=
                    -self.one_body_energies[p] * time
                    ).on(qubits[p])

        # Simulate each singular vector of the two-body terms.
        prior_basis_matrix = self.one_body_basis_change_matrix

        for j in range(len(self.eigenvalues)):

            # Get the two-body coefficients and basis change matrix.
            two_body_coefficients = self.scaled_density_density_matrices[j]
            basis_change_matrix = self.basis_change_matrices[j]

            # Merge previous basis change matrix with the inverse of the
            # current one
            merged_basis_change_matrix = numpy.dot(prior_basis_matrix,
                                                   basis_change_matrix.T.conj())
            yield bogoliubov_transform(qubits, merged_basis_change_matrix)

            # Simulate the off-diagonal two-body terms.
            yield swap_network(
                    qubits,
                    lambda p, q, a, b: rot11(rads=
                        -2 * two_body_coefficients[p, q] * time).on(a, b))
            qubits = qubits[::-1]

            # Simulate the diagonal two-body terms.
            for p in range(n_qubits):
                yield cirq.Rz(rads=
                        -two_body_coefficients[p, p] * time
                        ).on(qubits[p])

            # Update prior basis change matrix
            prior_basis_matrix = basis_change_matrix

        # Undo final basis transformation
        yield bogoliubov_transform(qubits, prior_basis_matrix)
Ejemplo n.º 9
0
def test_bogoliubov_transform_compose(n_qubits, atol):
    u = random_unitary_matrix(n_qubits, seed=24964)
    v = random_unitary_matrix(n_qubits, seed=33656)

    qubits = cirq.LineQubit.range(n_qubits)

    circuit1 = cirq.Circuit(bogoliubov_transform(qubits, u),
                            bogoliubov_transform(qubits, v))

    circuit2 = cirq.Circuit(bogoliubov_transform(qubits, u.dot(v)))

    cirq.testing.assert_allclose_up_to_global_phase(
        circuit1.to_unitary_matrix(), circuit2.to_unitary_matrix(), atol=atol)
Ejemplo n.º 10
0
 def finish(self,
            qubits: Sequence[cirq.QubitId],
            n_steps: int,
            control_qubit: Optional[cirq.QubitId] = None,
            omit_final_swaps: bool = False) -> cirq.OP_TREE:
     # Rotate back to the computational basis
     yield bogoliubov_transform(qubits, self.basis_change_matrix)
     # If the number of Trotter steps is odd, possibly swap qubits back
     if n_steps & 1 and not omit_final_swaps:
         yield swap_network(qubits)
    def operations(self, qubits: Sequence[cirq.QubitId]) -> cirq.OP_TREE:
        """Produce the operations of the ansatz circuit."""
        # TODO implement asymmetric ansatz

        # Change to the basis in which the one-body term is diagonal
        yield cirq.inverse(
            bogoliubov_transform(qubits, self.basis_change_matrix))

        for i in range(self.iterations):

            suffix = '-{}'.format(i) if self.iterations > 1 else ''

            # Simulate one-body terms
            yield (cirq.RotZGate(half_turns=self.params['U{}'.format(p) +
                                                        suffix]).on(qubits[p])
                   for p in range(len(qubits))
                   if 'U{}'.format(p) + suffix in self.params)

            # Rotate to the computational basis
            yield bogoliubov_transform(qubits, self.basis_change_matrix)

            # Simulate the two-body terms
            def two_body_interaction(p, q, a, b) -> cirq.OP_TREE:
                if 'V{}_{}'.format(p, q) + suffix in self.params:
                    yield cirq.Rot11Gate(
                        half_turns=self.params['V{}_{}'.format(p, q) +
                                               suffix]).on(a, b)

            yield swap_network(qubits, two_body_interaction)
            qubits = qubits[::-1]

            # Rotate back to the basis in which the one-body term is diagonal
            yield cirq.inverse(
                bogoliubov_transform(qubits, self.basis_change_matrix))

            # Simulate one-body terms again
            yield (cirq.RotZGate(half_turns=self.params['U{}'.format(p) +
                                                        suffix]).on(qubits[p])
                   for p in range(len(qubits))
                   if 'U{}'.format(p) + suffix in self.params)

        # Rotate to the computational basis
        yield bogoliubov_transform(qubits, self.basis_change_matrix)
    def operations(self, qubits: Sequence[cirq.QubitId]) -> cirq.OP_TREE:
        """Produce the operations of the ansatz circuit."""
        # TODO implement asymmetric ansatz

        param_set = set(self.params())

        # Change to the basis in which the one-body term is diagonal
        yield cirq.inverse(
            bogoliubov_transform(qubits, self.basis_change_matrix))

        for i in range(self.iterations):

            # Simulate one-body terms
            for p in range(len(qubits)):
                u_symbol = LetterWithSubscripts('U', p, i)
                if u_symbol in param_set:
                    yield cirq.RotZGate(half_turns=u_symbol).on(qubits[p])

            # Rotate to the computational basis
            yield bogoliubov_transform(qubits, self.basis_change_matrix)

            # Simulate the two-body terms
            def two_body_interaction(p, q, a, b) -> cirq.OP_TREE:
                v_symbol = LetterWithSubscripts('V', p, q, i)
                if v_symbol in param_set:
                    yield cirq.Rot11Gate(half_turns=v_symbol).on(a, b)

            yield swap_network(qubits, two_body_interaction)
            qubits = qubits[::-1]

            # Rotate back to the basis in which the one-body term is diagonal
            yield cirq.inverse(
                bogoliubov_transform(qubits, self.basis_change_matrix))

            # Simulate one-body terms again
            for p in range(len(qubits)):
                u_symbol = LetterWithSubscripts('U', p, i)
                if u_symbol in param_set:
                    yield cirq.RotZGate(half_turns=u_symbol).on(qubits[p])

        # Rotate to the computational basis
        yield bogoliubov_transform(qubits, self.basis_change_matrix)
Ejemplo n.º 13
0
def test_spin_symmetric_bogoliubov_transform(
        n_spatial_orbitals,
        conserves_particle_number,
        atol=5e-5):
    n_qubits = 2*n_spatial_orbitals
    qubits = LineQubit.range(n_qubits)

    # Initialize a random quadratic Hamiltonian
    quad_ham = random_quadratic_hamiltonian(
            n_spatial_orbitals,
            conserves_particle_number,
            real=True,
            expand_spin=True,
            seed=28166)

    # Reorder the Hamiltonian and get sparse matrix
    quad_ham = openfermion.get_quadratic_hamiltonian(
            openfermion.reorder(
                openfermion.get_fermion_operator(quad_ham),
                openfermion.up_then_down)
    )
    quad_ham_sparse = get_sparse_operator(quad_ham)

    # Compute the orbital energies and transformation_matrix
    up_orbital_energies, _, _ = (
            quad_ham.diagonalizing_bogoliubov_transform(spin_sector=0))
    down_orbital_energies, _, _ = (
            quad_ham.diagonalizing_bogoliubov_transform(spin_sector=1))
    _, transformation_matrix, _ = (
            quad_ham.diagonalizing_bogoliubov_transform())

    # Pick some orbitals to occupy
    up_orbitals = list(range(2))
    down_orbitals = [0, 2, 3]
    energy = sum(up_orbital_energies[up_orbitals]) + sum(
            down_orbital_energies[down_orbitals]) + quad_ham.constant

    # Construct initial state
    initial_state = (
            sum(2**(n_qubits - 1 - int(i)) for i in up_orbitals)
            + sum(2**(n_qubits - 1 - int(i+n_spatial_orbitals))
                  for i in down_orbitals)
    )

    # Apply the circuit
    circuit = cirq.Circuit.from_ops(
            bogoliubov_transform(
                qubits, transformation_matrix, initial_state=initial_state))
    state = circuit.apply_unitary_effect_to_state(initial_state)

    # Check that the result is an eigenstate with the correct eigenvalue
    numpy.testing.assert_allclose(
            quad_ham_sparse.dot(state), energy * state, atol=atol)
Ejemplo n.º 14
0
def test_bogoliubov_transform_fourier_transform(transformation_matrix,
                                                initial_state,
                                                correct_state,
                                                atol=5e-6):
    n_qubits = transformation_matrix.shape[0]
    qubits = LineQubit.range(n_qubits)
    if isinstance(initial_state, Container):
        initial_state = sum(1 << (n_qubits - 1 - i) for i in initial_state)

    circuit = cirq.Circuit.from_ops(bogoliubov_transform(
        qubits, transformation_matrix, initial_state=initial_state))
    state = circuit.apply_unitary_effect_to_state(initial_state)

    cirq.testing.assert_allclose_up_to_global_phase(
            state, correct_state, atol=atol)
Ejemplo n.º 15
0
def _create_quadratic_hamiltonian_circuit(
        qubits: List[cirq.Qid], particles: int,
        hamiltonian: openfermion.QuadraticHamiltonian) -> cirq.Circuit:
    """Creates circuit that realizes network of givens rotation."""

    circuit = cirq.Circuit()

    # Prepare ground state in diagonal basis and add initial circuits.
    circuit.append([cirq.X.on(q) for q in qubits[:particles]])

    # Create Givens network circuit.
    _, transform, _ = hamiltonian.diagonalizing_bogoliubov_transform()
    circuit += cirq.Circuit(
        openfermioncirq.bogoliubov_transform(qubits, transform,
                                             range(particles)))

    return circuit
Ejemplo n.º 16
0
def test_ffft_equal_to_bogoliubov(size):
    def fourier_transform_matrix():
        root_of_unity = np.exp(-2j * np.pi / size)
        return np.array([[root_of_unity**(j * k) for k in range(size)]
                         for j in range(size)]) / np.sqrt(size)

    qubits = LineQubit.range(size)

    ffft_circuit = cirq.Circuit(ffft(qubits),
                                strategy=cirq.InsertStrategy.EARLIEST)
    ffft_matrix = ffft_circuit.unitary(qubits_that_should_be_present=qubits)

    bogoliubov_circuit = cirq.Circuit(bogoliubov_transform(
        qubits, fourier_transform_matrix()),
                                      strategy=cirq.InsertStrategy.EARLIEST)
    bogoliubov_matrix = bogoliubov_circuit.unitary(
        qubits_that_should_be_present=qubits)

    cirq.testing.assert_allclose_up_to_global_phase(ffft_matrix,
                                                    bogoliubov_matrix,
                                                    atol=1e-8)
def test_bogoliubov_transform_fourier_transform(transformation_matrix,
                                                initial_state,
                                                correct_state,
                                                atol=5e-6):
    simulator = cirq.google.XmonSimulator()
    n_qubits = transformation_matrix.shape[0]
    qubits = LineQubit.range(n_qubits)

    circuit = cirq.Circuit.from_ops(
        bogoliubov_transform(qubits,
                             transformation_matrix,
                             initial_state=initial_state))

    if isinstance(initial_state, Container):
        initial_state = sum(1 << (n_qubits - 1 - i) for i in initial_state)
    result = simulator.simulate(circuit, initial_state=initial_state)
    state = result.final_state

    cirq.testing.assert_allclose_up_to_global_phase(state,
                                                    correct_state,
                                                    atol=atol)
Ejemplo n.º 18
0
 def prepare(self,
             qubits: Sequence[cirq.QubitId],
             control_qubits: Optional[cirq.QubitId] = None) -> cirq.OP_TREE:
     # Change to the basis in which the one-body term is diagonal
     yield cirq.inverse(
         bogoliubov_transform(qubits, self.basis_change_matrix))
Ejemplo n.º 19
0
    for q in range(n_qubits):
        term = ((p, 1), (q, 0))
        H += openfermion.FermionOperator(term, T[p, q])
print("\nFermion operator:")
print(H)

# Diagonalize T and obtain basis transformation matrix (aka "u")
eigenvalues, eigenvectors = numpy.linalg.eigh(T)
basis_transformation_matrix = eigenvectors.transpose()

# Initialize the qubit register
qubits = cirq.LineQubit.range(n_qubits)

# Rotate to the eigenbasis
inverse_basis_rotation = cirq.inverse(
    openfermioncirq.bogoliubov_transform(qubits, basis_transformation_matrix)
)
circuit = cirq.Circuit.from_ops(inverse_basis_rotation)

# Add diagonal phase rotations to circuit
for k, eigenvalue in enumerate(eigenvalues):
    phase = -eigenvalue * simulation_time
    circuit.append(cirq.Rz(rads=phase).on(qubits[k]))

# Finally, change back to the computational basis
basis_rotation = openfermioncirq.bogoliubov_transform(
    qubits, basis_transformation_matrix
)
circuit.append(basis_rotation)

# Initialize a random initial state
Ejemplo n.º 20
0
def test_bogoliubov_transform_bad_shape_raises_error():
    with pytest.raises(ValueError):
        _ = next(
            bogoliubov_transform(cirq.LineQubit.range(4), numpy.zeros((4, 7))))
Ejemplo n.º 21
0
    def trotter_step(
            self,
            qubits: Sequence[cirq.QubitId],
            time: float,
            control_qubit: Optional[cirq.QubitId]=None
            ) -> cirq.OP_TREE:

        n_qubits = len(qubits)

        # Simulate the off-diagonal one-body terms.
        yield swap_network(
                qubits,
                lambda p, q, a, b: ControlledXXYYGate(duration=
                    self.one_body_coefficients[p, q].real * time).on(
                        control_qubit, a, b),
                fermionic=True)
        qubits = qubits[::-1]

        # Simulate the diagonal one-body terms.
        yield (cirq.Rot11Gate(rads=
                   -self.one_body_coefficients[j, j].real * time).on(
                       control_qubit, qubits[j])
               for j in range(n_qubits))

        # Simulate each singular vector of the two-body terms.
        prior_basis_matrix = numpy.identity(n_qubits, complex)
        for j in range(len(self.eigenvalues)):

            # Get the basis change matrix and its inverse.
            two_body_coefficients = self.scaled_density_density_matrices[j]
            basis_change_matrix = self.basis_change_matrices[j]

            # If the basis change matrix is unitary, merge rotations.
            # Otherwise, you must simulate two basis rotations.
            is_unitary = cirq.is_unitary(basis_change_matrix)
            if is_unitary:
                inverse_basis_matrix = numpy.conjugate(
                    numpy.transpose(basis_change_matrix))
                merged_basis_matrix = numpy.dot(prior_basis_matrix,
                                                inverse_basis_matrix)
                yield bogoliubov_transform(qubits, merged_basis_matrix)
            else:
                # TODO add LiH test to cover this.
                # coverage: ignore
                if j:
                    yield bogoliubov_transform(qubits, prior_basis_matrix)
                yield cirq.inverse(
                    bogoliubov_transform(qubits, basis_change_matrix))

            # Simulate the off-diagonal two-body terms.
            yield swap_network(
                    qubits,
                    lambda p, q, a, b: Rot111Gate(rads=
                        -2 * two_body_coefficients[p, q] * time).on(
                            control_qubit, a, b))
            qubits = qubits[::-1]

            # Simulate the diagonal two-body terms.
            yield (cirq.Rot11Gate(rads=
                       -two_body_coefficients[k, k] * time).on(
                           control_qubit, qubits[k])
                   for k in range(n_qubits))

            # Undo basis transformation non-unitary case.
            # Else, set basis change matrix to prior matrix for merging.
            if is_unitary:
                prior_basis_matrix = basis_change_matrix
            else:
                # TODO add LiH test to cover this.
                # coverage: ignore
                yield bogoliubov_transform(qubits, basis_change_matrix)
                prior_basis_matrix = numpy.identity(n_qubits, complex)

        # Undo final basis transformation in unitary case.
        if is_unitary:
            yield bogoliubov_transform(qubits, prior_basis_matrix)

        # Apply phase from constant term
        yield cirq.RotZGate(rads=-self.constant * time).on(control_qubit)