Skip to content

Commit 768ea86

Browse files
author
maxbeer99
committed
Merge remote-tracking branch 'origin/feat/scheduling_test' into feat/linspace_measurements/atssimple
2 parents 05e3746 + bb126a2 commit 768ea86

16 files changed

+2051
-23
lines changed

qupulse/examples/expectation_checker_meta.py

Lines changed: 541 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# -*- coding: utf-8 -*-
2+
import numpy as np
3+
4+
from qupulse.pulses import PointPT, ConstantPT, RepetitionPT, ForLoopPT, TablePT,\
5+
FunctionPT, AtomicMultiChannelPT, SequencePT, MappingPT, ParallelConstantChannelPT,\
6+
SchedulerPT
7+
from qupulse.program.linspace import LinSpaceBuilder, to_increment_commands
8+
from qupulse.program.multi import MultiProgramBuilder
9+
from qupulse.program.loop import LoopBuilder
10+
11+
from qupulse.plotting import plot
12+
from qupulse.utils import to_next_multiple
13+
14+
from expectation_checker_meta import ExpectationChecker, HDAWGAlazar
15+
16+
#%% Get Devices
17+
18+
# Get the Alazar and the HDAWG in hardcoded configuration (2V p-p range)
19+
# Connect any Marker from the HDAWG to Trig in of the Alazar
20+
# Connect one dummy channel from HDAWG to the external clock of the Alazar,
21+
# then set 0.5V-10MHz-oscillator on this channel (e.g. in HDAWG-Webinterface)
22+
23+
ha = HDAWGAlazar("DEVXXXX","USB",)
24+
25+
#%% Example pulse definitions
26+
27+
# PulseTemplates must be defined on channels named as 'ZI0_X', X in A to H
28+
# and possibly marker channel(s), as denoted +'_MARKER_FRONT'
29+
# channel subsets {AB,CD,EF,GH} to be referenced as f'expcheck_{i}', i in range(4)
30+
# (ensure correct mapping to Alazar)
31+
# Markers need to be added on the according channel to trigger the alazar
32+
33+
34+
class MultiSchedule():
35+
def __init__(self,base_time=1e2,rep_factor=3):
36+
37+
init_chs = {'ZI0_A','ZI0_B','ZI0_A_MARKER_FRONT','ZI0_B_MARKER_FRONT',}
38+
manip_chs = {'ZI0_C','ZI0_D','ZI0_C_MARKER_FRONT','ZI0_D_MARKER_FRONT',}
39+
40+
zone1 = {'expcheck_0': init_chs, 'expcheck_1': manip_chs,}
41+
42+
init_chs_2 = {'ZI0_E','ZI0_F','ZI0_E_MARKER_FRONT','ZI0_F_MARKER_FRONT',}
43+
manip_chs_2 = {'ZI0_G','ZI0_H','ZI0_G_MARKER_FRONT','ZI0_H_MARKER_FRONT',}
44+
45+
top_channel_structure = {
46+
'zone1': zone1,
47+
'expcheck_2': init_chs_2,
48+
}
49+
50+
markers_ab = {f'ZI0_{x}_MARKER_FRONT':1.0 for x in 'AB'}#|{f'ZI0_{x}_MARKER_BACK':0.0 for x in 'AB'}
51+
markers_cd = {f'ZI0_{x}_MARKER_FRONT':1.0 for x in 'CD'}#|{f'ZI0_{x}_MARKER_BACK':0.0 for x in 'CD'}
52+
markers_ef = {f'ZI0_{x}_MARKER_FRONT':1.0 for x in 'EF'}#|{f'ZI0_{x}_MARKER_BACK':0.0 for x in 'EF'}
53+
markers_ab_to_ef = {ch1:ch2 for ch1,ch2 in zip(markers_ab.keys(),markers_ef.keys())}
54+
55+
some_pt = ParallelConstantChannelPT("amp*1/8"*FunctionPT("sin(t/100)","t_sin",channel='a'),{'b':-0.5}|markers_ab)
56+
some_pt2 = ParallelConstantChannelPT("amp*1/8"*FunctionPT("sin(t/100)","t_sin",channel='a'),{'b':-0.5}|markers_ab)
57+
some_other_pt2 = ParallelConstantChannelPT("amp*1/8"*FunctionPT("sin(t/200)","t_sin",channel='c'),{'d':-0.6}|markers_cd)
58+
59+
60+
schedule = SchedulerPT(zone1)
61+
top_schedule = SchedulerPT(top_channel_structure)
62+
63+
s1 = schedule.add_pt(MappingPT(some_pt,channel_mapping=dict(a='ZI0_A',b='ZI0_B')), None)
64+
s2 = schedule.add_pt(MappingPT(some_pt2,channel_mapping=dict(a='ZI0_A',b='ZI0_B')),s1)
65+
s3 = schedule.add_pt(MappingPT(some_other_pt2,channel_mapping=dict(c='ZI0_C',d='ZI0_D')),s1,rel_time=80+16.)
66+
s3 = schedule.add_pt(MappingPT(some_other_pt2,channel_mapping=dict(c='ZI0_C',d='ZI0_D'),parameter_mapping=dict(t_sin="3.5*t_sin",amp='0.7')),s3,rel_time=4*80+16.)
67+
68+
69+
looped = ForLoopPT(schedule, 'amp', 5)
70+
# looped = RepetitionPT(schedule,5)
71+
72+
n1 = top_schedule.add_pt(looped, None)
73+
n2 = top_schedule.add_pt(MappingPT(some_pt2,parameter_mapping={'amp':0.5,'t_sin':1e4},channel_mapping=dict(a='ZI0_E',b='ZI0_F')|markers_ab_to_ef), n1, ref_point="start",rel_time=-128.)
74+
# n2 = top_schedule.add_pt(MappingPT(some_pt2,parameter_mapping={'amp':0.5,'t_sin':1e4},channel_mapping=dict(a='ZI0_G',b='ZI0_H')), n1, ref_point="start",rel_time=192.)
75+
76+
self.pulse_template = MappingPT(top_schedule,parameter_mapping=dict(t_sin=192.))
77+
self.channel_subsets = top_channel_structure
78+
79+
80+
#%% Instantiate Checker
81+
82+
# select exemplary pulse
83+
pulse = MultiSchedule()
84+
85+
# # Define a program builder to test program with:
86+
default_program_builder = LinSpaceBuilder(
87+
#set to True to ensure triggering at Program start if program starts with constant pulse
88+
play_marker_when_constant=True,
89+
#in case stepped repetitions are needed, insert variables here:
90+
# to_stepping_repeat={'example',},
91+
# to_stepping_repeat={'amp','amp2','off','freq'},
92+
)
93+
94+
program_builder = MultiProgramBuilder.from_mapping(default_program_builder, channel_subsets=pulse.channel_subsets)
95+
96+
# Data will be saved as xr.Dataset in save_path
97+
# data_offsets corrects for offsets in Alazar (not in saved data, only in plotting)
98+
checker = ExpectationChecker(ha, pulse.pulse_template,
99+
program_builder=program_builder,
100+
save_path=r"D:\2302_experiment_env\qupulse_dev_scripts\expcheck",
101+
data_offsets={'t_offset':-100.,'v_offset':0.008,'v_scale':0.9975}
102+
)
103+
104+
assert float(pulse.pulse_template.duration) < 1e7, "Ensure you know what you're doing when recording long data"
105+
106+
#%% Run the checker
107+
108+
checker.run()

qupulse/hardware/setup.py

Lines changed: 166 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
from typing import NamedTuple, Set, Callable, Dict, Tuple, Union, Iterable, Any, Mapping
1+
from typing import NamedTuple, Set, Callable, Dict, Tuple, Union, Iterable, Any, Mapping, Self
22
from collections import defaultdict
3+
from dataclasses import dataclass
34
import warnings
45
import numbers
56

67
from qupulse.hardware.awgs.base import AWG
78
from qupulse.hardware.dacs import DAC
89
from qupulse.program.loop import Loop
10+
from qupulse.program.multi import MultiProgram
911

1012
from qupulse.utils.types import ChannelID
1113

@@ -313,8 +315,171 @@ def registered_programs(self) -> Dict[str, RegisteredProgram]:
313315
return self._registered_programs
314316

315317

318+
@dataclass
319+
class RegisteredMultiProgram:
320+
program: MultiProgram
321+
measurement_windows: Dict[str, Tuple[np.ndarray, np.ndarray]]
322+
run_callback: Callable
323+
hw_setups_to_utilize: Set[HardwareSetup]
324+
dacs_to_arm: Set[DAC]
316325

317326

327+
class MetaHardwareSetup:
328+
329+
def __init__(self):
330+
# self._channel_map: Dict[str, Set[ChannelID]] = dict()
331+
self._setup_map: Dict[str, HardwareSetup] = dict()
332+
333+
334+
self._registered_programs: Dict[str, RegisteredMultiProgram] = dict()
335+
336+
self._alibi_measurement_setup = HardwareSetup()
337+
338+
def register_program(self,
339+
name: str,
340+
program: MultiProgram,
341+
run_callback: Callable = lambda: None,
342+
update: bool = False,
343+
flatten_structure: bool = False,
344+
measurements: Mapping[str, Tuple[np.ndarray, np.ndarray]] = None
345+
) -> None:
346+
347+
hw_setups_to_utilize = set()
348+
349+
if flatten_structure:
350+
program_map = program._flattened_program_map
351+
else:
352+
program_map = program.program_map
353+
354+
############
355+
#GET MEASUREMENT WINDOWS, collect from all subprograms (can have different ones...)
356+
# measurements: Mapping[str, Tuple[np.ndarray, np.ndarray]]
357+
if measurements is None:
358+
measurements = program.get_measurement_windows(drop=True)
359+
360+
self._alibi_measurement_setup.register_program(name, None,
361+
run_callback=lambda: None,
362+
channels=set(),measurements=measurements
363+
)
364+
365+
#END MEAS.
366+
############
367+
368+
369+
370+
371+
for s_ident,prog in program_map.items():
372+
self.setup_map[s_ident].register_program(s_ident+'_'+name, prog,
373+
update=update,run_callback=lambda:None,
374+
measurements={})
375+
hw_setups_to_utilize.add(self.setup_map[s_ident])
376+
377+
self._registered_programs[name] = RegisteredMultiProgram(program,
378+
self._alibi_measurement_setup.registered_programs[name].measurement_windows, #meas todo TODOTODO exract from alibi setup
379+
run_callback,
380+
hw_setups_to_utilize,
381+
dacs_to_arm=self._alibi_measurement_setup.registered_programs[name].dacs_to_arm,
382+
# dacs_to_arm=set(affected_dacs.keys())
383+
)
384+
385+
return
386+
387+
@property
388+
def _measurement_map(self) -> Dict[str, Set[MeasurementMask]]:
389+
return self._alibi_measurement_setup._measurement_map
390+
391+
392+
def set_measurement(self, measurement_name: str,
393+
measurement_mask: Union[MeasurementMask, Iterable[MeasurementMask]],
394+
allow_multiple_registration: bool=False):
395+
return self._alibi_measurement_setup.set_measurement(measurement_name, measurement_mask, allow_multiple_registration)
396+
397+
398+
def remove_program(self, name: str):
399+
if name in self.registered_programs:
400+
for setup in self.setup_map.values():
401+
if name in setup.registered_programs:
402+
setup.remove_program(name)
403+
self._alibi_measurement_setup.remove_program(name)
404+
405+
406+
def clear_programs(self):
407+
for setup in self._setup_map.values():
408+
setup.clear_programs()
409+
410+
self._alibi_measurement_setup.clear_programs()
411+
self._registered_programs = {}
412+
413+
@property
414+
def known_hw_setups(self) -> Set[Union[HardwareSetup,"MetaHardwareSetup"]]:
415+
return set(self._setup_map.values())
416+
417+
@property
418+
def setup_map(self) -> Dict[str,Union[HardwareSetup,"MetaHardwareSetup"]]:
419+
return self._setup_map
420+
421+
@property
422+
def known_awgs(self) -> Set[AWG]:
423+
return set().union(*[s.known_awgs for s in self._setup_map.values()])
424+
425+
@property
426+
def known_dacs(self) -> Set[DAC]:
427+
return self._alibi_measurement_setup.known_dacs
428+
429+
def arm_program(self, name: str) -> None:
430+
"""Assert program is in memory. Hardware will wait for trigger event"""
431+
if name not in self._registered_programs:
432+
raise KeyError('{} is not a registered program'.format(name))
433+
434+
hw_setups_to_utilize = self._registered_programs[name].hw_setups_to_utilize
435+
for key,setup in self._setup_map.items():
436+
if setup in hw_setups_to_utilize:
437+
setup.arm_program(key+'_'+name)
438+
else:
439+
# The other AWGs should ignore the trigger
440+
# awg.arm(None)
441+
pass
442+
self._alibi_measurement_setup.arm_program(name)
443+
# for dac in dacs_to_arm:
444+
# dac.arm_program(name)
445+
@property
446+
def _channel_map(self) -> Dict[str, Set[ChannelID]]:
447+
return {k: s.registered_channels.keys() for k,s in self._setup_map.items()}
448+
449+
def run_program(self, name) -> None:
450+
"""Calls arm program and starts it using the run callback"""
451+
self.arm_program(name)
452+
self._registered_programs[name].run_callback()
453+
454+
def add_setup(self, identifier: str,
455+
setup: Union[HardwareSetup,"MetaHardwareSetup"],
456+
allow_multiple_registration: bool=False) -> None:
457+
458+
assert identifier not in self._setup_map
459+
assert setup not in self._setup_map.values()
460+
461+
assert not any(s in self.known_awgs for s in setup.known_awgs)
462+
463+
self._setup_map[identifier] = setup
464+
465+
def rm_setup(self, identifier: str) -> None:
466+
self._setup_map.pop(identifier)
467+
468+
@property
469+
def all_registered_channels(self) -> Dict[ChannelID, Set[_SingleChannel]]:
470+
return {k:v for setup in self._setup_map.values() for k,v in setup.registered_channels().items()}
471+
472+
# def update_parameters(self, name: str, parameters: Mapping[str, numbers.Real]):
473+
# *_, awgs, dacs = self._registered_programs[name]
474+
475+
# for awg in self.known_awgs:
476+
# if awg in awgs:
477+
# awg.set_volatile_parameters(name, parameters)
478+
479+
@property
480+
def registered_programs(self) -> Dict[str, RegisteredMultiProgram]:
481+
return self._registered_programs
482+
318483

319484

320485

qupulse/program/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ def with_iteration(self, index_name: str, rng: range,
7474
def evaluate_nested_stepping(self, scope: Scope, parameter_names: set[str]) -> bool:
7575
return False
7676

77-
def to_program(self, defined_channels: Set[ChannelID]) -> Optional[Program]:
77+
def to_program(self,
78+
# defined_channels: Set[ChannelID]
79+
) -> Optional[Program]:
7880
"""Further addition of new elements might fail after finalizing the program."""
7981

8082

0 commit comments

Comments
 (0)