From 4542f467a0dfe5fed5988de2b6f2de07307362a1 Mon Sep 17 00:00:00 2001 From: Clayton Ho Date: Mon, 4 Oct 2021 23:06:09 -0700 Subject: [PATCH 1/3] updated for pyqt5 and python 3 --- .gitignore | 4 +- DataVaultListWidget.py | 110 ++--- Dataset.py | 164 +++---- FitWindowWidget.py | 377 ++++++++-------- GUIConfig.py.example | 80 ---- GraphWidgetPyQtGraph.py | 519 +++++++++++----------- GraphWindow.py | 145 +++--- GridGraphWindow.py | 52 +-- HistWidgetPyQtGraph.py | 436 +++++++++--------- ImageWidget.py | 201 +++++---- ParameterListWidget.py | 44 +- PredictSpectrumWidget.py | 582 ++++++++++++------------- README.md | 6 +- ScrollingGraphWidgetPyQtGraph.py | 75 ++-- TraceListWidget.py | 251 ++++++----- analysis/fit_bessel.py | 114 ++--- analysis/fit_expdecay.py | 38 +- analysis/fit_gaussdecay.py | 38 +- analysis/fit_gaussian.py | 80 ++-- analysis/fit_linear.py | 56 +-- analysis/fit_lorentzian.py | 82 ++-- analysis/fit_rabi.py | 114 ++--- analysis/fit_ramsey.py | 2 +- analysis/fit_ramseybfield.py | 2 +- analysis/fit_rotrabi.py | 158 +++---- analysis/fit_rotramsey.py | 168 +++---- analysis/fit_sinusoid.py | 42 +- analysis/fit_sinusoid2.py | 42 +- analysis/fitting.py | 304 ++++++------- analysis/model.py | 170 ++++---- analysis/model_test.py | 116 ++--- analysis/rabi/lamb_dicke.py | 52 +-- analysis/rabi/motional_distribution.py | 202 ++++----- analysis/rabi/rabi_coupling.py | 70 +-- analysis/test_bessel.py | 20 +- analysis/test_gaussian.py | 22 +- analysis/test_linear.py | 22 +- analysis/test_lorentzian.py | 20 +- analysis/test_rabi.py | 28 +- analysis/testfit.py | 30 +- depricated/GraphWidget.py | 120 ----- depricated/ScrollingGraphWidget.py | 40 -- qt4reactor.py | 253 ----------- rsg.py | 171 ++++---- tests/add_data.py | 62 +-- tests/cursor_test.py | 25 +- tests/listView.py | 47 +- tests/plot_pyqt.py | 71 +-- 48 files changed, 2667 insertions(+), 3160 deletions(-) delete mode 100644 GUIConfig.py.example delete mode 100644 depricated/GraphWidget.py delete mode 100644 depricated/ScrollingGraphWidget.py delete mode 100644 qt4reactor.py diff --git a/.gitignore b/.gitignore index bac8729..85878bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -GUIConfig.py -*.pyc +GUIConfig.py +*.pyc diff --git a/DataVaultListWidget.py b/DataVaultListWidget.py index d5ab650..c8208ef 100644 --- a/DataVaultListWidget.py +++ b/DataVaultListWidget.py @@ -1,55 +1,55 @@ -from PyQt4 import QtGui -from twisted.internet.defer import inlineCallbacks -import socket - - -class DataVaultList(QtGui.QWidget): - - def __init__(self, tracename, parent=None): - super(DataVaultList, self).__init__() - self.tracename = tracename - self.connect() - - @inlineCallbacks - def connect(self): - from labrad.wrappers import connectAsync - self.cxn = yield connectAsync(name=socket.gethostname() + ' Data Vault Client') - self.grapher = yield self.cxn.grapher - self.dv = yield self.cxn.data_vault - self.initializeGUI() - - def initializeGUI(self): - mainLayout = QtGui.QVBoxLayout() - self.dataListWidget = QtGui.QListWidget() - self.dataListWidget.doubleClicked.connect(self.onDoubleclick) - mainLayout.addWidget(self.dataListWidget) - self.setWindowTitle('Data Vault') - self.setLayout(mainLayout) - self.populate() - self.show() - - @inlineCallbacks - def populate(self): - self.dataListWidget.clear() - ls = yield self.dv.dir() - self.dataListWidget.addItem('...') - self.dataListWidget.addItems(sorted(ls[0])) - if ls[1] is not None: - self.dataListWidget.addItems(sorted(ls[1])) - - @inlineCallbacks - def onDoubleclick(self, item): - item = self.dataListWidget.currentItem().text() - if item == '...': - yield self.dv.cd(1) - self.populate() - else: - try: - yield self.dv.cd(str(item)) - self.populate() - except: - path = yield self.dv.cd() - yield self.grapher.plot((path, str(item)), self.tracename, False) - - def closeEvent(self, event): - self.cxn.disconnect() +from PyQt5 import QtWidgets +from twisted.internet.defer import inlineCallbacks +import socket + + +class DataVaultList(QtWidgets.QWidget): + + def __init__(self, tracename, parent=None): + super(DataVaultList, self).__init__() + self.tracename = tracename + self.connect() + + @inlineCallbacks + def connect(self): + from labrad.wrappers import connectAsync + self.cxn = yield connectAsync(name=socket.gethostname() + ' Data Vault Client') + self.grapher = yield self.cxn.grapher + self.dv = yield self.cxn.data_vault + self.initializeGUI() + + def initializeGUI(self): + mainLayout = QtWidgets.QVBoxLayout() + self.dataListWidget = QtWidgets.QListWidget() + self.dataListWidget.doubleClicked.connect(self.onDoubleclick) + mainLayout.addWidget(self.dataListWidget) + self.setWindowTitle('Data Vault') + self.setLayout(mainLayout) + self.populate() + self.show() + + @inlineCallbacks + def populate(self): + self.dataListWidget.clear() + ls = yield self.dv.dir() + self.dataListWidget.addItem('...') + self.dataListWidget.addItems(sorted(ls[0])) + if ls[1] is not None: + self.dataListWidget.addItems(sorted(ls[1])) + + @inlineCallbacks + def onDoubleclick(self, item): + item = self.dataListWidget.currentItem().text() + if item == '...': + yield self.dv.cd(1) + self.populate() + else: + try: + yield self.dv.cd(str(item)) + self.populate() + except: + path = yield self.dv.cd() + yield self.grapher.plot((path, str(item)), self.tracename, False) + + def closeEvent(self, event): + self.cxn.disconnect() diff --git a/Dataset.py b/Dataset.py index 764935d..0fb656c 100644 --- a/Dataset.py +++ b/Dataset.py @@ -1,82 +1,82 @@ -''' -Parent class for datasets -''' -from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred -from PyQt4 import QtCore -from twisted.internet.threads import deferToThread -import numpy as np - -class Dataset(QtCore.QObject): - - def __init__(self, data_vault, context, dataset_location,reactor): - super(Dataset, self).__init__() - self.data = None - self.accessingData = DeferredLock() - self.reactor = reactor - self.dataset_location = dataset_location - self.data_vault = data_vault - self.updateCounter = 0 - self.context = context - self.connectDataVault() - self.setupListeners() - - @inlineCallbacks - def connectDataVault(self): - yield self.data_vault.cd(self.dataset_location[0], context = self.context) - path, dataset_name = yield self.data_vault.open(self.dataset_location[1], context = self.context) - self.dataset_name = dataset_name - - @inlineCallbacks - def setupListeners(self): - yield self.data_vault.signal__data_available(11111, context = self.context) - yield self.data_vault.addListener(listener = self.updateData, source = None, ID = 11111, context = self.context) - - - @inlineCallbacks - def openDataset(self): - yield self.data_vault.cd(self.dataset_location[0], context = self.context) - yield self.data_vault.open(self.dataset_location[1], context = self.context) - - @inlineCallbacks - def getParameters(self): - parameters = yield self.data_vault.parameters(context = self.context) - parameterValues = [] - for parameter in parameters: - parameterValue = yield self.data_vault.get_parameter(parameter, context = self.context) - parameterValues.append( (parameter, parameterValue) ) - returnValue(parameterValues) - - def updateData(self,x,y): - self.updateCounter += 1 - self.getData() - - @inlineCallbacks - def getData(self): - Data = yield self.data_vault.get(100, context = self.context) - if (self.data is None): - yield self.accessingData.acquire() - try: - self.data = Data.asarray - except: - self.data = Data - self.accessingData.release() - else: - yield self.accessingData.acquire() - try: - self.data = np.append(self.data, Data.asarray, 0) - except: - self.data = np.append(self.data, Data, 0) - self.accessingData.release() - - @inlineCallbacks - def getLabels(self): - labels = [] - yield self.openDataset() - variables = yield self.data_vault.variables(context = self.context) - for i in range(len(variables[1])): - labels.append(variables[1][i][1] + ' - ' + self.dataset_name) - returnValue(labels) - - @inlineCallbacks - def disconnectDataSignal(self): - yield self.data_vault.removeListener(listener = self.updateData, source = None, ID = 11111, context = self.context) +''' +Parent class for datasets +''' +from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred +from PyQt5 import QtCore +from twisted.internet.threads import deferToThread +import numpy as np + +class Dataset(QtCore.QObject): + + def __init__(self, data_vault, context, dataset_location,reactor): + super(Dataset, self).__init__() + self.data = None + self.accessingData = DeferredLock() + self.reactor = reactor + self.dataset_location = dataset_location + self.data_vault = data_vault + self.updateCounter = 0 + self.context = context + self.connectDataVault() + self.setupListeners() + + @inlineCallbacks + def connectDataVault(self): + yield self.data_vault.cd(self.dataset_location[0], context = self.context) + path, dataset_name = yield self.data_vault.open(self.dataset_location[1], context = self.context) + self.dataset_name = dataset_name + + @inlineCallbacks + def setupListeners(self): + yield self.data_vault.signal__data_available(11111, context = self.context) + yield self.data_vault.addListener(listener = self.updateData, source = None, ID = 11111, context = self.context) + + + @inlineCallbacks + def openDataset(self): + yield self.data_vault.cd(self.dataset_location[0], context = self.context) + yield self.data_vault.open(self.dataset_location[1], context = self.context) + + @inlineCallbacks + def getParameters(self): + parameters = yield self.data_vault.parameters(context = self.context) + parameterValues = [] + for parameter in parameters: + parameterValue = yield self.data_vault.get_parameter(parameter, context = self.context) + parameterValues.append( (parameter, parameterValue) ) + returnValue(parameterValues) + + def updateData(self,x,y): + self.updateCounter += 1 + self.getData() + + @inlineCallbacks + def getData(self): + Data = yield self.data_vault.get(100, context = self.context) + if (self.data is None): + yield self.accessingData.acquire() + try: + self.data = Data.asarray + except: + self.data = Data + self.accessingData.release() + else: + yield self.accessingData.acquire() + try: + self.data = np.append(self.data, Data.asarray, 0) + except: + self.data = np.append(self.data, Data, 0) + self.accessingData.release() + + @inlineCallbacks + def getLabels(self): + labels = [] + yield self.openDataset() + variables = yield self.data_vault.variables(context = self.context) + for i in range(len(variables[1])): + labels.append(variables[1][i][1] + ' - ' + self.dataset_name) + returnValue(labels) + + @inlineCallbacks + def disconnectDataSignal(self): + yield self.data_vault.removeListener(listener = self.updateData, source = None, ID = 11111, context = self.context) diff --git a/FitWindowWidget.py b/FitWindowWidget.py index 1be2844..e6ac541 100644 --- a/FitWindowWidget.py +++ b/FitWindowWidget.py @@ -1,189 +1,188 @@ -from PyQt4 import QtGui, QtCore -from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred -from analysis.fitting import FitWrapper - -class RowInfo(): - ''' - Container for the widgets with - each row in the parameters table - ''' - def __init__(self, vary, manual_value, fitted_value): - self.vary_select = vary - self.manual_value = manual_value - self.fitted_value = fitted_value - -class FitWindow(QtGui.QWidget): - - def __init__(self, dataset, index, parent): - super(FitWindow, self).__init__() - self.dataset = dataset - self.index = index - self.parent = parent - self.fw = FitWrapper(dataset, index) - self.row_info_dict = {} - self.ident = 'Fit: ' + str(self.dataset.dataset_name) - self.initUI() - - def initUI(self): - self.setWindowTitle(self.ident) - mainLayout = QtGui.QVBoxLayout() - buttons = QtGui.QHBoxLayout() - - self.model_select = QtGui.QComboBox(self) - for model in self.fw.models: - self.model_select.addItem(model) - - self.parameterTable = QtGui.QTableWidget() - self.parameterTable.setColumnCount(4) - - self.fitButton = QtGui.QPushButton('Fit', self) - - self.plotButton = QtGui.QPushButton('Plot manual', self) - - self.fw.setModel(str(self.model_select.currentText())) - - mainLayout.addWidget(self.model_select) - mainLayout.addWidget(self.parameterTable) - mainLayout.addLayout(buttons) - buttons.addWidget(self.fitButton) - buttons.addWidget(self.plotButton) - - self.model_select.activated.connect(self.onActivated) - self.fitButton.clicked.connect(self.onClick) - self.plotButton.clicked.connect(self.onPlot) - - self.setupParameterTable() - self.setLayout(mainLayout) - self.show() - - def setupParameterTable(self): - - self.parameterTable.clear() - - headerLabels = QtCore.QStringList(['Vary', 'Param', 'Manual', 'Fitted']) - self.parameterTable.setHorizontalHeaderLabels(headerLabels) - self.parameterTable.horizontalHeader().setStretchLastSection(True) - - params = self.fw.getParameters() - self.parameterTable.setRowCount(len(params)) - for i,p in enumerate(params): - - vary_select = QtGui.QTableWidgetItem() - label = QtGui.QLabel(p) - manual_value = QtGui.QDoubleSpinBox() - fitted_value = QtGui.QTableWidgetItem() - - self.row_info_dict[p] = RowInfo(vary_select, manual_value, fitted_value) - - vary_select.setFlags(QtCore.Qt.ItemIsUserCheckable|QtCore.Qt.ItemIsEnabled) - if self.fw.getVary(p): - vary_select.setCheckState(QtCore.Qt.Checked) - else: - vary_select.setCheckState(QtCore.Qt.Unchecked) - - manualValue = self.fw.getManualValue(p) - manual_value.setDecimals(6) - manual_value.setRange(-1000000000, 1000000000) - manual_value.setValue(manualValue) - - fittedValue = self.fw.getFittedValue(p) - #fitted_value.setDecimals(6) - #fitted_value.setRange(-1000000000, 1000000000) - fitted_value.setText(str(fittedValue)) - self.parameterTable.setItem(i, 0, vary_select) - self.parameterTable.setCellWidget(i, 1, label) - self.parameterTable.setCellWidget(i, 2, manual_value) - self.parameterTable.setItem(i, 3, fitted_value) - - def updateParametersToFitter(self): - params = self.fw.getParameters() - for p in params: - row = self.row_info_dict[p] - vary = row.vary_select.checkState() - manual_value = row.manual_value.value() - if vary: - self.fw.setVary(p, True) - else: - self.fw.setVary(p, False) - self.fw.setManualValue(p, manual_value) - - def updateParametersFromFitter(self): - ''' - Set the fitted and manual parameters - fields to the fit values - ''' - params = self.fw.getParameters() - for p in params: - row = self.row_info_dict[p] - fitted_value = self.fw.getFittedValue(p) - row.fitted_value.setText( str(fitted_value) ) - row.manual_value.setValue( fitted_value ) - - - def plotFit(self): - ''' - Plot the fitted parameters. - We need to wrap the data in a dataset - object to use add_artist in GraphWidget - ''' - - class dataset(): - def __init__(self, data): - self.data = data - self.updateCounter = 1 - data = self.fw.evaluateFittedParameters() - ds = dataset(data) - try: - # remove the previous fit - self.parent.parent.remove_artist(self.ident) - self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) - except: - self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) - - def onActivated(self): - ''' - Run when model is changed. - Reset row_info_dict each - time the model is changed. - ''' - model = str(self.model_select.currentText()) - self.fw.setModel(model) - self.row_info_dict = {} - self.setupParameterTable() - - def onClick(self): - ''' - Send table parameters to fitter, - perform fit, and then update - paramter table with the results - ''' - - self.updateParametersToFitter() - self.fw.doFit() - self.updateParametersFromFitter() - self.plotFit() - - def onPlot(self): - ''' - Plot the manual parameters. See documentation - for plotFit() - ''' - - class dataset(): - def __init__(self, data): - self.data = data - self.updateCounter = 1 - - self.updateParametersToFitter() - data = self.fw.evaluateManualParameters() - ds = dataset(data) - try: - # remove the previous plot - self.parent.parent.remove_artist(self.ident) - self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) - except: - self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) - - - def closeEvent(self, event): - self.parent.parent.remove_artist(self.ident) +from PyQt5 import QtCore, QtWidgets +from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred +from analysis.fitting import FitWrapper + +class RowInfo(): + ''' + Container for the widgets with + each row in the parameters table + ''' + def __init__(self, vary, manual_value, fitted_value): + self.vary_select = vary + self.manual_value = manual_value + self.fitted_value = fitted_value + +class FitWindow(QtWidgets.QWidget): + + def __init__(self, dataset, index, parent): + super(FitWindow, self).__init__() + self.dataset = dataset + self.index = index + self.parent = parent + self.fw = FitWrapper(dataset, index) + self.row_info_dict = {} + self.ident = 'Fit: ' + str(self.dataset.dataset_name) + self.initUI() + + def initUI(self): + self.setWindowTitle(self.ident) + mainLayout = QtWidgets.QVBoxLayout() + buttons = QtWidgets.QHBoxLayout() + + self.model_select = QtWidgets.QComboBox(self) + for model in self.fw.models: + self.model_select.addItem(model) + + self.parameterTable = QtWidgets.QTableWidget() + self.parameterTable.setColumnCount(4) + + self.fitButton = QtWidgets.QPushButton('Fit', self) + + self.plotButton = QtWidgets.QPushButton('Plot manual', self) + + self.fw.setModel(str(self.model_select.currentText())) + + mainLayout.addWidget(self.model_select) + mainLayout.addWidget(self.parameterTable) + mainLayout.addLayout(buttons) + buttons.addWidget(self.fitButton) + buttons.addWidget(self.plotButton) + + self.model_select.activated.connect(self.onActivated) + self.fitButton.clicked.connect(self.onClick) + self.plotButton.clicked.connect(self.onPlot) + + self.setupParameterTable() + self.setLayout(mainLayout) + self.show() + + def setupParameterTable(self): + + self.parameterTable.clear() + headerLabels = ['Vary', 'Param', 'Manual', 'Fitted'] + self.parameterTable.setHorizontalHeaderLabels(headerLabels) + self.parameterTable.horizontalHeader().setStretchLastSection(True) + + params = self.fw.getParameters() + self.parameterTable.setRowCount(len(params)) + for i,p in enumerate(params): + + vary_select = QtWidgets.QTableWidgetItem() + label = QtWidgets.QLabel(p) + manual_value = QtWidgets.QDoubleSpinBox() + fitted_value = QtWidgets.QTableWidgetItem() + + self.row_info_dict[p] = RowInfo(vary_select, manual_value, fitted_value) + + vary_select.setFlags(QtCore.Qt.ItemIsUserCheckable|QtCore.Qt.ItemIsEnabled) + if self.fw.getVary(p): + vary_select.setCheckState(QtCore.Qt.Checked) + else: + vary_select.setCheckState(QtCore.Qt.Unchecked) + + manualValue = self.fw.getManualValue(p) + manual_value.setDecimals(6) + manual_value.setRange(-1000000000, 1000000000) + manual_value.setValue(manualValue) + + fittedValue = self.fw.getFittedValue(p) + #fitted_value.setDecimals(6) + #fitted_value.setRange(-1000000000, 1000000000) + fitted_value.setText(str(fittedValue)) + self.parameterTable.setItem(i, 0, vary_select) + self.parameterTable.setCellWidget(i, 1, label) + self.parameterTable.setCellWidget(i, 2, manual_value) + self.parameterTable.setItem(i, 3, fitted_value) + + def updateParametersToFitter(self): + params = self.fw.getParameters() + for p in params: + row = self.row_info_dict[p] + vary = row.vary_select.checkState() + manual_value = row.manual_value.value() + if vary: + self.fw.setVary(p, True) + else: + self.fw.setVary(p, False) + self.fw.setManualValue(p, manual_value) + + def updateParametersFromFitter(self): + ''' + Set the fitted and manual parameters + fields to the fit values + ''' + params = self.fw.getParameters() + for p in params: + row = self.row_info_dict[p] + fitted_value = self.fw.getFittedValue(p) + row.fitted_value.setText( str(fitted_value) ) + row.manual_value.setValue( fitted_value ) + + + def plotFit(self): + ''' + Plot the fitted parameters. + We need to wrap the data in a dataset + object to use add_artist in GraphWidget + ''' + + class dataset(): + def __init__(self, data): + self.data = data + self.updateCounter = 1 + data = self.fw.evaluateFittedParameters() + ds = dataset(data) + try: + # remove the previous fit + self.parent.parent.remove_artist(self.ident) + self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) + except: + self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) + + def onActivated(self): + ''' + Run when model is changed. + Reset row_info_dict each + time the model is changed. + ''' + model = str(self.model_select.currentText()) + self.fw.setModel(model) + self.row_info_dict = {} + self.setupParameterTable() + + def onClick(self): + ''' + Send table parameters to fitter, + perform fit, and then update + paramter table with the results + ''' + + self.updateParametersToFitter() + self.fw.doFit() + self.updateParametersFromFitter() + self.plotFit() + + def onPlot(self): + ''' + Plot the manual parameters. See documentation + for plotFit() + ''' + + class dataset(): + def __init__(self, data): + self.data = data + self.updateCounter = 1 + + self.updateParametersToFitter() + data = self.fw.evaluateManualParameters() + ds = dataset(data) + try: + # remove the previous plot + self.parent.parent.remove_artist(self.ident) + self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) + except: + self.parent.parent.add_artist(self.ident, ds, 0, no_points = True) + + + def closeEvent(self, event): + self.parent.parent.remove_artist(self.ident) diff --git a/GUIConfig.py.example b/GUIConfig.py.example deleted file mode 100644 index 0a61db6..0000000 --- a/GUIConfig.py.example +++ /dev/null @@ -1,80 +0,0 @@ -''' -Configuration settings for Grapher gui -''' -import pyqtgraph as pg -pg.setConfigOption('background', 'k') -pg.setConfigOption('foreground', 'y') - -class traceListConfig(): - def __init__(self, background_color = 'white', use_trace_color = False): - self.background_color = background_color - self.use_trace_color = use_trace_color - -class graphConfig(): - def __init__(self, name, ylim=[0,1], isScrolling=False, max_datasets = 20, - show_points = True, grid_on = False, scatter_plot='all', isImages=False, - isHist=False, line_param=None, vline=None, vline_param=None, hline=None, hline_param=None): - self.name = name - self.ylim = ylim - self.isScrolling = isScrolling - self.max_datasets = max_datasets - self.graphs = 1 # just a single graph - self.show_points = show_points - self.grid_on = grid_on - self.scatter_plot = scatter_plot - self.isImages = isImages - self.isHist = isHist - self.line_param = line_param - self.vline = vline - self.vline_param = vline_param - self.hline = hline - self.hline_param = hline_param - -class gridGraphConfig(): - def __init__(self, tab, config_list): - self.tab = tab - self.config_list = config_list[0::3] - self.row_list = config_list[1::3] - self.column_list = config_list[2::3] - - self.graphs = len(self.config_list) - - -tabs =[ - gridGraphConfig('current', [graphConfig('current', max_datasets = 1), 0, 0]), - gridGraphConfig('pmt', [graphConfig('pmt', ylim=[0,30], isScrolling=True, max_datasets = 1, show_points = False), 0, 0]), - gridGraphConfig('spectrum', [graphConfig('spectrum'), 0, 0]), - gridGraphConfig('rabi', [graphConfig('rabi'), 0, 0]), - gridGraphConfig('calibrations', [ - graphConfig('car1'), 0, 0, - graphConfig('car2'), 0, 1, - graphConfig('radial1'), 1, 0, - graphConfig('radial2'), 1, 1]), - gridGraphConfig('molmer-sorensen',[ - graphConfig('ms_time'), 0, 0]), - - gridGraphConfig('vaet',[ - graphConfig('vaet_time'), 0, 0, - graphConfig('vaet_delta'), 0, 1]), - - gridGraphConfig('local_stark',[ - graphConfig('ms_local_stark'), 0, 0, - graphConfig('ms_local_stark_detuning'), 1, 0, - graphConfig('vaet_local_stark'), 0, 1, - graphConfig('vaet_local_stark_detuning'), 1, 1]), - - gridGraphConfig('parity', [graphConfig('parity'), 0, 0]), - gridGraphConfig('ramsey', [graphConfig('ramsey'), 0, 0]) -] - -# gridGraphConfig('testgrid', -# [ -# graphConfig('fig1'), 0, 0, -# graphConfig('fig2'), 0, 1, -# graphConfig('fig3'), 2, 2, -# graphConfig('fig4'), 1, 2 -# ]), -# gridGraphConfig('testgrid2', -# [ -# graphConfig('fig1123'), 0, 0, -# ]) diff --git a/GraphWidgetPyQtGraph.py b/GraphWidgetPyQtGraph.py index cfa41c3..4f2cac0 100644 --- a/GraphWidgetPyQtGraph.py +++ b/GraphWidgetPyQtGraph.py @@ -1,259 +1,260 @@ -import sys -from PyQt4 import QtGui, QtCore -import pyqtgraph as pg -from TraceListWidget import TraceList -from twisted.internet.defer import inlineCallbacks, returnValue -from twisted.internet.task import LoopingCall -import itertools -from Dataset import Dataset -import Queue - -import numpy as np -from numpy import random - -class artistParameters(): - def __init__(self, artist, dataset, index, shown): - self.artist = artist - self.dataset = dataset - self.index = index - self.shown = shown - self.last_update = 0 # update counter in the Dataset object - # only redraw if the dataset has a higher - # update count - -class Graph_PyQtGraph(QtGui.QWidget): - def __init__(self, config, reactor, cxn = None, parent=None): - super(Graph_PyQtGraph, self).__init__(parent) - from labrad.units import WithUnit as U - self.U = U - self.cxn = cxn - self.pv = self.cxn.parametervault - self.reactor = reactor - self.artists = {} - self.should_stop = False - self.name = config.name - self.vline_name = config.vline - self.vline_param = config.vline_param - self.hline_name = config.hline - self.hline_param = config.hline_param - self.show_points = config.show_points - self.grid_on = config.grid_on - self.scatter_plot = config.scatter_plot - - self.dataset_queue = Queue.Queue(config.max_datasets) - - self.live_update_loop = LoopingCall(self.update_figure) - self.live_update_loop.start(0) - - colors = ['r', 'g', 'y', 'c', 'm', 'w'] - self.colorChooser = itertools.cycle(colors) - self.initUI() - - @inlineCallbacks - def initUI(self): - self.tracelist = TraceList(self) - self.pw = pg.PlotWidget() - if self.vline_name: - self.inf = pg.InfiniteLine(movable=True, angle=90, - label=self.vline_name + '{value:0.0f}', - labelOpts={'position': 0.9, - 'color': (200, 200, 100), - 'fill': (200, 200, 200, 50), - 'movable': True}) - init_value = yield self.get_init_vline() - self.inf.setValue(init_value) - self.inf.setPen(width=5.0) - - if self.hline_name: - self.inf = pg.InfiniteLine(movable=True, angle=0, - label=self.hline_name + '{value:0.0f}', - labelOpts={'position': 0.9, - 'color': (200, 200, 100), - 'fill': (200, 200, 200, 50), - 'movable': True}) - init_value = yield self.get_init_hline() - self.inf.setValue(init_value) - self.inf.setPen(width=5.0) - - self.coords = QtGui.QLabel('') - self.title = QtGui.QLabel(self.name) - frame = QtGui.QFrame() - splitter = QtGui.QSplitter() - splitter.addWidget(self.tracelist) - hbox = QtGui.QHBoxLayout() - vbox = QtGui.QVBoxLayout() - vbox.addWidget(self.title) - vbox.addWidget(self.pw) - vbox.addWidget(self.coords) - frame.setLayout(vbox) - splitter.addWidget(frame) - hbox.addWidget(splitter) - self.setLayout(hbox) - #self.legend = self.pw.addLegend() - self.tracelist.itemChanged.connect(self.checkboxChanged) - self.pw.plot([],[]) - vb = self.pw.plotItem.vb - self.img = pg.ImageItem() - vb.addItem(self.img) - - if self.vline_name: - vb.addItem(self.inf) - self.inf.sigPositionChangeFinished.connect(self.vline_changed) - - if self.hline_name: - vb.addItem(self.inf) - self.inf.sigPositionChangeFinished.connect(self.hline_changed) - - self.pw.scene().sigMouseMoved.connect(self.mouseMoved) - self.pw.sigRangeChanged.connect(self.rangeChanged) - - def getItemColor(self, color): - color_dict = {"r": QtGui.QColor(QtCore.Qt.red).lighter(130), - "g": QtGui.QColor(QtCore.Qt.green), - "y": QtGui.QColor(QtCore.Qt.yellow), - "c": QtGui.QColor(QtCore.Qt.cyan), - "m": QtGui.QColor(QtCore.Qt.magenta).lighter(120), - "w": QtGui.QColor(QtCore.Qt.white)} - return color_dict[color] - - def update_figure(self): - for ident, params in self.artists.iteritems(): - if params.shown: - try: - ds = params.dataset - index = params.index - current_update = ds.updateCounter - if params.last_update < current_update: - x = ds.data[:,0] - y = ds.data[:,index+1] - params.last_update = current_update - params.artist.setData(x,y) - except: pass - - def add_artist(self, ident, dataset, index, no_points = False): - ''' - no_points is an override parameter to the global show_points setting. - It is to allow data fits to be plotted without points - ''' - new_color = self.colorChooser.next() - if self.show_points and not no_points: - line = self.pw.plot([], [], symbol='o', symbolBrush=self.getItemColor(new_color), - name=ident, pen = self.getItemColor(new_color), connect=self.scatter_plot) - else: - line = self.pw.plot([], [], pen = self.getItemColor(new_color), name = ident) - if self.grid_on: - self.pw.showGrid(x=True, y=True) - self.artists[ident] = artistParameters(line, dataset, index, True) - self.tracelist.addTrace(ident, new_color) - - def remove_artist(self, ident): - try: - artist = self.artists[ident].artist - self.pw.removeItem(artist) - #self.legend.removeItem(ident) - self.tracelist.removeTrace(ident) - self.artists[ident].shown = False - try: - del self.artists[ident] - except KeyError: - pass - except: - print "remove failed" - - def display(self, ident, shown): - try: - artist = self.artists[ident].artist - if shown: - self.pw.addItem(artist) - self.artists[ident].shown = True - else: - self.pw.removeItem(artist) - #self.legend.removeItem(ident) - self.artists[ident].shown = False - except KeyError: - raise Exception('404 Artist not found') - - def checkboxChanged(self): - for ident, item in self.tracelist.trace_dict.iteritems(): - try: - if item.checkState() and not self.artists[ident].shown: - self.display(ident, True) - if not item.checkState() and self.artists[ident].shown: - self.display(ident, False) - except KeyError: # this means the artist has been deleted. - pass - - def rangeChanged(self): - - lims = self.pw.viewRange() - self.pointsToKeep = lims[0][1] - lims[0][0] - self.current_limits = [lims[0][0], lims[0][1]] - - @inlineCallbacks - def add_dataset(self, dataset): - try: - self.dataset_queue.put(dataset, block=False) - except Queue.Full: - remove_ds = self.dataset_queue.get() - self.remove_dataset(remove_ds) - self.dataset_queue.put(dataset, block=False) - labels = yield dataset.getLabels() - for i, label in enumerate(labels): - self.add_artist(label, dataset, i) - - @inlineCallbacks - def remove_dataset(self, dataset): - labels = yield dataset.getLabels() - for label in labels: - self.remove_artist(label) - - def set_xlimits(self, limits): - self.pw.setXRange(limits[0], limits[1]) - self.current_limits = limits - - def set_ylimits(self, limits): - self.pw.setYRange(limits[0],limits[1]) - - def mouseMoved(self, pos): - #print "Image position:", self.img.mapFromScene(pos) - pnt = self.img.mapFromScene(pos) - string = '(' + str(pnt.x()) + ' , ' + str(pnt.y()) + ')' - self.coords.setText(string) - - @inlineCallbacks - def get_init_vline(self): - init_vline = yield self.pv.get_parameter(self.vline_param[0], - self.vline_param[1]) - returnValue(init_vline) - - @inlineCallbacks - def get_init_hline(self): - init_hline = yield self.pv.get_parameter(self.hline_param[0], - self.hline_param[1]) - returnValue(init_hline) - - @inlineCallbacks - def vline_changed(self, sig): - val = self.inf.value() - param = yield self.pv.get_parameter(self.vline_param[0], self.vline_param[1]) - units = param.units - val = self.U(val, units) - yield self.pv.set_parameter(self.vline_param[0], self.vline_param[1], val) - - @inlineCallbacks - def hline_changed(self, sig): - val = self.inf.value() - param = yield self.pv.get_parameter(self.hline_param[0], self.hline_param[1]) - units = param.units - val = self.U(val, units) - yield self.pv.set_parameter(self.hline_param[0], self.hline_param[1], val) - -if __name__ == '__main__': - app = QtGui.QApplication(sys.argv) - import qt4reactor - qt4reactor.install() - from twisted.internet import reactor - main = Graph_PyQtGraph('example', reactor) - main.show() - #sys.exit(app.exec_()) - reactor.run() +import sys +from PyQt5 import QtCore, QtWidgets, QtGui +import pyqtgraph as pg +from TraceListWidget import TraceList +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.internet.task import LoopingCall +import itertools +from Dataset import Dataset +import queue + +import numpy as np +from numpy import random + +import sys + +sys.settrace(None) +class artistParameters(): + def __init__(self, artist, dataset, index, shown): + self.artist = artist + self.dataset = dataset + self.index = index + self.shown = shown + self.last_update = 0 # update counter in the Dataset object + # only redraw if the dataset has a higher + # update count + +class Graph_PyQtGraph(QtWidgets.QWidget): + def __init__(self, config, reactor, cxn = None, parent=None): + super(Graph_PyQtGraph, self).__init__(parent) + from labrad.units import WithUnit as U + self.U = U + self.cxn = cxn + #todo: undo + #self.pv = self.cxn.parametervault + self.reactor = reactor + self.artists = {} + self.should_stop = False + self.name = config.name + self.vline_name = config.vline + self.vline_param = config.vline_param + self.hline_name = config.hline + self.hline_param = config.hline_param + self.show_points = config.show_points + self.grid_on = config.grid_on + self.scatter_plot = config.scatter_plot + + self.dataset_queue = queue.Queue(config.max_datasets) + + self.live_update_loop = LoopingCall(self.update_figure) + self.live_update_loop.start(0) + + colors = ['r', 'g', 'y', 'c', 'm', 'w'] + self.colorChooser = itertools.cycle(colors) + self.initUI() + + @inlineCallbacks + def initUI(self): + self.tracelist = TraceList(self) + self.pw = pg.PlotWidget() + if self.vline_name: + self.inf = pg.InfiniteLine(movable=True, angle=90, + label=self.vline_name + '{value:0.0f}', + labelOpts={'position': 0.9, + 'color': (200, 200, 100), + 'fill': (200, 200, 200, 50), + 'movable': True}) + init_value = yield self.get_init_vline() + self.inf.setValue(init_value) + self.inf.setPen(width=5.0) + + if self.hline_name: + self.inf = pg.InfiniteLine(movable=True, angle=0, + label=self.hline_name + '{value:0.0f}', + labelOpts={'position': 0.9, + 'color': (200, 200, 100), + 'fill': (200, 200, 200, 50), + 'movable': True}) + init_value = yield self.get_init_hline() + self.inf.setValue(init_value) + self.inf.setPen(width=5.0) + + self.coords = QtWidgets.QLabel('') + self.title = QtWidgets.QLabel(self.name) + frame = QtWidgets.QFrame() + splitter = QtWidgets.QSplitter() + splitter.addWidget(self.tracelist) + hbox = QtWidgets.QHBoxLayout() + vbox = QtWidgets.QVBoxLayout() + vbox.addWidget(self.title) + vbox.addWidget(self.pw) + vbox.addWidget(self.coords) + frame.setLayout(vbox) + splitter.addWidget(frame) + hbox.addWidget(splitter) + self.setLayout(hbox) + #self.legend = self.pw.addLegend() + self.tracelist.itemChanged.connect(self.checkboxChanged) + self.pw.plot([],[]) + vb = self.pw.plotItem.vb + self.img = pg.ImageItem() + vb.addItem(self.img) + if self.vline_name: + vb.addItem(self.inf) + self.inf.sigPositionChangeFinished.connect(self.vline_changed) + + if self.hline_name: + vb.addItem(self.inf) + self.inf.sigPositionChangeFinished.connect(self.hline_changed) + + self.pw.scene().sigMouseMoved.connect(self.mouseMoved) + self.pw.sigRangeChanged.connect(self.rangeChanged) + + def getItemColor(self, color): + color_dict = {"r": QtGui.QColor(QtCore.Qt.red).lighter(130), + "g": QtGui.QColor(QtCore.Qt.green), + "y": QtGui.QColor(QtCore.Qt.yellow), + "c": QtGui.QColor(QtCore.Qt.cyan), + "m": QtGui.QColor(QtCore.Qt.magenta).lighter(120), + "w": QtGui.QColor(QtCore.Qt.white)} + return color_dict[color] + + def update_figure(self): + for ident, params in self.artists.items(): + if params.shown: + try: + ds = params.dataset + index = params.index + current_update = ds.updateCounter + if params.last_update < current_update: + x = ds.data[:,0] + y = ds.data[:,index+1] + params.last_update = current_update + params.artist.setData(x,y) + except: pass + + def add_artist(self, ident, dataset, index, no_points = False): + ''' + no_points is an override parameter to the global show_points setting. + It is to allow data fits to be plotted without points + ''' + new_color = next(self.colorChooser) + if self.show_points and not no_points: + line = self.pw.plot([], [], symbol='o', symbolBrush=self.getItemColor(new_color), + name=ident, pen = self.getItemColor(new_color), connect=self.scatter_plot) + else: + line = self.pw.plot([], [], pen = self.getItemColor(new_color), name = ident) + if self.grid_on: + self.pw.showGrid(x=True, y=True) + self.artists[ident] = artistParameters(line, dataset, index, True) + self.tracelist.addTrace(ident, new_color) + + def remove_artist(self, ident): + try: + artist = self.artists[ident].artist + self.pw.removeItem(artist) + self.tracelist.removeTrace(ident) + self.artists[ident].shown = False + try: + del self.artists[ident] + except KeyError: + pass + except Exception as e: + print("remove failed") + + def display(self, ident, shown): + try: + artist = self.artists[ident].artist + if shown: + self.pw.addItem(artist) + self.artists[ident].shown = True + else: + self.pw.removeItem(artist) + self.artists[ident].shown = False + except KeyError: + raise Exception('404 Artist not found') + + def checkboxChanged(self): + for ident, item in self.tracelist.trace_dict.items(): + try: + if item.checkState() and not self.artists[ident].shown: + self.display(ident, True) + if not item.checkState() and self.artists[ident].shown: + self.display(ident, False) + except KeyError: # this means the artist has been deleted. + pass + + def rangeChanged(self): + + lims = self.pw.viewRange() + self.pointsToKeep = lims[0][1] - lims[0][0] + self.current_limits = [lims[0][0], lims[0][1]] + + @inlineCallbacks + def add_dataset(self, dataset): + try: + self.dataset_queue.put(dataset, block=False) + except queue.Full: + remove_ds = self.dataset_queue.get() + self.remove_dataset(remove_ds) + self.dataset_queue.put(dataset, block=False) + labels = yield dataset.getLabels() + for i, label in enumerate(labels): + self.add_artist(label, dataset, i) + + @inlineCallbacks + def remove_dataset(self, dataset): + labels = yield dataset.getLabels() + for label in labels: + self.remove_artist(label) + + def set_xlimits(self, limits): + self.pw.setXRange(limits[0], limits[1]) + self.current_limits = limits + + def set_ylimits(self, limits): + self.pw.setYRange(limits[0],limits[1]) + + def mouseMoved(self, pos): + #print("Image position:", self.img.mapFromScene(pos)) + pnt = self.img.mapFromScene(pos) + string = '(' + str(pnt.x()) + ' , ' + str(pnt.y()) + ')' + self.coords.setText(string) + + @inlineCallbacks + def get_init_vline(self): + init_vline = yield self.pv.get_parameter(self.vline_param[0], + self.vline_param[1]) + returnValue(init_vline) + + @inlineCallbacks + def get_init_hline(self): + init_hline = yield self.pv.get_parameter(self.hline_param[0], + self.hline_param[1]) + returnValue(init_hline) + + @inlineCallbacks + def vline_changed(self, sig): + val = self.inf.value() + param = yield self.pv.get_parameter(self.vline_param[0], self.vline_param[1]) + units = param.units + val = self.U(val, units) + yield self.pv.set_parameter(self.vline_param[0], self.vline_param[1], val) + + @inlineCallbacks + def hline_changed(self, sig): + val = self.inf.value() + param = yield self.pv.get_parameter(self.hline_param[0], self.hline_param[1]) + units = param.units + val = self.U(val, units) + yield self.pv.set_parameter(self.hline_param[0], self.hline_param[1], val) + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + import qt5reactor + qt5reactor.install() + from twisted.internet import reactor + main = Graph_PyQtGraph('example', reactor) + main.show() + reactor.run() + #sys.exit(app.exec_()) diff --git a/GraphWindow.py b/GraphWindow.py index d926424..9ff31b3 100644 --- a/GraphWindow.py +++ b/GraphWindow.py @@ -1,72 +1,73 @@ -''' -Window for holding Graphs -''' -import sys -from PyQt4 import QtGui -import GUIConfig -from GraphWidgetPyQtGraph import Graph_PyQtGraph as Graph -from HistWidgetPyQtGraph import Hist_PyQtGraph as Hist -from ScrollingGraphWidgetPyQtGraph import ScrollingGraph_PyQtGraph as ScrollingGraph -from ImageWidget import imageWidget as ImageGraph -from GridGraphWindow import GridGraphWindow - -class GraphWindow(QtGui.QTabWidget): - def __init__(self, reactor, cxn = None, parent=None): - super(GraphWindow, self).__init__(parent) - self.cxn = cxn - self.reactor = reactor - self.initUI() - self.show() - - def initUI(self): - reactor = self.reactor - - self.graphDict = {} - self.tabDict = {} - - for gc in GUIConfig.tabs: - gcli = gc.config_list - gli = [] - for config in gcli: - name = config.name - max_ds = config.max_datasets - if config.isScrolling: - g = ScrollingGraph(config, reactor, self.cxn) - elif config.isImages: - g = ImageGraph(config, reactor) - self.graphDict[name] = g - gli.append(g) - continue - elif config.isHist: - g = Hist(config, reactor, self.cxn) - self.graphDict[name] = g - gli.append(g) - continue - else: - g = Graph(config, reactor, self.cxn) - g.set_ylimits(config.ylim) - self.graphDict[name] = g - gli.append(g) - widget = GridGraphWindow(gli, gc.row_list, gc.column_list, reactor) - self.tabDict[name] = widget - self.addTab(widget, gc.tab) - self.setMovable(True) - - - def insert_tab(self, t): - g = Graph(t, reactor) - self.graphDict[t] = g - self.addTab(g, t) - - def closeEvent(self, x): - self.reactor.stop() - -if __name__ == '__main__': - app = QtGui.QApplication(sys.argv) - import qt4reactor - qt4reactor.install() - from twisted.internet import reactor - main = GraphWindow(reactor) - main.show() - #sys.exit(app.exec_()) - reactor.run() +''' +Window for holding Graphs +''' +import sys +import GUIConfig + +from PyQt5 import QtCore, QtWidgets + +from GraphWidgetPyQtGraph import Graph_PyQtGraph as Graph +from HistWidgetPyQtGraph import Hist_PyQtGraph as Hist +from ScrollingGraphWidgetPyQtGraph import ScrollingGraph_PyQtGraph as ScrollingGraph +from ImageWidget import imageWidget as ImageGraph +from GridGraphWindow import GridGraphWindow + +class GraphWindow(QtWidgets.QTabWidget): + def __init__(self, reactor, cxn = None, parent=None): + super(GraphWindow, self).__init__(parent) + self.cxn = cxn + self.reactor = reactor + self.initUI() + self.show() + + def initUI(self): + reactor = self.reactor + + self.graphDict = {} + self.tabDict = {} + + for gc in GUIConfig.tabs: + gcli = gc.config_list + gli = [] + for config in gcli: + name = config.name + max_ds = config.max_datasets + if config.isScrolling: + g = ScrollingGraph(config, reactor, self.cxn) + elif config.isImages: + g = ImageGraph(config, reactor) + self.graphDict[name] = g + gli.append(g) + continue + elif config.isHist: + g = Hist(config, reactor, self.cxn) + self.graphDict[name] = g + gli.append(g) + continue + else: + g = Graph(config, reactor, self.cxn) + g.set_ylimits(config.ylim) + self.graphDict[name] = g + gli.append(g) + widget = GridGraphWindow(gli, gc.row_list, gc.column_list, reactor) + self.tabDict[name] = widget + self.addTab(widget, gc.tab) + self.setMovable(True) + + + def insert_tab(self, t): + g = Graph(t, reactor) + self.graphDict[t] = g + self.addTab(g, t) + + def closeEvent(self, x): + self.reactor.stop() + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + import qt5reactor + qt5reactor.install() + main = GraphWindow(reactor) + main.show() + #sys.exit(app.exec_()) + reactor.run() diff --git a/GridGraphWindow.py b/GridGraphWindow.py index e2de645..2ffae1d 100644 --- a/GridGraphWindow.py +++ b/GridGraphWindow.py @@ -1,26 +1,26 @@ -''' -Window containing a grid of graphs -''' -import sys -from PyQt4 import QtGui -from GraphWidgetPyQtGraph import Graph_PyQtGraph as Graph -from ScrollingGraphWidgetPyQtGraph import ScrollingGraph_PyQtGraph as ScrollingGraph -import GUIConfig -from twisted.internet.defer import Deferred, inlineCallbacks, returnValue -from twisted.internet.task import LoopingCall -from twisted.internet.threads import blockingCallFromThread - -class GridGraphWindow(QtGui.QWidget): - def __init__(self, g_list, row_list, column_list, reactor, parent=None): - super(GridGraphWindow, self).__init__(parent) - self.reactor = reactor - self.initUI(g_list, row_list, column_list) - self.show() - - def initUI(self, g_list, row_list, column_list): - reactor = self.reactor - layout = QtGui.QGridLayout() - for k in range(len(g_list)): - layout.addWidget(g_list[k], row_list[k], column_list[k]) - self.setLayout(layout) - +''' +Window containing a grid of graphs +''' +import sys +from PyQt5 import QtWidgets +from GraphWidgetPyQtGraph import Graph_PyQtGraph as Graph +from ScrollingGraphWidgetPyQtGraph import ScrollingGraph_PyQtGraph as ScrollingGraph +import GUIConfig +from twisted.internet.defer import Deferred, inlineCallbacks, returnValue +from twisted.internet.task import LoopingCall +from twisted.internet.threads import blockingCallFromThread + +class GridGraphWindow(QtWidgets.QWidget): + def __init__(self, g_list, row_list, column_list, reactor, parent=None): + super(GridGraphWindow, self).__init__(parent) + self.reactor = reactor + self.initUI(g_list, row_list, column_list) + self.show() + + def initUI(self, g_list, row_list, column_list): + reactor = self.reactor + layout = QtWidgets.QGridLayout() + for k in range(len(g_list)): + layout.addWidget(g_list[k], row_list[k], column_list[k]) + self.setLayout(layout) + diff --git a/HistWidgetPyQtGraph.py b/HistWidgetPyQtGraph.py index 1988505..16c38b8 100644 --- a/HistWidgetPyQtGraph.py +++ b/HistWidgetPyQtGraph.py @@ -1,218 +1,218 @@ -import sys -from PyQt4 import QtGui, QtCore -import pyqtgraph as pg -from TraceListWidget import TraceList -from twisted.internet.defer import inlineCallbacks, returnValue -from twisted.internet.task import LoopingCall -import itertools -import Queue - - -class artistParameters(): - def __init__(self, artist, dataset, index, shown): - self.artist = artist - self.dataset = dataset - self.index = index - self.shown = shown - self.last_update = 0 # update counter in the Dataset object - # only redraw if the dataset has a higher - # update count - - -class Hist_PyQtGraph(QtGui.QWidget): - def __init__(self, config, reactor, cxn=None, parent=None): - super(Hist_PyQtGraph, self).__init__(parent) - self.cxn = cxn - self.pv = self.cxn.parametervault - self.reactor = reactor - self.artists = {} - self.should_stop = False - self.name = config.name - self.vline_name = config.vline - self.vline_param = config.vline_param - - self.dataset_queue = Queue.Queue(config.max_datasets) - - self.live_update_loop = LoopingCall(self.update_figure) - self.live_update_loop.start(0) - - colors = [(255, 0, 0, 80), - (0, 255, 0, 80), - (255, 255, 0, 80), - (0, 255, 255, 80), - (255, 0, 255, 80), - (255, 255, 255, 80)] - self.colorChooser = itertools.cycle(colors) - self.initUI() - - @inlineCallbacks - def initUI(self): - self.tracelist = TraceList(self) - self.pw = pg.PlotWidget() - if self.vline_name: - self.inf = pg.InfiniteLine(movable=True, angle=90, - label=self.vline_name + '{value:0.0f}', - labelOpts={'position': 0.9, - 'color': (200, 200, 100), - 'fill': (200, 200, 200, 50), - 'movable': True}) - init_value = yield self.get_init_vline() - self.inf.setValue(init_value) - self.inf.setPen(width=5.0) - self.coords = QtGui.QLabel('') - self.title = QtGui.QLabel(self.name) - frame = QtGui.QFrame() - splitter = QtGui.QSplitter() - splitter.addWidget(self.tracelist) - hbox = QtGui.QHBoxLayout() - vbox = QtGui.QVBoxLayout() - vbox.addWidget(self.title) - vbox.addWidget(self.pw) - vbox.addWidget(self.coords) - frame.setLayout(vbox) - splitter.addWidget(frame) - hbox.addWidget(splitter) - self.setLayout(hbox) - #self.legend = self.pw.addLegend() - self.tracelist.itemChanged.connect(self.checkboxChanged) - self.pw.plot([],[]) - vb = self.pw.plotItem.vb - self.img = pg.ImageItem() - vb.addItem(self.img) - if self.vline_name: - vb.addItem(self.inf) - self.inf.sigPositionChangeFinished.connect(self.vline_changed) - - self.pw.scene().sigMouseMoved.connect(self.mouseMoved) - self.pw.sigRangeChanged.connect(self.rangeChanged) - - def getItemColor(self, color): - - color_dict = {(255, 0, 0, 80): QtGui.QColor(QtCore.Qt.red).lighter(130), - (0, 255, 0, 80): QtGui.QColor(QtCore.Qt.green), - (255, 255, 0, 80): QtGui.QColor(QtCore.Qt.yellow), - (0, 255, 255, 80): QtGui.QColor(QtCore.Qt.cyan), - (255, 0, 255, 80): QtGui.QColor(QtCore.Qt.magenta).lighter(120), - (255, 255, 255, 80): QtGui.QColor(QtCore.Qt.white)} - return color_dict[color] - - def update_figure(self): - for ident, params in self.artists.iteritems(): - if params.shown: - try: - ds = params.dataset - index = params.index - current_update = ds.updateCounter - if params.last_update < current_update: - x = ds.data[:,0] - x = list(x) + [x[-1] + 1] - y = ds.data[:,index+1] - params.last_update = current_update - params.artist.setData(x,y) - except: pass - - def add_artist(self, ident, dataset, index, no_points = False): - ''' - no_points is an override parameter to the global show_points setting. - It is to allow data fits to be plotted without points - ''' - new_color = self.colorChooser.next() - hist = pg.PlotCurveItem([0,1],[1], stepMode=True, fillLevel=0, brush=new_color, pen=None) - self.artists[ident] = artistParameters(hist, dataset, index, True) - self.pw.addItem(hist) - self.tracelist.addTrace(ident, new_color) - - def remove_artist(self, ident): - try: - artist = self.artists[ident].artist - self.pw.removeItem(artist) - self.tracelist.removeTrace(ident) - self.artists[ident].shown = False - try: - del self.artists[ident] - except KeyError: - pass - except: - print "remove failed" - - def display(self, ident, shown): - try: - artist = self.artists[ident].artist - if shown: - self.pw.addItem(artist) - self.artists[ident].shown = True - else: - self.pw.removeItem(artist) - self.artists[ident].shown = False - except KeyError: - raise Exception('404 Artist not found') - - def checkboxChanged(self): - for ident, item in self.tracelist.trace_dict.iteritems(): - try: - if item.checkState() and not self.artists[ident].shown: - self.display(ident, True) - if not item.checkState() and self.artists[ident].shown: - self.display(ident, False) - except KeyError: # this means the artist has been deleted. - pass - - def rangeChanged(self): - - lims = self.pw.viewRange() - self.pointsToKeep = lims[0][1] - lims[0][0] - self.current_limits = [lims[0][0], lims[0][1]] - - @inlineCallbacks - def add_dataset(self, dataset): - try: - self.dataset_queue.put(dataset, block=False) - except Queue.Full: - remove_ds = self.dataset_queue.get() - self.remove_dataset(remove_ds) - self.dataset_queue.put(dataset, block=False) - labels = yield dataset.getLabels() - for i, label in enumerate(labels): - self.add_artist(label, dataset, i) - - @inlineCallbacks - def remove_dataset(self, dataset): - labels = yield dataset.getLabels() - for label in labels: - self.remove_artist(label) - - def set_xlimits(self, limits): - self.pw.setXRange(limits[0], limits[1]) - self.current_limits = limits - - def set_ylimits(self, limits): - self.pw.setYRange(limits[0], limits[1]) - - def mouseMoved(self, pos): - pnt = self.img.mapFromScene(pos) - string = '(' + str(pnt.x()) + ' , ' + str(pnt.y()) + ')' - self.coords.setText(string) - - @inlineCallbacks - def get_init_vline(self): - init_vline = yield self.pv.get_parameter(self.vline_param[0], - self.vline_param[1]) - print init_vline - returnValue(init_vline) - - @inlineCallbacks - def vline_changed(self, sig): - val = self.inf.value() - val = int(round(val)) - yield self.pv.set_parameter(self.vline_param[0], - self.vline_param[1], val) - - -if __name__ == '__main__': - app = QtGui.QApplication(sys.argv) - import qt4reactor - qt4reactor.install() - from twisted.internet import reactor - main = Hist_PyQtGraph('example', reactor) - main.show() - reactor.run() +import sys +from PyQt5 import QtWidgets, QtCore, QtGui +import pyqtgraph as pg +from TraceListWidget import TraceList +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.internet.task import LoopingCall +import itertools +import queue + + +class artistParameters(): + def __init__(self, artist, dataset, index, shown): + self.artist = artist + self.dataset = dataset + self.index = index + self.shown = shown + self.last_update = 0 # update counter in the Dataset object + # only redraw if the dataset has a higher + # update count + + +class Hist_PyQtGraph(QtWidgets.QWidget): + def __init__(self, config, reactor, cxn=None, parent=None): + super(Hist_PyQtGraph, self).__init__(parent) + self.cxn = cxn + self.pv = self.cxn.parametervault + self.reactor = reactor + self.artists = {} + self.should_stop = False + self.name = config.name + self.vline_name = config.vline + self.vline_param = config.vline_param + + self.dataset_queue = queue.Queue(config.max_datasets) + + self.live_update_loop = LoopingCall(self.update_figure) + self.live_update_loop.start(0) + + colors = [(255, 0, 0, 80), + (0, 255, 0, 80), + (255, 255, 0, 80), + (0, 255, 255, 80), + (255, 0, 255, 80), + (255, 255, 255, 80)] + self.colorChooser = itertools.cycle(colors) + self.initUI() + + @inlineCallbacks + def initUI(self): + self.tracelist = TraceList(self) + self.pw = pg.PlotWidget() + if self.vline_name: + self.inf = pg.InfiniteLine(movable=True, angle=90, + label=self.vline_name + '{value:0.0f}', + labelOpts={'position': 0.9, + 'color': (200, 200, 100), + 'fill': (200, 200, 200, 50), + 'movable': True}) + init_value = yield self.get_init_vline() + self.inf.setValue(init_value) + self.inf.setPen(width=5.0) + self.coords = QtWidgets.QLabel('') + self.title = QtWidgets.QLabel(self.name) + frame = QtWidgets.QFrame() + splitter = QtWidgets.QSplitter() + splitter.addWidget(self.tracelist) + hbox = QtWidgets.QHBoxLayout() + vbox = QtWidgets.QVBoxLayout() + vbox.addWidget(self.title) + vbox.addWidget(self.pw) + vbox.addWidget(self.coords) + frame.setLayout(vbox) + splitter.addWidget(frame) + hbox.addWidget(splitter) + self.setLayout(hbox) + #self.legend = self.pw.addLegend() + self.tracelist.itemChanged.connect(self.checkboxChanged) + self.pw.plot([],[]) + vb = self.pw.plotItem.vb + self.img = pg.ImageItem() + vb.addItem(self.img) + if self.vline_name: + vb.addItem(self.inf) + self.inf.sigPositionChangeFinished.connect(self.vline_changed) + + self.pw.scene().sigMouseMoved.connect(self.mouseMoved) + self.pw.sigRangeChanged.connect(self.rangeChanged) + + def getItemColor(self, color): + + color_dict = {(255, 0, 0, 80): QtGui.QColor(QtCore.Qt.red).lighter(130), + (0, 255, 0, 80): QtGui.QColor(QtCore.Qt.green), + (255, 255, 0, 80): QtGui.QColor(QtCore.Qt.yellow), + (0, 255, 255, 80): QtGui.QColor(QtCore.Qt.cyan), + (255, 0, 255, 80): QtGui.QColor(QtCore.Qt.magenta).lighter(120), + (255, 255, 255, 80): QtGui.QColor(QtCore.Qt.white)} + return color_dict[color] + + def update_figure(self): + for ident, params in self.artists.items(): + if params.shown: + try: + ds = params.dataset + index = params.index + current_update = ds.updateCounter + if params.last_update < current_update: + x = ds.data[:,0] + x = list(x) + [x[-1] + 1] + y = ds.data[:,index+1] + params.last_update = current_update + params.artist.setData(x,y) + except: pass + + def add_artist(self, ident, dataset, index, no_points = False): + ''' + no_points is an override parameter to the global show_points setting. + It is to allow data fits to be plotted without points + ''' + new_color = self.colorChooser.next() + hist = pg.PlotCurveItem([0,1],[1], stepMode=True, fillLevel=0, brush=new_color, pen=None) + self.artists[ident] = artistParameters(hist, dataset, index, True) + self.pw.addItem(hist) + self.tracelist.addTrace(ident, new_color) + + def remove_artist(self, ident): + try: + artist = self.artists[ident].artist + self.pw.removeItem(artist) + self.tracelist.removeTrace(ident) + self.artists[ident].shown = False + try: + del self.artists[ident] + except KeyError: + pass + except Exception as e: + print("remove failed") + + def display(self, ident, shown): + try: + artist = self.artists[ident].artist + if shown: + self.pw.addItem(artist) + self.artists[ident].shown = True + else: + self.pw.removeItem(artist) + self.artists[ident].shown = False + except KeyError: + raise Exception('404 Artist not found') + + def checkboxChanged(self): + for ident, item in self.tracelist.trace_dict.items(): + try: + if item.checkState() and not self.artists[ident].shown: + self.display(ident, True) + if not item.checkState() and self.artists[ident].shown: + self.display(ident, False) + except KeyError: # this means the artist has been deleted. + pass + + def rangeChanged(self): + + lims = self.pw.viewRange() + self.pointsToKeep = lims[0][1] - lims[0][0] + self.current_limits = [lims[0][0], lims[0][1]] + + @inlineCallbacks + def add_dataset(self, dataset): + try: + self.dataset_queue.put(dataset, block=False) + except queue.Full: + remove_ds = self.dataset_queue.get() + self.remove_dataset(remove_ds) + self.dataset_queue.put(dataset, block=False) + labels = yield dataset.getLabels() + for i, label in enumerate(labels): + self.add_artist(label, dataset, i) + + @inlineCallbacks + def remove_dataset(self, dataset): + labels = yield dataset.getLabels() + for label in labels: + self.remove_artist(label) + + def set_xlimits(self, limits): + self.pw.setXRange(limits[0], limits[1]) + self.current_limits = limits + + def set_ylimits(self, limits): + self.pw.setYRange(limits[0], limits[1]) + + def mouseMoved(self, pos): + pnt = self.img.mapFromScene(pos) + string = '(' + str(pnt.x()) + ' , ' + str(pnt.y()) + ')' + self.coords.setText(string) + + @inlineCallbacks + def get_init_vline(self): + init_vline = yield self.pv.get_parameter(self.vline_param[0], + self.vline_param[1]) + print(init_vline) + returnValue(init_vline) + + @inlineCallbacks + def vline_changed(self, sig): + val = self.inf.value() + val = int(round(val)) + yield self.pv.set_parameter(self.vline_param[0], + self.vline_param[1], val) + + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + import qt5reactor + qt5reactor.install() + from twisted.internet import reactor + main = Hist_PyQtGraph('example', reactor) + main.show() + reactor.run() diff --git a/ImageWidget.py b/ImageWidget.py index ba8607e..93a9843 100644 --- a/ImageWidget.py +++ b/ImageWidget.py @@ -1,102 +1,101 @@ -import sys -from PyQt4 import QtGui, QtCore -import pyqtgraph as pg -from TraceListWidget import TraceList -from twisted.internet.defer import inlineCallbacks -from twisted.internet.task import LoopingCall -import itertools -import Queue - - -class imageWidget(QtGui.QWidget): - - def __init__(self, config, reactor, parent=None, cxn=None): - super(imageWidget, self).__init__(parent) - - self.reactor = reactor - self.artists = {} - self.should_stop = False - self.name = config.name - self.image_list = [] - self.image_index = 0 - - self.initUI() - - def initUI(self): - self.plt = plt = pg.PlotItem() - self.imv = pg.ImageView(view = self.plt) - plt.showAxis('top') - plt.hideAxis('bottom') - plt.setAspectLocked(True) - self.vLine = pg.InfiniteLine(angle=90, movable=False) - self.hLine = pg.InfiniteLine(angle=0, movable=False) - plt.addItem(self.vLine, ignoreBounds=True) - plt.addItem(self.hLine, ignoreBounds=True) - self.plt.scene().sigMouseClicked.connect(self.mouse_clicked) - self.title = QtGui.QLabel(self.name) - self.next_button = QtGui.QPushButton('>') - self.prev_button = QtGui.QPushButton('<') - self.next_button.clicked.connect(self.on_next) - self.prev_button.clicked.connect(self.on_prev) - layout = QtGui.QGridLayout() - layout.addWidget(self.title, 0,0) - layout.addWidget(self.prev_button, 1,0) - layout.addWidget(self.next_button, 1,1) - layout.addWidget(self.imv, 2,0, 20,2) - self.setLayout(layout) - - def update_image(self, data, image_size, name): - image = data.reshape(image_size[0], image_size[1]) - if len(self.image_list) == 0: - self.imv.setImage(image) - else: - self.imv.setImage(image, autoRange=False, autoLevels=False, autoHistogramRange=False) - self.image_list.append([image, self.name + ' ' + name]) - self.image_index = len(self.image_list) - 1 - if len(self.image_list) > 100: - del self.image_list[0] - self.title.setText(self.name + ' ' + name) - - def on_next(self): - try: - if self.image_index < len(self.image_list) -1: - self.image_index += 1 - self.imv.setImage(self.image_list[self.image_index][0], autoRange=False, autoLevels=False, autoHistogramRange=False) - self.title.setText(self.image_list[self.image_index][1]) - else: - pass - - except: - print 'Could not access index: ' + str(self.image_index) - - def on_prev(self): - try: - if self.image_index > 0: - self.image_index -= 1 - self.imv.setImage(self.image_list[self.image_index][0], autoRange=False, autoLevels=False, autoHistogramRange=False) - self.title.setText(self.image_list[self.image_index][1]) - else: - pass - except: - print 'Could not access index: ' + str(self.image_index) - - def mouse_clicked(self, event): - ''' - draws the cross at the position of a double click - ''' - pos = event.pos() - if self.plt.sceneBoundingRect().contains(pos) and event.double(): - #only on double clicks within bounds - mousePoint = self.plt.vb.mapToView(pos) - self.vLine.setPos(mousePoint.x()) - self.hLine.setPos(mousePoint.y()) - -if __name__ == '__main__': - app = QtGui.QApplication(sys.argv) - import qt4reactor - qt4reactor.install() - from twisted.internet import reactor - main = imageWidget('example', reactor) - main.show() - #sys.exit(app.exec_()) +import sys +from PyQt5 import QtWidgets +import pyqtgraph as pg +from TraceListWidget import TraceList +from twisted.internet.defer import inlineCallbacks +from twisted.internet.task import LoopingCall +import itertools +import queue + + +class imageWidget(QtWidgets.QWidget): + + def __init__(self, config, reactor, parent=None, cxn=None): + super(imageWidget, self).__init__(parent) + + self.reactor = reactor + self.artists = {} + self.should_stop = False + self.name = config.name + self.image_list = [] + self.image_index = 0 + + self.initUI() + + def initUI(self): + self.plt = plt = pg.PlotItem() + self.imv = pg.ImageView(view = self.plt) + plt.showAxis('top') + plt.hideAxis('bottom') + plt.setAspectLocked(True) + self.vLine = pg.InfiniteLine(angle=90, movable=False) + self.hLine = pg.InfiniteLine(angle=0, movable=False) + plt.addItem(self.vLine, ignoreBounds=True) + plt.addItem(self.hLine, ignoreBounds=True) + self.plt.scene().sigMouseClicked.connect(self.mouse_clicked) + self.title = QtWidgets.QLabel(self.name) + self.next_button = QtWidgets.QPushButton('>') + self.prev_button = QtWidgets.QPushButton('<') + self.next_button.clicked.connect(self.on_next) + self.prev_button.clicked.connect(self.on_prev) + layout = QtWidgets.QGridLayout() + layout.addWidget(self.title, 0,0) + layout.addWidget(self.prev_button, 1,0) + layout.addWidget(self.next_button, 1,1) + layout.addWidget(self.imv, 2,0, 20,2) + self.setLayout(layout) + + def update_image(self, data, image_size, name): + image = data.reshape(image_size[0], image_size[1]) + if len(self.image_list) == 0: + self.imv.setImage(image) + else: + self.imv.setImage(image, autoRange=False, autoLevels=False, autoHistogramRange=False) + self.image_list.append([image, self.name + ' ' + name]) + self.image_index = len(self.image_list) - 1 + if len(self.image_list) > 100: + del self.image_list[0] + self.title.setText(self.name + ' ' + name) + + def on_next(self): + try: + if self.image_index < len(self.image_list) -1: + self.image_index += 1 + self.imv.setImage(self.image_list[self.image_index][0], autoRange=False, autoLevels=False, autoHistogramRange=False) + self.title.setText(self.image_list[self.image_index][1]) + else: + pass + + except: + print('Could not access index: ' + str(self.image_index)) + + def on_prev(self): + try: + if self.image_index > 0: + self.image_index -= 1 + self.imv.setImage(self.image_list[self.image_index][0], autoRange=False, autoLevels=False, autoHistogramRange=False) + self.title.setText(self.image_list[self.image_index][1]) + else: + pass + except: + print('Could not access index: ' + str(self.image_index)) + + def mouse_clicked(self, event): + ''' + draws the cross at the position of a double click + ''' + pos = event.pos() + if self.plt.sceneBoundingRect().contains(pos) and event.double(): + #only on double clicks within bounds + mousePoint = self.plt.vb.mapToView(pos) + self.vLine.setPos(mousePoint.x()) + self.hLine.setPos(mousePoint.y()) + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + import qt5reactor + qt5reactor.install() + main = imageWidget('Example', reactor) + main.show() + #sys.exit(app.exec_()) reactor.run() \ No newline at end of file diff --git a/ParameterListWidget.py b/ParameterListWidget.py index 4aedbbe..203d816 100644 --- a/ParameterListWidget.py +++ b/ParameterListWidget.py @@ -1,22 +1,22 @@ -import sys -from PyQt4 import QtGui, QtCore -from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred - -class ParameterList(QtGui.QWidget): - - def __init__(self, dataset): - super(ParameterList, self).__init__() - self.dataset = dataset - mainLayout = QtGui.QVBoxLayout() - self.parameterListWidget = QtGui.QListWidget() - mainLayout.addWidget(self.parameterListWidget) - self.setWindowTitle(str(dataset.dataset_name))# + " " + str(dataset.directory)) - self.populate() - self.setLayout(mainLayout) - self.show() - - @inlineCallbacks - def populate(self): - parameters = yield self.dataset.getParameters() - self.parameterListWidget.clear() - self.parameterListWidget.addItems([str(x) for x in sorted(parameters)]) +import sys +from PyQt5 import QtWidgets +from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred + +class ParameterList(QtWidgets.QWidget): + + def __init__(self, dataset): + super(ParameterList, self).__init__() + self.dataset = dataset + mainLayout = QtWidgets.QVBoxLayout() + self.parameterListWidget = QtWidgets.QListWidget() + mainLayout.addWidget(self.parameterListWidget) + self.setWindowTitle(str(dataset.dataset_name))# + " " + str(dataset.directory)) + self.populate() + self.setLayout(mainLayout) + self.show() + + @inlineCallbacks + def populate(self): + parameters = yield self.dataset.getParameters() + self.parameterListWidget.clear() + self.parameterListWidget.addItems([str(x) for x in sorted(parameters)]) diff --git a/PredictSpectrumWidget.py b/PredictSpectrumWidget.py index 4e5e8b1..ec86287 100644 --- a/PredictSpectrumWidget.py +++ b/PredictSpectrumWidget.py @@ -1,294 +1,290 @@ -# import qt4reactor -# qt4reactor.install() -from PyQt4 import QtGui, QtCore -from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred -from fractions import Fraction -# from labrad import units as U -# from labrad.units import WithUnit -import numpy as np -# from common.abstractdevices.SD_tracker.SD_calculator import Transitions_SD as tracker - -class ParamInfo(): - ''' - Container for the widgets with - each row in the parameters table - ''' - def __init__(self, value): - self.value = value - -class PredictSpectrum(QtGui.QWidget): - - def __init__(self, parent): - super(PredictSpectrum, self).__init__() - # self.reactor=reactor - self.parent = parent - self.value_dict = {} - self.ident = 'Predicted Spectrum' - self.Ca_data = Transitions_SD() - self.initUI() - - def initUI(self): - self.setWindowTitle(self.ident) - mainLayout = QtGui.QVBoxLayout() - buttons = QtGui.QHBoxLayout() - - self.parameterTable = QtGui.QTableWidget() - self.parameterTable.setColumnCount(2) - - self.plotButton = QtGui.QPushButton('Plot', self) - - mainLayout.addWidget(self.parameterTable) - mainLayout.addLayout(buttons) - buttons.addWidget(self.plotButton) - - self.OPpos = QtGui.QCheckBox("Positive Manifold") - self.OPpos.setChecked(True) - mainLayout.addWidget(self.OPpos) - self.OPneg = QtGui.QCheckBox("Negative Manifold") - self.OPneg.setChecked(True) - mainLayout.addWidget(self.OPneg) - self.deltam0 = QtGui.QCheckBox("Delta m=0") - self.deltam0.setChecked(True) - mainLayout.addWidget(self.deltam0) - self.deltam1 = QtGui.QCheckBox("Delta m=1") - self.deltam1.setChecked(True) - mainLayout.addWidget(self.deltam1) - self.deltam2 = QtGui.QCheckBox("Delta m=2") - self.deltam2.setChecked(True) - mainLayout.addWidget(self.deltam2) - - self.plotButton.clicked.connect(self.onPlot) - - self.setupParameterTable() - self.setLayout(mainLayout) - self.show() - - def setupParameterTable(self): - - self.parameterTable.clear() - - headerLabels = QtCore.QStringList(['Parameter', 'Value']) - self.parameterTable.setHorizontalHeaderLabels(headerLabels) - self.parameterTable.horizontalHeader().setStretchLastSection(True) - - params = ['B Field', 'Line Center','Mode 1 Freq', 'Orders1', 'Mode 2 Freq', 'Orders2', 'Mode 3 Freq', 'Orders3', 'Micromotion', 'Drive Frequency'] - self.parameterTable.setRowCount(len(params)) - for i,p in enumerate(params): - - label = QtGui.QLabel(p) - value = QtGui.QDoubleSpinBox() - - self.value_dict[p] = ParamInfo(value) - - value.setDecimals(3) - value.setRange(-100, 100) - value.setValue(0) - - self.parameterTable.setCellWidget(i, 0, label) - self.parameterTable.setCellWidget(i, 1, value) - - def generate_spectrum(self): - ##must be in gauss and MHz!! - b_field = self.value_dict['B Field'].value.value() - line_center = self.value_dict['Line Center'].value.value() - mode_1 = self.value_dict['Mode 1 Freq'].value.value() - order1 = int(self.value_dict['Orders1'].value.value()) - mode_2 = self.value_dict['Mode 2 Freq'].value.value() - order2 = int(self.value_dict['Orders2'].value.value()) - mode_3 = self.value_dict['Mode 3 Freq'].value.value() - order3 = int(self.value_dict['Orders3'].value.value()) - drive_freq = self.value_dict['Drive Frequency'].value.value() - micromotion = int(self.value_dict['Micromotion'].value.value()) - - all_carriers = self.Ca_data.get_transition_energies(b_field*1e-4,line_center) #to Tesla and MHz - - print all_carriers - - #choose which carriers to include - included_lines = [] - if self.OPneg.isChecked() == True: - included_lines.extend([el for el in all_carriers if el[0][1] == '-']) - if self.OPpos.isChecked() == True: - included_lines.extend([el for el in all_carriers if el[0][1] == '+']) - - final_lines =[] - if self.deltam0.isChecked() == True: - final_lines.extend([el for el in included_lines if np.abs(float(el[0][1:3])-float(el[0][6:8])) == 0]) - if self.deltam1.isChecked() == True: - final_lines.extend([el for el in included_lines if np.abs(float(el[0][1:3])-float(el[0][6:8])) == 2]) - if self.deltam2.isChecked() == True: - final_lines.extend([el for el in included_lines if np.abs(float(el[0][1:3])-float(el[0][6:8])) == 4]) - - carriers = [carrier[1] for carrier in final_lines] - sideband_orders = [[i,j,k] for i in range(-order1,order1+1) for j in range(-order2,order2+1) for k in range(-order3,order3+1)] - sideband_freqs = [mode_1,mode_2,mode_3] - - - #add all secular sidebands - all_lines = [] - for freq in carriers: - for el in sideband_orders: - all_lines.append(((freq + sum(np.multiply(el,sideband_freqs))),sum(np.abs(el)))) - - #add driven sidebands - if micromotion: - micro_lines = [] - for el in all_lines: - freq,order = el - micro_lines.append((freq+drive_freq,0.5+order)) - micro_lines.append((freq-drive_freq,0.5+order)) - all_lines.extend(micro_lines) - - freqs = np.arange(-50,50,0.005) - spec = np.zeros_like(freqs) - for line in all_lines: - spec = np.add(spec,self.make_gaussian(line[0],freqs,line[1])) - data = np.zeros((len(freqs), 2)) - data[:,0] = freqs - data[:,1] = spec - - return data - - def make_gaussian(self,center,freqs,amplitude): - #takes a center and makes a guassian around that point - gauss = (0.5**amplitude)*np.exp(-(freqs-center)**2/(0.010**2)) - return gauss - - def onPlot(self): - ''' - Plot the manual parameters. See documentation - for plotFit() - ''' - - class dataset(): - def __init__(self, data): - self.data = data - self.updateCounter = 1 - - data = self.generate_spectrum() ####Ths is where we add the lorenzians - ds = dataset(data) - try: - # remove the previous plot - self.parent.parent.remove_artist(self.ident) - self.parent.parent.add_artist(self.ident, ds, 0, no_points = False) - except: - self.parent.parent.add_artist(self.ident, ds, 0, no_points = False) - - - def closeEvent(self, event): - self.parent.parent.remove_artist(self.ident) - - - -#everything must be in Gauss and MHz. Copied from SD scanner -class EnergyLevel(object): - - spectoscopic_notation = { - 'S': 0, - 'P': 1, - 'D': 2, - } - - spectoscopic_notation_rev = { - 0 : 'S', - 1 : 'P', - 2 : 'D', - } - - - def __init__(self, angular_momentum_l, total_angular_momentum_j, spin_s = '1/2'): - #convert spectroscopic notation to the spin number - if type(angular_momentum_l) == str: - angular_momentum_l = self.spectoscopic_notation[angular_momentum_l] - total_angular_momentum_j = Fraction(total_angular_momentum_j) - spin_s = Fraction(spin_s) - S = spin_s - self.L = L = angular_momentum_l - J = total_angular_momentum_j - lande_factor = self.lande_factor(S, L, J) - #sublevels are found, 2* self.J is always an integer, so can use numerator - self.sublevels_m = [-J + i for i in xrange( 1 + (2 * J).numerator)] - self.energy_scale = (lande_factor * 9.274e-24 / 6.626e-34) #1.4 MHz / gauss - - def lande_factor(self, S, L ,J): - '''computes the lande g factor''' - g = Fraction(3,2) + Fraction( S * (S + 1) - L * (L + 1) , 2 * J*(J + 1)) - return g - - def magnetic_to_energy(self, B): - '''given the magnitude of the magnetic field, returns all energies of all zeeman sublevels''' - energies = [(self.energy_scale * m * B) *1e-6 for m in self.sublevels_m] #put in MHz - representations = [self.frac_to_string(m) for m in self.sublevels_m] - return zip(self.sublevels_m,energies,representations) - - def frac_to_string(self, sublevel): - #helper class for converting energy levels to strings - sublevel = str(sublevel) - if not sublevel.startswith('-'): - sublevel = '+' + sublevel - together = self.spectoscopic_notation_rev[self.L] + sublevel - return together - -class EnergyLevel_CA_ion(EnergyLevel): - ''' - Class for describing the energy levels of Calcium Ions. This is specific to Ca+ because it uses - precisely measured g factors of the S and D states in the calculations. - ''' - - def lande_factor(self, S, L, J): - g_factor_S = 2.00225664 #Eur Phys JD 25 113-125 - g_factor_D = 1.2003340 #PRL 102, 023002 (2009) - if S == Fraction('1/2') and L == Fraction('0') and J == Fraction('1/2'): - g = g_factor_S - elif S == Fraction('1/2') and L == Fraction('2') and J == Fraction('5/2'): - g = g_factor_D - return g - -class Transitions_SD(object): - - S = EnergyLevel_CA_ion('S', '1/2') - D = EnergyLevel_CA_ion('D', '5/2') - allowed_transitions = [0,1,2] - - def transitions(self): - transitions = [] - for m_s,E_s,repr_s in self.S.magnetic_to_energy(0): - for m_d,E_d,repr_d in self.D.magnetic_to_energy(0): - if abs(m_d-m_s) in self.allowed_transitions: - name = repr_s + repr_d - transitions.append(name) - return transitions - - def get_transition_energies(self, B, zero_offset = 0.): - '''returns the transition enenrgies in MHz where zero_offset is the 0-field transition energy between S and D''' - ans = [] - for m_s,E_s,repr_s in self.S.magnetic_to_energy(B): - for m_d,E_d,repr_d in self.D.magnetic_to_energy(B): - if abs(m_d-m_s) in self.allowed_transitions: - name = repr_s + repr_d - diff = E_d - E_s - diff+= zero_offset - ans.append((name, diff)) - return ans - - def energies_to_magnetic_field(self, transitions): - #given two points in the form [(S-1/2D5+1/2, 1.0 MHz), (-1/2, 5+/2, 2.0 MHz)], calculates the magnetic field - try: - transition1, transition2 = transitions - except ValueError: - raise Exception ("Wrong number of inputs in energies_to_magnetic_field") - ms1,md1 = self.str_to_fractions(transition1[0]) - ms2,md2 = self.str_to_fractions(transition2[0]) - en1,en2 = transition1[1], transition2[1] - if abs(md1 - ms1) not in self.allowed_transitions or abs(md2 - ms2) not in self.allowed_transitions: - raise Exception ("Such transitions are not allowed") - s_scale = self.S.energy_scale - d_scale = self.D.energy_scale - B = (en2 - en1) / ( d_scale * ( md2 - md1) - s_scale * (ms2 - ms1) ) - B = B *1e4 #(to guass from tesla) - offset = en1 - (md1 * d_scale - ms1 * s_scale) * B - return B, offset - - def str_to_fractions(self, inp): - #takes S-1/2D5+1/2 and converts to Fraction(-1/2), Fraction(1/2) +from PyQt5 import QtWidgets, QtCore +from twisted.internet.defer import inlineCallbacks, returnValue, DeferredLock, Deferred +from fractions import Fraction +# from labrad import units as U +# from labrad.units import WithUnit +import numpy as np + +class ParamInfo(): + ''' + Container for the widgets with + each row in the parameters table + ''' + def __init__(self, value): + self.value = value + +class PredictSpectrum(QtWidgets.QWidget): + + def __init__(self, parent): + super(PredictSpectrum, self).__init__() + # self.reactor=reactor + self.parent = parent + self.value_dict = {} + self.ident = 'Predicted Spectrum' + self.Ca_data = Transitions_SD() + self.initUI() + + def initUI(self): + self.setWindowTitle(self.ident) + mainLayout = QtWidgets.QVBoxLayout() + buttons = QtWidgets.QHBoxLayout() + + self.parameterTable = QtWidgets.QTableWidget() + self.parameterTable.setColumnCount(2) + + self.plotButton = QtWidgets.QPushButton('Plot', self) + + mainLayout.addWidget(self.parameterTable) + mainLayout.addLayout(buttons) + buttons.addWidget(self.plotButton) + + self.OPpos = QtWidgets.QCheckBox("Positive Manifold") + self.OPpos.setChecked(True) + mainLayout.addWidget(self.OPpos) + self.OPneg = QtWidgets.QCheckBox("Negative Manifold") + self.OPneg.setChecked(True) + mainLayout.addWidget(self.OPneg) + self.deltam0 = QtWidgets.QCheckBox("Delta m=0") + self.deltam0.setChecked(True) + mainLayout.addWidget(self.deltam0) + self.deltam1 = QtWidgets.QCheckBox("Delta m=1") + self.deltam1.setChecked(True) + mainLayout.addWidget(self.deltam1) + self.deltam2 = QtWidgets.QCheckBox("Delta m=2") + self.deltam2.setChecked(True) + mainLayout.addWidget(self.deltam2) + + self.plotButton.clicked.connect(self.onPlot) + + self.setupParameterTable() + self.setLayout(mainLayout) + self.show() + + def setupParameterTable(self): + + self.parameterTable.clear() + headerLabels = ['Parameter', 'Value'] + self.parameterTable.setHorizontalHeaderLabels(headerLabels) + self.parameterTable.horizontalHeader().setStretchLastSection(True) + + params = ['B Field', 'Line Center','Mode 1 Freq', 'Orders1', 'Mode 2 Freq', 'Orders2', 'Mode 3 Freq', 'Orders3', 'Micromotion', 'Drive Frequency'] + self.parameterTable.setRowCount(len(params)) + for i,p in enumerate(params): + + label = QtWidgets.QLabel(p) + value = QtWidgets.QDoubleSpinBox() + + self.value_dict[p] = ParamInfo(value) + + value.setDecimals(3) + value.setRange(-100, 100) + value.setValue(0) + + self.parameterTable.setCellWidget(i, 0, label) + self.parameterTable.setCellWidget(i, 1, value) + + def generate_spectrum(self): + ##must be in gauss and MHz!! + b_field = self.value_dict['B Field'].value.value() + line_center = self.value_dict['Line Center'].value.value() + mode_1 = self.value_dict['Mode 1 Freq'].value.value() + order1 = int(self.value_dict['Orders1'].value.value()) + mode_2 = self.value_dict['Mode 2 Freq'].value.value() + order2 = int(self.value_dict['Orders2'].value.value()) + mode_3 = self.value_dict['Mode 3 Freq'].value.value() + order3 = int(self.value_dict['Orders3'].value.value()) + drive_freq = self.value_dict['Drive Frequency'].value.value() + micromotion = int(self.value_dict['Micromotion'].value.value()) + + all_carriers = self.Ca_data.get_transition_energies(b_field*1e-4,line_center) #to Tesla and MHz + + print(all_carriers) + + #choose which carriers to include + included_lines = [] + if self.OPneg.isChecked() == True: + included_lines.extend([el for el in all_carriers if el[0][1] == '-']) + if self.OPpos.isChecked() == True: + included_lines.extend([el for el in all_carriers if el[0][1] == '+']) + + final_lines =[] + if self.deltam0.isChecked() == True: + final_lines.extend([el for el in included_lines if np.abs(float(el[0][1:3])-float(el[0][6:8])) == 0]) + if self.deltam1.isChecked() == True: + final_lines.extend([el for el in included_lines if np.abs(float(el[0][1:3])-float(el[0][6:8])) == 2]) + if self.deltam2.isChecked() == True: + final_lines.extend([el for el in included_lines if np.abs(float(el[0][1:3])-float(el[0][6:8])) == 4]) + + carriers = [carrier[1] for carrier in final_lines] + sideband_orders = [[i,j,k] for i in range(-order1,order1+1) for j in range(-order2,order2+1) for k in range(-order3,order3+1)] + sideband_freqs = [mode_1,mode_2,mode_3] + + + #add all secular sidebands + all_lines = [] + for freq in carriers: + for el in sideband_orders: + all_lines.append(((freq + sum(np.multiply(el,sideband_freqs))),sum(np.abs(el)))) + + #add driven sidebands + if micromotion: + micro_lines = [] + for el in all_lines: + freq,order = el + micro_lines.append((freq+drive_freq,0.5+order)) + micro_lines.append((freq-drive_freq,0.5+order)) + all_lines.extend(micro_lines) + + freqs = np.arange(-50,50,0.005) + spec = np.zeros_like(freqs) + for line in all_lines: + spec = np.add(spec,self.make_gaussian(line[0],freqs,line[1])) + data = np.zeros((len(freqs), 2)) + data[:,0] = freqs + data[:,1] = spec + + return data + + def make_gaussian(self,center,freqs,amplitude): + #takes a center and makes a guassian around that point + gauss = (0.5**amplitude)*np.exp(-(freqs-center)**2/(0.010**2)) + return gauss + + def onPlot(self): + ''' + Plot the manual parameters. See documentation + for plotFit() + ''' + + class dataset(): + def __init__(self, data): + self.data = data + self.updateCounter = 1 + + data = self.generate_spectrum() ####Ths is where we add the lorenzians + ds = dataset(data) + try: + # remove the previous plot + self.parent.parent.remove_artist(self.ident) + self.parent.parent.add_artist(self.ident, ds, 0, no_points = False) + except: + self.parent.parent.add_artist(self.ident, ds, 0, no_points = False) + + + def closeEvent(self, event): + self.parent.parent.remove_artist(self.ident) + + + +#everything must be in Gauss and MHz. Copied from SD scanner +class EnergyLevel(object): + + spectoscopic_notation = { + 'S': 0, + 'P': 1, + 'D': 2, + } + + spectoscopic_notation_rev = { + 0 : 'S', + 1 : 'P', + 2 : 'D', + } + + + def __init__(self, angular_momentum_l, total_angular_momentum_j, spin_s = '1/2'): + #convert spectroscopic notation to the spin number + if type(angular_momentum_l) == str: + angular_momentum_l = self.spectoscopic_notation[angular_momentum_l] + total_angular_momentum_j = Fraction(total_angular_momentum_j) + spin_s = Fraction(spin_s) + S = spin_s + self.L = L = angular_momentum_l + J = total_angular_momentum_j + lande_factor = self.lande_factor(S, L, J) + #sublevels are found, 2* self.J is always an integer, so can use numerator + self.sublevels_m = [-J + i for i in range( 1 + (2 * J).numerator)] + self.energy_scale = (lande_factor * 9.274e-24 / 6.626e-34) #1.4 MHz / gauss + + def lande_factor(self, S, L ,J): + '''computes the lande g factor''' + g = Fraction(3,2) + Fraction( S * (S + 1) - L * (L + 1) , 2 * J*(J + 1)) + return g + + def magnetic_to_energy(self, B): + '''given the magnitude of the magnetic field, returns all energies of all zeeman sublevels''' + energies = [(self.energy_scale * m * B) *1e-6 for m in self.sublevels_m] #put in MHz + representations = [self.frac_to_string(m) for m in self.sublevels_m] + return zip(self.sublevels_m,energies,representations) + + def frac_to_string(self, sublevel): + #helper class for converting energy levels to strings + sublevel = str(sublevel) + if not sublevel.startswith('-'): + sublevel = '+' + sublevel + together = self.spectoscopic_notation_rev[self.L] + sublevel + return together + +class EnergyLevel_CA_ion(EnergyLevel): + ''' + Class for describing the energy levels of Calcium Ions. This is specific to Ca+ because it uses + precisely measured g factors of the S and D states in the calculations. + ''' + + def lande_factor(self, S, L, J): + g_factor_S = 2.00225664 #Eur Phys JD 25 113-125 + g_factor_D = 1.2003340 #PRL 102, 023002 (2009) + if S == Fraction('1/2') and L == Fraction('0') and J == Fraction('1/2'): + g = g_factor_S + elif S == Fraction('1/2') and L == Fraction('2') and J == Fraction('5/2'): + g = g_factor_D + return g + +class Transitions_SD(object): + + S = EnergyLevel_CA_ion('S', '1/2') + D = EnergyLevel_CA_ion('D', '5/2') + allowed_transitions = [0,1,2] + + def transitions(self): + transitions = [] + for m_s,E_s,repr_s in self.S.magnetic_to_energy(0): + for m_d,E_d,repr_d in self.D.magnetic_to_energy(0): + if abs(m_d-m_s) in self.allowed_transitions: + name = repr_s + repr_d + transitions.append(name) + return transitions + + def get_transition_energies(self, B, zero_offset = 0.): + '''returns the transition enenrgies in MHz where zero_offset is the 0-field transition energy between S and D''' + ans = [] + for m_s,E_s,repr_s in self.S.magnetic_to_energy(B): + for m_d,E_d,repr_d in self.D.magnetic_to_energy(B): + if abs(m_d-m_s) in self.allowed_transitions: + name = repr_s + repr_d + diff = E_d - E_s + diff+= zero_offset + ans.append((name, diff)) + return ans + + def energies_to_magnetic_field(self, transitions): + #given two points in the form [(S-1/2D5+1/2, 1.0 MHz), (-1/2, 5+/2, 2.0 MHz)], calculates the magnetic field + try: + transition1, transition2 = transitions + except ValueError: + raise Exception ("Wrong number of inputs in energies_to_magnetic_field") + ms1,md1 = self.str_to_fractions(transition1[0]) + ms2,md2 = self.str_to_fractions(transition2[0]) + en1,en2 = transition1[1], transition2[1] + if abs(md1 - ms1) not in self.allowed_transitions or abs(md2 - ms2) not in self.allowed_transitions: + raise Exception ("Such transitions are not allowed") + s_scale = self.S.energy_scale + d_scale = self.D.energy_scale + B = (en2 - en1) / ( d_scale * ( md2 - md1) - s_scale * (ms2 - ms1) ) + B = B *1e4 #(to guass from tesla) + offset = en1 - (md1 * d_scale - ms1 * s_scale) * B + return B, offset + + def str_to_fractions(self, inp): + #takes S-1/2D5+1/2 and converts to Fraction(-1/2), Fraction(1/2) return Fraction(inp[1:5]), Fraction(inp[6:10]) \ No newline at end of file diff --git a/README.md b/README.md index 3493a80..ee4dbf3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# RealSimpleGrapher -LabRAD grapher written in pyqtgraph -checkout our [Wiki](https://github.com/HaeffnerLab/RealSimpleGrapher/wiki) +# RealSimpleGrapher +LabRAD grapher written in pyqtgraph +checkout our [Wiki](https://github.com/HaeffnerLab/RealSimpleGrapher/wiki) diff --git a/ScrollingGraphWidgetPyQtGraph.py b/ScrollingGraphWidgetPyQtGraph.py index 8a782f7..ae148f4 100644 --- a/ScrollingGraphWidgetPyQtGraph.py +++ b/ScrollingGraphWidgetPyQtGraph.py @@ -1,37 +1,38 @@ -from GraphWidgetPyQtGraph import Graph_PyQtGraph as Graph -from PyQt4 import QtGui, QtCore - -class ScrollingGraph_PyQtGraph(Graph): - def __init__(self, name, reactor, parent = None, ylim=[0,1], cxn=None): - super(ScrollingGraph_PyQtGraph, self).__init__(name, reactor, parent) - self.set_xlimits([0, 100]) - self.pointsToKeep = 100 - - def update_figure(self, _input = None): - for ident, params in self.artists.iteritems(): - if params.shown: - try: - index = params.index - x = params.dataset.data[:,0] - y = params.dataset.data[:,index+1] - params.artist.setData(x,y) - except: - pass - - - try: - mousepressed = QtGui.qApp.mouseButtons() - if (mousepressed == QtCore.Qt.LeftButton) or (mousepressed == QtCore.Qt.RightButton): - return - # see if we need to redraw - xmin_cur, xmax_cur = self.current_limits - x_cur = x[-1] # current largest x value - window_width = xmax_cur - xmin_cur - # scroll if we've reached 75% of the window - if (x_cur > (xmin_cur + 0.75*window_width) and (x_cur < xmax_cur)): - shift = (xmax_cur - xmin_cur)/2.0 - xmin = xmin_cur + shift - xmax = xmax_cur + shift - self.set_xlimits( [xmin, xmax] ) - except: - pass +from GraphWidgetPyQtGraph import Graph_PyQtGraph as Graph +from PyQt5 import QtGui, QtCore, QtWidgets + +class ScrollingGraph_PyQtGraph(Graph): + def __init__(self, name, reactor, parent = None, ylim=[0,1], cxn=None): + super(ScrollingGraph_PyQtGraph, self).__init__(name, reactor, parent) + self.set_xlimits([0, 100]) + self.pointsToKeep = 100 + + def update_figure(self, _input = None): + for ident, params in self.artists.items(): + if params.shown: + try: + index = params.index + x = params.dataset.data[:,0] + y = params.dataset.data[:,index+1] + params.artist.setData(x,y) + except: + pass + + + try: + mousepressed = QtWidgets.QApplication.instance().MouseButton + #a.mouseButtons() + if mousepressed == (QtCore.Qt.LeftButton or QtCore.Qt.RightButton): + return + # see if we need to redraw + xmin_cur, xmax_cur = self.current_limits + x_cur = x[-1] # current largest x value + window_width = xmax_cur - xmin_cur + # scroll if we've reached 75% of the window + if (x_cur > (xmin_cur + 0.75*window_width) and (x_cur < xmax_cur)): + shift = (xmax_cur - xmin_cur)/2.0 + xmin = xmin_cur + shift + xmax = xmax_cur + shift + self.set_xlimits( [xmin, xmax] ) + except: + pass diff --git a/TraceListWidget.py b/TraceListWidget.py index a5839f7..1bf8d1c 100644 --- a/TraceListWidget.py +++ b/TraceListWidget.py @@ -1,126 +1,125 @@ -from PyQt4 import QtGui -from PyQt4 import QtCore -from ParameterListWidget import ParameterList -from DataVaultListWidget import DataVaultList -from FitWindowWidget import FitWindow -from PredictSpectrumWidget import PredictSpectrum -from GUIConfig import traceListConfig - -class TraceList(QtGui.QListWidget): - def __init__(self, parent): - super(TraceList, self).__init__() - self.parent = parent - self.windows = [] - self.config = traceListConfig() - self.setStyleSheet("background-color:%s;" % self.config.background_color) - try: - self.use_trace_color = self.config.use_trace_color - except AttributeError: - self.use_trace_color = False - - self.name = 'pmt' - self.initUI() - - def initUI(self): - self.trace_dict = {} - item = QtGui.QListWidgetItem('Traces') - item.setCheckState(QtCore.Qt.Checked) - self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.customContextMenuRequested.connect(self.popupMenu) - - - def addTrace(self, ident, color): - item = QtGui.QListWidgetItem(ident) - - if self.use_trace_color: - foreground_color = self.parent.getItemColor(color) - item.setForeground(foreground_color) - else: - item.setForeground(QtGui.QColor(255, 255, 255)) - item.setBackground(QtGui.QColor(0, 0, 0)) - - item.setCheckState(QtCore.Qt.Checked) - self.addItem(item) - self.trace_dict[ident] = item - - def removeTrace(self, ident): - item = self.trace_dict[ident] - row = self.row(item) - self.takeItem(row) - item = None - - def changeTraceListColor(self, ident, new_color): - item = self.trace_dict[ident] - item.setForeground(self.parent.getItemColor(new_color)) - - def popupMenu(self, pos): - menu = QtGui.QMenu() - item = self.itemAt(pos) - if (item == None): - dataaddAction = menu.addAction('Add Data Set') - spectrumaddAction = menu.addAction('Add Predicted Spectrum') - - action = menu.exec_(self.mapToGlobal(pos)) - if action == dataaddAction: - dvlist = DataVaultList(self.parent.name) - self.windows.append(dvlist) - dvlist.show() - - if action == spectrumaddAction: - ps = PredictSpectrum(self) - self.windows.append(ps) - ps.show() - - - - else: - ident = str(item.text()) - parametersAction = menu.addAction('Parameters') - togglecolorsAction = menu.addAction('Toggle colors') - fitAction = menu.addAction('Fit') - selectColorMenu = menu.addMenu("Select color") - redAction = selectColorMenu.addAction("Red") - greenAction = selectColorMenu.addAction("Green") - yellowAction = selectColorMenu.addAction("Yellow") - cyanAction = selectColorMenu.addAction("Cyan") - magentaAction = selectColorMenu.addAction("Magenta") - whiteAction = selectColorMenu.addAction("White") - colorActionDict = {redAction:"r", greenAction:"g", yellowAction:"y", cyanAction:"c", magentaAction:"m", whiteAction:"w"} - - action = menu.exec_(self.mapToGlobal(pos)) - - if action == parametersAction: - # option to show parameters in separate window - dataset = self.parent.artists[ident].dataset - pl = ParameterList(dataset) - self.windows.append(pl) - pl.show() - - if action == togglecolorsAction: - # option to change color of line - new_color = self.parent.colorChooser.next() - #self.parent.artists[ident].artist.setData(color = new_color, symbolBrush = new_color) - self.parent.artists[ident].artist.setPen(new_color) - if self.parent.show_points: - self.parent.artists[ident].artist.setData(pen = new_color, symbolBrush = new_color) - self.changeTraceListColor(ident, new_color) - else: - self.parent.artists[ident].artist.setData(pen = new_color) - self.changeTraceListColor(ident, new_color) - - if action == fitAction: - dataset = self.parent.artists[ident].dataset - index = self.parent.artists[ident].index - fw = FitWindow(dataset, index, self) - self.windows.append(fw) - fw.show() - - if action in colorActionDict.keys(): - new_color = colorActionDict[action] - self.parent.artists[ident].artist.setPen(new_color) - if self.parent.show_points: - self.parent.artists[ident].artist.setData(pen = new_color, symbolBrush = new_color) - self.changeTraceListColor(ident, new_color) - else: - self.parent.artists[ident].artist.setData(pen = new_color) - self.changeTraceListColor(ident, new_color) +from PyQt5 import QtGui, QtWidgets, QtCore +from ParameterListWidget import ParameterList +from DataVaultListWidget import DataVaultList +from FitWindowWidget import FitWindow +from PredictSpectrumWidget import PredictSpectrum +from GUIConfig import traceListConfig + +class TraceList(QtWidgets.QListWidget): + def __init__(self, parent): + super(TraceList, self).__init__() + self.parent = parent + self.windows = [] + self.config = traceListConfig() + self.setStyleSheet("background-color:%s;" % self.config.background_color) + try: + self.use_trace_color = self.config.use_trace_color + except AttributeError: + self.use_trace_color = False + + self.name = 'pmt' + self.initUI() + + def initUI(self): + self.trace_dict = {} + item = QtWidgets.QListWidgetItem('Traces') + item.setCheckState(QtCore.Qt.Checked) + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.popupMenu) + + + def addTrace(self, ident, color): + item = QtWidgets.QListWidgetItem(ident) + + if self.use_trace_color: + foreground_color = self.parent.getItemColor(color) + item.setForeground(foreground_color) + else: + item.setForeground(QtGui.QColor(255, 255, 255)) + item.setBackground(QtGui.QColor(0, 0, 0)) + + item.setCheckState(QtCore.Qt.Checked) + self.addItem(item) + self.trace_dict[ident] = item + + def removeTrace(self, ident): + item = self.trace_dict[ident] + row = self.row(item) + self.takeItem(row) + item = None + + def changeTraceListColor(self, ident, new_color): + item = self.trace_dict[ident] + item.setForeground(self.parent.getItemColor(new_color)) + + def popupMenu(self, pos): + menu = QtWidgets.QMenu() + item = self.itemAt(pos) + if (item == None): + dataaddAction = menu.addAction('Add Data Set') + spectrumaddAction = menu.addAction('Add Predicted Spectrum') + + action = menu.exec_(self.mapToGlobal(pos)) + if action == dataaddAction: + dvlist = DataVaultList(self.parent.name) + self.windows.append(dvlist) + dvlist.show() + + if action == spectrumaddAction: + ps = PredictSpectrum(self) + self.windows.append(ps) + ps.show() + + + + else: + ident = str(item.text()) + parametersAction = menu.addAction('Parameters') + togglecolorsAction = menu.addAction('Toggle colors') + fitAction = menu.addAction('Fit') + selectColorMenu = menu.addMenu("Select color") + redAction = selectColorMenu.addAction("Red") + greenAction = selectColorMenu.addAction("Green") + yellowAction = selectColorMenu.addAction("Yellow") + cyanAction = selectColorMenu.addAction("Cyan") + magentaAction = selectColorMenu.addAction("Magenta") + whiteAction = selectColorMenu.addAction("White") + colorActionDict = {redAction:"r", greenAction:"g", yellowAction:"y", cyanAction:"c", magentaAction:"m", whiteAction:"w"} + + action = menu.exec_(self.mapToGlobal(pos)) + + if action == parametersAction: + # option to show parameters in separate window + dataset = self.parent.artists[ident].dataset + pl = ParameterList(dataset) + self.windows.append(pl) + pl.show() + + if action == togglecolorsAction: + # option to change color of line + new_color = self.parent.colorChooser.next() + #self.parent.artists[ident].artist.setData(color = new_color, symbolBrush = new_color) + self.parent.artists[ident].artist.setPen(new_color) + if self.parent.show_points: + self.parent.artists[ident].artist.setData(pen = new_color, symbolBrush = new_color) + self.changeTraceListColor(ident, new_color) + else: + self.parent.artists[ident].artist.setData(pen = new_color) + self.changeTraceListColor(ident, new_color) + + if action == fitAction: + dataset = self.parent.artists[ident].dataset + index = self.parent.artists[ident].index + fw = FitWindow(dataset, index, self) + self.windows.append(fw) + fw.show() + + if action in colorActionDict.keys(): + new_color = colorActionDict[action] + self.parent.artists[ident].artist.setPen(new_color) + if self.parent.show_points: + self.parent.artists[ident].artist.setData(pen = new_color, symbolBrush = new_color) + self.changeTraceListColor(ident, new_color) + else: + self.parent.artists[ident].artist.setData(pen = new_color) + self.changeTraceListColor(ident, new_color) diff --git a/analysis/fit_bessel.py b/analysis/fit_bessel.py index 1cd6a9e..0070d34 100644 --- a/analysis/fit_bessel.py +++ b/analysis/fit_bessel.py @@ -1,57 +1,57 @@ -# Fitter class for Lorentzians - -from model import Model, ParameterInfo -import numpy as np -import scipy.special as sp - -class Bessel(Model): - - def __init__(self): - self.parameters = { - 'center': ParameterInfo('center', 0, self.guess_center), - 'scale': ParameterInfo('scale', 1, self.guess_scale), - 'fwhm': ParameterInfo('fwhm', 2, self.guess_fwhm), - 'offset': ParameterInfo('offset', 3, self.guess_offset), - 'modDepth': ParameterInfo('modDepth', 4, self.guess_modDepth), - 'driveRF': ParameterInfo('driveRF', 5, self.guess_driveRF) - } - - def model(self, x, p): - ''' - Base Bessel function modulated spectrum model for micromotion. - Using definition from Pruttivarasin thesis. - p = [center, scale, gamma, offset, modulation depth] - ''' - p[2] = abs(p[2]) # fwhm is positive - return p[3] + p[1]*p[2]*0.5*((sp.jv(-6,p[4])**2/((x - p[0] - 6*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(-5,p[4])**2/((x - p[0] - 5*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(-4,p[4])**2/((x - p[0] - 4*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(-3,p[4])**2/((x - p[0] -3*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(-2,p[4])**2/((x - p[0] -2*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(-1,p[4])**2/((x - p[0] - p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(0,p[4])**2/((x - p[0])**2 + (0.5*p[2])**2)) + - (sp.jv(1,p[4])**2/((x - p[0] + p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(2,p[4])**2/((x - p[0] + 2*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(3,p[4])**2/((x - p[0] + 3*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(4,p[4])**2/((x - p[0] + 4*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(5,p[4])**2/((x - p[0] + 5*p[5])**2 + (0.5*p[2])**2)) + - (sp.jv(6,p[4])**2/((x - p[0] + 6*p[5])**2 + (0.5*p[2])**2))) - - def guess_center(self, x, y): - max_index = np.argmax(y) - return x[max_index] - - def guess_scale(self, x, y): - return 1500.0 - - def guess_fwhm(self, x, y): - return (max(x) - min(x))/6.0 - - def guess_offset(self, x, y): - return np.min(y) - - def guess_modDepth(self, x, y): - return 0.2 - - def guess_driveRF(self, x ,y): - return 48.537 +# Fitter class for Lorentzians + +from .model import Model, ParameterInfo +import numpy as np +import scipy.special as sp + +class Bessel(Model): + + def __init__(self): + self.parameters = { + 'center': ParameterInfo('center', 0, self.guess_center), + 'scale': ParameterInfo('scale', 1, self.guess_scale), + 'fwhm': ParameterInfo('fwhm', 2, self.guess_fwhm), + 'offset': ParameterInfo('offset', 3, self.guess_offset), + 'modDepth': ParameterInfo('modDepth', 4, self.guess_modDepth), + 'driveRF': ParameterInfo('driveRF', 5, self.guess_driveRF) + } + + def model(self, x, p): + ''' + Base Bessel function modulated spectrum model for micromotion. + Using definition from Pruttivarasin thesis. + p = [center, scale, gamma, offset, modulation depth] + ''' + p[2] = abs(p[2]) # fwhm is positive + return p[3] + p[1]*p[2]*0.5*((sp.jv(-6,p[4])**2/((x - p[0] - 6*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(-5,p[4])**2/((x - p[0] - 5*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(-4,p[4])**2/((x - p[0] - 4*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(-3,p[4])**2/((x - p[0] -3*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(-2,p[4])**2/((x - p[0] -2*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(-1,p[4])**2/((x - p[0] - p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(0,p[4])**2/((x - p[0])**2 + (0.5*p[2])**2)) + + (sp.jv(1,p[4])**2/((x - p[0] + p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(2,p[4])**2/((x - p[0] + 2*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(3,p[4])**2/((x - p[0] + 3*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(4,p[4])**2/((x - p[0] + 4*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(5,p[4])**2/((x - p[0] + 5*p[5])**2 + (0.5*p[2])**2)) + + (sp.jv(6,p[4])**2/((x - p[0] + 6*p[5])**2 + (0.5*p[2])**2))) + + def guess_center(self, x, y): + max_index = np.argmax(y) + return x[max_index] + + def guess_scale(self, x, y): + return 1500.0 + + def guess_fwhm(self, x, y): + return (max(x) - min(x))/6.0 + + def guess_offset(self, x, y): + return np.min(y) + + def guess_modDepth(self, x, y): + return 0.2 + + def guess_driveRF(self, x ,y): + return 48.537 diff --git a/analysis/fit_expdecay.py b/analysis/fit_expdecay.py index c349bfa..2ff7e87 100644 --- a/analysis/fit_expdecay.py +++ b/analysis/fit_expdecay.py @@ -1,20 +1,20 @@ -# Fitter class for Gaussians - -from model import Model, ParameterInfo -import numpy as np - -class ExponentialDecay(Model): - - def __init__(self): - self.parameters = { - 'tau':ParameterInfo('tau', 0, lambda x,y: 1000, vary=True), - 'startfrom': ParameterInfo('startfrom', 1, lambda x,y: 0, vary=True), - 'decayto': ParameterInfo('decayto', 2, lambda x,y: 0.5, vary=False), - } - - def model(self, x, p): - tau = p[0] - startfrom = p[1] - decayto = p[2] - +# Fitter class for Gaussians + +from .model import Model, ParameterInfo +import numpy as np + +class ExponentialDecay(Model): + + def __init__(self): + self.parameters = { + 'tau':ParameterInfo('tau', 0, lambda x,y: 1000, vary=True), + 'startfrom': ParameterInfo('startfrom', 1, lambda x,y: 0, vary=True), + 'decayto': ParameterInfo('decayto', 2, lambda x,y: 0.5, vary=False), + } + + def model(self, x, p): + tau = p[0] + startfrom = p[1] + decayto = p[2] + return startfrom + (decayto-startfrom)*(1-np.exp(-x/tau)) \ No newline at end of file diff --git a/analysis/fit_gaussdecay.py b/analysis/fit_gaussdecay.py index 3c1955c..d62a7de 100644 --- a/analysis/fit_gaussdecay.py +++ b/analysis/fit_gaussdecay.py @@ -1,20 +1,20 @@ -# Fitter class for Gaussians - -from model import Model, ParameterInfo -import numpy as np - -class GaussianDecay(Model): - - def __init__(self): - self.parameters = { - 'tau':ParameterInfo('tau', 0, lambda x,y: 1000, vary=True), - 'startfrom': ParameterInfo('startfrom', 1, lambda x,y: 0, vary=True), - 'decayto': ParameterInfo('decayto', 2, lambda x,y: 0.5, vary=False), - } - - def model(self, x, p): - tau = p[0] - startfrom = p[1] - decayto = p[2] - +# Fitter class for Gaussians + +from .model import Model, ParameterInfo +import numpy as np + +class GaussianDecay(Model): + + def __init__(self): + self.parameters = { + 'tau':ParameterInfo('tau', 0, lambda x,y: 1000, vary=True), + 'startfrom': ParameterInfo('startfrom', 1, lambda x,y: 0, vary=True), + 'decayto': ParameterInfo('decayto', 2, lambda x,y: 0.5, vary=False), + } + + def model(self, x, p): + tau = p[0] + startfrom = p[1] + decayto = p[2] + return startfrom + (decayto-startfrom)*(1-np.exp(-x**2/(2*tau**2))) \ No newline at end of file diff --git a/analysis/fit_gaussian.py b/analysis/fit_gaussian.py index 8d085f9..21fc221 100644 --- a/analysis/fit_gaussian.py +++ b/analysis/fit_gaussian.py @@ -1,40 +1,40 @@ -# Fitter class for Gaussians - -from model import Model, ParameterInfo -import numpy as np - -class Gaussian(Model): - - def __init__(self): - self.parameters = { - 'mean':ParameterInfo('mean', 0, self.guess_mean), - 'A': ParameterInfo('A', 1, self.guess_A), - 'sigma': ParameterInfo('sigma', 2, self.guess_sigma), - 'offset': ParameterInfo('offset', 3, self.guess_offset) - } - - def model(self, x, p): - - ''' - Base Gaussian model defined as - http://mathworld.wolfram.com/GaussianFunction.html - - without the normalization prefactor and adding - a constant offset - ''' - - mu = p[0]; A = p[1]; sigma_squared = p[2]**2; b = p[3] - return A*np.exp( -(x - mu)**2 / (2*sigma_squared) ) + b - - def guess_mean(self, x, y): - max_index = np.argmax(y) - return x[max_index] - - def guess_A(self, x, y): - return max(y) - - def guess_sigma(self, x, y): - return (max(x) - min(x))/6.0 - - def guess_offset(self, x, y): - return 0.0 +# Fitter class for Gaussians + +from .model import Model, ParameterInfo +import numpy as np + +class Gaussian(Model): + + def __init__(self): + self.parameters = { + 'mean':ParameterInfo('mean', 0, self.guess_mean), + 'A': ParameterInfo('A', 1, self.guess_A), + 'sigma': ParameterInfo('sigma', 2, self.guess_sigma), + 'offset': ParameterInfo('offset', 3, self.guess_offset) + } + + def model(self, x, p): + + ''' + Base Gaussian model defined as + http://mathworld.wolfram.com/GaussianFunction.html + + without the normalization prefactor and adding + a constant offset + ''' + + mu = p[0]; A = p[1]; sigma_squared = p[2]**2; b = p[3] + return A*np.exp( -(x - mu)**2 / (2*sigma_squared) ) + b + + def guess_mean(self, x, y): + max_index = np.argmax(y) + return x[max_index] + + def guess_A(self, x, y): + return max(y) + + def guess_sigma(self, x, y): + return (max(x) - min(x))/6.0 + + def guess_offset(self, x, y): + return 0.0 diff --git a/analysis/fit_linear.py b/analysis/fit_linear.py index 8281d77..8dfb2f5 100644 --- a/analysis/fit_linear.py +++ b/analysis/fit_linear.py @@ -1,28 +1,28 @@ -# Fitter class for linear fits - -from model import Model, ParameterInfo -import numpy as np - -class Linear(Model): - - def __init__(self): - self.parameters = { - 'm':ParameterInfo('m', 0, self.guess_m), - 'b':ParameterInfo('b', 1, self.guess_b) - } - - def model(self, x, p): - m = p[0] - b = p[1] - return m*x + b - - def guess_m(self, x, y): - dx = x[-1] - x[0] - dy = y[-1] - y[0] - return dy/dx - - def guess_b(self, x, y): - m = self.guess_m(x, y) - ybar = np.mean(y) - xbar = np.mean(x) - return ybar - m*xbar +# Fitter class for linear fits + +from .model import Model, ParameterInfo +import numpy as np + +class Linear(Model): + + def __init__(self): + self.parameters = { + 'm':ParameterInfo('m', 0, self.guess_m), + 'b':ParameterInfo('b', 1, self.guess_b) + } + + def model(self, x, p): + m = p[0] + b = p[1] + return m*x + b + + def guess_m(self, x, y): + dx = x[-1] - x[0] + dy = y[-1] - y[0] + return dy/dx + + def guess_b(self, x, y): + m = self.guess_m(x, y) + ybar = np.mean(y) + xbar = np.mean(x) + return ybar - m*xbar diff --git a/analysis/fit_lorentzian.py b/analysis/fit_lorentzian.py index f49649e..0bf4654 100644 --- a/analysis/fit_lorentzian.py +++ b/analysis/fit_lorentzian.py @@ -1,41 +1,41 @@ -# Fitter class for Lorentzians - -from model import Model, ParameterInfo -import numpy as np - -class Lorentzian(Model): - - def __init__(self): - self.parameters = { - 'center': ParameterInfo('center', 0, self.guess_center), - 'scale': ParameterInfo('scale', 1, self.guess_scale), - 'fwhm': ParameterInfo('fwhm', 2, self.guess_fwhm), - 'offset': ParameterInfo('offset', 3, self.guess_offset) - } - - def model(self, x, p): - ''' - Base Lorentzian model. Using definition from - http://mathworld.wolfram.com/LorentzianFunction.html - - where we add an overall scale factor to change the - peak height - - p = [center, scale, gamma, offset] - ''' - p[2] = abs(p[2]) # fwhm is positive - return p[3] + p[1]*0.5*p[2]/( (x - p[0])**2 + (0.5*p[2])**2) - - def guess_center(self, x, y): - max_index = np.argmax(y) - return x[max_index] - - def guess_scale(self, x, y): - area = (max(x) - min(x))*(max(y) - min(y)) - return area - - def guess_fwhm(self, x, y): - return (max(x) - min(x))/6.0 - - def guess_offset(self, x, y): - return np.min(y) +# Fitter class for Lorentzians + +from .model import Model, ParameterInfo +import numpy as np + +class Lorentzian(Model): + + def __init__(self): + self.parameters = { + 'center': ParameterInfo('center', 0, self.guess_center), + 'scale': ParameterInfo('scale', 1, self.guess_scale), + 'fwhm': ParameterInfo('fwhm', 2, self.guess_fwhm), + 'offset': ParameterInfo('offset', 3, self.guess_offset) + } + + def model(self, x, p): + ''' + Base Lorentzian model. Using definition from + http://mathworld.wolfram.com/LorentzianFunction.html + + where we add an overall scale factor to change the + peak height + + p = [center, scale, gamma, offset] + ''' + p[2] = abs(p[2]) # fwhm is positive + return p[3] + p[1]*0.5*p[2]/( (x - p[0])**2 + (0.5*p[2])**2) + + def guess_center(self, x, y): + max_index = np.argmax(y) + return x[max_index] + + def guess_scale(self, x, y): + area = (max(x) - min(x))*(max(y) - min(y)) + return area + + def guess_fwhm(self, x, y): + return (max(x) - min(x))/6.0 + + def guess_offset(self, x, y): + return np.min(y) diff --git a/analysis/fit_rabi.py b/analysis/fit_rabi.py index 62f2dd8..ff79967 100644 --- a/analysis/fit_rabi.py +++ b/analysis/fit_rabi.py @@ -1,57 +1,57 @@ -# Fitter class for Rabi flops - -from model import Model, ParameterInfo -from rabi.motional_distribution import motional_distribution as md -from rabi.rabi_coupling import rabi_coupling as rc - -import numpy as np - -class Rabi(Model): - - def __init__(self): - self.parameters = { - 'omega_rabi': ParameterInfo('f_rabi', 0, self.guess_omega_rabi), - 'nbar': ParameterInfo('nbar', 1, lambda x, y: 5, True), - 'eta':ParameterInfo('eta', 2, lambda x, y: 0.05, vary=False), - 'delta': ParameterInfo('delta', 3, lambda x,y: 0, vary=False), - 'sideband_order': ParameterInfo('sideband_order', 4, lambda x, y: 0, vary = False), - 'excitation_scaling': ParameterInfo('excitation_scaling', 5, lambda x,y: 1.0, vary = False) - } - - def model(self, x, p): - omega_rabi = p[0] - nbar = p[1] - eta = p[2] - delta = p[3] - sideband_order = p[4] - excitation_scaling = p[5] - - nmax = 1000 - - omega = rc.compute_rabi_coupling(eta, sideband_order, nmax) - ones = np.ones_like(x) - p_n = md.thermal(nbar, nmax) - # if 1 - p_n.sum() > 1e-6: - # raise Exception ('Hilbert space too small, missing population') - if delta == 0: - #prevents division by zero if delta == 0, omega == 0 - effective_omega = 1. - else: - effective_omega = omega/np.sqrt(omega**2+delta**2) - result = np.outer(p_n * effective_omega, ones) * (np.sin( np.outer( np.sqrt(omega**2+delta**2)*omega_rabi/2, x ))**2) - result = np.sum(result, axis = 0) - result = excitation_scaling * result - return result - - def guess_omega_rabi(self, x, y): - ''' - Take the first time the flop goes above the average excitation of the whole scan - to be pi/4 - ''' - - mean = np.mean(y) - for x0, y0 in zip(x,y): - if y0 > mean: break - t_2pi = 4*x0 - return 2*np.pi/(t_2pi) - +# Fitter class for Rabi flops + +from .model import Model, ParameterInfo +from .rabi.motional_distribution import motional_distribution as md +from .rabi.rabi_coupling import rabi_coupling as rc + +import numpy as np + +class Rabi(Model): + + def __init__(self): + self.parameters = { + 'omega_rabi': ParameterInfo('f_rabi', 0, self.guess_omega_rabi), + 'nbar': ParameterInfo('nbar', 1, lambda x, y: 5, True), + 'eta':ParameterInfo('eta', 2, lambda x, y: 0.05, vary=False), + 'delta': ParameterInfo('delta', 3, lambda x,y: 0, vary=False), + 'sideband_order': ParameterInfo('sideband_order', 4, lambda x, y: 0, vary = False), + 'excitation_scaling': ParameterInfo('excitation_scaling', 5, lambda x,y: 1.0, vary = False) + } + + def model(self, x, p): + omega_rabi = p[0] + nbar = p[1] + eta = p[2] + delta = p[3] + sideband_order = p[4] + excitation_scaling = p[5] + + nmax = 1000 + + omega = rc.compute_rabi_coupling(eta, sideband_order, nmax) + ones = np.ones_like(x) + p_n = md.thermal(nbar, nmax) + # if 1 - p_n.sum() > 1e-6: + # raise Exception ('Hilbert space too small, missing population') + if delta == 0: + #prevents division by zero if delta == 0, omega == 0 + effective_omega = 1. + else: + effective_omega = omega/np.sqrt(omega**2+delta**2) + result = np.outer(p_n * effective_omega, ones) * (np.sin( np.outer( np.sqrt(omega**2+delta**2)*omega_rabi/2, x ))**2) + result = np.sum(result, axis = 0) + result = excitation_scaling * result + return result + + def guess_omega_rabi(self, x, y): + ''' + Take the first time the flop goes above the average excitation of the whole scan + to be pi/4 + ''' + + mean = np.mean(y) + for x0, y0 in zip(x,y): + if y0 > mean: break + t_2pi = 4*x0 + return 2*np.pi/(t_2pi) + diff --git a/analysis/fit_ramsey.py b/analysis/fit_ramsey.py index b6412e2..981b01f 100644 --- a/analysis/fit_ramsey.py +++ b/analysis/fit_ramsey.py @@ -1,4 +1,4 @@ -from model import Model, ParameterInfo +from .model import Model, ParameterInfo import numpy as np class RamseyDecay(Model): diff --git a/analysis/fit_ramseybfield.py b/analysis/fit_ramseybfield.py index da25afd..07d4a24 100644 --- a/analysis/fit_ramseybfield.py +++ b/analysis/fit_ramseybfield.py @@ -1,4 +1,4 @@ -from model import Model, ParameterInfo +from .model import Model, ParameterInfo import numpy as np class RamseyBfield(Model): diff --git a/analysis/fit_rotrabi.py b/analysis/fit_rotrabi.py index 8d108cb..22c7188 100644 --- a/analysis/fit_rotrabi.py +++ b/analysis/fit_rotrabi.py @@ -1,80 +1,80 @@ -# Fitter class for Rabi flops - -from model import Model, ParameterInfo -from rabi.motional_distribution import motional_distribution as md -from rabi.rabi_coupling import rabi_coupling as rc -import scipy.constants as scc - -import math -import numpy as np - -class RotRabi(Model): - - def __init__(self): - self.parameters = { - 'omega_rabi': ParameterInfo('f_rabi', 0, self.guess_omega_rabi), - 'stdev_l': ParameterInfo('stdev_l', 1, lambda x, y: 50.0, True), - 'sideband_order': ParameterInfo('sideband_order', 2, lambda x, y: 4, vary = False), - 'f_trap': ParameterInfo('f_trap', 3, lambda x,y: 0.845, vary = False), - 'f_rot': ParameterInfo('f_rot', 4, lambda x,y: 0.1, vary = False), - 'delta': ParameterInfo('delta', 5, lambda x,y: 0, vary=False), - 'scale': ParameterInfo('scale,', 6, lambda x,y: 1.0, vary=False) - } - - def model(self, x, p): - - omega_rabi = p[0] - stdev_l = p[1] - sideband_order = p[2] - f_trap = p[3] - f_rot = p[4] - delta = p[5] - scale = p[6] - - #calculate the radius of the ion 'ring' (half the distance between the ions) - r = (scc.e**2/(40*scc.atomic_mass*4*math.pi**2*(f_trap**2-f_rot**2)*1e12*16*math.pi*scc.epsilon_0))**(1./3) - self.omega_r = scc.hbar/(4*40*scc.atomic_mass*r**2) - - result = self.rot_rabi_flop(1e-6*x,sideband_order,stdev_l,omega_rabi,delta,scale) - - return result - - def rot_rabi_flop(self, times, order, sigma_l, Omega_MHz, delta_kHz=0.0, scale=1.0): - if sigma_l > 3000: - sigma_l = 3000.0 - - # Convert to SI units - Omega = Omega_MHz*2*np.pi*1e6 - delta = delta_kHz*2*np.pi*1e3 - - # Get distribution of l's, their respective detunings, and calculate the excitation vs. time - (l_vals, c_ls) = self.calc_ls_cls(sigma_l) - delta_ls = 2*self.omega_r*order*l_vals - delta # Array of detunings, one for each l - exc = scale * np.sum(np.outer(c_ls**2 * Omega**2/(Omega**2+delta_ls**2), np.ones(len(times))) \ - * np.sin(np.outer(np.sqrt(Omega**2+delta_ls**2)/2, times))**2, axis=0) - - return exc - - def calc_ls_cls(self, sigma_l): - """Returns an array of l values and their amplitudes, given a standard deviation sigma_l - The distribution is Gaussian. - The center of the distribution is chosen to be centered at 0, which can be done without loss of generality - (all we care about is detunings, not absolute position in frequency space) - The l values returned are integers within 4*sigma""" - l_max = int(4*sigma_l) - l_vals = np.arange(-l_max, l_max+1) - c_ls_unnorm = np.exp(-l_vals**2/(4.0*sigma_l**2)) - c_ls = c_ls_unnorm/np.linalg.norm(c_ls_unnorm) - return (l_vals, c_ls) - - def guess_omega_rabi(self, x, y): - ''' - Take the first time the flop goes above the average excitation of the whole scan - to be pi/4 - ''' - - mean = np.mean(y) - for x0, y0 in zip(x,y): - if y0 > mean: break - t_2pi = 4*x0 +# Fitter class for Rabi flops + +from .model import Model, ParameterInfo +from .rabi.motional_distribution import motional_distribution as md +from .rabi.rabi_coupling import rabi_coupling as rc +import scipy.constants as scc + +import math +import numpy as np + +class RotRabi(Model): + + def __init__(self): + self.parameters = { + 'omega_rabi': ParameterInfo('f_rabi', 0, self.guess_omega_rabi), + 'stdev_l': ParameterInfo('stdev_l', 1, lambda x, y: 50.0, True), + 'sideband_order': ParameterInfo('sideband_order', 2, lambda x, y: 4, vary = False), + 'f_trap': ParameterInfo('f_trap', 3, lambda x,y: 0.845, vary = False), + 'f_rot': ParameterInfo('f_rot', 4, lambda x,y: 0.1, vary = False), + 'delta': ParameterInfo('delta', 5, lambda x,y: 0, vary=False), + 'scale': ParameterInfo('scale,', 6, lambda x,y: 1.0, vary=False) + } + + def model(self, x, p): + + omega_rabi = p[0] + stdev_l = p[1] + sideband_order = p[2] + f_trap = p[3] + f_rot = p[4] + delta = p[5] + scale = p[6] + + #calculate the radius of the ion 'ring' (half the distance between the ions) + r = (scc.e**2/(40*scc.atomic_mass*4*math.pi**2*(f_trap**2-f_rot**2)*1e12*16*math.pi*scc.epsilon_0))**(1./3) + self.omega_r = scc.hbar/(4*40*scc.atomic_mass*r**2) + + result = self.rot_rabi_flop(1e-6*x,sideband_order,stdev_l,omega_rabi,delta,scale) + + return result + + def rot_rabi_flop(self, times, order, sigma_l, Omega_MHz, delta_kHz=0.0, scale=1.0): + if sigma_l > 3000: + sigma_l = 3000.0 + + # Convert to SI units + Omega = Omega_MHz*2*np.pi*1e6 + delta = delta_kHz*2*np.pi*1e3 + + # Get distribution of l's, their respective detunings, and calculate the excitation vs. time + (l_vals, c_ls) = self.calc_ls_cls(sigma_l) + delta_ls = 2*self.omega_r*order*l_vals - delta # Array of detunings, one for each l + exc = scale * np.sum(np.outer(c_ls**2 * Omega**2/(Omega**2+delta_ls**2), np.ones(len(times))) \ + * np.sin(np.outer(np.sqrt(Omega**2+delta_ls**2)/2, times))**2, axis=0) + + return exc + + def calc_ls_cls(self, sigma_l): + """Returns an array of l values and their amplitudes, given a standard deviation sigma_l + The distribution is Gaussian. + The center of the distribution is chosen to be centered at 0, which can be done without loss of generality + (all we care about is detunings, not absolute position in frequency space) + The l values returned are integers within 4*sigma""" + l_max = int(4*sigma_l) + l_vals = np.arange(-l_max, l_max+1) + c_ls_unnorm = np.exp(-l_vals**2/(4.0*sigma_l**2)) + c_ls = c_ls_unnorm/np.linalg.norm(c_ls_unnorm) + return (l_vals, c_ls) + + def guess_omega_rabi(self, x, y): + ''' + Take the first time the flop goes above the average excitation of the whole scan + to be pi/4 + ''' + + mean = np.mean(y) + for x0, y0 in zip(x,y): + if y0 > mean: break + t_2pi = 4*x0 return 1/(t_2pi) \ No newline at end of file diff --git a/analysis/fit_rotramsey.py b/analysis/fit_rotramsey.py index c54ad4b..39cae68 100644 --- a/analysis/fit_rotramsey.py +++ b/analysis/fit_rotramsey.py @@ -1,85 +1,85 @@ -# Fitter class for Rabi flops - -from model import Model, ParameterInfo -from rabi.motional_distribution import motional_distribution as md -from rabi.rabi_coupling import rabi_coupling as rc -import scipy.constants as scc - -import math -import numpy as np - -class RotRamsey(Model): - - def __init__(self): - self.parameters = { - 'omega_rabi': ParameterInfo('f_rabi', 0, lambda x, y: 0.01, vary=True), - 'stdev_l': ParameterInfo('stdev_l', 1, lambda x, y: 50.0, vary=True), - 'sideband_order': ParameterInfo('sideband_order', 2, lambda x, y: 4, vary = False), - 'f_trap': ParameterInfo('f_trap', 3, lambda x, y: 0.845, vary=False), - 'f_rot': ParameterInfo('f_rot', 4, lambda x, y: 0.1, vary=False), - 'delta': ParameterInfo('delta', 5, lambda x, y: 0, vary=False), - 'scale': ParameterInfo('scale,', 6, lambda x, y: 1.0, vary=False) - } - - def model(self, x, p): - - omega_rabi = p[0] - stdev_l = p[1] - sideband_order = p[2] - f_trap = p[3] - f_rot = p[4] - delta = p[5] - scale = p[6] - - #calculate the radius of the ion 'ring' (half the distance between the ions) - r = (scc.e**2/(40*scc.atomic_mass*4*math.pi**2*(f_trap**2-f_rot**2)*1e12*16*math.pi*scc.epsilon_0))**(1./3) - self.omega_r = scc.hbar/(4*40*scc.atomic_mass*r**2) - - result = self.rot_ramsey(1e-6*x,sideband_order,stdev_l,omega_rabi,delta,scale) - - return result - - def rot_ramsey(self, times, order, sigma_l, Omega_MHz, delta_kHz=0.0, scale=1.0): - if sigma_l > 3000: - sigma_l = 3000.0 - - # Convert to SI units - Omega = Omega_MHz*2*np.pi*1e6 - delta = delta_kHz*2*np.pi*1e3 - - # Get distribution of l's, their respective detunings, and calculate the excitation vs. time - (l_vals, c_ls) = self.calc_ls_cls(sigma_l) - delta_ls = 2*self.omega_r*order*l_vals - delta # Array of detunings, one for each l - Omega_gens = np.sqrt(Omega**2 + delta_ls**2) #generalized Rabi frequencies - u1s = np.pi*Omega_gens/(4*Omega) - u2s = 1/2.0*np.outer(delta_ls, times) - exc = scale * np.sum(np.outer(np.abs(c_ls)**2, np.ones(len(times))) - *(np.outer(2*Omega/Omega_gens**2*np.sin(u1s), np.ones(len(times))) - *(np.outer(Omega_gens*np.cos(u1s), np.ones(len(times))) * np.cos(u2s) - -np.outer(delta_ls*np.sin(u1s), np.ones(len(times))) * np.sin(u2s) ))**2, axis=0) - - return exc - - def calc_ls_cls(self, sigma_l): - """Returns an array of l values and their amplitudes, given a standard deviation sigma_l - The distribution is Gaussian. - The center of the distribution is chosen to be centered at 0, which can be done without loss of generality - (all we care about is detunings, not absolute position in frequency space) - The l values returned are integers within 4*sigma""" - l_max = int(4*sigma_l) - l_vals = np.arange(-l_max, l_max+1) - c_ls_unnorm = np.exp(-l_vals**2/(4.0*sigma_l**2)) - c_ls = c_ls_unnorm/np.linalg.norm(c_ls_unnorm) - return (l_vals, c_ls) - - def guess_omega_rabi(self, x, y): - ''' - Take the first time the flop goes above the average excitation of the whole scan - to be pi/4 - ''' - - mean = np.mean(y) - for x0, y0 in zip(x,y): - if y0 > mean: break - t_2pi = 4*x0 +# Fitter class for Rabi flops + +from .model import Model, ParameterInfo +from .rabi.motional_distribution import motional_distribution as md +from .rabi.rabi_coupling import rabi_coupling as rc +import scipy.constants as scc + +import math +import numpy as np + +class RotRamsey(Model): + + def __init__(self): + self.parameters = { + 'omega_rabi': ParameterInfo('f_rabi', 0, lambda x, y: 0.01, vary=True), + 'stdev_l': ParameterInfo('stdev_l', 1, lambda x, y: 50.0, vary=True), + 'sideband_order': ParameterInfo('sideband_order', 2, lambda x, y: 4, vary = False), + 'f_trap': ParameterInfo('f_trap', 3, lambda x, y: 0.845, vary=False), + 'f_rot': ParameterInfo('f_rot', 4, lambda x, y: 0.1, vary=False), + 'delta': ParameterInfo('delta', 5, lambda x, y: 0, vary=False), + 'scale': ParameterInfo('scale,', 6, lambda x, y: 1.0, vary=False) + } + + def model(self, x, p): + + omega_rabi = p[0] + stdev_l = p[1] + sideband_order = p[2] + f_trap = p[3] + f_rot = p[4] + delta = p[5] + scale = p[6] + + #calculate the radius of the ion 'ring' (half the distance between the ions) + r = (scc.e**2/(40*scc.atomic_mass*4*math.pi**2*(f_trap**2-f_rot**2)*1e12*16*math.pi*scc.epsilon_0))**(1./3) + self.omega_r = scc.hbar/(4*40*scc.atomic_mass*r**2) + + result = self.rot_ramsey(1e-6*x,sideband_order,stdev_l,omega_rabi,delta,scale) + + return result + + def rot_ramsey(self, times, order, sigma_l, Omega_MHz, delta_kHz=0.0, scale=1.0): + if sigma_l > 3000: + sigma_l = 3000.0 + + # Convert to SI units + Omega = Omega_MHz*2*np.pi*1e6 + delta = delta_kHz*2*np.pi*1e3 + + # Get distribution of l's, their respective detunings, and calculate the excitation vs. time + (l_vals, c_ls) = self.calc_ls_cls(sigma_l) + delta_ls = 2*self.omega_r*order*l_vals - delta # Array of detunings, one for each l + Omega_gens = np.sqrt(Omega**2 + delta_ls**2) #generalized Rabi frequencies + u1s = np.pi*Omega_gens/(4*Omega) + u2s = 1/2.0*np.outer(delta_ls, times) + exc = scale * np.sum(np.outer(np.abs(c_ls)**2, np.ones(len(times))) + *(np.outer(2*Omega/Omega_gens**2*np.sin(u1s), np.ones(len(times))) + *(np.outer(Omega_gens*np.cos(u1s), np.ones(len(times))) * np.cos(u2s) + -np.outer(delta_ls*np.sin(u1s), np.ones(len(times))) * np.sin(u2s) ))**2, axis=0) + + return exc + + def calc_ls_cls(self, sigma_l): + """Returns an array of l values and their amplitudes, given a standard deviation sigma_l + The distribution is Gaussian. + The center of the distribution is chosen to be centered at 0, which can be done without loss of generality + (all we care about is detunings, not absolute position in frequency space) + The l values returned are integers within 4*sigma""" + l_max = int(4*sigma_l) + l_vals = np.arange(-l_max, l_max+1) + c_ls_unnorm = np.exp(-l_vals**2/(4.0*sigma_l**2)) + c_ls = c_ls_unnorm/np.linalg.norm(c_ls_unnorm) + return (l_vals, c_ls) + + def guess_omega_rabi(self, x, y): + ''' + Take the first time the flop goes above the average excitation of the whole scan + to be pi/4 + ''' + + mean = np.mean(y) + for x0, y0 in zip(x,y): + if y0 > mean: break + t_2pi = 4*x0 return 1/(t_2pi) \ No newline at end of file diff --git a/analysis/fit_sinusoid.py b/analysis/fit_sinusoid.py index bb7239e..7c319ed 100644 --- a/analysis/fit_sinusoid.py +++ b/analysis/fit_sinusoid.py @@ -1,22 +1,22 @@ -# Fitter class for Gaussians - -from model import Model, ParameterInfo -import numpy as np - -class Sinusoid(Model): - - def __init__(self): - self.parameters = { - 'contrast':ParameterInfo('contrast', 0, lambda x,y: 0.5, vary=True), - 'phi0': ParameterInfo('phi0', 1, lambda x,y: 0, vary=True), - 'offset': ParameterInfo('offset', 2, lambda x,y: 0, vary=False), - } - - def model(self, x, p): - contrast = p[0] - phi0 = p[1] - offset = p[2] - - phi_rad = x*np.pi/180.0 - phi0_rad = phi0*np.pi/180.0 +# Fitter class for Gaussians + +from .model import Model, ParameterInfo +import numpy as np + +class Sinusoid(Model): + + def __init__(self): + self.parameters = { + 'contrast':ParameterInfo('contrast', 0, lambda x,y: 0.5, vary=True), + 'phi0': ParameterInfo('phi0', 1, lambda x,y: 0, vary=True), + 'offset': ParameterInfo('offset', 2, lambda x,y: 0, vary=False), + } + + def model(self, x, p): + contrast = p[0] + phi0 = p[1] + offset = p[2] + + phi_rad = x*np.pi/180.0 + phi0_rad = phi0*np.pi/180.0 return 0.5 + contrast/2.0*np.sin(phi_rad-phi0_rad) + offset \ No newline at end of file diff --git a/analysis/fit_sinusoid2.py b/analysis/fit_sinusoid2.py index 244f2ff..e7ff85f 100644 --- a/analysis/fit_sinusoid2.py +++ b/analysis/fit_sinusoid2.py @@ -1,22 +1,22 @@ -# Fitter class for Gaussians - -from model import Model, ParameterInfo -import numpy as np - -class Sinusoid2(Model): - - def __init__(self): - self.parameters = { - 'contrast':ParameterInfo('contrast', 0, lambda x,y: 0.5, vary=True), - 'phi0': ParameterInfo('phi0', 1, lambda x,y: 0, vary=True), - 'offset': ParameterInfo('offset', 2, lambda x,y: 0, vary=False), - } - - def model(self, x, p): - contrast = p[0] - phi0 = p[1] - offset = p[2] - - phi_rad = x*np.pi/180.0 - phi0_rad = phi0*np.pi/180.0 +# Fitter class for Gaussians + +from .model import Model, ParameterInfo +import numpy as np + +class Sinusoid2(Model): + + def __init__(self): + self.parameters = { + 'contrast':ParameterInfo('contrast', 0, lambda x,y: 0.5, vary=True), + 'phi0': ParameterInfo('phi0', 1, lambda x,y: 0, vary=True), + 'offset': ParameterInfo('offset', 2, lambda x,y: 0, vary=False), + } + + def model(self, x, p): + contrast = p[0] + phi0 = p[1] + offset = p[2] + + phi_rad = x*np.pi/180.0 + phi0_rad = phi0*np.pi/180.0 return 0.5 + contrast/2.0*np.sin(2*(phi_rad-phi0_rad)) + offset \ No newline at end of file diff --git a/analysis/fitting.py b/analysis/fitting.py index 1c982b6..456aa16 100644 --- a/analysis/fitting.py +++ b/analysis/fitting.py @@ -1,152 +1,152 @@ -# Generic fitter class - -import numpy as np -from scipy import optimize -from fit_lorentzian import Lorentzian -from fit_gaussian import Gaussian -from fit_linear import Linear -from fit_rabi import Rabi -from fit_bessel import Bessel -from fit_rotrabi import RotRabi -from fit_rotramsey import RotRamsey -from fit_sinusoid import Sinusoid -from fit_sinusoid2 import Sinusoid2 -from fit_expdecay import ExponentialDecay -from fit_gaussdecay import GaussianDecay -from fit_ramsey import RamseyDecay -from fit_ramseybfield import RamseyBfield - -class FitWrapper(): - - models = ['Lorentzian', 'Gaussian', 'Rabi', 'RotRabi', 'RotRamsey', 'Linear', 'Bessel', 'Sinusoid', 'Sinusoid2', 'ExponentialDecay', 'GaussianDecay', 'RamseyDecay','RamseyBfield'] - - def __init__(self, dataset, index): - self.dataset = dataset - self.index = index - - def setModel(self, model): - - model_dict = { - 'Lorentzian': Lorentzian, - 'Gaussian': Gaussian, - 'Linear': Linear, - 'Rabi': Rabi, - 'RotRabi': RotRabi, - 'RotRamsey': RotRamsey, - 'Bessel': Bessel, - 'Sinusoid': Sinusoid, - 'Sinusoid2': Sinusoid2, - 'ExponentialDecay': ExponentialDecay, - 'GaussianDecay': GaussianDecay, - 'RamseyDecay': RamseyDecay, - 'RamseyBfield': RamseyBfield - } - self.model = model_dict[model]() - - def getParameters(self): - ''' - Returns a list of params - sorted in order of index - ''' - params = self.model.parameters.keys() - return sorted(params, key=lambda p: self.model.parameters[p].index) - - def getVary(self, p): - return self.model.parameters[p].vary - - def getManualValue(self, p): - try: - return self.model.parameters[p].manual_value - except: # value doesn't exist. Use automatic guess - x = self.dataset.data[:,0] - y = self.dataset.data[:,self.index+1] - guess = self.model.guess_param(p, x, y) - self.model.parameters[p].manual_value = guess - return guess - - def getFittedValue(self, p): - try: - return self.model.parameters[p].fit_value - except: # no fitted value exists yet - return None - - def setManualValue(self, p, value): - self.model.parameters[p].manual_value = value - - def setVary(self, p, value): - assert (value is True) or (value is False) - self.model.parameters[p].vary = value - - def doFit(self): - - x = self.dataset.data[:,0] - y = self.dataset.data[:, self.index + 1] - - def residual(p): - return y - self.model.reduced_model(x, p) - - varied_positions = self.model.varied_positions() - fixed_positions = self.model.fixed_positions() - x0 = [self.model.param_from_index(k).manual_value for k in varied_positions] - - result = optimize.leastsq(residual, x0) - result = result[0] - - # after the fit, assign the fitted values to the parameters - # For the fixed parameters, set the fit_value = manual_value - # so that we don't have to deal with it in the GUI display - for pos, fit_val in zip(varied_positions, result): - param = self.model.param_from_index(pos) - param.fit_value = fit_val - - for pos in fixed_positions: - param = self.model.param_from_index(pos) - param.fit_value = param.manual_value - - def evaluateFittedParameters(self): - ''' - Evaluate the model on a fine grid - Return 2-d numpy array data where - data[:,0] = fine_grid - data[:,1] = model evaluated on fitted parameters - ''' - - x = self.dataset.data[:,0] - n = len(x) - N = 10*n - xmin = x[0]; xmax = x[-1] - fine_grid = np.linspace(xmin, xmax, N) - - p0 = [] - for p in self.getParameters(): - p0.append(self.getFittedValue(p)) - y = self.model.model(fine_grid, p0) - - data = np.zeros((N, 2)) - data[:,0] = fine_grid - data[:,1] = y - return data - - def evaluateManualParameters(self): - ''' - Evaluate the model on a fine grid - Return 2-d numpy array data where - data[:,0] = fine_grid - data[:,1] = model evaluated on manual parameters - ''' - - x = self.dataset.data[:,0] - n = len(x) - N = 10*n - xmin = x[0]; xmax = x[-1] - fine_grid = np.linspace(xmin, xmax, N) - - p0 = [] - for p in self.getParameters(): - p0.append(self.getManualValue(p)) - y = self.model.model(fine_grid, p0) - - data = np.zeros((N, 2)) - data[:,0] = fine_grid - data[:,1] = y - return data +# Generic fitter class + +import numpy as np +from scipy import optimize +from .fit_lorentzian import Lorentzian +from .fit_gaussian import Gaussian +from .fit_linear import Linear +from .fit_rabi import Rabi +from .fit_bessel import Bessel +from .fit_rotrabi import RotRabi +from .fit_rotramsey import RotRamsey +from .fit_sinusoid import Sinusoid +from .fit_sinusoid2 import Sinusoid2 +from .fit_expdecay import ExponentialDecay +from .fit_gaussdecay import GaussianDecay +from .fit_ramsey import RamseyDecay +from .fit_ramseybfield import RamseyBfield + +class FitWrapper(): + + models = ['Lorentzian', 'Gaussian', 'Rabi', 'RotRabi', 'RotRamsey', 'Linear', 'Bessel', 'Sinusoid', 'Sinusoid2', 'ExponentialDecay', 'GaussianDecay', 'RamseyDecay','RamseyBfield'] + + def __init__(self, dataset, index): + self.dataset = dataset + self.index = index + + def setModel(self, model): + + model_dict = { + 'Lorentzian': Lorentzian, + 'Gaussian': Gaussian, + 'Linear': Linear, + 'Rabi': Rabi, + 'RotRabi': RotRabi, + 'RotRamsey': RotRamsey, + 'Bessel': Bessel, + 'Sinusoid': Sinusoid, + 'Sinusoid2': Sinusoid2, + 'ExponentialDecay': ExponentialDecay, + 'GaussianDecay': GaussianDecay, + 'RamseyDecay': RamseyDecay, + 'RamseyBfield': RamseyBfield + } + self.model = model_dict[model]() + + def getParameters(self): + ''' + Returns a list of params + sorted in order of index + ''' + params = self.model.parameters.keys() + return sorted(params, key=lambda p: self.model.parameters[p].index) + + def getVary(self, p): + return self.model.parameters[p].vary + + def getManualValue(self, p): + try: + return self.model.parameters[p].manual_value + except: # value doesn't exist. Use automatic guess + x = self.dataset.data[:,0] + y = self.dataset.data[:,self.index+1] + guess = self.model.guess_param(p, x, y) + self.model.parameters[p].manual_value = guess + return guess + + def getFittedValue(self, p): + try: + return self.model.parameters[p].fit_value + except: # no fitted value exists yet + return None + + def setManualValue(self, p, value): + self.model.parameters[p].manual_value = value + + def setVary(self, p, value): + assert (value is True) or (value is False) + self.model.parameters[p].vary = value + + def doFit(self): + + x = self.dataset.data[:,0] + y = self.dataset.data[:, self.index + 1] + + def residual(p): + return y - self.model.reduced_model(x, p) + + varied_positions = self.model.varied_positions() + fixed_positions = self.model.fixed_positions() + x0 = [self.model.param_from_index(k).manual_value for k in varied_positions] + + result = optimize.leastsq(residual, x0) + result = result[0] + + # after the fit, assign the fitted values to the parameters + # For the fixed parameters, set the fit_value = manual_value + # so that we don't have to deal with it in the GUI display + for pos, fit_val in zip(varied_positions, result): + param = self.model.param_from_index(pos) + param.fit_value = fit_val + + for pos in fixed_positions: + param = self.model.param_from_index(pos) + param.fit_value = param.manual_value + + def evaluateFittedParameters(self): + ''' + Evaluate the model on a fine grid + Return 2-d numpy array data where + data[:,0] = fine_grid + data[:,1] = model evaluated on fitted parameters + ''' + + x = self.dataset.data[:,0] + n = len(x) + N = 10*n + xmin = x[0]; xmax = x[-1] + fine_grid = np.linspace(xmin, xmax, N) + + p0 = [] + for p in self.getParameters(): + p0.append(self.getFittedValue(p)) + y = self.model.model(fine_grid, p0) + + data = np.zeros((N, 2)) + data[:,0] = fine_grid + data[:,1] = y + return data + + def evaluateManualParameters(self): + ''' + Evaluate the model on a fine grid + Return 2-d numpy array data where + data[:,0] = fine_grid + data[:,1] = model evaluated on manual parameters + ''' + + x = self.dataset.data[:,0] + n = len(x) + N = 10*n + xmin = x[0]; xmax = x[-1] + fine_grid = np.linspace(xmin, xmax, N) + + p0 = [] + for p in self.getParameters(): + p0.append(self.getManualValue(p)) + y = self.model.model(fine_grid, p0) + + data = np.zeros((N, 2)) + data[:,0] = fine_grid + data[:,1] = y + return data diff --git a/analysis/model.py b/analysis/model.py index 33c43b9..84e5b96 100644 --- a/analysis/model.py +++ b/analysis/model.py @@ -1,85 +1,85 @@ -# Fitting wrapper class - -class ParameterInfo(): - def __init__(self, parameter, index, guess_func, vary=True): - self.parameter = parameter - self.vary = vary - self.guess_func = guess_func - self.index = index - -class Model(): - - def __init__(self): - pass - - def model(self): - pass - - def guess_param(self, param, x, y): - return self.parameters[param].guess_func(x, y) - - def reduced_model(self, x, p): - ''' - reduced_model() allows model() to be optimized - if certain parameters are to be held fixed. - - Suppose model() needs N parameters, but we want to hold - k of them fixed. Then reduced_model() needs to accept - N - k parameters. - - Procedure: - full_params is an empty N-element list passed to model() - First, fill in the parameters which are not varied - into full_params. - Then, whichever places are unfilled in full_params, - fill them with the elements of p, an N - k parameter list. - Evaluate model on full_params. - ''' - - n = len(self.parameters.keys()) - full_params = [None for k in range(n)] - - for key in self.parameters.keys(): - if not self.parameters[key].vary: - index = self.parameters[key].index - value = self.parameters[key].manual_value - full_params[index] = value - - varied_positions = self.varied_positions() - for index, k in zip(varied_positions, p): - full_params[index] = k - return self.model(x, full_params) - - def varied_positions(self): - ''' - Indices of the parameters to vary in the fit - ''' - varied = [] - for param in self.parameters.keys(): - if self.parameters[param].vary: - varied.append(self.parameters[param].index) - varied = sorted(varied) - return varied - - def fixed_positions(self): - ''' - Indicies of the parameters to hold fixed in the fit - ''' - fixed = [] - for param in self.parameters.keys(): - if not self.parameters[param].vary: - fixed.append(self.parameters[param].index) - fixed = sorted(fixed) - return fixed - - def param_from_index(self, index): - ''' - Return a parameter from the index - ''' - - for param in self.parameters.keys(): - if self.parameters[param].index == index: - return self.parameters[param] - - # parmeter not found - raise Exception('Parameter not found') +# Fitting wrapper class + +class ParameterInfo(): + def __init__(self, parameter, index, guess_func, vary=True): + self.parameter = parameter + self.vary = vary + self.guess_func = guess_func + self.index = index + +class Model(): + + def __init__(self): + pass + + def model(self): + pass + + def guess_param(self, param, x, y): + return self.parameters[param].guess_func(x, y) + + def reduced_model(self, x, p): + ''' + reduced_model() allows model() to be optimized + if certain parameters are to be held fixed. + + Suppose model() needs N parameters, but we want to hold + k of them fixed. Then reduced_model() needs to accept + N - k parameters. + + Procedure: + full_params is an empty N-element list passed to model() + First, fill in the parameters which are not varied + into full_params. + Then, whichever places are unfilled in full_params, + fill them with the elements of p, an N - k parameter list. + Evaluate model on full_params. + ''' + + n = len(self.parameters.keys()) + full_params = [None for k in range(n)] + + for key in self.parameters.keys(): + if not self.parameters[key].vary: + index = self.parameters[key].index + value = self.parameters[key].manual_value + full_params[index] = value + + varied_positions = self.varied_positions() + for index, k in zip(varied_positions, p): + full_params[index] = k + return self.model(x, full_params) + + def varied_positions(self): + ''' + Indices of the parameters to vary in the fit + ''' + varied = [] + for param in self.parameters.keys(): + if self.parameters[param].vary: + varied.append(self.parameters[param].index) + varied = sorted(varied) + return varied + + def fixed_positions(self): + ''' + Indicies of the parameters to hold fixed in the fit + ''' + fixed = [] + for param in self.parameters.keys(): + if not self.parameters[param].vary: + fixed.append(self.parameters[param].index) + fixed = sorted(fixed) + return fixed + + def param_from_index(self, index): + ''' + Return a parameter from the index + ''' + + for param in self.parameters.keys(): + if self.parameters[param].index == index: + return self.parameters[param] + + # parmeter not found + raise Exception('Parameter not found') diff --git a/analysis/model_test.py b/analysis/model_test.py index b3ae352..25617a1 100644 --- a/analysis/model_test.py +++ b/analysis/model_test.py @@ -1,58 +1,58 @@ -# Generic class for testing -# models and fitting - -import numpy as np -import matplotlib.pyplot as plt -from fitting import FitWrapper - -class ModelTest(): - - def __init__(self, cls, name): - ''' - cls: model class - name: string to pass to FitWrapper - ''' - self.cls = cls() - self.name = name - - def generate_data(self, xmin, xmax, steps, noise, true_params): - ''' - Generate noisy data around the model - ''' - - class dataset(): - def __init__(self, x, y): - N = len(x) - self.data = np.zeros((N,2)) - self.data[:,0] = x - self.data[:,1] = y - - x = np.linspace(xmin, xmax, steps) - self.true_params = true_params - y = self.cls.model(x, true_params) + noise*np.random.normal(size=len(x)) - ds = dataset(x, y) - self.fw = FitWrapper(ds, 0) - self.fw.setModel(self.name) - - for p in self.fw.getParameters(): - self.fw.getManualValue(p) # force guess of initial parameters - - self.x = x - self.y = y - - def fit(self): - self.fw.doFit() - - def plot(self, fit = True): - plt.plot(self.x, self.y, 'ro') - if fit: - output = self.fw.evaluateFittedParameters() - plt.plot(output[:,0], output[:,1]) - plt.show() - - def print_results(self): - - print '***** FIT RESULTS *****' - print '{}\t{}\t{}'.format('PARAM','TRUE','FITTED') - for i, p in enumerate(self.fw.getParameters()): - print '{}\t{}\t{}'.format(p, self.true_params[i], self.fw.getFittedValue(p)) +# Generic class for testing +# models and fitting + +import numpy as np +import matplotlib.pyplot as plt +from .fitting import FitWrapper + +class ModelTest(): + + def __init__(self, cls, name): + ''' + cls: model class + name: string to pass to FitWrapper + ''' + self.cls = cls() + self.name = name + + def generate_data(self, xmin, xmax, steps, noise, true_params): + ''' + Generate noisy data around the model + ''' + + class dataset(): + def __init__(self, x, y): + N = len(x) + self.data = np.zeros((N,2)) + self.data[:,0] = x + self.data[:,1] = y + + x = np.linspace(xmin, xmax, steps) + self.true_params = true_params + y = self.cls.model(x, true_params) + noise*np.random.normal(size=len(x)) + ds = dataset(x, y) + self.fw = FitWrapper(ds, 0) + self.fw.setModel(self.name) + + for p in self.fw.getParameters(): + self.fw.getManualValue(p) # force guess of initial parameters + + self.x = x + self.y = y + + def fit(self): + self.fw.doFit() + + def plot(self, fit = True): + plt.plot(self.x, self.y, 'ro') + if fit: + output = self.fw.evaluateFittedParameters() + plt.plot(output[:,0], output[:,1]) + plt.show() + + def print_results(self): + + print('***** FIT RESULTS *****') + print('{}\t{}\t{}'.format('PARAM','TRUE','FITTED')) + for i, p in enumerate(self.fw.getParameters()): + print('{}\t{}\t{}'.format(p, self.true_params[i], self.fw.getFittedValue(p))) diff --git a/analysis/rabi/lamb_dicke.py b/analysis/rabi/lamb_dicke.py index 9d7f8ff..77cda24 100644 --- a/analysis/rabi/lamb_dicke.py +++ b/analysis/rabi/lamb_dicke.py @@ -1,26 +1,26 @@ -from labrad import units as U, types as T -import numpy as np - -class lamb_dicke(object): - - @classmethod - def lamb_dicke(self, trap_frequency, projection_angle, laser_wavelength = T.Value(729, 'nm'), amumass = 40): - '''computes the lamb dicke parameter - @var theta: angle between the laser and the mode of motion. 90 degrees is perpendicular - @var laser_wavelength: laser wavelength - @var trap_frequency: trap frequency - @amumass particle mass in amu - ''' - theta = projection_angle - frequency = trap_frequency - mass = amumass * U.amu - k = 2.*np.pi/laser_wavelength - eta = k*(U.hbar/(2*mass*2*np.pi*frequency))**.5 * np.abs(np.cos(theta*2.*np.pi / 360.0)) - eta = eta.inBaseUnits().value - return eta - -if __name__ == '__main__': - trap_frequency = T.Value(3.0, 'MHz') - projection_angle = 45 #degrees - eta = lamb_dicke.lamb_dicke(trap_frequency, projection_angle) - print 'eta {}'.format(eta) \ No newline at end of file +from labrad import units as U, types as T +import numpy as np + +class lamb_dicke(object): + + @classmethod + def lamb_dicke(self, trap_frequency, projection_angle, laser_wavelength = T.Value(729, 'nm'), amumass = 40): + '''computes the lamb dicke parameter + @var theta: angle between the laser and the mode of motion. 90 degrees is perpendicular + @var laser_wavelength: laser wavelength + @var trap_frequency: trap frequency + @amumass particle mass in amu + ''' + theta = projection_angle + frequency = trap_frequency + mass = amumass * U.amu + k = 2.*np.pi/laser_wavelength + eta = k*(U.hbar/(2*mass*2*np.pi*frequency))**.5 * np.abs(np.cos(theta*2.*np.pi / 360.0)) + eta = eta.inBaseUnits().value + return eta + +if __name__ == '__main__': + trap_frequency = T.Value(3.0, 'MHz') + projection_angle = 45 #degrees + eta = lamb_dicke.lamb_dicke(trap_frequency, projection_angle) + print('eta {}'.format(eta)) \ No newline at end of file diff --git a/analysis/rabi/motional_distribution.py b/analysis/rabi/motional_distribution.py index 1cd1c12..58abf8b 100644 --- a/analysis/rabi/motional_distribution.py +++ b/analysis/rabi/motional_distribution.py @@ -1,102 +1,102 @@ -import numpy as np -from scipy.special.orthogonal import eval_genlaguerre as laguerre -from scipy.misc import factorial -import mpmath as mp - -#see Michael Ramm writeup on 'Displaced Coherent States' and references therein. - -class motional_distribution(object): - - @classmethod - def thermal(cls, nbar, dimension): - ''' - returns a thermal distribution of occupational numbers of the required dimensions - @param nbar: temperature - @param dimension: number of required entries - ''' - return np.fromfunction(lambda n: cls._thermal(nbar, n), (dimension,)) - - @classmethod - def _thermal(cls, nbar, n): - #level population probability for a given nbar, see Leibfried 2003 (57) - return 1./ (nbar + 1.0) * (nbar / (nbar + 1.0))**n - - @classmethod - def displaced_thermal(cls, alpha, nbar, dimension): - ''' - outputs occupational numbers for a displaced thermal distribution - @param alpha: coherent displacement - @param nbar: temperature of the thermal distribution - @param dimension: required number of modes - ''' - return np.fromfunction(lambda n: cls._displaced_thermal(alpha, nbar, n), (dimension,)) - - @classmethod - def _displaced_thermal(cls, alpha, nbar, n): - ''' - returns the motional state distribution with the displacement alpha, motional temperature nbar for the state n - ''' - #this is needed because for some inputs (larrge n or small nbar, this term is 0 while the laguerre term is infinite. their product is zero but beyond the floating point precision - try: - old_settings = np.seterr(invalid='raise') - populations = 1./ (nbar + 1.0) * (nbar / (nbar + 1.0))**n * laguerre(n, 0 , -alpha**2 / ( nbar * (nbar + 1.0))) * np.exp( -alpha**2 / (nbar + 1.0)) - except FloatingPointError: - np.seterr(**old_settings) - print 'precise calculation required', alpha, nbar - populations = [mp.fprod(( 1./ (nbar + 1.0), mp.power(nbar / (nbar + 1.0), k), mp.laguerre(k, 0, -alpha**2 / ( nbar * (nbar + 1.0))), mp.exp(-alpha**2 / (nbar + 1.0)))) for k in n] - print 'done computing populations' - populations = np.array(populations) - print 'returned array' - return populations - - @classmethod - def test_displaced_thermal(cls): - ''' - compares the result of the computed distribution with the same result obtained through qutip - ''' - alpha = np.sqrt(5) * np.random.ranf() - nbar = 5 * np.random.ranf() - test_entries = 100 - computed = cls.displaced_thermal(alpha, nbar, test_entries) - from qutip import thermal_dm, displace - thermal_dm = thermal_dm(test_entries, nbar, method = 'analytic') - displace_operator = displace(test_entries, alpha) - displaced_dm = displace_operator * thermal_dm * displace_operator.dag() - qutip_result = displaced_dm.diag() - return np.allclose(qutip_result, computed) - - @classmethod - def test_thermal_distribution(cls): - ''' - compares the result of the computed distribution with the same result obtained through qutip - ''' - nbar = 3 * np.random.ranf() - test_entries = 10 - computed = cls.thermal(nbar, test_entries) - from qutip import thermal_dm - thermal_qutip = thermal_dm(test_entries, nbar, method = 'analytic').diag() - return np.allclose(thermal_qutip, computed) - -if __name__ == '__main__': - - md = motional_distribution - - def plot_displaced(): - from matplotlib import pyplot - hilbert_space_dimension = 5000 - nbar = 3.0 - for displ,color in [(0, 'k'), (5, 'b'), (10, 'g'), (2500, 'r')]: - displacement_nbar = displ - displacement_alpha = np.sqrt(displacement_nbar) - distribution = md.displaced_thermal(displacement_alpha, nbar, hilbert_space_dimension) - print distribution.sum() - pyplot.plot(distribution, 'x', color = color, label = 'displacement = {} nbar'.format(displ)) - - pyplot.title('Init temperature 3nbar', fontsize = 16) - pyplot.suptitle('Displaced Thermal States', fontsize = 20) - pyplot.legend() - pyplot.show() - -# print md.test_thermal_distribution() -# print md.test_displaced_thermal() +import numpy as np +from scipy.special.orthogonal import eval_genlaguerre as laguerre +from scipy.special import factorial +import mpmath as mp + +#see Michael Ramm writeup on 'Displaced Coherent States' and references therein. + +class motional_distribution(object): + + @classmethod + def thermal(cls, nbar, dimension): + ''' + returns a thermal distribution of occupational numbers of the required dimensions + @param nbar: temperature + @param dimension: number of required entries + ''' + return np.fromfunction(lambda n: cls._thermal(nbar, n), (dimension,)) + + @classmethod + def _thermal(cls, nbar, n): + #level population probability for a given nbar, see Leibfried 2003 (57) + return 1./ (nbar + 1.0) * (nbar / (nbar + 1.0))**n + + @classmethod + def displaced_thermal(cls, alpha, nbar, dimension): + ''' + outputs occupational numbers for a displaced thermal distribution + @param alpha: coherent displacement + @param nbar: temperature of the thermal distribution + @param dimension: required number of modes + ''' + return np.fromfunction(lambda n: cls._displaced_thermal(alpha, nbar, n), (dimension,)) + + @classmethod + def _displaced_thermal(cls, alpha, nbar, n): + ''' + returns the motional state distribution with the displacement alpha, motional temperature nbar for the state n + ''' + #this is needed because for some inputs (larrge n or small nbar, this term is 0 while the laguerre term is infinite. their product is zero but beyond the floating point precision + try: + old_settings = np.seterr(invalid='raise') + populations = 1./ (nbar + 1.0) * (nbar / (nbar + 1.0))**n * laguerre(n, 0 , -alpha**2 / ( nbar * (nbar + 1.0))) * np.exp( -alpha**2 / (nbar + 1.0)) + except FloatingPointError: + np.seterr(**old_settings) + print('precise calculation required', alpha, nbar) + populations = [mp.fprod(( 1./ (nbar + 1.0), mp.power(nbar / (nbar + 1.0), k), mp.laguerre(k, 0, -alpha**2 / ( nbar * (nbar + 1.0))), mp.exp(-alpha**2 / (nbar + 1.0)))) for k in n] + print('done computing populations') + populations = np.array(populations) + print('returned array') + return populations + + @classmethod + def test_displaced_thermal(cls): + ''' + compares the result of the computed distribution with the same result obtained through qutip + ''' + alpha = np.sqrt(5) * np.random.ranf() + nbar = 5 * np.random.ranf() + test_entries = 100 + computed = cls.displaced_thermal(alpha, nbar, test_entries) + from qutip import thermal_dm, displace + thermal_dm = thermal_dm(test_entries, nbar, method = 'analytic') + displace_operator = displace(test_entries, alpha) + displaced_dm = displace_operator * thermal_dm * displace_operator.dag() + qutip_result = displaced_dm.diag() + return np.allclose(qutip_result, computed) + + @classmethod + def test_thermal_distribution(cls): + ''' + compares the result of the computed distribution with the same result obtained through qutip + ''' + nbar = 3 * np.random.ranf() + test_entries = 10 + computed = cls.thermal(nbar, test_entries) + from qutip import thermal_dm + thermal_qutip = thermal_dm(test_entries, nbar, method = 'analytic').diag() + return np.allclose(thermal_qutip, computed) + +if __name__ == '__main__': + + md = motional_distribution + + def plot_displaced(): + from matplotlib import pyplot + hilbert_space_dimension = 5000 + nbar = 3.0 + for displ,color in [(0, 'k'), (5, 'b'), (10, 'g'), (2500, 'r')]: + displacement_nbar = displ + displacement_alpha = np.sqrt(displacement_nbar) + distribution = md.displaced_thermal(displacement_alpha, nbar, hilbert_space_dimension) + print(distribution.sum()) + pyplot.plot(distribution, 'x', color = color, label = 'displacement = {} nbar'.format(displ)) + + pyplot.title('Init temperature 3nbar', fontsize = 16) + pyplot.suptitle('Displaced Thermal States', fontsize = 20) + pyplot.legend() + pyplot.show() + +# print(md.test_thermal_distribution()) +# print(md.test_displaced_thermal()) plot_displaced() \ No newline at end of file diff --git a/analysis/rabi/rabi_coupling.py b/analysis/rabi/rabi_coupling.py index 400ce33..5b3197e 100644 --- a/analysis/rabi/rabi_coupling.py +++ b/analysis/rabi/rabi_coupling.py @@ -1,35 +1,35 @@ -import numpy as np -from scipy.special.orthogonal import eval_genlaguerre as laguerre - -class rabi_coupling(object): - - @classmethod - def compute_rabi_coupling(cls, eta, sideband_order, nmax): - ''' - Rabi couplings, see Leibfried (2003), eq:70 - ''' - if sideband_order == 0: - coupling_func = lambda n: np.exp(-1./2*eta**2) * laguerre(n, 0, eta**2) - elif sideband_order == 1: - coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(1)*(1./(n+1.))**0.5 * laguerre(n, 1, eta**2) - elif sideband_order == 2: - coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(2)*(1./((n+1.)*(n+2)))**0.5 * laguerre(n, 2, eta**2) - elif sideband_order == 3: - coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(3)*(1./((n+1)*(n+2)*(n+3)))**0.5 * laguerre(n, 3 , eta**2) - elif sideband_order == 4: - coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(4)*(1./((n+1)*(n+2)*(n+3)*(n+4)))**0.5 * laguerre(n, 4 , eta**2) - elif sideband_order == 5: - coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(5)*(1./((n+1)*(n+2)*(n+3)*(n+4)*(n+5)))**0.5 * laguerre(n, 5 , eta**2) - elif sideband_order == -1: - coupling_func = lambda n: 0 if n == 0 else np.exp(-1./2*eta**2) * eta**(1)*(1./(n))**0.5 * laguerre(n - 1, 1, eta**2) - elif sideband_order == -2: - coupling_func = lambda n: 0 if n <= 1 else np.exp(-1./2*eta**2) * eta**(2)*(1./((n)*(n-1.)))**0.5 * laguerre(n - 2, 2, eta**2) - elif sideband_order == -3: - coupling_func = lambda n: 0 if n <= 2 else np.exp(-1./2*eta**2) * eta**(3)*(1./((n)*(n-1.)*(n-2)))**0.5 * laguerre(n -3, 3, eta**2) - elif sideband_order == -4: - coupling_func = lambda n: 0 if n <= 3 else np.exp(-1./2*eta**2) * eta**(4)*(1./((n)*(n-1.)*(n-2)*(n-3)))**0.5 * laguerre(n -4, 4, eta**2) - elif sideband_order == -5: - coupling_func = lambda n: 0 if n <= 4 else np.exp(-1./2*eta**2) * eta**(5)*(1./((n)*(n-1.)*(n-2)*(n-3)*(n-4)))**0.5 * laguerre(n -5, 5, eta**2) - else: - raise NotImplementedError("Can't calculate rabi couplings sideband order {}".format(sideband_order)) - return np.array([coupling_func(n) for n in range(nmax)]) +import numpy as np +from scipy.special.orthogonal import eval_genlaguerre as laguerre + +class rabi_coupling(object): + + @classmethod + def compute_rabi_coupling(cls, eta, sideband_order, nmax): + ''' + Rabi couplings, see Leibfried (2003), eq:70 + ''' + if sideband_order == 0: + coupling_func = lambda n: np.exp(-1./2*eta**2) * laguerre(n, 0, eta**2) + elif sideband_order == 1: + coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(1)*(1./(n+1.))**0.5 * laguerre(n, 1, eta**2) + elif sideband_order == 2: + coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(2)*(1./((n+1.)*(n+2)))**0.5 * laguerre(n, 2, eta**2) + elif sideband_order == 3: + coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(3)*(1./((n+1)*(n+2)*(n+3)))**0.5 * laguerre(n, 3 , eta**2) + elif sideband_order == 4: + coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(4)*(1./((n+1)*(n+2)*(n+3)*(n+4)))**0.5 * laguerre(n, 4 , eta**2) + elif sideband_order == 5: + coupling_func = lambda n: np.exp(-1./2*eta**2) * eta**(5)*(1./((n+1)*(n+2)*(n+3)*(n+4)*(n+5)))**0.5 * laguerre(n, 5 , eta**2) + elif sideband_order == -1: + coupling_func = lambda n: 0 if n == 0 else np.exp(-1./2*eta**2) * eta**(1)*(1./(n))**0.5 * laguerre(n - 1, 1, eta**2) + elif sideband_order == -2: + coupling_func = lambda n: 0 if n <= 1 else np.exp(-1./2*eta**2) * eta**(2)*(1./((n)*(n-1.)))**0.5 * laguerre(n - 2, 2, eta**2) + elif sideband_order == -3: + coupling_func = lambda n: 0 if n <= 2 else np.exp(-1./2*eta**2) * eta**(3)*(1./((n)*(n-1.)*(n-2)))**0.5 * laguerre(n -3, 3, eta**2) + elif sideband_order == -4: + coupling_func = lambda n: 0 if n <= 3 else np.exp(-1./2*eta**2) * eta**(4)*(1./((n)*(n-1.)*(n-2)*(n-3)))**0.5 * laguerre(n -4, 4, eta**2) + elif sideband_order == -5: + coupling_func = lambda n: 0 if n <= 4 else np.exp(-1./2*eta**2) * eta**(5)*(1./((n)*(n-1.)*(n-2)*(n-3)*(n-4)))**0.5 * laguerre(n -5, 5, eta**2) + else: + raise NotImplementedError("Can't calculate rabi couplings sideband order {}".format(sideband_order)) + return np.array([coupling_func(n) for n in range(nmax)]) diff --git a/analysis/test_bessel.py b/analysis/test_bessel.py index f23d2dc..54bbd7e 100644 --- a/analysis/test_bessel.py +++ b/analysis/test_bessel.py @@ -1,10 +1,10 @@ -# test script for Lorentzian fits -from model_test import ModelTest -from fit_bessel import Bessel - -test = ModelTest(Bessel, 'Bessel') -true_params = [130., 1., 5., 0.1, 1] -test.generate_data(100, 200, 200, 0.02, 0.5, true_params) -test.fit() -test.print_results() -test.plot() +# test script for Lorentzian fits +from .model_test import ModelTest +from .fit_bessel import Bessel + +test = ModelTest(Bessel, 'Bessel') +true_params = [130., 1., 5., 0.1, 1] +test.generate_data(100, 200, 200, 0.02, 0.5, true_params) +test.fit() +test.print_results() +test.plot() diff --git a/analysis/test_gaussian.py b/analysis/test_gaussian.py index 717563d..cda11d9 100644 --- a/analysis/test_gaussian.py +++ b/analysis/test_gaussian.py @@ -1,11 +1,11 @@ -# test for Gaussian fits - -from model_test import ModelTest -from fit_gaussian import Gaussian - -test = ModelTest(Gaussian, 'Gaussian') -true_params = [130., 4., 5., 0.1] -test.generate_data(100, 200, 200, 0.02, true_params) -test.fit() -test.print_results() -test.plot() +# test for Gaussian fits + +from .model_test import ModelTest +from .fit_gaussian import Gaussian + +test = ModelTest(Gaussian, 'Gaussian') +true_params = [130., 4., 5., 0.1] +test.generate_data(100, 200, 200, 0.02, true_params) +test.fit() +test.print_results() +test.plot() diff --git a/analysis/test_linear.py b/analysis/test_linear.py index ee8d909..784f3cc 100644 --- a/analysis/test_linear.py +++ b/analysis/test_linear.py @@ -1,11 +1,11 @@ -# test for linear fits - -from model_test import ModelTest -from fit_linear import Linear - -test = ModelTest(Linear, 'Linear') -true_params = [0.3, 4] -test.generate_data(10, 20, 40, 1, true_params) -test.fit() -test.print_results() -test.plot() +# test for linear fits + +from .model_test import ModelTest +from .fit_linear import Linear + +test = ModelTest(Linear, 'Linear') +true_params = [0.3, 4] +test.generate_data(10, 20, 40, 1, true_params) +test.fit() +test.print_results() +test.plot() diff --git a/analysis/test_lorentzian.py b/analysis/test_lorentzian.py index d47d21e..f0f1acf 100644 --- a/analysis/test_lorentzian.py +++ b/analysis/test_lorentzian.py @@ -1,10 +1,10 @@ -# test script for Lorentzian fits -from model_test import ModelTest -from fit_lorentzian import Lorentzian - -test = ModelTest(Lorentzian, 'Lorentzian') -true_params = [130., 1., 5., 0.1] -test.generate_data(100, 200, 200, 0.02, true_params) -test.fit() -test.print_results() -test.plot() +# test script for Lorentzian fits +from .model_test import ModelTest +from .fit_lorentzian import Lorentzian + +test = ModelTest(Lorentzian, 'Lorentzian') +true_params = [130., 1., 5., 0.1] +test.generate_data(100, 200, 200, 0.02, true_params) +test.fit() +test.print_results() +test.plot() diff --git a/analysis/test_rabi.py b/analysis/test_rabi.py index 3259bc0..4dc5fd9 100644 --- a/analysis/test_rabi.py +++ b/analysis/test_rabi.py @@ -1,14 +1,14 @@ -# test for Rabi flop fits - -from model_test import ModelTest -from fit_rabi import Rabi - -import numpy as np - - -test = ModelTest(Rabi, 'Rabi') -true_params = [2*np.pi/(10), 10, 0.05, 0., 0, 0.6] -test.generate_data(0, 30, 300, 0.02, true_params) -test.fit() -test.print_results() -test.plot(fit=True) +# test for Rabi flop fits + +from .model_test import ModelTest +from .fit_rabi import Rabi + +import numpy as np + + +test = ModelTest(Rabi, 'Rabi') +true_params = [2*np.pi/(10), 10, 0.05, 0., 0, 0.6] +test.generate_data(0, 30, 300, 0.02, true_params) +test.fit() +test.print_results() +test.plot(fit=True) diff --git a/analysis/testfit.py b/analysis/testfit.py index 27cd1aa..2daf42c 100644 --- a/analysis/testfit.py +++ b/analysis/testfit.py @@ -1,15 +1,15 @@ -import numpy as np -from scipy import optimize - -def func(x, p): - return p[0] * np.exp(-p[1] * x) + p[2] - -xdata = np.linspace(0, 4, 50) -y = func(xdata, [2.5, 1.3, 0.5]) -ydata = y + 0.2 * np.random.normal(size=len(xdata)) - -def residual(p): - return ydata - func(xdata, p) - -result = optimize.leastsq(residual, [2.5, 1.3, 0.5]) -print result[0] +import numpy as np +from scipy import optimize + +def func(x, p): + return p[0] * np.exp(-p[1] * x) + p[2] + +xdata = np.linspace(0, 4, 50) +y = func(xdata, [2.5, 1.3, 0.5]) +ydata = y + 0.2 * np.random.normal(size=len(xdata)) + +def residual(p): + return ydata - func(xdata, p) + +result = optimize.leastsq(residual, [2.5, 1.3, 0.5]) +print(result[0]) diff --git a/depricated/GraphWidget.py b/depricated/GraphWidget.py deleted file mode 100644 index 9e6b33a..0000000 --- a/depricated/GraphWidget.py +++ /dev/null @@ -1,120 +0,0 @@ -import sys -from PyQt4 import QtGui - -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar -import matplotlib.pyplot as plt -import matplotlib.animation as animation -from TraceListWidget import TraceList -from twisted.internet.defer import Deferred, inlineCallbacks, returnValue -from twisted.internet.task import LoopingCall -from twisted.internet.threads import blockingCallFromThread - -from Dataset import Dataset - -import numpy as np - - -class Graph(QtGui.QWidget): - def __init__(self, name, reactor, parent=None, ylim=[0,1]): - super(Graph, self).__init__(parent) - self.reactor = reactor - self.artists = {} - self.datasets = {} # a single dataset might have multiple traces - self.should_stop = False - self.name = name - self.initUI(ylim) - - def initUI(self, ylim): - self.figure = plt.figure() - self.canvas = FigureCanvas(self.figure) - self.toolbar = NavigationToolbar(self.canvas, self) - - self.tracelist = TraceList() - - self.ax = self.figure.add_subplot(111) - self.ani = animation.FuncAnimation(self.figure, self.update_figure, self.should_continue, init_func=self.init, interval=25, blit=False) - - self.ax.set_xlim([0, 100]) - self.ax.set_ylim(ylim) - self.ax.set_title(self.name) - - hbox = QtGui.QHBoxLayout() - hbox.addWidget(self.tracelist) - vbox = QtGui.QVBoxLayout() - vbox.addWidget(self.toolbar) - vbox.addWidget(self.canvas) - hbox.addLayout(vbox) - self.setLayout(hbox) - #self.draw_stuff() - - def init(self): - line, = self.ax.plot([], []) - return line, - - def update_figure(self, _input = None): - artists = [] - for ident, (artist, dataset, index) in self.artists.iteritems(): - x = dataset.data[:,0] - y = dataset.data[:,index+1] - artist.set_data((x,y)) - artists.append(artist) - return artists - - def add_artist(self, ident, dataset, index): - line, = self.ax.plot([], [], '-o', markersize = 1.0, label = ident) - #self.ax.legend() - # dataset is the dataset object - # index is the position in the dataset object this trace lives - self.artists[ident] = [line, dataset, index] - self.tracelist.addTrace(ident) - - # connect the checkbox in the tracelist - self.tracelist.itemChanged.connect(self.checkboxChanged) - #cb = self.tracelist.trace_dict[ident] - #cb.itemChanged.connect(self.checkboxChanged) - - def display(self, ident, shown): - try: - self.artists[ident][0].set_visible(shown) - except KeyError: - raise Exception('404 Artist not found') - self.canvas.draw() - - def checkboxChanged(self, state): - for ident, item in self.tracelist.trace_dict.iteritems(): - if item.checkState(): - self.display(ident, True) - else: - self.display(ident, False) - - def should_continue(self): - while True: - if self.should_stop: return - yield True - - @inlineCallbacks - def add_dataset(self, dataset): - labels = yield dataset.getLabels() - for i, label in enumerate(labels): - self.add_artist(label, dataset, i) - - def set_xlimits(self, limits): - self.ax.set_xlim(limits) - self.current_limits = limits - - def set_ylimits(self, limits): - self.ax.set_ylim(limits) - - def redraw(self): - self.canvas.draw() - -if __name__ == '__main__': - app = QtGui.QApplication(sys.argv) - import qt4reactor - qt4reactor.install() - from twisted.internet import reactor - main = Graph('example', reactor) - main.show() - #sys.exit(app.exec_()) - reactor.run() diff --git a/depricated/ScrollingGraphWidget.py b/depricated/ScrollingGraphWidget.py deleted file mode 100644 index 3e12613..0000000 --- a/depricated/ScrollingGraphWidget.py +++ /dev/null @@ -1,40 +0,0 @@ -from GraphWidget import Graph - -''' -This is just the Graph with the update function -modified to also do scrolling -''' - -class ScrollingGraph(Graph): - - def __init__(self, name, reactor, parent = None, ylim = [0,1]): - super(ScrollingGraph, self).__init__(name, reactor, parent, ylim) - - def update_figure(self, _input = None): - artists = [] - for ident, (artist, dataset, index) in self.artists.iteritems(): - x = dataset.data[:,0] - y = dataset.data[:,index+1] - artist.set_data((x,y)) - artists.append(artist) - try: - pointsToKeep = 100 - if len(x) < pointsToKeep: - self.set_xlimits( [min(x), max(x)] ) - else: - # see if we need to redraw - xmin_cur, xmax_cur = self.current_limits - x_cur = x[-1] # current largest x value - window_width = xmax_cur - xmin_cur - - # scroll if we've reached 75% of the window - if (x_cur - xmin_cur > 0.75*window_width): - shift = (xmax_cur - xmin_cur)/2.0 - xmin = xmin_cur + shift - xmax = xmax_cur + shift - self.set_xlimits( [xmin, xmax] ) - #plt.draw() - except: - pass - return artists - diff --git a/qt4reactor.py b/qt4reactor.py deleted file mode 100644 index 5a61d9f..0000000 --- a/qt4reactor.py +++ /dev/null @@ -1,253 +0,0 @@ -# Copyright (c) 2001-2008 Twisted Matrix Laboratories. -# See LICENSE for details. - - -""" -This module provides support for Twisted to be driven by the Qt mainloop. - -In order to use this support, simply do the following:: - | app = QApplication(sys.argv) # your code to init Qt - | import qt4reactor - | qt4reactor.install() - -alternatively: - - | from twisted.application import reactors - | reactors.installReactor('qt4') - -Then use twisted.internet APIs as usual. The other methods here are not -intended to be called directly. - -If you don't instantiate a QApplication or QCoreApplication prior to -installing the reactor, a QCoreApplication will be constructed -by the reactor. QCoreApplication does not require a GUI so trial testing -can occur normally. - -Twisted can be initialized after QApplication.exec_() with a call to -reactor.runReturn(). calling reactor.stop() will unhook twisted but -leave your Qt application running - -API Stability: stable - -Maintainer: U{Glenn H Tarbox, PhD} - -Previous maintainer: U{Itamar Shtull-Trauring} -Original port to QT4: U{Gabe Rudy} -Subsequent port by therve -""" - -__all__ = ['install'] - - -import sys, time - -from zope.interface import implements - -from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication -from PyQt4.QtCore import QEventLoop - -from twisted.internet.interfaces import IReactorFDSet -from twisted.python import log -from twisted.internet.posixbase import PosixReactorBase - -class TwistedSocketNotifier(QSocketNotifier): - """ - Connection between an fd event and reader/writer callbacks. - """ - - def __init__(self, reactor, watcher, type): - QSocketNotifier.__init__(self, watcher.fileno(), type) - self.reactor = reactor - self.watcher = watcher - self.fn = None - if type == QSocketNotifier.Read: - self.fn = self.read - elif type == QSocketNotifier.Write: - self.fn = self.write - QObject.connect(self, SIGNAL("activated(int)"), self.fn) - - - def shutdown(self): - QObject.disconnect(self, SIGNAL("activated(int)"), self.fn) - self.setEnabled(False) - self.fn = self.watcher = None - self.deleteLater() - - - def read(self, sock): - w = self.watcher - #self.setEnabled(False) # ??? do I need this? - def _read(): - why = None - try: - why = w.doRead() - except: - log.err() - why = sys.exc_info()[1] - if why: - self.reactor._disconnectSelectable(w, why, True) - elif self.watcher: - pass - #self.setEnabled(True) - log.callWithLogger(w, _read) - self.reactor.reactorInvocation() - - def write(self, sock): - w = self.watcher - self.setEnabled(False) - def _write(): - why = None - try: - why = w.doWrite() - except: - log.err() - why = sys.exc_info()[1] - if why: - self.reactor._disconnectSelectable(w, why, False) - elif self.watcher: - self.setEnabled(True) - log.callWithLogger(w, _write) - self.reactor.reactorInvocation() - -class fakeApplication(QEventLoop): - def __init__(self): - QEventLoop.__init__(self) - - def exec_(self): - QEventLoop.exec_(self) - -class QTReactor(PosixReactorBase): - """ - Qt based reactor. - """ - implements(IReactorFDSet) - - _timer = None - - def __init__(self): - self._reads = {} - self._writes = {} - self._timer=QTimer() - self._timer.setSingleShot(True) - if QCoreApplication.startingUp(): - self.qApp=QCoreApplication([]) - self._ownApp=True - else: - self.qApp = QCoreApplication.instance() - self._ownApp=False - self._blockApp = None - self._readWriteQ=[] - - """ some debugging instrumentation """ - self._doSomethingCount=0 - - PosixReactorBase.__init__(self) - - def addReader(self, reader): - if not reader in self._reads: - self._reads[reader] = TwistedSocketNotifier(self, reader, - QSocketNotifier.Read) - - - def addWriter(self, writer): - if not writer in self._writes: - self._writes[writer] = TwistedSocketNotifier(self, writer, - QSocketNotifier.Write) - - - def removeReader(self, reader): - if reader in self._reads: - #self._reads[reader].shutdown() - #del self._reads[reader] - self._reads.pop(reader).shutdown() - - def removeWriter(self, writer): - if writer in self._writes: - self._writes[writer].shutdown() - #del self._writes[writer] - self._writes.pop(writer) - - - def removeAll(self): - return self._removeAll(self._reads, self._writes) - - - def getReaders(self): - return self._reads.keys() - - - def getWriters(self): - return self._writes.keys() - - def callLater(self,howlong, *args, **kargs): - rval = super(QTReactor,self).callLater(howlong, *args, **kargs) - self.reactorInvocation() - return rval - - def crash(self): - super(QTReactor,self).crash() - - def iterate(self,delay=0.0): - t=self.running # not sure I entirely get the state of running - self.running=True - self._timer.stop() # in case its not (rare?) - try: - if delay == 0.0: - self.reactorInvokePrivate() - self._timer.stop() # supports multiple invocations - else: - endTime = delay + time.time() - self.reactorInvokePrivate() - while True: - t = endTime - time.time() - if t <= 0.0: return - self.qApp.processEvents(QEventLoop.AllEvents | - QEventLoop.WaitForMoreEvents,t*1010) - finally: - self.running=t - - def addReadWrite(self,t): - self._readWriteQ.append(t) - - def runReturn(self, installSignalHandlers=True): - QObject.connect(self._timer, SIGNAL("timeout()"), - self.reactorInvokePrivate) - self.startRunning(installSignalHandlers=installSignalHandlers) - self._timer.start(0) - - def run(self, installSignalHandlers=True): - try: - if self._ownApp: - self._blockApp=self.qApp - else: - self._blockApp = fakeApplication() - self.runReturn(installSignalHandlers) - self._blockApp.exec_() - finally: - self._timer.stop() # should already be stopped - - def reactorInvocation(self): - self._timer.setInterval(0) - - def reactorInvokePrivate(self): - if not self.running: - self._blockApp.quit() - self._doSomethingCount += 1 - self.runUntilCurrent() - t = self.timeout() - if t is None: t=0.1 - else: t = min(t,0.1) - self._timer.setInterval(t*1010) - self.qApp.processEvents() # could change interval - self._timer.start() - - def doIteration(self): - assert False, "doiteration is invalid call" - -def install(): - """ - Configure the twisted mainloop to be run inside the qt mainloop. - """ - from twisted.internet import main - reactor = QTReactor() - main.installReactor(reactor) diff --git a/rsg.py b/rsg.py index c73ea4e..6622606 100644 --- a/rsg.py +++ b/rsg.py @@ -1,84 +1,87 @@ -''' -The Real Simple Grapher -''' - -from GraphWindow import GraphWindow -from Dataset import Dataset -from PyQt4 import QtGui -a = QtGui.QApplication( []) -import qt4reactor -qt4reactor.install() -#import server libraries -from twisted.internet.defer import returnValue, DeferredLock, Deferred, inlineCallbacks -from twisted.internet.threads import deferToThread -from twisted.internet import reactor -from labrad.server import LabradServer, setting - - -""" -### BEGIN NODE INFO -[info] -name = Real Simple Grapher -version = 1.0 -description = -instancename = Real Simple Grapher -[startup] -cmdline = %PYTHON% %FILE% -timeout = 20 -[shutdown] -message = 987654321 -timeout = 5 -### END NODE INFO -""" - -class RealSimpleGrapher(LabradServer): - - """ Methods for controlling graphing """ - - name = "Grapher" - - @inlineCallbacks - def initServer(self): - self.listeners = set() - self.gui = GraphWindow(reactor, cxn = self.client) - self.gui.setWindowTitle('Real Simple Grapher') - self.dv = yield self.client.data_vault - self.pv = yield self.client.parametervault - - def make_dataset(self, dataset_location): - cxt = self.client.context() - ds = Dataset(self.dv, cxt, dataset_location, reactor) - return ds - - def do_plot(self, dataset_location, graph, send_to_current): - if (graph != 'current') and (send_to_current == True): - # add the plot to the Current tab as well as an additional - # specified tab for later examination - ds = self.make_dataset(dataset_location) - self.gui.graphDict['current'].add_dataset(ds) - ds = self.make_dataset(dataset_location) - self.gui.graphDict[graph].add_dataset(ds) - - def do_imshow(self, data, image_size, graph, name): - self.gui.graphDict[graph].update_image(data, image_size, name) - - @setting(1, 'Plot', dataset_location = ['(*s, s)', '(*s, i)'], graph = 's', send_to_current = 'b' ,returns = '') - def plot(self, c, dataset_location, graph, send_to_current = True): - self.do_plot(dataset_location, graph, send_to_current) - - @setting(2, 'Plot with axis', dataset_location = ['(*s, s)', '(*s, i)'], graph = 's', axis = '*v', send_to_current = 'b', returns = '') - def plot_with_axis(self, c, dataset_location, graph, axis, send_to_current = True): - minim = min(axis) - maxim = max(axis) - if (graph != 'current') and (send_to_current == True): - self.gui.graphDict['current'].set_xlimits([minim[minim.units], maxim[maxim.units]]) - self.gui.graphDict[graph].set_xlimits([minim[minim.units], maxim[maxim.units]]) - self.do_plot(dataset_location, graph, send_to_current) - - @setting(3, 'Plot image', image='*i', image_size='*i', graph='s', name='s', returns='') - def plot_image(self, c, image, image_size, graph, name=''): - self.do_imshow(image, image_size, graph, name) - -if __name__ == '__main__': - from labrad import util - util.runServer(RealSimpleGrapher()) +''' +The Real Simple Grapher +''' +#import GUI elements +from GraphWindow import GraphWindow +from Dataset import Dataset +from PyQt5 import QtWidgets + +#install qt reactor +import sys +a = QtWidgets.QApplication(sys.argv) +import qt5reactor +qt5reactor.install() + +#import server libraries +from twisted.internet.defer import returnValue, DeferredLock, Deferred, inlineCallbacks +from twisted.internet.threads import deferToThread +from twisted.internet import reactor +from labrad.server import LabradServer, setting + +""" +### BEGIN NODE INFO +[info] +name = Real Simple Grapher +version = 1.0 +description = +instancename = Real Simple Grapher +[startup] +cmdline = %PYTHON% %FILE% +timeout = 20 +[shutdown] +message = 987654321 +timeout = 5 +### END NODE INFO +""" + +class RealSimpleGrapher(LabradServer): + + """ Methods for controlling graphing """ + + name = "Grapher" + + @inlineCallbacks + def initServer(self): + self.listeners = set() + self.gui = GraphWindow(reactor, cxn = self.client) + self.gui.setWindowTitle('Real Simple Grapher') + self.dv = yield self.client.data_vault + self.pv = yield self.client.parameter_vault + + def make_dataset(self, dataset_location): + cxt = self.client.context() + ds = Dataset(self.dv, cxt, dataset_location, reactor) + return ds + + def do_plot(self, dataset_location, graph, send_to_current): + if (graph != 'current') and (send_to_current == True): + # add the plot to the Current tab as well as an additional + # specified tab for later examination + ds = self.make_dataset(dataset_location) + self.gui.graphDict['current'].add_dataset(ds) + ds = self.make_dataset(dataset_location) + self.gui.graphDict[graph].add_dataset(ds) + + def do_imshow(self, data, image_size, graph, name): + self.gui.graphDict[graph].update_image(data, image_size, name) + + @setting(1, 'Plot', dataset_location = ['(*s, s)', '(*s, i)'], graph = 's', send_to_current = 'b' ,returns = '') + def plot(self, c, dataset_location, graph, send_to_current = True): + self.do_plot(dataset_location, graph, send_to_current) + + @setting(2, 'Plot with axis', dataset_location = ['(*s, s)', '(*s, i)'], graph = 's', axis = '*v', send_to_current = 'b', returns = '') + def plot_with_axis(self, c, dataset_location, graph, axis, send_to_current = True): + minim = min(axis) + maxim = max(axis) + if (graph != 'current') and (send_to_current == True): + self.gui.graphDict['current'].set_xlimits([minim[minim.units], maxim[maxim.units]]) + self.gui.graphDict[graph].set_xlimits([minim[minim.units], maxim[maxim.units]]) + self.do_plot(dataset_location, graph, send_to_current) + + @setting(3, 'Plot image', image='*i', image_size='*i', graph='s', name='s', returns='') + def plot_image(self, c, image, image_size, graph, name=''): + self.do_imshow(image, image_size, graph, name) + +if __name__ == '__main__': + from labrad import util + util.runServer(RealSimpleGrapher()) diff --git a/tests/add_data.py b/tests/add_data.py index 4ec45df..3a4bad3 100644 --- a/tests/add_data.py +++ b/tests/add_data.py @@ -1,31 +1,31 @@ -# adds random data to plot - -import time -import labrad -import random - -cxn = labrad.connect() -dv = cxn.data_vault -cxt = cxn.context() - -localtime = time.localtime() -datasetNameAppend = time.strftime("%Y%b%d_%H%M_%S",localtime) -dirappend = [ time.strftime("%Y%b%d",localtime) ,time.strftime("%H%M_%S", localtime)] -directory = ['','Experiments'] -directory.extend(['TestData']) -directory.extend(dirappend) -dv.cd(directory ,True, context = cxt) -output_size = 2 -dependents = [('Excitation','Ion {}'.format(ion),'Probability') for ion in range(output_size)] -ds = dv.new('Rabi Flopping {}'.format(datasetNameAppend),[('Excitation', 'us')], dependents , context = cxt) -dv.add_parameter('plotLive', True, context = cxt) -grapher = cxn.grapher -grapher.plot(ds, 'pmt', False) - -i = 0 -while True: - submission = [i] - submission.extend([random.random() for x in range(output_size)]) - dv.add(submission, context=cxt) - i+=1 - time.sleep(0.01) +# adds random data to plot + +import time +import labrad +import random + +cxn = labrad.connect() +dv = cxn.data_vault +cxt = cxn.context() + +localtime = time.localtime() +datasetNameAppend = time.strftime("%Y%b%d_%H%M_%S",localtime) +dirappend = [ time.strftime("%Y%b%d",localtime) ,time.strftime("%H%M_%S", localtime)] +directory = ['','Experiments'] +directory.extend(['TestData']) +directory.extend(dirappend) +dv.cd(directory ,True, context = cxt) +output_size = 2 +dependents = [('Excitation','Ion {}'.format(ion),'Probability') for ion in range(output_size)] +ds = dv.new('Rabi Flopping {}'.format(datasetNameAppend),[('Excitation', 'us')], dependents , context = cxt) +dv.add_parameter('plotLive', True, context = cxt) +grapher = cxn.grapher +grapher.plot(ds, 'pmt', False) + +i = 0 +while True: + submission = [i] + submission.extend([random.random() for x in range(output_size)]) + dv.add(submission, context=cxt) + i+=1 + time.sleep(0.01) diff --git a/tests/cursor_test.py b/tests/cursor_test.py index 6b0ee11..6535d96 100644 --- a/tests/cursor_test.py +++ b/tests/cursor_test.py @@ -1,13 +1,12 @@ - -import pyqtgraph as pg -import numpy as np -pg.mkQApp() -w = pg.GraphicsLayoutWidget() -w.show() -vb = w.addViewBox() -img = pg.ImageItem(np.random.normal(size=(100,100))) -vb.addItem(img) -def mouseMoved(pos): - print "Image position:", img.mapFromScene(pos) - -w.scene().sigMouseMoved.connect(mouseMoved) +import pyqtgraph as pg +import numpy as np +pg.mkQApp() +w = pg.GraphicsLayoutWidget() +w.show() +vb = w.addViewBox() +img = pg.ImageItem(np.random.normal(size=(100,100))) +vb.addItem(img) +def mouseMoved(pos): + print("Image position:", img.mapFromScene(pos)) + +w.scene().sigMouseMoved.connect(mouseMoved) diff --git a/tests/listView.py b/tests/listView.py index 2e360ed..f08ff94 100644 --- a/tests/listView.py +++ b/tests/listView.py @@ -1,23 +1,24 @@ -from PyQt4.QtCore import * -from PyQt4.QtGui import * -import sys -from random import randint - - -app = QApplication(sys.argv) - -model = QStandardItemModel() - -for n in range(10): - item = QStandardItem('Item %s' % randint(1, 100)) - check = Qt.Checked if randint(0, 1) == 1 else Qt.Unchecked - item.setCheckState(check) - item.setCheckable(True) - model.appendRow(item) - - -view = QListView() -view.setModel(model) - -view.show() -app.exec_() +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +import sys +from random import randint + + +app = QApplication(sys.argv) + +model = QStandardItemModel() + +for n in range(10): + item = QStandardItem('Item %s' % randint(1, 100)) + check = Qt.Checked if randint(0, 1) == 1 else Qt.Unchecked + item.setCheckState(check) + item.setCheckable(True) + model.appendRow(item) + + +view = QListView() +view.setModel(model) + +view.show() +app.exec_() diff --git a/tests/plot_pyqt.py b/tests/plot_pyqt.py index df61732..e90a9ab 100644 --- a/tests/plot_pyqt.py +++ b/tests/plot_pyqt.py @@ -1,35 +1,36 @@ -# test plotting window in pyqtgraph - -import pyqtgraph as pg -import numpy as np -import random -from PyQt4 import QtGui - -class Graph(QtGui.QWidget): - - def __init__(self, parent=None): - super(Graph, self).__init__(parent) - self.initUI() - - def initUI(self): - x = np.linspace(0,100,100) - y = [random.random() for k in x] - y2 = [random.random() for k in x] - self.pw = pg.PlotWidget() - layout = QtGui.QVBoxLayout() - layout.addWidget(self.pw) - self.setLayout(layout) - #self.pw = pg.plot([],[]) - self.legend = self.pw.addLegend() - self.pw.setXRange(1,503) - self.pw.setYRange(-2, 2) - p1=self.pw.plot(x,y, pen='r', name='plot1') - p2=self.pw.plot(x, y2, pen='g', name='plot2') - self.pw.removeItem(p2) - self.legend.removeItem('plot2') -if __name__ == '__main__': - import sys - app = QtGui.QApplication(sys.argv) - main = Graph() - main.show() - sys.exit(app.exec_()) +# test plotting window in pyqtgraph + +import pyqtgraph as pg +import numpy as np +import random +from PyQt5 import QtWidgets + +class Graph(QtWidgets.QWidget): + + def __init__(self, parent=None): + super(Graph, self).__init__(parent) + self.initUI() + + def initUI(self): + x = np.linspace(0,100,100) + y = [random.random() for k in x] + y2 = [random.random() for k in x] + self.pw = pg.PlotWidget() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.pw) + self.setLayout(layout) + #self.pw = pg.plot([],[]) + self.legend = self.pw.addLegend() + self.pw.setXRange(1,503) + self.pw.setYRange(-2, 2) + p1=self.pw.plot(x,y, pen='r', name='plot1') + p2=self.pw.plot(x, y2, pen='g', name='plot2') + self.pw.removeItem(p2) + self.legend.removeItem('plot2') + +if __name__ == '__main__': + import sys + app = QtWidgets.QApplication(sys.argv) + main = Graph() + main.show() + sys.exit(app.exec_()) From 1a5829da2f2268fd0e38ea9d3038c6bb3a20d462 Mon Sep 17 00:00:00 2001 From: Clayton Ho Date: Sat, 6 Nov 2021 10:02:58 -0700 Subject: [PATCH 2/3] fixed duplicate artist bug --- Dataset.py | 9 ++++++--- GraphWidgetPyQtGraph.py | 5 +---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dataset.py b/Dataset.py index 0fb656c..0d52213 100644 --- a/Dataset.py +++ b/Dataset.py @@ -72,9 +72,12 @@ def getData(self): def getLabels(self): labels = [] yield self.openDataset() - variables = yield self.data_vault.variables(context = self.context) - for i in range(len(variables[1])): - labels.append(variables[1][i][1] + ' - ' + self.dataset_name) + _, all_dep = yield self.data_vault.variables(context = self.context) + for i in range(len(all_dep)): + label_tmp = all_dep[i][0] + ' - ' + self.dataset_name + if label_tmp in labels: + label_tmp += ' (' + str(i) + ')' + labels.append(label_tmp) returnValue(labels) @inlineCallbacks diff --git a/GraphWidgetPyQtGraph.py b/GraphWidgetPyQtGraph.py index 4f2cac0..bcddff2 100644 --- a/GraphWidgetPyQtGraph.py +++ b/GraphWidgetPyQtGraph.py @@ -155,10 +155,7 @@ def remove_artist(self, ident): self.pw.removeItem(artist) self.tracelist.removeTrace(ident) self.artists[ident].shown = False - try: - del self.artists[ident] - except KeyError: - pass + del self.artists[ident] except Exception as e: print("remove failed") From aabf39963b6769d8cee54b50b38df07f6fb76b8c Mon Sep 17 00:00:00 2001 From: Clayton Ho Date: Sun, 19 Dec 2021 20:43:58 -0800 Subject: [PATCH 3/3] added export dataset functionality to tracelistwidget --- TraceListWidget.py | 111 ++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 32 deletions(-) diff --git a/TraceListWidget.py b/TraceListWidget.py index 1bf8d1c..b2e06cd 100644 --- a/TraceListWidget.py +++ b/TraceListWidget.py @@ -1,14 +1,29 @@ -from PyQt5 import QtGui, QtWidgets, QtCore +# imports +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor +from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QMenu, QFileDialog + +from FitWindowWidget import FitWindow from ParameterListWidget import ParameterList from DataVaultListWidget import DataVaultList -from FitWindowWidget import FitWindow from PredictSpectrumWidget import PredictSpectrum + from GUIConfig import traceListConfig -class TraceList(QtWidgets.QListWidget): - def __init__(self, parent): +import os +from numpy import savetxt + + +class TraceList(QListWidget): + """ + Manages the datasets that are being plotted. + Basically the left-hand column of each GraphWidget. + """ + + def __init__(self, parent, root=None): super(TraceList, self).__init__() self.parent = parent + self.root = root self.windows = [] self.config = traceListConfig() self.setStyleSheet("background-color:%s;" % self.config.background_color) @@ -22,28 +37,28 @@ def __init__(self, parent): def initUI(self): self.trace_dict = {} - item = QtWidgets.QListWidgetItem('Traces') - item.setCheckState(QtCore.Qt.Checked) - self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + item = QListWidgetItem('Traces') + item.setCheckState(Qt.Checked) + self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.popupMenu) def addTrace(self, ident, color): - item = QtWidgets.QListWidgetItem(ident) + item = QListWidgetItem(ident) if self.use_trace_color: foreground_color = self.parent.getItemColor(color) item.setForeground(foreground_color) else: - item.setForeground(QtGui.QColor(255, 255, 255)) - item.setBackground(QtGui.QColor(0, 0, 0)) + item.setForeground(QColor(255, 255, 255)) + item.setBackground(QColor(0, 0, 0)) - item.setCheckState(QtCore.Qt.Checked) + item.setCheckState(Qt.Checked) self.addItem(item) self.trace_dict[ident] = item def removeTrace(self, ident): - item = self.trace_dict[ident] + item = self.trace_dict[ident] row = self.row(item) self.takeItem(row) item = None @@ -53,30 +68,50 @@ def changeTraceListColor(self, ident, new_color): item.setForeground(self.parent.getItemColor(new_color)) def popupMenu(self, pos): - menu = QtWidgets.QMenu() + menu = QMenu() item = self.itemAt(pos) - if (item == None): + if item is None: dataaddAction = menu.addAction('Add Data Set') spectrumaddAction = menu.addAction('Add Predicted Spectrum') + removeallAction = menu.addAction('Remove All Traces') + exportallAction = menu.addAction('Export All Traces') + # process actions action = menu.exec_(self.mapToGlobal(pos)) if action == dataaddAction: - dvlist = DataVaultList(self.parent.name) + dvlist = DataVaultList(self.parent.name, root=self.root) self.windows.append(dvlist) dvlist.show() - - if action == spectrumaddAction: + elif action == spectrumaddAction: ps = PredictSpectrum(self) self.windows.append(ps) ps.show() - - - + elif action == removeallAction: + for index in reversed(range(self.count())): + ident = str(self.item(index).text()) + self.parent.remove_artist(ident) + elif action == exportallAction: + # get all datasets + datasets_all = set() + for index in range(self.count()): + ident = self.item(index).text() + dataset_tmp = self.parent.artists[ident].dataset + datasets_all.add(dataset_tmp) + # export all datasets + for dataset in datasets_all: + try: + filename = QFileDialog.getSaveFileName(self, 'Open File', os.getenv('HOME'), "CSV (*.csv)") + np.savetxt(filename[0], dataset.data, delimiter=',') + except Exception as e: + pass else: ident = str(item.text()) parametersAction = menu.addAction('Parameters') togglecolorsAction = menu.addAction('Toggle colors') fitAction = menu.addAction('Fit') + removeAction = menu.addAction('Remove') + exportAction = menu.addAction('Export') + # color menu selectColorMenu = menu.addMenu("Select color") redAction = selectColorMenu.addAction("Red") greenAction = selectColorMenu.addAction("Green") @@ -86,40 +121,52 @@ def popupMenu(self, pos): whiteAction = selectColorMenu.addAction("White") colorActionDict = {redAction:"r", greenAction:"g", yellowAction:"y", cyanAction:"c", magentaAction:"m", whiteAction:"w"} + # process actions action = menu.exec_(self.mapToGlobal(pos)) - if action == parametersAction: # option to show parameters in separate window dataset = self.parent.artists[ident].dataset pl = ParameterList(dataset) self.windows.append(pl) pl.show() - - if action == togglecolorsAction: + elif action == togglecolorsAction: # option to change color of line new_color = self.parent.colorChooser.next() - #self.parent.artists[ident].artist.setData(color = new_color, symbolBrush = new_color) + #self.parent.artists[ident].artist.setData(color=new_color, symbolBrush=new_color) self.parent.artists[ident].artist.setPen(new_color) if self.parent.show_points: - self.parent.artists[ident].artist.setData(pen = new_color, symbolBrush = new_color) + self.parent.artists[ident].artist.setData(pen=new_color, symbolBrush=new_color) self.changeTraceListColor(ident, new_color) else: - self.parent.artists[ident].artist.setData(pen = new_color) + self.parent.artists[ident].artist.setData(pen=new_color) self.changeTraceListColor(ident, new_color) - - if action == fitAction: + elif action == fitAction: dataset = self.parent.artists[ident].dataset index = self.parent.artists[ident].index fw = FitWindow(dataset, index, self) self.windows.append(fw) fw.show() - - if action in colorActionDict.keys(): + elif action in colorActionDict.keys(): new_color = colorActionDict[action] self.parent.artists[ident].artist.setPen(new_color) if self.parent.show_points: - self.parent.artists[ident].artist.setData(pen = new_color, symbolBrush = new_color) + self.parent.artists[ident].artist.setData(pen=new_color, symbolBrush=new_color) self.changeTraceListColor(ident, new_color) else: - self.parent.artists[ident].artist.setData(pen = new_color) + self.parent.artists[ident].artist.setData(pen=new_color) self.changeTraceListColor(ident, new_color) + elif action == removeAction: + self.parent.remove_artist(ident) + elif action == exportAction: + # get datasets and index + dataset = self.parent.artists[ident].dataset.data + index = self.parent.artists[ident].index + # get trace from dataset + trace = dataset[:, (0, index)] + # export trace + try: + filename = QFileDialog.getSaveFileName(self, 'Open File', os.getenv('HOME'), "CSV (*.csv)") + np.savetxt(filename[0], trace, delimiter=',') + except Exception as e: + pass + \ No newline at end of file