Index: src/sas/sascalc/fit/AbstractFitEngine.py
===================================================================
--- src/sas/sascalc/fit/AbstractFitEngine.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/AbstractFitEngine.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,632 @@
+
+import  copy
+#import logging
+import sys
+import math
+import numpy
+
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.data_info import Data2D
+_SMALLVALUE = 1.0e-10
+
+class FitHandler(object):
+    """
+    Abstract interface for fit thread handler.
+
+    The methods in this class are called by the optimizer as the fit
+    progresses.
+
+    Note that it is up to the optimizer to call the fit handler correctly,
+    reporting all status changes and maintaining the 'done' flag.
+    """
+    done = False
+    """True when the fit job is complete"""
+    result = None
+    """The current best result of the fit"""
+
+    def improvement(self):
+        """
+        Called when a result is observed which is better than previous
+        results from the fit.
+
+        result is a FitResult object, with parameters, #calls and fitness.
+        """
+    def error(self, msg):
+        """
+        Model had an error; print traceback
+        """
+    def progress(self, current, expected):
+        """
+        Called each cycle of the fit, reporting the current and the
+        expected amount of work.   The meaning of these values is
+        optimizer dependent, but they can be converted into a percent
+        complete using (100*current)//expected.
+
+        Progress is updated each iteration of the fit, whatever that
+        means for the particular optimization algorithm.  It is called
+        after any calls to improvement for the iteration so that the
+        update handler can control I/O bandwidth by suppressing
+        intermediate improvements until the fit is complete.
+        """
+    def finalize(self):
+        """
+        Fit is complete; best results are reported
+        """
+    def abort(self):
+        """
+        Fit was aborted.
+        """
+
+    # TODO: not sure how these are used, but they are needed for running the fit
+    def update_fit(self, last=False): pass
+    def set_result(self, result=None): self.result = result
+
+class Model:
+    """
+    Fit wrapper for SAS models.
+    """
+    def __init__(self, sas_model, sas_data=None, **kw):
+        """
+        :param sas_model: the sas model to wrap for fitting
+
+        """
+        self.model = sas_model
+        self.name = sas_model.name
+        self.data = sas_data
+
+    def get_params(self, fitparams):
+        """
+        return a list of value of paramter to fit
+
+        :param fitparams: list of paramaters name to fit
+
+        """
+        return [self.model.getParam(k) for k in fitparams]
+
+    def set_params(self, paramlist, params):
+        """
+        Set value for parameters to fit
+
+        :param params: list of value for parameters to fit
+
+        """
+        for k,v in zip(paramlist, params):
+            self.model.setParam(k,v)
+
+    def set(self, **kw):
+        self.set_params(*zip(*kw.items()))
+
+    def eval(self, x):
+        """
+            Override eval method of model.
+
+            :param x: the x value used to compute a function
+        """
+        try:
+            return self.model.evalDistribution(x)
+        except:
+            raise
+
+    def eval_derivs(self, x, pars=[]):
+        """
+        Evaluate the model and derivatives wrt pars at x.
+
+        pars is a list of the names of the parameters for which derivatives
+        are desired.
+
+        This method needs to be specialized in the model to evaluate the
+        model function.  Alternatively, the model can implement is own
+        version of residuals which calculates the residuals directly
+        instead of calling eval.
+        """
+        raise NotImplementedError('no derivatives available')
+
+    def __call__(self, x):
+        return self.eval(x)
+
+class FitData1D(Data1D):
+    """
+        Wrapper class  for SAS data
+        FitData1D inherits from DataLoader.data_info.Data1D. Implements
+        a way to get residuals from data.
+    """
+    def __init__(self, x, y, dx=None, dy=None, smearer=None, data=None):
+        """
+            :param smearer: is an object of class QSmearer or SlitSmearer
+               that will smear the theory data (slit smearing or resolution
+               smearing) when set.
+            
+            The proper way to set the smearing object would be to
+            do the following: ::
+            
+                from sas.models.qsmearing import smear_selection
+                smearer = smear_selection(some_data)
+                fitdata1d = FitData1D( x= [1,3,..,],
+                                        y= [3,4,..,8],
+                                        dx=None,
+                                        dy=[1,2...], smearer= smearer)
+           
+            :Note: that some_data _HAS_ to be of
+                class DataLoader.data_info.Data1D
+                Setting it back to None will turn smearing off.
+                
+        """
+        Data1D.__init__(self, x=x, y=y, dx=dx, dy=dy)
+        self.num_points = len(x)
+        self.sas_data = data
+        self.smearer = smearer
+        self._first_unsmeared_bin = None
+        self._last_unsmeared_bin = None
+        # Check error bar; if no error bar found, set it constant(=1)
+        # TODO: Should provide an option for users to set it like percent,
+        # constant, or dy data
+        if dy is None or dy == [] or dy.all() == 0:
+            self.dy = numpy.ones(len(y))
+        else:
+            self.dy = numpy.asarray(dy).copy()
+
+        ## Min Q-value
+        #Skip the Q=0 point, especially when y(q=0)=None at x[0].
+        if min(self.x) == 0.0 and self.x[0] == 0 and\
+                     not numpy.isfinite(self.y[0]):
+            self.qmin = min(self.x[self.x != 0])
+        else:
+            self.qmin = min(self.x)
+        ## Max Q-value
+        self.qmax = max(self.x)
+        
+        # Range used for input to smearing
+        self._qmin_unsmeared = self.qmin
+        self._qmax_unsmeared = self.qmax
+        # Identify the bin range for the unsmeared and smeared spaces
+        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
+        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
+                            & (self.x <= self._qmax_unsmeared)
+  
+    def set_fit_range(self, qmin=None, qmax=None):
+        """ to set the fit range"""
+        # Skip Q=0 point, (especially for y(q=0)=None at x[0]).
+        # ToDo: Find better way to do it.
+        if qmin == 0.0 and not numpy.isfinite(self.y[qmin]):
+            self.qmin = min(self.x[self.x != 0])
+        elif qmin != None:
+            self.qmin = qmin
+        if qmax != None:
+            self.qmax = qmax
+        # Determine the range needed in unsmeared-Q to cover
+        # the smeared Q range
+        self._qmin_unsmeared = self.qmin
+        self._qmax_unsmeared = self.qmax
+        
+        self._first_unsmeared_bin = 0
+        self._last_unsmeared_bin = len(self.x) - 1
+        
+        if self.smearer != None:
+            self._first_unsmeared_bin, self._last_unsmeared_bin = \
+                    self.smearer.get_bin_range(self.qmin, self.qmax)
+            self._qmin_unsmeared = self.x[self._first_unsmeared_bin]
+            self._qmax_unsmeared = self.x[self._last_unsmeared_bin]
+            
+        # Identify the bin range for the unsmeared and smeared spaces
+        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
+        ## zero error can not participate for fitting
+        self.idx = self.idx & (self.dy != 0)
+        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
+                            & (self.x <= self._qmax_unsmeared)
+
+    def get_fit_range(self):
+        """
+            Return the range of data.x to fit
+        """
+        return self.qmin, self.qmax
+
+    def size(self):
+        """
+        Number of measurement points in data set after masking, etc.
+        """
+        return len(self.x)
+
+    def residuals(self, fn):
+        """
+            Compute residuals.
+            
+            If self.smearer has been set, use if to smear
+            the data before computing chi squared.
+            
+            :param fn: function that return model value
+            
+            :return: residuals
+        """
+        # Compute theory data f(x)
+        fx = numpy.zeros(len(self.x))
+        fx[self.idx_unsmeared] = fn(self.x[self.idx_unsmeared])
+       
+        ## Smear theory data
+        if self.smearer is not None:
+            fx = self.smearer(fx, self._first_unsmeared_bin,
+                              self._last_unsmeared_bin)
+        ## Sanity check
+        if numpy.size(self.dy) != numpy.size(fx):
+            msg = "FitData1D: invalid error array "
+            msg += "%d <> %d" % (numpy.shape(self.dy), numpy.size(fx))
+            raise RuntimeError, msg
+        return (self.y[self.idx] - fx[self.idx]) / self.dy[self.idx], fx[self.idx]
+            
+    def residuals_deriv(self, model, pars=[]):
+        """
+            :return: residuals derivatives .
+            
+            :note: in this case just return empty array 
+        """
+        return []
+
+
+class FitData2D(Data2D):
+    """
+        Wrapper class  for SAS data
+    """
+    def __init__(self, sas_data2d, data=None, err_data=None):
+        Data2D.__init__(self, data=data, err_data=err_data)
+        # Data can be initialized with a sas plottable or with vectors.
+        self.res_err_image = []
+        self.num_points = 0 # will be set by set_data
+        self.idx = []
+        self.qmin = None
+        self.qmax = None
+        self.smearer = None
+        self.radius = 0
+        self.res_err_data = []
+        self.sas_data = sas_data2d
+        self.set_data(sas_data2d)
+
+    def set_data(self, sas_data2d, qmin=None, qmax=None):
+        """
+            Determine the correct qx_data and qy_data within range to fit
+        """
+        self.data = sas_data2d.data
+        self.err_data = sas_data2d.err_data
+        self.qx_data = sas_data2d.qx_data
+        self.qy_data = sas_data2d.qy_data
+        self.mask = sas_data2d.mask
+
+        x_max = max(math.fabs(sas_data2d.xmin), math.fabs(sas_data2d.xmax))
+        y_max = max(math.fabs(sas_data2d.ymin), math.fabs(sas_data2d.ymax))
+        
+        ## fitting range
+        if qmin == None:
+            self.qmin = 1e-16
+        if qmax == None:
+            self.qmax = math.sqrt(x_max * x_max + y_max * y_max)
+        ## new error image for fitting purpose
+        if self.err_data == None or self.err_data == []:
+            self.res_err_data = numpy.ones(len(self.data))
+        else:
+            self.res_err_data = copy.deepcopy(self.err_data)
+        #self.res_err_data[self.res_err_data==0]=1
+        
+        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
+        
+        # Note: mask = True: for MASK while mask = False for NOT to mask
+        self.idx = ((self.qmin <= self.radius) &\
+                            (self.radius <= self.qmax))
+        self.idx = (self.idx) & (self.mask)
+        self.idx = (self.idx) & (numpy.isfinite(self.data))
+        self.num_points = numpy.sum(self.idx)
+
+    def set_smearer(self, smearer):
+        """
+            Set smearer
+        """
+        if smearer == None:
+            return
+        self.smearer = smearer
+        self.smearer.set_index(self.idx)
+        self.smearer.get_data()
+
+    def set_fit_range(self, qmin=None, qmax=None):
+        """
+            To set the fit range
+        """
+        if qmin == 0.0:
+            self.qmin = 1e-16
+        elif qmin != None:
+            self.qmin = qmin
+        if qmax != None:
+            self.qmax = qmax
+        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
+        self.idx = ((self.qmin <= self.radius) &\
+                            (self.radius <= self.qmax))
+        self.idx = (self.idx) & (self.mask)
+        self.idx = (self.idx) & (numpy.isfinite(self.data))
+        self.idx = (self.idx) & (self.res_err_data != 0)
+
+    def get_fit_range(self):
+        """
+        return the range of data.x to fit
+        """
+        return self.qmin, self.qmax
+
+    def size(self):
+        """
+        Number of measurement points in data set after masking, etc.
+        """
+        return numpy.sum(self.idx)
+
+    def residuals(self, fn):
+        """
+        return the residuals
+        """
+        if self.smearer != None:
+            fn.set_index(self.idx)
+            # Get necessary data from self.data and set the data for smearing
+            fn.get_data()
+
+            gn = fn.get_value()
+        else:
+            gn = fn([self.qx_data[self.idx],
+                     self.qy_data[self.idx]])
+        # use only the data point within ROI range
+        res = (self.data[self.idx] - gn) / self.res_err_data[self.idx]
+
+        return res, gn
+        
+    def residuals_deriv(self, model, pars=[]):
+        """
+        :return: residuals derivatives .
+        
+        :note: in this case just return empty array
+        
+        """
+        return []
+    
+    
+class FitAbort(Exception):
+    """
+    Exception raise to stop the fit
+    """
+    #pass
+    #print"Creating fit abort Exception"
+
+
+
+class FitEngine:
+    def __init__(self):
+        """
+        Base class for the fit engine
+        """
+        #Dictionnary of fitArrange element (fit problems)
+        self.fit_arrange_dict = {}
+        self.fitter_id = None
+        
+    def set_model(self, model, id, pars=[], constraints=[], data=None):
+        """
+        set a model on a given  in the fit engine.
+        
+        :param model: sas.models type 
+        :param id: is the key of the fitArrange dictionary where model is saved as a value
+        :param pars: the list of parameters to fit 
+        :param constraints: list of 
+            tuple (name of parameter, value of parameters)
+            the value of parameter must be a string to constraint 2 different
+            parameters.
+            Example:  
+            we want to fit 2 model M1 and M2 both have parameters A and B.
+            constraints can be ``constraints = [(M1.A, M2.B+2), (M1.B= M2.A *5),...,]``
+            
+             
+        :note: pars must contains only name of existing model's parameters
+        
+        """
+        if not pars:
+            raise ValueError("no fitting parameters")
+
+        if model is None:
+            raise ValueError("no model to fit")
+
+        if not issubclass(model.__class__, Model):
+            model = Model(model, data)
+
+        sasmodel = model.model
+        available_parameters = sasmodel.getParamList()
+        for p in pars:
+            if p not in available_parameters:
+                raise ValueError("parameter %s not available in model %s; use one of [%s] instead"
+                                 %(p, sasmodel.name, ", ".join(available_parameters)))
+
+        if id not in self.fit_arrange_dict:
+            self.fit_arrange_dict[id] = FitArrange()
+
+        self.fit_arrange_dict[id].set_model(model)
+        self.fit_arrange_dict[id].pars = pars
+        self.fit_arrange_dict[id].vals = [sasmodel.getParam(name) for name in pars]
+        self.fit_arrange_dict[id].constraints = constraints
+
+    def set_data(self, data, id, smearer=None, qmin=None, qmax=None):
+        """
+        Receives plottable, creates a list of data to fit,set data
+        in a FitArrange object and adds that object in a dictionary
+        with key id.
+        
+        :param data: data added
+        :param id: unique key corresponding to a fitArrange object with data
+        """
+        if data.__class__.__name__ == 'Data2D':
+            fitdata = FitData2D(sas_data2d=data, data=data.data,
+                                 err_data=data.err_data)
+        else:
+            fitdata = FitData1D(x=data.x, y=data.y,
+                                 dx=data.dx, dy=data.dy, smearer=smearer)
+        fitdata.sas_data = data
+       
+        fitdata.set_fit_range(qmin=qmin, qmax=qmax)
+        #A fitArrange is already created but contains model only at id
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].add_data(fitdata)
+        else:
+        #no fitArrange object has been create with this id
+            fitproblem = FitArrange()
+            fitproblem.add_data(fitdata)
+            self.fit_arrange_dict[id] = fitproblem
+   
+    def get_model(self, id):
+        """
+        :param id: id is key in the dictionary containing the model to return
+        
+        :return:  a model at this id or None if no FitArrange element was
+            created with this id
+        """
+        if id in self.fit_arrange_dict:
+            return self.fit_arrange_dict[id].get_model()
+        else:
+            return None
+    
+    def remove_fit_problem(self, id):
+        """remove   fitarrange in id"""
+        if id in self.fit_arrange_dict:
+            del self.fit_arrange_dict[id]
+            
+    def select_problem_for_fit(self, id, value):
+        """
+        select a couple of model and data at the id position in dictionary
+        and set in self.selected value to value
+        
+        :param value: the value to allow fitting.
+                can only have the value one or zero
+        """
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].set_to_fit(value)
+             
+    def get_problem_to_fit(self, id):
+        """
+        return the self.selected value of the fit problem of id
+        
+        :param id: the id of the problem
+        """
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].get_to_fit()
+    
+    
+class FitArrange:
+    def __init__(self):
+        """
+        Class FitArrange contains a set of data for a given model
+        to perform the Fit.FitArrange must contain exactly one model
+        and at least one data for the fit to be performed.
+        
+        model: the model selected by the user
+        Ldata: a list of data what the user wants to fit
+            
+        """
+        self.model = None
+        self.data_list = []
+        self.pars = []
+        self.vals = []
+        self.selected = 0
+
+    def set_model(self, model):
+        """
+        set_model save a copy of the model
+        
+        :param model: the model being set
+        """
+        self.model = model
+        
+    def add_data(self, data):
+        """
+        add_data fill a self.data_list with data to fit
+        
+        :param data: Data to add in the list
+        """
+        if not data in self.data_list:
+            self.data_list.append(data)
+            
+    def get_model(self):
+        """
+        :return: saved model
+        """
+        return self.model
+     
+    def get_data(self):
+        """
+        :return: list of data data_list
+        """
+        return self.data_list[0]
+      
+    def remove_data(self, data):
+        """
+        Remove one element from the list
+        
+        :param data: Data to remove from data_list
+        """
+        if data in self.data_list:
+            self.data_list.remove(data)
+            
+    def set_to_fit(self, value=0):
+        """
+        set self.selected to 0 or 1  for other values raise an exception
+        
+        :param value: integer between 0 or 1
+        """
+        self.selected = value
+        
+    def get_to_fit(self):
+        """
+        return self.selected value
+        """
+        return self.selected
+
+class FResult(object):
+    """
+    Storing fit result
+    """
+    def __init__(self, model=None, param_list=None, data=None):
+        self.calls = None
+        self.fitness = None
+        self.chisqr = None
+        self.pvec = []
+        self.cov = []
+        self.info = None
+        self.mesg = None
+        self.success = None
+        self.stderr = None
+        self.residuals = []
+        self.index = []
+        self.model = model
+        self.data = data
+        self.theory = []
+        self.param_list = param_list
+        self.iterations = 0
+        self.inputs = []
+        self.fitter_id = None
+        if self.model is not None and self.data is not None:
+            self.inputs = [(self.model, self.data)]
+     
+    def set_model(self, model):
+        """
+        """
+        self.model = model
+        
+    def set_fitness(self, fitness):
+        """
+        """
+        self.fitness = fitness
+        
+    def __str__(self):
+        """
+        """
+        if self.pvec == None and self.model is None and self.param_list is None:
+            return "No results"
+
+        sasmodel = self.model.model
+        pars = enumerate(sasmodel.getParamList())
+        msg1 = "[Iteration #: %s ]" % self.iterations
+        msg3 = "=== goodness of fit: %s ===" % (str(self.fitness))
+        msg2 = ["P%-3d  %s......|.....%s" % (i, v, sasmodel.getParam(v))
+                for i,v in pars if v in self.param_list]
+        msg = [msg1, msg3] + msg2
+        return "\n".join(msg)
+    
+    def print_summary(self):
+        """
+        """
+        print str(self)
Index: src/sas/sascalc/fit/BumpsFitting.py
===================================================================
--- src/sas/sascalc/fit/BumpsFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/BumpsFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,365 @@
+"""
+BumpsFitting module runs the bumps optimizer.
+"""
+import os
+from datetime import timedelta, datetime
+
+import numpy
+
+from bumps import fitters
+try:
+    from bumps.options import FIT_CONFIG
+    # Default bumps to use the Levenberg-Marquardt optimizer
+    FIT_CONFIG.selected_id = fitters.LevenbergMarquardtFit.id
+    def get_fitter():
+        return FIT_CONFIG.selected_fitter, FIT_CONFIG.selected_values
+except:
+    # CRUFT: Bumps changed its handling of fit options around 0.7.5.6
+    # Default bumps to use the Levenberg-Marquardt optimizer
+    fitters.FIT_DEFAULT = 'lm'
+    def get_fitter():
+        fitopts = fitters.FIT_OPTIONS[fitters.FIT_DEFAULT]
+        return fitopts.fitclass, fitopts.options.copy()
+
+
+from bumps.mapper import SerialMapper, MPMapper
+from bumps import parameter
+from bumps.fitproblem import FitProblem
+
+
+from sas.sascalc.fit.AbstractFitEngine import FitEngine
+from sas.sascalc.fit.AbstractFitEngine import FResult
+from sas.sascalc.fit.expression import compile_constraints
+
+class Progress(object):
+    def __init__(self, history, max_step, pars, dof):
+        remaining_time = int(history.time[0]*(float(max_step)/history.step[0]-1))
+        # Depending on the time remaining, either display the expected
+        # time of completion, or the amount of time remaining.  Use precision
+        # appropriate for the duration.
+        if remaining_time >= 1800:
+            completion_time = datetime.now() + timedelta(seconds=remaining_time)
+            if remaining_time >= 36000:
+                time = completion_time.strftime('%Y-%m-%d %H:%M')
+            else:
+                time = completion_time.strftime('%H:%M')
+        else:
+            if remaining_time >= 3600:
+                time = '%dh %dm'%(remaining_time//3600, (remaining_time%3600)//60)
+            elif remaining_time >= 60:
+                time = '%dm %ds'%(remaining_time//60, remaining_time%60)
+            else:
+                time = '%ds'%remaining_time
+        chisq = "%.3g"%(2*history.value[0]/dof)
+        step = "%d of %d"%(history.step[0], max_step)
+        header = "=== Steps: %s  chisq: %s  ETA: %s\n"%(step, chisq, time)
+        parameters = ["%15s: %-10.3g%s"%(k,v,("\n" if i%3==2 else " | "))
+                      for i,(k,v) in enumerate(zip(pars,history.point[0]))]
+        self.msg = "".join([header]+parameters)
+
+    def __str__(self):
+        return self.msg
+
+
+class BumpsMonitor(object):
+    def __init__(self, handler, max_step, pars, dof):
+        self.handler = handler
+        self.max_step = max_step
+        self.pars = pars
+        self.dof = dof
+
+    def config_history(self, history):
+        history.requires(time=1, value=2, point=1, step=1)
+
+    def __call__(self, history):
+        if self.handler is None: return
+        self.handler.set_result(Progress(history, self.max_step, self.pars, self.dof))
+        self.handler.progress(history.step[0], self.max_step)
+        if len(history.step)>1 and history.step[1] > history.step[0]:
+            self.handler.improvement()
+        self.handler.update_fit()
+
+class ConvergenceMonitor(object):
+    """
+    ConvergenceMonitor contains population summary statistics to show progress
+    of the fit.  This is a list [ (best, 0%, 25%, 50%, 75%, 100%) ] or
+    just a list [ (best, ) ] if population size is 1.
+    """
+    def __init__(self):
+        self.convergence = []
+
+    def config_history(self, history):
+        history.requires(value=1, population_values=1)
+
+    def __call__(self, history):
+        best = history.value[0]
+        try:
+            p = history.population_values[0]
+            n,p = len(p), numpy.sort(p)
+            QI,Qmid, = int(0.2*n),int(0.5*n)
+            self.convergence.append((best, p[0],p[QI],p[Qmid],p[-1-QI],p[-1]))
+        except:
+            self.convergence.append((best, best,best,best,best,best))
+
+
+# Note: currently using bumps parameters for each parameter object so that
+# a SasFitness can be used directly in bumps with the usual semantics.
+# The disadvantage of this technique is that we need to copy every parameter
+# back into the model each time the function is evaluated.  We could instead
+# define reference parameters for each sas parameter, but then we would not
+# be able to express constraints using python expressions in the usual way
+# from bumps, and would instead need to use string expressions.
+class SasFitness(object):
+    """
+    Wrap SAS model as a bumps fitness object
+    """
+    def __init__(self, model, data, fitted=[], constraints={},
+                 initial_values=None, **kw):
+        self.name = model.name
+        self.model = model.model
+        self.data = data
+        if self.data.smearer is not None:
+            self.data.smearer.model = self.model
+        self._define_pars()
+        self._init_pars(kw)
+        if initial_values is not None:
+            self._reset_pars(fitted, initial_values)
+        self.constraints = dict(constraints)
+        self.set_fitted(fitted)
+        self.update()
+
+    def _reset_pars(self, names, values):
+        for k,v in zip(names, values):
+            self._pars[k].value = v
+
+    def _define_pars(self):
+        self._pars = {}
+        for k in self.model.getParamList():
+            name = ".".join((self.name,k))
+            value = self.model.getParam(k)
+            bounds = self.model.details.get(k,["",None,None])[1:3]
+            self._pars[k] = parameter.Parameter(value=value, bounds=bounds,
+                                                fixed=True, name=name)
+        #print parameter.summarize(self._pars.values())
+
+    def _init_pars(self, kw):
+        for k,v in kw.items():
+            # dispersion parameters initialized with _field instead of .field
+            if k.endswith('_width'): k = k[:-6]+'.width'
+            elif k.endswith('_npts'): k = k[:-5]+'.npts'
+            elif k.endswith('_nsigmas'): k = k[:-7]+'.nsigmas'
+            elif k.endswith('_type'): k = k[:-5]+'.type'
+            if k not in self._pars:
+                formatted_pars = ", ".join(sorted(self._pars.keys()))
+                raise KeyError("invalid parameter %r for %s--use one of: %s"
+                               %(k, self.model, formatted_pars))
+            if '.' in k and not k.endswith('.width'):
+                self.model.setParam(k, v)
+            elif isinstance(v, parameter.BaseParameter):
+                self._pars[k] = v
+            elif isinstance(v, (tuple,list)):
+                low, high = v
+                self._pars[k].value = (low+high)/2
+                self._pars[k].range(low,high)
+            else:
+                self._pars[k].value = v
+
+    def set_fitted(self, param_list):
+        """
+        Flag a set of parameters as fitted parameters.
+        """
+        for k,p in self._pars.items():
+            p.fixed = (k not in param_list or k in self.constraints)
+        self.fitted_par_names = [k for k in param_list if k not in self.constraints]
+        self.computed_par_names = [k for k in param_list if k in self.constraints]
+        self.fitted_pars = [self._pars[k] for k in self.fitted_par_names]
+        self.computed_pars = [self._pars[k] for k in self.computed_par_names]
+
+    # ===== Fitness interface ====
+    def parameters(self):
+        return self._pars
+
+    def update(self):
+        for k,v in self._pars.items():
+            #print "updating",k,v,v.value
+            self.model.setParam(k,v.value)
+        self._dirty = True
+
+    def _recalculate(self):
+        if self._dirty:
+            self._residuals, self._theory \
+                = self.data.residuals(self.model.evalDistribution)
+            self._dirty = False
+
+    def numpoints(self):
+        return numpy.sum(self.data.idx) # number of fitted points
+
+    def nllf(self):
+        return 0.5*numpy.sum(self.residuals()**2)
+
+    def theory(self):
+        self._recalculate()
+        return self._theory
+
+    def residuals(self):
+        self._recalculate()
+        return self._residuals
+
+    # Not implementing the data methods for now:
+    #
+    #     resynth_data/restore_data/save/plot
+
+class ParameterExpressions(object):
+    def __init__(self, models):
+        self.models = models
+        self._setup()
+
+    def _setup(self):
+        exprs = {}
+        for M in self.models:
+            exprs.update((".".join((M.name, k)), v) for k, v in M.constraints.items())
+        if exprs:
+            symtab = dict((".".join((M.name, k)), p)
+                          for M in self.models
+                          for k,p in M.parameters().items())
+            self.update = compile_constraints(symtab, exprs)
+        else:
+            self.update = lambda: 0
+
+    def __call__(self):
+        self.update()
+
+    def __getstate__(self):
+        return self.models
+
+    def __setstate__(self, state):
+        self.models = state
+        self._setup()
+
+class BumpsFit(FitEngine):
+    """
+    Fit a model using bumps.
+    """
+    def __init__(self):
+        """
+        Creates a dictionary (self.fit_arrange_dict={})of FitArrange elements
+        with Uid as keys
+        """
+        FitEngine.__init__(self)
+        self.curr_thread = None
+
+    def fit(self, msg_q=None,
+            q=None, handler=None, curr_thread=None,
+            ftol=1.49012e-8, reset_flag=False):
+        # Build collection of bumps fitness calculators
+        models = [SasFitness(model=M.get_model(),
+                             data=M.get_data(),
+                             constraints=M.constraints,
+                             fitted=M.pars,
+                             initial_values=M.vals if reset_flag else None)
+                  for M in self.fit_arrange_dict.values()
+                  if M.get_to_fit()]
+        if len(models) == 0:
+            raise RuntimeError("Nothing to fit")
+        problem = FitProblem(models)
+
+        # TODO: need better handling of parameter expressions and bounds constraints
+        # so that they are applied during polydispersity calculations.  This
+        # will remove the immediate need for the setp_hook in bumps, though
+        # bumps may still need something similar, such as a sane class structure
+        # which allows a subclass to override setp.
+        problem.setp_hook = ParameterExpressions(models)
+
+        # Run the fit
+        result = run_bumps(problem, handler, curr_thread)
+        if handler is not None:
+            handler.update_fit(last=True)
+
+        # TODO: shouldn't reference internal parameters of fit problem
+        varying = problem._parameters
+        # collect the results
+        all_results = []
+        for M in problem.models:
+            fitness = M.fitness
+            fitted_index = [varying.index(p) for p in fitness.fitted_pars]
+            param_list = fitness.fitted_par_names + fitness.computed_par_names
+            R = FResult(model=fitness.model, data=fitness.data,
+                        param_list=param_list)
+            R.theory = fitness.theory()
+            R.residuals = fitness.residuals()
+            R.index = fitness.data.idx
+            R.fitter_id = self.fitter_id
+            # TODO: should scale stderr by sqrt(chisq/DOF) if dy is unknown
+            R.success = result['success']
+            if R.success:
+                R.stderr = numpy.hstack((result['stderr'][fitted_index],
+                                         numpy.NaN*numpy.ones(len(fitness.computed_pars))))
+                R.pvec = numpy.hstack((result['value'][fitted_index],
+                                      [p.value for p in fitness.computed_pars]))
+                R.fitness = numpy.sum(R.residuals**2)/(fitness.numpoints() - len(fitted_index))
+            else:
+                R.stderr = numpy.NaN*numpy.ones(len(param_list))
+                R.pvec = numpy.asarray( [p.value for p in fitness.fitted_pars+fitness.computed_pars])
+                R.fitness = numpy.NaN
+            R.convergence = result['convergence']
+            if result['uncertainty'] is not None:
+                R.uncertainty_state = result['uncertainty']
+            all_results.append(R)
+
+        if q is not None:
+            q.put(all_results)
+            return q
+        else:
+            return all_results
+
+def run_bumps(problem, handler, curr_thread):
+    def abort_test():
+        if curr_thread is None: return False
+        try: curr_thread.isquit()
+        except KeyboardInterrupt:
+            if handler is not None:
+                handler.stop("Fitting: Terminated!!!")
+            return True
+        return False
+
+    fitclass, options = get_fitter()
+    steps = options.get('steps', 0)
+    if steps == 0:
+        pop = options.get('pop',0)*len(problem._parameters)
+        samples = options.get('samples', 0)
+        steps = (samples+pop-1)/pop if pop != 0 else samples
+    max_step = steps + options.get('burn', 0)
+    pars = [p.name for p in problem._parameters]
+    #x0 = numpy.asarray([p.value for p in problem._parameters])
+    options['monitors'] = [
+        BumpsMonitor(handler, max_step, pars, problem.dof),
+        ConvergenceMonitor(),
+        ]
+    fitdriver = fitters.FitDriver(fitclass, problem=problem,
+                                  abort_test=abort_test, **options)
+    omp_threads = int(os.environ.get('OMP_NUM_THREADS','0'))
+    mapper = MPMapper if omp_threads == 1 else SerialMapper
+    fitdriver.mapper = mapper.start_mapper(problem, None)
+    #import time; T0 = time.time()
+    try:
+        best, fbest = fitdriver.fit()
+    except:
+        import traceback; traceback.print_exc()
+        raise
+    finally:
+        mapper.stop_mapper(fitdriver.mapper)
+
+
+    convergence_list = options['monitors'][-1].convergence
+    convergence = (2*numpy.asarray(convergence_list)/problem.dof
+                   if convergence_list else numpy.empty((0,1),'d'))
+
+    success = best is not None
+    return {
+        'value': best if success else None,
+        'stderr': fitdriver.stderr() if success else None,
+        'success': success,
+        'convergence': convergence,
+        'uncertainty': getattr(fitdriver.fitter, 'state', None),
+        }
+
Index: src/sas/sascalc/fit/Loader.py
===================================================================
--- src/sas/sascalc/fit/Loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/Loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,86 @@
+# class Loader  to load any king of file
+#import wx
+#import string
+import numpy
+
+class Load:
+    """
+    This class is loading values from given file or value giving by the user
+    """
+    def __init__(self, x=None, y=None, dx=None, dy=None):
+        raise NotImplementedError("a code search shows that this code is not active, and you are not seeing this message")
+        # variable to store loaded values
+        self.x = x
+        self.y = y
+        self.dx = dx
+        self.dy = dy
+        self.filename = None
+        
+    def set_filename(self, path=None):
+        """
+        Store path into a variable.If the user doesn't give 
+        a path as a parameter a pop-up
+        window appears to select the file.
+        
+        :param path: the path given by the user
+        
+        """
+        self.filename = path
+       
+    def get_filename(self):
+        """ return the file's path"""
+        return self.filename
+    
+    def set_values(self):
+        """ Store the values loaded from file in local variables"""
+        if not self.filename == None:
+            input_f =  open(self.filename, 'r')
+            buff = input_f.read()
+            lines = buff.split('\n')
+            self.x = []
+            self.y = []
+            self.dx = [] 
+            self.dy = []
+            for line in lines:
+                try:
+                    toks = line.split()
+                    x = float(toks[0])
+                    y = float(toks[1])
+                    dy = float(toks[2])
+                    
+                    self.x.append(x)
+                    self.y.append(y)
+                    self.dy.append(dy)
+                    self.dx = numpy.zeros(len(self.x))
+                except:
+                    print "READ ERROR", line
+            # Sanity check
+            if not len(self.x) == len(self.dx):
+                raise ValueError, "x and dx have different length"
+            if not len(self.y) == len(self.dy):
+                raise ValueError, "y and dy have different length"
+            
+            
+    def get_values(self):
+        """ Return x, y, dx, dy"""
+        return self.x, self.y, self.dx, self.dy
+    
+    def load_data(self, data):
+        """ Return plottable"""
+        #load data
+        data.x = self.x
+        data.y = self.y
+        data.dx = self.dx
+        data.dy = self.dy
+        #Load its View class
+        #plottable.reset_view()
+       
+    
+if __name__ == "__main__": 
+    load = Load()
+    load.set_filename("testdata_line.txt")
+    print load.get_filename() 
+    load.set_values()
+    print load.get_values()
+    
+            
Index: src/sas/sascalc/fit/__init__.py
===================================================================
--- src/sas/sascalc/fit/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1 @@
+from .AbstractFitEngine import FitHandler
Index: src/sas/sascalc/fit/expression.py
===================================================================
--- src/sas/sascalc/fit/expression.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/expression.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,400 @@
+# This program is public domain
+"""
+Parameter expression evaluator.
+
+For systems in which constraints are expressed as string expressions rather
+than python code, :func:`compile_constraints` can construct an expression
+evaluator that substitutes the computed values of the expressions into the
+parameters.
+
+The compiler requires a symbol table, an expression set and a context.
+The symbol table maps strings containing fully qualified names such as
+'M1.c[3].full_width' to parameter objects with a 'value' property that
+can be queried and set.  The expression set maps symbol names from the
+symbol table to string expressions.  The context provides additional symbols
+for the expressions in addition to the usual mathematical functions and
+constants.
+
+The expressions are compiled and interpreted by python, with only minimal
+effort to make sure that they don't contain bad code.  The resulting
+constraints function returns 0 so it can be used directly in a fit problem
+definition.
+
+Extracting the symbol table from the model depends on the structure of the
+model.  If fitness.parameters() is set correctly, then this should simply
+be a matter of walking the parameter data, remembering the path to each
+parameter in the symbol table.  For compactness, dictionary elements should
+be referenced by .name rather than ["name"].  Model name can be used as the
+top level.
+
+Getting the parameter expressions applied correctly is challenging.
+The following monkey patch works by overriding model_update in FitProblem
+so that after setp(p) is called and, the constraints expression can be
+applied before telling the underlying fitness function that the model
+is out of date::
+
+        # Override model update so that parameter constraints are applied
+        problem._model_update = problem.model_update
+        def model_update():
+            constraints()
+            problem._model_update()
+        problem.model_update = model_update
+
+Ideally, this interface will change
+"""
+import math
+import re
+
+# simple pattern which matches symbols.  Note that it will also match
+# invalid substrings such as a3...9, but given syntactically correct
+# input it will only match symbols.
+_symbol_pattern = re.compile('([a-zA-Z_][a-zA-Z_0-9.]*)')
+
+def _symbols(expr,symtab):
+    """
+    Given an expression string and a symbol table, return the set of symbols
+    used in the expression.  Symbols are only returned once even if they
+    occur multiple times.  The return value is a set with the elements in
+    no particular order.
+    
+    This is the first step in computing a dependency graph.
+    """
+    matches = [m.group(0) for m in _symbol_pattern.finditer(expr)]
+    return set([symtab[m] for m in matches if m in symtab])
+
+def _substitute(expr,mapping):
+    """
+    Replace all occurrences of symbol s with mapping[s] for s in mapping.
+    """
+    # Find the symbols and the mapping
+    matches = [(m.start(),m.end(),mapping[m.group(1)])
+               for m in _symbol_pattern.finditer(expr)
+               if m.group(1) in mapping]
+
+    # Split the expression in to pieces, with new symbols replacing old
+    pieces = []
+    offset = 0
+    for start,end,text in matches:
+        pieces += [expr[offset:start],text]
+        offset = end
+    pieces.append(expr[offset:])
+    
+    # Join the pieces and return them
+    return "".join(pieces)
+
+def _find_dependencies(symtab, exprs):
+    """
+    Returns a list of pair-wise dependencies from the parameter expressions.
+    
+    For example, if p3 = p1+p2, then find_dependencies([p1,p2,p3]) will
+    return [(p3,p1),(p3,p2)].  For base expressions without dependencies,
+    such as p4 = 2*pi, this should return [(p4, None)]
+    """
+    deps = [(target,source)
+            for target,expr in exprs.items()
+            for source in _symbols_or_none(expr,symtab)]
+    return deps
+
+# Hack to deal with expressions without dependencies --- return a fake
+# dependency of None.
+# The better solution is fix order_dependencies so that it takes a
+# dictionary of {symbol: dependency_list}, for which no dependencies
+# is simply []; fix in parameter_mapping as well
+def _symbols_or_none(expr,symtab):
+    syms = _symbols(expr,symtab)
+    return syms if len(syms) else [None]
+
+def _parameter_mapping(pairs):
+    """
+    Find the parameter substitution we need so that expressions can
+    be evaluated without having to traverse a chain of 
+    model.layer.parameter.value
+    """
+    left,right = zip(*pairs)
+    pars = list(sorted(p for p in set(left+right) if p is not None))
+    definition = dict( ('P%d'%i,p)  for i,p in enumerate(pars) )
+    # p is None when there is an expression with no dependencies
+    substitution = dict( (p,'P%d.value'%i)
+                    for i,p in enumerate(sorted(pars))
+                    if p is not None)
+    return definition, substitution
+
+def no_constraints(): 
+    """
+    This parameter set has no constraints between the parameters.
+    """
+    pass
+
+def compile_constraints(symtab, exprs, context={}):
+    """
+    Build and return a function to evaluate all parameter expressions in
+    the proper order.
+
+    Input:
+
+        *symtab* is the symbol table for the model: { 'name': parameter }
+
+        *exprs* is the set of computed symbols: { 'name': 'expression' }
+
+        *context* is any additional context needed to evaluate the expression
+
+    Return:
+
+        updater function which sets parameter.value for each expression
+
+    Raises:
+
+       AssertionError - model, parameter or function is missing
+
+       SyntaxError - improper expression syntax
+
+       ValueError - expressions have circular dependencies
+
+    This function is not terribly sophisticated, and it would be easy to
+    trick.  However it handles the common cases cleanly and generates
+    reasonable messages for the common errors.
+
+    This code has not been fully audited for security.  While we have
+    removed the builtins and the ability to import modules, there may
+    be other vectors for users to perform more than simple function
+    evaluations.  Unauthenticated users should not be running this code.
+
+    Parameter names are assumed to contain only _.a-zA-Z0-9#[]
+    
+    Both names are provided for inverse functions, e.g., acos and arccos.
+
+    Should try running the function to identify syntax errors before
+    running it in a fit.
+    
+    Use help(fn) to see the code generated for the returned function fn.
+    dis.dis(fn) will show the corresponding python vm instructions.
+    """
+
+    # Sort the parameters in the order they need to be evaluated
+    deps = _find_dependencies(symtab, exprs)
+    if deps == []: return no_constraints
+    order = order_dependencies(deps)
+
+
+    # Rather than using the full path to the parameters in the parameter
+    # expressions, instead use Pn, and substitute Pn.value for each occurrence
+    # of the parameter in the expression.
+    names = list(sorted(symtab.keys()))
+    parameters = dict(('P%d'%i, symtab[k]) for i,k in enumerate(names))
+    mapping = dict((k, 'P%d.value'%i) for i,k in enumerate(names))
+
+
+    # Initialize dictionary with available functions
+    globals = {}
+    globals.update(math.__dict__)
+    globals.update(dict(arcsin=math.asin,arccos=math.acos,
+                        arctan=math.atan,arctan2=math.atan2))
+    globals.update(context)
+    globals.update(parameters)
+    globals['id'] = id
+    locals = {}
+
+    # Define the constraints function
+    assignments = ["=".join((p,exprs[p])) for p in order]
+    code = [_substitute(s, mapping) for s in assignments]
+    functiondef = """
+def eval_expressions():
+    '''
+    %s
+    '''
+    %s
+    return 0
+"""%("\n    ".join(assignments),"\n    ".join(code))
+
+    #print("Function: "+functiondef)
+    exec functiondef in globals,locals
+    retfn = locals['eval_expressions']
+
+    # Remove garbage added to globals by exec
+    globals.pop('__doc__',None)
+    globals.pop('__name__',None)
+    globals.pop('__file__',None)
+    globals.pop('__builtins__')
+    #print globals.keys()
+
+    return retfn
+
+def order_dependencies(pairs):
+    """
+    Order elements from pairs so that b comes before a in the
+    ordered list for all pairs (a,b).
+    """
+    #print "order_dependencies",pairs
+    emptyset = set()
+    order = []
+
+    # Break pairs into left set and right set
+    left,right = [set(s) for s in zip(*pairs)] if pairs != [] else ([],[])
+    while pairs != []:
+        #print "within",pairs
+        # Find which items only occur on the right
+        independent = right - left
+        if independent == emptyset:
+            cycleset = ", ".join(str(s) for s in left)
+            raise ValueError,"Cyclic dependencies amongst %s"%cycleset
+
+        # The possibly resolvable items are those that depend on the independents
+        dependent = set([a for a,b in pairs if b in independent])
+        pairs = [(a,b) for a,b in pairs if b not in independent]
+        if pairs == []:
+            resolved = dependent
+        else:
+            left,right = [set(s) for s in zip(*pairs)]
+            resolved = dependent - left
+        #print "independent",independent,"dependent",dependent,"resolvable",resolved
+        order += resolved
+        #print "new order",order
+    order.reverse()
+    return order
+
+# ========= Test code ========
+def _check(msg,pairs):
+    """
+    Verify that the list n contains the given items, and that the list
+    satisfies the partial ordering given by the pairs in partial order.
+    """
+    left,right = zip(*pairs) if pairs != [] else ([],[])
+    items = set(left)
+    n = order_dependencies(pairs)
+    if set(n) != items or len(n) != len(items):
+        n.sort()
+        items = list(items); items.sort()
+        raise Exception,"%s expect %s to contain %s for %s"%(msg,n,items,pairs)
+    for lo,hi in pairs:
+        if lo in n and hi in n and n.index(lo) >= n.index(hi):
+            raise Exception,"%s expect %s before %s in %s for %s"%(msg,lo,hi,n,pairs)
+
+def test_deps():
+    import numpy
+
+    # Null case
+    _check("test empty",[])
+
+    # Some dependencies
+    _check("test1",[(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)])
+    _check("test1 renumbered",[(6,1),(7,3),(7,4),(6,7),(5,7),(3,2)])
+    _check("test1 numpy",numpy.array([(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)]))
+
+    # No dependencies
+    _check("test2",[(4,1),(3,2),(8,4)])
+
+    # Cycle test
+    pairs = [(1,4),(4,3),(4,5),(5,1)]
+    try: n = order_dependencies(pairs)
+    except ValueError: pass
+    else: raise Exception,"test3 expect ValueError exception for %s"%(pairs,)
+
+    # large test for gross speed check
+    A = numpy.random.randint(4000,size=(1000,2))
+    A[:,1] += 4000  # Avoid cycles
+    _check("test-large",A)
+
+    # depth tests
+    k = 200
+    A = numpy.array([range(0,k),range(1,k+1)]).T
+    _check("depth-1",A)
+
+    A = numpy.array([range(1,k+1),range(0,k)]).T
+    _check("depth-2",A)
+
+def test_expr():
+    import inspect, dis
+    import math
+    
+    symtab = {'a.b.x':1, 'a.c':2, 'a.b':3, 'b.x':4}
+    expr = 'a.b.x + sin(4*pi*a.c) + a.b.x/a.b'
+    
+    # Check symbol lookup
+    assert _symbols(expr, symtab) == set([1,2,3])
+
+    # Check symbol rename
+    assert _substitute(expr,{'a.b.x':'Q'}) == 'Q + sin(4*pi*a.c) + Q/a.b'
+    assert _substitute(expr,{'a.b':'Q'}) == 'a.b.x + sin(4*pi*a.c) + a.b.x/Q'
+
+
+    # Check dependency builder
+    # Fake parameter class
+    class Parameter:
+        def __init__(self, name, value=0, expression=''):
+            self.path = name
+            self.value = value
+            self.expression = expression
+        def iscomputed(self): return (self.expression != '')
+        def __repr__(self): return self.path
+    def world(*pars):
+        symtab = dict((p.path,p) for p in pars)
+        exprs = dict((p.path,p.expression) for p in pars if p.iscomputed())
+        return symtab, exprs
+    p1 = Parameter('G0.sigma',5)
+    p2 = Parameter('other',expression='2*pi*sin(G0.sigma/.1875) + M1.G1')
+    p3 = Parameter('M1.G1',6)
+    p4 = Parameter('constant',expression='2*pi*35')
+    # Simple chain
+    assert set(_find_dependencies(*world(p1,p2,p3))) == set([(p2.path,p1),(p2.path,p3)])
+    # Constant expression
+    assert set(_find_dependencies(*world(p1,p4))) == set([(p4.path,None)])
+    # No dependencies
+    assert set(_find_dependencies(*world(p1,p3))) == set([])
+
+    # Check function builder
+    fn = compile_constraints(*world(p1,p2,p3))
+
+    # Inspect the resulting function
+    if 0:
+        print(inspect.getdoc(fn))
+        print(dis.dis(fn))
+
+    # Evaluate the function and see if it updates the
+    # target value as expected
+    fn()
+    expected = 2*math.pi*math.sin(5/.1875) + 6
+    assert p2.value == expected,"Value was %s, not %s"%(p2.value,expected)
+    
+    # Check empty dependency set doesn't crash
+    fn = compile_constraints(*world(p1,p3))
+    fn()
+
+    # Check that constants are evaluated properly
+    fn = compile_constraints(*world(p4))
+    fn()
+    assert p4.value == 2*math.pi*35
+
+    # Check additional context example; this also tests multiple
+    # expressions
+    class Table:
+        Si = 2.09
+        values = {'Si': 2.07}
+    tbl = Table()
+    p5 = Parameter('lookup',expression="tbl.Si")
+    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
+    fn()
+    assert p5.value == 2.09,"Value for %s was %s"%(p5.expression,p5.value)
+    p5.expression = "tbl.values['Si']"
+    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
+    fn()
+    assert p5.value == 2.07,"Value for %s was %s"%(p5.expression,p5.value)
+    
+
+    # Verify that we capture invalid expressions
+    for expr in ['G4.cage', 'M0.cage', 'M1.G1 + *2', 
+                 'piddle',
+                 '5; import sys; print "p0wned"',
+                 '__import__("sys").argv']:
+        try:
+            p6 = Parameter('broken',expression=expr)
+            fn = compile_constraints(*world(p6))
+            fn()
+        except Exception as msg:
+            #print(msg)
+            pass
+        else:
+            raise "Failed to raise error for %s"%expr
+
+if __name__ == "__main__":
+    test_expr()
+    test_deps()
