Skip to content

Commit

Permalink
Merge pull request #446 from BBN-Q/develop
Browse files Browse the repository at this point in the history
2020.1 Release
  • Loading branch information
grahamrow authored Jan 21, 2020
2 parents 2fcc4c5 + 1b81ef6 commit c654240
Show file tree
Hide file tree
Showing 31 changed files with 1,145 additions and 350 deletions.
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ cffi >= 1.9.1
scikit-learn >= 0.18.1
psutil >= 5.0.0
pyzmq >= 16.0.0
bbndb >= 2019.1
bbndb >= 2020.1
pyusb >= 1.0.2
ipykernel>=5.0.0
ipywidgets>=7.0.0
ipykernel >= 5.0.0
ipywidgets >= 7.0.0
setproctitle
serial
pyserial >= 3.4
git+https://github.com/spatialaudio/nbsphinx.git@master
9 changes: 5 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
sys.exit("Sorry, Python < 3.6 is not supported by Auspex.")

install_requires = [
"bbndb >= 2020.1",
"numpy >= 1.11.1",
"scipy >= 0.17.1",
"PyVISA >= 1.8",
Expand All @@ -19,11 +20,11 @@
"pyzmq >= 16.0.0",
"pyusb >= 1.0.2",
"python-usbtmc >= 0.8",
"ipykernel>=5.0.0",
"ipywidgets>=7.0.0",
"ipykernel >= 5.0.0",
"ipywidgets >= 7.0.0",
"sqlalchemy >= 1.2.15",
"pyserial >= 3.4",
"setproctitle",
"serial",
"progress"
]

Expand All @@ -43,7 +44,7 @@

setup(
name='auspex',
version='2019.2',
version='2020.1',
author='Auspex Developers',
package_dir={'':'src'},
packages=[
Expand Down
5 changes: 3 additions & 2 deletions src/auspex/analysis/CR_fits.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ def fit_CR_phase(xpoints, data0, data1):
fit1 = SineFit(xpoints, data1, np.pi, 1.0/xpoints[-1])
#find the phase for maximum contrast
contrast = (fit0.model(x_fine) - fit1.model(x_fine))/2.0
logger.info(f"CR Contrast = {np.max(contrast)}")
xopt = x_fine[np.argmax(contrast)] - np.pi
logger.info(f"CR Contrast = {np.min(contrast)}")
xopt = x_fine[np.argmin(contrast)] % (2*np.pi)

logger.info(f"CR phase = {xopt}")

return xopt, fit0.fit_params, fit1.fit_params
Expand Down
53 changes: 45 additions & 8 deletions src/auspex/analysis/fits.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,14 @@ def _do_fit(self, bounds=None):
self.fit_params = self._fit_dict(popt)
self.fit_errors = self._fit_dict(perr)

def model(self, x):
def model(self, x=None):
""" The fit function evaluated at the parameters found by `curve_fit`.
Args:
x: A number or `numpy.array` returned by the model function.
"""
if x is None:
return self.fit_function(self.xpoints)
if isinstance(x, Iterable):
return np.array([self.fit_function(_) for _ in x])
else:
Expand Down Expand Up @@ -201,7 +203,7 @@ def _initial_guess(self):
### IEEE Signal Processing Magazine. September 2011. DOI: 10.1109/MSP.2011.941846
N = len(self.xpts)

#use first and last points to
#use first and last points to
B = 0.5*(self.ypts[-1] + self.ypts[0])
y = self.ypts - B
mask = y>0
Expand All @@ -214,7 +216,7 @@ def _initial_guess(self):
v = np.array([np.sum(y**2*np.log(y)),
np.sum(x*y**2*np.log(y)),
np.sum(x*y**2*np.log(y))])
a, b, c = np.linalg.inv(M) @ v.T
a, b, c = np.linalg.inv(M) @ v.T

mu = -b/(2.0*c)
sigma = np.sqrt(-1/(2.0*c))
Expand Down Expand Up @@ -245,7 +247,7 @@ def __init__(self, x, y, make_plots=False, n_gaussians=2, n_samples=int(1e5)):
n_gaussians: Expected number of Gaussian peaks.
n_samples: Number of random samples to generate for GMM estimation. (see `MultiGaussianFit._initial_guess`)
"""
self.n_gaussians = n_gaussians
self.n_gaussians = n_gaussians
self.n_samples = n_samples
super().__init__(x, y, make_plots=make_plots)

Expand All @@ -266,10 +268,10 @@ def one_gaussian(x, *p):

def _initial_guess(self):
## Initial guess for the multi-gaussian fit
## The idea is to draw random samples using the data as a PDF, then run a
## Gaussian mixture model (ie. clustering) to get a good initial guess for the gaussians.
## The idea is to draw random samples using the data as a PDF, then run a
## Gaussian mixture model (ie. clustering) to get a good initial guess for the gaussians.
## Note that this is pretty slow...

B = 0.5*(self.ypts[-1] + self.ypts[0])
#normalize and center
y = self.ypts - B
Expand All @@ -281,7 +283,7 @@ def _initial_guess(self):
samples = np.random.choice(a=xn, size=self.n_samples, p=yn)
gmm = GaussianMixture(n_components=self.n_gaussians)
gmm.fit(samples.reshape(len(samples),1))
means = gmm.means_.flatten() + x0
means = gmm.means_.flatten() + x0
sigmas = np.sqrt(gmm.covariances_.flatten())
amps = gmm.weights_.flatten() * (2*np.pi)
output = np.zeros(1+3*self.n_gaussians)
Expand All @@ -304,5 +306,40 @@ def _fit_dict(self, p):
def __str__(self):
return f"Sum of Gaussians with N={self.n_gaussians}"

class QuadraticFit(AuspexFit):
"""A fit to a simple quadratic function A*(x-x0)**2 + b
"""

xlabel = "X Data"
ylabel = "Y Data"
title = "Quadratic Fit"

@staticmethod
def _model(x, *p):
"""Model for a simple qudratic."""
return p[0]*(x-p[1])**2 + p[2]

def _initial_guess(self):
"""Use quadratic regression to get an initial guess."""
n = len(self.xpts)
sx = np.sum(self.xpts)
sx2 = np.sum(self.xpts**2)
sx3 = np.sum(self.xpts**3)
sx4 = np.sum(self.xpts**4)
M = np.array([[sx4, sx3, sx2],
[sx3, sx2, sx] ,
[sx2, sx, n] ])
Y = np.array([np.sum(self.xpts**2 * self.ypts),
np.sum(self.xpts * self.ypts),
np.sum(self.ypts)])
X = np.linalg.inv(M) @ Y
A = X[0]
x0 = X[1]/(2*A)
b = X[2] - x0**2
return [A, x0, b]

def _fit_dict(self, p):
return {"A": p[0], "x0": p[1], "b": p[2]}

def __str__(self):
return "A(x - x0)^2 + b"
67 changes: 66 additions & 1 deletion src/auspex/analysis/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from auspex.data_format import AuspexDataContainer
from auspex.log import logger
import datetime
import os, re
from os import path
import numpy as np
from itertools import product
import bbndb
from IPython.display import HTML, display
import matplotlib.pyplot as plt

def get_file_name():
"""Helper function to get a filepath from a dialog box"""
Expand Down Expand Up @@ -72,6 +76,9 @@ def open_data(num=None, folder=None, groupname="main", datasetname="data", date=


def normalize_data(data, zero_id = 0, one_id = 1):
if np.any(np.iscomplex(data['Data'])):
logger.warning("normalize_data should not be used with complex data.")

metadata_str = [f for f in data.dtype.fields.keys() if 'metadata' in f]
if len(metadata_str)!=1:
raise ValueError('Data format not valid')
Expand All @@ -91,9 +98,12 @@ def normalize_data(data, zero_id = 0, one_id = 1):
return norm_data

def normalize_buffer_data(data, desc, qubit_index, zero_id = 0, one_id = 1):
if np.any(np.iscomplex(data)):
logger.warning("normalize_buffer_data should not be used with complex data.")

# Qubit index gives the string offset of the qubit in the metadata
metadata = [(i, int(v[qubit_index])) for i,v in enumerate(desc.axes[0].metadata) if v != "data"]

#find indeces for calibration points
zero_cal_idx = [i for i, x in metadata if x == zero_id]
one_cal_idx = [i for i, x in metadata if x == one_id]
Expand Down Expand Up @@ -203,3 +213,58 @@ def cal_data(data, quad=np.real, qubit_name="q1", group_name="main", \
x_dat = data[key][ind_axis][:-(len(ind0) + len(ind1))]
y_dat = (y_dat - zero_cal)/scale_factor + 1
return y_dat.astype(return_type), x_dat

def cal_ls():
''' List of auspex.pulse_calibration results '''
caldb = bbndb.calibration.Calibration
c = bbndb.get_cl_session().query(caldb.id, caldb.sample_id, caldb.name, caldb.value, caldb.uncertainty, caldb.date).order_by(-caldb.id).all()
table_code = ""
for i, (id, sample_id, name, value, uncertainty, time) in enumerate(c):
if not uncertainty:
uncertainty = 0
d,t = str(time).split()
sample = bbndb.get_cl_session().query(bbndb.calibration.Sample).filter_by(id=sample_id).first()
table_code += f"<tr><td>{id}</td><td>{d}</td><td>{t.split('.')[0]}</td><td>{sample.name}</td><td>{name}</td><td>{round(value,3)}</td><td>{round(uncertainty,3)}</td></tr>"
display(HTML(f"<table><tr><th>id</th><th>Date</th><th>Time</th><th>Sample</th><th>Name</th><th>Value</th><th>Uncertainty</th></tr><tr>{table_code}</tr></table>"))

def get_cals(qubit, params, make_plots=True, depth=0):
"""
Return and optionally plot the result of the most recent calibrations/characterizations
Parameters
-------------
qubit : qubit name (string)
params: parameter(s) to plot (string or list of strings)
make_plots: optional bool (default = True)
depth: optional integer, number of most recent calibrations to load (default = 0 for all cals)
-------------
Returns
List of: dates, values, errors
"""

if isinstance(params, str):
params = [params]
caldb = bbndb.calibration.Calibration
sample_id = bbndb.get_cl_session().query(bbndb.calibration.Sample).filter_by(name=qubit).first().id
dates_list = []
values_list = []
errors_list = []
for param in params:
c = bbndb.get_cl_session().query(caldb.value, caldb.uncertainty, caldb.date).order_by(-caldb.id).filter_by(name = param, sample_id = sample_id).all()
dates = [cc[2] for cc in c][-depth:]
values = [cc[0] for cc in c][-depth:]
errors = [cc[1] if cc[1] else 0 for cc in c][-depth:]
if make_plots:
plt.errorbar(dates, values, yerr=errors, fmt='-o', elinewidth=2.0, capsize=4.0, label=param)
plt.title(qubit)
plt.xlabel("Time")
units = {'T1':'s', 'T2':'s', 'f':'Hz'} #TODO: add more
unit = f'({units[param]})' if param in units else ''
plt.ylabel(f'{param} {unit}')
dates_list.append(dates)
values_list.append(values)
errors_list.append(errors)
if make_plots:
plt.legend()
ylabels = [f'{param} ({units[param]})' if param in units else '' for param in params]
plt.ylabel(", ".join(ylabels))
return dates, values, errors
Loading

0 comments on commit c654240

Please sign in to comment.