def test_external_multipliers_from_residual_multipliers(self):
        model = Model2by2()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [0.5, 1.0, 1.5, 2.5, 4.1]
        lam_init_list = [-2.5, -0.5, 0.0, 1.0, 2.0]
        init_list = list(
            itertools.product(x0_init_list, x1_init_list, lam_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x0, x1, lam in init_list:
            x = [x0, x1]
            lam = [lam]
            external_model.set_input_values(x)
            lam_g = external_model.calculate_external_constraint_multipliers(
                lam)
            pred_lam_g = model.calculate_external_multipliers(lam, x)
            np.testing.assert_allclose(lam_g, pred_lam_g, rtol=1e-8)
示例#2
0
    def test_cyipopt_unavailable(self):
        try:
            # HACK: Make external_pyomo_model.py think that cyipopt is not
            # available.
            epm_module.cyipopt_available = False

            m = pyo.ConcreteModel()
            m.ex_block = ExternalGreyBoxBlock(concrete=True)
            block = m.ex_block

            m_ex = _make_external_model()
            input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
            external_vars = [m_ex.x, m_ex.y]
            residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
            external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
            inner_solver = pyo.SolverFactory("ipopt")
            msg = "Constructing an ExternalPyomoModel with CyIpopt unavailable"
            with self.assertRaisesRegex(RuntimeError, msg):
                ex_model = ExternalPyomoModel(
                    input_vars,
                    external_vars,
                    residual_cons,
                    external_cons,
                    use_cyipopt=True,
                )
        finally:
            # HACK: Reset the global flag
            epm_module.cyipopt_available = cyipopt_available
    def make_model(self):
        m = pyo.ConcreteModel()
        m.x = pyo.Var(initialize=1.0)
        m.y = pyo.Var(initialize=1.0)
        m.u = pyo.Var(initialize=1.0)
        m.v = pyo.Var(initialize=1.0)
        m.con_1 = pyo.Constraint(expr=m.x * m.y == m.u)
        m.con_2 = pyo.Constraint(expr=m.x**2 * m.y**3 == m.v)
        m.con_3 = pyo.Constraint(
            expr=self.con_3_body(m.x, m.y, m.u, m.v) == self.con_3_rhs())
        m.con_4 = pyo.Constraint(
            expr=self.con_4_body(m.x, m.y, m.u, m.v) == self.con_4_rhs())

        epm_model = pyo.ConcreteModel()
        epm_model.x = pyo.Reference(m.x)
        epm_model.y = pyo.Reference(m.y)
        epm_model.u = pyo.Reference(m.u)
        epm_model.v = pyo.Reference(m.v)
        epm_model.epm = ExternalPyomoModel(
            [m.u, m.v],
            [m.x, m.y],
            [m.con_3, m.con_4],
            [m.con_1, m.con_2],
        )
        epm_model.obj = pyo.Objective(expr=m.x**2 + m.y**2 + m.u**2 + m.v**2)
        epm_model.egb = ExternalGreyBoxBlock()
        epm_model.egb.set_external_model(epm_model.epm, inputs=[m.u, m.v])
        return epm_model
    def test_evaluate_hessian_lagrangian_SimpleModel2x2_1(self):
        model = SimpleModel2by2_1()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [-4.5, -2.3, 0.0, 1.0, 4.1]
        x_init_list = list(itertools.product(x0_init_list, x1_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x in x_init_list:
            external_model.set_input_values(x)
            external_model.set_equality_constraint_multipliers([1.0, 1.0])
            hess_lag = external_model.evaluate_hessian_equality_constraints()
            hess_lag = hess_lag.toarray()
            expected_hess = model.evaluate_hessian(x)
            expected_hess_lag = np.tril(expected_hess[0] + expected_hess[1])
            np.testing.assert_allclose(hess_lag, expected_hess_lag, rtol=1e-8)
    def test_hessian_SimpleModel2(self):
        model = SimpleModel2()
        m = model.make_model()
        x_init_list = [
                [-5.0], [-4.0], [-3.0], [-1.5], [0.5], [1.0], [2.0], [3.5]
                ]
        external_model = ExternalPyomoModel(
                [m.x], [m.y], [m.residual_eqn], [m.external_eqn],
                )

        for x in x_init_list:
            external_model.set_input_values(x)
            hess = external_model.evaluate_hessians_of_residuals()
            self.assertAlmostEqual(
                    hess[0][0, 0],
                    model.evaluate_hessian(x[0]),
                    delta=1e-7,
                    )
    def test_evaluate_SimpleModel2(self):
        model = SimpleModel2()
        m = model.make_model()
        x_init_list = [
                [-5.0], [-4.0], [-3.0], [-1.5], [0.5], [1.0], [2.0], [3.5]
                ]
        external_model = ExternalPyomoModel(
                [m.x], [m.y], [m.residual_eqn], [m.external_eqn],
                )

        for x in x_init_list:
            external_model.set_input_values(x)
            resid = external_model.evaluate_equality_constraints()
            self.assertAlmostEqual(
                    resid[0],
                    model.evaluate_residual(x[0]),
                    delta=1e-8,
                    )
    def test_jacobian_SimpleModel1(self):
        model = SimpleModel1()
        m = model.make_model()
        x_init_list = [
                [-5.0], [-4.0], [-3.0], [-1.5], [0.5], [1.0], [2.0], [3.5]
                ]
        external_model = ExternalPyomoModel(
                [m.x], [m.y], [m.residual_eqn], [m.external_eqn],
                )

        for x in x_init_list:
            external_model.set_input_values(x)
            jac = external_model.evaluate_jacobian_equality_constraints()
            self.assertAlmostEqual(
                    jac.toarray()[0][0],
                    model.evaluate_jacobian(x[0]),
                    delta=1e-8,
                    )
    def test_external_jacobian_SimpleModel2(self):
        model = SimpleModel2()
        m = model.make_model()
        x_init_list = [
                [-5.0], [-4.0], [-3.0], [-1.5], [0.5], [1.0], [2.0], [3.5]
                ]
        external_model = ExternalPyomoModel(
                [m.x], [m.y], [m.residual_eqn], [m.external_eqn],
                )

        for x in x_init_list:
            external_model.set_input_values(x)
            jac = external_model.evaluate_jacobian_external_variables()
            expected_jac = model.evaluate_external_jacobian(x[0])
            self.assertAlmostEqual(
                    jac[0,0],
                    expected_jac,
                    delta=1e-8,
                    )
示例#9
0
    def test_optimize_no_cyipopt_for_inner_problem(self):
        # Here we don't specify a solver for the inner problem.
        # This is contrived, as clearly CyIpopt is available for the outer
        # solve. This test exercises the part of the ExternalPyomoModel
        # constructor that sets a default solver if CyIpopt is not available.
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
        )
        block.set_external_model(ex_model)

        a = m.ex_block.inputs["input_0"]
        b = m.ex_block.inputs["input_1"]
        r = m.ex_block.inputs["input_2"]
        x = m.ex_block.inputs["input_3"]
        y = m.ex_block.inputs["input_4"]
        m.obj = pyo.Objective(expr=(x - 2.0)**2 + (y - 2.0)**2 + (a - 2.0)**2 +
                              (b - 2.0)**2 + (r - 2.0)**2)

        # Solve with external model embedded
        solver = pyo.SolverFactory("cyipopt")
        solver.solve(m)

        m_ex.obj = pyo.Objective(expr=(m_ex.x - 2.0)**2 + (m_ex.y - 2.0)**2 +
                                 (m_ex.a - 2.0)**2 + (m_ex.b - 2.0)**2 +
                                 (m_ex.r - 2.0)**2)
        m_ex.a.set_value(0.0)
        m_ex.b.set_value(0.0)
        m_ex.r.set_value(0.0)
        m_ex.y.set_value(0.0)
        m_ex.x.set_value(0.0)

        # Solve external model, now with same objective function
        ipopt = pyo.SolverFactory("ipopt")
        ipopt.solve(m_ex)

        # Make sure full space and reduced space solves give same
        # answers.
        self.assertAlmostEqual(m_ex.a.value, a.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.b.value, b.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.r.value, r.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.x.value, x.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.y.value, y.value, delta=1e-8)
示例#10
0
    def test_optimize_with_ipopt_for_inner_problem(self):
        # Use Ipopt, rather than the default CyIpopt, for the inner problem
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        inner_solver = pyo.SolverFactory("ipopt")
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
            solver=inner_solver,
            use_cyipopt=False,
        )
        block.set_external_model(ex_model)

        a = m.ex_block.inputs["input_0"]
        b = m.ex_block.inputs["input_1"]
        r = m.ex_block.inputs["input_2"]
        x = m.ex_block.inputs["input_3"]
        y = m.ex_block.inputs["input_4"]
        m.obj = pyo.Objective(expr=(x - 2.0)**2 + (y - 2.0)**2 + (a - 2.0)**2 +
                              (b - 2.0)**2 + (r - 2.0)**2)

        # Solve with external model embedded
        solver = pyo.SolverFactory("cyipopt")
        solver.solve(m)

        m_ex.obj = pyo.Objective(expr=(m_ex.x - 2.0)**2 + (m_ex.y - 2.0)**2 +
                                 (m_ex.a - 2.0)**2 + (m_ex.b - 2.0)**2 +
                                 (m_ex.r - 2.0)**2)
        m_ex.a.set_value(0.0)
        m_ex.b.set_value(0.0)
        m_ex.r.set_value(0.0)
        m_ex.y.set_value(0.0)
        m_ex.x.set_value(0.0)

        # Solve external model, now with same objective function
        ipopt = pyo.SolverFactory("ipopt")
        ipopt.solve(m_ex)

        # Make sure full space and reduced space solves give same
        # answers.
        self.assertAlmostEqual(m_ex.a.value, a.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.b.value, b.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.r.value, r.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.x.value, x.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.y.value, y.value, delta=1e-8)
    def test_hessian_SimpleModel2x2_1(self):
        model = SimpleModel2by2_1()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [-4.5, -2.3, 0.0, 1.0, 4.1]
        x_init_list = list(itertools.product(x0_init_list, x1_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x in x_init_list:
            external_model.set_input_values(x)
            hess = external_model.evaluate_hessians_of_residuals()
            expected_hess = model.evaluate_hessian(x)
            np.testing.assert_allclose(hess, expected_hess, rtol=1e-8)
    def test_jacobian_SimpleModel2x2_1(self):
        model = SimpleModel2by2_1()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [-4.5, -2.3, 0.0, 1.0, 4.1]
        x_init_list = list(itertools.product(x0_init_list, x1_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x in x_init_list:
            external_model.set_input_values(x)
            jac = external_model.evaluate_jacobian_equality_constraints()
            expected_jac = model.evaluate_jacobian(x)
            np.testing.assert_allclose(jac.toarray(), expected_jac, rtol=1e-8)
    def test_external_jacobian_Model2by2(self):
        model = Model2by2()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [0.5, 1.0, 1.5, 2.5, 4.1]
        x_init_list = list(itertools.product(x0_init_list, x1_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x in x_init_list:
            external_model.set_input_values(x)
            jac = external_model.evaluate_jacobian_external_variables()
            expected_jac = model.evaluate_external_jacobian(x)
            np.testing.assert_allclose(jac, expected_jac, rtol=1e-8)
示例#14
0
    def test_jacobian_SimpleModel2(self):
        model = SimpleModel2()
        m = model.make_model()
        x_init_list = [
                [-5.0], [-4.0], [-3.0], [-1.5], [0.5], [1.0], [2.0], [3.5]
                ]
        external_model = ExternalPyomoModel(
                [m.x], [m.y], [m.residual_eqn], [m.external_eqn],
                )

        for x in x_init_list:
            external_model.set_input_values(x)
            jac = external_model.evaluate_jacobian_equality_constraints()
            # evaluate_jacobian_equality_constraints involves an LU
            # factorization and repeated back-solve. SciPy returns a
            # dense matrix from this operation. I am not sure if I should
            # cast it to a sparse matrix. For now it is dense...
            self.assertAlmostEqual(
                    jac.toarray()[0][0],
                    model.evaluate_jacobian(x[0]),
                    delta=1e-7,
                    )
    def test_reduced_hessian_lagrangian(self):
        model = Model2by2()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [0.5, 1.0, 1.5, 2.5, 4.1]
        lam_init_list = [-2.5, -0.5, 0.0, 1.0, 2.0]
        init_list = list(
            itertools.product(x0_init_list, x1_init_list, lam_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x0, x1, lam in init_list:
            x = [x0, x1]
            lam = [lam]
            external_model.set_input_values(x)
            # Same comment as previous test regarding calculation order
            external_model.set_external_constraint_multipliers(lam)
            hlxx, hlxy, hlyy = \
                external_model.get_full_space_lagrangian_hessians()
            hess = external_model.calculate_reduced_hessian_lagrangian(
                hlxx, hlxy, hlyy)
            pred_hess = model.calculate_reduced_lagrangian_hessian(lam, x)
            # This test asserts that we are doing the block reduction properly.
            np.testing.assert_allclose(np.array(hess), pred_hess, rtol=1e-8)

            from_individual = external_model.evaluate_hessians_of_residuals()
            hl_from_individual = sum(l * h
                                     for l, h in zip(lam, from_individual))
            # This test asserts that the block reduction is correct.
            np.testing.assert_allclose(np.array(hess),
                                       hl_from_individual,
                                       rtol=1e-8)
    def test_evaluate_hessian_equality_constraints_order(self):
        model = Model2by2()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [0.5, 1.0, 1.5, 2.5, 4.1]
        lam_init_list = [-2.5, -0.5, 0.0, 1.0, 2.0]
        init_list = list(
            itertools.product(x0_init_list, x1_init_list, lam_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x0, x1, lam in init_list:
            x = [x0, x1]
            lam = [lam]
            external_model.set_equality_constraint_multipliers(lam)
            external_model.set_input_values(x)
            # Using evaluate_hessian_equality_constraints, which calculates
            # external multiplier values, we can calculate the correct Hessian
            # regardless of the order in which primal and dual variables are
            # set.
            hess = external_model.evaluate_hessian_equality_constraints()
            pred_hess = model.calculate_reduced_lagrangian_hessian(lam, x)
            # This test asserts that we are doing the block reduction properly.
            np.testing.assert_allclose(hess.toarray(),
                                       np.tril(pred_hess),
                                       rtol=1e-8)

            from_individual = external_model.evaluate_hessians_of_residuals()
            hl_from_individual = sum(l * h
                                     for l, h in zip(lam, from_individual))
            # This test asserts that the block reduction is correct.
            np.testing.assert_allclose(hess.toarray(),
                                       np.tril(hl_from_individual),
                                       rtol=1e-8)
示例#17
0
    def test_solve_square(self):
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
        )
        block.set_external_model(ex_model)

        _add_linking_constraints(m)

        m.a.fix(1)
        m.b.fix(2)
        m.r.fix(3)

        m.obj = pyo.Objective(expr=0)

        solver = pyo.SolverFactory("cyipopt")
        solver.solve(m)

        self.assertFalse(m_ex.a.fixed)
        self.assertFalse(m_ex.b.fixed)
        self.assertFalse(m_ex.r.fixed)

        m_ex.a.fix(1)
        m_ex.b.fix(2)
        m_ex.r.fix(3)
        ipopt = pyo.SolverFactory("ipopt")
        ipopt.solve(m_ex)

        x = m.ex_block.inputs["input_3"]
        y = m.ex_block.inputs["input_4"]
        self.assertAlmostEqual(m_ex.x.value, x.value, delta=1e-8)
        self.assertAlmostEqual(m_ex.y.value, y.value, delta=1e-8)
示例#18
0
    def test_construct_scalar(self):
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block
        self.assertIs(type(block), ScalarExternalGreyBoxBlock)

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
        )
        block.set_external_model(ex_model)

        self.assertEqual(len(block.inputs), len(input_vars))
        self.assertEqual(len(block.outputs), 0)
        self.assertEqual(len(block._equality_constraint_names), 2)
示例#19
0
    def test_construct_indexed(self):
        block = ExternalGreyBoxBlock([0, 1, 2], concrete=True)
        self.assertIs(type(block), IndexedExternalGreyBoxBlock)

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
        )

        for i in block:
            b = block[i]
            b.set_external_model(ex_model)
            self.assertEqual(len(b.inputs), len(input_vars))
            self.assertEqual(len(b.outputs), 0)
            self.assertEqual(len(b._equality_constraint_names), 2)
示例#20
0
    def test_solver_with_cyipopt(self):
        # CyIpopt is required here just because we get a different error
        # (see test below) if CyIpopt is unavailable.
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        inner_solver = pyo.SolverFactory("ipopt")
        msg = "Please set use_cyipopt to False"
        with self.assertRaisesRegex(RuntimeError, msg):
            ex_model = ExternalPyomoModel(
                input_vars,
                external_vars,
                residual_cons,
                external_cons,
                solver=inner_solver,
                use_cyipopt=True,
            )
    def test_full_space_lagrangian_hessians(self):
        model = Model2by2()
        m = model.make_model()
        m.x[0].set_value(1.0)
        m.x[1].set_value(2.0)
        m.y[0].set_value(3.0)
        m.y[1].set_value(4.0)
        x0_init_list = [-5.0, -3.0, 0.5, 1.0, 2.5]
        x1_init_list = [0.5, 1.0, 1.5, 2.5, 4.1]
        lam_init_list = [-2.5, -0.5, 0.0, 1.0, 2.0]
        init_list = list(
            itertools.product(x0_init_list, x1_init_list, lam_init_list))
        external_model = ExternalPyomoModel(
            list(m.x.values()),
            list(m.y.values()),
            list(m.residual_eqn.values()),
            list(m.external_eqn.values()),
        )

        for x0, x1, lam in init_list:
            x = [x0, x1]
            lam = [lam]
            external_model.set_input_values(x)
            # Note that these multiplier calculations are dependent on x,
            # so if we switch their order, we will get "wrong" answers.
            # (This is wrong in the sense that the residual and external
            # multipliers won't necessarily correspond).
            external_model.set_external_constraint_multipliers(lam)
            hlxx, hlxy, hlyy = \
                external_model.get_full_space_lagrangian_hessians()
            pred_hlxx, pred_hlxy, pred_hlyy = \
                model.calculate_full_space_lagrangian_hessians(lam, x)

            # TODO: Is comparing the array representation sufficient here?
            # Should I make sure I get the sparse representation I expect?
            np.testing.assert_allclose(hlxx.toarray(), pred_hlxx, rtol=1e-8)
            np.testing.assert_allclose(hlxy.toarray(), pred_hlxy, rtol=1e-8)
            np.testing.assert_allclose(hlyy.toarray(), pred_hlyy, rtol=1e-8)
示例#22
0
    def test_optimize_dynamic(self):
        # Create the "external model"
        m = make_dynamic_model()
        time = m.time
        t0 = m.time.first()
        m.h[t0].fix(1.2)
        m.flow_in[t0].fix(1.5)

        m.obj = pyo.Objective(expr=sum(
            (m.h[t] - 2.0)**2 for t in m.time if t != t0))

        # Create the block that will hold the reduced space model.
        reduced_space = pyo.Block(concrete=True)
        reduced_space.diff_var = pyo.Reference(m.h)
        reduced_space.deriv_var = pyo.Reference(m.dhdt)
        reduced_space.input_var = pyo.Reference(m.flow_in)
        reduced_space.disc_eq = pyo.Reference(m.dhdt_disc_eqn)
        reduced_space.objective = pyo.Reference(m.obj)

        reduced_space.external_block = ExternalGreyBoxBlock(time)
        block = reduced_space.external_block
        block[t0].deactivate()
        for t in time:
            # TODO: skipping time.first() necessary?
            if t != t0:
                input_vars = [m.h[t], m.dhdt[t], m.flow_in[t]]
                external_vars = [m.flow_out[t]]
                residual_cons = [m.h_diff_eqn[t]]
                external_cons = [m.flow_out_eqn[t]]
                external_model = ExternalPyomoModel(
                    input_vars,
                    external_vars,
                    residual_cons,
                    external_cons,
                )
                block[t].set_external_model(external_model)

        n_inputs = len(input_vars)

        def linking_constraint_rule(m, i, t):
            if t == t0:
                return pyo.Constraint.Skip
            if i == 0:
                return m.diff_var[t] == m.external_block[t].inputs["input_0"]
            elif i == 1:
                return m.deriv_var[t] == m.external_block[t].inputs["input_1"]
            elif i == 2:
                return m.input_var[t] == m.external_block[t].inputs["input_2"]

        reduced_space.linking_constraint = pyo.Constraint(
            range(n_inputs),
            time,
            rule=linking_constraint_rule,
        )
        # Initialize new variables
        for t in time:
            if t != t0:
                block[t].inputs["input_0"].set_value(m.h[t].value)
                block[t].inputs["input_1"].set_value(m.dhdt[t].value)
                block[t].inputs["input_2"].set_value(m.flow_in[t].value)

        solver = pyo.SolverFactory("cyipopt")
        results = solver.solve(reduced_space)

        # These values were obtained by solving this problem in the full
        # space in a separate script.
        h_target = [1.2, 2.0, 2.0]
        dhdt_target = [-0.690890, 0.80, 0.0]
        flow_in_target = [1.5, 3.628427, 2.828427]
        flow_out_target = [2.190890, 2.828427, 2.828427]
        for t in time:
            if t == t0:
                continue
            values = [
                m.h[t].value, m.dhdt[t].value, m.flow_out[t].value,
                m.flow_in[t].value
            ]
            target_values = [
                h_target[t], dhdt_target[t], flow_out_target[t],
                flow_in_target[t]
            ]
            self.assertStructuredAlmostEqual(values, target_values, delta=1e-5)
示例#23
0
    def test_construct_dynamic(self):
        m = make_dynamic_model()
        time = m.time
        t0 = m.time.first()

        inputs = [m.h, m.dhdt, m.flow_in]
        ext_vars = [m.flow_out]
        residuals = [m.h_diff_eqn]
        ext_cons = [m.flow_out_eqn]

        external_model_dict = {
            t: ExternalPyomoModel(
                [var[t] for var in inputs],
                [var[t] for var in ext_vars],
                [con[t] for con in residuals],
                [con[t] for con in ext_cons],
            )
            for t in time
        }

        reduced_space = pyo.Block(concrete=True)
        reduced_space.external_block = ExternalGreyBoxBlock(
            time,
            external_model=external_model_dict,
        )
        block = reduced_space.external_block
        block[t0].deactivate()
        self.assertIs(type(block), IndexedExternalGreyBoxBlock)

        for t in time:
            b = block[t]
            self.assertEqual(len(b.inputs), len(inputs))
            self.assertEqual(len(b.outputs), 0)
            self.assertEqual(len(b._equality_constraint_names), len(residuals))

        reduced_space.diff_var = pyo.Reference(m.h)
        reduced_space.deriv_var = pyo.Reference(m.dhdt)
        reduced_space.input_var = pyo.Reference(m.flow_in)
        reduced_space.disc_eqn = pyo.Reference(m.dhdt_disc_eqn)

        pyomo_vars = list(reduced_space.component_data_objects(pyo.Var))
        pyomo_cons = list(reduced_space.component_data_objects(pyo.Constraint))
        # NOTE: Variables in the EGBB are not found by component_data_objects
        self.assertEqual(len(pyomo_vars), len(inputs) * len(time))
        # "Constraints" defined by the EGBB are not found either, although
        # this is expected.
        self.assertEqual(len(pyomo_cons), len(time) - 1)

        reduced_space._obj = pyo.Objective(expr=0)

        # This is required to avoid a failure in the implicit function
        # evaluation when "initializing" (?) the PNLPwGBB.
        # Why exactly is function evaluation necessary for this
        # initialization again?
        block[:].inputs[:].set_value(1.0)

        # This is necessary for these variables to appear in the PNLPwGBB.
        # Otherwise they don't appear in any "real" constraints of the
        # PyomoNLP.
        reduced_space.const_input_eqn = pyo.Constraint(
            expr=reduced_space.input_var[2] - reduced_space.input_var[1] == 0)

        nlp = PyomoNLPWithGreyBoxBlocks(reduced_space)
        self.assertEqual(
            nlp.n_primals(),
            # EGBB "inputs", dhdt, and flow_in exist for t != t0.
            # h exists for all time.
            (2 + len(inputs)) * (len(time) - 1) + len(time),
        )
        self.assertEqual(
            nlp.n_constraints(),
            # EGBB equality constraints and disc_eqn exist for t != t0.
            # const_input_eqn is a single constraint
            (len(residuals) + 1) * (len(time) - 1) + 1,
        )
示例#24
0
    def test_solve_square_dynamic(self):
        # Create the "external model"
        m = make_dynamic_model()
        time = m.time
        t0 = m.time.first()
        m.h[t0].fix(1.2)
        m.flow_in.fix(1.5)

        # Create the block that will hold the reduced space model.
        reduced_space = pyo.Block(concrete=True)
        reduced_space.diff_var = pyo.Reference(m.h)
        reduced_space.deriv_var = pyo.Reference(m.dhdt)
        reduced_space.input_var = pyo.Reference(m.flow_in)
        reduced_space.disc_eq = pyo.Reference(m.dhdt_disc_eqn)

        reduced_space.external_block = ExternalGreyBoxBlock(time)
        block = reduced_space.external_block
        block[t0].deactivate()
        for t in time:
            # TODO: skipping time.first() necessary?
            if t != t0:
                input_vars = [m.h[t], m.dhdt[t]]
                external_vars = [m.flow_out[t]]
                residual_cons = [m.h_diff_eqn[t]]
                external_cons = [m.flow_out_eqn[t]]
                external_model = ExternalPyomoModel(
                    input_vars,
                    external_vars,
                    residual_cons,
                    external_cons,
                )
                block[t].set_external_model(external_model)

        n_inputs = len(input_vars)

        def linking_constraint_rule(m, i, t):
            if t == t0:
                return pyo.Constraint.Skip
            if i == 0:
                return m.diff_var[t] == m.external_block[t].inputs["input_0"]
            elif i == 1:
                return m.deriv_var[t] == m.external_block[t].inputs["input_1"]

        reduced_space.linking_constraint = pyo.Constraint(
            range(n_inputs),
            time,
            rule=linking_constraint_rule,
        )
        # Initialize new variables
        for t in time:
            if t != t0:
                block[t].inputs["input_0"].set_value(m.h[t].value)
                block[t].inputs["input_1"].set_value(m.dhdt[t].value)

        reduced_space._obj = pyo.Objective(expr=0)

        solver = pyo.SolverFactory("cyipopt")
        results = solver.solve(reduced_space, tee=True)

        # Full space square model was solved in a separate script
        # to obtain these values.
        h_target = [1.2, 0.852923, 0.690725]
        dhdt_target = [-0.690890, -0.347077, -0.162198]
        flow_out_target = [2.190980, 1.847077, 1.662198]
        for t in time:
            if t == t0:
                continue
            values = [m.h[t].value, m.dhdt[t].value, m.flow_out[t].value]
            target_values = [h_target[t], dhdt_target[t], flow_out_target[t]]
            self.assertStructuredAlmostEqual(values, target_values, delta=1e-5)
示例#25
0
    def test_optimize_dynamic_references(self):
        """
        When when pre-existing variables are attached to the EGBB
        as references, linking constraints are no longer necessary.
        """
        # Create the "external model"
        m = make_dynamic_model()
        time = m.time
        t0 = m.time.first()
        m.h[t0].fix(1.2)
        m.flow_in[t0].fix(1.5)

        m.obj = pyo.Objective(expr=sum(
            (m.h[t] - 2.0)**2 for t in m.time if t != t0))

        # Create the block that will hold the reduced space model.
        reduced_space = pyo.Block(concrete=True)
        reduced_space.diff_var = pyo.Reference(m.h)
        reduced_space.deriv_var = pyo.Reference(m.dhdt)
        reduced_space.input_var = pyo.Reference(m.flow_in)
        reduced_space.disc_eq = pyo.Reference(m.dhdt_disc_eqn)
        reduced_space.objective = pyo.Reference(m.obj)

        reduced_space.external_block = ExternalGreyBoxBlock(time)
        block = reduced_space.external_block
        block[t0].deactivate()
        for t in time:
            # TODO: is skipping time.first() necessary?
            if t != t0:
                input_vars = [m.h[t], m.dhdt[t], m.flow_in[t]]
                external_vars = [m.flow_out[t]]
                residual_cons = [m.h_diff_eqn[t]]
                external_cons = [m.flow_out_eqn[t]]
                external_model = ExternalPyomoModel(
                    input_vars,
                    external_vars,
                    residual_cons,
                    external_cons,
                )
                block[t].set_external_model(external_model, inputs=input_vars)

        solver = pyo.SolverFactory("cyipopt")
        results = solver.solve(reduced_space)

        # These values were obtained by solving this problem in the full
        # space in a separate script.
        h_target = [1.2, 2.0, 2.0]
        dhdt_target = [-0.690890, 0.80, 0.0]
        flow_in_target = [1.5, 3.628427, 2.828427]
        flow_out_target = [2.190890, 2.828427, 2.828427]
        for t in time:
            if t == t0:
                continue
            values = [
                m.h[t].value, m.dhdt[t].value, m.flow_out[t].value,
                m.flow_in[t].value
            ]
            target_values = [
                h_target[t], dhdt_target[t], flow_out_target[t],
                flow_in_target[t]
            ]
            self.assertStructuredAlmostEqual(values, target_values, delta=1e-5)
示例#26
0
    def test_set_and_evaluate(self):
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
        )
        block.set_external_model(ex_model)

        a = m.ex_block.inputs["input_0"]
        b = m.ex_block.inputs["input_1"]
        r = m.ex_block.inputs["input_2"]
        x = m.ex_block.inputs["input_3"]
        y = m.ex_block.inputs["input_4"]
        m.obj = pyo.Objective(expr=(x - 2.0)**2 + (y - 2.0)**2 + (a - 2.0)**2 +
                              (b - 2.0)**2 + (r - 2.0)**2)

        _add_linking_constraints(m)

        nlp = PyomoNLPWithGreyBoxBlocks(m)

        # Set primals in model, get primals in nlp
        # set/get duals
        # evaluate constraints
        # evaluate Jacobian
        # evaluate Hessian
        self.assertEqual(nlp.n_primals(), 8)

        # PyomoNLPWithGreyBoxBlocks sorts variables by name
        primals_names = [
            "a",
            "b",
            "ex_block.inputs[input_0]",
            "ex_block.inputs[input_1]",
            "ex_block.inputs[input_2]",
            "ex_block.inputs[input_3]",
            "ex_block.inputs[input_4]",
            "r",
        ]
        self.assertEqual(nlp.primals_names(), primals_names)
        np.testing.assert_equal(np.zeros(8), nlp.get_primals())

        primals = np.array([0, 1, 2, 3, 4, 5, 6, 7])
        nlp.set_primals(primals)
        np.testing.assert_equal(primals, nlp.get_primals())
        nlp.load_state_into_pyomo()

        for name, val in zip(primals_names, primals):
            var = m.find_component(name)
            self.assertEqual(var.value, val)

        constraint_names = [
            "linking_constraint[0]",
            "linking_constraint[1]",
            "linking_constraint[2]",
            "ex_block.residual_0",
            "ex_block.residual_1",
        ]
        self.assertEqual(constraint_names, nlp.constraint_names())
        residuals = np.array([
            -2.0,
            -2.0,
            3.0,
            # These values were obtained by solving the same system
            # with Ipopt in another script. It may be better to do
            # the solve in this test in case the system changes.
            5.0 - (-3.03051522),
            6.0 - 3.583839997,
        ])
        np.testing.assert_allclose(residuals,
                                   nlp.evaluate_constraints(),
                                   rtol=1e-8)

        duals = np.array([1, 2, 3, 4, 5])
        nlp.set_duals(duals)

        self.assertEqual(ex_model.residual_con_multipliers, [4, 5])
        np.testing.assert_equal(nlp.get_duals(), duals)
示例#27
0
    def test_jacobian(self):
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
        )
        block.set_external_model(ex_model)

        a = m.ex_block.inputs["input_0"]
        b = m.ex_block.inputs["input_1"]
        r = m.ex_block.inputs["input_2"]
        x = m.ex_block.inputs["input_3"]
        y = m.ex_block.inputs["input_4"]
        m.obj = pyo.Objective(expr=(x - 2.0)**2 + (y - 2.0)**2 + (a - 2.0)**2 +
                              (b - 2.0)**2 + (r - 2.0)**2)

        _add_linking_constraints(m)

        nlp = PyomoNLPWithGreyBoxBlocks(m)
        primals = np.array([0, 1, 2, 3, 4, 5, 6, 7])
        nlp.set_primals(primals)
        jac = nlp.evaluate_jacobian()

        # Variable and constraint orders (verified by previous test):
        # Rows:
        # [
        #     "linking_constraint[0]",
        #     "linking_constraint[1]",
        #     "linking_constraint[2]",
        #     "ex_block.residual_0",
        #     "ex_block.residual_1",
        # ]
        # Cols:
        # [
        #     "a",
        #     "b",
        #     "ex_block.inputs[input_0]",
        #     "ex_block.inputs[input_1]",
        #     "ex_block.inputs[input_2]",
        #     "ex_block.inputs[input_3]",
        #     "ex_block.inputs[input_4]",
        #     "r",
        # ]
        row = [
            0,
            0,
            1,
            1,
            2,
            2,
            3,
            3,
            3,
            3,
            3,
            4,
            4,
            4,
            4,
            4,
        ]
        col = [
            0,
            2,
            1,
            3,
            7,
            4,
            2,
            3,
            4,
            5,
            6,
            2,
            3,
            4,
            5,
            6,
        ]
        data = [
            1,
            -1,
            1,
            -1,
            1,
            -1,
            -0.16747094,
            -1.00068434,
            1.72383729,
            1,
            0,
            -0.30708535,
            -0.28546127,
            -0.25235924,
            0,
            1,
        ]
        self.assertEqual(len(row), len(jac.row))
        rcd_dict = dict(((i, j), val) for i, j, val in zip(row, col, data))
        for i, j, val in zip(jac.row, jac.col, jac.data):
            self.assertIn((i, j), rcd_dict)
            self.assertAlmostEqual(rcd_dict[i, j], val, delta=1e-8)
示例#28
0
    def test_hessian_2(self):
        # Test with duals different than vector of ones
        m = pyo.ConcreteModel()
        m.ex_block = ExternalGreyBoxBlock(concrete=True)
        block = m.ex_block

        m_ex = _make_external_model()
        input_vars = [m_ex.a, m_ex.b, m_ex.r, m_ex.x_out, m_ex.y_out]
        external_vars = [m_ex.x, m_ex.y]
        residual_cons = [m_ex.c_out_1, m_ex.c_out_2]
        external_cons = [m_ex.c_ex_1, m_ex.c_ex_2]
        ex_model = ExternalPyomoModel(
            input_vars,
            external_vars,
            residual_cons,
            external_cons,
        )
        block.set_external_model(ex_model)

        a = m.ex_block.inputs["input_0"]
        b = m.ex_block.inputs["input_1"]
        r = m.ex_block.inputs["input_2"]
        x = m.ex_block.inputs["input_3"]
        y = m.ex_block.inputs["input_4"]
        m.obj = pyo.Objective(expr=(x - 2.0)**2 + (y - 2.0)**2 + (a - 2.0)**2 +
                              (b - 2.0)**2 + (r - 2.0)**2)

        _add_nonlinear_linking_constraints(m)

        nlp = PyomoNLPWithGreyBoxBlocks(m)
        primals = np.array([0, 1, 2, 3, 4, 5, 6, 7])
        duals = np.array([4.4, -3.3, 2.2, -1.1, 0.0])
        nlp.set_primals(primals)
        nlp.set_duals(duals)
        hess = nlp.evaluate_hessian_lag()

        # Variable order (verified by a previous test):
        # [
        #     "a",
        #     "b",
        #     "ex_block.inputs[input_0]",
        #     "ex_block.inputs[input_1]",
        #     "ex_block.inputs[input_2]",
        #     "ex_block.inputs[input_3]",
        #     "ex_block.inputs[input_4]",
        #     "r",
        # ]
        row = [0, 1, 7]
        col = [0, 1, 7]
        # Data entries are influenced by multiplier values.
        data = [4.4 * 2.0, -3.3 * 2.0, 2.2 * 2.0]
        # ^ These variables only appear in linking constraints
        rcd_dict = dict(((i, j), val) for i, j, val in zip(row, col, data))

        # These are the coordinates of the Hessian corresponding to
        # external variables with true nonzeros. The coordinates have
        # terms due to objective, linking constraints, and external
        # constraints. Values were extracted from the external model
        # while writing this test, which is just meant to verify
        # that the different Hessians combined properly.
        ex_block_nonzeros = {
            (2, 2): 2.0 + 4.4 * (-1.0) + -1.1 * (-0.10967928),
            (2, 3): -1.1 * (-0.10684633),
            (3, 2): -1.1 * (-0.10684633),
            (2, 4): -1.1 * (0.19329898),
            (4, 2): -1.1 * (0.19329898),
            (3, 3): 2.0 + (-3.3) * (-1.0) + -1.1 * (-1.31592135),
            (3, 4): -1.1 * (1.13920361),
            (4, 3): -1.1 * (1.13920361),
            (4, 4): 2.0 + 2.2 * (-1.0) + -1.1 * (-1.0891866),
            (5, 5): 2.0,
            (6, 6): 2.0,
        }
        rcd_dict.update(ex_block_nonzeros)

        # Because "external Hessians" are computed by factorizing matrices,
        # we have dense blocks in the Hessian for now.
        ex_block_coords = [2, 3, 4, 5, 6]
        for i, j in itertools.product(ex_block_coords, ex_block_coords):
            row.append(i)
            col.append(j)
            if (i, j) not in rcd_dict:
                rcd_dict[i, j] = 0.0

        self.assertEqual(len(row), len(hess.row))
        for i, j, val in zip(hess.row, hess.col, hess.data):
            self.assertIn((i, j), rcd_dict)
            self.assertAlmostEqual(rcd_dict[i, j], val, delta=1e-8)