Skip to content

time extension via SequencePT #830

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

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
9 changes: 7 additions & 2 deletions qupulse/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@


__all__ = ["checked_int_cast", "is_integer", "isclose", "pairwise", "replace_multiple", "cached_property",
"forced_hash", "to_next_multiple"]
"forced_hash", "to_next_multiple", "next_multiple_of"]


def checked_int_cast(x: Union[float, int, numpy.ndarray], epsilon: float=1e-6) -> int:
Expand Down Expand Up @@ -149,4 +149,9 @@ def to_next_multiple(sample_rate: ExpressionLike, quantum: int,
else:
#still return 0 if duration==0
return lambda duration: ExpressionScalar(f'{quantum}/({sample_rate})*Max({min_quanta},-(-{duration}*{sample_rate}//{quantum}))*Max(0, sign({duration}))')



def next_multiple_of(duration: ExpressionLike, sample_rate: ExpressionLike, quantum: int,
min_quanta: Optional[int] = None) -> ExpressionScalar:
"""thin wrapper around to_next_multiple to directly call the function"""
return to_next_multiple(sample_rate,quantum,min_quanta)(ExpressionScalar(duration))
23 changes: 22 additions & 1 deletion tests/pulses/time_extension_pulse_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from qupulse.pulses import FunctionPT, TimeExtensionPT, SingleWFTimeExtensionPT
from qupulse.program.loop import LoopBuilder
from qupulse.program.linspace import LinSpaceBuilder
from qupulse.utils import to_next_multiple, next_multiple_of
from qupulse.utils.types import TimeType

class TimeExtensionPulseTemplateTests(unittest.TestCase):

Expand Down Expand Up @@ -59,4 +61,23 @@ def test_quantities(self):
duration_extended = self.extend_both.duration.evaluate_in_scope(self.parameters)
duration_summed = self.main_pt.duration.evaluate_in_scope(self.parameters)\
+ self.parameters['t_prior'] + self.parameters['t_posterior']
self.assertEqual(duration_extended, duration_summed)
self.assertEqual(duration_extended, duration_summed)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What I mean by the previous comment is basically this, which gives a - in my opinion - quite convenient way of quickly extend waveforms with shorter and longer hold times guaranteed to match the hardware requirements

Copy link
Member

Choose a reason for hiding this comment

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

I think a utility function like this would be sufficient

def extend(self, *, total_duration = None, prepend = None, append = None) -> SequencePT:
    none_count = total_duration is None + prepend is None + append is None
    if none_count != 1:
        raise ValueError("Exactly two arguments must be specified.")
    # Return a SequencePT

Copy link
Collaborator Author

@Nomos11 Nomos11 Jun 5, 2024

Choose a reason for hiding this comment

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

for that case of not concatenating the waveforms, yes; what about the single wf case? (Which i initially added this for, the other one was then mostly to have similar syntax)

Copy link
Member

Choose a reason for hiding this comment

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

Ah, but why the SequencePT? Why represent the start and end as PulseTemplates? I would either have a PT that represents the time extention without introducing ConstantPTs in the front and in the back OR use SequencePT and add an atomic flag to it which encodes potential atomicity in the PT itself.

Copy link
Collaborator Author

@Nomos11 Nomos11 Jun 5, 2024

Choose a reason for hiding this comment

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

seemed like the least programmatic effort to me that does the job, but that can of course be changed.

def test_pad_to_usecase(self):

main_pt = FunctionPT("tanh(a*t**2 + b*t + c) * sin(b*t + c) + cos(a*t)/2","t_main",channel="a")

extended_pt = TimeExtensionPT(main_pt.pad_to(to_next_multiple("sample_rate",16,4)),
start=0.,
stop=next_multiple_of("t_posterior","sample_rate",16)
)

parameters = dict(t_main=13.8437652,t_posterior=654.,
a=1.,b=0.5,c=1.,
sample_rate=TimeType(12,5),
)

prog = extended_pt.create_program(program_builder=LoopBuilder(),
parameters=parameters)

self.assertEqual(prog.duration, TimeType(2060, 3))
Loading