# Path: openfermioncirq/optimization/scipy.py
# COBYLA = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'COBYLA'},
#         uses_bounds=False)
# 
# L_BFGS_B = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'L-BFGS-B'})
# 
# NELDER_MEAD = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'Nelder-Mead'},
#         uses_bounds=False)
# 
# SLSQP = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'SLSQP'})
# 
# class ScipyOptimizationAlgorithm(OptimizationAlgorithm):
#     """An optimization algorithm from the scipy.optimize module."""
# 
#     def __init__(self,
#                  options: Optional[Dict]=None,
#                  kwargs: Optional[Dict]=None,
#                  uses_bounds: bool=True) -> None:
#         """
#         Args:
#             options: The `options` dictionary passed to scipy.optimize.minimize.
#             kwargs: Other keyword arguments passed to scipy.optimize.minimize.
#                 This should NOT include the `bounds` or `options` keyword
#                 arguments.
#             uses_bounds: Whether the algorithm uses bounds on the input
#                 variables. Set this to False to prevent scipy.optimize.minimize
#                 from raising a warning if the chosen method does not use bounds.
#         """
#         self.kwargs = kwargs or {}
#         self.uses_bounds = uses_bounds
#         super().__init__(options)
# 
#     def optimize(self,
#                  black_box: BlackBox,
#                  initial_guess: Optional[numpy.ndarray]=None,
#                  initial_guess_array: Optional[numpy.ndarray]=None
#                  ) -> OptimizationResult:
#         if initial_guess is None:
#             raise ValueError('The chosen optimization algorithm requires an '
#                              'initial guess.')
#         bounds = black_box.bounds if self.uses_bounds else None
#         result = scipy.optimize.minimize(black_box.evaluate,
#                                          initial_guess,
#                                          bounds=bounds,
#                                          options=self.options,
#                                          **self.kwargs)
#         return OptimizationResult(optimal_value=result.fun,
#                                   optimal_parameters=result.x,
#                                   num_evaluations=result.nfev,
#                                   status=result.status,
#                                   message=result.message)
# 
#     @property
#     def name(self) -> str:
#         return self.kwargs.get('method', 'ScipyOptimizationAlgorithm')
# 
# Path: openfermioncirq/testing/example_classes.py
# class ExampleBlackBox(BlackBox):
#     """Returns the sum of the squares of the inputs."""
# 
#     @property
#     def dimension(self) -> int:
#         return 2
# 
#     def _evaluate(self,
#                   x: numpy.ndarray) -> float:
#         return numpy.sum(x**2)


import numpy
import pytest
from openfermioncirq.optimization.scipy import (
        COBYLA,
        L_BFGS_B,
        NELDER_MEAD,
        SLSQP,
        ScipyOptimizationAlgorithm)
from openfermioncirq.testing import ExampleBlackBox
#   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.




@pytest.mark.parametrize('algorithm', [COBYLA, L_BFGS_B, NELDER_MEAD, SLSQP])
def test_scipy_algorithm(algorithm):
    black_box = ExampleBlackBox()
    initial_guess = numpy.zeros(black_box.dimension)
    result = algorithm.optimize(black_box, initial_guess)

    assert isinstance(result.optimal_value, float)
    assert isinstance(result.optimal_parameters, numpy.ndarray)
    assert isinstance(result.num_evaluations, int)
    assert isinstance(result.status, int)
    assert isinstance(result.message, (str, bytes))


def test_scipy_algorithm_requires_initial_guess():
    black_box = ExampleBlackBox()
    with pytest.raises(ValueError):
        _ = COBYLA.optimize(black_box)

def test_scipy_algorithm_name():
    assert ScipyOptimizationAlgorithm().name == 'ScipyOptimizationAlgorithm'
    assert COBYLA.name == 'COBYLA'
    assert L_BFGS_B.name == 'L-BFGS-B'
    assert NELDER_MEAD.name == 'Nelder-Mead'

# Path: openfermioncirq/optimization/scipy.py
# COBYLA = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'COBYLA'},
#         uses_bounds=False)
# 
# L_BFGS_B = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'L-BFGS-B'})
# 
# NELDER_MEAD = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'Nelder-Mead'},
#         uses_bounds=False)
# 
# SLSQP = ScipyOptimizationAlgorithm(
#         kwargs={'method': 'SLSQP'})
# 
# class ScipyOptimizationAlgorithm(OptimizationAlgorithm):
#     """An optimization algorithm from the scipy.optimize module."""
# 
#     def __init__(self,
#                  options: Optional[Dict]=None,
#                  kwargs: Optional[Dict]=None,
#                  uses_bounds: bool=True) -> None:
#         """
#         Args:
#             options: The `options` dictionary passed to scipy.optimize.minimize.
#             kwargs: Other keyword arguments passed to scipy.optimize.minimize.
#                 This should NOT include the `bounds` or `options` keyword
#                 arguments.
#             uses_bounds: Whether the algorithm uses bounds on the input
#                 variables. Set this to False to prevent scipy.optimize.minimize
#                 from raising a warning if the chosen method does not use bounds.
#         """
#         self.kwargs = kwargs or {}
#         self.uses_bounds = uses_bounds
#         super().__init__(options)
# 
#     def optimize(self,
#                  black_box: BlackBox,
#                  initial_guess: Optional[numpy.ndarray]=None,
#                  initial_guess_array: Optional[numpy.ndarray]=None
#                  ) -> OptimizationResult:
#         if initial_guess is None:
#             raise ValueError('The chosen optimization algorithm requires an '
#                              'initial guess.')
#         bounds = black_box.bounds if self.uses_bounds else None
#         result = scipy.optimize.minimize(black_box.evaluate,
#                                          initial_guess,
#                                          bounds=bounds,
#                                          options=self.options,
#                                          **self.kwargs)
#         return OptimizationResult(optimal_value=result.fun,
#                                   optimal_parameters=result.x,
#                                   num_evaluations=result.nfev,
#                                   status=result.status,
#                                   message=result.message)
# 
#     @property
#     def name(self) -> str:
#         return self.kwargs.get('method', 'ScipyOptimizationAlgorithm')
# 
# Path: openfermioncirq/testing/example_classes.py
# class ExampleBlackBox(BlackBox):
#     """Returns the sum of the squares of the inputs."""
# 
#     @property
#     def dimension(self) -> int:
#         return 2
# 
#     def _evaluate(self,
#                   x: numpy.ndarray) -> float:
#         return numpy.sum(x**2)


# Path: openfermioncirq/optimization/scipy_test.py
import numpy
import pytest
from openfermioncirq.optimization.scipy import (
        COBYLA,
        L_BFGS_B,
        NELDER_MEAD,
        SLSQP,
        ScipyOptimizationAlgorithm)
from openfermioncirq.testing import ExampleBlackBox

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




@pytest.mark.parametrize('algorithm', [COBYLA, L_BFGS_B, NELDER_MEAD, SLSQP])
def test_scipy_algorithm(algorithm):
    black_box = ExampleBlackBox()
    initial_guess = numpy.zeros(black_box.dimension)
    result = algorithm.optimize(black_box, initial_guess)

    assert isinstance(result.optimal_value, float)
    assert isinstance(result.optimal_parameters, numpy.ndarray)
    assert isinstance(result.num_evaluations, int)
    assert isinstance(result.status, int)
    assert isinstance(result.message, (str, bytes))


def test_scipy_algorithm_requires_initial_guess():
    black_box = ExampleBlackBox()
    with pytest.raises(ValueError):
        _ = COBYLA.optimize(black_box)

def test_scipy_algorithm_name():
    assert ScipyOptimizationAlgorithm().name == 'ScipyOptimizationAlgorithm'
    assert COBYLA.name == 'COBYLA'
    assert L_BFGS_B.name == 'L-BFGS-B'
    assert NELDER_MEAD.name == 'Nelder-Mead'

    assert SLSQP.name == 'SLSQP'