Пример #1
0
    def __init__(self, exps=None, cs=None, nomials=None, simplify=True):
        if nomials and (exps or cs):
            raise ValueError("The NomialData initializor accepts either"
                             " exps and cs, or nomials, but not both.")
        elif nomials:
            self.nomials = nomials
            exps = functools_reduce(add, (tuple(s.exps) for s in nomials))
            cs = np.hstack((mag(s.cs) for s in nomials))
            simplify = False  # nomials have already been simplified
        elif exps is None or cs is None:
            raise ValueError("creation of a NomialData requires exps and cs.")

        if simplify:
            exps, cs = simplify_exps_and_cs(exps, cs)
        self.exps, self.cs = exps, cs
        self.any_nonpositive_cs = any(mag(c) <= 0 for c in self.cs)
        self.varlocs, self.varstrs = locate_vars(self.exps)
        self.values = {vk: vk.descr["value"] for vk in self.varlocs
                       if "value" in vk.descr}
        if nomials:
            self.units = tuple(s.units for s in nomials)
        elif isinstance(self.cs, Quantity):
            self.units = Quantity(1, self.cs.units)
        else:
            self.units = None

        self._hashvalue = None
Пример #2
0
 def init_from_nomials(self, nomials):
     """Way to initialize from nomials. Calls __init__.
     Used by subclass __init__ methods.
     """
     exps = functools_reduce(add, (tuple(s.exps) for s in nomials))
     cs = np.hstack((mag(s.cs) for s in nomials))
     # nomials are already simplified, so simplify=False
     NomialData.__init__(self, exps, cs, simplify=False)
     self.units = tuple(s.units for s in nomials)
Пример #3
0
    def _collect_explicit_graded(cls, block_structure):
        """
        Collect the 'explicit_graded' field for every block.
        """

        def _set_field(block_key, field_value):
            """
            Sets the explicit graded field to the given value for the
            given block.
            """
            block_structure.set_transformer_block_field(block_key, cls, cls.EXPLICIT_GRADED_FIELD_NAME, field_value)

        def _get_field(block_key):
            """
            Gets the explicit graded field to the given value for the
            given block.
            """
            return block_structure.get_transformer_block_field(block_key, cls, cls.EXPLICIT_GRADED_FIELD_NAME)

        block_types_to_ignore = {"course", "chapter", "sequential"}

        for block_key in block_structure.topological_traversal():
            if block_key.block_type in block_types_to_ignore:
                _set_field(block_key, None)
            else:
                explicit_field_on_block = get_field_on_block(block_structure.get_xblock(block_key), "graded")
                if explicit_field_on_block is not None:
                    _set_field(block_key, explicit_field_on_block)
                else:
                    values_from_parents = [
                        _get_field(parent)
                        for parent in block_structure.get_parents(block_key)
                        if parent.block_type not in block_types_to_ignore
                    ]
                    non_null_values_from_parents = [value for value in values_from_parents if not None]
                    explicit_from_parents = functools_reduce(lambda x, y: x or y, non_null_values_from_parents, None)
                    _set_field(block_key, explicit_from_parents)
Пример #4
0
def parse_result(result, constants, beforesubs, sweep={}, linkedsweep={},
                 freevar_sensitivity_tolerance=1e-4,
                 localmodel_sensitivity_requirement=0.1):
    "Parses a GP-like result dict into a SolutionArray-like dict."
    cost = result["cost"]
    freevariables = dict(result["variables"])
    sweepvariables = {var: val for var, val in constants.items()
                      if var in sweep or var in linkedsweep}
    constants = {var: val for var, val in constants.items()
                 if var not in sweepvariables}
    variables = dict(freevariables)
    variables.update(constants)
    variables.update(sweepvariables)
    sensitivities = dict(result["sensitivities"])

    # Remap monomials after substitution and simplification.
    #  The monomial sensitivities from the GP/SP are in terms of this
    #  smaller post-substitution list of monomials, so we need to map that
    #  back to the pre-substitution list.
    #
    #  Each "smap" is a list of HashVectors (mmaps),
    #    whose keys are monomial indexes pre-substitution,
    #    and whose values are the percentage of the simplified monomial's
    #    coefficient that came from that particular parent
    nu = result["sensitivities"]["monomials"]
    # HACK: simplified solves need a mutated beforesubs, as created in Model
    if hasattr(beforesubs, "smaps"):
        nu_ = np.zeros(len(beforesubs.cs))
        little_counter, big_counter = 0, 0
        for j, smap in enumerate(beforesubs.smaps):
            for i, mmap in enumerate(smap):
                for idx, percentage in mmap.items():
                    nu_[idx + big_counter] += percentage*nu[i + little_counter]
            little_counter += len(smap)
            big_counter += len(beforesubs.signomials[j].cs)
    sensitivities["monomials"] = nu_

    sens_vars = {var: sum([beforesubs.exps[i][var]*nu_[i] for i in locs])
                 for (var, locs) in beforesubs.varlocs.items()}
    sensitivities["variables"] = sens_vars

    # free-variable sensitivities must be <= some epsilon
    for var, S in sensitivities["variables"].items():
        if var in freevariables and abs(S) > freevar_sensitivity_tolerance:
            raise ValueError("free variable too sensitive: S_{%s} = "
                             "%0.2e" % (var, S))

    localexp = {var: S for (var, S) in sens_vars.items()
                if abs(S) >= localmodel_sensitivity_requirement}
    localcs = (variables[var]**-S for (var, S) in localexp.items())
    localc = functools_reduce(mul, localcs, cost)
    localmodel = Monomial(localexp, localc)

    # vectorvar substitution
    veckeys = set()
    for var in beforesubs.varlocs:
        if "idx" in var.descr and "shape" in var.descr:
            descr = dict(var.descr)
            idx = descr.pop("idx")
            if "value" in descr:
                descr.pop("value")
            if "units" in descr:
                units = descr.pop("units")
                veckey = VarKey(**descr)
                veckey.descr["units"] = units
            else:
                veckey = VarKey(**descr)
            veckeys.add(veckey)

            for vardict in [variables, sensitivities["variables"],
                            constants, sweepvariables, freevariables]:
                if var in vardict:
                    if veckey in vardict:
                        vardict[veckey][idx] = vardict[var]
                    else:
                        vardict[veckey] = np.full(var.descr["shape"], np.nan)
                        vardict[veckey][idx] = vardict[var]

                    del vardict[var]

    if hasattr(beforesubs, "varkeysubs"):
        for origvk, subvk in beforesubs.varkeysubs.items():
            for data in [constants, sweepvariables, freevariables, variables,
                         sensitivities["variables"]]:
                if subvk in data:
                    qty = isinstance(origvk.units, Quantity)
                    if data is sensitivities["variables"] or not qty:
                        data[origvk] = data[subvk]
                    else:
                        scale = (subvk.units/origvk.units).to("dimensionless")
                        data[origvk] = data[subvk] * scale

    return dict(cost=cost,
                constants=constants,
                sweepvariables=sweepvariables,
                freevariables=freevariables,
                variables=variables,
                sensitivities=sensitivities,
                localmodel=localmodel)
Пример #5
0
    def localsolve(self, solver=None, verbosity=1, x0=None, rel_tol=1e-4,
                   iteration_limit=50, *args, **kwargs):
        """Locally solves a SignomialProgram and returns the solution.

        Arguments
        ---------
        solver : str or function (optional)
            By default uses one of the solvers found during installation.
            If set to "mosek", "mosek_cli", or "cvxopt", uses that solver.
            If set to a function, passes that function cs, A, p_idxs, and k.
        verbosity : int (optional)
            If greater than 0, prints solve time and number of iterations.
            Each GP is created and solved with verbosity one less than this, so
            if greater than 1, prints solver name and time for each GP.
        x0 : dict (optional)
            Initial location to approximate signomials about.
        rel_tol : float
            Iteration ends when this is greater than the distance between two
            consecutive solve's objective values.
        iteration_limit : int
            Maximum GP iterations allowed.
        *args, **kwargs :
            Passed to solver function.


        Returns
        -------
        result : dict
            A dictionary containing the translated solver result.
        """
        if verbosity > 0:
            print("Beginning signomial solve.")
            self.starttime = time()
        self.gps = []  # NOTE: SIDE EFFECTS
        prevcost, cost, rel_improvement = None, None, None
        while rel_improvement is None or rel_improvement > rel_tol:
            if len(self.gps) > iteration_limit:
                raise RuntimeWarning("""problem unsolved after %s iterations.

    The last result is available in Model.program.gps[-1].result. If the gps
    appear to be converging, you may wish to increase the iteration limit by
    calling .localsolve(..., iteration_limit=NEWLIMIT).""" % len(self.gps))
            gp = self.step(x0, verbosity=verbosity-1)
            self.gps.append(gp)  # NOTE: SIDE EFFECTS
            try:
                result = gp.solve(solver, verbosity-1, *args, **kwargs)
            except (RuntimeWarning, ValueError):
                nearest_feasible = feasibility_model(gp, "max")
                self.gps.append(nearest_feasible)
                result = nearest_feasible.solve(verbosity=verbosity-1)
                result["cost"] = None
            x0 = result["variables"]
            prevcost, cost = cost, result["cost"]
            if prevcost and cost:
                rel_improvement = abs(prevcost-cost)/(prevcost + cost)
            else:
                rel_improvement = None
        # solved successfully!
        if verbosity > 0:
            print("Solving took %i GP solves" % len(self.gps)
                  + " and %.3g seconds." % (time() - self.starttime))

        # parse the result and return nu's of original monomials from
        #  variable sensitivities
        nu = result["sensitivities"]["monomials"]
        sens_vars = {var: sum([gp.exps[i][var]*nu[i] for i in locs])
                     for (var, locs) in gp.varlocs.items()}
        nu_ = []
        for signomial in self.signomials:
            for c, exp in zip(signomial.cs, signomial.exps):
                var_ss = [sens_vars[var]*val for var, val in exp.items()]
                nu_.append(functools_reduce(mul, var_ss, np.sign(c)))
        result["sensitivities"]["monomials"] = np.array(nu_)
        # TODO: SP sensitivities are weird, and potentially incorrect

        self.result = result  # NOTE: SIDE EFFECTS
        return result