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
RevLine 
[792db7d5]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"""
[7705306]6import time
7import numpy
[792db7d5]8
[7705306]9import park
10from park import fit,fitresult
11from park import assembly
[cf3b781]12from park.fitmc import FitSimplex, FitMC
[7705306]13
14from sans.guitools.plottables import Data1D
15from Loader import Load
[4c718654]16from AbstractFitEngine import FitEngine, Parameter
[792db7d5]17
[7705306]18class SansParameter(park.Parameter):
19    """
[792db7d5]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.
[7705306]23    """
24    def __init__(self, name, model):
25         self._model, self._name = model,name
[9e85792]26         self.set(model.getParam(name))
[792db7d5]27         
[7705306]28    def _getvalue(self): return self._model.getParam(self.name)
[792db7d5]29   
[9e85792]30    def _setvalue(self,value): 
31        self._model.setParam(self.name, value)
[792db7d5]32       
[7705306]33    value = property(_getvalue,_setvalue)
[792db7d5]34   
[7705306]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
[792db7d5]40   
[7705306]41    def _setrange(self,r):
42        self._model.details[self.name][1:] = r
43    range = property(_getrange,_setrange)
44
[792db7d5]45
[7705306]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)
[792db7d5]55       
[7705306]56    def eval(self,x):
57        return self.model.run(x)
58   
59class Data(object):
60    """ Wrapper class  for SANS data """
[792db7d5]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"
[7705306]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):
[792db7d5]85        """ @param fn: function that return model value
86            @return residuals
87        """
[7705306]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: 
[cf3b781]90            self.fx = fn(x)
[7705306]91            return (y - fn(x))/dy
92       
93        else:
[cf3b781]94            self.fx = fn(x[idx])
[7705306]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=[]):
[792db7d5]100        """
101            @return residuals derivatives .
102            @note: in this case just return empty array
103        """
[7705306]104        return []
105class FitArrange:
106    def __init__(self):
107        """
[792db7d5]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           
[7705306]114        """
115        self.model = None
116        self.dList =[]
117       
118    def set_model(self,model):
[792db7d5]119        """
120            set_model save a copy of the model
121            @param model: the model being set
122        """
[7705306]123        self.model = model
[792db7d5]124    def remove_model(self):
125        """ remove model """
126        self.model=None
[7705306]127    def add_data(self,data):
128        """
[792db7d5]129            add_data fill a self.dList with data to fit
130            @param data: Data to add in the list 
[7705306]131        """
132        if not data in self.dList:
133            self.dList.append(data)
134           
135    def get_model(self):
[792db7d5]136        """ @return: saved model """
[7705306]137        return self.model   
138     
139    def get_data(self):
[792db7d5]140        """ @return:  list of data dList"""
[7705306]141        return self.dList
142     
143    def remove_data(self,data):
144        """
145            Remove one element from the list
[792db7d5]146            @param data: Data to remove from dList
[7705306]147        """
148        if data in self.dList:
149            self.dList.remove(data)
[cf3b781]150    def remove_datalist(self):
[792db7d5]151        """ empty the complet list dLst"""
[cf3b781]152        self.dList=[]
[7705306]153           
[792db7d5]154           
[4c718654]155class ParkFit(FitEngine):
[7705306]156    """
[792db7d5]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.
[7705306]180    """
181    def __init__(self,data=[]):
[792db7d5]182        """
183            Creates a dictionary (self.fitArrangeList={})of FitArrange elements
184            with Uid as keys
185        """
[7705306]186        self.fitArrangeList={}
[792db7d5]187       
[4dd63eb]188    def createProblem(self):
[7705306]189        """
[792db7d5]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)])
[7705306]193        """
194        mylist=[]
[9e85792]195        listmodel=[]
[7705306]196        for k,value in self.fitArrangeList.iteritems():
[9e85792]197            sansmodel=value.get_model()
[792db7d5]198            #wrap sans model
[9e85792]199            parkmodel = Model(sansmodel)
200            for p in parkmodel.parameterset:
201                if p.isfixed():
202                    p.set([-numpy.inf,numpy.inf])
203               
[7705306]204            Ldata=value.get_data()
[792db7d5]205            x,y,dy,dx=self._concatenateData(Ldata)
206            #wrap sansdata
207            parkdata=Data(x,y,dy,dx)
208            couple=(parkmodel,parkdata)
[7705306]209            mylist.append(couple)
[792db7d5]210       
[cf3b781]211        self.problem =  park.Assembly(mylist)
[792db7d5]212       
[7705306]213   
[4dd63eb]214    def fit(self, qmin=None, qmax=None):
[7705306]215        """
[792db7d5]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
[7705306]229        """
[cf3b781]230
[792db7d5]231       
[4dd63eb]232        self.createProblem()
[cf3b781]233        pars=self.problem.fit_parameters()
234        self.problem.eval()
[792db7d5]235   
[cf3b781]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))
[4dd63eb]243        print "result",result
[792db7d5]244        return result.fitness,result.pvec,result.cov
[7705306]245   
[4dd63eb]246    def set_model(self,model,name,Uid,pars={}):
[792db7d5]247        """
[4dd63eb]248     
249            Receive a dictionary of parameter and save it Parameter list
250            For scipy.fit use.
[792db7d5]251            Set model in a FitArrange object and add that object in a dictionary
252            with key Uid.
[4dd63eb]253            @param model: model on with parameter values are set
254            @param name: model name
[792db7d5]255            @param Uid: unique key corresponding to a fitArrange object with model
[4dd63eb]256            @param pars: dictionary of paramaters name and value
257            pars={parameter's name: parameter's value}
258           
[792db7d5]259        """
[4dd63eb]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       
[792db7d5]269        #A fitArrange is already created but contains dList only at Uid
[7705306]270        if self.fitArrangeList.has_key(Uid):
271            self.fitArrangeList[Uid].set_model(model)
272        else:
[792db7d5]273        #no fitArrange object has been create with this Uid
[7705306]274            fitproblem= FitArrange()
275            fitproblem.set_model(model)
276            self.fitArrangeList[Uid]=fitproblem
277       
[4dd63eb]278       
[7705306]279    def set_data(self,data,Uid):
[792db7d5]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
[7705306]287        if self.fitArrangeList.has_key(Uid):
288            self.fitArrangeList[Uid].add_data(data)
289        else:
[792db7d5]290        #no fitArrange object has been create with this Uid
[7705306]291            fitproblem= FitArrange()
292            fitproblem.add_data(data)
293            self.fitArrangeList[Uid]=fitproblem
294           
295    def get_model(self,Uid):
[792db7d5]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
[7705306]305   
[792db7d5]306    def remove_Fit_Problem(self,Uid):
307        """remove   fitarrange in Uid"""
308        if self.fitArrangeList.has_key(Uid):
309            del self.fitArrangeList[Uid]
[4dd63eb]310           
[4c718654]311     
[7705306]312   
313   
Note: See TracBrowser for help on using the repository browser.