Ejemplo n.º 1
0
    def test_3x3_using_linear_regression(self):
        """ simple linear regression with two x columns, so 3x3 Hessian"""

        model = self._simple_model()
        solver = pe.SolverFactory("ipopt")
        status = solver.solve(model)
        self.assertTrue(check_optimal_termination(status))
        tstar = [
            pe.value(model.b0),
            pe.value(model.b['tofu']),
            pe.value(model.b['chard'])
        ]

        def _ndwrap(x):
            # wrapper for numdiff call
            model.b0.fix(x[0])
            model.b["tofu"].fix(x[1])
            model.b["chard"].fix(x[2])
            rval = pe.value(model.SSE)
            return rval

        H = nd.Hessian(_ndwrap)(tstar)
        HInv = np.linalg.inv(H)

        model.b0.fixed = False
        model.b["tofu"].fixed = False
        model.b["chard"].fixed = False
        status, H_inv_red_hess = inv_reduced_hessian_barrier(
            model, [model.b0, model.b["tofu"], model.b["chard"]])
        # this passes at decimal=6, BTW
        np.testing.assert_array_almost_equal(HInv, H_inv_red_hess, decimal=3)
Ejemplo n.º 2
0
    def test_covariance(self):

        from pyomo.contrib.interior_point.inverse_reduced_hessian import inv_reduced_hessian_barrier

        # Number of datapoints.
        # 3 data components (ca, cb, cc), 20 timesteps, 1 scenario = 60
        # In this example, this is the number of data points in data_df, but that's
        # only because the data is indexed by time and contains no additional inforamtion.
        n = 60

        # Compute covariance using parmest
        obj, theta, cov = self.pest_df.theta_est(calc_cov=True, cov_n=n)

        # Compute covariance using interior_point
        vars_list = [self.m_df.k1, self.m_df.k2]
        solve_result, inv_red_hes = inv_reduced_hessian_barrier(
            self.m_df, independent_variables=vars_list, tee=True)
        l = len(vars_list)
        cov_interior_point = 2 * obj / (n - l) * inv_red_hes
        cov_interior_point = pd.DataFrame(cov_interior_point, ['k1', 'k2'],
                                          ['k1', 'k2'])

        cov_diff = (cov - cov_interior_point).abs().sum().sum()

        self.assertTrue(cov.loc['k1', 'k1'] > 0)
        self.assertTrue(cov.loc['k2', 'k2'] > 0)
        self.assertAlmostEqual(cov_diff, 0, places=6)
Ejemplo n.º 3
0
    def test_with_binding_constraint(self):
        """ there is a binding constraint"""

        model = self._simple_model(add_constraint=True)

        status, H_inv_red_hess = inv_reduced_hessian_barrier(
            model, [model.b0, model.b["tofu"], model.b["chard"]])
        print("test_with_binding_constraint should see an error raised.")
Ejemplo n.º 4
0
    def test_invrh_zavala_thesis(self):
        m = pyo.ConcreteModel()
        m.x = pyo.Var([1,2,3]) 
        m.obj = pyo.Objective(expr=(m.x[1]-1)**2 + (m.x[2]-2)**2 + (m.x[3]-3)**2)
        m.c1 = pyo.Constraint(expr=m.x[1] + 2*m.x[2] + 3*m.x[3]==0)

        status, invrh = inv_reduced_hessian_barrier(m, [m.x[2], m.x[3]])
        expected_invrh = np.asarray([[ 0.35714286, -0.21428571],
                                     [-0.21428571, 0.17857143]])
        np.testing.assert_array_almost_equal(invrh, expected_invrh)
Ejemplo n.º 5
0
    def _Q_opt(self,
               ThetaVals=None,
               solver="ef_ipopt",
               return_values=[],
               bootlist=None,
               calc_cov=False):
        """
        Set up all thetas as first stage Vars, return resulting theta
        values as well as the objective function value.

        NOTE: If thetavals is present it will be attached to the
        scenario tree so it can be used by the scenario creation
        callback.  Side note (feb 2018, dlw): if you later decide to
        construct the tree just once and reuse it, then remember to
        remove thetavals from it when none is desired.
        """

        assert (solver != "k_aug" or ThetaVals == None)
        # Create a tree with dummy scenarios (callback will supply when needed).
        # Which names to use (i.e., numbers) depends on if it is for bootstrap.
        # (Bootstrap scenarios will use indirection through the bootlist)
        if bootlist is None:
            tree_model = _treemaker(self._numbers_list)
        else:
            tree_model = _treemaker(range(len(self._numbers_list)))
        stage1 = tree_model.Stages[1]
        stage2 = tree_model.Stages[2]
        tree_model.StageVariables[stage1] = self.theta_names
        tree_model.StageVariables[stage2] = []
        tree_model.StageCost[stage1] = "FirstStageCost"
        tree_model.StageCost[stage2] = "SecondStageCost"

        # Now attach things to the tree_model to pass them to the callback
        tree_model.CallbackModule = None
        tree_model.CallbackFunction = self._instance_creation_callback
        if ThetaVals is not None:
            tree_model.ThetaVals = ThetaVals
        if bootlist is not None:
            tree_model.BootList = bootlist
        tree_model.cb_data = self.callback_data  # None is OK

        stsolver = st.StochSolver(fsfile="pyomo.contrib.parmest.parmest",
                                  fsfct="_pysp_instance_creation_callback",
                                  tree_model=tree_model)

        # Solve the extensive form with ipopt
        if solver == "ef_ipopt":

            # Generate the extensive form of the stochastic program using pysp
            self.ef_instance = stsolver.make_ef()

            # need_gap is a holdover from solve_ef in rapper.py. Would we ever want
            # need_gap = True with parmest?
            need_gap = False

            assert not (
                need_gap and self.calc_cov
            ), "Calculating both the gap and reduced hessian (covariance) is not currently supported."

            if not calc_cov:
                # Do not calculate the reduced hessian

                solver = SolverFactory('ipopt')
                if self.solver_options is not None:
                    for key in self.solver_options:
                        solver.options[key] = self.solver_options[key]

                if need_gap:
                    solve_result = solver.solve(self.ef_instance,
                                                tee=self.tee,
                                                load_solutions=False)
                    if len(solve_result.solution) > 0:
                        absgap = solve_result.solution(0).gap
                    else:
                        absgap = None
                    self.ef_instance.solutions.load_from(solve_result)
                else:
                    solve_result = solver.solve(self.ef_instance, tee=self.tee)

            elif not asl_available:
                raise ImportError(
                    "parmest requires ASL to calculate the covariance matrix with solver 'ipopt'"
                )
            else:
                # parmest makes the fitted parameters stage 1 variables
                # thus we need to convert from var names (string) to
                # Pyomo vars
                ind_vars = []
                for v in self.theta_names:

                    #ind_vars.append(eval('ef.'+v))
                    ind_vars.append(
                        self.ef_instance.MASTER_BLEND_VAR_RootNode[v])

                # calculate the reduced hessian
                solve_result, inv_red_hes = inv_reduced_hessian_barrier(
                    self.ef_instance,
                    independent_variables=ind_vars,
                    solver_options=self.solver_options,
                    tee=self.tee)

            # Extract solution from pysp
            stsolver.scenario_tree.pullScenarioSolutionsFromInstances()
            stsolver.scenario_tree.snapshotSolutionFromScenarios(
            )  # update nodes

            if self.diagnostic_mode:
                print('    Solver termination condition = ',
                      str(solve_result.solver.termination_condition))

            # assume all first stage are thetas...
            thetavals = {}
            for name, solval in stsolver.root_Var_solution():
                thetavals[name] = solval

            objval = stsolver.root_E_obj()

            if calc_cov:
                # Calculate the covariance matrix

                # Extract number of data points considered
                n = len(self.callback_data)

                # Extract number of fitted parameters
                l = len(thetavals)

                # Assumption: Objective value is sum of squared errors
                sse = objval
                '''Calculate covariance assuming experimental observation errors are
                independent and follow a Gaussian 
                distribution with constant variance.
                
                The formula used in parmest was verified against equations (7-5-15) and
                (7-5-16) in "Nonlinear Parameter Estimation", Y. Bard, 1974.
                
                This formula is also applicable if the objective is scaled by a constant;
                the constant cancels out. (PySP scaled by 1/n because it computes an
                expected value.)
                '''
                cov = 2 * sse / (n - l) * inv_red_hes

            if len(return_values) > 0:
                var_values = []
                for exp_i in self.ef_instance.component_objects(
                        Block, descend_into=False):
                    vals = {}
                    for var in return_values:
                        exp_i_var = eval('exp_i.' + str(var))
                        temp = [pyo.value(_) for _ in exp_i_var.itervalues()]
                        if len(temp) == 1:
                            vals[var] = temp[0]
                        else:
                            vals[var] = temp
                    var_values.append(vals)
                var_values = pd.DataFrame(var_values)
                if calc_cov:
                    return objval, thetavals, var_values, cov
                else:
                    return objval, thetavals, var_values

            if calc_cov:
                return objval, thetavals, cov
            else:
                return objval, thetavals

        # Solve with sipopt and k_aug
        elif solver == "k_aug":
            # Just hope for the best with respect to degrees of freedom.

            model = stsolver.make_ef()
            stream_solver = True
            ipopt = SolverFactory('ipopt')
            sipopt = SolverFactory('ipopt_sens')
            kaug = SolverFactory('k_aug')

            #: ipopt suffixes  REQUIRED FOR K_AUG!
            model.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT_EXPORT)
            model.ipopt_zL_out = pyo.Suffix(direction=pyo.Suffix.IMPORT)
            model.ipopt_zU_out = pyo.Suffix(direction=pyo.Suffix.IMPORT)
            model.ipopt_zL_in = pyo.Suffix(direction=pyo.Suffix.EXPORT)
            model.ipopt_zU_in = pyo.Suffix(direction=pyo.Suffix.EXPORT)

            # declare the suffix to be imported by the solver
            model.red_hessian = pyo.Suffix(direction=pyo.Suffix.EXPORT)
            #: K_AUG SUFFIXES
            model.dof_v = pyo.Suffix(direction=pyo.Suffix.EXPORT)
            model.rh_name = pyo.Suffix(direction=pyo.Suffix.IMPORT)

            for vstrindex in range(len(self.theta_names)):
                vstr = self.theta_names[vstrindex]
                varobject = _ef_ROOT_node_Object_from_string(model, vstr)
                varobject.set_suffix_value(model.red_hessian, vstrindex + 1)
                varobject.set_suffix_value(model.dof_v, 1)

            #: rh_name will tell us which position the corresponding variable has on the reduced hessian text file.
            #: be sure to declare the suffix value (order)
            # dof_v is "degree of freedom variable"
            kaug.options[
                "compute_inv"] = ""  #: if the reduced hessian is desired.
            #: please check the inv_.in file if the compute_inv option was used

            #: write some options for ipopt sens
            with open('ipopt.opt', 'w') as f:
                f.write('compute_red_hessian yes\n'
                        )  #: computes the reduced hessian (sens_ipopt)
                f.write('output_file my_ouput.txt\n')
                f.write('rh_eigendecomp yes\n')
                f.close()
            #: Solve
            sipopt.solve(model, tee=stream_solver)
            with open('ipopt.opt', 'w') as f:
                f.close()

            ipopt.solve(model, tee=stream_solver)

            model.ipopt_zL_in.update(model.ipopt_zL_out)
            model.ipopt_zU_in.update(model.ipopt_zU_out)

            #: k_aug
            print('k_aug \n\n\n')
            #m.write('problem.nl', format=ProblemFormat.nl)
            kaug.solve(model, tee=stream_solver)
            HessDict = {}
            thetavals = {}
            print('k_aug red_hess')
            with open('result_red_hess.txt', 'r') as f:
                lines = f.readlines()
            # asseble the return values
            objval = model.MASTER_OBJECTIVE_EXPRESSION.expr()
            for i in range(len(lines)):
                HessDict[self.theta_names[i]] = {}
                linein = lines[i]
                print(linein)
                parts = linein.split()
                for j in range(len(parts)):
                    HessDict[self.theta_names[i]][self.theta_names[j]] = \
                        float(parts[j])
                # Get theta value (there is probably a better way...)
                vstr = self.theta_names[i]
                varobject = _ef_ROOT_node_Object_from_string(model, vstr)
                thetavals[self.theta_names[i]] = pyo.value(varobject)
            return objval, thetavals, HessDict

        else:
            raise RuntimeError("Unknown solver in Q_Opt=" + solver)