Repository: scipy/scipy
File: scipy/optimize/tests/test_linprog.py (Python)

Code before:
        c = np.array([-3, -2])
        A_ub = [[2, 1], [1, 1], [1, 0]]
        b_ub = [10, 8, 4]
        assert_raises(NotImplementedError, linprog, c, A_ub=A_ub, b_ub=b_ub,
                      callback=cb, method=self.method)
        res = linprog(c, A_ub=A_ub, b_ub=b_ub, method=self.method)
        _assert_success(res, desired_fun=-18.0, desired_x=[2, 6])

    @pytest.mark.parametrize("options",
                             [{"maxiter": -1},
                              {"disp": -1},
                              {"presolve": -1},
                              {"time_limit": -1},
                              {"dual_feasibility_tolerance": -1},
                              {"primal_feasibility_tolerance": -1},
                              {"ipm_optimality_tolerance": -1},
                              {"simplex_dual_edge_weight_strategy": "ekki"},
                              ])
    def test_invalid_option_values(self, options):
        def f(options):
            linprog(1, method=self.method, options=options)
        options.update(self.options)
        with pytest.warns(OptimizeWarning):
            f(options=options)

    def test_crossover(self):
        A_eq, b_eq, c, _, _ = magic_square(5)
        bounds = (0, 1)
        res = linprog(c, A_eq=A_eq, b_eq=b_eq,
                      bounds=bounds, method=self.method, options=self.options)
        # there should be nonzero crossover iterations for IPM (only)
        assert_equal(res.crossover_nit == 0, self.method != "highs-ipm")

    @pytest.mark.fail_slow(10)
    def test_marginals(self):
        # Ensure lagrange multipliers are correct by comparing the derivative
        # w.r.t. b_ub/b_eq/ub/lb to the reported duals.
        c, A_ub, b_ub, A_eq, b_eq, bounds = very_random_gen(seed=0)
        res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq,
                      bounds=bounds, method=self.method, options=self.options)
        lb, ub = bounds.T

        # sensitivity w.r.t. b_ub
        def f_bub(x):
            return linprog(c, A_ub, x, A_eq, b_eq, bounds,
                           method=self.method).fun

        dfdbub = approx_derivative(f_bub, b_ub, method='3-point', f0=res.fun)
        assert_allclose(res.ineqlin.marginals, dfdbub)

        # sensitivity w.r.t. b_eq

Review comment (suggestion):
@jajhall can you suggest a problem for which you'd expect HiGHS IPM to always do crossover iterations? I thought they were typically needed for this magic square problem generator, but maybe not.

Code after:
        c = np.array([-3, -2])
        A_ub = [[2, 1], [1, 1], [1, 0]]
        b_ub = [10, 8, 4]
        assert_raises(NotImplementedError, linprog, c, A_ub=A_ub, b_ub=b_ub,
                      callback=cb, method=self.method)
        res = linprog(c, A_ub=A_ub, b_ub=b_ub, method=self.method)
        _assert_success(res, desired_fun=-18.0, desired_x=[2, 6])

    @pytest.mark.parametrize("options",
                             [{"maxiter": -1},
                              {"disp": -1},
                              {"presolve": -1},
                              {"time_limit": -1},
                              {"dual_feasibility_tolerance": -1},
                              {"primal_feasibility_tolerance": -1},
                              {"ipm_optimality_tolerance": -1},
                              {"simplex_dual_edge_weight_strategy": "ekki"},
                              ])
    def test_invalid_option_values(self, options):
        def f(options):
            linprog(1, method=self.method, options=options)
        options.update(self.options)
        with pytest.warns(OptimizeWarning):
            f(options=options)

    def test_crossover(self):
        A_eq, b_eq, c, _, _ = magic_square(4, rng=np.random.default_rng(2212392))
        bounds = (0, 1)
        res = linprog(c, A_eq=A_eq, b_eq=b_eq,
                      bounds=bounds, method=self.method, options=self.options)
        # there should be nonzero crossover iterations for IPM (only)
        assert_equal(res.crossover_nit == 0, self.method != "highs-ipm")

    @pytest.mark.fail_slow(10)
    def test_marginals(self):
        # Ensure lagrange multipliers are correct by comparing the derivative
        # w.r.t. b_ub/b_eq/ub/lb to the reported duals.
        c, A_ub, b_ub, A_eq, b_eq, bounds = very_random_gen(seed=0)
        res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq,
                      bounds=bounds, method=self.method, options=self.options)
        lb, ub = bounds.T

        # sensitivity w.r.t. b_ub
        def f_bub(x):
            return linprog(c, A_ub, x, A_eq, b_eq, bounds,
                           method=self.method).fun

        dfdbub = approx_derivative(f_bub, b_ub, method='3-point', f0=res.fun)
        assert_allclose(res.ineqlin.marginals, dfdbub)

        # sensitivity w.r.t. b_eq