# Path: openfermioncirq/testing/example_classes.py
# class ExampleAnsatz(VariationalAnsatz):
#     """An example variational ansatz.
# 
#     The ansatz produces the operations::
# 
#         0: ───X^theta0───@───X^theta0───M('all')───
#                          │              │
#         1: ───X^theta1───@───X^theta1───M──────────
#     """
# 
#     def params(self) -> Iterable[sympy.Symbol]:
#         for i in range(2):
#             yield sympy.Symbol('theta{}'.format(i))
# 
#     def _generate_qubits(self) -> Sequence[cirq.Qid]:
#         return cirq.LineQubit.range(2)
# 
#     def operations(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE:
#         a, b = qubits
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta0')).on(a)
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta1')).on(b)
#         yield cirq.CZ(a, b)
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta0')).on(a)
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta1')).on(b)
#         yield cirq.measure(a, b, key='all')
# 
# class ExampleVariationalObjective(VariationalObjective):
#     """An example variational objective.
# 
#     The value of the study is the number of qubits that were measured to be 1.
#     """
# 
#     def value(self,
#               circuit_output: Union[cirq.TrialResult,
#                                     cirq.SimulationTrialResult,
#                                     numpy.ndarray]
#               ) -> float:
#         if isinstance(circuit_output, numpy.ndarray):
#             return sum(bin(i).count('1') * p for i, p in
#                     enumerate(circuit_output))
#         measurements = circuit_output.measurements['all']
#         return numpy.sum(measurements)
# 
# Path: openfermioncirq/variational/variational_black_box.py
# class VariationalBlackBox(BlackBox):
#     """A black box encapsulating a variational ansatz objective function.
# 
#     Attributes:
#         ansatz: The variational ansatz circuit.
#         objective: The objective function.
#         preparation_circuit: An optional circuit used to prepare the
#             initial state
#     """
# 
#     def __init__(self,
#                  ansatz: VariationalAnsatz,
#                  objective: VariationalObjective,
#                  preparation_circuit: Optional[cirq.Circuit]=None,
#                  initial_state: Union[int, numpy.ndarray]=0,
#                  **kwargs) -> None:
#         self.ansatz = ansatz
#         self.objective = objective
#         self.preparation_circuit = preparation_circuit or cirq.Circuit()
#         self.initial_state = initial_state
#         super().__init__(**kwargs)
# 
#     @property
#     def dimension(self) -> int:
#         """The dimension of the array accepted by the objective function."""
#         return len(list(self.ansatz.params()))
# 
#     @property
#     def bounds(self) -> Optional[Sequence[Tuple[float, float]]]:
#         """Optional bounds on the inputs to the objective function."""
#         return self.ansatz.param_bounds()
# 
#     @abc.abstractmethod
#     def evaluate_noiseless(self,
#                            x: numpy.ndarray) -> float:
#         """Evaluate parameters with a noiseless simulation."""
# 
#     def _evaluate(self,
#                   x: numpy.ndarray) -> float:
#         """Determine the value of some parameters."""
#         # Default: defer to evaluate_noiseless
#         return self.evaluate_noiseless(x)
# 
#     def _evaluate_with_cost(self,
#                             x: numpy.ndarray,
#                             cost: float) -> float:
#         """Evaluate parameters with a specified cost."""
#         # Default: add artifical noise with the specified cost
#         return self._evaluate(x) + self.objective.noise(cost)
# 
#     def noise_bounds(self,
#                      cost: float,
#                      confidence: Optional[float]=None
#                      ) -> Tuple[float, float]:
#         """Exact or approximate bounds on noise in the objective function."""
#         return self.objective.noise_bounds(cost, confidence)


import pytest
from openfermioncirq.testing import ExampleAnsatz, ExampleVariationalObjective
from openfermioncirq.variational.variational_black_box import (
        VariationalBlackBox)
#
#       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_variational_black_box_is_abstract_cant_instantiate():
    with pytest.raises(TypeError):
        _ = VariationalBlackBox(ExampleAnsatz(), ExampleVariationalObjective())


def test_variational_black_box_is_abstract_must_implement():
    class Missing(VariationalBlackBox):
        pass

    with pytest.raises(TypeError):
        _ = Missing(ExampleAnsatz(), ExampleVariationalObjective())


def test_variational_black_box_is_abstract_can_implement():
    class Included(VariationalBlackBox):
        def evaluate_noiseless(self, x):
            pass


# Path: openfermioncirq/testing/example_classes.py
# class ExampleAnsatz(VariationalAnsatz):
#     """An example variational ansatz.
# 
#     The ansatz produces the operations::
# 
#         0: ───X^theta0───@───X^theta0───M('all')───
#                          │              │
#         1: ───X^theta1───@───X^theta1───M──────────
#     """
# 
#     def params(self) -> Iterable[sympy.Symbol]:
#         for i in range(2):
#             yield sympy.Symbol('theta{}'.format(i))
# 
#     def _generate_qubits(self) -> Sequence[cirq.Qid]:
#         return cirq.LineQubit.range(2)
# 
#     def operations(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE:
#         a, b = qubits
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta0')).on(a)
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta1')).on(b)
#         yield cirq.CZ(a, b)
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta0')).on(a)
#         yield cirq.XPowGate(exponent=sympy.Symbol('theta1')).on(b)
#         yield cirq.measure(a, b, key='all')
# 
# class ExampleVariationalObjective(VariationalObjective):
#     """An example variational objective.
# 
#     The value of the study is the number of qubits that were measured to be 1.
#     """
# 
#     def value(self,
#               circuit_output: Union[cirq.TrialResult,
#                                     cirq.SimulationTrialResult,
#                                     numpy.ndarray]
#               ) -> float:
#         if isinstance(circuit_output, numpy.ndarray):
#             return sum(bin(i).count('1') * p for i, p in
#                     enumerate(circuit_output))
#         measurements = circuit_output.measurements['all']
#         return numpy.sum(measurements)
# 
# Path: openfermioncirq/variational/variational_black_box.py
# class VariationalBlackBox(BlackBox):
#     """A black box encapsulating a variational ansatz objective function.
# 
#     Attributes:
#         ansatz: The variational ansatz circuit.
#         objective: The objective function.
#         preparation_circuit: An optional circuit used to prepare the
#             initial state
#     """
# 
#     def __init__(self,
#                  ansatz: VariationalAnsatz,
#                  objective: VariationalObjective,
#                  preparation_circuit: Optional[cirq.Circuit]=None,
#                  initial_state: Union[int, numpy.ndarray]=0,
#                  **kwargs) -> None:
#         self.ansatz = ansatz
#         self.objective = objective
#         self.preparation_circuit = preparation_circuit or cirq.Circuit()
#         self.initial_state = initial_state
#         super().__init__(**kwargs)
# 
#     @property
#     def dimension(self) -> int:
#         """The dimension of the array accepted by the objective function."""
#         return len(list(self.ansatz.params()))
# 
#     @property
#     def bounds(self) -> Optional[Sequence[Tuple[float, float]]]:
#         """Optional bounds on the inputs to the objective function."""
#         return self.ansatz.param_bounds()
# 
#     @abc.abstractmethod
#     def evaluate_noiseless(self,
#                            x: numpy.ndarray) -> float:
#         """Evaluate parameters with a noiseless simulation."""
# 
#     def _evaluate(self,
#                   x: numpy.ndarray) -> float:
#         """Determine the value of some parameters."""
#         # Default: defer to evaluate_noiseless
#         return self.evaluate_noiseless(x)
# 
#     def _evaluate_with_cost(self,
#                             x: numpy.ndarray,
#                             cost: float) -> float:
#         """Evaluate parameters with a specified cost."""
#         # Default: add artifical noise with the specified cost
#         return self._evaluate(x) + self.objective.noise(cost)
# 
#     def noise_bounds(self,
#                      cost: float,
#                      confidence: Optional[float]=None
#                      ) -> Tuple[float, float]:
#         """Exact or approximate bounds on noise in the objective function."""
#         return self.objective.noise_bounds(cost, confidence)


# Path: openfermioncirq/variational/variational_black_box_test.py
import pytest
from openfermioncirq.testing import ExampleAnsatz, ExampleVariationalObjective
from openfermioncirq.variational.variational_black_box import (
        VariationalBlackBox)

#
#       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_variational_black_box_is_abstract_cant_instantiate():
    with pytest.raises(TypeError):
        _ = VariationalBlackBox(ExampleAnsatz(), ExampleVariationalObjective())


def test_variational_black_box_is_abstract_must_implement():
    class Missing(VariationalBlackBox):
        pass

    with pytest.raises(TypeError):
        _ = Missing(ExampleAnsatz(), ExampleVariationalObjective())


def test_variational_black_box_is_abstract_can_implement():
    class Included(VariationalBlackBox):
        def evaluate_noiseless(self, x):
            pass


    assert isinstance(Included(ExampleAnsatz(), ExampleVariationalObjective()),