Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lcmaes_interface #125

Closed
wants to merge 8 commits into from
Closed

lcmaes_interface #125

wants to merge 8 commits into from

Conversation

nikohansen
Copy link
Collaborator

Tries to make usage from python even more simple.

@beniz
Copy link
Collaborator

beniz commented Feb 16, 2015

Cool, thanks. I think we should extend this interface to bound handling. That is, with additional options to to_params to define whether bounds and/or linear scaling of parameters are requested. Then we would need another function in the interface, say optimize, that would select between pcmaes, pcmaes_pwqb, etc... What do you think ?

@nikohansen
Copy link
Collaborator Author

Does that imply that the interface would use a different make_parameters_... and consequently compute certain default values on its own?

An approach closer to the C++ interface would be to have a to_params functions for each of the different parameter types. They would probably share the way of setting fplot and kwargs, but would generate a specified parameters class in the beginning.

@beniz
Copy link
Collaborator

beniz commented Feb 16, 2015

So, FTR, the C++ doesn't really exhibit different functions for each of the different parameter types, it is more of a difficulty to translate the C++ templates into Python functions. The only solution I did find is to generate different functions for different parameter types. I'm pretty much positive there's no real way around this.

Next comes my tentative at supporting bounds from within the interface without too many changes:

from __future__ import (absolute_import, division,
                        print_function, unicode_literals)
import lcmaes
import cma_multiplt as cmaplt
fplot_current = b'lcmaes.dat'

def to_params(x0, sigma0, str_algo=b'acmaes', fplot=None, lbounds=None, ubounds=None, **kwargs):
    """return parameter object instance for `lcmaes.pcmaes`.                                                                                         

    Keys in `kwargs` must correspond to `name` in `set_name` attributes                                                                              
    of `lcmaes.CMAParameters`, e.g. ``ftarget=1e-7``.                                                                                                

    Details: when `fplot is None` (default), the default output filename                                                                             
    is used.                                                                                                                                         
    """
    has_bounds = not lbounds==None and not ubounds == None
    p = None
    if has_bounds:
    gp = lcmaes.make_genopheno_pwqb(lbounds,ubounds,len(ubounds))
        p = lcmaes.make_parameters_pwqb(x0,sigma0,gp)
    else:
        p = lcmaes.make_simple_parameters(x0, sigma0)
    p.set_str_algo(str_algo)
    if fplot and fplot != True:  # then fplot must be filename                                                                                       
        global fplot_current
        fplot_current = fplot
    if fplot or fplot is None:  # 0 or False or '' or "" prevents writing                                                                            
        p.set_fplot(fplot_current)
    for key, val in kwargs.items():
    setter = "set_" + key
    if not hasattr(p, setter):
            raise ValueError(setter + " is not known as method of CMAParameters")
        getattr(p, setter)(val)  # call setter with value                                                                                            
    return p

def pcmaes(fitfunc,p,has_bounds=False):
    if not has_bounds:
    return lcmaes.pcmaes(fitfunc,p)
    else:
        return lcmaes.pcmaes_pwqb(fitfunc,p)

def to_fitfunc(f):
    """return function for lcmaes from callable `f`, where `f` accepts a list of numbers as input."""
    return lcmaes.fitfunc_pbf.from_callable(lambda x, n: f(x))

def plot(file=None):
    cmaplt.plot(file if file else fplot_current)

I use it as follows:

# setup input parameters                                                                                  
myfun = lambda x: sum([xi**2 for xi in x])  # myfun accepts a list of numbers as input                    
x0 = [2.1] * 10
sigma0 = 0.1
mlbounds = [-4]*10
mubounds = [4]*10

# run optimization via lci                                                                                
res = lci.pcmaes(lci.to_fitfunc(myfun),
                 lci.to_params(x0, sigma0,
                               str_algo=b'aipop', # b=bytes, unicode fails                               
                               lbounds=mlbounds,ubounds=mubounds,
                               restarts=2),True)
lci.plot()  # plot from file set in lci.to_params

There's redundancy in passing both the bounds and a 'True' parameter to the lci.pcmaes function, so I'm not fully happy with it, but maybe it could be a step.

@nikohansen
Copy link
Collaborator Author

Looks like a good start (I believe there are still a few typos). It shouldn't be difficult to remove the True: it is possible to test the type of p with something like isinstance(p, lcmaes.CMAParameters_pwqb) and make a respective decision in the method lci.pcmaes. If you commit the changes I can try to take care of the details.

beniz pushed a commit that referenced this pull request Feb 20, 2015
beniz pushed a commit that referenced this pull request Feb 20, 2015
@beniz
Copy link
Collaborator

beniz commented Feb 20, 2015

Done with the two commits above. test_interface.py is an example of how to use the interface, and it basically looks like this:

import lcmaes_interface as lci

# setup input parameters                                                                                  
myfun = lambda x: sum([xi**2 for xi in x])  # myfun accepts a list of numbers as input                    
x0 = [2.1] * 10
sigma0 = 0.1
mlbounds = [-4]*10
mubounds = [4]*10

# run optimization via lci                                                                                
res = lci.pcmaes(lci.to_fitfunc(myfun),
                 lci.to_params(x0, sigma0,
                               str_algo=b'abipop',quiet=True,                                                                     
                               lbounds=mlbounds,ubounds=mubounds,
                               scaling=False,
                               restarts=2))
lci.plot()  # plot from file set in lci.to_params

The only thing I don't like too much is that lci.to_params has both a boolean scaling argument and a vscaling vector when needed. This is because linear scaling of parameters requires a vector only when no bounds have been specified.

I can merge into 'dev' branch and ready this for next release if we agree on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants