Ejemplo n.º 1
0
class LoadingHistory(object):
    def __init__(self, dbConnection):
        self.database_conn_str = dbConnection.connectionString
        self.engine = create_engine(self.database_conn_str, echo=dbConnection.echo)
        self.isOpen = False
        self.beginInsertRows = Observable()
        self.endInsertRows = Observable()
        self.beginResetModel = Observable()
        self.endResetModel = Observable()
        self._loadingEvents = list()
        self._currentProfile = None
        self._profiles = set()
        
    def open(self):
        try:
            Base.metadata.create_all(self.engine)
            self.Session = sessionmaker(bind=self.engine, expire_on_commit=False)
            self.session = self.Session()
            self.isOpen = True
        except OperationalError as e:
            logging.getLogger(__name__).warning("Cannot open database: {0}".format(str(e)))
            self.isOpen = False
        
    def close(self):
        self.session.commit()
        self.isOpen = False

    def __enter__(self):
        if not self.isOpen:
            self.open()
        return self
        
    def __exit__(self, exittype, value, tb):
        self.session.commit()

    def addLoadingEvent(self, loadingEvent):
        try:
            self.session.add( loadingEvent )
            self.session.commit()
            if self._currentProfile is None or loadingEvent.loadingProfile==self._currentProfile:
                self.beginInsertRows.fire(first=len(self._loadingEvents), last=len(self._loadingEvents))
                self._loadingEvents.append( loadingEvent )
                self.endInsertRows.firebare()
        except (InvalidRequestError, IntegrityError, ProgrammingError) as e:
            logging.getLogger(__name__).warning( str(e) )
            self.session.rollback()
            self.session = self.Session()
            
    def setTrappingDuration(self, duration):
        self._loadingEvents[-1].trappingDuration = duration
        self.commit()
        
    def commit(self):
        try:
            self.session.commit()
        except (InvalidRequestError, IntegrityError, ProgrammingError) as e:
            logging.getLogger(__name__).warning( str(e) )
            self.session.rollback()
            self.session = self.Session()
        
    def query(self, fromTime, toTime, loadingProfile):
        self.beginResetModel.firebare()
        self._loadingEvents[:] = []   # clear the list in place
        self._loadingEvents.extend( self.session.query(LoadingEvent).filter(LoadingEvent.trappingTime>=fromTime).filter(LoadingEvent.trappingTime<=toTime).filter(LoadingEvent.loadingProfile == loadingProfile).order_by(LoadingEvent.trappingTime).all())
        self.endResetModel.firebare()
        self._currentProfile = loadingProfile
    
    def getProfiles(self):
        return None
    
    @property
    def loadingEvents(self):
        return self._loadingEvents
    
    def lastEvent(self):
        return self._loadingEvents[-1]
Ejemplo n.º 2
0
class ShuttlingGraph(list):
    def __init__(self, shuttlingEdges=list() ):
        super(ShuttlingGraph, self).__init__(shuttlingEdges) 
        self.currentPosition = None
        self.currentPositionName = None
        self.nodeLookup = dict()
        self.currentPositionObservable = Observable()
        self.graphChangedObservable = Observable()
        self.initGraph()
        self._hasChanged = True
        
    def initGraph(self):
        self.shuttlingGraph = MultiGraph()
        for edge in self:
            self.shuttlingGraph.add_node(edge.startName)
            self.shuttlingGraph.add_node(edge.stopName)
            self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge,
                                         weight=abs(edge.stopLine-edge.startLine))
            self.nodeLookup[edge.startLine] = edge.startName
            self.nodeLookup[edge.stopLine] = edge.stopName

    def rgenerateNodeLookup(self):
        self.nodeLookup.clear()
        for edge in self:
            self.nodeLookup[edge.startLine] = edge.startName
            self.nodeLookup[edge.stopLine] = edge.stopName

    @property
    def hasChanged(self):
        return self._hasChanged
    
    @hasChanged.setter
    def hasChanged(self, value):
        self._hasChanged = value
            
    def position(self, line):
        return self.nodeLookup.get(line)
    
    def setPosition(self, line):
        if self.currentPosition!=line:
            self.currentPosition = line
            self.currentPositionName = self.position(line)
            self.currentPositionObservable.fire( line=line, text=firstNotNone(self.currentPositionName, "") )

    def getMatchingPosition(self,graph):
        """Try to match node name/position to the current settings in the provided ShuttlingGraph."""
        if not graph:
            return self.currentPosition # no change
        # Matching node name. Need to set the corresponding position
        for edge in self:
            if edge.startName == graph.currentPositionName:
                return edge.startLine
            if edge.stopName == graph.currentPositionName:
                return edge.stopLine
        #if graph.currentPosition:
        #    return graph.currentPosition #just use the graph's position
        return self.currentPosition

    def addEdge(self, edge):
        self._hasChanged = True
        self.append(edge)
        self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge, weight=abs(edge.stopLine-edge.startLine))
        self.nodeLookup[edge.startLine] = edge.startName
        self.nodeLookup[edge.stopLine] = edge.stopName
        self.graphChangedObservable.firebare()
        self.setPosition(self.currentPosition)
            
    def isValidEdge(self, edge):
        return ((edge.startLine not in self.nodeLookup or self.nodeLookup[edge.startLine] == edge.startName)
                and (edge.stopLine not in self.nodeLookup or self.nodeLookup[edge.stopLine] == edge.stopName))
        
    def getValidEdge(self):
        index = 0
        while self.shuttlingGraph.has_node("Start_{0}".format(index)):
            index += 1
        startName = "Start_{0}".format(index)
        index = 0
        while self.shuttlingGraph.has_node("Stop_{0}".format(index)):
            index += 1
        stopName = "Stop_{0}".format(index)
        index = 0
        startLine = (max( self.nodeLookup.keys() )+1) if self.nodeLookup else 1
        stopLine = startLine + 1
        return ShuttleEdge(startName, stopName, startLine, stopLine, 0, 0, 0, 0)
    
    def removeEdge(self, edgeno):
        self._hasChanged = True
        edge = self.pop(edgeno)
        self.shuttlingGraph.remove_edge(edge.startName, edge.stopName, hash(edge))
        if self.shuttlingGraph.degree(edge.startName) == 0:
            self.shuttlingGraph.remove_node(edge.startName)
        if self.shuttlingGraph.degree(edge.stopName) == 0:
            self.shuttlingGraph.remove_node(edge.stopName)
        self.graphChangedObservable.firebare()
        self.rgenerateNodeLookup()
        self.setPosition(self.currentPosition)
    
    def setStartName(self, edgeno, startName):
        self._hasChanged = True
        startName = str(startName)
        edge = self[edgeno]
        if edge.startName != startName:
            self.shuttlingGraph.remove_edge(edge.startName, edge.stopName, key=hash(edge))
            if self.shuttlingGraph.degree(edge.startName) == 0:
                self.shuttlingGraph.remove_node(edge.startName)
            edge.startName = startName
            self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge,
                                         weight=abs(edge.stopLine-edge.startLine) )
            self.graphChangedObservable.firebare()
            self.setPosition(self.currentPosition)
            self.rgenerateNodeLookup()
        return True
    
    def setStopName(self, edgeno, stopName):
        self._hasChanged = True
        stopName = str(stopName)
        edge = self[edgeno]
        if edge.stopName != stopName:
            self.shuttlingGraph.remove_edge(edge.startName, edge.stopName, key=hash(edge))
            if self.shuttlingGraph.degree(edge.stopName) == 0:
                self.shuttlingGraph.remove_node(edge.stopName)
            edge.stopName = stopName
            self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge,
                                         weight=abs(edge.stopLine-edge.startLine) )
            self.graphChangedObservable.firebare()
            self.rgenerateNodeLookup()
            self.setPosition(self.currentPosition)
        return True
    
    def setStartLine(self, edgeno, startLine):
        self._hasChanged = True
        edge = self[edgeno]
        if startLine != edge.startLine and (startLine not in self.nodeLookup or self.nodeLookup[startLine] == edge.startName):
            self.nodeLookup.pop(edge.startLine)
            edge.startLine = startLine
            self.shuttlingGraph.edge[edge.startName][edge.stopName][hash(edge)]['weight'] = abs(edge.stopLine-edge.startLine)
            self.rgenerateNodeLookup()
            self.graphChangedObservable.firebare()
            self.setPosition(self.currentPosition)
            return True    
        return False  
    
    def setStopLine(self, edgeno, stopLine):
        self._hasChanged = True
        edge = self[edgeno]
        if stopLine != edge.stopLine and (stopLine not in self.nodeLookup or self.nodeLookup[stopLine] == edge.stopName):
            self.nodeLookup.pop(edge.stopLine)
            edge.stopLine = stopLine
            self.shuttlingGraph.edge[edge.startName][edge.stopName][hash(edge)]['weight'] = abs(edge.stopLine-edge.startLine)
            self.rgenerateNodeLookup()
            self.graphChangedObservable.firebare()
            self.setPosition(self.currentPosition)
            return True  
        return False
    
    def setIdleCount(self, edgeno, idleCount):
        self._hasChanged = True
        self[edgeno].idleCount = idleCount
        return True      

    def setSteps(self, edgeno, steps):
        self._hasChanged = True
        self[edgeno].steps = steps
        return True      
    
    def shuttlePath(self, fromName, toName ):
        fromName = firstNotNone(fromName, self.currentPositionName)
        fromName = fromName if fromName else self.position(float(self.currentPosition))
        if fromName not in self.shuttlingGraph:
            raise ShuttlingGraphException("Shuttling failed, origin '{0}' is not a valid shuttling node".format(fromName))
        if toName not in self.shuttlingGraph:
            raise ShuttlingGraphException("Shuttling failed, target '{0}' is not a valid shuttling node".format(toName))
        sp = shortest_path(self.shuttlingGraph, fromName, toName)
        path = list()
        for a, b in pairs_iter(sp):
            edge = sorted(self.shuttlingGraph.edge[a][b].values(), key=itemgetter('weight'))[0]['edge']
            path.append((a, b, edge, self.index(edge)))
        return path
    
    def nodes(self):
        return self.shuttlingGraph.nodes()
    
    def toXmlElement(self, root):
        mydict = dict( ( (key, str(getattr(self, key))) for key in ('currentPosition', 'currentPositionName') if getattr(self, key) is not None  ) ) 
        myElement = ElementTree.SubElement(root, "ShuttlingGraph", attrib=mydict )
        for edge in self:
            edge.toXmlElement( myElement )
        return myElement
    
    def setStartType(self, edgeno, Type):
        self._hasChanged = True
        self[edgeno].startType = str(Type)
        return True
    
    def setStopType(self, edgeno, Type):
        self._hasChanged = True
        self[edgeno].stopType = str(Type)
        return True
    
    def setStartLength(self, edgeno, length):
        edge = self[edgeno]
        if length!=edge.startLength:
            if length+edge.stopLength<edge.sampleCount:
                self._hasChanged = True
                edge.startLength = int(length)
            else:
                return False
        return True
    
    def setStopLength(self, edgeno, length):
        edge = self[edgeno]
        if length!=edge.stopLength:
            if edge.startLength+length<edge.sampleCount:
                self._hasChanged = True
                edge.stopLength = int(length)
            else:
                return False
        return True
    
    @staticmethod
    def fromXmlElement( element ):
        edgeElementList = element.findall("ShuttleEdge")
        edgeList = [ ShuttleEdge.fromXmlElement(e) for e in edgeElementList ]
        return ShuttlingGraph(edgeList)
Ejemplo n.º 3
0
class LoadingHistory(object):
    def __init__(self, dbConnection):
        self.database_conn_str = dbConnection.connectionString
        self.engine = create_engine(self.database_conn_str,
                                    echo=dbConnection.echo)
        self.isOpen = False
        self.beginInsertRows = Observable()
        self.endInsertRows = Observable()
        self.beginResetModel = Observable()
        self.endResetModel = Observable()
        self._loadingEvents = list()
        self._currentProfile = None
        self._profiles = set()

    def open(self):
        try:
            Base.metadata.create_all(self.engine)
            self.Session = sessionmaker(bind=self.engine,
                                        expire_on_commit=False)
            self.session = self.Session()
            self.isOpen = True
        except OperationalError as e:
            logging.getLogger(__name__).warning(
                "Cannot open database: {0}".format(str(e)))
            self.isOpen = False

    def close(self):
        self.session.commit()
        self.isOpen = False

    def __enter__(self):
        if not self.isOpen:
            self.open()
        return self

    def __exit__(self, exittype, value, tb):
        self.session.commit()

    def addLoadingEvent(self, loadingEvent):
        try:
            self.session.add(loadingEvent)
            self.session.commit()
            if self._currentProfile is None or loadingEvent.loadingProfile == self._currentProfile:
                self.beginInsertRows.fire(first=len(self._loadingEvents),
                                          last=len(self._loadingEvents))
                self._loadingEvents.append(loadingEvent)
                self.endInsertRows.firebare()
        except (InvalidRequestError, IntegrityError, ProgrammingError) as e:
            logging.getLogger(__name__).warning(str(e))
            self.session.rollback()
            self.session = self.Session()

    def setTrappingDuration(self, duration):
        self._loadingEvents[-1].trappingDuration = duration
        self.commit()

    def commit(self):
        try:
            self.session.commit()
        except (InvalidRequestError, IntegrityError, ProgrammingError) as e:
            logging.getLogger(__name__).warning(str(e))
            self.session.rollback()
            self.session = self.Session()

    def query(self, fromTime, toTime, loadingProfile):
        self.beginResetModel.firebare()
        self._loadingEvents[:] = []  # clear the list in place
        self._loadingEvents.extend(
            self.session.query(LoadingEvent).
            filter(LoadingEvent.trappingTime >= fromTime).filter(
                LoadingEvent.trappingTime <= toTime).filter(
                    LoadingEvent.loadingProfile == loadingProfile).order_by(
                        LoadingEvent.trappingTime).all())
        self.endResetModel.firebare()
        self._currentProfile = loadingProfile

    def getProfiles(self):
        return None

    @property
    def loadingEvents(self):
        return self._loadingEvents

    def lastEvent(self):
        return self._loadingEvents[-1]
Ejemplo n.º 4
0
class MeasurementContainer(object):
    def __init__(self, dbConnection):
        self.database_conn_str = dbConnection.connectionString
        self.engine = create_engine(self.database_conn_str, echo=dbConnection.echo)
        self.studies = list()
        self.measurements = list()
        self.measurementDict = dict() #key - startDate, val - Measurement
        self.spaces = list()
        self.isOpen = False
        self.beginInsertMeasurement = Observable()
        self.endInsertMeasurement = Observable()
        self.studiesObservable = Observable()
        self.measurementsUpdated = Observable()
        self.scanNamesChanged = Observable()
        self._scanNames = SequenceDict()
        self._scanNameFilter = None
        self.fromTime = datetime(2014, 11, 1, 0, 0)
        self.toTime = datetime.combine((datetime.now()+timedelta(days=1)).date(), time())
        
    def setScanNameFilter(self, scanNameFilter):
        if self._scanNameFilter!=scanNameFilter:
            self._scanNameFilter = scanNameFilter
            self.query(self.fromTime, self.toTime, self._scanNameFilter)
        
    def open(self):
        Base.metadata.create_all(self.engine)
        self.Session = sessionmaker(bind=self.engine, expire_on_commit=False)
        self.session = self.Session()
        self.isOpen = True
        
    def close(self):
        self.session.commit()
        self.isOpen = False

    def __enter__(self):
        if not self.isOpen:
            self.open()
        return self
        
    def __exit__(self, exittype, value, tb):
        self.session.commit()

    def addMeasurement(self, measurement):
        try:
            self.session.add( measurement )
            self.session.commit()
            self.measurementDict[str(measurement.startDate)] = measurement
            if self._scanNameFilter is None or measurement.scanName in self._scanNameFilter:
                self.beginInsertMeasurement.fire(first=0, last=0)
                self.measurements.insert( 0, measurement )
                self.endInsertMeasurement.firebare()
            if measurement.scanName not in self._scanNames:
                self._scanNames.setdefault( measurement.scanName, True )
                self.scanNamesChanged.fire( scanNames=self._scanNames )
        except (InvalidRequestError, IntegrityError, ProgrammingError) as e:
            logging.getLogger(__name__).warning( str(e) )
            self.session.rollback()
            self.session = self.Session()
        
    def commit(self):
        try:
            self.session.commit()
        except (InvalidRequestError, IntegrityError, ProgrammingError) as e:
            logging.getLogger(__name__).warning( str(e) )
            self.session.rollback()
            self.session = self.Session()
        
    def query(self, fromTime, toTime, scanNameFilter=None):
        if scanNameFilter is None:
            self.measurements = self.session.query(Measurement).filter(Measurement.startDate>=fromTime).filter(Measurement.startDate<=toTime).order_by(Measurement.id.desc()).all() 
            self._scanNames = SequenceDict(((m.scanName, self._scanNames.get(m.scanName, True)) for m in self.measurements))
            self._scanNames.sort()
        else:
            self.measurements = self.session.query(Measurement).filter(Measurement.startDate>=fromTime).filter(Measurement.startDate<=toTime).filter(Measurement.scanName.in_(scanNameFilter)).order_by(Measurement.id.desc()).all() 
            scanNames = self.session.query(Measurement.scanName).filter(Measurement.startDate>=fromTime).filter(Measurement.startDate<=toTime).group_by(Measurement.scanName).order_by(Measurement.scanName).all()
            self._scanNames = SequenceDict(((name, name in scanNameFilter) for name, in scanNames))
        self.scanNamesChanged.fire( scanNames=self.scanNames )
        self.measurementsUpdated.fire(measurements=self.measurements)
        self.fromTime, self.toTime = fromTime, toTime
    
    def refreshLookups(self):
        """Load the basic short tables into memory
        those are: Space"""
        try:
            self.spaces = dict(( (s.name, s) for s in self.session.query(Space).all() ))
        except (InvalidRequestError, IntegrityError, ProgrammingError) as e:
            logging.getLogger(__name__).warning( str(e) )
            self.session.rollback()
            self.session = self.Session()
        
    def getSpace(self, name):
        if name not in self.spaces:
            self.refreshLookups()
        if name in self.spaces:
            return self.spaces[name]
        s = Space(name=name)
        self.spaces[name] = s
        return s
    
    @property
    def scanNames(self):
        return self._scanNames
Ejemplo n.º 5
0
class FitFunctionBase(object, metaclass=FitFunctionMeta):
    expression = Expression()
    name = 'None'
    parameterNames = list()

    def __init__(self):
        numParameters = len(self.parameterNames)
        self.epsfcn = 0.0
        self.parameters = [0] * numParameters
        self.startParameters = [1] * numParameters
        self.startParameterExpressions = None  # will be initialized by FitUiTableModel if values are available
        self.parameterEnabled = [True] * numParameters
        self.parametersConfidence = [None] * numParameters
        self.units = None
        self.results = SequenceDict({'RMSres': ResultRecord(name='RMSres')})
        self.useSmartStartValues = False
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative')
        self.parametersUpdated = Observable()
        self.parameterBounds = [[None, None] for _ in range(numParameters)]
        self.parameterBoundsExpressions = None
        self.useErrorBars = True

    def __setstate__(self, state):
        state.pop('parameterNames', None)
        state.pop('cov_x', None)
        state.pop('infodict', None)
        state.pop('laguerreCacheEta', None)
        state.pop('laguerreTable', None)
        state.pop('pnCacheBeta', None)
        state.pop('pnTable', None)
        self.__dict__ = state
        self.__dict__.setdefault('useSmartStartValues', False)
        self.__dict__.setdefault('startParameterExpressions', None)
        self.__dict__.setdefault('parameterBounds',
                                 [[None, None]
                                  for _ in range(len(self.parameterNames))])
        self.__dict__.setdefault('parameterBoundsExpressions', None)
        self.__dict__.setdefault('useErrorBars', True)
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative')

    def allFitParameters(self, p):
        """return a list where the disabled parameters are added to the enabled parameters given in p"""
        pindex = 0
        params = list()
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                params.append(p[pindex])
                pindex += 1
            else:
                params.append(float(self.startParameters[index]))
        return params

    @staticmethod
    def coercedValue(val, bounds):
        if bounds[1] is not None and val >= bounds[1]:
            val = float(0.95 * bounds[1] + 0.05 *
                        bounds[0] if bounds[0] is not None else bounds[1] -
                        0.01)
        if bounds[0] is not None and val <= bounds[0]:
            val = float(0.95 * bounds[0] + 0.05 *
                        bounds[1] if bounds[1] is not None else bounds[0] +
                        0.01)
        return val

    def enabledStartParameters(self, parameters=None, bounded=False):
        """return a list of only the enabled start parameters"""
        if parameters is None:
            parameters = self.startParameters
        params = list()
        if bounded:
            for enabled, param, bounds in zip(self.parameterEnabled,
                                              parameters,
                                              self.parameterBounds):
                if enabled:
                    params.append(self.coercedValue(float(param), bounds))
        else:
            for enabled, param in zip(self.parameterEnabled, parameters):
                if enabled:
                    params.append(float(param))
        return params

    def enabledFitParameters(self, parameters=None):
        """return a list of only the enabled fit parameters"""
        if parameters is None:
            parameters = self.parameters
        params = list()
        for enabled, param in zip(self.parameterEnabled, parameters):
            if enabled:
                params.append(float(param))
        return params

    def enabledParameterNames(self):
        """return a list of only the enabled fit parameters"""
        params = list()
        for enabled, param in zip(self.parameterEnabled, self.parameterNames):
            if enabled:
                params.append(param)
        return params

    def setEnabledFitParameters(self, parameters):
        """set the fitted parameters if enabled"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parameters[index] = parameters[pindex]
                pindex += 1
            else:
                self.parameters[index] = float(self.startParameters[index])

    def setEnabledConfidenceParameters(self, confidence):
        """set the parameter confidence values for the enabled parameters"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parametersConfidence[index] = confidence[pindex]
                pindex += 1
            else:
                self.parametersConfidence[index] = None

    @native
    def smartStartValues(self, x, y, parameters, enabled):
        return None

    def enabledSmartStartValues(self, x, y, parameters):
        smartParameters = self.smartStartValues(x, y, parameters,
                                                self.parameterEnabled)
        return [
            smartparam if enabled else param for enabled, param, smartparam in
            zip(self.parameterEnabled, parameters, smartParameters)
        ] if smartParameters is not None else None

    def evaluate(self, globalDict):
        myReplacementDict = self.replacementDict()
        if globalDict is not None:
            myReplacementDict.update(globalDict)
        if self.startParameterExpressions is not None:
            self.startParameters = [
                param if expr is None else self.expression.evaluateAsMagnitude(
                    expr, myReplacementDict) for param, expr in zip(
                        self.startParameters, self.startParameterExpressions)
            ]
        if self.parameterBoundsExpressions is not None:
            self.parameterBounds = [[
                bound[0]
                if expr[0] is None else self.expression.evaluateAsMagnitude(
                    expr[0], myReplacementDict),
                bound[1] if expr[1] is None else
                self.expression.evaluateAsMagnitude(expr[0], myReplacementDict)
            ] for bound, expr in zip(self.parameterBounds,
                                     self.parameterBoundsExpressions)]

    def enabledBounds(self):
        result = [[
            float(bounds[0]) if bounds[0] is not None else None,
            float(bounds[1]) if bounds[1] is not None else None
        ] for enabled, bounds in zip(self.parameterEnabled,
                                     self.parameterBounds) if enabled]
        enabled = any((any(bounds) for bounds in result))
        return result if enabled else None

    def leastsq(self, x, y, parameters=None, sigma=None):
        logger = logging.getLogger(__name__)
        # Ensure all values of sigma or non zero by replacing with the minimum nonzero value
        if sigma is not None and self.useErrorBars:
            nonzerosigma = sigma[sigma > 0]
            sigma[sigma == 0] = numpy.min(
                nonzerosigma) if len(nonzerosigma) > 0 else 1.0
        else:
            sigma = None
        if parameters is None:
            parameters = [float(param) for param in self.startParameters]
        if self.useSmartStartValues:
            smartParameters = self.smartStartValues(x, y, parameters,
                                                    self.parameterEnabled)
            if smartParameters is not None:
                parameters = [
                    smartparam if enabled else param
                    for enabled, param, smartparam in zip(
                        self.parameterEnabled, parameters, smartParameters)
                ]

        myEnabledBounds = self.enabledBounds()
        if myEnabledBounds:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsqbound(
                self.residuals,
                self.enabledStartParameters(parameters, bounded=True),
                args=(y, x, sigma),
                epsfcn=self.epsfcn,
                full_output=True,
                bounds=myEnabledBounds)
        else:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsq(
                self.residuals,
                self.enabledStartParameters(parameters),
                args=(y, x, sigma),
                epsfcn=self.epsfcn,
                full_output=True)
        self.setEnabledFitParameters(enabledOnlyParameters)
        self.update(self.parameters)
        logger.info("chisq {0}".format(sum(infodict["fvec"] *
                                           infodict["fvec"])))

        # calculate final chi square
        self.chisq = sum(infodict["fvec"] * infodict["fvec"])

        self.dof = max(len(x) - len(parameters), 1)
        RMSres = Q(sqrt(self.chisq / self.dof))
        RMSres.significantDigits = 3
        self.results['RMSres'].value = RMSres
        # chisq, sqrt(chisq/dof) agrees with gnuplot
        logger.info("success {0} {1}".format(self.ier, self.mesg))
        logger.info("Converged with chi squared {0}".format(self.chisq))
        logger.info("degrees of freedom, dof {0}".format(self.dof))
        logger.info(
            "RMS of residuals (i.e. sqrt(chisq/dof)) {0}".format(RMSres))
        logger.info("Reduced chisq (i.e. variance of residuals) {0}".format(
            self.chisq / self.dof))

        # uncertainties are calculated as per gnuplot, "fixing" the result
        # for non unit values of the reduced chisq.
        # values at min match gnuplot
        enabledParameterNames = self.enabledParameterNames()
        if cov_x is not None:
            enabledOnlyParametersConfidence = numpy.sqrt(
                numpy.diagonal(cov_x)) * sqrt(self.chisq / self.dof)
            self.setEnabledConfidenceParameters(
                enabledOnlyParametersConfidence)
            logger.info("Fitted parameters at minimum, with 68% C.I.:")
            for i, pmin in enumerate(enabledOnlyParameters):
                logger.info(
                    "%2i %-10s %12f +/- %10f" %
                    (i, enabledParameterNames[i], pmin,
                     sqrt(max(cov_x[i, i], 0)) * sqrt(self.chisq / self.dof)))

            logger.info("Correlation matrix")
            # correlation matrix close to gnuplot
            messagelist = ["               "]
            for i in range(len(enabledOnlyParameters)):
                messagelist.append("%-10s" % (enabledParameterNames[i], ))
            logger.info(" ".join(messagelist))
            messagelist = []
            for i in range(len(enabledOnlyParameters)):
                messagelist.append("%10s" % enabledParameterNames[i])
                for j in range(i + 1):
                    messagelist.append(
                        "%10f" %
                        (cov_x[i, j] / sqrt(abs(cov_x[i, i] * cov_x[j, j])), ))
                logger.info(" ".join(messagelist))

                #-----------------------------------------------
        else:
            self.parametersConfidence = [None] * len(self.parametersConfidence)

        return self.parameters

    def __str__(self):
        return "; ".join([
            ", ".join([self.name, self.functionString] + [
                "{0}={1}".format(name, value)
                for name, value in zip(self.parameterNames, self.parameters)
            ])
        ])

    def setConstant(self, name, value):
        setattr(self, name, value)

    def update(self, parameters=None):
        self.parametersUpdated.fire(values=self.replacementDict())

    def toXmlElement(self, parent):
        myroot = ElementTree.SubElement(parent, 'FitFunction', {
            'name': self.name,
            'functionString': self.functionString
        })
        for name, value, confidence, enabled, startExpression, bounds, boundsexpression in zip_longest(
                self.parameterNames, self.parameters,
                self.parametersConfidence, self.parameterEnabled,
                self.startParameterExpressions, self.parameterBounds,
                self.parameterBoundsExpressions):
            e = ElementTree.SubElement(
                myroot, 'Parameter', {
                    'name': name,
                    'confidence': repr(confidence),
                    'enabled': str(enabled),
                    'startExpression': str(startExpression),
                    'bounds': ",".join(map(str, bounds)),
                    'boundsExpression': ",".join(map(str, boundsexpression))
                })
            e.text = str(value)
        for result in list(self.results.values()):
            e = ElementTree.SubElement(myroot, 'Result', {
                'name': result.name,
                'definition': str(result.definition)
            })
            e.text = str(result.value)
        return myroot

    def toHdf5(self, group):
        fitfunction_group = group.require_group('fitfunction')
        fitfunction_group.attrs['name'] = self.name
        fitfunction_group.attrs['functionString'] = self.functionString
        parameter_group = fitfunction_group.require_group('parameters')
        for index, (name, value, confidence, enabled, startExpression, bounds,
                    boundsexpression) in enumerate(
                        zip_longest(self.parameterNames, self.parameters,
                                    self.parametersConfidence,
                                    self.parameterEnabled,
                                    self.startParameterExpressions,
                                    self.parameterBounds,
                                    self.parameterBoundsExpressions)):
            g = parameter_group.require_group(name)
            g.attrs['confidence'] = confidence
            g.attrs['enabled'] = enabled
            g.attrs['startExpression'] = str(startExpression)
            g.attrs['bounds'] = bounds
            g.attrs['boundsExpression'] = ",".join(map(str, boundsexpression))
            g.attrs['value'] = value
            g.attrs['index'] = index
        results_group = fitfunction_group.require_group('results')
        for result in list(self.results.values()):
            g = results_group.requie_group(result.name)
            g.attrs['definition'] = str(result.definition)
            g.attrs['value'] = repr(result.value)

    def residuals(self, p, y, x, sigma):
        p = self.allFitParameters(p)
        if sigma is not None:
            return (y - self.functionEval(x, *p)) / sigma
        else:
            return y - self.functionEval(x, *p)

    def value(self, x, p=None):
        p = self.parameters if p is None else p
        return self.functionEval(x, *p)

    def replacementDict(self):
        replacement = dict(list(zip(self.parameterNames, self.parameters)))
        replacement.update(
            dict(((v.name, v.value) for v in list(self.results.values()))))
        return replacement
Ejemplo n.º 6
0
class FitFunctionBase(object, metaclass=FitFunctionMeta):
    expression = Expression()
    name = 'None'
    parameterNames = list()
    def __init__(self):
        numParameters = len(self.parameterNames)
        self.epsfcn=0.0
        self.parameters = [0] * numParameters
        self.startParameters = [1] * numParameters 
        self.startParameterExpressions = None   # will be initialized by FitUiTableModel if values are available
        self.parameterEnabled = [True] * numParameters
        self.parametersConfidence = [None] * numParameters
        self.units = None
        self.results = SequenceDict({'RMSres': ResultRecord(name='RMSres')})
        self.useSmartStartValues = False
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative' )
        self.parametersUpdated = Observable()
        self.parameterBounds = [[None, None] for _ in range(numParameters) ]
        self.parameterBoundsExpressions = None
        self.useErrorBars = True
        
    def __setstate__(self, state):
        state.pop('parameterNames', None )
        state.pop('cov_x', None)
        state.pop('infodict', None)
        state.pop('laguerreCacheEta', None )
        state.pop('laguerreTable', None)
        state.pop('pnCacheBeta', None )
        state.pop('pnTable', None)
        self.__dict__ = state
        self.__dict__.setdefault( 'useSmartStartValues', False )
        self.__dict__.setdefault( 'startParameterExpressions', None )
        self.__dict__.setdefault( 'parameterBounds', [[None, None] for _ in range(len(self.parameterNames)) ]  )
        self.__dict__.setdefault( 'parameterBoundsExpressions', None)
        self.__dict__.setdefault( 'useErrorBars', True)
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative' )
 
    def allFitParameters(self, p):
        """return a list where the disabled parameters are added to the enabled parameters given in p"""
        pindex = 0
        params = list()
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                params.append(p[pindex])
                pindex += 1
            else:
                params.append(float(self.startParameters[index]))
        return params
    
    @staticmethod
    def coercedValue( val, bounds ):
        if bounds[1] is not None and val>=bounds[1]:
            val = float(0.95*bounds[1]+0.05*bounds[0] if bounds[0] is not None else bounds[1]-0.01)
        if bounds[0] is not None and val<=bounds[0]:
            val = float(0.95*bounds[0]+0.05*bounds[1] if bounds[1] is not None else bounds[0]+0.01)
        return val
    
    def enabledStartParameters(self, parameters=None, bounded=False):
        """return a list of only the enabled start parameters"""
        if parameters is None:
            parameters = self.startParameters
        params = list()
        if bounded:
            for enabled, param, bounds in zip(self.parameterEnabled, parameters, self.parameterBounds):
                if enabled:
                    params.append(self.coercedValue(float(param), bounds))
        else:
            for enabled, param in zip(self.parameterEnabled, parameters):
                if enabled:
                    params.append(float(param))
        return params

    def enabledFitParameters(self, parameters=None):
        """return a list of only the enabled fit parameters"""
        if parameters is None:
            parameters = self.parameters
        params = list()
        for enabled, param in zip(self.parameterEnabled, parameters):
            if enabled:
                params.append(float(param))
        return params

    def enabledParameterNames(self):
        """return a list of only the enabled fit parameters"""
        params = list()
        for enabled, param in zip(self.parameterEnabled, self.parameterNames):
            if enabled:
                params.append(param)
        return params
    
    def setEnabledFitParameters(self, parameters):
        """set the fitted parameters if enabled"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parameters[index] = parameters[pindex]
                pindex += 1
            else:
                self.parameters[index] = float(self.startParameters[index])
    
    def setEnabledConfidenceParameters(self, confidence):
        """set the parameter confidence values for the enabled parameters"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parametersConfidence[index] = confidence[pindex]
                pindex += 1
            else:
                self.parametersConfidence[index] = None        

    @native
    def smartStartValues(self, x, y, parameters, enabled):
        return None
    
    def enabledSmartStartValues(self, x, y, parameters):
        smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled)
        return [ smartparam if enabled else param for enabled, param, smartparam in zip(self.parameterEnabled, parameters, smartParameters)] if smartParameters is not None else None

    def evaluate(self, globalDict ):
        myReplacementDict = self.replacementDict()
        if globalDict is not None:
            myReplacementDict.update( globalDict )
        if self.startParameterExpressions is not None:
            self.startParameters = [param if expr is None else self.expression.evaluateAsMagnitude(expr, myReplacementDict ) for param, expr in zip(self.startParameters, self.startParameterExpressions)]
        if self.parameterBoundsExpressions is not None:
            self.parameterBounds = [[bound[0] if expr[0] is None else self.expression.evaluateAsMagnitude(expr[0], myReplacementDict),
                                     bound[1] if expr[1] is None else self.expression.evaluateAsMagnitude(expr[0], myReplacementDict)]
                                     for bound, expr in zip(self.parameterBounds, self.parameterBoundsExpressions)]

    def enabledBounds(self):
        result = [[float(bounds[0]) if bounds[0] is not None else None,
                   float(bounds[1]) if bounds[1] is not None else None] for enabled, bounds in zip(self.parameterEnabled, self.parameterBounds) if enabled]
        enabled = any( (any(bounds) for bounds in result) )
        return result if enabled else None

    def leastsq(self, x, y, parameters=None, sigma=None):
        logger = logging.getLogger(__name__)
        # Ensure all values of sigma or non zero by replacing with the minimum nonzero value
        if sigma is not None and self.useErrorBars:
            nonzerosigma = sigma[sigma>0]
            sigma[sigma==0] = numpy.min(nonzerosigma) if len(nonzerosigma)>0 else 1.0
        else:
            sigma = None 
        if parameters is None:
            parameters = [float(param) for param in self.startParameters]
        if self.useSmartStartValues:
            smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled)
            if smartParameters is not None:
                parameters = [ smartparam if enabled else param for enabled, param, smartparam in zip(self.parameterEnabled, parameters, smartParameters)]
        
        myEnabledBounds = self.enabledBounds()
        if myEnabledBounds:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsqbound(self.residuals, self.enabledStartParameters(parameters, bounded=True),
                                                                                                 args=(y, x, sigma), epsfcn=self.epsfcn, full_output=True, bounds=myEnabledBounds)
        else:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsq(self.residuals, self.enabledStartParameters(parameters), args=(y, x, sigma),
                                                                                            epsfcn=self.epsfcn, full_output=True)
        self.setEnabledFitParameters(enabledOnlyParameters)
        self.update(self.parameters)
        logger.info( "chisq {0}".format( sum(infodict["fvec"]*infodict["fvec"]) ) )        
        
        # calculate final chi square
        self.chisq=sum(infodict["fvec"]*infodict["fvec"])
        
        self.dof = max( len(x)-len(parameters), 1)
        RMSres = Q(sqrt(self.chisq/self.dof))
        RMSres.significantDigits = 3
        self.results['RMSres'].value = RMSres
        # chisq, sqrt(chisq/dof) agrees with gnuplot
        logger.info(  "success {0} {1}".format( self.ier, self.mesg ) )
        logger.info(  "Converged with chi squared {0}".format(self.chisq) )
        logger.info(  "degrees of freedom, dof {0}".format( self.dof ) )
        logger.info(  "RMS of residuals (i.e. sqrt(chisq/dof)) {0}".format( RMSres ) )
        logger.info(  "Reduced chisq (i.e. variance of residuals) {0}".format( self.chisq/self.dof ) )
        
        # uncertainties are calculated as per gnuplot, "fixing" the result
        # for non unit values of the reduced chisq.
        # values at min match gnuplot
        enabledParameterNames = self.enabledParameterNames()
        if cov_x is not None:
            enabledOnlyParametersConfidence = numpy.sqrt(numpy.diagonal(cov_x))*sqrt(self.chisq/self.dof)
            self.setEnabledConfidenceParameters(enabledOnlyParametersConfidence)
            logger.info(  "Fitted parameters at minimum, with 68% C.I.:" )
            for i, pmin in enumerate(enabledOnlyParameters):
                logger.info(  "%2i %-10s %12f +/- %10f"%(i, enabledParameterNames[i], pmin, sqrt(max(cov_x[i, i], 0))*sqrt(self.chisq/self.dof)) )
        
            logger.info(  "Correlation matrix" )
            # correlation matrix close to gnuplot
            messagelist = ["               "]
            for i in range(len(enabledOnlyParameters)): messagelist.append( "%-10s"%(enabledParameterNames[i],) )
            logger.info( " ".join(messagelist))
            messagelist = []
            for i in range(len(enabledOnlyParameters)):
                messagelist.append( "%10s"%enabledParameterNames[i] )
                for j in range(i+1):
                    messagelist.append(  "%10f"%(cov_x[i, j]/sqrt(abs(cov_x[i, i]*cov_x[j, j])),) )
                logger.info( " ".join(messagelist))
    
                #-----------------------------------------------
        else:
            self.parametersConfidence = [None]*len(self.parametersConfidence)
 
        return self.parameters
                
    def __str__(self):
        return "; ".join([", ".join([self.name, self.functionString] + [ "{0}={1}".format(name, value) for name, value in zip(self.parameterNames, self.parameters)])])

    def setConstant(self, name, value):
        setattr(self, name, value)
        
    def update(self,parameters=None):
        self.parametersUpdated.fire( values=self.replacementDict() )
    
    def toXmlElement(self, parent):
        myroot  = ElementTree.SubElement(parent, 'FitFunction', {'name': self.name, 'functionString': self.functionString})
        for name, value, confidence, enabled, startExpression, bounds, boundsexpression in zip_longest(self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions):
            e = ElementTree.SubElement( myroot, 'Parameter', {'name':name, 'confidence':repr(confidence), 'enabled': str(enabled), 'startExpression': str(startExpression), 'bounds': ",".join(map(str, bounds)),
                                                              'boundsExpression': ",".join(map(str, boundsexpression))})
            e.text = str(value)
        for result in list(self.results.values()):
            e = ElementTree.SubElement( myroot, 'Result', {'name':result.name, 'definition':str(result.definition)})
            e.text = str(result.value)
        return myroot

    def toHdf5(self, group):
        fitfunction_group = group.require_group('fitfunction')
        fitfunction_group.attrs['name'] = self.name
        fitfunction_group.attrs['functionString'] = self.functionString
        parameter_group = fitfunction_group.require_group('parameters')
        for index, (name, value, confidence, enabled, startExpression, bounds, boundsexpression) in enumerate(zip_longest(self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions)):
            g = parameter_group.require_group(name)
            g.attrs['confidence'] = confidence
            g.attrs['enabled'] = enabled
            g.attrs['startExpression'] = str(startExpression)
            g.attrs['bounds'] = bounds
            g.attrs['boundsExpression'] = ",".join(map(str, boundsexpression))
            g.attrs['value'] = value
            g.attrs['index'] = index
        results_group =  fitfunction_group.require_group('results')
        for result in list(self.results.values()):
            g = results_group.requie_group(result.name)
            g.attrs['definition'] = str(result.definition)
            g.attrs['value'] = repr(result.value)

    def residuals(self, p, y, x, sigma):
        p = self.allFitParameters(p)
        if sigma is not None:
            return (y-self.functionEval(x, *p))/sigma
        else:
            return y-self.functionEval(x, *p)
        
    def value(self,x,p=None):
        p = self.parameters if p is None else p
        return self.functionEval(x, *p )

    def replacementDict(self):
        replacement = dict(list(zip(self.parameterNames, self.parameters)))
        replacement.update( dict( ( (v.name, v.value) for v in list(self.results.values()) ) ) )
        return replacement