source: sasview/park_integration/ParkFitting.py @ 83ca047

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 83ca047 was 792db7d5, checked in by Gervaise Alina <gervyh@…>, 16 years ago

more tests added …most of them are failing because of uncertainty , scipy result and park resuls also little bit different

  • Property mode set to 100644
File size: 14.0 KB
Line 
1"""
2    @organization: ParkFitting module contains SansParameter,Model,Data
3    FitArrange, ParkFit,Parameter classes.All listed classes work together to perform a
4    simple fit with park optimizer.
5"""
6import time
7import numpy
8
9import park
10from park import fit,fitresult
11from park import assembly
12from park.fitmc import FitSimplex, FitMC
13
14from sans.guitools.plottables import Data1D
15from Loader import Load
16
17class SansParameter(park.Parameter):
18    """
19        SANS model parameters for use in the PARK fitting service.
20        The parameter attribute value is redirected to the underlying
21        parameter value in the SANS model.
22    """
23    def __init__(self, name, model):
24         self._model, self._name = model,name
25         self.set(model.getParam(name))
26         
27    def _getvalue(self): return self._model.getParam(self.name)
28   
29    def _setvalue(self,value): 
30        self._model.setParam(self.name, value)
31       
32    value = property(_getvalue,_setvalue)
33   
34    def _getrange(self):
35        lo,hi = self._model.details[self.name][1:]
36        if lo is None: lo = -numpy.inf
37        if hi is None: hi = numpy.inf
38        return lo,hi
39   
40    def _setrange(self,r):
41        self._model.details[self.name][1:] = r
42    range = property(_getrange,_setrange)
43
44
45class Model(object):
46    """
47        PARK wrapper for SANS models.
48    """
49    def __init__(self, sans_model):
50        self.model = sans_model
51        sansp = sans_model.getParamList()
52        parkp = [SansParameter(p,sans_model) for p in sansp]
53        self.parameterset = park.ParameterSet(sans_model.name,pars=parkp)
54       
55    def eval(self,x):
56        return self.model.run(x)
57   
58class Data(object):
59    """ Wrapper class  for SANS data """
60    def __init__(self,x=None,y=None,dy=None,dx=None,sans_data=None):
61        if not sans_data==None:
62            self.x= sans_data.x
63            self.y= sans_data.y
64            self.dx= sans_data.dx
65            self.dy= sans_data.dy
66        else:
67            if x!=None and y!=None and dy!=None:
68                self.x=x
69                self.y=y
70                self.dx=dx
71                self.dy=dy
72            else:
73                raise ValueError,\
74                "Data is missing x, y or dy, impossible to compute residuals later on"
75        self.qmin=None
76        self.qmax=None
77       
78    def setFitRange(self,mini=None,maxi=None):
79        """ to set the fit range"""
80        self.qmin=mini
81        self.qmax=maxi
82       
83    def residuals(self, fn):
84        """ @param fn: function that return model value
85            @return residuals
86        """
87        x,y,dy = [numpy.asarray(v) for v in (self.x,self.y,self.dy)]
88        if self.qmin==None and self.qmax==None: 
89            self.fx = fn(x)
90            return (y - fn(x))/dy
91       
92        else:
93            self.fx = fn(x[idx])
94            idx = x>=self.qmin & x <= self.qmax
95            return (y[idx] - fn(x[idx]))/dy[idx]
96           
97         
98    def residuals_deriv(self, model, pars=[]):
99        """
100            @return residuals derivatives .
101            @note: in this case just return empty array
102        """
103        return []
104class FitArrange:
105    def __init__(self):
106        """
107            Class FitArrange contains a set of data for a given model
108            to perform the Fit.FitArrange must contain exactly one model
109            and at least one data for the fit to be performed.
110            model: the model selected by the user
111            Ldata: a list of data what the user wants to fit
112           
113        """
114        self.model = None
115        self.dList =[]
116       
117    def set_model(self,model):
118        """
119            set_model save a copy of the model
120            @param model: the model being set
121        """
122        self.model = model
123    def remove_model(self):
124        """ remove model """
125        self.model=None
126    def add_data(self,data):
127        """
128            add_data fill a self.dList with data to fit
129            @param data: Data to add in the list 
130        """
131        if not data in self.dList:
132            self.dList.append(data)
133           
134    def get_model(self):
135        """ @return: saved model """
136        return self.model   
137     
138    def get_data(self):
139        """ @return:  list of data dList"""
140        return self.dList
141     
142    def remove_data(self,data):
143        """
144            Remove one element from the list
145            @param data: Data to remove from dList
146        """
147        if data in self.dList:
148            self.dList.remove(data)
149    def remove_datalist(self):
150        """ empty the complet list dLst"""
151        self.dList=[]
152           
153           
154class ParkFit:
155    """
156        ParkFit performs the Fit.This class can be used as follow:
157        #Do the fit Park
158        create an engine: engine = ParkFit()
159        Use data must be of type plottable
160        Use a sans model
161       
162        Add data with a dictionnary of FitArrangeList where Uid is a key and data
163        is saved in FitArrange object.
164        engine.set_data(data,Uid)
165       
166        Set model parameter "M1"= model.name add {model.parameter.name:value}.
167        @note: Set_param() if used must always preceded set_model()
168             for the fit to be performed.
169        engine.set_param( model,"M1", {'A':2,'B':4})
170       
171        Add model with a dictionnary of FitArrangeList{} where Uid is a key and model
172        is save in FitArrange object.
173        engine.set_model(model,Uid)
174       
175        engine.fit return chisqr,[model.parameter 1,2,..],[[err1....][..err2...]]
176        chisqr1, out1, cov1=engine.fit({model.parameter.name:value},qmin,qmax)
177        @note: {model.parameter.name:value} is ignored in fit function since
178        the user should make sure to call set_param himself.
179    """
180    def __init__(self,data=[]):
181        """
182            Creates a dictionary (self.fitArrangeList={})of FitArrange elements
183            with Uid as keys
184        """
185        self.fitArrangeList={}
186       
187    def createProblem(self,pars={}):
188        """
189        Extract sansmodel and sansdata from self.FitArrangelist ={Uid:FitArrange}
190        Create parkmodel and park data ,form a list couple of parkmodel and parkdata
191        create an assembly self.problem=  park.Assembly([(parkmodel,parkdata)])
192        """
193        mylist=[]
194        listmodel=[]
195        for k,value in self.fitArrangeList.iteritems():
196            sansmodel=value.get_model()
197            #wrap sans model
198            parkmodel = Model(sansmodel)
199            for p in parkmodel.parameterset:
200                if p.isfixed():
201                    p.set([-numpy.inf,numpy.inf])
202               
203            Ldata=value.get_data()
204            x,y,dy,dx=self._concatenateData(Ldata)
205            #wrap sansdata
206            parkdata=Data(x,y,dy,dx)
207            couple=(parkmodel,parkdata)
208            mylist.append(couple)
209       
210        self.problem =  park.Assembly(mylist)
211       
212   
213    def fit(self,pars=None, qmin=None, qmax=None):
214        """
215            Performs fit with park.fit module.It can  perform fit with one model
216            and a set of data, more than two fit of  one model and sets of data or
217            fit with more than two model associated with their set of data and constraints
218           
219           
220            @param pars: Dictionary of parameter names for the model and their values.
221            @param qmin: The minimum value of data's range to be fit
222            @param qmax: The maximum value of data's range to be fit
223            @note:all parameter are ignored most of the time.Are just there to keep ScipyFit
224            and ParkFit interface the same.
225            @return result.fitness: Value of the goodness of fit metric
226            @return result.pvec: list of parameter with the best value found during fitting
227            @return result.cov: Covariance matrix
228        """
229
230       
231        self.createProblem(pars)
232        pars=self.problem.fit_parameters()
233        self.problem.eval()
234   
235        localfit = FitSimplex()
236        localfit.ftol = 1e-8
237        fitter = FitMC(localfit=localfit)
238
239        result = fit.fit(self.problem,
240                         fitter=fitter,
241                         handler= fitresult.ConsoleUpdate(improvement_delta=0.1))
242       
243        return result.fitness,result.pvec,result.cov
244   
245    def set_model(self,model,Uid):
246        """
247            Set model in a FitArrange object and add that object in a dictionary
248            with key Uid.
249            @param model: the model added
250            @param Uid: unique key corresponding to a fitArrange object with model
251        """
252        #A fitArrange is already created but contains dList only at Uid
253        if self.fitArrangeList.has_key(Uid):
254            self.fitArrangeList[Uid].set_model(model)
255        else:
256        #no fitArrange object has been create with this Uid
257            fitproblem= FitArrange()
258            fitproblem.set_model(model)
259            self.fitArrangeList[Uid]=fitproblem
260       
261    def set_data(self,data,Uid):
262        """ Receives plottable, creates a list of data to fit,set data
263            in a FitArrange object and adds that object in a dictionary
264            with key Uid.
265            @param data: data added
266            @param Uid: unique key corresponding to a fitArrange object with data
267            """
268        #A fitArrange is already created but contains model only at Uid
269        if self.fitArrangeList.has_key(Uid):
270            self.fitArrangeList[Uid].add_data(data)
271        else:
272        #no fitArrange object has been create with this Uid
273            fitproblem= FitArrange()
274            fitproblem.add_data(data)
275            self.fitArrangeList[Uid]=fitproblem
276           
277    def get_model(self,Uid):
278        """
279            @param Uid: Uid is key in the dictionary containing the model to return
280            @return  a model at this uid or None if no FitArrange element was created
281            with this Uid
282        """
283        if self.fitArrangeList.has_key(Uid):
284            return self.fitArrangeList[Uid].get_model()
285        else:
286            return None
287   
288    def set_param(self,model,name, pars):
289        """
290            Recieve a dictionary of parameter and save it
291            @param model: model on with parameter values are set
292            @param name: model name
293            @param pars: dictionary of paramaters name and value
294            pars={parameter's name: parameter's value}
295            @return list of Parameter instance
296        """
297        parameters=[]
298        if model==None:
299            raise ValueError, "Cannot set parameters for empty model"
300        else:
301            model.name=name
302            for key, value in pars.iteritems():
303                param = Parameter(model, key, value)
304                parameters.append(param)
305        return parameters
306   
307    def remove_data(self,Uid,data=None):
308        """ remove one or all data.if data ==None will remove the whole
309            list of data at Uid; else will remove only data in that list.
310            @param Uid: unique id containing FitArrange object with data
311            @param data:data to be removed
312        """
313        if data==None:
314        # remove all element in data list
315            if self.fitArrangeList.has_key(Uid):
316                self.fitArrangeList[Uid].remove_datalist()
317        else:
318        #remove only data in dList
319            if self.fitArrangeList.has_key(Uid):
320                self.fitArrangeList[Uid].remove_data(data)
321               
322    def remove_model(self,Uid):
323        """
324            remove model in FitArrange object with Uid.
325            @param Uid: Unique id corresponding to the FitArrange object
326            where model must be removed.
327        """
328        if self.fitArrangeList.has_key(Uid):
329            self.fitArrangeList[Uid].remove_model()
330    def remove_Fit_Problem(self,Uid):
331        """remove   fitarrange in Uid"""
332        if self.fitArrangeList.has_key(Uid):
333            del self.fitArrangeList[Uid]
334    def _concatenateData(self, listdata=[]):
335        """ 
336            _concatenateData method concatenates each fields of all data contains ins listdata.
337            @param listdata: list of data
338           
339            @return xtemp, ytemp,dytemp:  x,y,dy respectively of data all combined
340                if xi,yi,dyi of two or more data are the same the second appearance of xi,yi,
341                dyi is ignored in the concatenation.
342               
343            @raise: if listdata is empty  will return None
344            @raise: if data in listdata don't contain dy field ,will create an error
345            during fitting
346        """
347        if listdata==[]:
348            raise ValueError, " data list missing"
349        else:
350            xtemp=[]
351            ytemp=[]
352            dytemp=[]
353            dx=None 
354            for data in listdata:
355                for i in range(len(data.x)):
356                    if not data.x[i] in xtemp:
357                        xtemp.append(data.x[i])
358                       
359                    if not data.y[i] in ytemp:
360                        ytemp.append(data.y[i])
361                    if data.dy and len(data.dy)>0:   
362                        if not data.dy[i] in dytemp:
363                            dytemp.append(data.dy[i])
364                    else:
365                        raise ValueError,"dy is missing will not be able to fit later on"
366            return xtemp, ytemp,dytemp,dx
367 
368           
369class Parameter:
370    """
371        Class to handle model parameters
372    """
373    def __init__(self, model, name, value=None):
374            self.model = model
375            self.name = name
376            if not value==None:
377                self.model.setParam(self.name, value)
378           
379    def set(self, value):
380        """
381            Set the value of the parameter
382        """
383        self.model.setParam(self.name, value)
384
385    def __call__(self):
386        """
387            Return the current value of the parameter
388        """
389        return self.model.getParam(self.name)
390   
391
392
393   
394   
395   
Note: See TracBrowser for help on using the repository browser.