Changeset 608 for branches


Ignore:
Timestamp:
12/10/12 11:53:49 (3 years ago)
Author:
mmckerns
Message:

added solve, as_constraint, and as_penalty;
added tests for solve, issolution, as_penalty, and as_constraint;
issolution now works for penalty functions and constraints solvers;
fix for randomstate and a first better handling of solver init in sumt;

Location:
branches/decorate
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/decorate/constraints.py

    r603 r608  
    7676 
    7777 
     78# EOF 
  • branches/decorate/penalty.py

    r606 r608  
    419419 
    420420 
    421 def issolution(penalty, candidate, tol=1e-3): 
    422     """check if the candidate is a solution to penalty constraints 
    423  
    424 penalty: a penalty function 
    425 candidate: a candidate solution 
    426     """ 
    427     if penalty.error(candidate) <= tol: return True 
     421def issolution(constraints, candidate, tol=1e-3): 
     422    """Returns whether the candidate is a solution to the constraints 
     423 
     424Input: 
     425    constraints -- a constraints solver function or a penalty function 
     426    candidate -- list of parameter values prposed to solve the constraints 
     427    tol -- residual error magnitude for which constraints are considered solved 
     428    """ 
     429    if hasattr(constraints, 'error'): 
     430        error = constraints.error(candidate) 
     431    else: # is a constraints solver 
     432        error = 0.0 
     433        constrained = constraints(candidate) 
     434        for i in range(len(candidate)): 
     435            error += (constrained[i] - candidate[i])**2  #XXX: better rnorm ? 
     436        error = error**0.5 
     437 
     438    if error <= tol: return True 
    428439    return False 
    429440 
     441 
    430442#XXX: nice if penalty.error could give error for each condition... or total 
    431443 
    432444 
     445def solve(constraints, guess=None, nvars=None, solver=None, \ 
     446          lower_bounds=None, upper_bounds=None, termination=None): 
     447    """Use optimization to find a solution to a set of constraints. 
     448 
     449Inputs: 
     450    constraints -- a constraints solver function or a penalty function 
     451 
     452Additional Inputs: 
     453    guess -- list of parameter values proposed to solve the constraints. 
     454    lower_bounds -- list of lower bounds on solution values. 
     455    upper_bounds -- list of upper bounds on solution values. 
     456    nvars -- number of parameter values. 
     457    solver -- the mystic solver to use in the optimization 
     458    termination -- the mystic termination to use in the optimization 
     459 
     460NOTE: The resulting constraints will likely be more expensive to evaluate 
     461    and less accurate than writing the constraints solver from scratch. 
     462    """ 
     463    ndim = 1 #XXX: better, increase in while loop catching IndexError ? 
     464    if nvars is not None: ndim = nvars 
     465    elif guess is not None: ndim = len(guess) 
     466    elif lower_bounds is not None: ndim = len(lower_bounds) 
     467    elif upper_bounds is not None: ndim = len(upper_bounds) 
     468 
     469    def cost(x): return 1. 
     470 
     471    #XXX: don't allow solver string as a short-cut? 
     472    if solver is None or solver == 'diffev': 
     473        from mystic.solvers import DifferentialEvolutionSolver as TheSolver 
     474        solver = TheSolver(ndim, min(40, ndim*5)) 
     475    elif solver == 'diffev2': 
     476        from mystic.solvers import DifferentialEvolutionSolver2 as TheSolver 
     477        solver = TheSolver(ndim, min(40, ndim*5)) 
     478    elif solver == 'fmin_powell': #XXX: better as the default? (it's not random) 
     479        from mystic.solvers import PowellDirectionalSolver as TheSolver 
     480        solver = TheSolver(ndim) 
     481    elif solver == 'fmin': 
     482        from mystic.solvers import NelderMeadSimplexSolver as TheSolver 
     483        solver = TheSolver(ndim) 
     484     
     485    if termination is None: 
     486        from mystic.termination import ChangeOverGeneration as COG 
     487        termination = COG() 
     488    if guess != None: 
     489        solver.SetInitialPoints(guess) #XXX: nice if 'diffev' also had methods 
     490    else: 
     491        solver.SetRandomInitialPoints(lower_bounds, upper_bounds) 
     492    if lower_bounds or upper_bounds: 
     493        solver.SetStrictRanges(lower_bounds, upper_bounds) 
     494    if hasattr(constraints, 'iter') and hasattr(constraints, 'error'): 
     495        solver.SetPenalty(constraints) #i.e. is a penalty function 
     496    else: # is a constraints solver 
     497        solver.SetConstraints(constraints) 
     498    solver.Solve(cost, termination) 
     499    soln = solver.bestSolution 
     500 
     501    from numpy import ndarray, array 
     502    if isinstance(soln, ndarray) and not isinstance(guess, ndarray): 
     503        soln = soln.tolist() 
     504    elif isinstance(guess, ndarray) and not isinstance(soln, ndarray): 
     505        soln = array(soln)  #XXX: or always return a list ? 
     506 
     507    return soln #XXX: check with 'issolution' ? 
     508 
     509 
     510def as_constraint(penalty, *args, **kwds): 
     511    """Convert a penalty function to a constraints solver. 
     512 
     513Inputs: 
     514    penalty -- a penalty function 
     515 
     516Additional Inputs: 
     517    lower_bounds -- list of lower bounds on solution values. 
     518    upper_bounds -- list of upper bounds on solution values. 
     519    nvars -- number of parameter values. 
     520    solver -- the mystic solver to use in the optimization 
     521    termination -- the mystic termination to use in the optimization 
     522    """ 
     523    def constraint(x): #XXX: better to enable args kwds for penalty ? 
     524        return solve(penalty, x, *args, **kwds) 
     525    return constraint 
     526 
     527 
     528def as_penalty(constraint, ptype=None, *args, **kwds): 
     529    """Convert a constraints solver to a penalty function. 
     530 
     531Inputs: 
     532    constraint -- a constraints solver 
     533    ptype -- penalty function type [default: quadratic_equality] 
     534 
     535Additional Inputs: 
     536    args -- arguments for the constraints solver [default: ()] 
     537    kwds -- keyword arguments for the constraints solver [default: {}] 
     538    k -- penalty multiplier 
     539    h -- iterative multiplier 
     540    """ 
     541    def rnorm(x, *argz, **kwdz): 
     542        error = 0.0 
     543        constrained = constraint(x, *argz, **kwdz) 
     544        for i in range(len(x)): 
     545            error += (constrained[i] - x[i])**2  #XXX: better rnorm ? 
     546        error = error**0.5 
     547        return error 
     548 
     549    if ptype is None: ptype = quadratic_equality 
     550 
     551    @ptype(rnorm, *args, **kwds) #XXX: yes to k,h... but otherwise? 
     552    def penalty(x): 
     553        return 0.0 
     554 
     555    return penalty 
     556 
     557 
    433558# EOF  
  • branches/decorate/restarts.py

    r605 r608  
    1 # XXX: solver restart live, from log... restart from saved population as initial 
    2 # XXX: extract solver restart loop for penalties from constraints.py line 1785 
    3 # XXX: extract solver restart loop for collapse from collapse_code.py line 67 
    4  
    5 # XXX: capture and restart from full population 
    6 # XXX: restart from RandomInputs + bestSolution 
    7  
    8 #def live_restart(solver): 
    9 #def log_restart(logs): 
    10 #def penalty_restart(solver): 
    11 #def collapse_restart(solver): 
    12  
    131 
    142def sumt(cost, solver, term, eps = 1e-4, max_SUMT_iters = 10, **kwds): 
     
    4432 
    4533        if randomstate: 
     34            import random 
    4635            random.setstate(randomstate) 
    4736 
    4837        # 'Clear' some of the attributes of the solver 
    49         solver.population = [[0.0 for i in range(solver.nDim)] for j in range(solver.nPop)]  #XXX no 
     38        solver.population = [[0.0 for i in range(solver.nDim)] for j in range(solver.nPop)] 
    5039        solver.generations = 0 
    5140        solver.bestEnergy = 0.0 
     
    5342        solver.trialSolution = [0.0] * solver.nDim 
    5443        solver._init_popEnergy  = inf 
    55         solver.popEnergy = [solver._init_popEnergy] * solver.nPop  #XXX: no 
     44        solver.popEnergy = [solver._init_popEnergy] * solver.nPop 
    5645        solver.energy_history = [] 
    5746 
    58        #XXX: needs __init__ to correct the above 'XXX' 
    59        #dim = solver.nDim 
    60        #simplex = dim + 1 
    61        #solver.popEnergy = [0.0] * simplex 
    62        #solver.population = [[0.0 for i in range(dim)] for j in range(simplex)] 
     47        # individual solver __init__ 
     48        if solver.__class__.__name__ == 'NelderMeadSimplexSolver': 
     49            simplex = solver.nDim + 1 
     50            solver.popEnergy = [solver._init_popEnergy] * simplex 
     51            solver.population = [[0.0 for i in range(solver.nDim)] for j in range(simplex)] 
     52        elif solver.__class__.__name__ == 'PowellDirectionalSolver': 
     53            solver._direc = None 
     54        elif solver.__class__.__name__ in ['DifferentialEvolutionSolver','DifferentialEvolutionSolver2']: 
     55            solver.genealogy = [ [] for j in range(solver.nPop)] 
     56        else: pass 
    6357 
    6458        # Perform the 'inner' optimization 
  • branches/decorate/test_penalty.py

    r604 r608  
    11from penalty import * 
    2 #from mystic.math import almostEqual 
     2from mystic.math import almostEqual 
    33 
    44def test_penalize(): 
     
    2929 
    3030 
     31def test_solve(): 
     32 
     33  from mystic.math.measures import mean 
     34  def mean_constraint(x, target): 
     35    return mean(x) - target 
     36 
     37  def parameter_constraint(x): 
     38    return x[-1] - x[0] 
     39 
     40  @quadratic_equality(condition=mean_constraint, kwds={'target':5.0}) 
     41  @quadratic_equality(condition=parameter_constraint) 
     42  def penalty(x): 
     43    return 0.0 
     44 
     45  x = solve(penalty, guess=[2,3,1]) 
     46 
     47  assert round(mean_constraint(x, 5.0)) == 0.0 
     48  assert round(parameter_constraint(x)) == 0.0 
     49  assert issolution(penalty, x) 
     50 
     51 
     52def test_solve_constraint(): 
     53 
     54  from mystic.math.measures import mean 
     55  from constraints import with_mean 
     56 
     57  @with_mean(1.0) 
     58  def constraint(x): 
     59    x[-1] = x[0] 
     60    return x 
     61 
     62  x = solve(constraint, guess=[2,3,1]) 
     63 
     64  assert almostEqual(mean(x), 1.0, tol=1e-15) 
     65  assert x[-1] == x[0] 
     66  assert issolution(constraint, x) 
     67 
     68 
     69def test_as_constraint(): 
     70 
     71  from mystic.math.measures import mean, spread 
     72  def mean_constraint(x, target): 
     73    return mean(x) - target 
     74 
     75  def range_constraint(x, target): 
     76    return spread(x) - target 
     77 
     78  @quadratic_equality(condition=range_constraint, kwds={'target':5.0}) 
     79  @quadratic_equality(condition=mean_constraint, kwds={'target':5.0}) 
     80  def penalty(x): 
     81    return 0.0 
     82 
     83  ndim = 3 
     84  constraints = as_constraint(penalty, solver='fmin_powell') 
     85  #XXX: this is expensive to evaluate, as there are nested optimizations 
     86 
     87  from numpy import arange 
     88  x = arange(ndim) 
     89  _x = constraints(x) 
     90   
     91  assert round(mean(_x)) == 5.0 
     92  assert round(spread(_x)) == 5.0 
     93 
     94  def cost(x): 
     95    return abs(sum(x) - 5.0) 
     96 
     97  npop = ndim*3 
     98  from mystic.solvers import fmin_powell, diffev 
     99  y = diffev(cost, x, npop, constraints=constraints, disp=False, gtol=10) 
     100 
     101  assert round(mean(y)) == 5.0 
     102  assert round(spread(y)) == 5.0 
     103  assert round(cost(y)) == 5.0*(ndim-1) 
     104 
     105 
     106def test_as_penalty(): 
     107 
     108  from mystic.math.measures import mean, spread 
     109  from constraints import with_mean, with_spread 
     110 
     111  @with_spread(5.0) 
     112  @with_mean(5.0) 
     113  def constraint(x): 
     114    return x 
     115 
     116  penalty = as_penalty(constraint) 
     117 
     118  from numpy import array 
     119  x = array([1,2,3,4,5]) 
     120   
     121  def cost(x): 
     122    return abs(sum(x) - 5.0) 
     123 
     124  from mystic.solvers import fmin_powell 
     125  y = fmin_powell(cost, x, penalty=penalty, disp=False) 
     126 
     127  assert round(mean(y)) == 5.0 
     128  assert round(spread(y)) == 5.0 
     129  assert round(cost(y)) == 4*(5.0) 
     130 
     131 
    31132 
    32133if __name__ == '__main__': 
    33134  test_penalize() 
     135  test_solve() 
     136  test_solve_constraint() 
     137  test_as_constraint() 
     138  test_as_penalty() 
    34139 
    35140 
  • branches/decorate/test_restarts.py

    r606 r608  
    55random_seed(123) 
    66 
    7 def test_sumt1(verbose=False): 
    8  
     7def test_sumt1(solver, verbose=False): 
    98    def constraint1(x): 
    109        x1,x2 = x[0],x[1] 
     
    2726        print "constraints equations:%s" % (constraints_string.rstrip(),) 
    2827 
    29     def costfunc(x): 
     28    def cost(x): 
    3029        x1 = x[0] 
    3130        x2 = x[1] 
    3231        return  x1**4 - 2.*x1**2*x2 + x1**2 + x1*x2**2 - 2.*x1 + 4. 
    3332 
    34     ndim = 2 
    35     x0 = [3., 2.] 
    36     npop = 25 
    37     from mystic.solvers import DifferentialEvolutionSolver 
    38     from mystic.solvers import NelderMeadSimplexSolver 
     33    #XXX: Rand1Bin needs randomstate to work properly when no random_seed ? 
     34   #import random 
     35   #randomstate = random.getstate() #XXX: this should be an internal 
     36    solver.SetPenalty(penalty) 
    3937    from mystic.termination import VTR 
    40     #solver = DifferentialEvolutionSolver(ndim, npop) 
    41     solver = NelderMeadSimplexSolver(ndim) 
    42     solver.SetInitialPoints(x0) 
    43     solver.SetPenalty(penalty) 
    4438    term = VTR() 
    45     end_solver = sumt(costfunc, solver, term, disp=verbose) 
    46     soln = end_solver.Solution() 
     39    _solver = sumt(cost, solver, term, disp=verbose)#, randomstate=randomstate) 
     40    soln = _solver.Solution() 
    4741    satisfied = issolution(penalty, soln) 
    4842    if verbose:  
     
    5347 
    5448 
    55 def test_sumt2(verbose=False): 
     49def test_sumt2(solver, verbose=False): 
    5650    def constraint1(x): 
    5751        x1,x2,x3 = x[0],x[1],x[2] 
     
    8074        print "constraints equations:%s" % (constraints_string.rstrip(),) 
    8175 
    82     def costfunc(x): 
     76    def cost(x): 
    8377        return (x[0] - 1.)**2 + (x[1]-2.)**2 + (x[2]-3.)**4 
    8478 
    85     ndim = 3 
    86     x0 = [0.]*ndim 
    87     from mystic.solvers import DifferentialEvolutionSolver 
    88     from mystic.solvers import NelderMeadSimplexSolver 
     79    #XXX: Rand1Bin needs randomstate to work properly when no random_seed ? 
     80   #import random 
     81   #randomstate = random.getstate() #XXX: this should be an internal 
     82    solver.SetPenalty(penalty) 
    8983    from mystic.termination import VTR 
    90     #solver = DifferentialEvolutionSolver(ndim, npop) 
    91     solver = NelderMeadSimplexSolver(ndim) 
    92     solver.SetInitialPoints(x0) 
    93     solver.SetPenalty(penalty) 
    9484    term = VTR() 
    95     end_solver = sumt(costfunc, solver, term, disp=verbose) 
    96     soln = end_solver.Solution() 
     85    _solver = sumt(cost, solver, term, disp=verbose)#, randomstate=randomstate) 
     86    soln = _solver.Solution() 
    9787    satisfied = issolution(penalty, soln) 
    9888    if verbose: 
     
    10494 
    10595if __name__ == '__main__': 
    106     test_sumt1() 
    107     test_sumt2() 
     96    from mystic.solvers import DifferentialEvolutionSolver 
     97    from mystic.solvers import DifferentialEvolutionSolver2 
     98    from mystic.solvers import PowellDirectionalSolver 
     99    from mystic.solvers import NelderMeadSimplexSolver 
     100    verbose = True 
     101 
     102    x0 = [3., 2.]; ndim = 2; npop = 25 
     103   #solver = DifferentialEvolutionSolver(ndim, npop) 
     104   #solver = DifferentialEvolutionSolver2(ndim, npop) 
     105   #solver = PowellDirectionalSolver(ndim) 
     106    solver = NelderMeadSimplexSolver(ndim) 
     107    solver.SetInitialPoints(x0) 
     108    test_sumt1(solver, verbose=verbose) 
     109 
     110    x0 = [0., 0., 0.]; ndim = 3; npop = 40 
     111   #solver = DifferentialEvolutionSolver(ndim, npop) 
     112   #solver = DifferentialEvolutionSolver2(ndim, npop) 
     113   #solver = PowellDirectionalSolver(ndim) 
     114    solver = NelderMeadSimplexSolver(ndim) 
     115    solver.SetInitialPoints(x0) 
     116    test_sumt2(solver, verbose=verbose) 
    108117 
    109118 
  • branches/decorate/wrapper.py

    r603 r608  
    11#XXX: be mindful if the decorators restrict to functions that expect arrays 
    22#     compare against the originals for restrictions 
     3 
     4#XXX: when_decorated registers methods to 'populate up' when is decorated ? 
     5 
    36 
    47def wrap(outer=lambda x:x, args=None, kwds=None): #XXX: *args, **kwds ? 
Note: See TracChangeset for help on using the changeset viewer.