def _getAllAttachedConstraints(oofuns): from FuncDesigner import broadcast r = set() def F(oof): r.update(oof.attachedConstraints) broadcast(F, oofuns, useAttachedConstraints=True) return r
def formDepCounter(oofuns): # TODO: mb exclude fixed oovars/oofuncs? from FuncDesigner import broadcast, oofun R = {} def func(oof): if oof.is_oovar: R[oof] = {oof: 1} return dicts = [R.get(inp) for inp in oof.input if isinstance(inp, oofun)] R[oof] = dictSum(dicts) broadcast(func, oofuns, useAttachedConstraints=False) return R
def formResolveSchedule(oof): depsNumber = formDepCounter(oof) def F(ff, depsNumberDict, baseFuncDepsNumber, R): tmp = depsNumberDict[ff] s = [] for k, v in tmp.items(): if baseFuncDepsNumber[k] == v: s.append(k) baseFuncDepsNumber[k] -= 1 if len(s): R[ff] = s R = {} broadcast(F, oof, False, depsNumber, depsNumber[oof].copy(), R) R.pop(oof, None) oof.resolveSchedule = R
def handleConstraint(self, c, StartPointVars, areFixed, oovD, A, b, Aeq, beq, Z, D_kwargs, LB, UB, inplaceLinearRender): #import FuncDesigner as fd from FuncDesigner.constraints import SmoothFDConstraint from FuncDesigner.BooleanOOFun import BooleanOOFun if not isinstance(c, SmoothFDConstraint) and isinstance(c, BooleanOOFun): self.hasLogicalConstraints = True #continue probtol = self.contol f, tol = c.oofun, c.tol _lb, _ub = c.lb, c.ub Name = f.name dep = set([f]) if f.is_oovar else f._getDep() isFixed = areFixed(dep) if f.is_oovar and isFixed: if self._x0 is None or f not in self._x0: self.err('your problem has fixed oovar '+ Name + ' but no value for the one in start point is provided') return True if not dep.issubset(StartPointVars): self.err('your start point has no enough variables to define constraint ' + c.name) if tol < 0: if any(_lb == _ub): self.err("You can't use negative tolerance for the equality constraint " + c.name) elif any(_lb - tol >= _ub + tol): self.err("You can't use negative tolerance for so small gap in constraint" + c.name) Shift = (1.0+1e-13)*probtol ####################### # not inplace modification!!!!!!!!!!!!! _lb = _lb + Shift _ub = _ub - Shift ####################### if tol != 0: self.useScaledResidualOutput = True # TODO: omit it for interalg if tol not in (0, probtol, -probtol): scaleFactor = abs(probtol / tol) f *= scaleFactor #c.oofun = f#c.oofun * scaleFactor _lb, _ub = _lb * scaleFactor, _ub * scaleFactor Contol = tol Contol2 = Contol * scaleFactor else: Contol = asscalar(copy(probtol)) Contol2 = Contol if isFixed: # TODO: get rid of self.contol, use separate contols for each constraint if not c(self._x0, tol=Contol2): s = """'constraint "%s" with all-fixed optimization variables it depends on is infeasible in start point, hence the problem is infeasible, maybe you should change start point'""" % c.name self.err(s) return True from FuncDesigner import broadcast broadcast(formDictOfFixedFuncs, f, self.useAttachedConstraints, self.dictOfFixedFuncs, areFixed, self._x0) #self.dictOfFixedFuncs[f] = f(self.x0) f_order = f.getOrder(self.freeVarsSet, self.fixedVarsSet, fixedVarsScheduleID = self._FDVarsID) if self.probType in ['LP', 'MILP', 'LLSP', 'LLAVP'] and f_order > 1: self.err('for LP/MILP/LLSP/LLAVP all constraints have to be linear, while ' + f.name + ' is not') if not f.is_oovar and f_order < 2: D_kwargs2 = D_kwargs.copy() if inplaceLinearRender: # interalg only D_kwargs2['useSparse'] = False D = f.D(Z, **D_kwargs2) if inplaceLinearRender: # interalg only if any([asarray(val).size > 1 for val in D.values()]): self.err('currently interalg can handle only FuncDesigner.oovars(n), not FuncDesigner.oovar() with size > 1') f = linear_render(f, D, Z) else: D = 0 # TODO: simplify condition of box-bounded oovar detection if f.is_oovar: inds = oovD[f] f_size = inds[1] - inds[0] if any(isfinite(_lb)): if _lb.size not in (f_size, 1): self.err('incorrect size of lower box-bound constraint for %s: 1 or %d expected, %d obtained' % (Name, f_size, _lb.size)) # for PyPy compatibility if type(_lb) == ndarray and _lb.size == 1: _lb = _lb.item() val = array(f_size*[_lb] if type(_lb) == ndarray and _lb.size < f_size else _lb) LB[f] = val if f not in LB else where(val > LB[f], val, LB[f]) if any(isfinite(_ub)): if _ub.size not in (f_size, 1): self.err('incorrect size of upper box-bound constraint for %s: 1 or %d expected, %d obtained' % (Name, f_size, _ub.size)) # for PyPy compatibility if type(_ub) == ndarray and _ub.size == 1: _ub = _ub.item() val = array(f_size*[_ub] if type(_ub) == ndarray and _ub.size < f_size else _ub) UB[f] = val if f not in UB else where(val < UB[f], val, UB[f]) elif _lb == _ub: if f_order < 2: Aeq.append(self._pointDerivative2array(D)) beq.append(-f(Z)+_lb) elif self.h is None: self.h = [f-_lb] else: self.h.append(f-_lb) elif isfinite(_ub): if f_order < 2: A.append(self._pointDerivative2array(D)) b.append(-f(Z)+_ub) elif self.c is None: self.c = [f - _ub] else: self.c.append(f - _ub) elif isfinite(_lb): if f_order < 2: A.append(-self._pointDerivative2array(D)) b.append(f(Z) - _lb) elif self.c is None: self.c = [- f + _lb] else: self.c.append(- f + _lb) else: self.err('inform OpenOpt developers of the bug') if not f.is_oovar: Contol = max((0, Contol2)) # TODO: handle it more properly, especially for lb, ub of array type # FIXME: name of f0 vs f # self._FD.nonBoxConsWithTolShift.append((f0, lb_0 - Contol, ub_0 + Contol)) # self._FD.nonBoxCons.append((f0, lb_0, ub_0, Contol)) self._FD.nonBoxConsWithTolShift.append((c, f, _lb - Contol, _ub + Contol)) self._FD.nonBoxCons.append((c, f, _lb, _ub, Contol)) # if tol not in (0, probtol, -probtol): # print('!', f, _lb, _ub, Contol) return False
def _prepare(self): if self._baseProblemIsPrepared: return if self.useSparse == 0: self.useSparse = False elif self.useSparse == 1: self.useSparse = True if self.useSparse == 'auto' and not scipyInstalled: self.useSparse = False if self.useSparse == True and not scipyInstalled: self.err("You can't set useSparse=True without scipy installed") if self._isFDmodel(): self.isFDmodel = True self._FD = EmptyClass() self._FD.nonBoxConsWithTolShift = [] self._FD.nonBoxCons = [] from FuncDesigner import _getAllAttachedConstraints, _getDiffVarsID, ooarray, oopoint, oofun#, _Stochastic self._FDVarsID = _getDiffVarsID() probDep = set() updateDep = lambda Dep, elem: \ [updateDep(Dep, f) for f in elem] if isinstance(elem, (tuple, list, set))\ else [updateDep(Dep, f) for f in atleast_1d(elem)] if isinstance(elem, ndarray)\ else Dep.update(set([elem]) if elem.is_oovar else elem._getDep()) if isinstance(elem, oofun) else None if self.probType in ['SLE', 'NLSP', 'SNLE', 'LLSP']: equations = self.C if self.probType in ('SLE', 'LLSP') else self.f F = equations updateDep(probDep, equations) ConstraintTags = [(elem if not isinstance(elem, (set, list, tuple, ndarray)) else elem[0]).isConstraint for elem in equations] cond_all_oofuns_but_not_cons = not any(ConstraintTags) cond_cons = all(ConstraintTags) if not cond_all_oofuns_but_not_cons and not cond_cons: raise OpenOptException('for FuncDesigner SLE/SNLE constructors args must be either all-equalities or all-oofuns') if self.fTol is not None: fTol = min((self.ftol, self.fTol)) self.warn(''' both ftol and fTol are passed to the SNLE; minimal value of the pair will be used (%0.1e); also, you can modify each personal tolerance for equation, e.g. equations = [(sin(x)+cos(y)=-0.5)(tol = 0.001), ...] ''' % fTol) else: fTol = self.ftol self.fTol = self.ftol = fTol appender = lambda arg: [appender(elem) for elem in arg] if isinstance(arg, (ndarray, list, tuple, set))\ else ((arg.oofun*(fTol/arg.tol) if arg.tol != fTol and arg.tol != 0 else arg.oofun) if arg.isConstraint else arg) EQs = [] for eq in equations: rr = appender(eq) if type(rr) == list: EQs += rr else: EQs.append(rr) #EQs = [((elem.oofun*(fTol/elem.tol) if elem.tol != 0 else elem.oofun) if elem.isConstraint else elem) for elem in equations] if self.probType in ('SLE', 'LLSP'): self.C = EQs elif self.probType in ('NLSP', 'SNLE'): self.f = EQs # self.user.F = EQs else: raise OpenOptException('bug in OO kernel') else: F = [self.f] updateDep(probDep, self.f) updateDep(probDep, self.constraints) # TODO: implement it # startPointVars = set(self.x0.keys()) # D = startPointVars.difference(probDep) # if len(D): # print('values for variables %s are missing in start point' % D) # D2 = probDep.difference(startPointVars) # if len(D2): # self.x0 = dict([(key, self.x0[key]) for key in D2]) for fn in ['lb', 'ub', 'A', 'Aeq', 'b', 'beq']: if not hasattr(self, fn): continue val = getattr(self, fn) if val is not None and any(isfinite(val)): self.err('while using oovars providing lb, ub, A, Aeq for whole prob is forbidden, use for each oovar instead') if not isinstance(self.x0, dict): self.err('Unexpected start point type: ooPoint or Python dict expected, '+ str(type(self.x0)) + ' obtained') x0 = self.x0.copy() tmp = [] for key, val in x0.items(): if not isinstance(key, (list, tuple, ndarray)): tmp.append((key, val)) else: # can be only ooarray although val = atleast_1d(val) if len(key) != val.size: self.err(''' for the sake of possible bugs prevention lenght of oovars array must be equal to lenght of its start point value, assignments like x = oovars(m); startPoint[x] = 0 are forbidden, use startPoint[x] = [0]*m or np.zeros(m) instead''') for i in range(val.size): tmp.append((key[i], val[i])) Tmp = dict(tmp) if isinstance(self.fixedVars, dict): for key, val in self.fixedVars.items(): if isinstance(key, (list, tuple, ndarray)): # can be only ooarray although if len(key) != len(val): self.err(''' for the sake of possible bugs prevention lenght of oovars array must be equal to lenght of its start point value, assignments like x = oovars(m); fixedVars[x] = 0 are forbidden, use fixedVars[x] = [0]*m or np.zeros(m) instead''') for i in range(len(val)): Tmp[key[i]] = val[i] else: Tmp[key] = val self.fixedVars = set(self.fixedVars.keys()) # mb other operations will speedup it? if self.probType != 'ODE': Keys = set(Tmp.keys()).difference(probDep) for key in Keys: Tmp.pop(key) self.probDep = probDep self.x0 = Tmp self._stringVars = set() for key, val in self.x0.items(): #if key.domain is not None and key.domain is not bool and key.domain is not 'bool': if type(val) in (str, string_): self._stringVars.add(key) key.formAuxDomain() self.x0[key] = key.aux_domain[val]#searchsorted(key.aux_domain, val, 'left') elif key.fields == () and key.domain is not None and key.domain is not bool and key.domain is not 'bool' \ and key.domain is not int and key.domain is not 'int' and val not in key.domain: self.x0[key] = key.domain[0] self.x0 = oopoint(self.x0) self.x0.maxDistributionSize = self.maxDistributionSize setStartVectorAndTranslators(self) if self.probType in ['LP', 'MILP', 'SOCP'] and self.f.getOrder(self.freeVarsSet, self.fixedVarsSet, fixedVarsScheduleID = self._FDVarsID) > 1: self.err('for LP/MILP objective function has to be linear, while this one ("%s") is not' % self.f.name) if self.fixedVars is None: D_kwargs = {'fixedVars':self.fixedVarsSet} elif self.freeVars is not None and len(self.freeVars)<len(self.fixedVars): D_kwargs = {'Vars':self.freeVarsSet} else: D_kwargs = {'fixedVars':self.fixedVarsSet} D_kwargs['useSparse'] = self.useSparse D_kwargs['fixedVarsScheduleID'] = self._FDVarsID D_kwargs['exactShape'] = True self._D_kwargs = D_kwargs variableTolerancesDict = dict((v, v.tol) for v in self._freeVars) self.variableTolerances = self._point2vector(variableTolerancesDict) if len(self._fixedVars) < len(self._freeVars) and 'isdisjoint' in dir(set()): areFixed = lambda dep: dep.issubset(self.fixedVarsSet) #isFixed = lambda v: v in self._fixedVars Z = dict((v, zeros_like(val) if v not in self.fixedVarsSet else val) for v, val in self._x0.items()) else: areFixed = lambda dep: dep.isdisjoint(self.freeVarsSet) #isFixed = lambda v: v not in self._freeVars Z = dict((v, zeros_like(val) if v in self.freeVarsSet else val) for v, val in self._x0.items()) Z = oopoint(Z, maxDistributionSize = self.maxDistributionSize) self._Z = Z #p.isFixed = isFixed lb, ub = -inf*ones(self.n), inf*ones(self.n) # TODO: get rid of start c, h = None, use [] instead A, b, Aeq, beq = [], [], [], [] if type(self.constraints) not in (list, tuple, set): self.constraints = [self.constraints] oovD = self._oovarsIndDict LB = {} UB = {} """ gather attached constraints """ C = list(self.constraints) self.constraints = set(self.constraints) for v in self._x0.keys(): # if v.fields != (): # v.aux_domain = Copy(v.domain) ## # TODO: mb rework it ## ind_numeric = [j for j, elem in enumerate(v.aux_domain[0]) if type(elem) not in (str, np.str_)] ## if len(ind_numeric): ## ind_first_numeric = ind_numeric[0] ## v.aux_domain.sort(key = lambda elem: elem[ind_first_numeric]) # v.domain = np.arange(len(v.domain)) if not array_equal(v.lb, -inf): self.constraints.add(v >= v.lb) if not array_equal(v.ub, inf): self.constraints.add(v <= v.ub) if self.useAttachedConstraints: if hasattr(self, 'f'): if type(self.f) in [list, tuple, set]: C += list(self.f) else: # self.f is oofun C.append(self.f) self.constraints.update(_getAllAttachedConstraints(C)) FF = self.constraints.copy() for _F in F: if isinstance(_F, (tuple, list, set)): FF.update(_F) elif isinstance(_F, ndarray): if _F.size > 1: FF.update(_F) else: FF.add(_F.item()) else: FF.add(_F) unvectorizableFuncs = set() #unvectorizableVariables = set([var for var, val in self._x0.items() if isinstance(val, _Stochastic) or asarray(val).size > 1]) # TODO: use this unvectorizableVariables = set([]) # temporary replacement: #unvectorizableVariables = set([var for var, val in self._x0.items() if asarray(val).size > 1]) cond = False #debug # unvectorizableVariables = set(self._x0.keys()) # hasVectorizableFuncs = False # cond = True #debug end if 1 and isPyPy: hasVectorizableFuncs = False unvectorizableFuncs = FF else: hasVectorizableFuncs = False if len(unvectorizableVariables) != 0: for ff in FF: _dep = ff._getDep() if cond or len(_dep & unvectorizableVariables) != 0: unvectorizableFuncs.add(ff) else: hasVectorizableFuncs = True else: hasVectorizableFuncs = True self.unvectorizableFuncs = unvectorizableFuncs self.hasVectorizableFuncs = hasVectorizableFuncs for v in self.freeVarsSet: d = v.domain if d is bool or d is 'bool': self.constraints.update([v>0, v<1]) elif d is not None and d is not int and d is not 'int': # TODO: mb add integer domains? v.domain = array(list(d)) v.domain.sort() self.constraints.update([v >= v.domain[0], v <= v.domain[-1]]) if hasattr(v, 'aux_domain'): self.constraints.add(v <= len(v.aux_domain)-1) # for v in self._stringVars: # if isFixed(v): # ind = searchsorted(v.aux_domain, p._x0[v], 'left') # if v.aux_domain """ handling constraints """ StartPointVars = set(self._x0.keys()) self.dictOfFixedFuncs = {} from FuncDesigner import broadcast if self.probType in ['SLE', 'NLSP', 'SNLE', 'LLSP']: for eq in equations: broadcast(formDictOfFixedFuncs, eq, self.useAttachedConstraints, self.dictOfFixedFuncs, areFixed, self._x0) else: broadcast(formDictOfFixedFuncs, self.f, self.useAttachedConstraints, self.dictOfFixedFuncs, areFixed, self._x0) if oosolver(self.solver).useLinePoints: self._firstLinePointDict = {} self._secondLinePointDict = {} self._currLinePointDict = {} inplaceLinearRender = oosolver(self.solver).__name__ == 'interalg' if inplaceLinearRender and hasattr(self, 'f'): D_kwargs2 = D_kwargs.copy() D_kwargs2['useSparse'] = False if type(self.f) in [list, tuple, set]: ff = [] for f in self.f: if f.getOrder(self.freeVarsSet, self.fixedVarsSet, fixedVarsScheduleID = self._FDVarsID) < 2: D = f.D(Z, **D_kwargs2) f2 = linear_render(f, D, Z) ff.append(f2) else: ff.append(f) self.f = ff else: # self.f is oofun if self.f.getOrder(self.freeVarsSet, self.fixedVarsSet, fixedVarsScheduleID = self._FDVarsID) < 2: D = self.f.D(Z, **D_kwargs2) self.f = linear_render(self.f, D, Z) if self.isObjFunValueASingleNumber: self._linear_objective = True self._linear_objective_factor = self._pointDerivative2array(D).flatten() self._linear_objective_scalar = self.f(Z) handleConstraint_args = (StartPointVars, areFixed, oovD, A, b, Aeq, beq, Z, D_kwargs, LB, UB, inplaceLinearRender) for c in self.constraints: if isinstance(c, ooarray): for elem in c: self.handleConstraint(elem, *handleConstraint_args) elif c is True: continue elif c is False: self.err('one of elements from constraints is "False", solution is impossible') elif not hasattr(c, 'isConstraint'): self.err('The type ' + str(type(c)) + ' is inappropriate for problem constraints') else: self.handleConstraint(c, *handleConstraint_args) if len(b) != 0: self.A, self.b = Vstack(A), Hstack([asfarray(elem).flatten() for elem in b])#Vstack(b).flatten() if hasattr(self.b, 'toarray'): self.b = self.b.toarray() if len(beq) != 0: self.Aeq, self.beq = Vstack(Aeq), Hstack([ravel(elem) for elem in beq])#Vstack(beq).flatten() if hasattr(self.beq, 'toarray'): self.beq = self.beq.toarray() for vName, vVal in LB.items(): inds = oovD[vName] lb[inds[0]:inds[1]] = vVal for vName, vVal in UB.items(): inds = oovD[vName] ub[inds[0]:inds[1]] = vVal self.lb, self.ub = lb, ub else: # not FuncDesigner if self.fixedVars is not None or self.freeVars is not None: self.err('fixedVars and freeVars are valid for optimization of FuncDesigner models only') if self.x0 is None: arr = ['lb', 'ub'] if self.probType in ['LP', 'MILP', 'QP', 'SOCP', 'SDP']: arr.append('f') if self.probType in ['LLSP', 'LLAVP', 'LUNP']: arr.append('D') for fn in arr: if not hasattr(self, fn): continue tmp = getattr(self, fn) fv = asarray(tmp) if not isspmatrix(tmp) else tmp.A if any(isfinite(fv)): self.x0 = np.zeros(fv.size) break self.x0 = ravel(self.x0) if not hasattr(self, 'n'): self.n = self.x0.size if not hasattr(self, 'lb'): self.lb = -inf * ones(self.n) if not hasattr(self, 'ub'): self.ub = inf * ones(self.n) for fn in ('A', 'Aeq'): fv = getattr(self, fn) if fv is not None: #afv = asfarray(fv) if not isspmatrix(fv) else fv.toarray() # TODO: omit casting to dense matrix afv = asfarray(fv) if type(fv) in [list, tuple] else fv if len(afv.shape) > 1: if afv.shape[1] != self.n: self.err('incorrect ' + fn + ' size') else: if afv.shape != () and afv.shape[0] == self.n: afv = afv.reshape(1, self.n) setattr(self, fn, afv) else: setattr(self, fn, asfarray([]).reshape(0, self.n)) nA, nAeq = prod(self.A.shape), prod(self.Aeq.shape) SizeThreshold = 2 ** 15 if scipyInstalled: from scipy.sparse import csc_matrix if isspmatrix(self.A) or (nA > SizeThreshold and np.flatnonzero(self.A).size < 0.25*nA): self._A = csc_matrix(self.A) if isspmatrix(self.Aeq) or (nAeq > SizeThreshold and np.flatnonzero(self.Aeq).size < 0.25*nAeq): self._Aeq = csc_matrix(self.Aeq) elif nA > SizeThreshold or nAeq > SizeThreshold: self.pWarn(scipyAbsentMsg) self._baseProblemIsPrepared = True