source: sasview/src/sans/fit/AbstractFitEngine.py @ 5777106

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 5777106 was 5777106, checked in by Mathieu Doucet <doucetm@…>, 11 years ago

Moving things around. Will definitely not build.

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