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

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

Introduced abstract engine

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