Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/master' into issues/pyfl…
Browse files Browse the repository at this point in the history
…akes_syntax_check_fixes
  • Loading branch information
lumip committed Apr 23, 2018
2 parents 37f0cba + 9b0a442 commit 925a850
Show file tree
Hide file tree
Showing 29 changed files with 418 additions and 62 deletions.
5 changes: 4 additions & 1 deletion MATLAB/+qc/awg_program.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
if strcmp(ctrl, 'add')
[~, bool, msg] = qc.awg_program('fresh', qc.change_field(a, 'verbosity', 0));
if ~bool || a.force_update
plsdata.awg.currentProgam = '';

% Deleting old program should not be necessary. In practice however,
% updating an existing program seemed to crash Matlab sometimes.
Expand Down Expand Up @@ -68,11 +69,13 @@

% --- arm ---------------------------------------------------------------
elseif strcmp(ctrl, 'arm')
% Call directly before trigger comes, otherwise you might encounter a
% trigger timeout. Also, call after daq_operations('add')!
[~, bool, msg] = qc.awg_program('present', qc.change_field(a, 'verbosity', 0));
if bool
% qc.workaround_alazar_single_buffer_acquisition();

hws.arm_program(a.program_name);
hws.arm_program(a.program_name);
plsdata.awg.currentProgam = a.program_name;
bool = true;
msg = sprintf('Program ''%s'' armed', a.program_name);
Expand Down
14 changes: 7 additions & 7 deletions MATLAB/+qc/conf_seq.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

global plsdata

alazarName = 'ATS9440Python';
alazarName = plsdata.daq.instSmName;

% None of the arguments except pulse_template should contain any python
% objects to avoid erroneous saving when the scan is executed.
Expand All @@ -45,14 +45,14 @@
'nrep', 10, ... % numer of repetition of pulse
'fig_id', 2000, ...
'fig_position', [-1919 2 1693 994], ...
'disp_ops', 'default', ... % list of indices of operations to show
'disp_ops', 'default', ... % Refers to operations: List of indices of operations to show
'disp_dim', [1 2], ... % dimension of display
'delete_getchans', [1], ... % indices of getchans (including those generated by procfns) to delete after the scan is complete
'procfn_ops', {{}}, ... % one entry for each virtual channel, each cell entry has two element: fn, args and dim.
... If there are more entreis than operations, the nth+1 entry is applied to the 1st operation again.
'delete_getchans', [1], ... % Refers to getchans: Indices of getchans (including those generated by procfns) to delete after the scan is complete
'procfn_ops', {{}}, ... % Refers to operations: One entry for each virtual channel, each cell entry has two element: fn, args and dim.
... If there are more entries than operations, the nth+1 entry is applied to the 1st operation again.
'saveloop', 0, ... % save every nth loop
'dnp', false, ... % enable DNP
'arm_global', false, ... % If true, set the program to be armed via tunedata.global_opts.conf_seq.arm_program_name.
'arm_global', false, ... % If true, set the program to be armed via tunedata.global_opts.conf_seq.arm_program_name.
... % If you use this, all programs need to be uploaded manually before the scan and need to
... % have the same Alazar configuration.
'rf_switches', true, ... % turn RF switches on and off automatically using the DecaDAC channels (to be removed)
Expand Down Expand Up @@ -106,7 +106,7 @@
% in reconfiguration of the Alazar which takes a long time. Thus this
% should only be done before a scan is started (i.e. in a configfn).
% * qc.dac_operations('add', a) also resets the virtual channel in
% smdata.inst(sminstlookup('ATS9440Python')).data.virtual_channel.
% smdata.inst(sminstlookup(alazarName)).data.virtual_channel.
scan.configfn(end+1).fn = @smaconfigwrap_save_data;
scan.configfn(end).args = {'daq_operations', @qc.daq_operations, 'add', a};

Expand Down
33 changes: 26 additions & 7 deletions MATLAB/+qc/daq_operations.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
global plsdata smdata
hws = plsdata.awg.hardwareSetup;
daq = plsdata.daq.inst;
instIndex = sminstlookup('ATS9440Python');
instIndex = sminstlookup(plsdata.daq.instSmName);

program = struct();
msg = '';
Expand All @@ -18,18 +18,26 @@
output = a.operations;

% --- add ---------------------------------------------------------------
if strcmp(ctrl, 'add') % output is operations
% qc.daq_operations('remove', qc.change_field(a, 'verbosity', 0));
if strcmp(ctrl, 'add') % output is operations
% Call before qc.awg_program('arm')!

smdata.inst(instIndex).data.virtual_channel = struct( ...
'operations', {a.operations} ...
);

% alazar.update_settings = py.True is automatically set if
% register_operations is executed. This results in reconfiguration
% of the Alazar which takes a long time.
daq.register_operations(a.program_name, qc.operations_to_python(a.operations));
msg = sprintf('Operations for program ''%s'' added', a.program_name);
% of the Alazar which takes a long time. Thus, we avoid registering
% operations if the last armed program is the same as the currently
% armed program. We know plsdata.awg.currentProgam contains the last
% armed program since qc.daq_operations should be called before
% qc.awg_program('arm').
if plsdata.daq.reuseOperations && strcmp(plsdata.awg.currentProgam, a.program_name)
msg = sprintf('Operations from last armed program ''%s'' reused.\n If an error occurs, try executing another program\n first to update the operations.', plsdata.awg.currentProgam);
else
daq.register_operations(a.program_name, qc.operations_to_python(a.operations));
msg = sprintf('Operations for program ''%s'' added', a.program_name);
end
bool = true;

% --- set length --------------------------------------------------------
Expand All @@ -43,10 +51,21 @@
% Operations need to have been added beforehand
masks = util.py.py2mat(py.getattr(daq, '_registered_programs'));
masks = util.py.py2mat(masks.(a.program_name));
operations = masks.operations;
masks = util.py.py2mat(masks.masks);
output = [];
for k = 1:numel(masks)
output(k) = util.py.py2mat(size(masks{k}.length));
if isa(operations{k}, 'py.atsaverage._atsaverage_release.ComputeDownsampleDefinition')
output(k) = util.py.py2mat(size(masks{k}.length));
elseif isa(operations{k}, 'py.atsaverage._atsaverage_release.ComputeRepAverageDefinition')
n = util.py.py2mat(masks{k}.length.to_ndarray);
if any(n ~= n(1))
error('daq_operations assumes that all masks should have the same length if using ComputeRepAverageDefinition.');
end
output(k) = n(1);
else
error('Operation ''%s'' not yet implemented', class(operations{k}));
end
end
if isempty(output)
warning('No masks configured');
Expand Down
2 changes: 1 addition & 1 deletion MATLAB/+qc/operations_to_python.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
case 'RepeatedDownsample'
pyOp = py.atsaverage.operations.RepeatedDownsample(args{:});
otherwise
error('Operation %s not recognized', operations{k}{1});
error('Operation %s not recognized', operations{k}{1});
end
pyOperations{end+1} = pyOp;

Expand Down
56 changes: 36 additions & 20 deletions MATLAB/+qc/plot_pulse.m
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
function plot_pulse(pulse, varargin)
function [t, channels, measurements] = plot_pulse(pulse, varargin)

global plsdata

defaultArgs = struct(...
'sample_rate', plsdata.awg.sampleRate, ... % in 1/s, converted to 1/ns below
'channel_names', {{}}, ... % names of channels to plot, all if empty
'parameters', [], ...
'channel_mapping', py.None, ...
'window_mapping' , py.None, ...
'fig_id', plsdata.qc.figId, ...
'clear_fig', true, ...
'charge_diagram', {{'X', 'Y'}}, ...
'lead_points', 1e-3*[-4 -1; -1 -2; 0 -4; 4 0; 2 1; 1 4], ...
'special_points', struct('M', [0 0], 'R1', [-2.5e-3 -3.75e-3], 'R2', [-2e-3 1e-3], 'S', [-2e-3 -1e-3], 'Tp', [1.75e-3 0], 'STp', [1e-3 -1e-3]), ...
'plotRange', [-8e-3 8e-3], ...
'max_n_points', 1e5 ...
'sample_rate', plsdata.awg.sampleRate, ... % in 1/s, converted to 1/ns below
'channel_names', {{}}, ... % names of channels to plot, all if empty
'parameters', [], ...
'channel_mapping', py.None, ...
'window_mapping' , py.None, ...
'fig_id', plsdata.qc.figId, ...
'charge_diagram_data', {{}}, ... % inputs to imagesc
'clear_fig', true, ...
'charge_diagram', {{'X', 'Y'}}, ...
'lead_points', 1e-3*[-4 -1; -1 -2; 0 -4; 4 0; 2 1; 1 4], ...
'special_points', struct('M', [0 0], 'R1', [-2.5e-3 -3.75e-3], 'R2', [-2e-3 1e-3], 'S', [-2e-3 -1e-3], 'Tp', [1.75e-3 0], 'STp', [1e-3 -1e-3]), ...
'plot_range', [-8e-3 8e-3], ...
'max_n_points', 1e4,...
'dont_plot', false ...
);

args = util.parse_varargin(varargin, defaultArgs);
Expand All @@ -39,9 +41,9 @@ function plot_pulse(pulse, varargin)
t = data{1}*1e-9;

channels = data{2};
if ~isempty(args.plotRange)
if ~isempty(args.plot_range)
for chan_name = fieldnames(channels)'
channels.(chan_name{1}) = util.clamp(channels.(chan_name{1}), args.plotRange);
channels.(chan_name{1}) = util.clamp(channels.(chan_name{1}), args.plot_range);
end
end
measurements = struct();
Expand All @@ -52,9 +54,19 @@ function plot_pulse(pulse, varargin)
measurements.(m{1}{1})(end+1, 1:2) = [m{1}{2} m{1}{2}+m{1}{3}] * 1e-9;
end

if args.dont_plot
return;
end

plotChargeDiagram = ~isempty(args.charge_diagram) && all(cellfun(@(x)(isfield(channels, x)), args.charge_diagram));

figure(args.fig_id);
hFig = figure(args.fig_id);
if ~qc.is_instantiated_pulse(pulse)
pulseName = sprintf('Pulse: %s', char(pulse.identifier));
else
pulseName = 'Pulse';
end
set(hFig, 'Name', pulseName);
if args.clear_fig
clf
end
Expand Down Expand Up @@ -84,8 +96,8 @@ function plot_pulse(pulse, varargin)
end
end

if ~isempty(args.plotRange)
title(['Plot range: ' sprintf('%g ', args.plotRange)]);
if ~isempty(args.plot_range)
title(['Plot range: ' sprintf('%g ', args.plot_range)]);
end
xlabel('t(s)');
[~, hObj] = legend(legendHandles, legendEntries);
Expand All @@ -97,9 +109,13 @@ function plot_pulse(pulse, varargin)
hold on
ax = gca;
userData = get(ax, 'userData');
if ~isempty(args.plotRange)
title(['Plot range: ' sprintf('%g ', args.plotRange)]);
if ~isempty(args.plot_range)
title(['Plot range: ' sprintf('%g ', args.plot_range)]);
end

if ~isempty(args.charge_diagram_data)
imagesc(args.charge_diagram_data{:});
end

if isempty(userData) || ~isstruct(userData) || ~isfield(userData, 'leadsPlotted') || ~userData.leadsPlotted
color = [1 1 1]*0.7;
Expand Down
16 changes: 12 additions & 4 deletions MATLAB/+qc/qctoolkitTestSetup.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
'awg', struct('inst', [], 'hardwareSetup', [], 'sampleRate', 2e9, 'currentProgam', '', 'registeredPrograms', struct(), 'defaultChannelMapping', struct(), 'defaultWindowMapping', struct(), 'defaultParametersAndDicts', {{}}, 'defaultAddMarker', {{}}), ...
'dict', struct('cache', [], 'path', 'Y:\Cerfontaine\Code\qc-toolkit-dicts'), ...
'qc', struct('figId', 801), ...
'daq', struct('inst', [], 'defaultOperations', {{}}) ...
'daq', struct('inst', [], 'defaultOperations', {{}}, 'reuseOperations', false) ...
);
plsdata.daq.instSmName = 'ATS9440Python';
plsdata.qc.backend = py.qctoolkit.serialization.FilesystemBackend(plsdata.path);
plsdata.qc.serializer = py.qctoolkit.serialization.Serializer(plsdata.qc.backend);
% -------------------------------------------------------------------------
Expand All @@ -21,23 +22,30 @@
% Loading
if util.yes_no_input('Really load smdata?', 'n')
load(fullfile(savePath, 'smdata_recent.mat'));
fprintf('Loaded smdata\n');
info = dir(fullfile(savePath, 'smdata_recent.mat'));
fprintf('Loaded smdata from %s', datestr(info.datenum));
end
load(fullfile(savePath, 'tunedata_recent.mat'));
info = dir(fullfile(savePath, 'tunedata_recent.mat'));
fprintf('Loaded tunedata from %s', datestr(info.datenum));

load(fullfile(savePath, 'plsdata_recent.mat'));
info = dir(fullfile(savePath, 'plsdata_recent.mat'));
fprintf('Loaded plsdata from %s', datestr(info.datenum));

global tunedata
global plsdata

% Alazar dummy instrument (simulator not implemented yet)
smdata.inst(sminstlookup('ATS9440Python')).data.address = 'simulator';
smdata.inst(sminstlookup(plsdata.daq.instSmName)).data.address = 'simulator';
plsdata.daq.inst = py.qctoolkit.hardware.dacs.alazar.AlazarCard([]);

% Setup AWG
% Turns on AWG for short time but turns it off again
% Initializes hardware setup
% Can also be used for deleting all programs/resetting but then also need to setup Alazar again, i.e. the cell above and the three cells below )
plsdata.awg.hardwareSetup = [];
qc.setup_tabor_awg('realAWG', false, 'simulateAWG', true, 'taborDriverPath', 'Y:\Cerfontaine\Code\tabor');
qc.setup_tabor_awg('realAWG', false, 'simulateAWG', false, 'taborDriverPath', 'Y:\Cerfontaine\Code\tabor');

% AWG default settings
awgctrl('default');
Expand Down
2 changes: 1 addition & 1 deletion MATLAB/+qc/setup_tabor_awg.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function setup_tabor_awg(varargin)
plsdata.awg.hardwareSetup.set_channel('TABOR_A', ...
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst.channel_pair_AB, int64(0), multiply));
plsdata.awg.hardwareSetup.set_channel('TABOR_B', ...
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst.channel_pair_AB, int64(1), multiply));
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst.channel_pair_AB, int64(1), multiply));
plsdata.awg.hardwareSetup.set_channel('TABOR_C', ...
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst.channel_pair_CD, int64(0), multiply));
plsdata.awg.hardwareSetup.set_channel('TABOR_D', ...
Expand Down
3 changes: 3 additions & 0 deletions qctoolkit/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ def __eq__(self, other):
return False
return numpy.all(self._expression_vector == other.underlying_expression)

def __getitem__(self, item) -> Expression:
return self._expression_vector[item]

@property
def underlying_expression(self) -> numpy.ndarray:
return self._expression_vector
Expand Down
7 changes: 7 additions & 0 deletions qctoolkit/pulses/function_pulse_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import numbers

import numpy as np
import sympy

from qctoolkit.expressions import ExpressionScalar
from qctoolkit.serialization import Serializer
Expand Down Expand Up @@ -144,6 +145,12 @@ def deserialize(serializer: Serializer,
parameter_constraints=parameter_constraints
)

@property
def integral(self) -> Dict[ChannelID, ExpressionScalar]:
return {self.__channel: ExpressionScalar(
sympy.integrate(self.__expression.sympified_expression, ('t', 0, self.duration.sympified_expression))
)}


class FunctionWaveform(Waveform):
"""Waveform obtained from instantiating a FunctionPulseTemplate."""
Expand Down
39 changes: 39 additions & 0 deletions qctoolkit/pulses/interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from typing import Any, Tuple
import numpy as np

from qctoolkit.expressions import ExpressionScalar


__all__ = ["InterpolationStrategy", "HoldInterpolationStrategy",
"JumpInterpolationStrategy", "LinearInterpolationStrategy"]
Expand All @@ -37,6 +39,19 @@ def __call__(self,
A numpy.ndarray containing the interpolated values.
"""

@property
@abstractmethod
def integral(self) -> ExpressionScalar:
"""Returns the symbolic integral of this interpolation strategy using (v0,t0) and (v1,t1)
to represent start and end point."""

@property
@abstractmethod
def expression(self) -> ExpressionScalar:
"""Returns a symbolic expression of the interpolation strategy using (v0,t0) and (v1, t1)
to represent start and end point and t as free variable. Note that the expression is only valid for values of t
between t0 and t1."""

@abstractmethod
def __repr__(self) -> str:
"""String representation of the Interpolation Strategy Class"""
Expand All @@ -62,6 +77,14 @@ def __call__(self,
m = (end[1] - start[1])/(end[0] - start[0])
return m * (times - start[0]) + start[1]

@property
def integral(self) -> ExpressionScalar:
return ExpressionScalar('0.5 * (t1-t0) * (v0 + v1)')

@property
def expression(self) -> ExpressionScalar:
return ExpressionScalar('v0 + (v1-v0) * (t-t0)/(t1-t0)')

def __str__(self) -> str:
return 'linear'

Expand All @@ -85,6 +108,14 @@ def __call__(self,
)
return np.full_like(times, fill_value=start[1], dtype=float)

@property
def integral(self) -> ExpressionScalar:
return ExpressionScalar('v0*(t1-t0)')

@property
def expression(self) -> ExpressionScalar:
return ExpressionScalar('v0')

def __str__(self) -> str:
return 'hold'

Expand All @@ -108,6 +139,14 @@ def __call__(self,
)
return np.full_like(times, fill_value=end[1], dtype=float)

@property
def integral(self) -> ExpressionScalar:
return ExpressionScalar('v1*(t1-t0)')

@property
def expression(self) -> ExpressionScalar:
return ExpressionScalar('v1')

def __str__(self) -> str:
return 'jump'

Expand Down
Loading

0 comments on commit 925a850

Please sign in to comment.