source: sasview/park_integration/ParkFitting.py @ 4dd63eb

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 4dd63eb was 4dd63eb, checked in by Gervaise Alina <gervyh@…>, 16 years ago

change on set_model

  • Property mode set to 100644
File size: 13.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):
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, 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()
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        print "result",result
243        return result.fitness,result.pvec,result.cov
244   
245    def set_model(self,model,name,Uid,pars={}):
246        """
247     
248            Receive a dictionary of parameter and save it Parameter list
249            For scipy.fit use.
250            Set model in a FitArrange object and add that object in a dictionary
251            with key Uid.
252            @param model: model on with parameter values are set
253            @param name: model name
254            @param Uid: unique key corresponding to a fitArrange object with model
255            @param pars: dictionary of paramaters name and value
256            pars={parameter's name: parameter's value}
257           
258        """
259        self.parameters=[]
260        if model==None:
261            raise ValueError, "Cannot set parameters for empty model"
262        else:
263            model.name=name
264            for key, value in pars.iteritems():
265                param = Parameter(model, key, value)
266                self.parameters.append(param)
267       
268        #A fitArrange is already created but contains dList only at Uid
269        if self.fitArrangeList.has_key(Uid):
270            self.fitArrangeList[Uid].set_model(model)
271        else:
272        #no fitArrange object has been create with this Uid
273            fitproblem= FitArrange()
274            fitproblem.set_model(model)
275            self.fitArrangeList[Uid]=fitproblem
276       
277       
278    def set_data(self,data,Uid):
279        """ Receives plottable, creates a list of data to fit,set data
280            in a FitArrange object and adds that object in a dictionary
281            with key Uid.
282            @param data: data added
283            @param Uid: unique key corresponding to a fitArrange object with data
284            """
285        #A fitArrange is already created but contains model only at Uid
286        if self.fitArrangeList.has_key(Uid):
287            self.fitArrangeList[Uid].add_data(data)
288        else:
289        #no fitArrange object has been create with this Uid
290            fitproblem= FitArrange()
291            fitproblem.add_data(data)
292            self.fitArrangeList[Uid]=fitproblem
293           
294    def get_model(self,Uid):
295        """
296            @param Uid: Uid is key in the dictionary containing the model to return
297            @return  a model at this uid or None if no FitArrange element was created
298            with this Uid
299        """
300        if self.fitArrangeList.has_key(Uid):
301            return self.fitArrangeList[Uid].get_model()
302        else:
303            return None
304   
305    def remove_Fit_Problem(self,Uid):
306        """remove   fitarrange in Uid"""
307        if self.fitArrangeList.has_key(Uid):
308            del self.fitArrangeList[Uid]
309           
310    def _concatenateData(self, listdata=[]):
311        """ 
312            _concatenateData method concatenates each fields of all data contains ins listdata.
313            @param listdata: list of data
314           
315            @return xtemp, ytemp,dytemp:  x,y,dy respectively of data all combined
316                if xi,yi,dyi of two or more data are the same the second appearance of xi,yi,
317                dyi is ignored in the concatenation.
318               
319            @raise: if listdata is empty  will return None
320            @raise: if data in listdata don't contain dy field ,will create an error
321            during fitting
322        """
323        if listdata==[]:
324            raise ValueError, " data list missing"
325        else:
326            xtemp=[]
327            ytemp=[]
328            dytemp=[]
329            dx=None 
330            for data in listdata:
331                for i in range(len(data.x)):
332                    if not data.x[i] in xtemp:
333                        xtemp.append(data.x[i])
334                       
335                    if not data.y[i] in ytemp:
336                        ytemp.append(data.y[i])
337                    if data.dy and len(data.dy)>0:   
338                        if not data.dy[i] in dytemp:
339                            dytemp.append(data.dy[i])
340                    else:
341                        raise ValueError,"dy is missing will not be able to fit later on"
342            return xtemp, ytemp,dytemp,dx
343 
344           
345class Parameter:
346    """
347        Class to handle model parameters
348    """
349    def __init__(self, model, name, value=None):
350            self.model = model
351            self.name = name
352            if not value==None:
353                self.model.setParam(self.name, value)
354           
355    def set(self, value):
356        """
357            Set the value of the parameter
358        """
359        self.model.setParam(self.name, value)
360
361    def __call__(self):
362        """
363            Return the current value of the parameter
364        """
365        return self.model.getParam(self.name)
366   
367
368
369   
370   
371   
Note: See TracBrowser for help on using the repository browser.