# Path: openfermioncirq/gates/fermionic_simulation.py
# def sum_of_interaction_operator_gate_generators(
#         n_modes: int,
#         gates: Dict[Tuple[int, ...], Union[float, cirq.Gate]],
# ) -> openfermion.InteractionOperator:
#     """The interaction operator that is the sum of the generators of the
#     specified fermionic simulation gates.
# 
#     The gates are specified as a dictionary whose items are (indices, gate),
#     where
#         * indices is a tuple of ints specifying the modes on which the gate
#           acts;
#         * gate is one of type
#           - float, which is interpreted as a constant, regardless of the
#             indices,
#           - cirq.ZPowGate, which is interpreted as a "linear" fermionic
#             simulation gate,
#           - openfermioncirq.InteractionOperatorFermionicGate.
# 
#     Args:
#         n_modes: The number of modes.
#         gates: The gates.
# 
#     Returns:
#         The interaction operator.
#     """
#     # assumes gate indices in JW order
#     operator = openfermion.InteractionOperator.zero(n_modes)
# 
#     for indices, gate in gates.items():
#         if not indices:
#             operator.constant += gate
#         elif isinstance(gate, cirq.ZPowGate):
#             coeff = gate._exponent * np.pi
#             operator.constant += gate._exponent * gate._global_shift * np.pi
#             operator.one_body_tensor[indices * 2] += coeff
#         elif isinstance(gate, InteractionOperatorFermionicGate):
#             gate.interaction_operator_generator(operator=operator,
#                                                 modes=indices)
#         else:
#             raise TypeError(f'Gate type {gate} not supported.')
# 
#     return operator
# 
# def state_swap_eigen_component(x: str, y: str, sign: int = 1, angle: float = 0):
#     """The +/- eigen-component of the operation that swaps states x and y.
# 
#     For example, state_swap_eigen_component('01', '10', ±1) with angle θ returns
#         ┌                               ┐
#         │0, 0,           0,            0│
#         │0, 0.5,         ±0.5 e^{-iθ}, 0│
#         │0, ±0.5 e^{iθ}, 0.5,          0│
#         │0, 0,           0,            0│
#         └                               ┘
# 
#     Args:
#         x: The first state to swap, as a bitstring.
#         y: The second state to swap, as a bitstring. Must have high index than
#             x.
#         sign: The sign of the off-diagonal elements (indicated by +/-1).
#         angle: The phase of the complex off-diagonal elements. Defaults to 0.
# 
#     Returns: The eigen-component.
# 
#     Raises:
#         ValueError:
#             * x and y have different lengths
#             * x or y contains a character other than '0' and '1'
#             * x and y are the same
#             * sign is not -1 or 1
#         TypeError: x or y is not a string
#     """
#     if not (isinstance(x, str) and isinstance(y, str)):
#         raise TypeError('not (isinstance(x, str) and isinstance(y, str))')
#     if len(x) != len(y):
#         raise ValueError('len(x) != len(y)')
#     if set(x).union(y).difference('01'):
#         raise ValueError('Arguments must be 0-1 strings.')
#     if x == y:
#         raise ValueError('x == y')
#     if sign not in (-1, 1):
#         raise ValueError('sign not in (-1, 1)')
# 
#     dim = 2**len(x)
#     i, j = int(x, 2), int(y, 2)
# 
#     component = np.zeros((dim, dim), dtype=np.complex128)
#     component[i, i] = component[j, j] = 0.5
#     component[j, i] = sign * 0.5 * 1j**(angle * 2 / np.pi)
#     component[i, j] = sign * 0.5 * 1j**(-angle * 2 / np.pi)
#     return component


import itertools
import cirq
import cirq.contrib.acquaintance as cca
import numpy as np
import openfermion
import pytest
import scipy.linalg as la
import sympy
import openfermioncirq as ofc
from typing import cast, Tuple
from openfermioncirq.gates.fermionic_simulation import (
    sum_of_interaction_operator_gate_generators,
    state_swap_eigen_component,
)
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.





def test_state_swap_eigen_component_args():
    with pytest.raises(TypeError):
        state_swap_eigen_component(0, '12', 1)
    with pytest.raises(ValueError):
        state_swap_eigen_component('01', '01', 1)
    with pytest.raises(ValueError):
        state_swap_eigen_component('01', '10', 0)
    with pytest.raises(ValueError):
        state_swap_eigen_component('01', '100', 1)
    with pytest.raises(ValueError):

# Path: openfermioncirq/gates/fermionic_simulation.py
# def sum_of_interaction_operator_gate_generators(
#         n_modes: int,
#         gates: Dict[Tuple[int, ...], Union[float, cirq.Gate]],
# ) -> openfermion.InteractionOperator:
#     """The interaction operator that is the sum of the generators of the
#     specified fermionic simulation gates.
# 
#     The gates are specified as a dictionary whose items are (indices, gate),
#     where
#         * indices is a tuple of ints specifying the modes on which the gate
#           acts;
#         * gate is one of type
#           - float, which is interpreted as a constant, regardless of the
#             indices,
#           - cirq.ZPowGate, which is interpreted as a "linear" fermionic
#             simulation gate,
#           - openfermioncirq.InteractionOperatorFermionicGate.
# 
#     Args:
#         n_modes: The number of modes.
#         gates: The gates.
# 
#     Returns:
#         The interaction operator.
#     """
#     # assumes gate indices in JW order
#     operator = openfermion.InteractionOperator.zero(n_modes)
# 
#     for indices, gate in gates.items():
#         if not indices:
#             operator.constant += gate
#         elif isinstance(gate, cirq.ZPowGate):
#             coeff = gate._exponent * np.pi
#             operator.constant += gate._exponent * gate._global_shift * np.pi
#             operator.one_body_tensor[indices * 2] += coeff
#         elif isinstance(gate, InteractionOperatorFermionicGate):
#             gate.interaction_operator_generator(operator=operator,
#                                                 modes=indices)
#         else:
#             raise TypeError(f'Gate type {gate} not supported.')
# 
#     return operator
# 
# def state_swap_eigen_component(x: str, y: str, sign: int = 1, angle: float = 0):
#     """The +/- eigen-component of the operation that swaps states x and y.
# 
#     For example, state_swap_eigen_component('01', '10', ±1) with angle θ returns
#         ┌                               ┐
#         │0, 0,           0,            0│
#         │0, 0.5,         ±0.5 e^{-iθ}, 0│
#         │0, ±0.5 e^{iθ}, 0.5,          0│
#         │0, 0,           0,            0│
#         └                               ┘
# 
#     Args:
#         x: The first state to swap, as a bitstring.
#         y: The second state to swap, as a bitstring. Must have high index than
#             x.
#         sign: The sign of the off-diagonal elements (indicated by +/-1).
#         angle: The phase of the complex off-diagonal elements. Defaults to 0.
# 
#     Returns: The eigen-component.
# 
#     Raises:
#         ValueError:
#             * x and y have different lengths
#             * x or y contains a character other than '0' and '1'
#             * x and y are the same
#             * sign is not -1 or 1
#         TypeError: x or y is not a string
#     """
#     if not (isinstance(x, str) and isinstance(y, str)):
#         raise TypeError('not (isinstance(x, str) and isinstance(y, str))')
#     if len(x) != len(y):
#         raise ValueError('len(x) != len(y)')
#     if set(x).union(y).difference('01'):
#         raise ValueError('Arguments must be 0-1 strings.')
#     if x == y:
#         raise ValueError('x == y')
#     if sign not in (-1, 1):
#         raise ValueError('sign not in (-1, 1)')
# 
#     dim = 2**len(x)
#     i, j = int(x, 2), int(y, 2)
# 
#     component = np.zeros((dim, dim), dtype=np.complex128)
#     component[i, i] = component[j, j] = 0.5
#     component[j, i] = sign * 0.5 * 1j**(angle * 2 / np.pi)
#     component[i, j] = sign * 0.5 * 1j**(-angle * 2 / np.pi)
#     return component


# Path: openfermioncirq/gates/fermionic_simulation_test.py
import itertools
import cirq
import cirq.contrib.acquaintance as cca
import numpy as np
import openfermion
import pytest
import scipy.linalg as la
import sympy
import openfermioncirq as ofc
from typing import cast, Tuple
from openfermioncirq.gates.fermionic_simulation import (
    sum_of_interaction_operator_gate_generators,
    state_swap_eigen_component,
)

#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.





def test_state_swap_eigen_component_args():
    with pytest.raises(TypeError):
        state_swap_eigen_component(0, '12', 1)
    with pytest.raises(ValueError):
        state_swap_eigen_component('01', '01', 1)
    with pytest.raises(ValueError):
        state_swap_eigen_component('01', '10', 0)
    with pytest.raises(ValueError):
        state_swap_eigen_component('01', '100', 1)
    with pytest.raises(ValueError):

        state_swap_eigen_component('01', 'ab', 1)