Example #1
0
        def update_bounds_lists(var_name):

            var_lb = None
            var_ub = None

            if var_data.fixed and self._output_fixed_variable_bounds:
                var_lb = var_ub = var_data.value
            elif var_data.fixed:
                # if we've been directed to not deal with fixed
                # variables, then skip - they should have been
                # compiled out of any description of the constraints
                return
            else:
                if not var_data.has_lb():
                    var_lb = -CPLEXDirect._cplex_module.infinity
                else:
                    var_lb = value(var_data.lb)

                if not var_data.has_ub():
                    var_ub = CPLEXDirect._cplex_module.infinity
                else:
                    var_ub= value(var_data.ub)

            var_cplex_id = self._cplex_variable_ids[var_name]

            new_lower_bounds.append((var_cplex_id, var_lb))
            new_upper_bounds.append((var_cplex_id, var_ub))
Example #2
0
    def cycle_ics_noisy(self, sigma_bar=0.0001):
        """Patches the initial conditions with the last result from the simulation with noise.
        Args:
            sigma_bar (float): The variance.
        Return
            None"""
        print("-" * 120)
        print("I[[cycle_ics]] Cycling initial state -- NOISY.")
        print("-" * 120)
        s = np.random.normal(0, sigma_bar)
        for x in self.states:
            x_ic = getattr(self.d1, x + "_ic")
            v_tgt = getattr(self.d1, x)
            for ks in x_ic.keys():
                if type(ks) != tuple:
                    ks = (ks, )
                x_ic[ks].value = value(v_tgt[(1, self.ncp_t) + ks])
                sigma = value(v_tgt[(1, self.ncp_t) + ks]) * s

                self.curr_state_noise[(x, ks)] = sigma
                tst_val = value(v_tgt[(1, self.ncp_t) + ks]) + sigma
                if tst_val < 0:
                    print("error", tst_val, x, ks)
                x_ic[ks].value = value(v_tgt[(1, self.ncp_t) + ks]) + sigma
        self._c_it += 1
Example #3
0
def instance2dat(instance, output_filename):

    output_file = open(output_filename, "w")

    for set_name, set_object in instance.component_map(Set,
                                                       active=True).items():
        if (set_object.initialize is not None) and (type(set_object.initialize)
                                                    is types.FunctionType):
            continue

        if (set_name.find("_index_set") == -1) and (set_name.find("_domain")
                                                    == -1):
            if set_object.dim() == 0:
                if len(set_object) == 0:
                    continue
                output_file.write("set " + set_name + " := \n")
                for element in set_object:
                    output_file.write(element, )
                output_file.write(";\n")
            elif set_object.dim() == 1:
                for index in set_object:
                    output_file.write("set " + set_name + "[\"" + str(index) +
                                      "\"]" + " :=")
                    for element in set_object[index]:
                        output_file.write(element, )
                    output_file.write(";\n")
            else:
                output_file.write(
                    "***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!\n")
                pass

            output_file.write("\n")

    for param_name, param_object in instance.component_map(
            Param, active=True).items():
        if (param_object._initialize is not None) and (type(
                param_object._initialize) is types.FunctionType):
            continue
        elif len(param_object) == 0:
            continue

        if None in param_object:
            output_file.write("param " + param_name + " := " +
                              str(value(param_object[None])) + " ;\n")
            output_file.write("\n")
        else:
            output_file.write("param " + param_name + " := \n")
            if param_object.dim() == 1:
                for index in param_object:
                    output_file.write(
                        str(index) + str(value(param_object[index])) + "\n")
            else:
                for index in param_object:
                    for i in index:
                        output_file.write(i, )
                    output_file.write(str(value(param_object[index])) + "\n")
            output_file.write(";\n")
            output_file.write("\n")

    output_file.close()
Example #4
0
        def update_bounds_lists(var_name):

            var_lb = None
            var_ub = None

            if var_data.fixed and self._output_fixed_variable_bounds:
                var_lb = var_ub = var_data.value
            elif var_data.fixed:
                # if we've been directed to not deal with fixed
                # variables, then skip - they should have been
                # compiled out of any description of the constraints
                return
            else:
                if var_data.lb is None:
                    var_lb = -cplex.infinity
                else:
                    var_lb = value(var_data.lb)

                if var_data.ub is None:
                    var_ub = cplex.infinity
                else:
                    var_ub= value(var_data.ub)

            var_cplex_id = self._cplex_variable_ids[var_name]

            new_lower_bounds.append((var_cplex_id, var_lb))
            new_upper_bounds.append((var_cplex_id, var_ub))
Example #5
0
    def _simulate_with_casadi_no_inputs(self, initcon, tsim, integrator,
                                        integrator_options):
        # Old way (10 times faster, but can't incorporate time
        # varying parameters/controls)
        xalltemp = [self._templatemap[i] for i in self._diffvars]
        xall = casadi.vertcat(*xalltemp)

        odealltemp = [value(self._rhsdict[i]) for i in self._derivlist]
        odeall = casadi.vertcat(*odealltemp)
        dae = {'x': xall, 'ode': odeall}

        if len(self._algvars) != 0:
            zalltemp = [self._templatemap[i] for i in self._simalgvars]
            zall = casadi.vertcat(*zalltemp)

            algalltemp = [value(i) for i in self._alglist]
            algall = casadi.vertcat(*algalltemp)
            dae['z'] = zall
            dae['alg'] = algall

        integrator_options['grid'] = tsim
        integrator_options['output_t0'] = True
        F = casadi.integrator('F', integrator, dae, integrator_options)
        sol = F(x0=initcon)
        profile = sol['xf'].full().T

        if len(self._algvars) != 0:
            algprofile = sol['zf'].full().T
            profile = np.concatenate((profile, algprofile), axis=1)

        return [tsim, profile]
Example #6
0
    def patch_meas_mhe(self, t, **kwargs):
        """Mechanism to assign a value of y0 to the current mhe from the dynamic model
        Args:
            t (int): int The current collocation point
        Returns:
            meas_dict (dict): A dictionary containing the measurements list by meas_var
        """
        src = kwargs.pop("src", None)
        skip_update = kwargs.pop("skip_update", False)
        noisy = kwargs.pop("noisy", True)

        meas_dic = dict.fromkeys(self.y)
        l = []
        for i in self.y:
            lm = []
            var = getattr(src, i)
            for j in self.y_vars[i]:
                lm.append(value(var[(1, self.ncp_t,) + j]))
                l.append(value(var[(1, self.ncp_t,) + j]))
            meas_dic[i] = lm

        if not skip_update:  #: Update the mhe model
            self.journalizer("I", self._c_it, "patch_meas_mhe", "Measurement patched to " + str(t))
            y0dest = getattr(self.lsmhe, "yk0_mhe")
            # print("there is an update", file=sys.stderr)
            for i in self.y:
                for j in self.y_vars[i]:
                    k = self.yk_key[(i, j)]
                    #: Adding noise to the mhe measurement
                    y0dest[t, k].value = l[k] + self.curr_m_noise[(i, j)] if noisy else l[k]
        return meas_dic
Example #7
0
def instance2dat(instance, output_filename):

    output_file = open(output_filename, "w")

    for set_name, set_object in iteritems(
            instance.component_map(Set, active=True)):
        if (set_object.initialize is not None) and (type(set_object.initialize)
                                                    is types.FunctionType):
            continue

        if (set_name.find("_index") == -1) and (set_name.find("_domain")
                                                == -1):
            if set_object.dim() == 0:
                if len(set_object) == 0:
                    continue
                print >> output_file, "set " + set_name + " := "
                for element in set_object:
                    print >> output_file, element
                print >> output_file, ";"
            elif set_object.dim() == 1:
                for index in set_object:
                    print >> output_file, "set " + set_name + "[\"" + str(
                        index) + "\"]" + " := ",
                    for element in set_object[index]:
                        print >> output_file, element,
                    print >> output_file, ";"
            else:
                print >> output_file, "***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!"
                pass

            print >> output_file, ""

    for param_name, param_object in iteritems(
            instance.component_map(Param, active=True)):
        if (param_object._initialize is not None) and (type(
                param_object._initialize) is types.FunctionType):
            continue
        elif len(param_object) == 0:
            continue

        if None in param_object:
            print >> output_file, "param " + param_name + " := " + str(
                value(param_object[None])) + " ;"
            print >> output_file, ""
        else:
            print >> output_file, "param " + param_name + " := "
            if param_object.dim() == 1:
                for index in param_object:
                    print >> output_file, index, str(value(
                        param_object[index]))
            else:
                for index in param_object:
                    for i in index:
                        print >> output_file, i,
                    print >> output_file, str(value(param_object[index]))
            print >> output_file, ";"
            print >> output_file, ""

    output_file.close()
Example #8
0
 def adjust_nu0_mhe(self):
     """Adjust the initial guess for the nu variable"""
     for t in self.lsmhe.fe_t:
         k = 0
         for i in self.y:
             for j in self.y_vars[i]:
                 target = value(self.lsmhe.yk0_mhe[t, k]) - value(self.yk_l[t][k])
                 self.lsmhe.nuk_mhe[t, k].set_value(target)
                 k += 1
Example #9
0
 def shift_measurement_input_mhe(self):
     """Shifts current measurements for the mhe problem"""
     y0 = getattr(self.lsmhe, "yk0_mhe")
     for i in range(2, self.nfe_t + 1):
         for j in self.lsmhe.yk0_mhe.keys():
             y0[i-1, j[1:]].value = value(y0[i, j[1:]])
         for u in self.u:
             umhe = getattr(self.lsmhe, u)
             umhe[i-1] = value(umhe[i])
     self.adjust_nu0_mhe()
Example #10
0
 def compute_y_offset(self, noisy=True):
     mhe_y = getattr(self.lsmhe, "yk0_mhe")
     for y in self.y:
         plant_y = getattr(self.d1, y)
         for j in self.y_vars[y]:
             k = self.yk_key[(y, j)]
             mhe_yval = value(mhe_y[self.nfe_t, k])
             plant_yval = value(plant_y[(1, self.ncp_t) + j])
             y_noise = self.curr_m_noise[(y, j)] if noisy else 0.0
             self.curr_y_offset[(y, j)] = mhe_yval - plant_yval - y_noise
Example #11
0
    def get_result(self, name, index, state, start_time):
        obj = self.block.find_component(name)

        result = []

        if obj is None:
            raise Exception(
                '{} is not a valid parameter or variable of {}'.format(
                    name, self.name))

        time = self.get_time_axis(state)

        if isinstance(obj, IndexedVar) and self.repr_days is None:
            if index is None:
                for i in obj:
                    result.append(value(obj[i]))

                resname = self.name + '.' + name

            else:
                for i in time:
                    result.append(obj[(index, i)].value)

                    resname = self.name + '.' + name + '.' + index
        elif isinstance(obj, IndexedVar) and self.repr_days is not None:
            for d in self.DAYS_OF_YEAR:
                for t in time:
                    result.append(value(obj[t, self.repr_days[d]]))

                    resname = self.name + '.' + name

        elif isinstance(obj, IndexedParam):
            resname = self.name + '.' + name
            if self.repr_days is None:
                result = []
                for t in obj:
                    result.append(value(obj[t]))

            else:
                for d in self.DAYS_OF_YEAR:
                    for t in time:
                        result.append(value(obj[t, self.repr_days[d]]))

        else:
            self.logger.warning(
                '{}.{} was a different type of variable/parameter than what has been implemented: '
                '{}'.format(self.name, name, type(obj)))
            return None

        timeindex = pd.DatetimeIndex(start=start_time,
                                     freq=str(self.params['time_step'].v()) +
                                     'S',
                                     periods=len(result))

        return pd.Series(data=result, index=timeindex, name=resname)
Example #12
0
def _collect_linear_pow(exp, idMap, multiplier, coef, varmap, compute_values):

    if exp.is_fixed():
        if compute_values:
            coef[None] += multiplier * value(exp)
        else:
            coef[None] += multiplier * exp
    elif value(exp._args[1]) == 1:
        arg = exp._args[0]
        _linear_collectors[arg.__class__](arg, idMap, multiplier, coef, varmap, compute_values)
    else:
        raise TypeError( "Unsupported power expression: "+str(exp._args) )
Example #13
0
 def equalize_u(self, direction="u_to_r"):
     """set current controls to the values of their respective dummies"""
     if direction == "u_to_r":
         for i in iterkeys(self.Rec):
             self.Rec[i].set_value(value(self.u1[i]))
         for i in iterkeys(self.Rec):
             self.Qr[i].set_value(value(self.u2[i]))
     elif direction == "r_to_u":
         for i in iterkeys(self.u1):
             self.u1[i].value = value(self.Rec[i])
         for i in iterkeys(self.u2):
             self.u2[i].value = value(self.Qr[i])
Example #14
0
 def adjust_w_mhe(self):
     for i in range(1, self.nfe_t):
         j = 0
         for x in self.x_noisy:
             x_var = getattr(self.lsmhe, x)
             for k in self.x_vars[x]:
                 x1pvar_val = value(x_var[(i+1, 0), k])
                 x1var_val = value(x_var[(i, self.ncp_t), k])
                 if self.IgnoreProcessNoise:
                     pass
                 else:
                     self.lsmhe.wk_mhe[i, j].set_value(x1pvar_val - x1var_val)
                 j += 1
Example #15
0
def instance2dat(instance, output_filename):

    output_file = open(output_filename,"w")

    for set_name, set_object in iteritems(instance.component_map(Set, active=True)):
        if (set_object.initialize is not None) and (type(set_object.initialize) is types.FunctionType):
            continue

        if (set_name.find("_index") == -1) and (set_name.find("_domain") == -1):
            if set_object.dim() == 0:
                if len(set_object) == 0:
                    continue
                print >>output_file, "set "+set_name+" := "
                for element in set_object:
                    print >>output_file, element
                print >>output_file, ";"
            elif set_object.dim() == 1:
                for index in set_object:
                    print >>output_file, "set "+set_name+"[\""+str(index)+"\"]"+" := ",
                    for element in set_object[index]:
                        print >>output_file, element,
                    print >>output_file, ";"
            else:
                print >>output_file, "***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!"
                pass

            print >>output_file, ""

    for param_name, param_object in iteritems(instance.component_map(Param, active=True)):
        if (param_object._initialize is not None) and (type(param_object._initialize) is types.FunctionType):
            continue
        elif len(param_object) == 0:
            continue

        if None in param_object:
            print >>output_file, "param "+param_name+" := "+str(value(param_object[None]))+" ;"
            print >>output_file, ""
        else:
            print >>output_file, "param "+param_name+" := "
            if param_object.dim() == 1:
                for index in param_object:
                    print >>output_file, index, str(value(param_object[index]))
            else:
                for index in param_object:
                    for i in index:
                        print >>output_file, i,
                    print >>output_file, str(value(param_object[index]))
            print >>output_file, ";"
            print >>output_file, ""

    output_file.close()
Example #16
0
    def solve_ss(self):
        """Solves steady state model
        Args:
            None
        Return:
            None"""
        # self.k_aug.solve(self.ss, tee=True, symbolic_solver_labels=True)
        with open("ipopt.opt", "w") as f:
            f.write("max_iter 100\n")
            f.write("mu_init 1e-08\n")
            f.write("bound_push 1e-08\n")
            f.write("print_info_string yes\n")
            f.close()
        ip = SolverFactory("ipopt")
        # ip.options["halt_on_ampl_error"] = "yes"
        ip.options["print_user_options"] = "yes"
        ip.options["linear_solver"] = "ma57"
        results = ip.solve(self.ss,
                           tee=True,
                           symbolic_solver_labels=True,
                           report_timing=True)
        self.ss.solutions.load_from(results)
        for x in self.states:
            self.state_vars[x] = []
            try:
                xv = getattr(self.ss, x)
            except AttributeError:  # delete this
                continue
            for j in xv.keys():
                if xv[j].stale:
                    continue
                if type(j[2:]) == tuple:
                    self.state_vars[x].append(j[2:])
                else:
                    self.state_vars[x].append((j[2:], ))

        for x in self.states:
            try:
                xvar = getattr(self.ss, x)
            except AttributeError:  # delete this
                continue
            for j in self.state_vars[x]:
                self.curr_state_offset[(x, j)] = 0.0
                self.curr_state_noise[(x, j)] = 0.0
                self.curr_estate[(x, j)] = value(xvar[1, 1, j])
                self.curr_rstate[(x, j)] = value(xvar[1, 1, j])
                self.curr_state_target[(x, j)] = value(xvar[1, 1, j])
        for u in self.u:
            uvar = getattr(self.ss, u)
            self.curr_u_target[u] = value(uvar[1])
            self.curr_u[u] = value(uvar[1])
Example #17
0
 def load_input_mhe(self, src_kind, **kwargs):
     """Loads inputs into the mhe model"""
     src = kwargs.pop("src", self.d1)
     fe = kwargs.pop("fe", 1)
     # src_kind = kwargs.pop("src_kind", "mod")
     if src_kind == "mod":
         for u in self.u:
             usrc = getattr(src, u)
             utrg = getattr(self.lsmhe, u)
             utrg[fe].value = value(usrc[1])
     elif src_kind == "self.dict":
         for u in self.u:
             utrg = getattr(self.lsmhe, u)
             utrg[fe].value = value(self.curr_u[u])
Example #18
0
 def __iadd__(self, other):
     _type = other.__class__
     if _type in native_numeric_types:
         self.constant += other
     elif _type is CompiledLinearCanonicalRepn:
         self.constant += other.constant
         for v in other.variables:
             _id = id(v)
             if _id in self.linear:
                 self.linear[_id] += other.linear[_id]
             else:
                 self.variables.append(v)
                 self.linear[_id] = other.linear[_id]
         CompiledLinearCanonicalRepn_Pool.append(other)
     elif other.is_fixed():
         self.constant += value(other)
     else:
         assert isinstance(other, _VarData)
         _id = id(other)
         if _id in self.linear:
             self.linear[_id] += 1.
         else:
             self.variables.append(other)
             self.linear[_id] = 1.
     return self
Example #19
0
    def update_state_mhe(self, as_nmpc_mhe_strategy=False):
        # Improvised strategy
        if as_nmpc_mhe_strategy:
            self.journalizer("I", self._c_it, "update_state_mhe", "offset ready for asnmpcmhe")
            for x in self.states:
                xvar = getattr(self.lsmhe, x)
                x0 = getattr(self.olnmpc, x + "_ic")
                for j in self.state_vars[x]:
                    # self.curr_state_offset[(x, j)] = self.curr_estate[(x, j)] - value(xvar[self.nfe_t, self.ncp_t, j])
                    self.curr_state_offset[(x, j)] = value(x0[j] )- value(xvar[self.nfe_t, self.ncp_t, j])
                    print("state !", self.curr_state_offset[(x, j)])

        for x in self.states:
            xvar = getattr(self.lsmhe, x)
            for j in self.state_vars[x]:
                self.curr_estate[(x, j)] = value(xvar[self.nfe_t, self.ncp_t, j])
Example #20
0
    def get_objective(self, objtype=None, get_value=True):
        """
        Return value of objective function. With no argument supplied, the active objective is returned. Otherwise, the
        objective specified in the argument is returned.

        :param objtype: Name of the objective to be returned. Default None: returns the active objective.
        :param value: True if value of objective should be returned. If false, the objective object instance is returned.
        :return:
        """
        if objtype is None:
            # Find active objective
            if self.act_objective is not None:
                obj = self.act_objective
            else:
                raise ValueError('No active objective found.')

        else:
            assert objtype in self.objectives.keys(
            ), 'Requested objective does not exist. Please choose from {}'.format(
                self.objectives.keys())
            obj = self.objectives[objtype]

        if get_value:
            return value(obj)
        else:
            return obj
Example #21
0
    def __imul__(self, other):
        _type = other.__class__
        if _type in native_numeric_types:
            pass
        elif _type is CompiledLinearCanonicalRepn:
            if other.variables:
                self, other = other, self
            assert(not other.variables)
            CompiledLinearCanonicalRepn_Pool.append(other)
            other = other.constant
        elif other.is_fixed():
            other = value(other)
        else:
            assert isinstance(other, _VarData)
            assert not self.variables
            self.variables.append(other)
            self.linear[id(other)] = self.constant
            self.constant = 0.
            return self

        if other:
            for _id in self.linear:
                self.linear[_id] *= other
        else:
            self.linear = {}
            self.variables = []
        self.constant *= other
        return self
Example #22
0
def check_expr(expr, name, solver_io):
    # Check if GAMS will encounter domain violations in presolver
    # operations at current values, which are None (0) by default
    # Used to handle log and log10 violations, for example
    try:
        value(expr)
    except ValueError:
        logger.warning("While evaluating model.%s's expression, GAMS solver "
                       "encountered an error.\nGAMS requires that all "
                       "equations and expressions evaluate at initial values.\n"
                       "Ensure variable values do not violate any domains, "
                       "and use the warmstart=True keyword to solve()." % name)
        if solver_io == 'shell':
            # For shell, there is no previous exception to worry about
            # overwriting, so raise the ValueError.
            # But for direct, the GamsExceptionExecution will be raised.
            raise
Example #23
0
    def propagate_solution(self, scaled_model, original_model):
        """
        This method takes the solution in scaled_model and maps it back to the original model.

        It will also transform duals and reduced costs if the suffixes 'dual' and/or 'rc' are present.
        The :code:`scaled_model` argument must be a model that was already scaled using this transformation
        as it expects data from the transformation to perform the back mapping.

        Parameters
        ----------
        scaled_model : Pyomo Model
           The model that was previously scaled with this transformation
        original_model : Pyomo Model
           The original unscaled source model

        """
        if not hasattr(scaled_model, 'component_scaling_factor_map'):
            raise AttributeError('ScaleModel:propagate_solution called with scaled_model that does not '
                                 'have a component_scaling_factor_map. It is possible this method was called '
                                 'using a model that was not scaled with the ScaleModel transformation')
        if not hasattr(scaled_model, 'scaled_component_to_original_name_map'):
            raise AttributeError('ScaleModel:propagate_solution called with scaled_model that does not '
                                 'have a scaled_component_to_original_name_map. It is possible this method was called '
                                 'using a model that was not scaled with the ScaleModel transformation')

        component_scaling_factor_map = scaled_model.component_scaling_factor_map
        scaled_component_to_original_name_map = scaled_model.scaled_component_to_original_name_map

        # get the objective scaling factor
        scaled_objectives = list(scaled_model.component_data_objects(ctype=Objective, active=True, descend_into=True))
        if len(scaled_objectives) != 1:
            raise NotImplementedError(
                'ScaleModel.propagate_solution requires a single active objective function, but %d objectives found.' % (
                    len(objectives)))
        objective_scaling_factor = component_scaling_factor_map[scaled_objectives[0]]

        # transfer the variable values and reduced costs
        check_reduced_costs = type(scaled_model.component('rc')) is Suffix
        for scaled_v in scaled_model.component_objects(ctype=Var, descend_into=True):
            # get the unscaled_v from the original model
            original_v_path = scaled_component_to_original_name_map[scaled_v]
            original_v = original_model.find_component(original_v_path)

            for k in scaled_v:
                original_v[k].value = value(scaled_v[k]) / component_scaling_factor_map[scaled_v[k]]
                if check_reduced_costs and scaled_v[k] in scaled_model.rc:
                    original_model.rc[original_v[k]] = scaled_model.rc[scaled_v[k]] * component_scaling_factor_map[
                        scaled_v[k]] / objective_scaling_factor

        # transfer the duals
        if type(scaled_model.component('dual')) is Suffix and type(original_model.component('dual')) is Suffix:
            for scaled_c in scaled_model.component_objects(ctype=Constraint, descend_into=True):
                original_c = original_model.find_component(scaled_component_to_original_name_map[scaled_c])

                for k in scaled_c:
                    original_model.dual[original_c[k]] = scaled_model.dual[scaled_c[k]] * component_scaling_factor_map[
                        scaled_c[k]] / objective_scaling_factor
Example #24
0
    def propagate_solution(self, scaled_model, original_model):
        """
        This method takes the solution in scaled_model and maps it back to the original model.

        It will also transform duals and reduced costs if the suffixes 'dual' and/or 'rc' are present.
        The :code:`scaled_model` argument must be a model that was already scaled using this transformation
        as it expects data from the transformation to perform the back mapping.

        Parameters
        ----------
        scaled_model : Pyomo Model
           The model that was previously scaled with this transformation
        original_model : Pyomo Model
           The original unscaled source model

        """
        if not hasattr(scaled_model, 'component_scaling_factor_map'):
            raise AttributeError('ScaleModel:propagate_solution called with scaled_model that does not '
                                 'have a component_scaling_factor_map. It is possible this method was called '
                                 'using a model that was not scaled with the ScaleModel transformation')
        if not hasattr(scaled_model, 'scaled_component_to_original_name_map'):
            raise AttributeError('ScaleModel:propagate_solution called with scaled_model that does not '
                                 'have a scaled_component_to_original_name_map. It is possible this method was called '
                                 'using a model that was not scaled with the ScaleModel transformation')

        component_scaling_factor_map = scaled_model.component_scaling_factor_map
        scaled_component_to_original_name_map = scaled_model.scaled_component_to_original_name_map

        # get the objective scaling factor
        scaled_objectives = list(scaled_model.component_data_objects(ctype=Objective, active=True, descend_into=True))
        if len(scaled_objectives) != 1:
            raise NotImplementedError(
                'ScaleModel.propagate_solution requires a single active objective function, but %d objectives found.' % (
                    len(objectives)))
        objective_scaling_factor = component_scaling_factor_map[scaled_objectives[0]]

        # transfer the variable values and reduced costs
        check_reduced_costs = type(scaled_model.component('rc')) is Suffix
        for scaled_v in scaled_model.component_objects(ctype=Var, descend_into=True):
            # get the unscaled_v from the original model
            original_v_path = scaled_component_to_original_name_map[scaled_v]
            original_v = original_model.find_component(original_v_path)

            for k in scaled_v:
                original_v[k].value = value(scaled_v[k]) / component_scaling_factor_map[scaled_v[k]]
                if check_reduced_costs and scaled_v[k] in scaled_model.rc:
                    original_model.rc[original_v[k]] = scaled_model.rc[scaled_v[k]] * component_scaling_factor_map[
                        scaled_v[k]] / objective_scaling_factor

        # transfer the duals
        if type(scaled_model.component('dual')) is Suffix and type(original_model.component('dual')) is Suffix:
            for scaled_c in scaled_model.component_objects(ctype=Constraint, descend_into=True):
                original_c = original_model.find_component(scaled_component_to_original_name_map[scaled_c])

                for k in scaled_c:
                    original_model.dual[original_c[k]] = scaled_model.dual[scaled_c[k]] * component_scaling_factor_map[
                        scaled_c[k]] / objective_scaling_factor
Example #25
0
def _collect_linear_intrinsic(exp, idMap, multiplier, coef, varmap, compute_values):

    if exp.is_fixed():
        if compute_values:
            coef[None] += multiplier * value(exp)
        else:
            coef[None] += multiplier * exp
    else:
        raise TypeError( "Unsupported intrinsic expression: %s: %s" % (exp, str(exp._args)) )
Example #26
0
def _collect_identity(exp, idMap, multiplier, coef, varmap, compute_values):
    exp = exp.expr
    if exp.is_fixed():
        if compute_values:
            coef[None] += multiplier * value(exp)
        else:
            coef[None] += multiplier * exp
    else:
        _linear_collectors[exp.__class__](exp, idMap, multiplier, coef, varmap, compute_values)
Example #27
0
        def visiting_potential_leaf(self, node):
            """ 
            Visiting a potential leaf.

            Return True if the node is not expanded.
            """
            if node.__class__ in native_numeric_types:
                return True, node

            if node.__class__ is casadi.SX:
                return True, node

            if node.is_variable_type():
                return True, value(node)

            if not node.is_expression_type():
                return True, value(node)

            return False, None
Example #28
0
            def _temp_bal_incoming(b, t, l):

                incoming_comps = collections.defaultdict(list)
                incoming_pipes = collections.defaultdict(list)

                for name, comp in c.items():
                    if value(comp.get_mflo(t)) >= 0:
                        incoming_comps['supply'].append(name)
                    else:
                        incoming_comps['return'].append(name)

                for name, pipe in p.items():
                    if value(pipe.get_edge_mflo(self.name, t)) >= 0:
                        incoming_pipes['supply'].append(name)
                    else:
                        incoming_pipes['return'].append(name)
                # Zero mass flow rate:
                if value(
                        sum(c[comp].get_mflo(t) for comp in incoming_comps[l]) + \
                        sum(p[pipe].get_edge_mflo(self.name, t) for pipe in
                            incoming_pipes[l])) == 0:
                    # mixed temperature is average of all joined pipes, actual value should not matter,
                    # because packages in pipes of this time step will have zero size and components do not take over
                    # mixed temperature in case there is no mass flow

                    return b.mix_temp[
                        t, l] == (sum(c[comp].get_temperature(t, l)
                                      for comp in c) +
                                  sum(p[pipe].get_temperature(self.name, t, l)
                                      for pipe in p)) / (len(p) + len(c))

                else:  # mass flow rate through the node
                    return (sum(
                        c[comp].get_mflo(t) for comp in incoming_comps[l]) +
                            sum(p[pipe].get_edge_mflo(self.name, t) for pipe in
                                incoming_pipes[l])) * b.mix_temp[t, l] == \
                           sum(c[comp].get_mflo(t) * c[comp].get_temperature(t,
                                                                             l)
                               for comp in incoming_comps[l]) + \
                           sum(p[pipe].get_edge_mflo(self.name, t) * p[
                               pipe].get_edge_temperature(self.name, t, l)
                               for pipe in incoming_pipes[l])
Example #29
0
def _collect_linear_prod(exp, idMap, multiplier, coef, varmap, compute_values):

    multiplier *= exp._coef
    _coef = {None: 0}
    _varmap = {}

    for subexp in exp._denominator:
        if compute_values:
            x = value(
                subexp)  # only have constants/fixed terms in the denominator.
            if x == 0:
                logger.error(
                    "Divide-by-zero: offending sub-expression:\n   %s" %
                    str(subexp))
                raise ZeroDivisionError
            multiplier /= x
        else:
            multiplier /= subexp

    for subexp in exp._numerator:
        if _varmap:
            if compute_values:
                multiplier *= value(subexp)
            else:
                multiplier *= subexp
        else:
            _get_linear_collector(subexp, idMap, 1, _coef, _varmap,
                                  compute_values)
            if not _varmap:
                multiplier *= _coef[None]
                _coef[None] = 0

    if _varmap:
        for key, val in iteritems(_coef):
            if key in coef:
                coef[key] += multiplier * val
            else:
                coef[key] = multiplier * val
        varmap.update(_varmap)
    else:
        # constant expression; i.e. 1/x
        coef[None] += multiplier
Example #30
0
 def cycle_ics(self, plant_step=False):
     """Patches the initial conditions with the last result from the simulation
     Args:
         None
     Return
         None"""
     print("-" * 120)
     print("I[[cycle_ics]] Cycling initial state.")
     print("-" * 120)
     for x in self.states:
         x_ic = getattr(self.d1, x + "_ic")
         v_tgt = getattr(self.d1, x)
         for ks in x_ic.keys():
             if type(ks) != tuple:
                 ks = (ks, )
             x_ic[ks].value = value(v_tgt[(1, self.ncp_t) + ks])
             v_tgt[(1, 0) + ks].set_value(value(v_tgt[(1, self.ncp_t) +
                                                      ks]))
     if plant_step:
         self._c_it += 1
Example #31
0
 def update_noise_meas(self, mod, cov_dict):
     self.journalizer("I", self._c_it, "introduce_noise_meas", "Noise introduction")
     # f = open("m0.txt", "w")
     # f1 = open("m1.txt", "w")
     for y in self.y:
         vy = getattr(mod,  y)
         # vy.display(ostream=f)
         for j in self.y_vars[y]:
             vv = value(vy[(1, self.ncp_t) + j])
             sigma = cov_dict[(y, j), (y, j), 1]
             self.curr_m_noise[(y, j)] = np.random.normal(0, sigma)
Example #32
0
 def initialize_xreal(self, ref):
     """Wanted to keep the states in a horizon-like window, this should be done in the main dyngen class"""
     dum = self.d_mod(1, self.ncp_t, _t=self.hi_t)
     dum.name = "Dummy [xreal]"
     self.load_d_d(ref, dum, 1)
     for fe in range(1, self._window_keep):
         for i in self.states:
             pn = i + "_ic"
             p = getattr(dum, pn)
             vs = getattr(dum, i)
             for ks in p.iterkeys():
                 p[ks].value = value(vs[(1, self.ncp_t) + (ks,)])
         #: Solve
         self.solve_d(dum, o_tee=False)
         for i in self.states:
             self.xreal_W[(i, fe)] = []
             xs = getattr(dum, i)
             for k in xs.keys():
                 if k[1] == self.ncp_t:
                     print(i)
                     self.xreal_W[(i, fe)].append(value(xs[k]))
Example #33
0
def _collect_linear_prod(exp, idMap, multiplier, coef, varmap, compute_values):

    multiplier *= exp._coef
    _coef = { None : 0 }
    _varmap = {}

    for subexp in exp._denominator:
        if compute_values:
            x = value(subexp) # only have constants/fixed terms in the denominator.
            if x == 0:
                buf = StringIO()
                subexp.pprint(buf)
                logger.error("Divide-by-zero: offending sub-expression:\n   " + buf)
                raise ZeroDivisionError
            multiplier /= x
        else:
            multiplier /= subexp

    for subexp in exp._numerator:
        if _varmap:
            if compute_values:
                multiplier *= value(subexp)
            else:
                multiplier *= subexp
        else:
            _linear_collectors[subexp.__class__](subexp, idMap, 1, _coef, _varmap, compute_values)
            if not _varmap:
                multiplier *= _coef[None]
                _coef[None] = 0

    if _varmap:
        for key, val in iteritems(_coef):
            if key in coef:
                coef[key] += multiplier * val
            else:
                coef[key] = multiplier * val
        varmap.update(_varmap)
    else:
        # constant expression; i.e. 1/x
        coef[None] += multiplier
Example #34
0
def coopr3_generate_canonical_repn(exp, idMap=None, compute_values=True):
    if exp is None:
        return CompiledLinearCanonicalRepn()
    degree = exp.polynomial_degree()

    if idMap is None:
        idMap = {}
    idMap.setdefault(None, {})

    if degree == 0:
        ans = CompiledLinearCanonicalRepn()
        ans.constant = value(exp)
        return ans

    elif degree == 1:
        # varmap is a map from the variable id() to a _VarData.
        # coef is a map from the variable id() to its coefficient.
        coef, varmap = collect_linear_canonical_repn(exp, idMap,
                                                     compute_values)
        ans = CompiledLinearCanonicalRepn()
        if None in coef:
            val = coef.pop(None)
            if type(val) not in [int, float] or val != 0.0:
                ans.constant = val

        # the six module is inefficient in terms of wrapping iterkeys
        # and itervalues, in the context of Python 2.7. use the native
        # dictionary methods where possible.
        if using_py3:
            ans.linear = tuple(itervalues(coef))
            ans.variables = tuple(varmap[var_hash]
                                  for var_hash in iterkeys(coef))
        else:
            ans.linear = tuple(coef.itervalues())
            ans.variables = tuple(varmap[var_hash]
                                  for var_hash in coef.iterkeys())
        return ans

    # **Py3k: degree > 1 comparision will error if degree is None
    elif degree and degree > 1:
        ans = collect_general_canonical_repn(exp, idMap, compute_values)
        if 1 in ans:
            linear_terms = {}
            for key, coef in iteritems(ans[1]):
                linear_terms[list(key.keys())[0]] = coef
            ans[1] = linear_terms
        return GeneralCanonicalRepn(ans)
    else:
        return GeneralCanonicalRepn({
            None: exp,
            -1: collect_variables(exp, idMap)
        })
Example #35
0
 def get_table(self):
     tmp = []
     if not self.options.columns is None:
         tmp.append(self.options.columns)
     if not self.options.set is None:
         # Create column names
         if self.options.columns is None:
             cols = []
             for i in xrange(self.options.set.dimen):
                 cols.append(self.options.set.local_name+str(i))
             tmp.append(cols)
         # Get rows
         if not self.options.sort is None:
             for data in sorted(self.options.set):
                 if self.options.set.dimen > 1:
                     tmp.append(list(data))
                 else:
                     tmp.append([data])
         else:
             for data in self.options.set:
                 if self.options.set.dimen > 1:
                     tmp.append(list(data))
                 else:
                     tmp.append([data])
     elif not self.options.param is None:
         if type(self.options.param) in (list,tuple):
             _param = self.options.param
         else:
             _param = [self.options.param]
         tmp = []
         # Collect data
         for index in _param[0]:
             if index is None:
                 row = []
             elif type(index) in (list,tuple):
                 row = list(index)
             else:
                 row = [index]
             for param in _param:
                 row.append(value(param[index]))
             tmp.append(row)
         # Create column names
         if self.options.columns is None:
             cols = []
             for i in xrange(len(tmp[0])-len(_param)):
                 cols.append('I'+str(i))
             for param in _param:
                 cols.append(param)
             tmp = [cols] + tmp
     return tmp
Example #36
0
File: GDPbb.py Project: Pyomo/pyomo
    def subproblem_solve(gdp, solver, config):
        subproblem = gdp.clone()
        TransformationFactory('gdp.fix_disjuncts').apply_to(subproblem)

        result = solver.solve(subproblem, **config.solver_args)
        main_obj = next(subproblem.component_data_objects(Objective, active=True))
        obj_sign = 1 if main_obj.sense == minimize else -1
        if (result.solver.status is SolverStatus.ok and
                result.solver.termination_condition is tc.optimal):
            return value(main_obj.expr), result, subproblem.GDPbb_utils.variable_list
        elif result.solver.termination_condition is tc.unbounded:
            return obj_sign * float('-inf'), result, subproblem.GDPbb_utils.variable_list
        else:
            return obj_sign * float('inf'), result, subproblem.GDPbb_utils.variable_list
Example #37
0
def coopr3_generate_canonical_repn(exp, idMap=None, compute_values=True):
    if exp is None:
        return CompiledLinearCanonicalRepn()
    degree = exp.polynomial_degree()

    if idMap is None:
        idMap = {}
    idMap.setdefault(None, {})

    if degree == 0:
        ans = CompiledLinearCanonicalRepn()
        ans.constant = value(exp)
        return ans

    elif degree == 1:
        # varmap is a map from the variable id() to a _VarData.
        # coef is a map from the variable id() to its coefficient.
        coef, varmap = collect_linear_canonical_repn(exp, idMap, compute_values)
        ans = CompiledLinearCanonicalRepn()
        if None in coef:
            val = coef.pop(None)
            if type(val) not in [int,float] or val != 0.0:
                ans.constant = val

        # the six module is inefficient in terms of wrapping iterkeys
        # and itervalues, in the context of Python 2.7. use the native
        # dictionary methods where possible.
        if using_py3:
            ans.linear = tuple( itervalues(coef) )
            ans.variables = tuple(varmap[var_hash] for var_hash in iterkeys(coef) )
        else:
            ans.linear = tuple( coef.itervalues() )
            ans.variables = tuple(varmap[var_hash] for var_hash in coef.iterkeys() )
        return ans

    # **Py3k: degree > 1 comparision will error if degree is None
    elif degree and degree > 1:
        ans = collect_general_canonical_repn(exp, idMap, compute_values)
        if 1 in ans:
            linear_terms = {}
            for key, coef in iteritems(ans[1]):
                linear_terms[list(key.keys())[0]] = coef
            ans[1] = linear_terms
        return GeneralCanonicalRepn(ans)
    else:
        return GeneralCanonicalRepn(
            { None: exp, -1 : collect_variables(exp, idMap) } )
Example #38
0
def _collect_linear_var(exp, idMap, multiplier, coef, varmap, compute_values):

    if exp.is_fixed():
        if compute_values:
            coef[None] += multiplier * value(exp)
        else:
            coef[None] += multiplier * exp
    else:
        id_ = id(exp)
        if id_ in idMap[None]:
            key = idMap[None][id_]
        else:
            key = len(idMap) - 1
            idMap[None][id_] = key
            idMap[key] = exp
        #
        if key in coef:
            coef[key] += multiplier
        else:
            coef[key] = multiplier
        varmap[key] = exp
Example #39
0
    def compile_instance(self,
                         pyomo_instance,
                         symbolic_solver_labels=False,
                         output_fixed_variable_bounds=False,
                         skip_trivial_constraints=False):

        from pyomo.core.base import Var, Constraint, SOSConstraint
        from pyomo.repn import canonical_is_constant, LinearCanonicalRepn, canonical_degree

        self._symbolic_solver_labels = symbolic_solver_labels
        self._output_fixed_variable_bounds = output_fixed_variable_bounds
        self._skip_trivial_constraints = skip_trivial_constraints

        self._has_quadratic_constraints = False
        self._has_quadratic_objective = False
        used_sos_constraints = False

        self._active_cplex_instance = cplex.Cplex()

        if self._symbolic_solver_labels:
            labeler = self._labeler = TextLabeler()
        else:
            labeler = self._labeler = NumericLabeler('x')

        self._symbol_map = SymbolMap()
        self._instance = pyomo_instance
        pyomo_instance.solutions.add_symbol_map(self._symbol_map)
        self._smap_id = id(self._symbol_map)

        # we use this when iterating over the constraints because it
        # will have a much smaller hash table, we also use this for
        # the warm start code after it is cleaned to only contain
        # variables referenced in the constraints
        self._variable_symbol_map = SymbolMap()

        # cplex wants the caller to set the problem type, which is (for
        # current purposes) strictly based on variable type counts.
        num_binary_variables = 0
        num_integer_variables = 0
        num_continuous_variables = 0

        #############################################
        # populate the variables in the cplex model #
        #############################################

        var_names = []
        var_lbs = []
        var_ubs = []
        var_types = []

        self._referenced_variable_ids.clear()

        # maps pyomo var data labels to the corresponding CPLEX variable id.
        self._cplex_variable_ids.clear()

        # cached in the loop below - used to update the symbol map
        # immediately following loop termination.
        var_label_pairs = []

        for var_data in pyomo_instance.component_data_objects(Var, active=True):

            if var_data.fixed and not self._output_fixed_variable_bounds:
                # if a variable is fixed, and we're preprocessing
                # fixed variables (as in not outputting them), there
                # is no need to add them to the compiled model.
                continue

            var_name = self._symbol_map.getSymbol(var_data, labeler)
            var_names.append(var_name)
            var_label_pairs.append((var_data, var_name))

            self._cplex_variable_ids[var_name] = len(self._cplex_variable_ids)

            if (var_data.lb is None) or (var_data.lb == -infinity):
                var_lbs.append(-cplex.infinity)
            else:
                var_lbs.append(value(var_data.lb))

            if (var_data.ub is None) or (var_data.ub == infinity):
                var_ubs.append(cplex.infinity)
            else:
                var_ubs.append(value(var_data.ub))

            if var_data.is_integer():
                var_types.append(self._active_cplex_instance.variables.type.integer)
                num_integer_variables += 1
            elif var_data.is_binary():
                var_types.append(self._active_cplex_instance.variables.type.binary)
                num_binary_variables += 1
            elif var_data.is_continuous():
                var_types.append(self._active_cplex_instance.variables.type.continuous)
                num_continuous_variables += 1
            else:
                raise TypeError("Invalid domain type for variable with name '%s'. "
                                "Variable is not continuous, integer, or binary.")

        self._active_cplex_instance.variables.add(names=var_names,
                                                  lb=var_lbs,
                                                  ub=var_ubs,
                                                  types=var_types)

        self._active_cplex_instance.variables.add(lb=[1],
                                                  ub=[1],
                                                  names=["ONE_VAR_CONSTANT"])

        self._cplex_variable_ids["ONE_VAR_CONSTANT"] = len(self._cplex_variable_ids)

        self._variable_symbol_map.addSymbols(var_label_pairs)
        self._cplex_variable_names = self._active_cplex_instance.variables.get_names()

        ########################################################
        # populate the standard constraints in the cplex model #
        ########################################################

        expressions = []
        senses = []
        rhss = []
        range_values = []
        names = []

        qexpressions = []
        qlinears = []
        qsenses = []
        qrhss = []
        qnames = []

        for block in pyomo_instance.block_data_objects(active=True):

            gen_con_canonical_repn = \
                getattr(block, "_gen_con_canonical_repn", True)
            # Get/Create the ComponentMap for the repn
            if not hasattr(block,'_canonical_repn'):
                block._canonical_repn = ComponentMap()
            block_canonical_repn = block._canonical_repn

            for con in block.component_data_objects(Constraint,
                                                    active=True,
                                                    descend_into=False):

                if (con.lower is None) and \
                   (con.upper is None):
                    continue  # not binding at all, don't bother

                con_repn = None
                if isinstance(con, LinearCanonicalRepn):
                    con_repn = con
                else:
                    if gen_con_canonical_repn:
                        con_repn = generate_canonical_repn(con.body)
                        block_canonical_repn[con] = con_repn
                    else:
                        con_repn = block_canonical_repn[con]

                # There are conditions, e.g., when fixing variables, under which
                # a constraint block might be empty.  Ignore these, for both
                # practical reasons and the fact that the CPLEX LP format
                # requires a variable in the constraint body.  It is also
                # possible that the body of the constraint consists of only a
                # constant, in which case the "variable" of
                if isinstance(con_repn, LinearCanonicalRepn):
                    if (con_repn.linear is None) and \
                       self._skip_trivial_constraints:
                       continue
                else:
                    # we shouldn't come across a constant canonical repn
                    # that is not LinearCanonicalRepn
                    assert not canonical_is_constant(con_repn)

                name = self._symbol_map.getSymbol(con, labeler)
                expr = None
                qexpr = None
                quadratic = False
                if isinstance(con_repn, LinearCanonicalRepn):
                    expr, offset = \
                        self._encode_constraint_body_linear_specialized(con_repn,
                                                                        labeler,
                                                                        use_variable_names=False,
                                                                        cplex_variable_name_index_map=self._cplex_variable_ids)
                else:
                    degree = canonical_degree(con_repn)
                    if degree == 2:
                        quadratic = True
                    elif (degree != 0) or (degree != 1):
                        raise ValueError(
                            "CPLEXPersistent plugin does not support general nonlinear "
                            "constraint expression (only linear or quadratic).\n"
                            "Constraint: %s" % (con.cname(True)))
                    expr, offset = self._encode_constraint_body_linear(con_repn,
                                                                       labeler)

                if quadratic:
                    if expr is None:
                        expr = cplex.SparsePair(ind=[0],val=[0.0])
                    self._has_quadratic_constraints = True

                    qexpr = self._encode_constraint_body_quadratic(con_repn,labeler)
                    qnames.append(name)

                    if con.equality:
                        # equality constraint.
                        qsenses.append('E')
                        qrhss.append(self._get_bound(con.lower) - offset)

                    elif (con.lower is not None) and (con.upper is not None):
                        raise RuntimeError(
                            "The CPLEXDirect plugin can not translate range "
                            "constraints containing quadratic expressions.")

                    elif con.lower is not None:
                        assert con.upper is None
                        qsenses.append('G')
                        qrhss.append(self._get_bound(con.lower) - offset)

                    else:
                        qsenses.append('L')
                        qrhss.append(self._get_bound(con.upper) - offset)

                    qlinears.append(expr)
                    qexpressions.append(qexpr)

                else:
                    names.append(name)
                    expressions.append(expr)

                    if con.equality:
                        # equality constraint.
                        senses.append('E')
                        rhss.append(self._get_bound(con.lower) - offset)
                        range_values.append(0.0)

                    elif (con.lower is not None) and (con.upper is not None):
                        # ranged constraint.
                        senses.append('R')
                        lower_bound = self._get_bound(con.lower) - offset
                        upper_bound = self._get_bound(con.upper) - offset
                        rhss.append(lower_bound)
                        range_values.append(upper_bound - lower_bound)

                    elif con.lower is not None:
                        senses.append('G')
                        rhss.append(self._get_bound(con.lower) - offset)
                        range_values.append(0.0)

                    else:
                        senses.append('L')
                        rhss.append(self._get_bound(con.upper) - offset)
                        range_values.append(0.0)

        ###################################################
        # populate the SOS constraints in the cplex model #
        ###################################################

        # SOS constraints - largely taken from cpxlp.py so updates there,
        # should be applied here
        # TODO: Allow users to specify the variables coefficients for custom
        # branching/set orders - refer to cpxlp.py
        sosn = self._capabilities.sosn
        sos1 = self._capabilities.sos1
        sos2 = self._capabilities.sos2
        modelSOS = ModelSOS()
        for soscondata in pyomo_instance.component_data_objects(SOSConstraint,
                                                                active=True):
            level = soscondata.level
            if (level == 1 and not sos1) or \
               (level == 2 and not sos2) or \
               (level > 2 and not sosn):
                raise Exception("Solver does not support SOS level %s constraints"
                                % (level,))
            modelSOS.count_constraint(self._symbol_map,
                                      labeler,
                                      self._variable_symbol_map,
                                      soscondata)

        if modelSOS.sosType:
            for key in modelSOS.sosType:
                self._active_cplex_instance.SOS.add(type = modelSOS.sosType[key],
                                       name = modelSOS.sosName[key],
                                       SOS = [modelSOS.varnames[key],
                                              modelSOS.weights[key]])
                self._referenced_variable_ids.update(modelSOS.varids[key])
            used_sos_constraints = True

        self._active_cplex_instance.linear_constraints.add(
            lin_expr=expressions,
            senses=senses,
            rhs=rhss,
            range_values=range_values,
            names=names)

        for index in xrange(len(qexpressions)):
            self._active_cplex_instance.quadratic_constraints.add(
                lin_expr=qlinears[index],
                quad_expr=qexpressions[index],
                sense=qsenses[index],
                rhs=qrhss[index],
                name=qnames[index])

        #############################################
        # populate the objective in the cplex model #
        #############################################

        self.compile_objective(pyomo_instance)

        ################################################
        # populate the problem type in the cplex model #
        ################################################

        # This gets rid of the annoying "Freeing MIP data." message.
        def _filter_freeing_mip_data(val):
            if val.strip() == 'Freeing MIP data.':
                return ""
            return val
        self._active_cplex_instance.set_warning_stream(sys.stderr,
                                                       fn=_filter_freeing_mip_data)

        if (self._has_quadratic_objective is True) or \
           (self._has_quadratic_constraints is True):
            if (num_integer_variables > 0) or \
               (num_binary_variables > 0) or \
               (used_sos_constraints):
                if self._has_quadratic_constraints is True:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.MIQCP)
                else:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.MIQP)
            else:
                if self._has_quadratic_constraints is True:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.QCP)
                else:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.QP)
        elif (num_integer_variables > 0) or \
             (num_binary_variables > 0) or \
             (used_sos_constraints):
            self._active_cplex_instance.set_problem_type(
                self._active_cplex_instance.problem_type.MILP)
        else:
            self._active_cplex_instance.set_problem_type(
                self._active_cplex_instance.problem_type.LP)

        # restore the warning stream without our filter function
        self._active_cplex_instance.set_warning_stream(sys.stderr)
Example #40
0
    def simulate(self, numpoints=None, tstep=None, integrator=None,
                 varying_inputs=None, initcon=None, integrator_options=None):
        """
        Simulate the model. Integrator-specific options may be specified as
        keyword arguments and will be passed on to the integrator.

        Parameters
        ----------
        numpoints : int
            The number of points for the profiles returned by the simulator.
            Default is 100

        tstep : int or float
            The time step to use in the profiles returned by the simulator.
            This is not the time step used internally by the integrators.
            This is an optional parameter that may be specified in place of
            'numpoints'.

        integrator : string
            The string name of the integrator to use for simulation. The
            default is 'lsoda' when using Scipy and 'idas' when using CasADi

        varying_inputs : ``pyomo.environ.Suffix``
            A :py:class:`Suffix<pyomo.environ.Suffix>` object containing the
            piecewise constant profiles to be used for certain time-varying
            algebraic variables.

        initcon : list of floats
            The initial conditions for the the differential variables. This
            is an optional argument. If not specified then the simulator
            will use the current value of the differential variables at the
            lower bound of the ContinuousSet for the initial condition.

        integrator_options : dict
            Dictionary containing options that should be passed to the
            integrator. See the documentation for a specific integrator for a
            list of valid options.

        Returns
        -------
        numpy array, numpy array
            The first return value is a 1D array of time points corresponding
            to the second return value which is a 2D array of the profiles for
            the simulated differential and algebraic variables.
        """

        if not numpy_available:
            raise ValueError("The numpy module is not available. "
                              "Cannot simulate the model.")

        if integrator_options is None:
            integrator_options = {}

        if self._intpackage == 'scipy':
            # Specify the scipy integrator to use for simulation
            valid_integrators = ['vode', 'zvode', 'lsoda', 'dopri5', 'dop853']
            if integrator is None:
                integrator = 'lsoda'
            elif integrator is 'odeint':
                integrator = 'lsoda'
        else:
            # Specify the casadi integrator to use for simulation.
            # Only a subset of these integrators may be used for 
            # DAE simulation. We defer this check to CasADi.
            valid_integrators = ['cvodes', 'idas', 'collocation', 'rk']
            if integrator is None:
                integrator = 'idas'

        if integrator not in valid_integrators:
            raise DAE_Error("Unrecognized %s integrator \'%s\'. Please select"
                            " an integrator from %s" % (self._intpackage,
                                                        integrator,
                                                        valid_integrators))

        # Set the time step or the number of points for the lists
        # returned by the integrator
        if tstep is not None and \
           tstep > (self._contset.last() - self._contset.first()):
            raise ValueError(
                "The step size %6.2f is larger than the span of the "
                "ContinuousSet %s" % (tstep, self._contset.name()))
        
        if tstep is not None and numpoints is not None:
            raise ValueError(
                "Cannot specify both the step size and the number of "
                "points for the simulator")
        if tstep is None and numpoints is None:
            # Use 100 points by default
            numpoints = 100

        if tstep is None:
            tsim = np.linspace(
                self._contset.first(), self._contset.last(), num=numpoints)

            # Consider adding an option for log spaced time points. Can be
            # important for simulating stiff systems.
            # tsim = np.logspace(-4,6, num=100)
            # np.log10(self._contset.first()),np.log10(
            # self._contset.last()),num=1000, endpoint=True)

        else:
            tsim = np.arange(
                self._contset.first(), self._contset.last(), tstep)

        switchpts = []
        self._siminputvars = {}
        self._simalgvars = []
        if varying_inputs is not None:
            if type(varying_inputs) is not Suffix:
                raise TypeError(
                    "Varying input values must be specified using a "
                    "Suffix. Please refer to the simulator documentation.")

            for alg in self._algvars:
                if alg._base in varying_inputs:
                    # Find all the switching points         
                    switchpts += varying_inputs[alg._base].keys()
                    # Add to dictionary of siminputvars
                    self._siminputvars[alg._base] = alg
                else:
                    self._simalgvars.append(alg)

            if self._intpackage is 'scipy' and len(self._simalgvars) != 0:
                raise DAE_Error("When simulating with Scipy you must "
                                "provide values for all parameters "
                                "and algebraic variables that are indexed "
                                "by the ContinuoutSet using the "
                                "'varying_inputs' keyword argument. "
                                "Please refer to the simulator documentation "
                                "for more information.")

            # Get the set of unique points
            switchpts = list(set(switchpts)) 
            switchpts.sort()

            # Make sure all the switchpts are within the bounds of
            # the ContinuousSet
            if switchpts[0] < self._contset.first() or \
                            switchpts[-1] > self._contset.last():
                raise ValueError("Found a switching point for one or more of "
                                 "the time-varying inputs that is not within "
                                 "the bounds of the ContinuousSet.")

            # Update tsim to include input switching points
            # This numpy function returns the unique, sorted points
            tsim = np.union1d(tsim, switchpts)
        else:
            self._simalgvars = self._algvars

        # Check if initial conditions were provided, otherwise obtain
        # them from the current variable values
        if initcon is not None:
            if len(initcon) > len(self._diffvars):
                raise ValueError(
                    "Too many initial conditions were specified. The "
                    "simulator was expecting a list with %i values."
                    % len(self._diffvars))
            if len(initcon) < len(self._diffvars):
                raise ValueError(
                    "Too few initial conditions were specified. The "
                    "simulator was expecting a list with %i values."
                    % len(self._diffvars))
        else:
            initcon = []
            for v in self._diffvars:
                for idx, i in enumerate(v._args):
                    if type(i) is IndexTemplate:
                        break
                initpoint = self._contset.first()
                vidx = tuple(v._args[0:idx]) + (initpoint,) + \
                       tuple(v._args[idx + 1:])
                # This line will raise an error if no value was set
                initcon.append(value(v._base[vidx]))

        # Call the integrator
        if self._intpackage is 'scipy':
            if not scipy_available:
                raise ValueError("The scipy module is not available. "
                                  "Cannot simulate the model.")
            tsim, profile = self._simulate_with_scipy(initcon, tsim, switchpts,
                                                      varying_inputs,
                                                      integrator,
                                                      integrator_options)
        else:

            if len(switchpts) != 0:
                tsim, profile = \
                    self._simulate_with_casadi_with_inputs(initcon, tsim,
                                                           varying_inputs,
                                                           integrator,
                                                           integrator_options)
            else:
                tsim, profile = \
                    self._simulate_with_casadi_no_inputs(initcon, tsim,
                                                         integrator,
                                                         integrator_options)

        self._tsim = tsim
        self._simsolution = profile
            
        return [tsim, profile]
Example #41
0
    def _apply_to(self, model, **kwds):
        # create a map of component to scaling factor
        component_scaling_factor_map = ComponentMap()

        # if the scaling_method is 'user', get the scaling parameters from the suffixes
        if self._scaling_method == 'user':
            # perform some checks to make sure we have the necessary suffixes
            if type(model.component('scaling_factor')) is not Suffix:
                raise ValueError("ScaleModel transformation called with scaling_method='user'"
                                 ", but cannot find the suffix 'scaling_factor' on the model")

            # get the scaling factors
            for c in model.component_data_objects(ctype=(Var, Constraint, Objective), descend_into=True):
                component_scaling_factor_map[c] = self._get_float_scaling_factor(model, c)
        else:
            raise ValueError("ScaleModel transformation: unknown scaling_method found"
                             "-- supported values: 'user' ")

        # rename all the Vars, Constraints, and Objectives from foo to scaled_foo
        scaled_component_to_original_name_map = \
            rename_components(model=model,
                              component_list=list(model.component_objects(ctype=[Var, Constraint, Objective])),
                              prefix='scaled_')

        # scale the variable bounds and values and build the variable substitution map
        # for scaling vars in constraints
        variable_substitution_map = ComponentMap()
        for variable in [var for var in model.component_objects(ctype=Var, descend_into=True)]:
            # set the bounds/value for the scaled variable
            for k in variable:
                v = variable[k]
                scaling_factor = component_scaling_factor_map[v]
                variable_substitution_map[v] = v / scaling_factor

                if v.lb is not None:
                    v.setlb(v.lb * scaling_factor)
                if v.ub is not None:
                    v.setub(v.ub * scaling_factor)
                if scaling_factor < 0:
                    temp = v.lb
                    v.setlb(v.ub)
                    v.setub(temp)

                if v.value is not None:
                    v.value = value(v) * scaling_factor

        # scale the objectives/constraints and perform the scaled variable substitution
        scale_constraint_dual = False
        if type(model.component('dual')) is Suffix:
            scale_constraint_dual = True

        # translate the variable_substitution_map (ComponentMap)
        # to variable_substition_dict (key: id() of component)
        # ToDo: We should change replace_expressions to accept a ComponentMap as well
        variable_substitution_dict = dict()
        for k in variable_substitution_map:
            variable_substitution_dict[id(k)] = variable_substitution_map[k]

        for component in model.component_objects(ctype=(Constraint, Objective), descend_into=True):
            for k in component:
                c = component[k]
                # perform the constraint/objective scaling and variable sub
                scaling_factor = component_scaling_factor_map[c]
                if isinstance(c, _ConstraintData):
                    body = scaling_factor * \
                           replace_expressions(expr=c.body,
                                               substitution_map=variable_substitution_dict,
                                               descend_into_named_expressions=True,
                                               remove_named_expressions=True)

                    # scale the rhs
                    if c._lower is not None:
                        c._lower = c._lower * scaling_factor
                    if c._upper is not None:
                        c._upper = c._upper * scaling_factor

                    if scaling_factor < 0:
                        c._lower, c._upper = c._upper, c._lower

                    if scale_constraint_dual and c in model.dual:
                        dual_value = model.dual[c]
                        if dual_value is not None:
                            model.dual[c] = dual_value / scaling_factor

                    c.set_value((c._lower, body, c._upper))

                elif isinstance(c, _ObjectiveData):
                    c.expr = scaling_factor * \
                             replace_expressions(expr=c.expr,
                                                 substitution_map=variable_substitution_dict,
                                                 descend_into_named_expressions=True,
                                                 remove_named_expressions=True)
                else:
                    raise NotImplementedError(
                        'Unknown object type found when applying scaling factors in ScaleModel transformation - Internal Error')

        model.component_scaling_factor_map = component_scaling_factor_map
        model.scaled_component_to_original_name_map = scaled_component_to_original_name_map

        return model
Example #42
0
def pyomo4_generate_canonical_repn(exp, idMap=None, compute_values=True):
    # A **very** special case
    if TreeWalkerHelper.typeList.get(exp.__class__,0) == 4: # _LinearExpression:
        ans = CompiledLinearCanonicalRepn()

        # old format
        ans.constant = exp._const
        ans.variables = list( exp._args )
        _l = exp._coef
        ans.linear = [_l[id(v)] for v in exp._args]

        if idMap:
            if None not in idMap:
                idMap[None] = {}
            _test = idMap[None]
            _key = len(idMap) - 1
            for v in exp._args:
                if id(v) not in _test:
                    _test[id(v)] = _key
                    idMap[_key] = v
                    _key += 1
        return ans
    else:
        degree = exp.polynomial_degree()

    if degree == 1:
        _typeList = TreeWalkerHelper.typeList
        _stackMax = len(_stack)
        _stackIdx = 0
        _stackPtr = _stack[0]

        _stackPtr[0] = exp
        try:
            _stackPtr[1] = exp._args
        except AttributeError:
            ans = CompiledLinearCanonicalRepn()
            ans.variables.append(exp)
            # until we can redefine CompiledLinearCanonicalRepn, restore
            # old format
            #ans.linear[id(exp)] = 1.
            ans.linear = [1.]
            return ans
        try:
            _stackPtr[2] = _type = _typeList[exp.__class__]
            if _stackPtr[2] == 2:
                _stackPtr[5].constant = 1.
        except KeyError:
            _stackPtr[2] = _type = 0
        _stackPtr[3] = len(_stackPtr[1])
        _stackPtr[4] = 0
        #_stackPtr[5] = CompiledLinearCanonicalRepn()

        if _type == 4: # _LinearExpression
            _stackPtr[4] = _stackPtr[3]
            _stackPtr[5].constant = exp._const
            _stackPtr[5].linear = dict(exp._coef)
            _stackPtr[5].variables = list(exp._args)

        while 1: # Note: 1 is faster than True for Python 2.x
            if _stackPtr[4] < _stackPtr[3]:
                _sub = _stackPtr[1][_stackPtr[4]]
                _stackPtr[4] += 1
                _test = _sub.__class__ in native_numeric_types
                if _test or not _sub.is_expression():
                    if not _test and _sub.is_fixed():
                        _sub = value(_sub)
                        _test = 1 # True
                    if _test:
                        if _type == 2:
                            _stackPtr[5].constant *= _sub
                            _l = _stackPtr[5].linear
                            if _l:
                                for _id in _l:
                                    _l[_id] *= _sub
                        elif _type == 1:
                            _stackPtr[5].constant += _sub
                        elif _type == 3:
                            _stackPtr[5].constant = -1. * _sub
                        else:
                            raise RuntimeError("HELP")
                    else:
                        _id = id(_sub)
                        if _type == 2:
                            _lcr = _stackPtr[5]
                            _lcr.variables.append(_sub)
                            _lcr.linear[_id] = _lcr.constant
                            _lcr.constant = 0
                        elif _type == 1:
                            if _id in _stackPtr[5].linear:
                                _stackPtr[5].linear[_id] += 1.
                            else:
                                _stackPtr[5].variables.append(_sub)
                                _stackPtr[5].linear[_id] = 1.
                        elif _type == 3:
                            _lcr = _stackPtr[5]
                            _lcr.variables.append(_sub)
                            _lcr.linear[_id] = -1.
                        else:
                            raise RuntimeError("HELP")
                else:
                    _stackIdx += 1
                    if _stackMax == _stackIdx:
                        _stackMax += 1
                        _stack.append([0,0,0,0,0, CompiledLinearCanonicalRepn()])
                    _stackPtr = _stack[_stackIdx]

                    _stackPtr[0] = _sub
                    _stackPtr[1] = _sub._args
                    #_stackPtr[2] = _type = _typeList.get(_sub.__class__, 0)
                    #if _type == 2:
                    #    _stackPtr[5].constant = 1.
                    try:
                        _stackPtr[2] = _type = _typeList[_sub.__class__]
                        if _type == 2:
                            _stackPtr[5].constant = 1.
                    except KeyError:
                        _stackPtr[2] = _type = 0
                    _stackPtr[3] = len(_stackPtr[1])
                    _stackPtr[4] = 0
                    #_stackPtr[5] = CompiledLinearCanonicalRepn()

                    if _type == 4: # _LinearExpression
                        _stackPtr[4] = _stackPtr[3]
                        _stackPtr[5].constant = _sub._const
                        _stackPtr[5].linear = dict(_sub._coef)
                        _stackPtr[5].variables = list(_sub._args)
            else:
                old = _stackPtr[5]
                if not _type:
                    old.constant = _stackPtr[0]._apply_operation(old.variables)
                    old.variables = []

                if _stackIdx == 0:
                    ans = CompiledLinearCanonicalRepn()
                    ans.variables, old.variables = old.variables, ans.variables
                    ans.linear, old.linear = old.linear, ans.linear
                    ans.constant, old.constant = old.constant, ans.constant
                    # until we can redefine CompiledLinearCanonicalRepn, restore
                    # old format
                    ans.linear = [ans.linear[id(v)] for v in ans.variables]

                    if idMap:
                        if None not in idMap:
                            idMap[None] = {}
                        _test = idMap[None]
                        _key = len(idMap) - 1
                        for v in ans.variables:
                            if id(v) not in _test:
                                _test[id(v)] = _key
                                idMap[_key] = v
                                _key += 1
                    return ans

                _stackIdx -= 1
                _stackPtr = _stack[_stackIdx]
                new = _stackPtr[5]
                _type = _stackPtr[2]
                if _type == 1:
                    new.constant += old.constant
                    _nl = new.linear
                    # Note: append the variables in the order that they
                    # were originally added to the CompiledLinearCanonicalRepn.
                    # This keeps things deterministic.
                    for v in old.variables:
                        _id = id(v)
                        if _id in _nl:
                            _nl[_id] += old.linear[_id]
                        else:
                            new.variables.append(v)
                            _nl[_id] = old.linear[_id]
                    old.constant = 0.
                    old.variables = []
                    old.linear = {}
                elif _type == 2:
                    if old.variables:
                        old.variables, new.variables = new.variables, old.variables
                        old.linear, new.linear = new.linear, old.linear
                        old.constant, new.constant = new.constant, old.constant
                    _c = old.constant
                    new.constant *= _c
                    _nl = new.linear
                    for _id in _nl:
                        _nl[_id] *= _c
                    old.constant = 0.
                elif _type == 3:
                    old.variables, new.variables = new.variables, old.variables
                    old.linear, new.linear = new.linear, old.linear
                    new.constant = -1 * old.constant
                    old.constant = 0.
                    _nl = new.linear
                    for _id in _nl:
                        _nl[_id] *= -1
                else:
                    raise RuntimeError("HELP")

    elif degree == 0:
        if CompiledLinearCanonicalRepn_Pool:
            ans = CompiledLinearCanonicalRepn_Pool.pop()
            ans.__init__()
        else:
            ans = CompiledLinearCanonicalRepn()
        ans.constant = value(exp)
        return ans

    # **Py3k: degree > 1 comparision will error if degree is None
    elif degree and degree > 1:
        ans = collect_general_canonical_repn(exp, idMap, compute_values)
        if 1 in ans:
            linear_terms = {}
            for key, coef in iteritems(ans[1]):
                linear_terms[list(key.keys())[0]] = coef
            ans[1] = linear_terms
        return GeneralCanonicalRepn(ans)
    else:
        return GeneralCanonicalRepn(
            { None: exp, -1 : collect_variables(exp, idMap) } )
Example #43
0
    def _print_model_LP(self,
                        model,
                        output_file,
                        solver_capability,
                        labeler,
                        output_fixed_variable_bounds=False,
                        file_determinism=1,
                        row_order=None,
                        column_order=None,
                        skip_trivial_constraints=False,
                        force_objective_constant=False,
                        include_all_variable_bounds=False):

        symbol_map = SymbolMap()
        variable_symbol_map = SymbolMap()
        # NOTE: we use createSymbol instead of getSymbol because we
        #       know whether or not the symbol exists, and don't want
        #       to the overhead of error/duplicate checking.
        # cache frequently called functions
        create_symbol_func = SymbolMap.createSymbol
        create_symbols_func = SymbolMap.createSymbols
        alias_symbol_func = SymbolMap.alias
        variable_label_pairs = []

        # populate the symbol map in a single pass.
        #objective_list, constraint_list, sosconstraint_list, variable_list \
        #    = self._populate_symbol_map(model,
        #                                symbol_map,
        #                                labeler,
        #                                variable_symbol_map,
        #                                file_determinism=file_determinism)
        sortOrder = SortComponents.unsorted
        if file_determinism >= 1:
            sortOrder = sortOrder | SortComponents.indices
            if file_determinism >= 2:
                sortOrder = sortOrder | SortComponents.alphabetical

        #
        # Create variable symbols (and cache the block list)
        #
        all_blocks = []
        variable_list = []
        for block in model.block_data_objects(active=True,
                                              sort=sortOrder):

            all_blocks.append(block)

            for vardata in block.component_data_objects(
                    Var,
                    active=True,
                    sort=sortOrder,
                    descend_into=False):

                variable_list.append(vardata)
                variable_label_pairs.append(
                    (vardata,create_symbol_func(symbol_map,
                                                vardata,
                                                labeler)))

        variable_symbol_map.addSymbols(variable_label_pairs)

        # and extract the information we'll need for rapid labeling.
        object_symbol_dictionary = symbol_map.byObject
        variable_symbol_dictionary = variable_symbol_map.byObject

        # cache - these are called all the time.
        print_expr_canonical = self._print_expr_canonical

        # print the model name and the source, so we know roughly where
        # it came from.
        #
        # NOTE: this *must* use the "\* ... *\" comment format: the GLPK
        # LP parser does not correctly handle other formats (notably, "%").
        output_file.write(
            "\\* Source Pyomo model name=%s *\\\n\n" % (model.name,) )

        #
        # Objective
        #

        supports_quadratic_objective = \
            solver_capability('quadratic_objective')

        numObj = 0
        onames = []
        for block in all_blocks:

            gen_obj_canonical_repn = \
                getattr(block, "_gen_obj_canonical_repn", True)

            # Get/Create the ComponentMap for the repn
            if not hasattr(block,'_canonical_repn'):
                block._canonical_repn = ComponentMap()
            block_canonical_repn = block._canonical_repn

            for objective_data in block.component_data_objects(
                    Objective,
                    active=True,
                    sort=sortOrder,
                    descend_into=False):

                numObj += 1
                onames.append(objective_data.name)
                if numObj > 1:
                    raise ValueError(
                        "More than one active objective defined for input "
                        "model '%s'; Cannot write legal LP file\n"
                        "Objectives: %s" % (model.name, ' '.join(onames)))

                create_symbol_func(symbol_map,
                                   objective_data,
                                   labeler)

                symbol_map.alias(objective_data, '__default_objective__')
                if objective_data.is_minimizing():
                    output_file.write("min \n")
                else:
                    output_file.write("max \n")

                if gen_obj_canonical_repn:
                    canonical_repn = \
                        generate_canonical_repn(objective_data.expr)
                    block_canonical_repn[objective_data] = canonical_repn
                else:
                    canonical_repn = block_canonical_repn[objective_data]

                degree = canonical_degree(canonical_repn)

                if degree == 0:
                    logger.warning("Constant objective detected, replacing "
                          "with a placeholder to prevent solver failure.")
                    force_objective_constant = True
                elif degree == 2:
                    if not supports_quadratic_objective:
                        raise RuntimeError(
                            "Selected solver is unable to handle "
                            "objective functions with quadratic terms. "
                            "Objective at issue: %s."
                            % objective_data.name)
                elif degree != 1:
                    raise RuntimeError(
                        "Cannot write legal LP file.  Objective '%s' "
                        "has nonlinear terms that are not quadratic."
                        % objective_data.name)

                output_file.write(
                    object_symbol_dictionary[id(objective_data)]+':\n')

                offset = print_expr_canonical(
                    canonical_repn,
                    output_file,
                    object_symbol_dictionary,
                    variable_symbol_dictionary,
                    True,
                    column_order,
                    force_objective_constant=force_objective_constant)

        if numObj == 0:
            raise ValueError(
                "ERROR: No objectives defined for input model '%s'; "
                " cannot write legal LP file" % str(model.name))

        # Constraints
        #
        # If there are no non-trivial constraints, you'll end up with an empty
        # constraint block. CPLEX is OK with this, but GLPK isn't. And
        # eliminating the constraint block (i.e., the "s.t." line) causes GLPK
        # to whine elsewhere. Output a warning if the constraint block is empty,
        # so users can quickly determine the cause of the solve failure.

        output_file.write("\n")
        output_file.write("s.t.\n")
        output_file.write("\n")

        have_nontrivial = False

        supports_quadratic_constraint = solver_capability('quadratic_constraint')

        def constraint_generator():
            for block in all_blocks:

                gen_con_canonical_repn = \
                    getattr(block, "_gen_con_canonical_repn", True)

                # Get/Create the ComponentMap for the repn
                if not hasattr(block,'_canonical_repn'):
                    block._canonical_repn = ComponentMap()
                block_canonical_repn = block._canonical_repn

                for constraint_data in block.component_data_objects(
                        Constraint,
                        active=True,
                        sort=sortOrder,
                        descend_into=False):

                    if isinstance(constraint_data, LinearCanonicalRepn):
                        canonical_repn = constraint_data
                    else:
                        if gen_con_canonical_repn:
                            canonical_repn = generate_canonical_repn(constraint_data.body)
                            block_canonical_repn[constraint_data] = canonical_repn
                        else:
                            canonical_repn = block_canonical_repn[constraint_data]

                    yield constraint_data, canonical_repn

        if row_order is not None:
            sorted_constraint_list = list(constraint_generator())
            sorted_constraint_list.sort(key=lambda x: row_order[x[0]])
            def yield_all_constraints():
                for constraint_data, canonical_repn in sorted_constraint_list:
                    yield constraint_data, canonical_repn
        else:
            yield_all_constraints = constraint_generator

        # FIXME: This is a hack to get nested blocks working...
        eq_string_template = "= %"+self._precision_string+'\n'
        geq_string_template = ">= %"+self._precision_string+'\n\n'
        leq_string_template = "<= %"+self._precision_string+'\n\n'
        for constraint_data, canonical_repn in yield_all_constraints():
            have_nontrivial = True

            degree = canonical_degree(canonical_repn)

            #
            # Write constraint
            #

            # There are conditions, e.g., when fixing variables, under which
            # a constraint block might be empty.  Ignore these, for both
            # practical reasons and the fact that the CPLEX LP format
            # requires a variable in the constraint body.  It is also
            # possible that the body of the constraint consists of only a
            # constant, in which case the "variable" of
            if degree == 0:
                if skip_trivial_constraints:
                    continue
            elif degree == 2:
                if not supports_quadratic_constraint:
                    raise ValueError(
                        "Solver unable to handle quadratic expressions. Constraint"
                        " at issue: '%s'" % (constraint_data.name))
            elif degree != 1:
                raise ValueError(
                    "Cannot write legal LP file.  Constraint '%s' has a body "
                    "with nonlinear terms." % (constraint_data.name))

            # Create symbol
            con_symbol = create_symbol_func(symbol_map, constraint_data, labeler)

            if constraint_data.equality:
                label = 'c_e_' + con_symbol + '_'
                alias_symbol_func(symbol_map, constraint_data, label)
                output_file.write(label+':\n')
                offset = print_expr_canonical(canonical_repn,
                                              output_file,
                                              object_symbol_dictionary,
                                              variable_symbol_dictionary,
                                              False,
                                              column_order)
                bound = constraint_data.lower
                bound = self._get_bound(bound) - offset
                output_file.write(eq_string_template
                                  % (_no_negative_zero(bound)))
                output_file.write("\n")
            else:
                if constraint_data.lower is not None:
                    if constraint_data.upper is not None:
                        label = 'r_l_' + con_symbol + '_'
                    else:
                        label = 'c_l_' + con_symbol + '_'
                    alias_symbol_func(symbol_map, constraint_data, label)
                    output_file.write(label+':\n')
                    offset = print_expr_canonical(canonical_repn,
                                                  output_file,
                                                  object_symbol_dictionary,
                                                  variable_symbol_dictionary,
                                                  False,
                                                  column_order)
                    bound = constraint_data.lower
                    bound = self._get_bound(bound) - offset
                    output_file.write(geq_string_template
                                      % (_no_negative_zero(bound)))
                if constraint_data.upper is not None:
                    if constraint_data.lower is not None:
                        label = 'r_u_' + con_symbol + '_'
                    else:
                        label = 'c_u_' + con_symbol + '_'
                    alias_symbol_func(symbol_map, constraint_data, label)
                    output_file.write(label+':\n')
                    offset = print_expr_canonical(canonical_repn,
                                                  output_file,
                                                  object_symbol_dictionary,
                                                  variable_symbol_dictionary,
                                                  False,
                                                  column_order)
                    bound = constraint_data.upper
                    bound = self._get_bound(bound) - offset
                    output_file.write(leq_string_template
                                      % (_no_negative_zero(bound)))

        if not have_nontrivial:
            logger.warning('Empty constraint block written in LP format '  \
                  '- solver may error')

        # the CPLEX LP format doesn't allow constants in the objective (or
        # constraint body), which is a bit silly.  To avoid painful
        # book-keeping, we introduce the following "variable", constrained
        # to the value 1.  This is used when quadratic terms are present.
        # worst-case, if not used, is that CPLEX easily pre-processes it out.
        prefix = ""
        output_file.write('%sc_e_ONE_VAR_CONSTANT: \n' % prefix)
        output_file.write('%sONE_VAR_CONSTANT = 1.0\n' % prefix)
        output_file.write("\n")

        # SOS constraints
        #
        # For now, we write out SOS1 and SOS2 constraints in the cplex format
        #
        # All Component objects are stored in model._component, which is a
        # dictionary of {class: {objName: object}}.
        #
        # Consider the variable X,
        #
        #   model.X = Var(...)
        #
        # We print X to CPLEX format as X(i,j,k,...) where i, j, k, ... are the
        # indices of X.
        #
        SOSlines = StringIO()
        sos1 = solver_capability("sos1")
        sos2 = solver_capability("sos2")
        writtenSOS = False
        for block in all_blocks:

            for soscondata in block.component_data_objects(
                    SOSConstraint,
                    active=True,
                    sort=sortOrder,
                    descend_into=False):

                create_symbol_func(symbol_map, soscondata, labeler)

                level = soscondata.level
                if (level == 1 and not sos1) or \
                   (level == 2 and not sos2) or \
                   (level > 2):
                    raise ValueError(
                        "Solver does not support SOS level %s constraints" % (level))
                if writtenSOS == False:
                    SOSlines.write("SOS\n")
                    writtenSOS = True
                # This updates the referenced_variable_ids, just in case
                # there is a variable that only appears in an
                # SOSConstraint, in which case this needs to be known
                # before we write the "bounds" section (Cplex does not
                # handle this correctly, Gurobi does)
                self.printSOS(symbol_map,
                              labeler,
                              variable_symbol_map,
                              soscondata,
                              SOSlines)

        #
        # Bounds
        #

        output_file.write("bounds\n")

        # Scan all variables even if we're only writing a subset of them.
        # required because we don't store maps by variable type currently.

        # FIXME: This is a hack to get nested blocks working...
        lb_string_template = "%"+self._precision_string+" <= "
        ub_string_template = " <= %"+self._precision_string+"\n"
        # Track the number of integer and binary variables, so you can
        # output their status later.
        integer_vars = []
        binary_vars = []
        for vardata in variable_list:

            # TODO: We could just loop over the set of items in
            #       self._referenced_variable_ids, except this is
            #       a dictionary that is hashed by id(vardata)
            #       which would make the bounds section
            #       nondeterministic (bad for unit testing)
            if (not include_all_variable_bounds) and \
               (id(vardata) not in self._referenced_variable_ids):
                continue

            if vardata.fixed:
                if not output_fixed_variable_bounds:
                    raise ValueError(
                        "Encountered a fixed variable (%s) inside an active "
                        "objective or constraint expression on model %s, which is "
                        "usually indicative of a preprocessing error. Use the "
                        "IO-option 'output_fixed_variable_bounds=True' to suppress "
                        "this error and fix the variable by overwriting its bounds "
                        "in the LP file." % (vardata.name, model.name))
                if vardata.value is None:
                    raise ValueError("Variable cannot be fixed to a value of None.")
                vardata_lb = value(vardata.value)
                vardata_ub = value(vardata.value)
            else:
                vardata_lb = self._get_bound(vardata.lb)
                vardata_ub = self._get_bound(vardata.ub)

            name_to_output = variable_symbol_dictionary[id(vardata)]

            # track the number of integer and binary variables, so we know whether
            # to output the general / binary sections below.
            if vardata.is_integer():
                integer_vars.append(name_to_output)
            elif vardata.is_binary():
                binary_vars.append(name_to_output)
            elif not vardata.is_continuous():
                raise TypeError("Invalid domain type for variable with name '%s'. "
                                "Variable is not continuous, integer, or binary."
                                % (vardata.name))

            # in the CPLEX LP file format, the default variable
            # bounds are 0 and +inf.  These bounds are in
            # conflict with Pyomo, which assumes -inf and +inf
            # (which we would argue is more rational).
            output_file.write("   ")
            if (vardata_lb is not None) and (vardata_lb != -infinity):
                output_file.write(lb_string_template
                                  % (_no_negative_zero(vardata_lb)))
            else:
                output_file.write(" -inf <= ")
            if name_to_output == "e":
                raise ValueError(
                    "Attempting to write variable with name 'e' in a CPLEX LP "
                    "formatted file will cause a parse failure due to confusion with "
                    "numeric values expressed in scientific notation")

            output_file.write(name_to_output)
            if (vardata_ub is not None) and (vardata_ub != infinity):
                output_file.write(ub_string_template
                                  % (_no_negative_zero(vardata_ub)))
            else:
                output_file.write(" <= +inf\n")

        if len(integer_vars) > 0:

            output_file.write("general\n")
            for var_name in integer_vars:
                output_file.write('  %s\n' % var_name)

        if len(binary_vars) > 0:

            output_file.write("binary\n")
            for var_name in binary_vars:
                output_file.write('  %s\n' % var_name)


        # Write the SOS section
        output_file.write(SOSlines.getvalue())

        #
        # wrap-up
        #
        output_file.write("end\n")

        # Clean up the symbol map to only contain variables referenced
        # in the active constraints **Note**: warm start method may
        # rely on this for choosing the set of potential warm start
        # variables
        vars_to_delete = set(variable_symbol_map.byObject.keys()) - \
                         set(self._referenced_variable_ids.keys())
        sm_byObject = symbol_map.byObject
        sm_bySymbol = symbol_map.bySymbol
        var_sm_byObject = variable_symbol_map.byObject
        for varid in vars_to_delete:
            symbol = var_sm_byObject[varid]
            del sm_byObject[varid]
            del sm_bySymbol[symbol]
        del variable_symbol_map

        return symbol_map
Example #44
0
    def _print_model_MPS(self,
                         model,
                         output_file,
                         solver_capability,
                         labeler,
                         output_fixed_variable_bounds=False,
                         file_determinism=1,
                         row_order=None,
                         column_order=None,
                         skip_trivial_constraints=False,
                         force_objective_constant=False,
                         include_all_variable_bounds=False,
                         skip_objective_sense=False):

        symbol_map = SymbolMap()
        variable_symbol_map = SymbolMap()
        # NOTE: we use createSymbol instead of getSymbol because we
        #       know whether or not the symbol exists, and don't want
        #       to the overhead of error/duplicate checking.
        # cache frequently called functions
        extract_variable_coefficients = self._extract_variable_coefficients
        create_symbol_func = SymbolMap.createSymbol
        create_symbols_func = SymbolMap.createSymbols
        alias_symbol_func = SymbolMap.alias
        variable_label_pairs = []

        sortOrder = SortComponents.unsorted
        if file_determinism >= 1:
            sortOrder = sortOrder | SortComponents.indices
            if file_determinism >= 2:
                sortOrder = sortOrder | SortComponents.alphabetical

        #
        # Create variable symbols (and cache the block list)
        #
        all_blocks = []
        variable_list = []
        for block in model.block_data_objects(active=True,
                                              sort=sortOrder):

            all_blocks.append(block)

            for vardata in block.component_data_objects(
                    Var,
                    active=True,
                    sort=sortOrder,
                    descend_into=False):

                variable_list.append(vardata)
                variable_label_pairs.append(
                    (vardata,create_symbol_func(symbol_map,
                                                vardata,
                                                labeler)))

        variable_symbol_map.addSymbols(variable_label_pairs)

        # and extract the information we'll need for rapid labeling.
        object_symbol_dictionary = symbol_map.byObject
        variable_symbol_dictionary = variable_symbol_map.byObject

        # sort the variable ordering by the user
        # column_order ComponentMap
        if column_order is not None:
            variable_list.sort(key=lambda _x: column_order[_x])

        # prepare to hold the sparse columns
        variable_to_column = ComponentMap(
            (vardata, i) for i, vardata in enumerate(variable_list))
        # add one position for ONE_VAR_CONSTANT
        column_data = [[] for i in xrange(len(variable_list)+1)]
        quadobj_data = []
        quadmatrix_data = []
        # constraint rhs
        rhs_data = []

        # print the model name and the source, so we know
        # roughly where
        output_file.write("* Source:     Pyomo MPS Writer\n")
        output_file.write("* Format:     Free MPS\n")
        output_file.write("*\n")
        output_file.write("NAME %s\n" % (model.name,))

        #
        # ROWS section
        #

        objective_label = None
        numObj = 0
        onames = []
        for block in all_blocks:

            gen_obj_canonical_repn = \
                getattr(block, "_gen_obj_canonical_repn", True)

            # Get/Create the ComponentMap for the repn
            if not hasattr(block,'_canonical_repn'):
                block._canonical_repn = ComponentMap()
            block_canonical_repn = block._canonical_repn
            for objective_data in block.component_data_objects(
                    Objective,
                    active=True,
                    sort=sortOrder,
                    descend_into=False):

                numObj += 1
                onames.append(objective_data.cname())
                if numObj > 1:
                    raise ValueError(
                        "More than one active objective defined for input "
                        "model '%s'; Cannot write legal MPS file\n"
                        "Objectives: %s" % (model.cname(True), ' '.join(onames)))

                objective_label = create_symbol_func(symbol_map,
                                                     objective_data,
                                                     labeler)

                symbol_map.alias(objective_data, '__default_objective__')
                if not skip_objective_sense:
                    output_file.write("OBJSENSE\n")
                    if objective_data.is_minimizing():
                        output_file.write(" MIN\n")
                    else:
                        output_file.write(" MAX\n")
                # This section is not recognized by the COIN-OR
                # MPS reader
                #output_file.write("OBJNAME\n")
                #output_file.write(" %s\n" % (objective_label))
                output_file.write("ROWS\n")
                output_file.write(" N  %s\n" % (objective_label))

                if gen_obj_canonical_repn:
                    canonical_repn = \
                        generate_canonical_repn(objective_data.expr)
                    block_canonical_repn[objective_data] = canonical_repn
                else:
                    canonical_repn = block_canonical_repn[objective_data]

                degree = canonical_degree(canonical_repn)
                if degree == 0:
                    print("Warning: Constant objective detected, replacing "
                          "with a placeholder to prevent solver failure.")
                    force_objective_constant = True
                elif (degree != 1) and (degree != 2):
                    raise RuntimeError(
                        "Cannot write legal MPS file. Objective '%s' "
                        "has nonlinear terms that are not quadratic."
                        % objective_data.cname(True))

                constant = extract_variable_coefficients(
                    objective_label,
                    canonical_repn,
                    column_data,
                    quadobj_data,
                    variable_to_column)
                if force_objective_constant or (constant != 0.0):
                    # ONE_VAR_CONSTANT
                    column_data[-1].append((objective_label, constant))

        if numObj == 0:
            raise ValueError(
                "Cannot write legal MPS file: No objective defined "
                "for input model '%s'." % str(model))
        assert objective_label is not None

        # Constraints
        def constraint_generator():
            for block in all_blocks:

                gen_con_canonical_repn = \
                    getattr(block, "_gen_con_canonical_repn", True)

                # Get/Create the ComponentMap for the repn
                if not hasattr(block,'_canonical_repn'):
                    block._canonical_repn = ComponentMap()
                block_canonical_repn = block._canonical_repn

                for constraint_data in block.component_data_objects(
                        Constraint,
                        active=True,
                        sort=sortOrder,
                        descend_into=False):

                    if isinstance(constraint_data, LinearCanonicalRepn):
                        canonical_repn = constraint_data
                    else:
                        if gen_con_canonical_repn:
                            canonical_repn = generate_canonical_repn(
                                constraint_data.body)
                            block_canonical_repn[constraint_data] = canonical_repn
                        else:
                            canonical_repn = block_canonical_repn[constraint_data]

                    yield constraint_data, canonical_repn

        if row_order is not None:
            sorted_constraint_list = list(constraint_generator())
            sorted_constraint_list.sort(key=lambda x: row_order[x[0]])
            def yield_all_constraints():
                for constraint_data, canonical_repn in sorted_constraint_list:
                    yield constraint_data, canonical_repn
        else:
            yield_all_constraints = constraint_generator

        for constraint_data, canonical_repn in yield_all_constraints():

            degree = canonical_degree(canonical_repn)

            # Write constraint
            if degree == 0:
                if skip_trivial_constraints:
                    continue
            elif (degree != 1) and (degree != 2):
                raise RuntimeError(
                    "Cannot write legal MPS file. Constraint '%s' "
                    "has nonlinear terms that are not quadratic."
                    % constraint_data.cname(True))

            # Create symbol
            con_symbol = create_symbol_func(symbol_map,
                                            constraint_data,
                                            labeler)

            if constraint_data.equality:
                label = 'c_e_' + con_symbol + '_'
                alias_symbol_func(symbol_map, constraint_data, label)
                output_file.write(" E  %s\n" % (label))
                offset = extract_variable_coefficients(
                    label,
                    canonical_repn,
                    column_data,
                    quadmatrix_data,
                    variable_to_column)
                bound = constraint_data.lower
                bound = self._get_bound(bound) - offset
                rhs_data.append((label, bound))
            else:
                if constraint_data.lower is not None:
                    if constraint_data.upper is not None:
                        label = 'r_l_' + con_symbol + '_'
                    else:
                        label = 'c_l_' + con_symbol + '_'
                    alias_symbol_func(symbol_map, constraint_data, label)
                    output_file.write(" G  %s\n" % (label))
                    offset = extract_variable_coefficients(
                        label,
                        canonical_repn,
                        column_data,
                        quadmatrix_data,
                        variable_to_column)
                    bound = constraint_data.lower
                    bound = self._get_bound(bound) - offset
                    rhs_data.append((label, bound))
                if constraint_data.upper is not None:
                    if constraint_data.lower is not None:
                        label = 'r_u_' + con_symbol + '_'
                    else:
                        label = 'c_u_' + con_symbol + '_'
                    alias_symbol_func(symbol_map, constraint_data, label)
                    output_file.write(" L  %s\n" % (label))
                    offset = extract_variable_coefficients(
                        label,
                        canonical_repn,
                        column_data,
                        quadmatrix_data,
                        variable_to_column)
                    bound = constraint_data.upper
                    bound = self._get_bound(bound) - offset
                    rhs_data.append((label, bound))

        if len(column_data[-1]) > 0:
            # ONE_VAR_CONSTANT = 1
            output_file.write(" E  c_e_ONE_VAR_CONSTANT\n")
            column_data[-1].append(("c_e_ONE_VAR_CONSTANT",1))
            rhs_data.append(("c_e_ONE_VAR_CONSTANT",1))

        #
        # COLUMNS section
        #
        column_template = "     %s %s %"+self._precision_string+"\n"
        output_file.write("COLUMNS\n")
        cnt = 0
        for vardata in variable_list:
            col_entries = column_data[variable_to_column[vardata]]
            cnt += 1
            if len(col_entries) > 0:
                var_label = variable_symbol_dictionary[id(vardata)]
                for i, (row_label, coef) in enumerate(col_entries):
                    output_file.write(column_template % (var_label,
                                                         row_label,
                                                         coef))
            elif include_all_variable_bounds:
                # the column is empty, so add a (0 * var)
                # term to the objective
                # * Note that some solvers (e.g., Gurobi)
                #   will accept an empty column as a line
                #   with just the column name. This doesn't
                #   seem to work for CPLEX 12.6, so I am
                #   doing it this way so that it will work for both
                var_label = variable_symbol_dictionary[id(vardata)]
                output_file.write(column_template % (var_label,
                                                     objective_label,
                                                     0))

        assert cnt == len(column_data)-1
        if len(column_data[-1]) > 0:
            col_entries = column_data[-1]
            var_label = "ONE_VAR_CONSTANT"
            for i, (row_label, coef) in enumerate(col_entries):
                output_file.write(column_template % (var_label,
                                                     row_label,
                                                     coef))

        #
        # RHS section
        #
        rhs_template = "     RHS %s %"+self._precision_string+"\n"
        output_file.write("RHS\n")
        for i, (row_label, rhs) in enumerate(rhs_data):
            output_file.write(rhs_template % (row_label, rhs))

        # SOS constraints
        SOSlines = StringIO()
        sos1 = solver_capability("sos1")
        sos2 = solver_capability("sos2")
        for block in all_blocks:

            for soscondata in block.component_data_objects(
                    SOSConstraint,
                    active=True,
                    sort=sortOrder,
                    descend_into=False):

                create_symbol_func(symbol_map, soscondata, labeler)

                level = soscondata.level
                if (level == 1 and not sos1) or \
                   (level == 2 and not sos2) or \
                   (level > 2):
                    raise ValueError(
                        "Solver does not support SOS level %s constraints" % (level))
                # This updates the referenced_variable_ids, just in case
                # there is a variable that only appears in an
                # SOSConstraint, in which case this needs to be known
                # before we write the "bounds" section (Cplex does not
                # handle this correctly, Gurobi does)
                self._printSOS(symbol_map,
                               labeler,
                               variable_symbol_map,
                               soscondata,
                               SOSlines)

        #
        # BOUNDS section
        #
        entry_template = "%s %"+self._precision_string+"\n"
        output_file.write("BOUNDS\n")
        for vardata in variable_list:
            if include_all_variable_bounds or \
               (id(vardata) in self._referenced_variable_ids):
                var_label = variable_symbol_dictionary[id(vardata)]
                if vardata.fixed:
                    if not output_fixed_variable_bounds:
                        raise ValueError(
                            "Encountered a fixed variable (%s) inside an active "
                            "objective or constraint expression on model %s, which is "
                            "usually indicative of a preprocessing error. Use the "
                            "IO-option 'output_fixed_variable_bounds=True' to suppress "
                            "this error and fix the variable by overwriting its bounds "
                            "in the MPS file." % (vardata.cname(True), model.cname(True)))
                    if vardata.value is None:
                        raise ValueError("Variable cannot be fixed to a value of None.")
                    output_file.write((" FX BOUND "+entry_template)
                                      % (var_label, value(vardata.value)))
                    continue

                vardata_lb = self._get_bound(vardata.lb)
                vardata_ub = self._get_bound(vardata.ub)
                # Make it harder for -0 to show up in
                # the output. This makes file diffing
                # for test baselines slightly less
                # annoying
                if vardata_lb == 0:
                    vardata_lb = 0
                if vardata_ub == 0:
                    vardata_ub = 0
                unbounded_lb = (vardata_lb is None) or (vardata_lb == -infinity)
                unbounded_ub = (vardata_ub is None) or (vardata_ub == infinity)
                treat_as_integer = False
                if vardata.is_binary():
                    if (vardata_lb == 0) and (vardata_ub == 1):
                        output_file.write(" BV BOUND %s\n" % (var_label))
                        continue
                    else:
                        # so we can add bounds
                        treat_as_integer = True
                if treat_as_integer or vardata.is_integer():
                    # Indicating unbounded integers is tricky because
                    # the only way to indicate a variable is integer
                    # is using the bounds section. Thus, we signify
                    # infinity with a large number (10E20)
                    # * Note: Gurobi allows values like inf and -inf
                    #         but CPLEX 12.6 does not, so I am just
                    #         using a large value
                    if not unbounded_lb:
                        output_file.write((" LI BOUND "+entry_template)
                                          % (var_label, vardata_lb))
                    else:
                        output_file.write(" LI BOUND %s -10E20\n" % (var_label))
                    if not unbounded_ub:
                        output_file.write((" UI BOUND "+entry_template)
                                          % (var_label, vardata_ub))
                    else:
                        output_file.write(" UI BOUND %s 10E20\n" % (var_label))
                else:
                    assert vardata.is_continuous()
                    if unbounded_lb and unbounded_ub:
                        output_file.write(" FR BOUND %s\n" % (var_label))
                    else:
                        if not unbounded_lb:
                            output_file.write((" LO BOUND "+entry_template)
                                              % (var_label, vardata_lb))
                        else:
                            output_file.write(" MI BOUND %s\n" % (var_label))

                        if not unbounded_ub:
                            output_file.write((" UP BOUND "+entry_template)
                                              % (var_label, vardata_ub))

        #
        # SOS section
        #
        output_file.write(SOSlines.getvalue())

        # Formatting of the next two sections comes from looking
        # at Gurobi and Cplex output

        #
        # QUADOBJ section
        #
        if len(quadobj_data) > 0:
            assert len(quadobj_data) == 1
            # it looks like the COIN-OR MPS Reader only
            # recognizes QUADOBJ (Gurobi and Cplex seem to
            # be okay with this)
            output_file.write("QUADOBJ\n")
            #output_file.write("QMATRIX\n")
            label, quad_terms = quadobj_data[0]
            assert label == objective_label
            for (var1, var2), coef in sorted(quad_terms,
                                             key=lambda _x: (variable_to_column[_x[0][0]],
                                                             variable_to_column[_x[0][1]])):
                var1_label = variable_symbol_dictionary[id(var1)]
                var2_label = variable_symbol_dictionary[id(var2)]
                # Don't forget that a quadratic objective is always
                # assumed to be divided by 2
                if var1_label == var2_label:
                    output_file.write(column_template % (var1_label,
                                                         var2_label,
                                                         coef * 2))
                else:
                    # the matrix needs to be symmetric so split
                    # the coefficient (but remember it is divided by 2)
                    output_file.write(column_template % (var1_label,
                                                         var2_label,
                                                         coef))
                    output_file.write(column_template % (var2_label,
                                                         var1_label,
                                                         coef))

        #
        # QCMATRIX section
        #
        if len(quadmatrix_data) > 0:
            for row_label, quad_terms in quadmatrix_data:
                output_file.write("QCMATRIX    %s\n" % (row_label))
                for (var1, var2), coef in sorted(quad_terms,
                                                 key=lambda _x: (variable_to_column[_x[0][0]],
                                                                 variable_to_column[_x[0][1]])):
                    var1_label = variable_symbol_dictionary[id(var1)]
                    var2_label = variable_symbol_dictionary[id(var2)]
                    if var1_label == var2_label:
                        output_file.write(column_template % (var1_label,
                                                             var2_label,
                                                             coef))
                    else:
                        # the matrix needs to be symmetric so split
                        # the coefficient
                        output_file.write(column_template % (var1_label,
                                                             var2_label,
                                                             coef * 0.5))
                        output_file.write(column_template % (var2_label,
                                                             var1_label,
                                                             coef * 0.5))

        output_file.write("ENDATA\n")

        # Clean up the symbol map to only contain variables referenced
        # in the active constraints **Note**: warm start method may
        # rely on this for choosing the set of potential warm start
        # variables
        vars_to_delete = set(variable_symbol_map.byObject.keys()) - \
                         set(self._referenced_variable_ids.keys())
        sm_byObject = symbol_map.byObject
        sm_bySymbol = symbol_map.bySymbol
        var_sm_byObject = variable_symbol_map.byObject
        for varid in vars_to_delete:
            symbol = var_sm_byObject[varid]
            del sm_byObject[varid]
            del sm_bySymbol[symbol]
        del variable_symbol_map

        return symbol_map
Example #45
0
def _collect_linear_const(exp, idMap, multiplier, coef, varmap, compute_values):
    if compute_values:
        coef[None] += multiplier * value(exp)
    else:
        coef[None] += multiplier * exp
Example #46
0
 def _get_bound(self, exp):
     if exp is None:
         return None
     if is_fixed(exp):
         return value(exp)
     raise ValueError("non-fixed bound: " + str(exp))
Example #47
0
    def _populate_gurobi_instance (self, pyomo_instance):

        from pyomo.core.base import Var, Objective, Constraint, SOSConstraint
        from pyomo.repn import LinearCanonicalRepn, canonical_degree

        try:
            grbmodel = Model(name=pyomo_instance.name)
        except Exception:
            e = sys.exc_info()[1]
            msg = 'Unable to create Gurobi model.  Have you installed the Python'\
            '\n       bindings for Gurobi?\n\n\tError message: %s'
            raise Exception(msg % e)

        if self._symbolic_solver_labels:
            labeler = TextLabeler()
        else:
            labeler = NumericLabeler('x')
        # cache to avoid dictionary getitem calls in the loops below.
        self_symbol_map = self._symbol_map = SymbolMap()
        pyomo_instance.solutions.add_symbol_map(self_symbol_map)
        self._smap_id = id(self_symbol_map)

        # we use this when iterating over the constraints because it
        # will have a much smaller hash table, we also use this for
        # the warm start code after it is cleaned to only contain
        # variables referenced in the constraints
        self_variable_symbol_map = self._variable_symbol_map = SymbolMap()
        var_symbol_pairs = []

        # maps _VarData labels to the corresponding Gurobi variable object
        pyomo_gurobi_variable_map = {}

        self._referenced_variable_ids.clear()

        # cache to avoid dictionary getitem calls in the loop below.
        grb_infinity = GRB.INFINITY

        for var_value in pyomo_instance.component_data_objects(Var, active=True):

            lb = -grb_infinity
            ub = grb_infinity

            if (var_value.lb is not None) and (var_value.lb != -infinity):
                lb = value(var_value.lb)
            if (var_value.ub is not None) and (var_value.ub != infinity):
                ub = value(var_value.ub)

            # _VarValue objects will not be in the symbol map yet, so
            # avoid some checks.
            var_value_label = self_symbol_map.createSymbol(var_value, labeler)
            var_symbol_pairs.append((var_value, var_value_label))

            # be sure to impart the integer and binary nature of any variables
            if var_value.is_integer():
                var_type = GRB.INTEGER
            elif var_value.is_binary():
                var_type = GRB.BINARY
            elif var_value.is_continuous():
                var_type = GRB.CONTINUOUS
            else:
                raise TypeError("Invalid domain type for variable with name '%s'. "
                                "Variable is not continuous, integer, or binary.")

            pyomo_gurobi_variable_map[var_value_label] = \
                grbmodel.addVar(lb=lb, \
                                ub=ub, \
                                vtype=var_type, \
                                name=var_value_label)

        self_variable_symbol_map.addSymbols(var_symbol_pairs)

        grbmodel.update()

        # The next loop collects the following component types from the model:
        #  - SOSConstraint
        #  - Objective
        #  - Constraint
        sos1 = self._capabilities.sos1
        sos2 = self._capabilities.sos2
        modelSOS = ModelSOS()
        objective_cntr = 0
        # Track the range constraints and their associated variables added by gurobi
        self._last_native_var_idx = grbmodel.NumVars-1
        range_var_idx = grbmodel.NumVars
        _self_range_con_var_pairs = self._range_con_var_pairs = []
        for block in pyomo_instance.block_data_objects(active=True):

            gen_obj_canonical_repn = \
                getattr(block, "_gen_obj_canonical_repn", True)
            gen_con_canonical_repn = \
                getattr(block, "_gen_con_canonical_repn", True)
            # Get/Create the ComponentMap for the repn
            if not hasattr(block,'_canonical_repn'):
                block._canonical_repn = ComponentMap()
            block_canonical_repn = block._canonical_repn

            # SOSConstraints
            for soscondata in block.component_data_objects(SOSConstraint,
                                                           active=True,
                                                           descend_into=False):
                level = soscondata.level
                if (level == 1 and not sos1) or \
                   (level == 2 and not sos2) or \
                   (level > 2):
                    raise RuntimeError(
                        "Solver does not support SOS level %s constraints" % (level,))
                modelSOS.count_constraint(self_symbol_map,
                                          labeler,
                                          self_variable_symbol_map,
                                          pyomo_gurobi_variable_map,
                                          soscondata)

            # Objective
            for obj_data in block.component_data_objects(Objective,
                                                         active=True,
                                                         descend_into=False):

                if objective_cntr > 1:
                    raise ValueError(
                        "Multiple active objectives found on Pyomo instance '%s'. "
                        "Solver '%s' will only handle a single active objective" \
                        % (pyomo_instance.cname(True), self.type))

                sense = GRB_MIN if (obj_data.is_minimizing()) else GRB_MAX
                grbmodel.ModelSense = sense
                obj_expr = LinExpr()

                if gen_obj_canonical_repn:
                    obj_repn = generate_canonical_repn(obj_data.expr)
                    block_canonical_repn[obj_data] = obj_repn
                else:
                    obj_repn = block_canonical_repn[obj_data]

                if isinstance(obj_repn, LinearCanonicalRepn):

                    if obj_repn.constant != None:
                        obj_expr.addConstant(obj_repn.constant)

                    if obj_repn.linear != None:

                        for i in xrange(len(obj_repn.linear)):
                            var_coefficient = obj_repn.linear[i]
                            var_value = obj_repn.variables[i]
                            self._referenced_variable_ids.add(id(var_value))
                            label = self_variable_symbol_map.getSymbol(var_value)
                            obj_expr.addTerms(var_coefficient,
                                              pyomo_gurobi_variable_map[label])
                else:

                    if 0 in obj_repn: # constant term
                        obj_expr.addConstant(obj_repn[0][None])

                    if 1 in obj_repn: # first-order terms
                        hash_to_variable_map = obj_repn[-1]
                        for var_hash, var_coefficient in iteritems(obj_repn[1]):
                            vardata = hash_to_variable_map[var_hash]
                            self._referenced_variable_ids.add(id(vardata))
                            label = self_variable_symbol_map.getSymbol(vardata)
                            obj_expr.addTerms(var_coefficient,
                                              pyomo_gurobi_variable_map[label])

                    if 2 in obj_repn:
                        obj_expr = QuadExpr(obj_expr)
                        hash_to_variable_map = obj_repn[-1]
                        for quad_repn, coef in iteritems(obj_repn[2]):
                            gurobi_expr = QuadExpr(coef)
                            for var_hash, exponent in iteritems(quad_repn):
                                vardata = hash_to_variable_map[var_hash]
                                self._referenced_variable_ids.add(id(vardata))
                                gurobi_var = pyomo_gurobi_variable_map\
                                             [self_variable_symbol_map.\
                                              getSymbol(vardata)]
                                gurobi_expr *= gurobi_var
                                if exponent == 2:
                                    gurobi_expr *= gurobi_var
                            obj_expr += gurobi_expr

                    degree = canonical_degree(obj_repn)
                    if (degree is None) or (degree > 2):
                        raise ValueError(
                            "gurobi_direct plugin does not support general nonlinear "
                            "objective expressions (only linear or quadratic).\n"
                            "Objective: %s" % (obj_data.cname(True)))

                # need to cache the objective label, because the
                # GUROBI python interface doesn't track this.
                # _ObjectiveData objects will not be in the symbol map
                # yet, so avoid some checks.
                self._objective_label = \
                    self_symbol_map.createSymbol(obj_data, labeler)

                grbmodel.setObjective(obj_expr, sense=sense)

            # Constraint
            for constraint_data in block.component_data_objects(Constraint,
                                                                active=True,
                                                                descend_into=False):

                if (constraint_data.lower is None) and \
                   (constraint_data.upper is None):
                    continue  # not binding at all, don't bother

                con_repn = None
                if isinstance(constraint_data, LinearCanonicalRepn):
                    con_repn = constraint_data
                else:
                    if gen_con_canonical_repn:
                        con_repn = generate_canonical_repn(constraint_data.body)
                        block_canonical_repn[constraint_data] = con_repn
                    else:
                        con_repn = block_canonical_repn[constraint_data]

                offset = 0.0
                # _ConstraintData objects will not be in the symbol
                # map yet, so avoid some checks.
                constraint_label = \
                    self_symbol_map.createSymbol(constraint_data, labeler)

                trivial = False
                if isinstance(con_repn, LinearCanonicalRepn):

                    #
                    # optimization (these might be generated on the fly)
                    #
                    constant = con_repn.constant
                    coefficients = con_repn.linear
                    variables = con_repn.variables

                    if constant is not None:
                        offset = constant
                    expr = LinExpr() + offset

                    if coefficients is not None:

                        linear_coefs = list()
                        linear_vars = list()

                        for i in xrange(len(coefficients)):

                            var_coefficient = coefficients[i]
                            var_value = variables[i]
                            self._referenced_variable_ids.add(id(var_value))
                            label = self_variable_symbol_map.getSymbol(var_value)
                            linear_coefs.append(var_coefficient)
                            linear_vars.append(pyomo_gurobi_variable_map[label])

                        expr += LinExpr(linear_coefs, linear_vars)

                    else:

                        trivial = True

                else:

                    if 0 in con_repn:
                        offset = con_repn[0][None]
                    expr = LinExpr() + offset

                    if 1 in con_repn: # first-order terms

                        linear_coefs = list()
                        linear_vars = list()

                        hash_to_variable_map = con_repn[-1]
                        for var_hash, var_coefficient in iteritems(con_repn[1]):
                            var = hash_to_variable_map[var_hash]
                            self._referenced_variable_ids.add(id(var))
                            label = self_variable_symbol_map.getSymbol(var)
                            linear_coefs.append( var_coefficient )
                            linear_vars.append( pyomo_gurobi_variable_map[label] )

                        expr += LinExpr(linear_coefs, linear_vars)

                    if 2 in con_repn: # quadratic constraint
                        if _GUROBI_VERSION_MAJOR < 5:
                            raise ValueError(
                                "The gurobi_direct plugin does not handle quadratic "
                                "constraint expressions for Gurobi major versions "
                                "< 5. Current version: Gurobi %s.%s%s"
                                % (gurobi.version()))

                        expr = QuadExpr(expr)
                        hash_to_variable_map = con_repn[-1]
                        for quad_repn, coef in iteritems(con_repn[2]):
                            gurobi_expr = QuadExpr(coef)
                            for var_hash, exponent in iteritems(quad_repn):
                                vardata = hash_to_variable_map[var_hash]
                                self._referenced_variable_ids.add(id(vardata))
                                gurobi_var = pyomo_gurobi_variable_map\
                                             [self_variable_symbol_map.\
                                              getSymbol(vardata)]
                                gurobi_expr *= gurobi_var
                                if exponent == 2:
                                    gurobi_expr *= gurobi_var
                            expr += gurobi_expr

                    degree = canonical_degree(con_repn)
                    if (degree is None) or (degree > 2):
                        raise ValueError(
                            "gurobi_direct plugin does not support general nonlinear "
                            "constraint expressions (only linear or quadratic).\n"
                            "Constraint: %s" % (constraint_data.cname(True)))

                if (not trivial) or (not self._skip_trivial_constraints):

                    if constraint_data.equality:
                        sense = GRB.EQUAL
                        bound = self._get_bound(constraint_data.lower)
                        grbmodel.addConstr(lhs=expr,
                                           sense=sense,
                                           rhs=bound,
                                           name=constraint_label)
                    else:
                        # L <= body <= U
                        if (constraint_data.upper is not None) and \
                           (constraint_data.lower is not None):
                            grb_con = grbmodel.addRange(
                                expr,
                                self._get_bound(constraint_data.lower),
                                self._get_bound(constraint_data.upper),
                                constraint_label)
                            _self_range_con_var_pairs.append((grb_con,range_var_idx))
                            range_var_idx += 1
                        # body <= U
                        elif constraint_data.upper is not None:
                            bound = self._get_bound(constraint_data.upper)
                            if bound < float('inf'):
                                grbmodel.addConstr(
                                    lhs=expr,
                                    sense=GRB.LESS_EQUAL,
                                    rhs=bound,
                                    name=constraint_label
                                    )
                        # L <= body
                        else:
                            bound = self._get_bound(constraint_data.lower)
                            if bound > -float('inf'):
                                grbmodel.addConstr(
                                    lhs=expr,
                                    sense=GRB.GREATER_EQUAL,
                                    rhs=bound,
                                    name=constraint_label
                                    )

        if modelSOS.sosType:
            for key in modelSOS.sosType:
                grbmodel.addSOS(modelSOS.sosType[key], \
                                modelSOS.varnames[key], \
                                modelSOS.weights[key] )
                self._referenced_variable_ids.update(modelSOS.varids[key])

        for var_id in self._referenced_variable_ids:
            varname = self._variable_symbol_map.byObject[var_id]
            vardata = self._variable_symbol_map.bySymbol[varname]()
            if vardata.fixed:
                if not self._output_fixed_variable_bounds:
                    raise ValueError("Encountered a fixed variable (%s) inside an active objective "
                                     "or constraint expression on model %s, which is usually indicative of "
                                     "a preprocessing error. Use the IO-option 'output_fixed_variable_bounds=True' "
                                     "to suppress this error and fix the variable by overwriting its bounds in "
                                     "the Gurobi instance."
                                     % (vardata.cname(True),pyomo_instance.cname(True),))

                grbvar = pyomo_gurobi_variable_map[varname]
                grbvar.setAttr(GRB.Attr.UB, vardata.value)
                grbvar.setAttr(GRB.Attr.LB, vardata.value)

        grbmodel.update()

        self._gurobi_instance = grbmodel
        self._pyomo_gurobi_variable_map = pyomo_gurobi_variable_map
Example #48
0
 def to_common_form(self, cdata, free_vars):
     """
     Convert a common form that can processed by AMPL
     """
     _e1 = cdata._canonical_expression(cdata._args[0])
     _e2 = cdata._canonical_expression(cdata._args[1])
     if False:  # pragma:nocover
         if _e1[0] is None:
             print(None)
         else:
             print(str(_e1[0]))
         if _e1[1] is None:
             print(None)
         else:
             print(str(_e1[1]))
         if len(_e1) > 2:
             if _e1[2] is None:
                 print(None)
             else:
                 print(str(_e1[2]))
         if _e2[0] is None:
             print(None)
         else:
             print(str(_e2[0]))
         if _e2[1] is None:
             print(None)
         else:
             print(str(_e2[1]))
         if len(_e2) > 2:
             if _e2[2] is None:
                 print(None)
             else:
                 print(str(_e2[2]))
     if len(_e1) == 2:
         cdata.c = Constraint(expr=_e1)
         return
     if len(_e2) == 2:
         cdata.c = Constraint(expr=_e2)
         return
     if (_e1[0] is None) + (_e1[2] is None) + (_e2[0] is None) + (_e2[2] is None) != 2:
         raise RuntimeError("Complementarity condition %s must have exactly two finite bounds" % cdata.name)
     #
     # Swap if the body of the second constraint is not a free variable
     #
     if not id(_e2[1]) in free_vars and id(_e1[1]) in free_vars:
         _e1, _e2 = _e2, _e1
     #
     # Rework the first constraint to have a zero bound.
     # The bound is a lower or upper bound depending on the
     # variable bound.
     #
     if not _e1[0] is None:
         cdata.bv = Var()
         cdata.c = Constraint(expr=0 <= cdata.bv)
         if not _e2[0] is None:
             cdata.bc = Constraint(expr=cdata.bv == _e1[1] - _e1[0])
         else:
             cdata.bc = Constraint(expr=cdata.bv == _e1[0] - _e1[1])
     elif not _e1[2] is None:
         cdata.bv = Var()
         cdata.c = Constraint(expr=0 <= cdata.bv)
         if not _e2[2] is None:
             cdata.bc = Constraint(expr=cdata.bv == _e1[1] - _e1[2])
         else:
             cdata.bc = Constraint(expr=cdata.bv == _e1[2] - _e1[1])
     else:
         cdata.bv = Var()
         cdata.bc = Constraint(expr=cdata.bv == _e1[1])
         cdata.c = Constraint(expr=(None, cdata.bv, None))
     #
     # If the body of the second constraint is a free variable, then keep it.
     # Otherwise, create a new variable and a new constraint.
     #
     if id(_e2[1]) in free_vars:
         var = _e2[1]
         cdata.c._vid = id(_e2[1])
         del free_vars[cdata.c._vid]
     else:
         var = cdata.v = Var()
         cdata.c._vid = id(cdata.v)
         cdata.e = Constraint(expr=cdata.v == _e2[1])
     #
     # Set the variable bound values, and corresponding _complementarity value
     #
     cdata.c._complementarity = 0
     if not _e2[0] is None:
         if var.lb is None or value(_e2[0]) > value(var.lb):
             var.setlb(_e2[0])
         cdata.c._complementarity += 1
     if not _e2[2] is None:
         if var.ub is None or value(_e2[2]) > value(var.ub):
             var.setub(_e2[2])
         cdata.c._complementarity += 2
Example #49
0
def convert_dakota(options=Options(), parser=None):
    #
    # Import plugins
    #
    import pyomo.environ

    model_file = os.path.basename(options.model.save_file)
    model_file_no_ext = os.path.splitext(model_file)[0]

    #
    # Set options for writing the .nl and related files
    #

    # By default replace .py with .nl
    if options.model.save_file is None:
       options.model.save_file = model_file_no_ext + '.nl'
    options.model.save_format = ProblemFormat.nl
    # Dakota requires .row/.col files
    options.model.symbolic_solver_labels = True

    #
    # Call the core converter
    #
    model_data = convert(options, parser)

    #
    # Generate Dakota input file fragments for the Vars, Objectives, Constraints
    #

    # TODO: the converted model doesn't expose the right symbol_map
    #       for only the vars active in the .nl

    model = model_data.instance

    # Easy way
    #print "VARIABLE:"
    #lines = open(options.save_model.replace('.nl','.col'),'r').readlines()
    #for varName in lines:
    #    varName = varName.strip()
    #    var = model_data.symbol_map.getObject(varName)
    #    print "'%s': %s" % (varName, var)
    #    #print var.pprint()

    # Hard way
    variables = 0
    var_descriptors = []
    var_lb = []
    var_ub = []
    var_initial = []
    tmpDict = model_data.symbol_map.getByObjectDictionary()
    for var in model.component_data_objects(Var, active=True):
        if id(var) in tmpDict:
            variables += 1
            var_descriptors.append(var.cname(True))

            # apply user bound, domain bound, or infinite
            _lb, _ub = var.bounds
            if _lb is not None:
                var_lb.append(str(_lb))
            else:
                var_lb.append("-inf")

            if _ub is not None:
                var_ub.append(str(_ub))
            else:
                var_ub.append("inf")

            try:
                val = value(var)
            except:
                val = None
            var_initial.append(str(val))

    objectives = 0
    obj_descriptors = []
    for obj in model.component_data_objects(Objective, active=True):
        objectives += 1
        obj_descriptors.append(obj.cname(True))

    constraints = 0
    cons_descriptors = []
    cons_lb = []
    cons_ub = []
    for con in model.component_data_objects(Constraint, active=True):
        constraints += 1
        cons_descriptors.append(con.cname(True))
        if con.lower is not None:
            cons_lb.append(str(con.lower))
        else:
            cons_lb.append("-inf")
        if con.upper is not None:
            cons_ub.append(str(con.upper))
        else:
            cons_ub.append("inf")

    # Write the Dakota input file fragments

    dakfrag = open(model_file_no_ext + ".dak", 'w')

    dakfrag.write("#--- Dakota variables block ---#\n")
    dakfrag.write("variables\n")
    dakfrag.write("  continuous_design " + str(variables) + '\n')
    dakfrag.write("    descriptors\n")
    for vd in var_descriptors:
        dakfrag.write("      '%s'\n" % vd)
    dakfrag.write("    lower_bounds " + " ".join(var_lb) + '\n')
    dakfrag.write("    upper_bounds " + " ".join(var_ub) + '\n')
    dakfrag.write("    initial_point " + " ".join(var_initial) + '\n')

    dakfrag.write("#--- Dakota interface block ---#\n")
    dakfrag.write("interface\n")
    dakfrag.write("  algebraic_mappings = '" + options.model.save_file  + "'\n")

    dakfrag.write("#--- Dakota responses block ---#\n")
    dakfrag.write("responses\n")
    dakfrag.write("  objective_functions " + str(objectives) + '\n')

    if (constraints > 0):
        dakfrag.write("  nonlinear_inequality_constraints " + str(constraints) + '\n')
        dakfrag.write("    lower_bounds " + " ".join(cons_lb) + '\n')
        dakfrag.write("    upper_bounds " + " ".join(cons_ub) + '\n')

    dakfrag.write("    descriptors\n")
    for od in obj_descriptors:
        dakfrag.write("      '%s'\n" % od)
    if (constraints > 0):
        for cd in cons_descriptors:
            dakfrag.write("      '%s'\n" % cd)

    # TODO: detect whether gradient information available in model
    dakfrag.write("  analytic_gradients\n")
    dakfrag.write("  no_hessians\n")

    dakfrag.close()

    sys.stdout.write( "Dakota input fragment written to file '%s'\n" 
                      % (model_file_no_ext + ".dak",) )
    return model_data
Example #50
0
def collect_general_canonical_repn(exp, idMap, compute_values):
#     global temp_const
#     global temp_var
#     global temp_nonl
    temp_const = { 0: {None:0.0} }
    temp_var = { 1: {GeneralCanonicalRepn({None:1}):1.0} }
    temp_nonl = { None: None }
    exp_type = type(exp)

    #
    # Constant
    #
    if exp.is_fixed():
        if compute_values:
            temp_const[0][None] = value(exp)
        else:
            temp_const[0][None] = exp
        return temp_const
    #
    # Expression
    #
    elif exp.is_expression():

        #
        # Sum
        #
        if exp_type is expr._SumExpression:
            if exp._const != 0.0:
                repn = { 0: {None:exp._const} }
            else:
                repn = {}
            for i in xrange(len(exp._args)):
                repn = repn_add(
                    repn,
                    collect_general_canonical_repn(exp._args[i],
                                                   idMap,
                                                   compute_values),
                    coef=exp._coef[i])
            return repn
        #
        # Product
        #
        elif exp_type is expr._ProductExpression:
            #
            # Iterate through the denominator.  If they aren't all
            # constants, then simply return this expression.
            #
            denom=1.0
            for e in exp._denominator:
                if e.is_fixed():
                    denom *= e()
                else:
                    temp_nonl[None] = exp
                    return temp_nonl
                if denom == 0.0:
                    print("Divide-by-zero error - offending sub-expression:")
                    e.pprint()
                    raise ZeroDivisionError
            #
            # OK, the denominator is a constant.
            #
            repn = { 0: {None:exp._coef / denom} }
            for e in exp._numerator:
                repn = repn_mult(
                    repn,
                    collect_general_canonical_repn(e,
                                                   idMap,
                                                   compute_values))
            return repn
        #
        # Power Expression
        #
        elif exp_type is expr._PowExpression:
            if exp.polynomial_degree() is None:
                raise TypeError("Unsupported general power expression: "
                                +str(exp._args))

            # If this is of the form EXPR**1, we can just get the
            # representation of EXPR
            if exp._args[1] == 1:
                return collect_general_canonical_repn(exp._args[0],
                                                      idMap,
                                                      compute_values)
            # The only other way to get a polynomial expression is if
            # exp=EXPR**p where p is fixed a nonnegative integer.  We
            # can expand this expression and generate a canonical
            # representation from there.  If p=0, this expression is
            # constant (and is processed by the is_fixed code above
            # NOTE: There is no check for 0**0
            return collect_general_canonical_repn(
                        reduce( lambda x,y: x*y, [exp._args[0]]*int(value(exp._args[1])), 1.0 ),
                        idMap,
                        compute_values)
        elif exp_type is expr.Expr_if:
            if exp._if.is_fixed():
                if exp._if():
                    return collect_general_canonical_repn(exp._then,
                                                          idMap,
                                                          compute_values)
                else:
                    return collect_general_canonical_repn(exp._else,
                                                          idMap,
                                                          compute_values)
            else:
                temp_nonl[None] = exp
                return temp_nonl
        #
        # Expression (the component)
        # (faster check)
        elif isinstance(exp, _ExpressionData):
            return collect_general_canonical_repn(exp.expr,
                                                  idMap,
                                                  compute_values)
        #
        # ERROR
        #
        else:
            raise ValueError("Unsupported expression type: "+str(exp))
    #
    # Variable
    #
    elif (exp.__class__ is _GeneralVarData) or isinstance(exp, _VarData):
        id_ = id(exp)
        if id_ in idMap[None]:
            key = idMap[None][id_]
        else:
            key = len(idMap) - 1
            idMap[None][id_] = key
            idMap[key] = exp
        temp_var = { -1: {key:exp}, 1: {GeneralCanonicalRepn({key:1}):1.0} }
        return temp_var
    #
    # Connector
    #
    elif exp_type is _ConnectorValue or exp.type() is Connector:
        # Silently omit constraint...  The ConnectorExpander should
        # expand this constraint into indvidual constraints that
        # reference "real" variables.
        return {}
    #
    # ERROR
    #
    else:
        raise ValueError("Unexpected expression (type %s): " %
                         ( type(exp).__name__, str(exp) ))
Example #51
0
def pyomo4_generate_canonical_repn(exp, idMap=None, compute_values=True):
    if exp is None:
        return CompiledLinearCanonicalRepn()
    if exp.__class__ in native_numeric_types:
        ans = CompiledLinearCanonicalRepn()
        ans.constant = value(exp)
        return ans
    if not exp.is_expression():
        if exp.is_fixed():
            ans = CompiledLinearCanonicalRepn()
            ans.constant = value(exp)
            return ans
        elif isinstance(exp, _VarData):
            ans = CompiledLinearCanonicalRepn()
            ans.constant = 0
            ans.linear = (1.,)
            ans.variables = (exp,)
            return ans
        else:
            raise RuntimeError(
                "Unrecognized expression node: %s" % (type(exp),) )

    degree = exp.polynomial_degree()

    if degree == 1:
        _stack = []
        _args = exp._args
        _idx = 0
        _len = len(_args)
        _result = None
        while 1:
            # Linear expressions just need to be filteres and copied
            if exp.__class__ is expr_pyomo4._LinearExpression:
                _result = expr_pyomo4._LinearExpression(None, 0)
                _result._args = []
                _result._coef.clear()
                _result._const = value(exp._const)
                for v in _args:
                    _id = id(v)
                    if v.is_fixed():
                        _result._const += v.value * value(exp._coef[_id])
                    else:
                        _result._args.append(v)
                        _result._coef[_id] = value(exp._coef[_id])
                _idx = _len

            # Other expressions get their arguments parsed one at a time
            if _idx < _len:
                _stack.append((exp, _args, _idx+1, _len, _result))
                exp = _args[_idx]
                if exp.__class__ in native_numeric_types:
                    _len = _idx = 0
                    _result = exp
                elif exp.is_expression():
                    _args = exp._args
                    _idx = 0
                    _len = len(_args)
                    _result = None
                    continue
                elif isinstance(exp, _VarData):
                    _len = _idx = 0
                    if exp.is_fixed():
                        _result = exp.value
                    else:
                        _result = expr_pyomo4._LinearExpression(exp, 1.)
                else:
                    raise RuntimeError(
                        "Unrecognized expression node: %s" % (type(exp),) )

            #
            # End of _args... time to move up the stack
            #

            # Top of the stack.  _result had better be a _LinearExpression
            if not _stack:
                ans = CompiledLinearCanonicalRepn()
                # old format
                ans.constant = _result._const
                ans.linear = []
                for v in _result._args:
                    # Note: this also filters out the bogus NONE we added above
                    _coef = _result._coef[id(v)]
                    if _coef:
                        ans.variables.append(v)
                        ans.linear.append(_coef)

                if idMap:
                    if None not in idMap:
                        idMap[None] = {}
                    _test = idMap[None]
                    _key = len(idMap) - 1
                    for v in ans.variables:
                        if id(v) not in _test:
                            _test[id(v)] = _key
                            idMap[_key] = v
                            _key += 1
                return ans

            # Ok ... process the new argument to the node.  Note that
            # _idx is 1-based now...
            _inner_result = _result
            exp, _args, _idx, _len, _result = _stack.pop()
            if exp.__class__ is expr_pyomo4._SumExpression:
                if _idx == 1:
                    _result = _inner_result
                else:
                    _result += _inner_result
            elif exp.__class__ is expr_pyomo4._ProductExpression:
                if _idx == 1:
                    _result = _inner_result
                else:
                    _result *= _inner_result
            elif exp.__class__ is expr_pyomo4._DivisionExpression:
                if _idx == 1:
                    _result = _inner_result
                else:
                    _result /= _inner_result
            elif exp.__class__ is expr_pyomo4._NegationExpression:
                _result = -_inner_result
            elif exp.__class__ is expr_pyomo4._PowExpression:
                # We know this is either constant or linear
                if _idx == 1:
                    _result = _inner_result
                else:
                    coef = value(_inner_result)
                    if not coef:
                        _result = 1.
                    elif coef != 1:
                        _result = _result ** coef
            elif exp.__class__ is expr_pyomo4.Expr_if:
                if _idx == 1:
                    _result = [_inner_result]
                else:
                    _result.append(_inner_result)
                if _idx == 3:
                    if value(_result[0]):
                        _result = _result[1]
                    else:
                        _result = _result[2]
            elif exp.__class__ in _identity_collectors:
                _result = _inner_result
            elif exp.is_fixed():
                _result = value(exp)
            else:
                raise RuntimeError(
                    "Unknown non-fixed subexpression type %s" % (type(exp),) )

    elif degree == 0:
        ans = CompiledLinearCanonicalRepn()
        ans.constant = value(exp)
        return ans


    # **Py3k: degree > 1 comparision will error if degree is None
    elif degree and degree > 1:
        raise RuntimeError("generate_canonical_repn does not support nonlinear Pyomo4 expressions")

        ans = collect_general_canonical_repn(exp, idMap, compute_values)
        if 1 in ans:
            linear_terms = {}
            for key, coef in iteritems(ans[1]):
                linear_terms[list(key.keys())[0]] = coef
            ans[1] = linear_terms
        return GeneralCanonicalRepn(ans)
    else:
        return GeneralCanonicalRepn(
            { None: exp, -1 : collect_variables(exp, idMap) } )