diff --git a/QDS.py b/QDS.py index 64d96e7..679603d 100755 --- a/QDS.py +++ b/QDS.py @@ -10,7 +10,7 @@ from PyQt4.QtGui import * import matplotlib from Container import Conductivity, PowerComplex, Static, Peak, YAFF -from mathlib import fit_anneal, fit_lbfgsb, fit_odr_cmplx, FunctionRegister, FitRoutine +from mathlib import FunctionRegister, FitRoutine matplotlib.use('agg') @@ -26,11 +26,8 @@ import QDSMain from data import Data import pyqtgraph as pg -#import yaff +from ContainerWidgets import ParameterWidget -from ContainerWidgets import ParameterWidget, YaffWidget - -USE_CROSSH=False class AppWindow(QMainWindow): @@ -73,16 +70,6 @@ class AppWindow(QMainWindow): self.ui.pgPlotWidget_imag.addItem(self.data.fitted_curve_imag) self.ui.pgPlotWidget_real.addItem(self.data.fitted_curve_real) - # cross hair - if USE_CROSSH: - #self.poslabel = pg.LabelItem(justify='right') - self.horizontalLine = pg.InfiniteLine(angle=0) - self.verticalLine = pg.InfiniteLine(angle=90) - - #self.ui.pgPlotWidget_imag.plotItem.addItem(self.poslabel) - self.ui.pgPlotWidget_imag.addItem(self.horizontalLine) - self.ui.pgPlotWidget_imag.addItem(self.verticalLine) - self.ui.pgPlotWidget_imag.addItem(self.fit_boundary_imag) self.ui.pgPlotWidget_real.addItem(self.fit_boundary_real) @@ -144,21 +131,15 @@ class AppWindow(QMainWindow): # fitting methods fitMenu = self.menuBar().addMenu("Standard Fits") # lm - fit_lmAction = QAction("&Levenberg-Marquardt", self) + fit_lmAction = QAction("Complex NLS", self) fit_lmAction.setShortcut(QKeySequence("Ctrl+F")) fitMenu.addAction(fit_lmAction) # lbfgsb - fit_lbfgsbAction = QAction("&L-BFGS-B", self) + fit_lbfgsbAction = QAction("NLS (Imag.)", self) fitMenu.addAction(fit_lbfgsbAction) # Simulated Annealing fit_annealAction = QAction("&Simulated Annealing", self) fitMenu.addAction(fit_annealAction) - # YAFF - yaffMenu = self.menuBar().addMenu("YAFF") - start_yaff = QAction("&Startparam.", self) - yaffMenu.addAction(start_yaff) - fit_yaff = QAction("&Fit", self) - yaffMenu.addAction(fit_yaff) self.signalMapper = QSignalMapper(self) for i, fit_action in enumerate([fit_lmAction, fit_lbfgsbAction, fit_annealAction @@ -179,17 +160,11 @@ class AppWindow(QMainWindow): else: pos = QPointF(0.0, 0.0) self.last_pos = pos - if USE_CROSSH: - self.horizontalLine.setBounds([self.data.frequency.min(), self.data.frequency.max()]) - self.horizontalLine.setPos(pos.y()) - self.verticalLine.setPos(pos.x()) def mousePress(self, evt): data_pos = self.last_pos - mouse_in_imag = self.ui.pgPlotWidget_imag.underMouse() mouse_in_real = self.ui.pgPlotWidget_real.underMouse() - msgBox = QMessageBox() if self.ui.actionAdd_Peak.isChecked(): @@ -396,7 +371,13 @@ class AppWindow(QMainWindow): self.updatePlot() def fitData_start(self, method): - fit_methods = [fit_odr_cmplx, fit_lbfgsb, fit_anneal] + print method + #fit_methods = [fit_odr_cmplx, fit_odr_imag, fit_lbfgsb, fit_anneal] + + fit_method = [ + self._fit_method.fit_odr_cmplx, + self._fit_method.fit_odr_imag, + ][method] # build function list p0,funcs,fixed_params = [],[],[] @@ -408,7 +389,8 @@ class AppWindow(QMainWindow): _freq, _fit = self.data.get_data() if not self._fit_thread.isRunning(): - self._fit_method.fit_odr_cmplx(_freq, _fit, p0, fixed_params, funcs) + #self._fit_method.fit_odr_cmplx(_freq, _fit, p0, fixed_params, funcs) + fit_method(_freq, _fit, p0, fixed_params, funcs) self._fit_thread.start() self.ui.statusbar.showMessage("Fitting ...") else: diff --git a/mathlib.py b/mathlib.py index 466d97a..9120166 100644 --- a/mathlib.py +++ b/mathlib.py @@ -11,6 +11,217 @@ from scipy import optimize as opt, odr import libyaff + +def id_to_color(id): + colors = [ + QColor(54,22,115), + QColor(160,16,36), + QColor(45,142,15), + QColor(168,149,17), + QColor(36,10,85), + QColor(118,8,23), + QColor(31,105,7), + QColor(124,109,8), + ] + return colors[id % len(colors)] + + + +class FitFunctionCreator(QObject): + new_data = pyqtSignal(np.ndarray, np.ndarray) + + def __init__(self): + super(FitFunctionCreator,self).__init__() + self.data = None + self.functions = Functions() + + + def fitfcn(self, p0, x, *funcs): + if x.ndim == 2: + self.data = np.zeros( x.shape ) + else: + self.data = np.zeros( (2,x.size) ) + ndx = 0 + for fn in funcs: # loop over functions and add the results + f, num_p = fn.function, fn.param_number + p = p0[ndx:ndx + num_p] + if x.ndim == 2: + x = x[0] + result = f(p, x) + self.data += result # fit functions take only 1-dim x + ndx += num_p + self.new_data.emit(x, self.data) + return self.data + + def fitfcn_imag(self, p0, x, *funcs): + if x.ndim == 2: + self.data = np.zeros( x.shape ) + else: + self.data = np.zeros( (2,x.size) ) + ndx = 0 + for fn in funcs: # loop over functions and add the results + f, num_p = fn.function, fn.param_number + p = p0[ndx:ndx + num_p] + if x.ndim == 2: + x = x[0] + result = f(p, x) + self.data += result # fit functions take only 1-dim x + ndx += num_p + self.new_data.emit(x, self.data) + return self.data[1] + + +class FitRoutine(QObject): + finished_fit = pyqtSignal() + data_ready = pyqtSignal(np.ndarray, np.ndarray) + def __init__(self): + super(FitRoutine,self).__init__() + self.f = FitFunctionCreator() + self.f.new_data.connect(self.data_ready.emit) + self._fitter = self.fit_odr_cmplx + self._odr_fit = None + + @property + def fitter(self): + return self._fitter + + @fitter.setter + def fitter(self, f): + self._fitter = f + + def fit_odr_cmplx(self, x, y, p0, fixed, fcns): + #if x.ndim < 2: + # x = N.resize(x, (2,x.size)) + if np.iscomplexobj(y) and y.ndim == 1: + weights = 1/np.abs(y)**2 + we = np.resize(weights, (2, weights.size)) + #we = 1/N.array([y.real**2, y.imag**2]) + y = np.array([y.real, y.imag]) + else: + raise NotImplementedError, "need complex input for now" + dat = odr.Data(x, y, we=we) + mod = odr.Model(self.f.fitfcn, extra_args=fcns) + self._odr_fit = odr.ODR(dat, mod, p0, ifixx=(0,), ifixb=fixed, maxit=800) + + def fit_odr_imag(self, x, y, p0, fixed, fcns): + if np.iscomplexobj(y) and y.ndim == 1: + we = 1/np.imag(y)**2 + else: + raise NotImplementedError, "need complex input for now" + dat = odr.Data(x, y.imag, we=we) + mod = odr.Model(self.f.fitfcn_imag, extra_args=fcns) + self._odr_fit = odr.ODR(dat, mod, p0, ifixx=(0,), ifixb=fixed, maxit=800) + + @pyqtSlot() + def fit(self): + #print "TID in FitRoutine", QThread.thread() + self._odr_fit.run() + self.finished_fit.emit() + + def result(self): + return self._odr_fit.output + + +class FunctionRegister: + def __init__(self): + self.registry = {} + + def register_function(self, obj): + #print "FR: Registering:",obj + id_string = obj.id_label + if self.registry.has_key(obj): + raise AssertionError,"The object is already registered! This should NOT happen" + self.registry[obj]=id_string + #print "FR: ",self.registry + + def unregister_function(self, obj): + #print "FR: UnRegistering:",obj + if self.registry.has_key(obj): + self.registry.pop(obj) + else: + obj.deleteLater() + raise AssertionError,"The object is not in the registry! This should NOT happen" + #print "FR: ",self.registry + + def get_registered_functions(self): + return self.registry + + + +############## deprecated ##################### +def fit_odr_cmplx(x, y, p0, fixed, fcns): + f = FitFunctionCreator() + #if x.ndim < 2: + # x = N.resize(x, (2,x.size)) + if np.iscomplexobj(y) and y.ndim == 1: + weights = 1/np.abs(y)**2 + we = np.resize(weights, (2, weights.size)) + #we = 1/N.array([y.real**2, y.imag**2]) + y = np.array([y.real, y.imag]) + else: + raise NotImplementedError, "need complex input for now" + dat = odr.Data(x, y, we=we) + mod = odr.Model(f.fitfcn, extra_args=fcns) + fit = odr.ODR(dat, mod, p0, ifixx=(0,), ifixb=fixed, maxit=8000) + fit.run() + #print fit.output.pprint() + return fit.output + +### define funcs here +class Functions(QObject): + def __init__(self): + super(Functions,self).__init__() + self.list = { + # provides functions: "id_string":(function, number_of_parameters) + "hn":(self.hn_cmplx, 4), + "conductivity":(self.cond_cmplx, 3), + "power":(self.power_cmplx, 2), + "static":(self.static_cmplx, 1), + "yaff":(self.yaff, 8) + } + self.YAFF = libyaff.Yaff() + + def hn_cmplx(self, p, x): + om = 2*np.pi*x + #hn = om*1j + eps,t,a,b = p + hn = eps/(1+(1j*om*t)**a)**b + cplx = np.array([hn.real, -hn.imag]) + return cplx + + def cond_cmplx(self, p, x): + om = 2*np.pi*x + sgma, isgma, n = p + cond = sgma/(om**n) + isgma/(1j*om**n) # Jonscher (Universal Dielectric Response: e",e' prop sigma/omega**n + cplx = np.array([cond.real, -cond.imag]) + return cplx + + def power_cmplx(self, p, x): + om = 2*np.pi*x + sgma,n = p + power = sgma/(om*1j)**n + cplx = np.array([power.real, -power.imag]) + return cplx + + def static_cmplx(self, p, x): + eps_inf = p[0] + static = np.ones( (2,x.size) )*eps_inf + static[1,:] *= 0 # set imag part zero + #cplx = N.array([static.real, static.imag]) + return static + + def yaff(self,p,x): + ya = self.YAFF.yaff(p[:8],x) + cplx = np.array([ya.imag, ya.real]) + return cplx + + def get(self,name): + return self.list[name] + + def get_function(self,name): + return self.list[name][0] + + def fit_anneal(x, y, p0, fixed, funcs): raise NotImplementedError bounds = [(0, 1e14), (0, 1)] @@ -85,19 +296,6 @@ def hn(p, nu): return e_loss # 2* oder nicht? -def id_to_color(id): - colors = [ - QColor(54,22,115), - QColor(160,16,36), - QColor(45,142,15), - QColor(168,149,17), - QColor(36,10,85), - QColor(118,8,23), - QColor(31,105,7), - QColor(124,109,8), - ] - return colors[id % len(colors)] - def mini_func(p, x, y): res = y - multi_hn(p, x) @@ -133,188 +331,3 @@ def tau_peak(f, a, b): - -### define funcs here -class Functions(QObject): - def __init__(self): - super(Functions,self).__init__() - self.list = { - # provides functions: "id_string":(function, number_of_parameters) - "hn":(self.hn_cmplx, 4), - "conductivity":(self.cond_cmplx, 3), - "power":(self.power_cmplx, 2), - "static":(self.static_cmplx, 1), - "yaff":(self.yaff, 8) - } - self.YAFF = libyaff.Yaff() - - def hn_cmplx(self, p, x): - om = 2*np.pi*x - #hn = om*1j - eps,t,a,b = p - hn = eps/(1+(1j*om*t)**a)**b - cplx = np.array([hn.real, -hn.imag]) - return cplx - - def cond_cmplx(self, p, x): - om = 2*np.pi*x - sgma, isgma, n = p - cond = sgma/(om**n) + isgma/(1j*om**n) # Jonscher (Universal Dielectric Response: e",e' prop sigma/omega**n - cplx = np.array([cond.real, -cond.imag]) - return cplx - - def power_cmplx(self, p, x): - om = 2*np.pi*x - sgma,n = p - power = sgma/(om*1j)**n - cplx = np.array([power.real, -power.imag]) - return cplx - - def static_cmplx(self, p, x): - eps_inf = p[0] - static = np.ones( (2,x.size) )*eps_inf - static[1,:] *= 0 # set imag part zero - #cplx = N.array([static.real, static.imag]) - return static - - def yaff(self,p,x): - ya = self.YAFF.yaff(p[:8],x) - cplx = np.array([ya.imag, ya.real]) - return cplx - - def get(self,name): - return self.list[name] - - def get_function(self,name): - return self.list[name][0] - - -class FitFunctionCreator(QObject): - new_data = pyqtSignal(np.ndarray, np.ndarray) - - def __init__(self): - super(FitFunctionCreator,self).__init__() - self.data = None - self.functions = Functions() - - - def fitfcn(self, p0, x, *funcs): - if x.ndim == 2: - self.data = np.zeros( x.shape ) - else: - self.data = np.zeros( (2,x.size) ) - ndx = 0 - for fn in funcs: # loop over functions and add the results - f, num_p = fn.function, fn.param_number - p = p0[ndx:ndx + num_p] - if x.ndim == 2: - x = x[0] - result = f(p, x) - self.data += result # fit functions take only 1-dim x - ndx += num_p - self.new_data.emit(x, self.data) - return self.data - - def _fitfcn(self, p0, x, *funcs): - if x.ndim == 2: - self.data = np.zeros( x.shape ) - else: - self.data = np.zeros( (2,x.size) ) - ndx = 0 - for fn in funcs: # loop over functions and add the results - f,num_p = self.functions.get(fn) - p = p0[ndx:ndx + num_p] - if x.ndim == 2: - x = x[0] - result = f(p, x) - self.data += result # fit functions take only 1-dim x - ndx += num_p - self.new_data.emit(x, self.data) - return self.data - - -def fit_odr_cmplx(x, y, p0, fixed, fcns): - f = FitFunctionCreator() - #if x.ndim < 2: - # x = N.resize(x, (2,x.size)) - if np.iscomplexobj(y) and y.ndim == 1: - weights = 1/np.abs(y)**2 - we = np.resize(weights, (2, weights.size)) - #we = 1/N.array([y.real**2, y.imag**2]) - y = np.array([y.real, y.imag]) - else: - raise NotImplementedError, "need complex input for now" - dat = odr.Data(x, y, we=we) - mod = odr.Model(f.fitfcn, extra_args=fcns) - fit = odr.ODR(dat, mod, p0, ifixx=(0,), ifixb=fixed, maxit=8000) - fit.run() - #print fit.output.pprint() - return fit.output - -class FitRoutine(QObject): - finished_fit = pyqtSignal() - data_ready = pyqtSignal(np.ndarray, np.ndarray) - def __init__(self): - super(FitRoutine,self).__init__() - self.f = FitFunctionCreator() - self.f.new_data.connect(self.data_ready.emit) - self._fitter = self.fit_odr_cmplx - self._odr_fit = None - - @property - def fitter(self): - return self._fitter - - @fitter.setter - def fitter(self,f): - self._fitter = f - - def fit_odr_cmplx(self, x, y, p0, fixed, fcns): - #if x.ndim < 2: - # x = N.resize(x, (2,x.size)) - if np.iscomplexobj(y) and y.ndim == 1: - weights = 1/np.abs(y)**2 - we = np.resize(weights, (2, weights.size)) - #we = 1/N.array([y.real**2, y.imag**2]) - y = np.array([y.real, y.imag]) - else: - raise NotImplementedError, "need complex input for now" - - dat = odr.Data(x, y, we=we) - mod = odr.Model(self.f.fitfcn, extra_args=fcns) - self._odr_fit = odr.ODR(dat, mod, p0, ifixx=(0,), ifixb=fixed, maxit=10) - - @pyqtSlot() - def fit(self): - #print "TID in FitRoutine", QThread.thread() - self._odr_fit.run() - self.finished_fit.emit() - - def result(self): - return self._odr_fit.output - - - -class FunctionRegister: - def __init__(self): - self.registry = {} - - def register_function(self, obj): - #print "FR: Registering:",obj - id_string = obj.id_label - if self.registry.has_key(obj): - raise AssertionError,"The object is already registered! This should NOT happen" - self.registry[obj]=id_string - #print "FR: ",self.registry - - def unregister_function(self, obj): - #print "FR: UnRegistering:",obj - if self.registry.has_key(obj): - self.registry.pop(obj) - else: - obj.deleteLater() - raise AssertionError,"The object is not in the registry! This should NOT happen" - #print "FR: ",self.registry - - def get_registered_functions(self): - return self.registry