-
Notifications
You must be signed in to change notification settings - Fork 29
Feat/linspace measurements reversed registers divisor #901
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
base: master
Are you sure you want to change the base?
Changes from 85 commits
1132ee1
4d2fdb9
7cfcb74
74eb4d7
f595806
bc985cd
627a27f
06aa1b3
f87b1ea
e6b1e9e
3c65a9c
634cd30
7d6b01a
1ea5176
37ee51d
b37b510
c796b61
6d4e835
9fd050c
051e8b9
e58277c
cf74051
2447e23
a9f1710
d9dad64
5f22a52
b04cbcf
e4c2366
f250226
e84ecf8
73513eb
468a46c
a7eacf4
550a31c
fc8aee4
b3176f0
def9369
66dc3b5
419f91f
417c6c2
e939b5d
640672e
87dc2db
c320fe5
caadef8
d762797
eb917b7
6c6abb1
7c55850
4000f06
090c0a3
245de3b
4f29e2b
fa03049
31fb3ea
fcd6510
bb1a508
56db9d7
9293abc
4325ea3
74d5dc8
cb8d1bd
1e48d85
6011ab3
4d89e4a
de1295a
feafac0
b1c55b4
4922d71
82bd04c
5050d92
55a4e6b
f93e02f
6d78bec
ec18543
60d493f
0d377c1
30dfecb
2e8c9c9
3d2c634
224907e
88f64c2
a0b1194
4cbc158
88faf7b
8ed592c
1b299e7
d88f45d
461202b
2fbb63a
e8337e2
88740ba
f6e66c5
0d25a97
457a7d0
a9822d7
c539dad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to root examples directory |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
# -*- coding: utf-8 -*- | ||
import numpy as np | ||
|
||
from qupulse.pulses import PointPT, ConstantPT, RepetitionPT, ForLoopPT, TablePT,\ | ||
FunctionPT, AtomicMultiChannelPT, SequencePT, MappingPT, ParallelConstantChannelPT | ||
from qupulse.program.linspace import LinSpaceBuilder | ||
|
||
from qupulse.plotting import plot | ||
from qupulse.utils import to_next_multiple | ||
|
||
from expectation_checker import ExpectationChecker, HDAWGAlazar | ||
|
||
#%% Get Devices | ||
|
||
# Get the Alazar and the HDAWG in hardcoded configuration (2V p-p range) | ||
# Connect any Marker from the HDAWG to Trig in of the Alazar | ||
# Connect one dummy channel from HDAWG to the external clock of the Alazar, | ||
# then set 0.5V-10MHz-oscillator on this channel (e.g. in HDAWG-Webinterface) | ||
|
||
ha = HDAWGAlazar("DEVXXXX","USB",) | ||
|
||
#%% Example pulse definitions | ||
|
||
# PulseTemplates must be defined on channels named as 'ZI0_X', X in A to H | ||
# (ensure correct mapping to Alazar) | ||
# Markers will be overwritten to play a marker on each channel to trigger the Alazar | ||
# identifiers of the final PT will be the names of the plotted object | ||
|
||
|
||
class ShortSingleRampTest(): | ||
def __init__(self, base_time=1e3): | ||
hold = ConstantPT(base_time, {'a': '-1. + idx * 0.01'}) | ||
pt = hold.with_iteration('idx', 200) | ||
self.pulse_template = MappingPT(pt, | ||
channel_mapping={'a':'ZI0_A',}, | ||
identifier=self.__class__.__name__ | ||
) | ||
|
||
class ShortSingleRampTestWithPlay(): | ||
def __init__(self,base_time=1e3+8): | ||
# init = PointPT([(1.0,1e4)],channel_names=('ZI0_MARKER_FRONT',)) | ||
init = FunctionPT('1.0+1e-9*t',base_time,channel='ZI0_A_MARKER_FRONT')#.pad_to(to_next_multiple(1.0,16,4),) | ||
|
||
hold = ConstantPT(base_time, {'a': '-1. + idx * 0.01'})#.pad_to(to_next_multiple(1.0,16,4)) | ||
pt = ParallelConstantChannelPT(init,dict(ZI0_A=0.))@(ParallelConstantChannelPT(hold,dict(ZI0_A_MARKER_FRONT=0.)).with_iteration('idx', 200)) | ||
self.pulse_template = MappingPT(pt, | ||
channel_mapping={'a':'ZI0_A',}, | ||
identifier=self.__class__.__name__ | ||
) | ||
|
||
class SequencedRepetitionTest(): | ||
def __init__(self,base_time=1e2,rep_factor=2): | ||
wait = AtomicMultiChannelPT( | ||
ConstantPT(f'64*{base_time}', {'a': '-1. + idx_a * 0.01 + y_gain', }), | ||
ConstantPT(f'64*{base_time}', {'b': '-0.5 + idx_b * 0.02'}) | ||
) | ||
|
||
dependent_constant = AtomicMultiChannelPT( | ||
ConstantPT(64*base_time, {'a': '-1.0 + y_gain'}), | ||
ConstantPT(64*base_time, {'b': '-0.5 + idx_b*0.02',}), | ||
) | ||
|
||
dependent_constant2 = AtomicMultiChannelPT( | ||
ConstantPT(64*base_time, {'a': '-0.5 + y_gain'}), | ||
ConstantPT(64*base_time, {'b': '-0.3 + idx_b*0.02',}), | ||
) | ||
|
||
|
||
pt = (dependent_constant @ dependent_constant2.with_repetition(rep_factor) @ (wait.with_iteration('idx_a', rep_factor))).with_iteration('idx_b', rep_factor)\ | ||
|
||
self.pulse_template = MappingPT(pt,parameter_mapping=dict(y_gain=0.3,), | ||
channel_mapping={'a':'ZI0_A','b':'ZI0_C'}, | ||
identifier=self.__class__.__name__ | ||
) | ||
|
||
|
||
class SteppedRepetitionTest(): | ||
def __init__(self,base_time=1e2,rep_factor=2): | ||
|
||
wait = ConstantPT(f'64*{base_time}*(1+idx_t)', {'a': '-0.5 + idx_a * 0.15', 'b': '-.5 + idx_a * 0.3'}) | ||
normal_pt = ParallelConstantChannelPT(FunctionPT("sin(t/1000)","t_sin",channel='a'),{'b':-0.2}) | ||
amp_pt = ParallelConstantChannelPT("amp*1/8"*FunctionPT("sin(t/2000)","t_sin",channel='a'),{'b':-0.5}) | ||
# amp_pt2 = ParallelConstantChannelPT("amp2*1/8"*FunctionPT("sin(t/1000)","t_sin",channel='a'),{'b':-0.5}) | ||
amp_inner = ParallelConstantChannelPT(FunctionPT(f"(1+amp)*1/(2*{rep_factor})*sin(4*pi*t/t_sin)","t_sin",channel='a'),{'b':-0.5}) | ||
amp_inner2 = ParallelConstantChannelPT(FunctionPT(f"(1+amp2)*1/(2*{rep_factor})*sin((1*freq)*4*pi*t/t_sin)+off/(2*{rep_factor})","t_sin",channel='a'),{'b':-0.3}) | ||
|
||
pt = ((((normal_pt@amp_inner2).with_iteration('off', rep_factor)@normal_pt@wait)\ | ||
.with_repetition(rep_factor))@amp_inner.with_iteration('amp', rep_factor))\ | ||
.with_iteration('amp2', rep_factor).with_iteration('freq', rep_factor).with_iteration('idx_a',rep_factor) | ||
|
||
self.pulse_template = MappingPT(pt,parameter_mapping=dict(t_sin=64*base_time,idx_t=1, | ||
#idx_a=1,#freq=1,#amp2=1 | ||
), | ||
channel_mapping={'a':'ZI0_A','b':'ZI0_C'}, | ||
identifier=self.__class__.__name__) | ||
|
||
class TimeSweepTest(): | ||
def __init__(self,base_time=1e2,rep_factor=3): | ||
wait = ConstantPT(f'64*{base_time}*(1+idx_t)', | ||
{'a': '-1. + idx_a * 0.01', 'b': '-.5 + idx_b * 0.02'}) | ||
|
||
random_constant = ConstantPT(64*base_time, {'a': -.4, 'b': -.3}) | ||
meas = ConstantPT(64*base_time, {'a': 0.05, 'b': 0.06}) | ||
|
||
singlet_scan = (SequencePT(random_constant,wait,meas,identifier='s')).with_iteration('idx_a', rep_factor)\ | ||
.with_iteration('idx_b', rep_factor)\ | ||
.with_iteration('idx_t', rep_factor) | ||
|
||
self.pulse_template = MappingPT(singlet_scan,channel_mapping={'a':'ZI0_A','b':'ZI0_C'}, | ||
identifier=self.__class__.__name__) | ||
|
||
|
||
#%% Instantiate Checker | ||
|
||
# select exemplary pulse | ||
pulse = ShortSingleRampTest(1e3+8) | ||
# pulse = ShortSingleRampTestWithPlay() | ||
# pulse = SequencedRepetitionTest(1e3,4) | ||
# pulse = SteppedRepetitionTest(1e2,3) | ||
# pulse = TimeSweepTest(1e2,3) | ||
|
||
# Define a program builder to test program with: | ||
program_builder = LinSpaceBuilder( | ||
#set to True to ensure triggering at Program start if program starts with constant pulse | ||
play_marker_when_constant=True, | ||
#in case stepped repetitions are needed, insert variables here: | ||
to_stepping_repeat={'example',}, | ||
) | ||
|
||
# Data will be saved as xr.Dataset in save_path | ||
# data_offsets corrects for offsets in Alazar (not in saved data, only in plotting) | ||
checker = ExpectationChecker(ha, pulse.pulse_template, | ||
program_builder=program_builder, | ||
save_path=SAVE_HERE, | ||
data_offsets={'t_offset':-100.,'v_offset':0.008,'v_scale':0.9975} | ||
) | ||
|
||
assert float(pulse.pulse_template.duration) < 1e7, "Ensure you know what you're doing when recording long data" | ||
|
||
#%% Run the checker | ||
|
||
checker.run() |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I reconsidered and remembered I did not put them there for a reason. The idea is that SimpleExpressions are more like context sensitive numbers than expressions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only needed to move them due to some circular import otherwise i believe. The name might indeed be misleading |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import numpy as np | ||
from numbers import Real, Number | ||
from typing import Optional, Union, Sequence, ContextManager, Mapping, Tuple, Generic, TypeVar, Iterable, Dict, List | ||
from dataclasses import dataclass | ||
|
||
from functools import total_ordering | ||
from qupulse.utils.sympy import _lambdify_modules | ||
from qupulse.expressions import sympy as sym_expr, Expression | ||
from qupulse.utils.types import MeasurementWindow, TimeType, FrozenMapping | ||
|
||
|
||
NumVal = TypeVar('NumVal', bound=Real) | ||
|
||
|
||
@total_ordering | ||
@dataclass | ||
class SimpleExpression(Generic[NumVal]): | ||
"""This is a potential hardware evaluable expression of the form | ||
|
||
C + C1*R1 + C2*R2 + ... | ||
where R1, R2, ... are potential runtime parameters. | ||
|
||
The main use case is the expression of for loop dependent variables where the Rs are loop indices. There the | ||
expressions can be calculated via simple increments. | ||
""" | ||
|
||
base: NumVal | ||
offsets: Mapping[str, NumVal] | ||
|
||
def __post_init__(self): | ||
assert isinstance(self.offsets, Mapping) | ||
|
||
def value(self, scope: Mapping[str, NumVal]) -> NumVal: | ||
value = self.base | ||
for name, factor in self.offsets.items(): | ||
value += scope[name] * factor | ||
return value | ||
|
||
def __abs__(self): | ||
return abs(self.base)+sum([abs(o) for o in self.offsets.values()]) | ||
|
||
def __eq__(self, other): | ||
#there is no good way to compare it without having a value, | ||
#but cannot require more parameters in magic method? | ||
#so have this weird full equality for now which doesn logically make sense | ||
#in most cases to catch unintended consequences | ||
|
||
if isinstance(other, (float, int, TimeType)): | ||
return self.base==other and all([o==other for o in self.offsets]) | ||
|
||
if type(other) == type(self): | ||
if len(self.offsets)!=len(other.offsets): return False | ||
return self.base==other.base and all([o1==o2 for o1,o2 in zip(self.offsets,other.offsets)]) | ||
|
||
return NotImplemented | ||
|
||
def __gt__(self, other): | ||
return all([b for b in self._return_greater_comparison_bools(other)]) | ||
|
||
def __lt__(self, other): | ||
return all([not b for b in self._return_greater_comparison_bools(other)]) | ||
|
||
def _return_greater_comparison_bools(self, other) -> List[bool]: | ||
#there is no good way to compare it without having a value, | ||
#but cannot require more parameters in magic method? | ||
#so have this weird full equality for now which doesn logically make sense | ||
#in most cases to catch unintended consequences | ||
if isinstance(other, (float, int, TimeType)): | ||
return [self.base>other] + [o>other for o in self.offsets.values()] | ||
|
||
if type(other) == type(self): | ||
if len(self.offsets)!=len(other.offsets): return [False] | ||
return [self.base>other.base] + [o1>o2 for o1,o2 in zip(self.offsets.values(),other.offsets.values())] | ||
|
||
return NotImplemented | ||
|
||
def __add__(self, other): | ||
if isinstance(other, (float, int, TimeType)): | ||
return SimpleExpression(self.base + other, self.offsets) | ||
|
||
if type(other) == type(self): | ||
offsets = self.offsets.copy() | ||
for name, value in other.offsets.items(): | ||
offsets[name] = value + offsets.get(name, 0) | ||
return SimpleExpression(self.base + other.base, offsets) | ||
|
||
return NotImplemented | ||
|
||
def __radd__(self, other): | ||
return self.__add__(other) | ||
|
||
def __sub__(self, other): | ||
return self.__add__(-other) | ||
|
||
def __rsub__(self, other): | ||
return (-self).__add__(other) | ||
|
||
def __neg__(self): | ||
return SimpleExpression(-self.base, {name: -value for name, value in self.offsets.items()}) | ||
|
||
def __mul__(self, other: NumVal): | ||
if isinstance(other, (float, int, TimeType)): | ||
return SimpleExpression(self.base * other, {name: other * value for name, value in self.offsets.items()}) | ||
|
||
return NotImplemented | ||
|
||
def __rmul__(self, other): | ||
return self.__mul__(other) | ||
|
||
def __truediv__(self, other): | ||
inv = 1 / other | ||
return self.__mul__(inv) | ||
|
||
def __hash__(self): | ||
return hash((self.base,frozenset(sorted(self.offsets.items())))) | ||
|
||
@property | ||
def free_symbols(self): | ||
return () | ||
|
||
def _sympy_(self): | ||
return self | ||
|
||
def replace(self, r, s): | ||
return self | ||
|
||
def evaluate_in_scope_(self, *args, **kwargs): | ||
# TODO: remove. It is currently required to avoid nesting this class in an expression for the MappedScope | ||
# We can maybe replace is with a HardwareScope or something along those lines | ||
return self | ||
|
||
|
||
#alibi class to allow instance check? | ||
@dataclass | ||
class SimpleExpressionStepped(SimpleExpression): | ||
step_nesting_level: int | ||
rng: range | ||
reverse: int|bool | ||
|
||
|
||
_lambdify_modules.append({'SimpleExpression': SimpleExpression, 'SimpleExpressionStepped': SimpleExpressionStepped}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to root examples directory