Ejemplo n.º 1
0
def clone_without_expression_components(expr, substitute=None):
    """A function that is used to clone an expression.

    Cloning is roughly equivalent to calling ``copy.deepcopy``.
    However, the :attr:`clone_leaves` argument can be used to
    clone only interior (i.e. non-leaf) nodes in the expression
    tree.   Note that named expression objects are treated as
    leaves when :attr:`clone_leaves` is :const:`True`, and hence
    those subexpressions are not cloned.

    This function uses a non-recursive
    logic, which makes it more scalable than the logic in
    ``copy.deepcopy``.

    Args:
        expr: The expression that will be cloned.
        substitute (dict): A dictionary mapping object ids to
            objects.  This dictionary has the same semantics as
            the memo object used with ``copy.deepcopy``.  Defaults
            to None, which indicates that no user-defined
            dictionary is used.

    Returns:
        The cloned expression.
    """
    if substitute is None:
        substitute = {}
    #
    visitor = EXPR.ExpressionReplacementVisitor(substitute=substitute,
                                                remove_named_expressions=True)
    return visitor.dfs_postorder_stack(expr)
Ejemplo n.º 2
0
def grad_fd(c, scaled=False, h=1e-6):
    """Finite difference the gradient for a constraint, objective or named
    expression.  This is only for use in examining scaling.  For faster more
    accurate gradients refer to pynumero.

    Args:
        c: constraint to evaluate
        scaled: if True calculate the scaled grad (default=False)
        h: step size for calculating finite differnced derivatives

    Returns:
        (list of gradient values, list for varibles in the constraint) The order
            of the variables coresoponds to the gradient values.
    """
    try:
        ex = c.body
    except AttributeError:
        ex = c.expr

    vars = list(EXPR.identify_variables(ex))
    grad = [None]*len(vars)

    r = {}
    if scaled: # If you want the scaled grad put in variable scaling tansform
        orig = [pyo.value(v) for v in vars]
        for i, v in enumerate(vars):
            try:
                sf = v.parent_block().scaling_factor.get(v, 1)
            except AttributeError:
                sf = 1
            r[id(v)] = v/sf
            v.value = orig[i]*sf
        vis = EXPR.ExpressionReplacementVisitor(
            substitute=r,
            remove_named_expressions=True,
        )
        e = vis.dfs_postorder_stack(ex)
    else:
        e = ex

    for i, v in enumerate(vars):
        ov = pyo.value(v) # original variable value
        f1 = pyo.value(e)
        v.value = ov + h
        f2 = pyo.value(e)
        v.value = ov
        if scaled:
            try:
                sf = c.parent_block().scaling_factor.get(c, 1)
            except AttributeError:
                sf = 1
            grad[i] = sf*(f2 - f1)/h
        else:
            grad[i] = (f2 - f1)/h

    if scaled:
        for i, v in enumerate(vars):
            v.value = orig[i]

    return grad, vars
Ejemplo n.º 3
0
    def replace(instance, substitute):
        # Create the replacement dict. Do some argument validation and indexed
        # var handling
        d = {}
        for r in substitute:
            if not (_is_var(r[0]) and _is_var(r[1])):
                raise TypeError(
                    "Replace only allows variables to be replaced, {} is type {}"
                    " and {} is type {}".format(r[0], type(r[0]), r[1],
                                                type(r[1])))
            if r[0].is_indexed() != r[1].is_indexed():
                raise TypeError(
                    "IndexedVars must be replaced by IndexedVars, {} is type {}"
                    " and {} is type {}".format(r[0], type(r[0]), r[1],
                                                type(r[1])))
            if r[0].is_indexed() and r[1].is_indexed():
                if not r[0].index_set().issubset(r[1].index_set()):
                    raise ValueError("The index set of {} must be a subset of"
                                     " {}.".format(r[0], r[1]))
                for i in r[0]:
                    d[id(r[0][i])] = r[1][i]
            else:
                #scalar replace
                d[id(r[0])] = r[1]

        # Replacement Visitor
        vis = EXPR.ExpressionReplacementVisitor(
            substitute=d,
            descend_into_named_expressions=True,
            remove_named_expressions=False,
        )

        # Do replacements in Expressions, Constraints, and Objectives
        for c in instance.component_data_objects(
            (Constraint, Expression, Objective),
                descend_into=True,
                active=True):
            c.set_value(expr=vis.dfs_postorder_stack(c.expr))
Ejemplo n.º 4
0
    def _apply_to(self, instance, max_iter=5, reversible=True):
        """
        Apply the transformation.  This is called by ``apply_to`` in the
        superclass, and should not be called directly.  ``apply_to`` takes the
        same arguments.

        Args:
            instance: A block or model to apply the transformation to

        Returns:
            None
        """
        self.reversible = reversible
        if reversible:
            self._instance = instance
            self._all_subs = []
            self._subs_map = {}
            self._expr_map = {}
            self._all_fixes = []
            self._all_deactivate = []
            self._original = {}

            # The named expressions could be changed as a side effect of the
            # constraint expression replacements, so for maximum saftey, just
            # store all the expressions for Expressions
            for c in instance.component_data_objects(
                pyo.Expression,
                descend_into=True,
            ):
                self._original[id(c)] = c.expr
                self._expr_map[id(c)] = c

        nr_tot = 0
        # repeat elimination until no more can be eliminated or hit max_iter
        for i in range(max_iter):
            subs, cnstr, fixes, subs_map = self._get_subs(instance)

            if reversible:
                self._all_fixes += fixes
                self._all_deactivate += cnstr
                self._all_subs.append(subs)
                self._subs_map.update(subs_map)

            nr = len(cnstr)
            if nr == 0:
                break
            nr_tot += nr

            for c in cnstr: # deactivate constraints that aren't needed
                c.deactivate()
            for v in fixes: # fix variables that can be fixed
                v[0].fix(v[1])

            # Do replacements in Expressions, Constraints, and Objectives
            # where one var is replaced with a linear expression containing
            # another
            vis = EXPR.ExpressionReplacementVisitor(
                substitute=subs,
                descend_into_named_expressions=True,
                remove_named_expressions=False,
            )
            for c in instance.component_data_objects(
                (pyo.Constraint, pyo.Objective),
                descend_into=True,
                active=True
            ):
                if id(c) not in self._original and reversible:
                    self._original[id(c)] = c.expr
                    self._expr_map[id(c)] = c
                c.set_value(expr=vis.dfs_postorder_stack(c.expr))

        _log.info("Eliminated {} variables and constraints".format(nr_tot))
Ejemplo n.º 5
0
def _replacement(m, basis):
    """PRIVATE FUNCTION
    Create a replacement visitor. The replacement visitor is used on
    user-provided scaling expressions. These expressions are written
    with model variables, but you generally don't want to calculate
    scaling factors based on the curent value of the model variables,
    you want to use their scaling factors, so the replacment visitor
    takes the user-defined scaling expression and replaces the model
    varible by some scaling factor, and returns a new expression. The
    basis argument can be used to specify the basis to use for scaling.

    Args:
        m (Block): model to collect vars from
        basis (list of ScalingBasis): value type to use as basis for scaling calcs

    Return:
        None or ExpressionReplacementVisitor

    """
    # These long ifs up front find values to replace variables in the scaling
    # expressions with.
    if basis[0] == ScalingBasis.Value:
        return None # no need to replace anything if using value
    else:
        rdict = {}
        for v in m.component_data_objects((pyo.Var)):
            val = 1.0
            for b in basis:
                try:
                    if b == ScalingBasis.VarScale:
                        val = v.parent_block().scaling_factor[v]
                        break
                    elif b == ScalingBasis.InverseVarScale:
                        val = 1/v.parent_block().scaling_factor[v]
                        break
                    elif b == ScalingBasis.Value:
                        val = pyo.value(v)
                        break
                    elif b == ScalingBasis.Mid:
                        if v.lb is not None and v.ub is not None:
                            val = (v.ub + v.lb)/2.0
                            break
                    elif b == ScalingBasis.Lower:
                        if v.lb is not None:
                            val = v.lb
                            break
                    elif b == ScalingBasis.Upper:
                        if v.ub is not None:
                            val = v.ub
                            break
                    else:
                        _log.warning("Unknown scaling expression basis {}".format(b))
                except AttributeError:
                    pass
                except KeyError:
                    pass
            rdict[id(v)] = val
        for v in m.component_data_objects((pyo.Expression)):
            # check for expression scaling factors, while expressions don't
            # get scaled, the factor can be used in the calculation of other
            # scale factors.
            val = 1.0
            for b in basis:
                try:
                    if b == ScalingBasis.VarScale:
                        val = v.parent_block().scaling_factor[v]
                        break
                    elif b == ScalingBasis.InverseVarScale:
                        val = 1/v.parent_block().scaling_factor[v]
                        break
                    elif b == ScalingBasis.Value:
                        val = pyo.value(v)
                        break
                    else: # Expressions don't have bounds
                        continue
                except AttributeError:
                    pass
                except KeyError:
                    pass
            rdict[id(v)] = val
        # Use the substitutions dictionary from above to make a replacemnt visitor
        return EXPR.ExpressionReplacementVisitor(substitute=rdict)