From 0626939c3dbdb260bcb49df3c8a1569d96501d27 Mon Sep 17 00:00:00 2001 From: Christoph Hansknecht Date: Fri, 15 Sep 2023 22:03:49 +0200 Subject: [PATCH] Add derivative checks to unit tests --- cyipopt/tests/unit/test_deriv_errors.py | 182 ++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 cyipopt/tests/unit/test_deriv_errors.py diff --git a/cyipopt/tests/unit/test_deriv_errors.py b/cyipopt/tests/unit/test_deriv_errors.py new file mode 100644 index 00000000..4c7cb436 --- /dev/null +++ b/cyipopt/tests/unit/test_deriv_errors.py @@ -0,0 +1,182 @@ +import numpy as np + +import cyipopt + +import pytest + + +def full_indices(shape): + def indices(): + r, c = np.indices(shape) + return r.flatten(), c.flatten() + + return indices + + +def tril_indices(size): + def indices(): + return np.tril_indices(size) + + return indices + + +def flatten(func): + def _func(*args): + return func(*args).flatten() + + return _func + + +@pytest.fixture +def hs071_sparse_definition_fixture(hs071_variable_lower_bounds_fixture, + hs071_constraint_lower_bounds_fixture, + hs071_definition_instance_fixture): + problem = hs071_definition_instance_fixture + n = len(hs071_variable_lower_bounds_fixture) + m = len(hs071_constraint_lower_bounds_fixture) + + problem.jacobianstructure = full_indices((m, n)) + problem.hessianstructure = tril_indices(n) + + problem.jacobian = flatten(problem.jacobian) + problem.hessian = flatten(problem.hessian) + + return problem + + +@pytest.fixture +def hs071_sparse_instance(hs071_initial_guess_fixture, + hs071_variable_lower_bounds_fixture, + hs071_variable_upper_bounds_fixture, + hs071_constraint_lower_bounds_fixture, + hs071_constraint_upper_bounds_fixture, + hs071_sparse_definition_fixture): + + class Instance: + pass + + instance = Instance() + instance.problem_definition = hs071_sparse_definition_fixture + instance.x0 = hs071_initial_guess_fixture + instance.lb = hs071_variable_lower_bounds_fixture + instance.ub = hs071_variable_upper_bounds_fixture + instance.cl = hs071_constraint_lower_bounds_fixture + instance.cu = hs071_constraint_upper_bounds_fixture + instance.n = len(instance.x0) + instance.m = len(instance.cl) + + return instance + + +def problem_for_instance(instance): + return cyipopt.Problem(n=instance.n, + m=instance.m, + problem_obj=instance.problem_definition, + lb=instance.lb, + ub=instance.ub, + cl=instance.cl, + cu=instance.cu) + + +def test_solve_sparse(hs071_sparse_instance): + instance = hs071_sparse_instance + problem = problem_for_instance(instance) + + x, info = problem.solve(instance.x0) + + assert info['status'] == 0 + + +def ensure_invalid_option(instance): + problem = problem_for_instance(instance) + + problem.add_option('max_iter', 50) + x, info = problem.solve(instance.x0) + + # -12: Invalid option + assert info['status'] == -12 + + +@pytest.mark.skip +def test_solve_neg_jac(hs071_sparse_instance): + n = hs071_sparse_instance.n + m = hs071_sparse_instance.m + problem_definition = hs071_sparse_instance.problem_definition + + def jacobianstructure(): + r = np.full((m*n,), fill_value=-1, dtype=int) + c = np.full((m*n,), fill_value=-1, dtype=int) + return r, c + + problem_definition.jacobianstructure = jacobianstructure + + ensure_invalid_option(hs071_sparse_instance) + + +@pytest.mark.skip +def test_solve_large_jac(hs071_sparse_instance): + n = hs071_sparse_instance.n + m = hs071_sparse_instance.m + problem_definition = hs071_sparse_instance.problem_definition + + import logging + logging.basicConfig(level=logging.DEBUG) + + def jacobianstructure(): + r = np.full((m*n,), fill_value=(m + n + 100), dtype=int) + c = np.full((m*n,), fill_value=(m + n + 100), dtype=int) + return r, c + + problem_definition.jacobianstructure = jacobianstructure + + ensure_invalid_option(hs071_sparse_instance) + + +def test_solve_triu_hess(hs071_sparse_instance): + n = hs071_sparse_instance.n + problem_definition = hs071_sparse_instance.problem_definition + problem_definition.hessianstructure = lambda: np.triu_indices(n) + + ensure_invalid_option(hs071_sparse_instance) + + +def test_solve_neg_hess_entries(hs071_sparse_instance): + n = hs071_sparse_instance.n + problem_definition = hs071_sparse_instance.problem_definition + + def hessianstructure(): + r, c = np.tril_indices(n) + rneg = np.full_like(r, -1, dtype=int) + cneg = np.full_like(c, -1, dtype=int) + return rneg, cneg + + problem_definition.hessianstructure = hessianstructure + + ensure_invalid_option(hs071_sparse_instance) + + +def test_solve_large_hess_entries(hs071_sparse_instance): + n = hs071_sparse_instance.n + problem_definition = hs071_sparse_instance.problem_definition + + def hessianstructure(): + r, c = np.tril_indices(n) + rlarge = np.full_like(r, n + 100, dtype=int) + clarge = np.full_like(c, n + 100, dtype=int) + return rlarge, clarge + + problem_definition.hessianstructure = hessianstructure + + ensure_invalid_option(hs071_sparse_instance) + + +def test_solve_wrong_size(hs071_sparse_instance): + n = hs071_sparse_instance.n + problem_definition = hs071_sparse_instance.problem_definition + + def hessianstructure(): + return np.tril_indices(n + 10) + + problem_definition.hessianstructure = hessianstructure + + ensure_invalid_option(hs071_sparse_instance)