Changeset 297


Ignore:
Timestamp:
07/07/10 12:59:19 (6 years ago)
Author:
altafang
Message:

Added ability for nonlinear constraints function generator to try simplifying in different ways until bounds are satisfied.

Location:
branches/alta/mystic-0.2a1
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/alta/mystic-0.2a1/constraint_tools.py

    r295 r297  
    332332        return target + ' = ' + str(soln[0]) 
    333333 
    334 def form_constraints_nonlinear(equations_string, ndim, x0=None, \ 
     334def form_constraints_nonlinear_old(equations_string, ndim, x0=None, \ 
    335335                              lower_bounds=None, upper_bounds=None, varname='x'): 
    336336    """Use Sympy to simplify/invert equations to find a formulation of  
     
    473473                varname=varname) 
    474474 
    475 def form_constraints_nonlinear_sub(equations_string, ndim, varname='x'): 
    476     """Simplify using substitution, algebraically with help from Sympy. 
    477 Algorithm: 
    478 Same as using linear algebra to simplify a linear system.... 
    479 Treat the list of equations as a matrix.  
    480 """ 
     475def form_constraints_nonlinear(equations_string, ndim, varname='x', x0=None,\ 
     476                                    lower_bounds=None, upper_bounds=None): 
     477    """Simplify a linear or nonlinear system using substitution with help from Sympy. 
     478 
     479Input parameters: 
     480equations_string -- A string with constraints equations on each line 
     481ndim -- Number of parameters. 
     482 
     483Optional inputs: 
     484x0 -- Initial x value. 
     485lower_bounds -- Lower bounds on x. 
     486upper_bounds -- Upper bounds on x. 
     487varname -- A string, default is 'x'. 
     488 
     489Returns a constraints function. 
     490""" 
     491    # Figure out if trying various permutations is necessary 
     492    strict = False 
     493    if (x0 and lower_bounds) or (x0 and upper_bounds): 
     494        strict = True 
     495 
    481496    eqns = equations_string.splitlines() 
    482497    # Remove empty strings: 
     
    485500        if eqns[j]: 
    486501           actual_eqns.append(eqns[j]) 
     502    orig_eqns = actual_eqns[:] 
    487503 
    488504    neqns = len(actual_eqns) 
    489505 
    490     # Sort the list actual_eqns so that any equation containing x1 is first, etc. 
    491     sorted_eqns = [] 
    492     actual_eqns_copy = actual_eqns[:] 
    493     for i in range(ndim): 
    494         variable = varname + str(i+1) 
    495         for eqn in actual_eqns_copy: 
    496             if eqn.find(variable) == -1: 
    497                 pass 
    498             else: 
    499                 sorted_eqns.append(eqn) 
    500                 actual_eqns_copy.remove(eqn) 
    501                 break 
    502     actual_eqns = sorted_eqns 
    503  
    504     for i in range(neqns): 
    505         # Trying to use xi as a pivot. Loop through the equations looking for one 
    506         # containing xi. 
    507         target = varname + str(i+1) 
    508         for eqn in actual_eqns[i:]: 
    509             invertedstring = invert_equation(eqn, ndim, varname=varname, target=target) 
    510             if invertedstring: 
    511                 break 
    512         # substitute into the remaining equations. the equations' order in the list 
    513         # newsystem is like in a linear coefficient matrix. 
    514         newsystem = ['']*neqns 
    515         j = actual_eqns.index(eqn) 
    516         newsystem[j] = eqn 
    517         othereqns = actual_eqns[:j] + actual_eqns[j+1:] 
    518         for othereqn in othereqns: 
    519             expression = invertedstring.split("=")[1] 
    520             fixed = othereqn.replace(target, '(' + expression + ')') 
    521             k = actual_eqns.index(othereqn) 
    522             newsystem[k] = fixed 
    523         actual_eqns = newsystem 
    524  
    525     # Invert so that it can be fed properly to form_constraints_directly 
    526     simplified = [] 
    527     for eqn in actual_eqns: 
    528         target = varname + str(actual_eqns.index(eqn) + 1) 
    529         simplified.append(invert_equation(eqn, ndim, varname=varname, target=target)) 
    530     #print 'simplified:' 
    531     #print '\n'.join(simplified) 
    532  
    533     # Turn this into a constraints function 
    534     return form_constraints_directly('\n'.join(simplified), ndim, varname=varname) 
     506    # Some of the permutations will give the same answer; look into reducing the number 
     507    # of repeats? 
     508    for p in permutations(range(ndim)): 
     509        thisorder = p 
     510        # Sort the list actual_eqns so that any equation containing x1 is first, etc. 
     511        sorted_eqns = [] 
     512        actual_eqns_copy = orig_eqns[:] 
     513        for i in thisorder:#range(ndim): 
     514            variable = varname + str(i+1) 
     515            for eqn in actual_eqns_copy: 
     516                if eqn.find(variable) == -1: 
     517                    pass 
     518                else: 
     519                    sorted_eqns.append(eqn) 
     520                    actual_eqns_copy.remove(eqn) 
     521                    break 
     522        if actual_eqns_copy: 
     523            for item in actual_eqns_copy: 
     524                sorted_eqns.append(item) 
     525        actual_eqns = sorted_eqns 
     526 
     527        for i in range(neqns): 
     528            # Trying to use xi as a pivot. Loop through the equations looking for one 
     529            # containing xi. 
     530            target = varname + str(thisorder[i] + 1)#str(i+1) 
     531            for eqn in actual_eqns[i:]: 
     532                invertedstring = invert_equation(eqn, ndim, varname=varname, target=target) 
     533                if invertedstring: 
     534                    break 
     535            # substitute into the remaining equations. the equations' order in the list 
     536            # newsystem is like in a linear coefficient matrix. 
     537            newsystem = ['']*neqns 
     538            j = actual_eqns.index(eqn) 
     539            newsystem[j] = eqn 
     540            othereqns = actual_eqns[:j] + actual_eqns[j+1:] 
     541            for othereqn in othereqns: 
     542                expression = invertedstring.split("=")[1] 
     543                fixed = othereqn.replace(target, '(' + expression + ')') 
     544                k = actual_eqns.index(othereqn) 
     545                newsystem[k] = fixed 
     546            actual_eqns = newsystem 
     547             
     548        # Invert so that it can be fed properly to form_constraints_directly 
     549        simplified = [] 
     550        for eqn in actual_eqns: 
     551            target = varname + str(thisorder[actual_eqns.index(eqn)]+1)#actual_eqns.index(eqn) + 1) 
     552            simplified.append(invert_equation(eqn, ndim, varname=varname, target=target)) 
     553 
     554        cf = form_constraints_directly('\n'.join(simplified), ndim, varname=varname)   
     555 
     556        if not strict: 
     557            return cf 
     558        else: 
     559            compatible = constraints_inside_bounds(cf, x0, lower_bounds=\ 
     560                 lower_bounds, upper_bounds=upper_bounds) 
     561            satisfied = verify_constraints_satisfied(equations_string, cf(x0), ndim, disp=False) 
     562            if compatible and satisfied: 
     563                return cf 
     564    print 'Warning! Initial x value cannot satisfy constraints and bounds, \ 
     565or constraints cannot be enforced correctly. Returning a constraints function \ 
     566anyways, but it may not work correctly.' 
     567    return cf 
    535568 
    536569 
     
    538571                                        varname='x', disp=True): 
    539572    """For debugging. Makes sure that constraints are *really* satisified. 
    540 constraints_string is the constraints string 
     573constraints_string is the constraints string -- Note: not the function! 
    541574x is cf(x), i.e. some array that is supposed to satisfy the constraints. 
    542575ndim is number of dimensions. 
     
    612645def test5(): 
    613646    # Test a nonlinear constraints example with x0 and bounds. 
    614     #XXX This is a case where it does not work properly because the second line 
    615     # erases the effects of the first line, and since it's nonlinear, Sympy can't 
    616     # simplify it. If it were linear, form_constraints_linear would pass both 
    617     # equations simultaneously to Sympy to simplify, and this would not be 
    618     # a problem. In order to solve something like this, you would have to solve for  
    619     # one variable and then plug that into the other equation. Can get bad if there 
    620     # are many equations and many dimensions though. Maybe I could try to hack  
    621     # something together that tries to eliminate variables?? For example, solves for x1, 
    622     # then plugs that into x1 everywhere.... 
    623647    string = """ 
    624648x2 - 2.*x1*x3 = 1. 
    625 x3 = x1*x2 + 3. 
     649x3 = x1/x2 + 3. 
    626650""" 
    627651    # string2 is linear 
     
    6306542.*x1 - 3.*x3 = 0.5*x2""" 
    631655    x0 = [0.8,1.2,-0.7]  
    632     lb = [-1., -1., -1.]   
     656    lb = [-1., -1., -1.] 
    633657    ub = [200., 100., 200.] 
    634     #cf = form_constraints_function(string2, len(x0), x0=x0, \ 
    635     #                                lower_bounds=lb, upper_bounds=ub) 
    636     cf = form_constraints_nonlinear_sub(string, len(x0)) 
     658    cf = form_constraints_function(string, len(x0), x0=x0, \ 
     659                                    lower_bounds=lb, upper_bounds=ub) 
    637660    c = cf(x0) 
    638     print 'constraints satisfied?', verify_constraints_satisfied(string, c, len(x0)) 
     661    print 'constraints satisfied?\n', verify_constraints_satisfied(string, c, len(x0)) 
    639662    print 'Strict: constraints(x0) = ', c 
    640663 
     
    662685    #print 'cf([1.]*4)', cf([1.]*4) 
    663686    #print 'cf(cf([1.]*4))', cf(cf([1.]*4)) 
    664     cf = form_contsraints_nonlinear_sub(eqnstring, 4)#form_constraints_function(eqnstring, 4) 
     687    cf = form_contsraints_nonlinear(eqnstring, 4)#form_constraints_function(eqnstring, 4) 
    665688    print 'cf([1.]*4)', cf([1.]*4) 
    666689    print 'cf(cf([1.]*4))', cf(cf([1.]*4)) 
     
    669692    # Test a nonlinear constraints example with no x0 or bounds and not too many  
    670693    # variables in the same equation. 
    671     # Notes: string1 works poorly due to a **(1/2) being evaluated as **(0) by python. 
     694    # string1 now works fine. 
    672695    # stringlinear works fine, and can be compared to form_constraints_linear. 
    673696    # string2 works fine. 
    674     string = """ 
     697    string1 = """ 
    675698x2*x3 = 1. + x1*0.9 
    676699x3 = x1/x2 - 3. + x4 
     
    680703x2 + 0.5*x1 = x3*2. + x4 + 2. 
    6817045.*x3 - x2 = x1 + x4*3. 
     705x1 = x2 
    682706""" 
    683707    string2 = """ 
     
    687711""" 
    688712    x0 = [0.8,1.2,-0.7, 1.3] 
     713    lb = [-5., -5., -5., -5.]   
     714    ub = [10., 10., 10., 10.] 
    689715    #cf=form_constraints_function(stringlinear, len(x0)) 
    690     cf = form_constraints_nonlinear_sub(string2, len(x0)) 
     716    cf = form_constraints_nonlinear(string1, len(x0), x0=x0, lower_bounds=lb,\ 
     717            upper_bounds=ub) 
    691718    c = cf(x0) 
    692719    print 'consistent?', verify_constraints_consistent(c, cf) 
    693     print 'satisfied?', verify_constraints_satisfied(string2, c, len(x0)) 
     720    print 'satisfied?\n', verify_constraints_satisfied(string1, c, len(x0)) 
    694721 
    695722if __name__ == '__main__': 
     
    698725    #test3() 
    699726    #test4() 
    700     test5() 
     727    #test5() 
    701728    #test6() 
    702729    #test7() 
    703     #test8() 
     730    test8() 
  • branches/alta/mystic-0.2a1/differential_evolution.py

    r288 r297  
    229229        the current parameter vector.  [default = None] 
    230230    disp -- non-zero to print convergence messages. 
    231  
     231    usebounceback -- True to use 'bounce back'. 
    232232        """ 
    233233        #allow for inputs that don't conform to AbstractSolver interface 
     
    238238        callback=None        #user-supplied function, called after each step 
    239239        disp=0               #non-zero to print convergence messages 
     240        usebounceback = True # True to use 'bounce back'.  
    240241        if kwds.has_key('strategy'): strategy = kwds['strategy'] 
    241242        if kwds.has_key('CrossProbability'): \ 
     
    244245        if kwds.has_key('callback'): callback = kwds['callback'] 
    245246        if kwds.has_key('disp'): disp = kwds['disp'] 
     247        if kwds.has_key('usebounceback'): usebounceback = kwds['usebounceback'] 
    246248        #------------------------------------------------------------- 
    247249 
     
    315317 
    316318                # Use 'bounce back'. Helps a little when soln is on the boundary. 
    317                 self._keepSolutionWithinRangeBoundary(base) 
     319                if usebounceback: 
     320                    self._keepSolutionWithinRangeBoundary(base) 
    318321 
    319322                if costfunction(self.constraints(self.trialSolution[:])) != inf: 
     
    461464        encounters a parameter set that satisfies user-supplied constraints. 
    462465    disp -- non-zero to print convergence messages. 
     466    usebounceback -- True to use 'bounce back'. 
    463467        """ 
    464468        #allow for inputs that don't conform to AbstractSolver interface 
     
    469473        callback=None        #user-supplied function, called after each step 
    470474        disp=0               #non-zero to print convergence messages 
     475        usebounceback = True # True to use 'bounce back'.  
    471476        if kwds.has_key('strategy'): strategy = kwds['strategy'] 
    472477        if kwds.has_key('CrossProbability'): \ 
     
    475480        if kwds.has_key('callback'): callback = kwds['callback'] 
    476481        if kwds.has_key('disp'): disp = kwds['disp'] 
     482        if kwds.has_key('usebounceback'): usebounceback = kwds['usebounceback'] 
    477483        #------------------------------------------------------------- 
    478484 
     
    561567                strategy(self, candidate) 
    562568 
    563                 # Use 'bounce back'. Helps a little when soln is on the boundary. 
    564                 self._keepSolutionWithinRangeBoundary(base) 
     569                if usebounceback: 
     570                    # Use 'bounce back'. Helps a little when soln is on the boundary. 
     571                    self._keepSolutionWithinRangeBoundary(base) 
    565572 
    566573                # Only apply constraints if they don't violate bounds. 
Note: See TracChangeset for help on using the changeset viewer.