source: sasview/park_integration/src/sans/fit/AbstractFitEngine.py @ fb732dc

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since fb732dc was 06e7c26, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed combined/batch

  • Property mode set to 100644
File size: 28.2 KB
Line 
1
2import  copy
3#import logging
4import sys
5import numpy
6import math
7import park
8from sans.dataloader.data_info import Data1D
9from sans.dataloader.data_info import Data2D
10import time
11_SMALLVALUE = 1.0e-10   
12   
13class SansParameter(park.Parameter):
14    """
15    SANS model parameters for use in the PARK fitting service.
16    The parameter attribute value is redirected to the underlying
17    parameter value in the SANS model.
18    """
19    def __init__(self, name, model, data):
20        """
21        :param name: the name of the model parameter
22        :param model: the sans model to wrap as a park model
23       
24        """
25        park.Parameter.__init__(self, name)
26        self._model, self._name = model, name
27        self.data = data
28        self.model = model
29        #set the value for the parameter of the given name
30        self.set(model.getParam(name))
31         
32    def _getvalue(self):
33        """
34        override the _getvalue of park parameter
35       
36        :return value the parameter associates with self.name
37       
38        """
39        return self._model.getParam(self.name)
40   
41    def _setvalue(self, value):
42        """
43        override the _setvalue pf park parameter
44       
45        :param value: the value to set on a given parameter
46       
47        """
48        self._model.setParam(self.name, value)
49       
50    value = property(_getvalue, _setvalue)
51   
52    def _getrange(self):
53        """
54        Override _getrange of park parameter
55        return the range of parameter
56        """
57        #if not  self.name in self._model.getDispParamList():
58        lo, hi = self._model.details[self.name][1:3]
59        if lo is None: lo = -numpy.inf
60        if hi is None: hi = numpy.inf
61        #else:
62            #lo,hi = self._model.details[self.name][1:]
63            #if lo is None: lo = -numpy.inf
64            #if hi is None: hi = numpy.inf
65        if lo > hi:
66            raise ValueError,"wrong fit range for parameters"
67       
68        return lo, hi
69   
70    def get_name(self):
71        """
72        """
73        return self._getname()
74   
75    def _setrange(self, r):
76        """
77        override _setrange of park parameter
78       
79        :param r: the value of the range to set
80       
81        """
82        self._model.details[self.name][1:3] = r
83    range = property(_getrange, _setrange)
84   
85class Model(park.Model):
86    """
87    PARK wrapper for SANS models.
88    """
89    def __init__(self, sans_model, sans_data=None, **kw):
90        """
91        :param sans_model: the sans model to wrap using park interface
92       
93        """
94        park.Model.__init__(self, **kw)
95        self.model = sans_model
96        self.name = sans_model.name
97        self.data = sans_data
98        #list of parameters names
99        self.sansp = sans_model.getParamList()
100        #list of park parameter
101        self.parkp = [SansParameter(p, sans_model, sans_data) for p in self.sansp]
102        #list of parameterset
103        self.parameterset = park.ParameterSet(sans_model.name, pars=self.parkp)
104        self.pars = []
105 
106    def get_params(self, fitparams):
107        """
108        return a list of value of paramter to fit
109       
110        :param fitparams: list of paramaters name to fit
111       
112        """
113        list_params = []
114        self.pars = []
115        self.pars = fitparams
116        for item in fitparams:
117            for element in self.parkp:
118                if element.name == str(item):
119                    list_params.append(element.value)
120        return list_params
121   
122    def set_params(self, paramlist, params):
123        """
124        Set value for parameters to fit
125       
126        :param params: list of value for parameters to fit
127       
128        """
129        try:
130            for i in range(len(self.parkp)):
131                for j in range(len(paramlist)):
132                    if self.parkp[i].name == paramlist[j]:
133                        self.parkp[i].value = params[j]
134                        self.model.setParam(self.parkp[i].name, params[j])
135        except:
136            raise
137 
138    def eval(self, x):
139        """
140        override eval method of park model.
141       
142        :param x: the x value used to compute a function
143       
144        """
145        try:
146            return self.model.evalDistribution(x)
147        except:
148            raise
149       
150    def eval_derivs(self, x, pars=[]):
151        """
152        Evaluate the model and derivatives wrt pars at x.
153
154        pars is a list of the names of the parameters for which derivatives
155        are desired.
156
157        This method needs to be specialized in the model to evaluate the
158        model function.  Alternatively, the model can implement is own
159        version of residuals which calculates the residuals directly
160        instead of calling eval.
161        """
162        return []
163
164
165   
166class FitData1D(Data1D):
167    """
168    Wrapper class  for SANS data
169    FitData1D inherits from DataLoader.data_info.Data1D. Implements
170    a way to get residuals from data.
171    """
172    def __init__(self, x, y, dx=None, dy=None, smearer=None, data=None):
173        """
174        :param smearer: is an object of class QSmearer or SlitSmearer
175           that will smear the theory data (slit smearing or resolution
176           smearing) when set.
177       
178        The proper way to set the smearing object would be to
179        do the following: ::
180       
181            from DataLoader.qsmearing import smear_selection
182            smearer = smear_selection(some_data)
183            fitdata1d = FitData1D( x= [1,3,..,],
184                                    y= [3,4,..,8],
185                                    dx=None,
186                                    dy=[1,2...], smearer= smearer)
187       
188        :Note: that some_data _HAS_ to be of class DataLoader.data_info.Data1D
189            Setting it back to None will turn smearing off.
190           
191        """
192        Data1D.__init__(self, x=x, y=y, dx=dx, dy=dy)
193        self.sans_data = data
194        self.smearer = smearer
195        self._first_unsmeared_bin = None
196        self._last_unsmeared_bin = None
197        # Check error bar; if no error bar found, set it constant(=1)
198        # TODO: Should provide an option for users to set it like percent,
199        # constant, or dy data
200        if dy == None or dy == [] or dy.all() == 0:
201            self.dy = numpy.ones(len(y)) 
202        else:
203            self.dy = numpy.asarray(dy).copy()
204
205        ## Min Q-value
206        #Skip the Q=0 point, especially when y(q=0)=None at x[0].
207        if min (self.x) == 0.0 and self.x[0] == 0 and\
208                     not numpy.isfinite(self.y[0]):
209            self.qmin = min(self.x[self.x!=0])
210        else:                             
211            self.qmin = min(self.x)
212        ## Max Q-value
213        self.qmax = max(self.x)
214       
215        # Range used for input to smearing
216        self._qmin_unsmeared = self.qmin
217        self._qmax_unsmeared = self.qmax
218        # Identify the bin range for the unsmeared and smeared spaces
219        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
220        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
221                            & (self.x <= self._qmax_unsmeared)
222 
223    def set_fit_range(self, qmin=None, qmax=None):
224        """ to set the fit range"""
225        # Skip Q=0 point, (especially for y(q=0)=None at x[0]).
226        # ToDo: Find better way to do it.
227        if qmin == 0.0 and not numpy.isfinite(self.y[qmin]):
228            self.qmin = min(self.x[self.x != 0])
229        elif qmin != None:                       
230            self.qmin = qmin           
231        if qmax != None:
232            self.qmax = qmax
233        # Determine the range needed in unsmeared-Q to cover
234        # the smeared Q range
235        self._qmin_unsmeared = self.qmin
236        self._qmax_unsmeared = self.qmax   
237       
238        self._first_unsmeared_bin = 0
239        self._last_unsmeared_bin  = len(self.x) - 1
240       
241        if self.smearer != None:
242            self._first_unsmeared_bin, self._last_unsmeared_bin = \
243                    self.smearer.get_bin_range(self.qmin, self.qmax)
244            self._qmin_unsmeared = self.x[self._first_unsmeared_bin]
245            self._qmax_unsmeared = self.x[self._last_unsmeared_bin]
246           
247        # Identify the bin range for the unsmeared and smeared spaces
248        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
249        ## zero error can not participate for fitting
250        self.idx = self.idx & (self.dy != 0) 
251        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
252                            & (self.x <= self._qmax_unsmeared)
253
254    def get_fit_range(self):
255        """
256        return the range of data.x to fit
257        """
258        return self.qmin, self.qmax
259       
260    def residuals(self, fn):
261        """
262        Compute residuals.
263       
264        If self.smearer has been set, use if to smear
265        the data before computing chi squared.
266       
267        :param fn: function that return model value
268       
269        :return: residuals
270       
271        """
272        # Compute theory data f(x)
273        fx = numpy.zeros(len(self.x))
274        fx[self.idx_unsmeared] = fn(self.x[self.idx_unsmeared])
275       
276        ## Smear theory data
277        if self.smearer is not None:
278            fx = self.smearer(fx, self._first_unsmeared_bin, 
279                              self._last_unsmeared_bin)
280        ## Sanity check
281        if numpy.size(self.dy) != numpy.size(fx):
282            msg = "FitData1D: invalid error array "
283            msg += "%d <> %d" % (numpy.shape(self.dy), numpy.size(fx))                                                     
284            raise RuntimeError, msg 
285        return (self.y[self.idx] - fx[self.idx]) / self.dy[self.idx], fx[self.idx]
286           
287     
288    def residuals_deriv(self, model, pars=[]):
289        """
290        :return: residuals derivatives .
291       
292        :note: in this case just return empty array
293       
294        """
295        return []
296   
297class FitData2D(Data2D):
298    """ Wrapper class  for SANS data """
299    def __init__(self, sans_data2d, data=None, err_data=None):
300        Data2D.__init__(self, data=data, err_data=err_data)
301        """
302        Data can be initital with a data (sans plottable)
303        or with vectors.
304        """
305        self.res_err_image = []
306        self.idx = []
307        self.qmin = None
308        self.qmax = None
309        self.smearer = None
310        self.radius = 0
311        self.res_err_data = []
312        self.sans_data = sans_data2d
313        self.set_data(sans_data2d)
314
315    def set_data(self, sans_data2d, qmin=None, qmax=None):
316        """
317        Determine the correct qx_data and qy_data within range to fit
318        """
319        self.data = sans_data2d.data
320        self.err_data = sans_data2d.err_data
321        self.qx_data = sans_data2d.qx_data
322        self.qy_data = sans_data2d.qy_data
323        self.mask = sans_data2d.mask
324
325        x_max = max(math.fabs(sans_data2d.xmin), math.fabs(sans_data2d.xmax))
326        y_max = max(math.fabs(sans_data2d.ymin), math.fabs(sans_data2d.ymax))
327       
328        ## fitting range
329        if qmin == None:
330            self.qmin = 1e-16
331        if qmax == None:
332            self.qmax = math.sqrt(x_max * x_max + y_max * y_max)
333        ## new error image for fitting purpose
334        if self.err_data == None or self.err_data == []:
335            self.res_err_data = numpy.ones(len(self.data))
336        else:
337            self.res_err_data = copy.deepcopy(self.err_data)
338        #self.res_err_data[self.res_err_data==0]=1
339       
340        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
341       
342        # Note: mask = True: for MASK while mask = False for NOT to mask
343        self.idx = ((self.qmin <= self.radius)&\
344                            (self.radius <= self.qmax))
345        self.idx = (self.idx) & (self.mask)
346        self.idx = (self.idx) & (numpy.isfinite(self.data))
347
348    def set_smearer(self, smearer): 
349        """
350        Set smearer
351        """
352        if smearer == None:
353            return
354        self.smearer = smearer
355        self.smearer.set_index(self.idx)
356        self.smearer.get_data()
357
358    def set_fit_range(self, qmin=None, qmax=None):
359        """ to set the fit range"""
360        if qmin == 0.0:
361            self.qmin = 1e-16
362        elif qmin != None:                       
363            self.qmin = qmin           
364        if qmax != None:
365            self.qmax = qmax       
366        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
367        self.idx = ((self.qmin <= self.radius)&\
368                            (self.radius <= self.qmax))
369        self.idx = (self.idx) &(self.mask)
370        self.idx = (self.idx) & (numpy.isfinite(self.data))
371        self.idx = (self.idx) & (self.res_err_data != 0)
372
373    def get_fit_range(self):
374        """
375        return the range of data.x to fit
376        """
377        return self.qmin, self.qmax
378     
379    def residuals(self, fn): 
380        """
381        return the residuals
382        """ 
383        if self.smearer != None:
384            fn.set_index(self.idx)
385            # Get necessary data from self.data and set the data for smearing
386            fn.get_data()
387
388            gn = fn.get_value() 
389        else:
390            gn = fn([self.qx_data[self.idx],
391                     self.qy_data[self.idx]])
392        # use only the data point within ROI range
393        res = (self.data[self.idx] - gn)/self.res_err_data[self.idx]
394
395        return res, gn
396       
397    def residuals_deriv(self, model, pars=[]):
398        """
399        :return: residuals derivatives .
400       
401        :note: in this case just return empty array
402       
403        """
404        return []
405   
406class FitAbort(Exception):
407    """
408    Exception raise to stop the fit
409    """
410    #pass
411    #print"Creating fit abort Exception"
412
413
414class SansAssembly:
415    """
416    Sans Assembly class a class wrapper to be call in optimizer.leastsq method
417    """
418    def __init__(self, paramlist, model=None , data=None, fitresult=None,
419                 handler=None, curr_thread=None, msg_q=None):
420        """
421        :param Model: the model wrapper fro sans -model
422        :param Data: the data wrapper for sans data
423       
424        """
425        self.model = model
426        self.data  = data
427        self.paramlist = paramlist
428        self.msg_q = msg_q
429        self.curr_thread = curr_thread
430        self.handler = handler
431        self.fitresult = fitresult
432        self.res = []
433        self.true_res = []
434        self.func_name = "Functor"
435        self.theory = None
436       
437    #def chisq(self, params):
438    def chisq(self):
439        """
440        Calculates chi^2
441       
442        :param params: list of parameter values
443       
444        :return: chi^2
445       
446        """
447        sum = 0
448        for item in self.true_res:
449            sum += item * item
450        if len(self.true_res) == 0:
451            return None
452        return sum / len(self.true_res)
453   
454    def __call__(self, params):
455        """
456        Compute residuals
457       
458        :param params: value of parameters to fit
459       
460        """ 
461        #import thread
462        self.model.set_params(self.paramlist, params)
463        #print "params", params
464        self.true_res, theory = self.data.residuals(self.model.eval)
465        self.theory = copy.deepcopy(theory)
466        # check parameters range
467        if self.check_param_range():
468            # if the param value is outside of the bound
469            # just silent return res = inf
470            return self.res
471        self.res = self.true_res       
472       
473        if self.fitresult is not None:
474            self.fitresult.set_model(model=self.model)
475            self.fitresult.residuals = self.true_res
476            self.fitresult.iterations += 1
477            self.fitresult.theory = theory
478           
479            #fitness = self.chisq(params=params)
480            fitness = self.chisq()
481            self.fitresult.pvec = params
482            self.fitresult.set_fitness(fitness=fitness)
483            if self.msg_q is not None:
484                self.msg_q.put(self.fitresult)
485               
486            if self.handler is not None:
487                self.handler.set_result(result=self.fitresult)
488                self.handler.update_fit()
489
490            if self.curr_thread != None :
491                try:
492                    self.curr_thread.isquit()
493                except:
494                    msg = "Fitting: Terminated...       Note: Forcing to stop " 
495                    msg += "fitting may cause a 'Functor error message' "
496                    msg += "being recorded in the log file....."
497                    self.handler.error(msg)
498                    raise
499                    #return
500         
501        return self.res
502   
503    def check_param_range(self):
504        """
505        Check the lower and upper bound of the parameter value
506        and set res to the inf if the value is outside of the
507        range
508        :limitation: the initial values must be within range.
509        """
510
511        #time.sleep(0.01)
512        is_outofbound = False
513        # loop through the fit parameters
514        for p in self.model.parameterset:
515            param_name = p.get_name()
516            if param_name in self.paramlist:
517               
518                # if the range was defined, check the range
519                if numpy.isfinite(p.range[0]):
520                    if p.value == 0:
521                        # This value works on Scipy
522                        # Do not change numbers below
523                        value = _SMALLVALUE
524                    else:
525                        value = p.value
526                    # For leastsq, it needs a bit step back from the boundary
527                    val = p.range[0] - value * _SMALLVALUE
528                    if p.value < val: 
529                        self.res *= 1e+6
530                       
531                        is_outofbound = True
532                        break
533                if numpy.isfinite(p.range[1]):
534                    # This value works on Scipy
535                    # Do not change numbers below
536                    if p.value == 0:
537                        value = _SMALLVALUE
538                    else:
539                        value = p.value
540                    # For leastsq, it needs a bit step back from the boundary
541                    val = p.range[1] + value * _SMALLVALUE
542                    if p.value > val:
543                        self.res *= 1e+6
544                        is_outofbound = True
545                        break
546
547        return is_outofbound
548   
549   
550class FitEngine:
551    def __init__(self):
552        """
553        Base class for scipy and park fit engine
554        """
555        #List of parameter names to fit
556        self.param_list = []
557        #Dictionnary of fitArrange element (fit problems)
558        self.fit_arrange_dict = {}
559        self.fitter_id = None
560       
561    def set_model(self, model,  id,  pars=[], constraints=[], data=None):
562        """
563        set a model on a given  in the fit engine.
564       
565        :param model: sans.models type
566        :param : is the key of the fitArrange dictionary where model is
567                saved as a value
568        :param pars: the list of parameters to fit
569        :param constraints: list of
570            tuple (name of parameter, value of parameters)
571            the value of parameter must be a string to constraint 2 different
572            parameters.
573            Example: 
574            we want to fit 2 model M1 and M2 both have parameters A and B.
575            constraints can be:
576             constraints = [(M1.A, M2.B+2), (M1.B= M2.A *5),...,]
577           
578             
579        :note: pars must contains only name of existing model's parameters
580       
581        """
582        if model == None:
583            raise ValueError, "AbstractFitEngine: Need to set model to fit"
584       
585        new_model = model
586        if not issubclass(model.__class__, Model):
587            new_model = Model(model, data)
588       
589        if len(constraints) > 0:
590            for constraint in constraints:
591                name, value = constraint
592                try:
593                    new_model.parameterset[str(name)].set(str(value))
594                except:
595                    msg = "Fit Engine: Error occurs when setting the constraint"
596                    msg += " %s for parameter %s " % (value, name)
597                    raise ValueError, msg
598               
599        if len(pars) > 0:
600            temp = []
601            for item in pars:
602                if item in new_model.model.getParamList():
603                    temp.append(item)
604                    self.param_list.append(item)
605                else:
606                   
607                    msg = "wrong parameter %s used" % str(item)
608                    msg += "to set model %s. Choose" % str(new_model.model.name)
609                    msg += "parameter name within %s" % \
610                                str(new_model.model.getParamList())
611                    raise ValueError, msg
612             
613            #A fitArrange is already created but contains data_list only at id
614            if self.fit_arrange_dict.has_key(id):
615                self.fit_arrange_dict[id].set_model(new_model)
616                self.fit_arrange_dict[id].pars = pars
617            else:
618            #no fitArrange object has been create with this id
619                fitproblem = FitArrange()
620                fitproblem.set_model(new_model)
621                fitproblem.pars = pars
622                self.fit_arrange_dict[id] = fitproblem
623                vals = []
624                for name in pars:
625                    vals.append(new_model.model.getParam(name))
626                self.fit_arrange_dict[id].vals = vals
627        else:
628            raise ValueError, "park_integration:missing parameters"
629   
630    def set_data(self, data, id, smearer=None, qmin=None, qmax=None):
631        """
632        Receives plottable, creates a list of data to fit,set data
633        in a FitArrange object and adds that object in a dictionary
634        with key id.
635       
636        :param data: data added
637        :param id: unique key corresponding to a fitArrange object with data
638       
639        """
640        if data.__class__.__name__ == 'Data2D':
641            fitdata = FitData2D(sans_data2d=data, data=data.data,
642                                 err_data=data.err_data)
643        else:
644            fitdata = FitData1D(x=data.x, y=data.y ,
645                                 dx=data.dx, dy=data.dy, smearer=smearer)
646        fitdata.sans_data = data
647       
648        fitdata.set_fit_range(qmin=qmin, qmax=qmax)
649        #A fitArrange is already created but contains model only at id
650        if self.fit_arrange_dict.has_key(id):
651            self.fit_arrange_dict[id].add_data(fitdata)
652        else:
653        #no fitArrange object has been create with this id
654            fitproblem = FitArrange()
655            fitproblem.add_data(fitdata)
656            self.fit_arrange_dict[id] = fitproblem   
657   
658    def get_model(self, id):
659        """
660       
661        :param id: id is key in the dictionary containing the model to return
662       
663        :return:  a model at this id or None if no FitArrange element was
664            created with this id
665           
666        """
667        if self.fit_arrange_dict.has_key(id):
668            return self.fit_arrange_dict[id].get_model()
669        else:
670            return None
671   
672    def remove_fit_problem(self, id):
673        """remove   fitarrange in id"""
674        if self.fit_arrange_dict.has_key(id):
675            del self.fit_arrange_dict[id]
676           
677    def select_problem_for_fit(self, id, value):
678        """
679        select a couple of model and data at the id position in dictionary
680        and set in self.selected value to value
681       
682        :param value: the value to allow fitting.
683                can only have the value one or zero
684               
685        """
686        if self.fit_arrange_dict.has_key(id):
687            self.fit_arrange_dict[id].set_to_fit(value)
688             
689    def get_problem_to_fit(self, id):
690        """
691        return the self.selected value of the fit problem of id
692       
693        :param id: the id of the problem
694       
695        """
696        if self.fit_arrange_dict.has_key(id):
697            self.fit_arrange_dict[id].get_to_fit()
698   
699class FitArrange:
700    def __init__(self):
701        """
702        Class FitArrange contains a set of data for a given model
703        to perform the Fit.FitArrange must contain exactly one model
704        and at least one data for the fit to be performed.
705       
706        model: the model selected by the user
707        Ldata: a list of data what the user wants to fit
708           
709        """
710        self.model = None
711        self.data_list = []
712        self.pars = []
713        self.vals = []
714        #self.selected  is zero when this fit problem is not schedule to fit
715        #self.selected is 1 when schedule to fit
716        self.selected = 0
717       
718    def set_model(self, model):
719        """
720        set_model save a copy of the model
721       
722        :param model: the model being set
723       
724        """
725        self.model = model
726       
727    def add_data(self, data):
728        """
729        add_data fill a self.data_list with data to fit
730       
731        :param data: Data to add in the list 
732       
733        """
734        if not data in self.data_list:
735            self.data_list.append(data)
736           
737    def get_model(self):
738        """
739       
740        :return: saved model
741       
742        """
743        return self.model   
744     
745    def get_data(self):
746        """
747       
748        :return: list of data data_list
749       
750        """
751        #return self.data_list
752        return self.data_list[0] 
753     
754    def remove_data(self, data):
755        """
756        Remove one element from the list
757       
758        :param data: Data to remove from data_list
759       
760        """
761        if data in self.data_list:
762            self.data_list.remove(data)
763           
764    def set_to_fit (self, value=0):
765        """
766        set self.selected to 0 or 1  for other values raise an exception
767       
768        :param value: integer between 0 or 1
769       
770        """
771        self.selected = value
772       
773    def get_to_fit(self):
774        """
775        return self.selected value
776        """
777        return self.selected
778   
779   
780IS_MAC = True
781if sys.platform.count("win32") > 0:
782    IS_MAC = False
783   
784class FResult(object):
785    """
786    Storing fit result
787    """
788    def __init__(self, model=None, param_list=None, data=None):
789        self.calls = None
790        self.pars = []
791        self.fitness = None
792        self.chisqr = None
793        self.pvec = []
794        self.cov = []
795        self.info = None
796        self.mesg = None
797        self.success = None
798        self.stderr = None
799        self.residuals = []
800        self.index = []
801        self.parameters = None
802        self.is_mac = IS_MAC
803        self.model = model
804        self.data = data
805        self.theory = []
806        self.param_list = param_list
807        self.iterations = 0
808        self.inputs = []
809        self.fitter_id = None
810        if self.model is not None and self.data is not None:
811            self.inputs = [(self.model, self.data)]
812     
813    def set_model(self, model):
814        """
815        """
816        self.model = model
817       
818    def set_fitness(self, fitness):
819        """
820        """
821        self.fitness = fitness
822       
823    def __str__(self):
824        """
825        """
826        if self.pvec == None and self.model is None and self.param_list is None:
827            return "No results"
828        n = len(self.model.parameterset)
829       
830        result_param = zip(xrange(n), self.model.parameterset)
831        msg1 = ["[Iteration #: %s ]" % self.iterations]
832        msg3 = ["=== goodness of fit: %s ===" % (str(self.fitness))]
833        if not self.is_mac:
834            msg2 = ["P%-3d  %s......|.....%s" % \
835                (p[0], p[1], p[1].value)\
836                  for p in result_param if p[1].name in self.param_list]
837            msg =  msg1 + msg3 + msg2
838        else:
839            msg = msg1 + msg3
840        msg = "\n".join(msg)
841        return msg
842   
843    def print_summary(self):
844        """
845        """
846        print self 
Note: See TracBrowser for help on using the repository browser.