Exemplo n.º 1
0
class Shekel(AbstractFunction):
    __doc__ = \
    """a Shekel's Foxholes function generator

Shekel's Foxholes function [1,2,3] has a generally flat surface
with several narrow wells. The function's global minimum is at
(-32, -32), with local minima at (i,j) in (-32, -16, 0, 16, 32).

The generated function f(x) is a modified version of equation (21)
of [2], where len(x) == 2.
    """ + _doc

    def __init__(self, ndim=2):
        AbstractFunction.__init__(self, ndim=ndim)
        return

    def function(self, coeffs):
        """evaluates a 2-D Shekel's Foxholes function for a list of coeffs

f(x) = 1 / (0.002 + f_0(x))

Where:
f_0(x) = \sum_(i=0)^(24) 1 / (i + \sum_(j=0)^(1) (x_j - a_ij)^(6))
with a_ij=(-32,-16,0,16,32).
for j=0 and i=(0,1,2,3,4), a_i0=a_k0 with k=i \mod 5
also j=1 and i=(0,5,10,15,20), a_i1=a_k1 with k=i+k' and k'=(1,2,3,4).

Inspect with mystic_model_plotter using::
    mystic.models.shekel -b "-50:50:1, -50:50:1" -d -x 1

The minimum is f(x)=0 for x=(-32,-32)"""
        A = [-32., -16., 0., 16., 32.]
        a1 = A * 5
        a2 = reduce(lambda x1, x2: x1 + x2, [[c] * 5 for c in A])

        x, y = coeffs
        r = 0.0
        for i in range(25):
            #           r += 1.0/ (1.0*i + pow(x-a1[i],6) + pow(y-a2[i],6) + 1e-15)
            z = 1.0 * i + pow(x - a1[i], 6) + pow(y - a2[i], 6)
            if z: r += 1.0 / z
            else: r += inf
        return 1.0 / (0.002 + r)

    minimizers = [(j, i) for (i, j) in sorted(
        list(permutations(range(-32, 33, 16), 2)) +
        [(i, i) for i in range(-32, 33, 16)])]
    pass
Exemplo n.º 2
0
def _solve_nonlinear(constraints, variables='x', target=None, **kwds):
    """Build a constraints function given a string of nonlinear constraints.
Returns a constraints function. 

Inputs:
    constraints -- a string of symbolic constraints, with one constraint
        equation per line. Constraints must be equality constraints only.
        Standard python syntax should be followed (with the math and numpy
        modules already imported).

    For example:
        >>> constraints = '''x1 = x3*3. + x0*x2'''
        >>> print _solve_nonlinear(constraints)
        x0 = (x1 - 3.0*x3)/x2
        >>> constraints = '''
        ...     spread([x0,x1]) - 1.0 = mean([x0,x1])   
        ...     mean([x0,x1,x2]) = x2'''
        >>> print _solve_nonlinear(constraints)
        x0 = -0.5 + 0.5*x2
        x1 = 0.5 + 1.5*x2

Additional Inputs:
    variables -- desired variable name. Default is 'x'. A list of variable
        name strings is also accepted for when desired variable names
        don't have the same base, and can include variables that are not
        found in the constraints equation string.
    target -- list providing the order for which the variables will be solved.
        If there are "N" constraint equations, the first "N" variables given
        will be selected as the dependent variables. By default, increasing
        order is used.

    For example:
        >>> constraints = '''
        ...     spread([x0,x1]) - 1.0 = mean([x0,x1])   
        ...     mean([x0,x1,x2]) = x2'''
        >>> print _solve_nonlinear(constraints, target=['x1'])
        x1 = -0.833333333333333 + 0.166666666666667*x2
        x0 = -0.5 + 0.5*x2

Further Inputs:
    locals -- a dictionary of additional variables used in the symbolic
        constraints equations, and their desired values.
"""
    nvars = None
    permute = False # if True, return all permutations
    warn = True  # if True, don't supress warnings
    verbose = False # if True, print details from _classify_variables
    #-----------------------undocumented-------------------------------
    permute = kwds['permute'] if 'permute' in kwds else permute
    warn = kwds['warn'] if 'warn' in kwds else warn
    verbose = kwds['verbose'] if 'verbose' in kwds else verbose
    #------------------------------------------------------------------
    if target in [None, False]:
        target = []
    elif isinstance(target, str):
        target = target.split(',')
    else:
        target = list(target) # not the best for ndarray, but should work

    from mystic.symbolic import replace_variables, get_variables
    if list_or_tuple_or_ndarray(variables):
        if nvars is not None: variables = variables[:nvars]
        constraints = replace_variables(constraints, variables, '_')
        varname = '_'
        ndim = len(variables)
    else:
        varname = variables # varname used below instead of variables
        myvar = get_variables(constraints, variables)
        if myvar: ndim = max([int(v.strip(varname)) for v in myvar]) + 1
        else: ndim = 0
    if nvars is not None: ndim = nvars

    # create function to replace "_" with original variables
    def restore(variables, mystring):
        if list_or_tuple_or_ndarray(variables):
            vars = get_variables(mystring,'_')
            indices = [int(v.strip('_')) for v in vars]
            for i in range(len(vars)):
                mystring = mystring.replace(vars[i],variables[indices[i]])
        return mystring

    locals = kwds['locals'] if 'locals' in kwds else None
    if locals is None: locals = {}

    eqns = constraints.splitlines()
    # Remove empty strings:
    actual_eqns = []
    for j in range(len(eqns)):
        if eqns[j].strip():
           actual_eqns.append(eqns[j].strip())
    orig_eqns = actual_eqns[:]

    neqns = len(actual_eqns)

    xperms = [varname+str(i) for i in range(ndim)]
    if target:
        [target.remove(i) for i in target if i not in xperms]
        [target.append(i) for i in xperms if i not in target]
        _target = []
        [_target.append(i) for i in target if i not in _target]
        target = _target
    target = tuple(target)

    xperms = list(permutations(xperms)) #XXX: takes a while if nvars is ~10
    if target: # Try the suggested order first.
        xperms.remove(target)
        xperms.insert(0, target)

    complete_list = []

    constraints_function_list = []
    # Some of the permutations will give the same answer;
    # look into reducing the number of repeats?
    for perm in xperms:
        # Sort the list actual_eqns so any equation containing x0 is first, etc.
        sorted_eqns = []
        actual_eqns_copy = orig_eqns[:]
        usedvars = []
        for variable in perm: # range(ndim):
            for eqn in actual_eqns_copy:
                if eqn.find(variable) != -1:
                    sorted_eqns.append(eqn)
                    actual_eqns_copy.remove(eqn)
                    usedvars.append(variable)
                    break
        if actual_eqns_copy: # Append the remaining equations
            for item in actual_eqns_copy:
                sorted_eqns.append(item)
        actual_eqns = sorted_eqns

        # Append the remaining variables to usedvars
        tempusedvar = usedvars[:]
        tempusedvar.sort()
        nmissing = ndim - len(tempusedvar)
        for m in range(nmissing):
            usedvars.append(varname + str(len(tempusedvar) + m))

        for i in range(neqns):
            # Trying to use xi as a pivot. Loop through the equations
            # looking for one containing xi.
            _target = usedvars[i]
            for eqn in actual_eqns[i:]:
                invertedstring = _solve_single(eqn, variables=varname, target=_target, warn=warn)
                if invertedstring:
                    warn = False
                    break
            # substitute into the remaining equations. the equations' order
            # in the list newsystem is like in a linear coefficient matrix.
            newsystem = ['']*neqns
            j = actual_eqns.index(eqn)
            newsystem[j] = eqn
            othereqns = actual_eqns[:j] + actual_eqns[j+1:]
            for othereqn in othereqns:
                expression = invertedstring.split("=")[1]
                fixed = othereqn.replace(_target, '(' + expression + ')')
                k = actual_eqns.index(othereqn)
                newsystem[k] = fixed
            actual_eqns = newsystem
            
        # Invert so that it can be fed properly to generate_constraint
        simplified = []
        for eqn in actual_eqns:
            _target = usedvars[actual_eqns.index(eqn)]
            mysoln = _solve_single(eqn, variables=varname, target=_target, warn=warn)
            if mysoln: simplified.append(mysoln)
        simplified = restore(variables, '\n'.join(simplified).rstrip())

        if permute:
            complete_list.append(simplified)
            continue

        if verbose:
            print _classify_variables(simplified, variables, ndim)
        return simplified

    warning='Warning: an error occurred in building the constraints.'
    if warn: print warning
    if verbose:
        print _classify_variables(simplified, variables, ndim)
    if permute: #FIXME: target='x3,x1' may order correct, while 'x1,x3' doesn't
        filter = []; results = []
        for i in complete_list:
            _eqs = '\n'.join(sorted(i.split('\n')))
            if _eqs and (_eqs not in filter):
                filter.append(_eqs)
                results.append(i)
        return tuple(results) #FIXME: somehow 'rhs = xi' can be in results
    return simplified
Exemplo n.º 3
0
def _solve_linear(constraints, variables='x', target=None, **kwds):
    """Solve a system of symbolic linear constraints equations.

Inputs:
    constraints -- a string of symbolic constraints, with one constraint
        equation per line. Constraints must be equality constraints only.
        Standard python syntax should be followed (with the math and numpy
        modules already imported).

    For example:
        >>> constraints = '''
        ...     x0 - x2 = 2.
        ...     x2 = x3*2.'''
        >>> print _solve_linear(constraints)
        x2 = 2.0*x3
        x0 = 2.0 + 2.0*x3

Additional Inputs:
    variables -- desired variable name. Default is 'x'. A list of variable
        name strings is also accepted for when desired variable names
        don't have the same base, and can include variables that are not
        found in the constraints equation string.
    target -- list providing the order for which the variables will be solved.
        If there are "N" constraint equations, the first "N" variables given
        will be selected as the dependent variables. By default, increasing
        order is used.

    For example:
        >>> constraints = '''
        ...     x0 - x2 = 2.
        ...     x2 = x3*2.'''
        >>> print _solve_linear(constraints, target=['x3','x2'])
        x3 = -1.0 + 0.5*x0
        x2 = -2.0 + x0

Further Inputs:
    locals -- a dictionary of additional variables used in the symbolic
        constraints equations, and their desired values.
"""
    nvars = None
    permute = False # if True, return all permutations
    warn = True  # if True, don't supress warnings
    verbose = False  # if True, print debug info
    #-----------------------undocumented-------------------------------
    permute = kwds['permute'] if 'permute' in kwds else permute
    warn = kwds['warn'] if 'warn' in kwds else warn
    verbose = kwds['verbose'] if 'verbose' in kwds else verbose
    #------------------------------------------------------------------
    if target in [None, False]:
        target = []
    elif isinstance(target, str):
        target = target.split(',')
    else:
        target = list(target) # not the best for ndarray, but should work

    from mystic.symbolic import replace_variables, get_variables
    if list_or_tuple_or_ndarray(variables):
        if nvars is not None: variables = variables[:nvars]
        _constraints = replace_variables(constraints, variables, '_')
        varname = '_'
        ndim = len(variables)
        for i in range(len(target)):
            if variables.count(target[i]):
                target[i] = replace_variables(target[i],variables,markers='_')
    else:
        _constraints = constraints
        varname = variables # varname used below instead of variables
        myvar = get_variables(constraints, variables)
        if myvar: ndim = max([int(v.strip(varname)) for v in myvar]) + 1
        else: ndim = 0
    if nvars is not None: ndim = nvars

    # create function to replace "_" with original variables
    def restore(variables, mystring):
        if list_or_tuple_or_ndarray(variables):
            vars = get_variables(mystring,'_')
            indices = [int(v.strip('_')) for v in vars]
            for i in range(len(vars)):
                mystring = mystring.replace(vars[i],variables[indices[i]])
        return mystring

    # default is _locals with sympy imported
    _locals = {}
    locals = kwds['locals'] if 'locals' in kwds else None
    if locals is None: locals = {}
    # if sympy not installed, return original constraints
    try:
        code = """from sympy import Eq, Symbol;"""
        code += """from sympy import solve as symsol;"""
        code = compile(code, '<string>', 'exec')
        exec code in _locals
    except ImportError: # Equation will not be simplified."
        if warn: print "Warning: sympy not installed."
        return constraints

    # default is _locals with numpy and math imported
    # numpy throws an 'AttributeError', but math passes error to sympy
    code = """from numpy import *; from math import *;""" # prefer math
    code += """from numpy import mean as average;""" # use np.mean not average
    code += """from numpy import var as variance;""" # look like mystic.math
    code += """from numpy import ptp as spread;"""   # look like mystic.math
    code = compile(code, '<string>', 'exec')
    exec code in _locals
    _locals.update(locals) #XXX: allow this?

    code,left,right,xlist,neqns = _prepare_sympy(_constraints, varname, ndim)

    eqlist = ""
    for i in range(1, neqns+1):
        eqn = 'eq' + str(i)
        eqlist += eqn + ","
        code += eqn + '= Eq(' + left[i-1] + ',' + right[i-1] + ')\n'
    eqlist = eqlist.rstrip(',')

    # get full list of variables in 'targeted' order
    xperms = xlist.split(',')[:-1]
    targeted = target[:]
    [targeted.remove(i) for i in targeted if i not in xperms]
    [targeted.append(i) for i in xperms if i not in targeted]
    _target = []
    [_target.append(i) for i in targeted if i not in _target]
    targeted = _target
    targeted = tuple(targeted)

    if permute:
        # Form constraints equations for each permutation.
        # This will change the order of the x variables passed to symsol()
        # to get different variables solved for.
        xperms = list(permutations(xperms)) #XXX: takes a while if nvars is ~10
        if target: # put the tuple with the 'targeted' order first
            xperms.remove(targeted)
            xperms.insert(0, targeted)
    else:
        xperms = [tuple(targeted)]

    solns = []
    for perm in xperms: 
        _code = code
        xlist = ','.join(perm).rstrip(',') #XXX: if not all, use target ?
        # solve dependent xi: symsol([linear_system], [x0,x1,...,xi,...,xn])
        # returns: {x0: f(xn,...), x1: f(xn,...), ...}
        _code += 'soln = symsol([' + eqlist + '], [' + xlist + '])'
        #XXX: need to convert/check soln similarly as in _solve_single ?
        if verbose: print _code
        _code = compile(_code, '<string>', 'exec')
        try: 
            exec _code in globals(), _locals
            soln = _locals['soln']
            if not soln:
                if warn: print "Warning: could not simplify equation."
                soln = {}
        except NotImplementedError: # catch 'multivariate' error
            if warn: print "Warning: could not simplify equation."
            soln = {}
        except NameError, error: # catch when variable is not defined
            if warn: print "Warning:", error
            soln = {}
        if verbose: print soln

        solved = ""
        for key, value in soln.iteritems():
            solved += str(key) + ' = ' + str(value) + '\n'
        if solved: solns.append( restore(variables, solved.rstrip()) )
Exemplo n.º 4
0
def _solve_nonlinear(constraints, variables='x', target=None, **kwds):
    """Build a constraints function given a string of nonlinear constraints.
Returns a constraints function. 

Inputs:
    constraints -- a string of symbolic constraints, with one constraint
        equation per line. Constraints must be equality constraints only.
        Standard python syntax should be followed (with the math and numpy
        modules already imported).

    For example:
        >>> constraints = '''x1 = x3*3. + x0*x2'''
        >>> print(_solve_nonlinear(constraints))
        x0 = (x1 - 3.0*x3)/x2
        >>> constraints = '''
        ...     spread([x0,x1]) - 1.0 = mean([x0,x1])   
        ...     mean([x0,x1,x2]) = x2'''
        >>> print(_solve_nonlinear(constraints))
        x0 = -0.5 + 0.5*x2
        x1 = 0.5 + 1.5*x2

Additional Inputs:
    variables -- desired variable name. Default is 'x'. A list of variable
        name strings is also accepted for when desired variable names
        don't have the same base, and can include variables that are not
        found in the constraints equation string.
    target -- list providing the order for which the variables will be solved.
        If there are "N" constraint equations, the first "N" variables given
        will be selected as the dependent variables. By default, increasing
        order is used.

    For example:
        >>> constraints = '''
        ...     spread([x0,x1]) - 1.0 = mean([x0,x1])   
        ...     mean([x0,x1,x2]) = x2'''
        >>> print(_solve_nonlinear(constraints, target=['x1']))
        x1 = -0.833333333333333 + 0.166666666666667*x2
        x0 = -0.5 + 0.5*x2

Further Inputs:
    locals -- a dictionary of additional variables used in the symbolic
        constraints equations, and their desired values.
"""
    nvars = None
    permute = False # if True, return all permutations
    warn = True  # if True, don't suppress warnings
    verbose = False # if True, print details from _classify_variables
    #-----------------------undocumented-------------------------------
    permute = kwds['permute'] if 'permute' in kwds else permute
    warn = kwds['warn'] if 'warn' in kwds else warn
    verbose = kwds['verbose'] if 'verbose' in kwds else verbose
    #------------------------------------------------------------------
    if target in [None, False]:
        target = []
    elif isinstance(target, str):
        target = target.split(',')
    else:
        target = list(target) # not the best for ndarray, but should work

    from mystic.symbolic import replace_variables, get_variables
    if list_or_tuple_or_ndarray(variables):
        if nvars is not None: variables = variables[:nvars]
        constraints = replace_variables(constraints, variables, '_')
        varname = '_'
        ndim = len(variables)
    else:
        varname = variables # varname used below instead of variables
        myvar = get_variables(constraints, variables)
        if myvar: ndim = max([int(v.strip(varname)) for v in myvar]) + 1
        else: ndim = 0
    if nvars is not None: ndim = nvars

    # create function to replace "_" with original variables
    def restore(variables, mystring):
        if list_or_tuple_or_ndarray(variables):
            vars = get_variables(mystring,'_')
            indices = [int(v.strip('_')) for v in vars]
            for i in range(len(vars)):
                mystring = mystring.replace(vars[i],variables[indices[i]])
        return mystring

    locals = kwds['locals'] if 'locals' in kwds else None
    if locals is None: locals = {}

    eqns = constraints.splitlines()
    # Remove empty strings:
    actual_eqns = []
    for j in range(len(eqns)):
        if eqns[j].strip():
           actual_eqns.append(eqns[j].strip())
    orig_eqns = actual_eqns[:]

    neqns = len(actual_eqns)

    xperms = [varname+str(i) for i in range(ndim)]
    if target:
        [target.remove(i) for i in target if i not in xperms]
        [target.append(i) for i in xperms if i not in target]
        _target = []
        [_target.append(i) for i in target if i not in _target]
        target = _target
    target = tuple(target)

    xperms = list(permutations(xperms)) #XXX: takes a while if nvars is ~10
    if target: # Try the suggested order first.
        xperms.remove(target)
        xperms.insert(0, target)

    complete_list = []

    constraints_function_list = []
    # Some of the permutations will give the same answer;
    # look into reducing the number of repeats?
    for perm in xperms:
        # Sort the list actual_eqns so any equation containing x0 is first, etc.
        sorted_eqns = []
        actual_eqns_copy = orig_eqns[:]
        usedvars = []
        for variable in perm: # range(ndim):
            for eqn in actual_eqns_copy:
                if eqn.find(variable) != -1:
                    sorted_eqns.append(eqn)
                    actual_eqns_copy.remove(eqn)
                    usedvars.append(variable)
                    break
        if actual_eqns_copy: # Append the remaining equations
            for item in actual_eqns_copy:
                sorted_eqns.append(item)
        actual_eqns = sorted_eqns

        # Append the remaining variables to usedvars
        tempusedvar = usedvars[:]
        tempusedvar.sort()
        nmissing = ndim - len(tempusedvar)
        for m in range(nmissing):
            usedvars.append(varname + str(len(tempusedvar) + m))

        #FIXME: not sure if the code below should be totally trusted...
        for i in range(neqns):
            # Trying to use xi as a pivot. Loop through the equations
            # looking for one containing xi.
            _target = usedvars[i%len(usedvars)] #XXX: ...to make it len of neqns
            for eqn in actual_eqns[i:]:
                invertedstring = _solve_single(eqn, variables=varname, target=_target, warn=warn)
                if invertedstring:
                    warn = False
                    break
            if invertedstring is None: continue #XXX: ...when _solve_single fails
            # substitute into the remaining equations. the equations' order
            # in the list newsystem is like in a linear coefficient matrix.
            newsystem = ['']*neqns
            j = actual_eqns.index(eqn)
            newsystem[j] = invertedstring #XXX: ...was eqn. I think correct now
            othereqns = actual_eqns[:j] + actual_eqns[j+1:]
            for othereqn in othereqns:
                expression = invertedstring.split("=")[1]
                fixed = othereqn.replace(_target, '(' + expression + ')')
                k = actual_eqns.index(othereqn)
                newsystem[k] = fixed
            actual_eqns = newsystem #XXX: potentially carrying too many eqns

        # Invert so that it can be fed properly to generate_constraint
        simplified = []
        for eqn in actual_eqns[:len(usedvars)]: #XXX: ...needs to be same len
            _target = usedvars[actual_eqns.index(eqn)]
            mysoln = _solve_single(eqn, variables=varname, target=_target, warn=warn)
            if mysoln: simplified.append(mysoln)
        simplified = restore(variables, '\n'.join(simplified).rstrip())

        if permute:
            complete_list.append(simplified)
            continue

        if verbose:
            print(_classify_variables(simplified, variables, ndim))
        return simplified

    warning='Warning: an error occurred in building the constraints.'
    if warn: print(warning)
    if verbose:
        print(_classify_variables(simplified, variables, ndim))
    if permute: #FIXME: target='x3,x1' may order correct, while 'x1,x3' doesn't
        filter = []; results = []
        for i in complete_list:
            _eqs = '\n'.join(sorted(i.split('\n')))
            if _eqs and (_eqs not in filter):
                filter.append(_eqs)
                results.append(i)
        return tuple(results) #FIXME: somehow 'rhs = xi' can be in results
    return simplified
Exemplo n.º 5
0
def _solve_linear(constraints, variables='x', target=None, **kwds):
    """Solve a system of symbolic linear constraints equations.

Inputs:
    constraints -- a string of symbolic constraints, with one constraint
        equation per line. Constraints must be equality constraints only.
        Standard python syntax should be followed (with the math and numpy
        modules already imported).

    For example:
        >>> constraints = '''
        ...     x0 - x2 = 2.
        ...     x2 = x3*2.'''
        >>> print _solve_linear(constraints)
        x2 = 2.0*x3
        x0 = 2.0 + 2.0*x3

Additional Inputs:
    variables -- desired variable name. Default is 'x'. A list of variable
        name strings is also accepted for when desired variable names
        don't have the same base, and can include variables that are not
        found in the constraints equation string.
    target -- list providing the order for which the variables will be solved.
        If there are "N" constraint equations, the first "N" variables given
        will be selected as the dependent variables. By default, increasing
        order is used.

    For example:
        >>> constraints = '''
        ...     x0 - x2 = 2.
        ...     x2 = x3*2.'''
        >>> print _solve_linear(constraints, target=['x3','x2'])
        x3 = -1.0 + 0.5*x0
        x2 = -2.0 + x0

Further Inputs:
    locals -- a dictionary of additional variables used in the symbolic
        constraints equations, and their desired values.
"""
    nvars = None
    permute = False  # if True, return all permutations
    warn = True  # if True, don't supress warnings
    verbose = False  # if True, print debug info
    #-----------------------undocumented-------------------------------
    permute = kwds['permute'] if 'permute' in kwds else permute
    warn = kwds['warn'] if 'warn' in kwds else warn
    verbose = kwds['verbose'] if 'verbose' in kwds else verbose
    #------------------------------------------------------------------
    if target in [None, False]:
        target = []
    elif isinstance(target, str):
        target = target.split(',')
    else:
        target = list(target)  # not the best for ndarray, but should work

    from mystic.symbolic import replace_variables, get_variables
    if list_or_tuple_or_ndarray(variables):
        if nvars is not None: variables = variables[:nvars]
        _constraints = replace_variables(constraints, variables, '_')
        varname = '_'
        ndim = len(variables)
        for i in range(len(target)):
            if variables.count(target[i]):
                target[i] = replace_variables(target[i],
                                              variables,
                                              markers='_')
    else:
        _constraints = constraints
        varname = variables  # varname used below instead of variables
        myvar = get_variables(constraints, variables)
        if myvar: ndim = max([int(v.strip(varname)) for v in myvar]) + 1
        else: ndim = 0
    if nvars is not None: ndim = nvars

    # create function to replace "_" with original variables
    def restore(variables, mystring):
        if list_or_tuple_or_ndarray(variables):
            vars = get_variables(mystring, '_')
            indices = [int(v.strip('_')) for v in vars]
            for i in range(len(vars)):
                mystring = mystring.replace(vars[i], variables[indices[i]])
        return mystring

    # default is _locals with sympy imported
    _locals = {}
    locals = kwds['locals'] if 'locals' in kwds else None
    if locals is None: locals = {}
    # if sympy not installed, return original constraints
    try:
        code = """from sympy import Eq, Symbol;"""
        code += """from sympy import solve as symsol;"""
        code = compile(code, '<string>', 'exec')
        exec code in _locals
    except ImportError:  # Equation will not be simplified."
        if warn: print "Warning: sympy not installed."
        return constraints

    # default is _locals with numpy and math imported
    # numpy throws an 'AttributeError', but math passes error to sympy
    code = """from numpy import *; from math import *;"""  # prefer math
    code += """from numpy import mean as average;"""  # use np.mean not average
    code += """from numpy import var as variance;"""  # look like mystic.math
    code += """from numpy import ptp as spread;"""  # look like mystic.math
    code = compile(code, '<string>', 'exec')
    exec code in _locals
    _locals.update(locals)  #XXX: allow this?

    code, left, right, xlist, neqns = _prepare_sympy(_constraints, varname,
                                                     ndim)

    eqlist = ""
    for i in range(1, neqns + 1):
        eqn = 'eq' + str(i)
        eqlist += eqn + ","
        code += eqn + '= Eq(' + left[i - 1] + ',' + right[i - 1] + ')\n'
    eqlist = eqlist.rstrip(',')

    # get full list of variables in 'targeted' order
    xperms = xlist.split(',')[:-1]
    targeted = target[:]
    [targeted.remove(i) for i in targeted if i not in xperms]
    [targeted.append(i) for i in xperms if i not in targeted]
    _target = []
    [_target.append(i) for i in targeted if i not in _target]
    targeted = _target
    targeted = tuple(targeted)

    if permute:
        # Form constraints equations for each permutation.
        # This will change the order of the x variables passed to symsol()
        # to get different variables solved for.
        xperms = list(
            permutations(xperms))  #XXX: takes a while if nvars is ~10
        if target:  # put the tuple with the 'targeted' order first
            xperms.remove(targeted)
            xperms.insert(0, targeted)
    else:
        xperms = [tuple(targeted)]

    solns = []
    for perm in xperms:
        _code = code
        xlist = ','.join(perm).rstrip(',')  #XXX: if not all, use target ?
        # solve dependent xi: symsol([linear_system], [x0,x1,...,xi,...,xn])
        # returns: {x0: f(xn,...), x1: f(xn,...), ...}
        _code += 'soln = symsol([' + eqlist + '], [' + xlist + '])'
        #XXX: need to convert/check soln similarly as in _solve_single ?
        if verbose: print _code
        _code = compile(_code, '<string>', 'exec')
        try:
            exec _code in globals(), _locals
            soln = _locals['soln']
            if not soln:
                if warn: print "Warning: could not simplify equation."
                soln = {}
        except NotImplementedError:  # catch 'multivariate' error
            if warn: print "Warning: could not simplify equation."
            soln = {}
        except NameError, error:  # catch when variable is not defined
            if warn: print "Warning:", error
            soln = {}
        if verbose: print soln

        solved = ""
        for key, value in soln.iteritems():
            solved += str(key) + ' = ' + str(value) + '\n'
        if solved: solns.append(restore(variables, solved.rstrip()))