Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes from experiment (helios) #570

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion MATLAB/+qc/awg_program.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
'window_mapping', plsdata.awg.defaultWindowMapping, ...
'global_transformation', plsdata.awg.globalTransformation, ...
'add_marker', {plsdata.awg.defaultAddMarker}, ...
'to_single_waveform', plsdata.awg.toSingleWaveform,...
'force_update', false, ...
'verbosity', 10 ...
);
Expand Down Expand Up @@ -55,7 +56,7 @@
fprintf('Program ''%s'' is now being instantiated...', a.program_name);
tic;
end
instantiated_pulse = qc.instantiate_pulse(a.pulse_template, 'parameters', qc.join_params_and_dicts(program.parameters_and_dicts), 'channel_mapping', program.channel_mapping, 'window_mapping', program.window_mapping, 'global_transformation', program.global_transformation);
instantiated_pulse = qc.instantiate_pulse(a.pulse_template, 'parameters', qc.join_params_and_dicts(program.parameters_and_dicts), 'channel_mapping', program.channel_mapping, 'window_mapping', program.window_mapping, 'global_transformation', program.global_transformation,'to_single_waveform',py.set(a.to_single_waveform));

if a.verbosity > 9
fprintf('took %.0fs\n', toc);
Expand Down
17 changes: 10 additions & 7 deletions MATLAB/+qc/setup_alazar_measurements.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,28 @@

defaultArgs = struct( ...
'disp', true, ...
'nMeasPerQubit', 2, ...
'nQubits', 2 ...
'nMeasPerQubit', 1, ...
'nQubits', 1 ...
);
args = util.parse_varargin(varargin, defaultArgs);
nAlazarChannels = 4;
nQubits = args.nQubits;
nMeasPerQubit = args.nMeasPerQubit;

py.setattr(hws, '_measurement_map', py.dict);
py.setattr(daq, '_mask_prototypes', py.dict);
% py.setattr(hws, '_measurement_map', py.dict());
py.getattr(hws, '_measurement_map').clear();
% py.setattr(daq, '_mask_prototypes', py.dict() );
py.getattr(daq, '_mask_prototypes').clear();
warning('Removing measurement_map and measurement_map might break stuff if previously set. Needs testing.');

for q = 1:nQubits
for m = 1:nMeasPerQubit
% qubitIndex, measIndex, hwChannel, auxFlag1
add_meas_and_mask(q, m, q+nQubits-1, false);
add_meas_and_mask(q, m, q+nQubits-2, false);
end
end

for a = 1:(nAlazarChannels-nQubits)
for a = (nAlazarChannels-nQubits-1):nAlazarChannels
for m = 1:nMeasPerQubit
% qubitIndex, measIndex, hwChannel, auxFlag1
add_meas_and_mask(a, m, a-1, true);
Expand All @@ -71,7 +73,8 @@

if args.nQubits > 2
warning('Simultaneous measurements for more than 2 qubits not implemented at the moment.');
end
end

if q == 2
for m = 1:nMeasPerQubit
% Q1 Q2 qubitIndex, measIndex, hwChannel, auxFlag1, secondQubitIndex, secondHwChannel, auxFlag2
Expand Down
79 changes: 79 additions & 0 deletions MATLAB/+qc/setup_tek_awg.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
function setup_tek_awg(varargin)

global smdata
global plsdata

defaultArgs = struct( ...
'sampleVoltPerAwgVolt', [util.db('dB2F',-45)*2 util.db('dB2F',-45)*2 util.db('dB2F',-45)*2 util.db('dB2F',-45)*2], ... % 10^(-dB/20)*ImpedanceMismatch
'smChannels', {{'RFA', 'RFB', 'RFC', 'RFD'}}, ...
'tekName', 'AWG5000', ...
'globalTransformation', [], ...
'ip', '169.254.40.80', ... %IP's:
'dcMode', false, ...
'maxPulseWait', 60 ... % Maximum waiting time in s in qc.awg_program before arming DAQ again
);

args = util.parse_varargin(varargin, defaultArgs);
plsdata.awg.sampleVoltPerAwgVolt = args.sampleVoltPerAwgVolt;
plsdata.awg.dcMode = args.dcMode;
plsdata.awg.triggerStartTime = 0;
plsdata.awg.maxPulseWait = args.maxPulseWait;
plsdata.awg.minSamples = 250;
plsdata.awg.sampleQuantum = 1;
plsdata.awg.globalTransformation = args.globalTransformation;

for k = 1:numel(args.smChannels)
smChannel = args.smChannels(k);
if ~(smdata.channels(smchanlookup(smChannel)).instchan(1) == sminstlookup(args.tekName))
error('Channel %s does not belong to %s\n', smChannel, args.tekName);
end
smdata.channels(smchanlookup(smChannel)).rangeramp(end) = 1/args.sampleVoltPerAwgVolt(k);
end

% Reload qctoolkit TEK AWG integration
qctoolkit_tek = py.importlib.reload(py.importlib.import_module('qctoolkit.hardware.awgs.tektronix'));

py.importlib.import_module('tek_awg');

awg = py.tek_awg.TekAwg.connect_to_ip('169.254.40.80');
awg.instrument.timeout = py.int(1200000);
% awg = py.tek_awg.TekAwg.connect_raw_visa_socket('169.254.40.80', '4001','@ni');
% %
tawg=qctoolkit_tek.TektronixAWG(awg, pyargs('synchronize','clear'));
% Only real instrument
smdata.inst(sminstlookup(args.tekName)).data.tawg = tawg;

plsdata.awg.inst = smdata.inst(sminstlookup(args.tekName)).data.tawg;
if exist('awgctrl_tek.m', 'file')
awgctrl('off')
end

% Create hardware setup for qctoolkit integration
plsdata.awg.hardwareSetup = py.qctoolkit.hardware.setup.HardwareSetup();

% Create python lambda function in Matlab
numpy = py.importlib.import_module('numpy');
for k = 1:numel(args.sampleVoltPerAwgVolt)
multiply{k} = py.functools.partial(numpy.multiply, double(1./(args.sampleVoltPerAwgVolt(k))));
end

% PlaybackChannels can take more than two values (analog channels)
plsdata.awg.hardwareSetup.set_channel('TEK_A', ...
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst, int64(0), multiply{1}));
plsdata.awg.hardwareSetup.set_channel('TEK_B', ...
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst, int64(1), multiply{2}));
plsdata.awg.hardwareSetup.set_channel('TEK_C', ...
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst, int64(2), multiply{3}));
plsdata.awg.hardwareSetup.set_channel('TEK_D', ...
py.qctoolkit.hardware.setup.PlaybackChannel(plsdata.awg.inst, int64(3), multiply{4}));

% MarkerChannel can only take on two values (digital channels)
plsdata.awg.hardwareSetup.set_channel('TEK_A_MARKER', ...
py.qctoolkit.hardware.setup.MarkerChannel(plsdata.awg.inst, int64(0)));
plsdata.awg.hardwareSetup.set_channel('TEK_B_MARKER', ...
py.qctoolkit.hardware.setup.MarkerChannel(plsdata.awg.inst, int64(1)));
plsdata.awg.hardwareSetup.set_channel('TEK_C_MARKER', ...
py.qctoolkit.hardware.setup.MarkerChannel(plsdata.awg.inst, int64(2)));
plsdata.awg.hardwareSetup.set_channel('TEK_D_MARKER', ...
py.qctoolkit.hardware.setup.MarkerChannel(plsdata.awg.inst, int64(3)));
end
66 changes: 36 additions & 30 deletions qupulse/_program/_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,36 +323,42 @@ def flatten_and_balance(self, depth: int) -> None:
Args:
depth: Target depth of the program
"""
i = 0
while i < len(self):
# only used by type checker
sub_program = cast(Loop, self[i])

if sub_program.depth() < depth - 1:
# increase nesting because the subprogram is not deep enough
sub_program.encapsulate()

elif not sub_program.is_balanced():
# balance the sub program. We revisit it in the next iteration (no change of i )
# because it might modify self. While writing this comment I am not sure this is true. 14.01.2020 Simon
sub_program.flatten_and_balance(depth - 1)

elif sub_program.depth() == depth - 1:
# subprogram is balanced with the correct depth
i += 1

elif sub_program._has_single_child_that_can_be_merged():
# subprogram is balanced but to deep and has no measurements -> we can "lift" the sub-sub-program
# TODO: There was a len(sub_sub_program) == 1 check here that I cannot explain
sub_program._merge_single_child()

elif not sub_program.is_leaf():
# subprogram is balanced but too deep
sub_program.unroll()

else:
# we land in this case if the function gets called with depth == 0 and the current subprogram is a leaf
i += 1
if self._waveform is None:
i = 0
while i < len(self):
# only used by type checker
sub_program = cast(Loop, self[i])

if sub_program.depth() < depth - 1:
# increase nesting because the subprogram is not deep enough
sub_program.encapsulate()

elif not sub_program.is_balanced():
# balance the sub program. We revisit it in the next iteration (no change of i )
# because it might modify self. While writing this comment I am not sure this is true. 14.01.2020 Simon
sub_program.flatten_and_balance(depth - 1)

elif sub_program.depth() == depth - 1:
# subprogram is balanced with the correct depth
i += 1

elif sub_program._has_single_child_that_can_be_merged():
# subprogram is balanced but to deep and has no measurements -> we can "lift" the sub-sub-program
# TODO: There was a len(sub_sub_program) == 1 check here that I cannot explain
sub_program._merge_single_child()

elif not sub_program.is_leaf():
# subprogram is balanced but too deep
sub_program.unroll()

else:
# we land in this case if the function gets called with depth == 0 and the current subprogram is a leaf
i += 1

else:
assert len(self) == 0
for _ in range(depth):
self.encapsulate()

def _has_single_child_that_can_be_merged(self) -> bool:
if len(self) == 1:
Expand Down
11 changes: 11 additions & 0 deletions qupulse/hardware/awgs/tektronix.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

__all__ = ['TektronixAWG']

class TektronixException(Exception):
pass

class WaveformEntry:
def __init__(self, name: str, length: int, waveform: tek_awg.Waveform, timestamp):
Expand Down Expand Up @@ -810,3 +812,12 @@ def run_current_program(self, channel_states: Optional[Tuple[bool, bool, bool, b
self.logger.info("Running program '%s'", program_name)
self.device.jump_to_sequence_element(program_index)
self.device.wait_until_commands_executed()

def amplitude(self, channel) -> float:
if channel not in (1, 2, 3, 4):
raise TektronixException('Invalid channel: {}'.format(channel))

return self._device.get_amplitude(channel)



36 changes: 35 additions & 1 deletion qupulse/hardware/dacs/alazar.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
from qupulse.hardware.dacs.dac_base import DAC


logger = logging.getLogger(__name__)


class AlazarProgram:
def __init__(self):
self._sample_factor = None
self._masks = {}
self.operations = []
self._total_length = None
self._auto_rearm_count = 1

def masks(self, mask_maker: Callable[[str, np.ndarray, np.ndarray], Mask]) -> List[Mask]:
return [mask_maker(mask_name, *data) for mask_name, data in self._masks.items()]
Expand All @@ -39,6 +43,21 @@ def total_length(self) -> int:
def total_length(self, val: int):
self._total_length = val

@property
def auto_rearm_count(self) -> int:
"""This is passed to AlazarCard.startAcquisition. The card will (re-)arm automatically for this many times."""
return self._auto_rearm_count

@auto_rearm_count.setter
def auto_rearm_count(self, value: int):
trigger_count = int(value)
if trigger_count == 0:
raise ValueError("Trigger count of 0 is not supported in qupulse (yet) because tracking the number of "
"remaining triggers is too hard in case of infinity :(")
if not 0 < trigger_count < 2**64:
raise ValueError("Trigger count has to be in the interval [0, 2**64-1]")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[1, 2**64-1]

self._auto_rearm_count = trigger_count

def clear_masks(self):
self._masks.clear()

Expand Down Expand Up @@ -192,6 +211,8 @@ def __init__(self, card, config: Optional[ScanlineConfiguration]=None):
# defaults to self.__card.minimum_record_size
self._buffer_strategy = None

self._remaining_auto_triggers = 0

self._mask_prototypes = dict() # type: Dict

self._registered_programs = defaultdict(AlazarProgram) # type: Dict[str, AlazarProgram]
Expand Down Expand Up @@ -248,8 +269,13 @@ def register_operations(self, program_name: str, operations) -> None:
self._registered_programs[program_name].operations = operations

def arm_program(self, program_name: str) -> None:
logger.debug("Arming program %s on %r", program_name, self.__card)

to_arm = self._registered_programs[program_name]
if self.update_settings or self.__armed_program is not to_arm:
logger.info("Arming %r by calling applyConfiguration. Update settings flag: %r",
self.__card, self.update_settings)

config = self.config
config.masks, config.operations, total_record_size = self._registered_programs[program_name].iter(
self._make_mask)
Expand Down Expand Up @@ -287,7 +313,15 @@ def arm_program(self, program_name: str) -> None:

self.update_settings = False
self.__armed_program = to_arm
self.__card.startAcquisition(1)

elif self.__armed_program is to_arm and self._remaining_auto_triggers > 0:
self._remaining_auto_triggers -= 1
logger.info("Relying on atsaverage auto-arm with %d auto triggers remaining after this one",
self._remaining_auto_triggers)
return

self.__card.startAcquisition(to_arm.auto_rearm_count)
self._remaining_auto_triggers = to_arm.auto_rearm_count - 1

def delete_program(self, program_name: str) -> None:
self._registered_programs.pop(program_name)
Expand Down