def __init__(self,l,u,fs, A=None,b=None,C=None,d=None,constr=None,variable=None, tol=.01,sub_tol=None,name='',nthreads = 1,check_z=False, solver = 'glpk'): # parameters self.tol = tol if sub_tol: self.sub_tol = sub_tol else: self.sub_tol = tol if name: self.name = name else: self.name = 'sp' self.nthreads = nthreads self.solver = solver # box constraints # cast everything to float, since cvxopt breaks if numpy.float64 floats are used self.l = [float(li) for li in l] self.u = [float(ui) for ui in u] self.fs = fs # check dimensions and adequacy of data if not (len(fs)==len(l) and len(fs)==len(u)): raise ValueError('Check problem dimensions') for i,f in enumerate(self.fs): if len(f)<3: self.fs[i] = utilities.find_z(f,l[i],u[i],self.sub_tol) self.check_z = check_z utilities.get_constraints(self,A,b,C,d,constr,variable,n=len(l),format=solver) # initialize self.x = self.u self.best_node = Node(l,u,self) self.LB = self.best_node.LB self._bounds = Queue() self.bounds = [] # sort nodes for splitting in descending order by upper bound self.partition = MaxQueue() self.partition.put((self.best_node.UB,self.best_node))
def __init__(self, l, u, fs, A=None, b=None, C=None, d=None, constr=None, variable=None, tol=.01, sub_tol=None, name='', nthreads=1, check_z=False, solver='glpk'): # parameters self.tol = tol if sub_tol: self.sub_tol = sub_tol else: self.sub_tol = tol if name: self.name = name else: self.name = 'sp' self.nthreads = nthreads self.solver = solver # box constraints # cast everything to float, since cvxopt breaks if numpy.float64 floats are used self.l = [float(li) for li in l] self.u = [float(ui) for ui in u] self.fs = fs # check dimensions and adequacy of data if not (len(fs) == len(l) and len(fs) == len(u)): raise ValueError('Check problem dimensions') for i, f in enumerate(self.fs): if len(f) < 3: self.fs[i] = utilities.find_z(f, l[i], u[i], self.sub_tol) self.check_z = check_z utilities.get_constraints(self, A, b, C, d, constr, variable, n=len(l), format=solver) # initialize self.x = self.u self.best_node = Node(l, u, self) self.LB = self.best_node.LB self._bounds = Queue() self.bounds = [] # sort nodes for splitting in descending order by upper bound self.partition = MaxQueue() self.partition.put((self.best_node.UB, self.best_node))
class Problem(object): '''Container for problem parameters Represents the problem maximize sum(f_i(x_i)) subject to: A*x <= b Cx == d l <= x <= u constr where each fuction f_i is sigmoidal. Solves the problem using a branch and bound solver to specified accuracy Arguments: l : a list of upper bounds on variable x u : a list of lower bounds on variable x fs : a list of tuples (f,fprime,z) where fprime gives the derivative of f. f should be convex for x<z and concave for x>z A : an arbitrary matrix (n x m) b : an arbitrary vector (m x 1) tol : the accuracy required for the problem sub_tol : the accuracy with which to compute the concave envelopes problem.solve(maxiters) runs the branch and bound solver until a solution of accuracy self.tol is reached, or until maxiters concave subproblems have been solved. problem.best_node is the best node found so far (the one with the highest lower bound) at any time. problem.LB is the best lower bound on the optimal value found so far; it is achieved by problem.best_node.x problem.partition is a priority queue containing all nodes under consideration indexed by their upper bounds, in decreasing order. problem.bounds is a list of the bounds obtained after each iteration. ''' def __init__(self,l,u,fs, A=None,b=None,C=None,d=None,constr=None,variable=None, tol=.01,sub_tol=None,name='',nthreads = 1,check_z=False, solver = 'glpk'): # parameters self.tol = tol if sub_tol: self.sub_tol = sub_tol else: self.sub_tol = tol if name: self.name = name else: self.name = 'sp' self.nthreads = nthreads self.solver = solver # box constraints # cast everything to float, since cvxopt breaks if numpy.float64 floats are used self.l = [float(li) for li in l] self.u = [float(ui) for ui in u] self.fs = fs # check dimensions and adequacy of data if not (len(fs)==len(l) and len(fs)==len(u)): raise ValueError('Check problem dimensions') for i,f in enumerate(self.fs): if len(f)<3: self.fs[i] = utilities.find_z(f,l[i],u[i],self.sub_tol) self.check_z = check_z utilities.get_constraints(self,A,b,C,d,constr,variable,n=len(l),format=solver) # initialize self.x = self.u self.best_node = Node(l,u,self) self.LB = self.best_node.LB self._bounds = Queue() self.bounds = [] # sort nodes for splitting in descending order by upper bound self.partition = MaxQueue() self.partition.put((self.best_node.UB,self.best_node)) def run_serial(self,maxiters=0,verbose=False,prune=False,tol=None): ''' Finds a solution of quality problem.tol to the problem. The optimal node found at any point (ie, the one with the best lower bound and an x that achieves it) is stored in problem.best_node. The algorithm works by popping the node with the highest upper bound off of the partition, splitting it, and computing bounds for the resulting subrectangles. The subrectangles are then inserted into the problem.partition. The algorithm terminates when the stopping criterion is met, ie when the highest upper bound is less than problem.tol greater than the highest lower bound, or after maxiters subproblems have been solved. ''' if tol is None: tol = self.tol iter = 0 while not maxiters or iter < maxiters: iter += 1 UB, node = self.partition.get_nowait() # record bounds self._bounds.put((self.LB,UB)) if verbose: print 'Bounds',self.LB,UB,'found by',self.name # stopping criterion if UB - self.LB < tol: # a tolerable solution has been found! # put current node back in the partition if verbose: print 'Stopping criterion reached by %s' % (self.name) self.partition.put((UB,node)) break if UB == -float("inf"): if verbose: print 'Problem infeasible' break # keep going for child in node.split(self): if verbose: print 'split into child with bounds',child.LB,child.UB if child.LB > self.LB: if verbose: print 'Node with best lower bound %.4f found' \ % (child.LB) self.LB = child.LB self.best_node = child # only put child into partition if child is active if prune is False or child.UB >= self.LB: self.partition.put((child.UB,child)) else: if verbose: print 'Discarded node with bounds',child.LB,child.UB,'since LB is',self.LB self._get_bounds() self.x = self.best_node.x def solve(self,*args,**kwargs): ''' Convenience wrapper around run_serial ''' self.run_serial(*args,**kwargs) def _get_bounds(self): while not self._bounds.empty(): next = self._bounds.get() self.bounds.append(next) return self.bounds
class Problem(object): '''Container for problem parameters Represents the problem maximize sum(f_i(x_i)) subject to: A*x <= b Cx == d l <= x <= u constr where each fuction f_i is sigmoidal. Solves the problem using a branch and bound solver to specified accuracy Arguments: l : a list of upper bounds on variable x u : a list of lower bounds on variable x fs : a list of tuples (f,fprime,z) where fprime gives the derivative of f. f should be convex for x<z and concave for x>z A : an arbitrary matrix (n x m) b : an arbitrary vector (m x 1) tol : the accuracy required for the problem sub_tol : the accuracy with which to compute the concave envelopes problem.solve(maxiters) runs the branch and bound solver until a solution of accuracy self.tol is reached, or until maxiters concave subproblems have been solved. problem.best_node is the best node found so far (the one with the highest lower bound) at any time. problem.LB is the best lower bound on the optimal value found so far; it is achieved by problem.best_node.x problem.partition is a priority queue containing all nodes under consideration indexed by their upper bounds, in decreasing order. problem.bounds is a list of the bounds obtained after each iteration. ''' def __init__(self, l, u, fs, A=None, b=None, C=None, d=None, constr=None, variable=None, tol=.01, sub_tol=None, name='', nthreads=1, check_z=False, solver='glpk'): # parameters self.tol = tol if sub_tol: self.sub_tol = sub_tol else: self.sub_tol = tol if name: self.name = name else: self.name = 'sp' self.nthreads = nthreads self.solver = solver # box constraints # cast everything to float, since cvxopt breaks if numpy.float64 floats are used self.l = [float(li) for li in l] self.u = [float(ui) for ui in u] self.fs = fs # check dimensions and adequacy of data if not (len(fs) == len(l) and len(fs) == len(u)): raise ValueError('Check problem dimensions') for i, f in enumerate(self.fs): if len(f) < 3: self.fs[i] = utilities.find_z(f, l[i], u[i], self.sub_tol) self.check_z = check_z utilities.get_constraints(self, A, b, C, d, constr, variable, n=len(l), format=solver) # initialize self.x = self.u self.best_node = Node(l, u, self) self.LB = self.best_node.LB self._bounds = Queue() self.bounds = [] # sort nodes for splitting in descending order by upper bound self.partition = MaxQueue() self.partition.put((self.best_node.UB, self.best_node)) def run_serial(self, maxiters=0, verbose=False, prune=False, tol=None): ''' Finds a solution of quality problem.tol to the problem. The optimal node found at any point (ie, the one with the best lower bound and an x that achieves it) is stored in problem.best_node. The algorithm works by popping the node with the highest upper bound off of the partition, splitting it, and computing bounds for the resulting subrectangles. The subrectangles are then inserted into the problem.partition. The algorithm terminates when the stopping criterion is met, ie when the highest upper bound is less than problem.tol greater than the highest lower bound, or after maxiters subproblems have been solved. ''' if tol is None: tol = self.tol iter = 0 while not maxiters or iter < maxiters: iter += 1 UB, node = self.partition.get_nowait() # record bounds self._bounds.put((self.LB, UB)) if verbose: print 'Bounds', self.LB, UB, 'found by', self.name # stopping criterion if UB - self.LB < tol: # a tolerable solution has been found! # put current node back in the partition if verbose: print 'Stopping criterion reached by %s' % (self.name) self.partition.put((UB, node)) break if UB == -float("inf"): if verbose: print 'Problem infeasible' break # keep going for child in node.split(self): if verbose: print 'split into child with bounds', child.LB, child.UB if child.LB > self.LB: if verbose: print 'Node with best lower bound %.4f found' \ % (child.LB) self.LB = child.LB self.best_node = child # only put child into partition if child is active if prune is False or child.UB >= self.LB: self.partition.put((child.UB, child)) else: if verbose: print 'Discarded node with bounds', child.LB, child.UB, 'since LB is', self.LB self._get_bounds() self.x = self.best_node.x def solve(self, *args, **kwargs): ''' Convenience wrapper around run_serial ''' self.run_serial(*args, **kwargs) def _get_bounds(self): while not self._bounds.empty(): next = self._bounds.get() self.bounds.append(next) return self.bounds