From 18ef03c5450b03b6266c2274346c316c9a620635 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 28 May 2014 22:42:01 +0200 Subject: [PATCH] Refactor, introduce experiment class, kernel decorator, parameters and channel objects --- artiq/language/experiment.py | 70 ++++++++++++++++++++++++++++++++++ artiq/{ => language}/units.py | 0 artiq/sim/devices.py | 46 +++++++++++++++++++++++ artiq/{sim.py => sim/time.py} | 56 +++------------------------ examples/al_spectroscopy.py | 71 +++++++++++++++++++++++------------ 5 files changed, 168 insertions(+), 75 deletions(-) create mode 100644 artiq/language/experiment.py rename artiq/{ => language}/units.py (100%) create mode 100644 artiq/sim/devices.py rename artiq/{sim.py => sim/time.py} (50%) diff --git a/artiq/language/experiment.py b/artiq/language/experiment.py new file mode 100644 index 000000000..019c474dd --- /dev/null +++ b/artiq/language/experiment.py @@ -0,0 +1,70 @@ +import itertools + +class Experiment: + def __init__(self, *args, **kwargs): + channels = self.channels.split() + parameters = self.parameters.split() + argnames = channels + parameters + undef_args = list(argnames) + + if len(argnames) < len(args): + raise TypeError("__init__() takes {} positional arguments but {} were given".format(len(argnames), len(args))) + for argname, value in itertools.chain(zip(argnames, args), kwargs.items()): + if hasattr(self, argname): + raise TypeError("__init__() got multiple values for argument '{}'".format(argname)) + if argname not in argnames: + raise TypeError("__init__() got an unexpected keyword argument: '{}'".format(argname)) + setattr(self, argname, value) + undef_args.remove(argname) + if undef_args: + raise TypeError("__init__() missing {} argument(s): ".format(len(undef_args), + ", ".join(["'"+s+"'" for s in undef_args]))) + + self.kernel_attr_ro = set(parameters) + +def kernel(arg): + if isinstance(arg, str): + def real_decorator(function): + def run_on_core(exp, *k_args, **k_kwargs): + getattr(exp, arg).run(function, exp, *k_args, **k_kwargs) + return run_on_core + return real_decorator + else: + def run_on_core(exp, *k_args, **k_kwargs): + exp.core.run(arg, exp, *k_args, **k_kwargs) + return run_on_core + +class _DummyTimeManager: + def _not_implemented(self, *args, **kwargs): + raise NotImplementedError("Attempted to interpret kernel without a time manager") + + enter_sequential = _not_implemented + exit = _not_implemented + take_time = _not_implemented + +_time_manager = _DummyTimeManager() + +def set_time_manager(time_manager): + global _time_manager + _time_manager = time_manager + +# global namespace for interpreted kernels + +class _Sequential: + def __enter__(self): + _time_manager.enter_sequential() + + def __exit__(self, type, value, traceback): + _time_manager.exit() +sequential = _Sequential() + +class _Parallel: + def __enter__(self): + _time_manager.enter_parallel() + + def __exit__(self, type, value, traceback): + _time_manager.exit() +parallel = _Parallel() + +def delay(duration): + _time_manager.take_time(duration.amount) diff --git a/artiq/units.py b/artiq/language/units.py similarity index 100% rename from artiq/units.py rename to artiq/language/units.py diff --git a/artiq/sim/devices.py b/artiq/sim/devices.py new file mode 100644 index 000000000..4d0039445 --- /dev/null +++ b/artiq/sim/devices.py @@ -0,0 +1,46 @@ +from random import Random + +from artiq.language import units +from artiq.sim import time + +class Core: + def run(self, function, *args, **kwargs): + return function(*args, **kwargs) + +class Input: + def __init__(self, name, prng_seed=None, wait_max=20, count_max=100, wait_min=0, count_min=0): + self.name = name + self.wait_min = wait_min + self.wait_max = wait_max + self.count_min = count_min + self.count_max = count_max + self.prng = Random(prng_seed) + + def wait_edge(self): + duration = self.prng.randrange(self.wait_min, self.wait_max)*units.ms + time.manager.event(("wait_edge", self.name, duration)) + time.manager.take_time(duration.amount) + + def count_gate(self, duration): + result = self.prng.randrange(self.count_min, self.count_max) + units.check_unit(duration, units.base_s_unit) + time.manager.event(("count_gate", self.name, duration, result)) + time.manager.take_time(duration.amount) + return result + +class WaveOutput: + def __init__(self, name): + self.name = name + + def pulse(self, frequency, duration): + units.check_unit(frequency, units.base_Hz_unit) + units.check_unit(duration, units.base_s_unit) + time.manager.event(("pulse", self.name, frequency, duration)) + time.manager.take_time(duration.amount) + +class VoltageOutput: + def __init__(self, name): + self.name = name + + def set(self, value): + time.manager.event(("set_voltage", self.name, value)) diff --git a/artiq/sim.py b/artiq/sim/time.py similarity index 50% rename from artiq/sim.py rename to artiq/sim/time.py index d6cdb99ef..87b84e96f 100644 --- a/artiq/sim.py +++ b/artiq/sim/time.py @@ -1,7 +1,6 @@ -from random import Random from operator import itemgetter -from artiq import units +from artiq.language import units, experiment class SequentialTimeContext: def __init__(self, current_time): @@ -21,7 +20,7 @@ class ParallelTimeContext: if amount > self.block_duration: self.block_duration = amount -class TimeManager: +class Manager: def __init__(self): self.stack = [SequentialTimeContext(0)] self.timeline = [] @@ -38,8 +37,8 @@ class TimeManager: old_context = self.stack.pop() self.take_time(old_context.block_duration) - def take_time(self, amount): - self.stack[-1].take_time(amount) + def take_time(self, duration): + self.stack[-1].take_time(duration) def event(self, description): self.timeline.append((self.stack[-1].current_time, description)) @@ -57,48 +56,5 @@ class TimeManager: prev_time = time return r -# global namespace for interpreted kernels - -time_manager = TimeManager() -prng = Random(42) - -class _Sequential: - def __enter__(self): - time_manager.enter_sequential() - - def __exit__(self, type, value, traceback): - time_manager.exit() -sequential = _Sequential() - -class _Parallel: - def __enter__(self): - time_manager.enter_parallel() - - def __exit__(self, type, value, traceback): - time_manager.exit() -parallel = _Parallel() - -def delay(duration): - units.check_unit(duration, units.base_s_unit) - time_manager.take_time(duration.amount) - -def wait_edge(input): - duration = prng.randrange(17)*units.ms - time_manager.event(("wait_edge", input, duration)) - time_manager.take_time(duration.amount) - -def pulse(output, frequency, duration): - units.check_unit(frequency, units.base_Hz_unit) - units.check_unit(duration, units.base_s_unit) - time_manager.event(("pulse", output, frequency, duration)) - time_manager.take_time(duration.amount) - -def count_gate(input, duration): - result = prng.randrange(100) - units.check_unit(duration, units.base_s_unit) - time_manager.event(("count_gate", input, duration, result)) - time_manager.take_time(duration.amount) - return result - -def set_dac_voltage(output): - time_manager.event(("set_dac_voltage", output)) +manager = Manager() +experiment.set_time_manager(manager) diff --git a/examples/al_spectroscopy.py b/examples/al_spectroscopy.py index 7219899a8..cffa14546 100644 --- a/examples/al_spectroscopy.py +++ b/examples/al_spectroscopy.py @@ -1,30 +1,51 @@ -from artiq.sim import * -from artiq.units import * +from artiq.language.units import * +from artiq.language.experiment import * -def al_clock_probe(spectroscopy_freq, A1, A2): - state_0_count = 0 - for count in range(100): - wait_edge("mains_sync") - delay(10*us) - pulse("laser_cooling", 100*MHz, 100*us) - delay(5*us) - with parallel: - pulse("spectroscopy", spectroscopy_freq, 100*us) - with sequential: - delay(50*us) - set_dac_voltage("spectroscopy_b") - delay(5*us) - while True: +class AluminumSpectroscopy(Experiment): + channels = "core mains_sync laser_cooling spectroscopy spectroscopy_b state_detection pmt" + parameters = "spectroscopy_freq photon_limit_low photon_limit_high" + + @kernel + def run(self): + state_0_count = 0 + for count in range(100): + self.mains_sync.wait_edge() + delay(10*us) + self.laser_cooling.pulse(100*MHz, 100*us) delay(5*us) with parallel: - pulse("state_detection", 100*MHz, 10*us) - photon_count = count_gate("pmt", 10*us) - if photon_count < A1 or photon_count > A2: - break - if photon_count < A1: - state_0_count += 1 - return state_0_count + self.spectroscopy.pulse(self.spectroscopy_freq, 100*us) + with sequential: + delay(50*us) + self.spectroscopy_b.set(200) + delay(5*us) + while True: + delay(5*us) + with parallel: + self.state_detection.pulse(100*MHz, 10*us) + photon_count = self.pmt.count_gate(10*us) + if photon_count < self.photon_limit_low or photon_count > self.photon_limit_high: + break + if photon_count < self.photon_limit_low: + state_0_count += 1 + return state_0_count if __name__ == "__main__": - al_clock_probe(30*MHz, 3, 30) - print(time_manager.format_timeline()) + from artiq.sim import devices as sd + from artiq.sim import time + + exp = AluminumSpectroscopy( + core=sd.Core(), + mains_sync=sd.Input("mains_sync"), + laser_cooling=sd.WaveOutput("laser_cooling"), + spectroscopy=sd.WaveOutput("spectroscopy"), + spectroscopy_b=sd.VoltageOutput("spectroscopy_b"), + state_detection=sd.WaveOutput("state_detection"), + pmt=sd.Input("pmt"), + + spectroscopy_freq=432*MHz, + photon_limit_low=10, + photon_limit_high=15 + ) + exp.run() + print(time.manager.format_timeline())