task: metadata: install: pip3 install .[plotting,zurich-instruments,tektronix,tabor-instruments]
patch: diff --git a/changes.d/622.feature b/changes.d/622.feature
deleted file mode 100644
index cfcbc8db..00000000
--- a/changes.d/622.feature
+++ /dev/null
@@ -1,2 +0,0 @@
-Add option to automatically reduce the sample rate of HDAWG playback for piecewise constant pulses.
-Use `qupulse._program.seqc.WaveformPlayback.ENABLE_DYNAMIC_RATE_REDUCTION` to enable it.
diff --git a/qupulse/_program/_loop.py b/qupulse/_program/_loop.py
index 8c9f7219..9310cf5f 100644
--- a/qupulse/_program/_loop.py
+++ b/qupulse/_program/_loop.py
@@ -2,18 +2,16 @@ from typing import Union, Dict, Iterable, Tuple, cast, List, Optional, Generator
 from collections import defaultdict
 from enum import Enum
 import warnings
-import bisect
 
 import numpy as np
-import sympy.ntheory
 
-from qupulse._program.waveforms import Waveform, ConstantWaveform
+
+from qupulse._program.waveforms import Waveform
 from qupulse._program.volatile import VolatileRepetitionCount, VolatileProperty
 
 from qupulse.utils import is_integer
 from qupulse.utils.types import TimeType, MeasurementWindow
 from qupulse.utils.tree import Node, is_tree_circular
-from qupulse.utils.numeric import smallest_factor_ge
 
 from qupulse._program.waveforms import SequenceWaveform, RepetitionWaveform
 
@@ -580,58 +578,6 @@ def make_compatible(program: Loop, minimal_waveform_length: int, waveform_quantu
         assert comp_level == _CompatibilityLevel.compatible
 
 
-def roll_constant_waveforms(program: Loop, minimal_waveform_quanta: int, waveform_quantum: int, sample_rate: TimeType):
-    """This function finds waveforms in program that can be replaced with repetitions of shorter waveforms and replaces
-    them. Complexity O(N_waveforms)
-
-    This is possible if:
-     - The waveform is constant on all channels
-     - waveform.duration * sample_rate / waveform_quantum has a factor that is bigger than minimal_waveform_quanta
-
-    Args:
-        program:
-        minimal_waveform_quanta:
-        waveform_quantum:
-        sample_rate:
-    """
-    waveform = program.waveform
-
-    if waveform is None:
-        for child in program:
-            roll_constant_waveforms(child, minimal_waveform_quanta, waveform_quantum, sample_rate)
-    else:
-        waveform_quanta = (waveform.duration * sample_rate) // waveform_quantum
-
-        # example
-        # waveform_quanta = 15
-        # minimal_waveform_quanta = 2
-        # => repetition_count = 5, new_waveform_quanta = 3
-        if waveform_quanta < minimal_waveform_quanta * 2:
-            # there is no way to roll this waveform because it is too short
-            return
-
-        const_values = waveform.constant_value_dict()
-        if const_values is None:
-            # The waveform is not constant
-            return
-
-        new_waveform_quanta = smallest_factor_ge(waveform_quanta, min_factor=minimal_waveform_quanta)
-        if new_waveform_quanta == waveform_quanta:
-            # the waveform duration in samples has no suitable factor
-            # TODO: Option to insert multiple Loop objects
-            return
-
-        additional_repetition_count = waveform_quanta // new_waveform_quanta
-
-        new_waveform = ConstantWaveform.from_mapping(
-            duration=waveform_quantum * new_waveform_quanta / sample_rate,
-            constant_values=const_values)
-
-        # use the private properties to avoid invalidating the duration cache of the parent loop
-        program._repetition_definition = program.repetition_definition * additional_repetition_count
-        program._waveform = new_waveform
-
-
 class MakeCompatibleWarning(ResourceWarning):
     pass
 
diff --git a/qupulse/_program/seqc.py b/qupulse/_program/seqc.py
index 52d6042a..a139710d 100644
--- a/qupulse/_program/seqc.py
+++ b/qupulse/_program/seqc.py
@@ -64,16 +64,13 @@ class BinaryWaveform:
     """
     __slots__ = ('data',)
 
-    PLAYBACK_QUANTUM = 16
-    PLAYBACK_MIN_QUANTA = 2
-
     def __init__(self, data: np.ndarray):
-        """ TODO: always use both channels?
+        """ always use both channels?
 
         Args:
             data: data as returned from zhinst.utils.convert_awg_waveform
         """
-        n_quantum, remainder = divmod(data.size, 3 * self.PLAYBACK_QUANTUM)
+        n_quantum, remainder = divmod(data.size, 3 * 16)
         assert n_quantum > 1, "Waveform too short (min len is 32)"
         assert remainder == 0, "Waveform has not a valid length"
         assert data.dtype is np.dtype('uint16')
@@ -155,8 +152,6 @@ class BinaryWaveform:
 
         >>> np.savetxt(waveform_dir, binary_waveform.to_csv_compatible_table(), fmt='%u')
         """
-        assert self.data.size % self.PLAYBACK_QUANTUM == 0, "conversion to csv requires a valid length"
-
         table = np.zeros((len(self), 2), dtype=np.uint32)
         table[:, 0] = self.ch1
         table[:, 1] = self.ch2
@@ -166,21 +161,6 @@ class BinaryWaveform:
 
         return table
 
-    def playback_possible(self) -> bool:
-        """Returns if the waveform can be played without padding"""
-        return self.data.size % self.PLAYBACK_QUANTUM == 0
-
-    def dynamic_rate(self, max_rate: int = 12) -> int:
-        min_pre_division_quanta = 2 * self.PLAYBACK_QUANTUM
-
-        reduced = self.data.reshape(-1, 3)
-        for n in range(max_rate):
-            n_quantum, remainder = divmod(reduced.shape[0], min_pre_division_quanta)
-            if remainder != 0 or n_quantum < self.PLAYBACK_MIN_QUANTA or np.any(reduced[::2, :] != reduced[1::2, :]):
-                return n
-            reduced = reduced[::2, :]
-        return max_rate
-
 
 class ConcatenatedWaveform:
     def __init__(self):
@@ -1376,38 +1356,23 @@ class SteppingRepeat(SEQCNode):
 
 class WaveformPlayback(SEQCNode):
     ADVANCE_DISABLED_COMMENT = ' // advance disabled do to parent repetition'
-    ENABLE_DYNAMIC_RATE_REDUCTION = False
 
-    __slots__ = ('waveform', 'shared', 'rate')
+    __slots__ = ('waveform', 'shared')
 
-    def __init__(self, waveform: Tuple[BinaryWaveform, ...], shared: bool = False, rate: int = None):
+    def __init__(self, waveform: Tuple[BinaryWaveform, ...], shared: bool = False):
         assert isinstance(waveform, tuple)
-        if self.ENABLE_DYNAMIC_RATE_REDUCTION and rate is None:
-            for wf in waveform:
-                rate = wf.dynamic_rate(12 if rate is None else rate)
         self.waveform = waveform
         self.shared = shared
-        self.rate = rate
 
     def samples(self) -> int:
-        """Samples consumed in the big concatenated waveform"""
         if self.shared:
             return 0
         else:
             wf_lens = set(map(len, self.waveform))
             assert len(wf_lens) == 1
             wf_len, = wf_lens
-            if self.rate is not None:
-                wf_len //= (1 << self.rate)
             return wf_len
 
-    def rate_reduced_waveform(self) -> Tuple[BinaryWaveform]:
-        if self.rate is None:
-            return self.waveform
-        else:
-            return tuple(BinaryWaveform(wf.data.reshape((-1, 3))[::(1 << self.rate), :].ravel())
-                         for wf in self.waveform)
-
     def stepping_hash(self) -> int:
         if self.shared:
             return hash((type(self), self.waveform))
@@ -1417,7 +1382,7 @@ class WaveformPlayback(SEQCNode):
     def same_stepping(self, other: 'WaveformPlayback') -> bool:
         same_type = type(self) is type(other) and self.shared == other.shared
         if self.shared:
-            return same_type and self.rate == other.rate and self.waveform == other.waveform
+            return same_type and self.waveform == other.waveform
         else:
             return same_type and self.samples() == other.samples()
 
@@ -1426,23 +1391,24 @@ class WaveformPlayback(SEQCNode):
 
     def _visit_nodes(self, waveform_manager: ProgramWaveformManager):
         if not self.shared:
-            waveform_manager.request_concatenated(self.rate_reduced_waveform())
+            waveform_manager.request_concatenated(self.waveform)
 
     def to_source_code(self, waveform_manager: ProgramWaveformManager,
                        node_name_generator: Iterator[str], line_prefix: str, pos_var_name: str,
                        advance_pos_var: bool = True):
-        rate_adjustment = "" if self.rate is None else f", {self.rate}"
         if self.shared:
-            yield f'{line_prefix}playWave(' \
-                  f'{waveform_manager.request_shared(self.rate_reduced_waveform())}' \
-                  f'{rate_adjustment});'
+            yield '{line_prefix}playWave({waveform});'.format(waveform=waveform_manager.request_shared(self.waveform),
+                                                              line_prefix=line_prefix)
         else:
-            wf_name = waveform_manager.request_concatenated(self.rate_reduced_waveform())
+            wf_name = waveform_manager.request_concatenated(self.waveform)
             wf_len = self.samples()
-            play_cmd = f'{line_prefix}playWaveIndexed({wf_name}, {pos_var_name}, {wf_len}{rate_adjustment});'
+            play_cmd = '{line_prefix}playWaveIndexed({wf_name}, {pos_var_name}, {wf_len});'
 
             if advance_pos_var:
-                advance_cmd = f' {pos_var_name} = {pos_var_name} + {wf_len};'
+                advance_cmd = ' {pos_var_name} = {pos_var_name} + {wf_len};'
             else:
                 advance_cmd = self.ADVANCE_DISABLED_COMMENT
-            yield play_cmd + advance_cmd
+            yield (play_cmd + advance_cmd).format(wf_name=wf_name,
+                                                  wf_len=wf_len,
+                                                  pos_var_name=pos_var_name,
+                                                  line_prefix=line_prefix)
diff --git a/qupulse/_program/volatile.py b/qupulse/_program/volatile.py
index b92637fd..98106c4a 100644
--- a/qupulse/_program/volatile.py
+++ b/qupulse/_program/volatile.py
@@ -62,9 +62,3 @@ class VolatileRepetitionCount(VolatileValue):
     def update_volatile_dependencies(self, new_constants: Mapping[str, numbers.Number]) -> int:
         self._scope = self._scope.change_constants(new_constants)
         return int(self)
-
-    def __eq__(self, other):
-        if type(self) == type(other):
-            return self._scope is other._scope and self._expression == other._expression
-        else:
-            return NotImplemented
diff --git a/qupulse/_program/waveforms.py b/qupulse/_program/waveforms.py
index bb5e37ec..6036fafa 100644
--- a/qupulse/_program/waveforms.py
+++ b/qupulse/_program/waveforms.py
@@ -779,7 +779,7 @@ class RepetitionWaveform(Waveform):
             end = time + body_duration
             indices = slice(*np.searchsorted(sample_times, (float(time), float(end)), 'left'))
             self._body.unsafe_sample(channel=channel,
-                                     sample_times=sample_times[indices] - time,
+                                     sample_times=sample_times[indices] - float(time),
                                      output_array=output_array[indices])
             time = end
         return output_array
diff --git a/qupulse/utils/numeric.py b/qupulse/utils/numeric.py
index b7b505f9..53c640bb 100644
--- a/qupulse/utils/numeric.py
+++ b/qupulse/utils/numeric.py
@@ -1,10 +1,6 @@
 from typing import Tuple, Type
 from numbers import Rational
 from math import gcd
-from operator import le
-from functools import partial
-
-import sympy
 
 
 def lcm(a: int, b: int):
@@ -12,28 +8,6 @@ def lcm(a: int, b: int):
     return a * b // gcd(a, b)
 
 
-def smallest_factor_ge(n: int, min_factor: int, brute_force: int = 5):
-    """Find the smallest factor of n that is greater or equal min_factor
-
-    Args:
-        n: number to factorize
-        min_factor: factor must be larger this
-        brute_force: range(min_factor, min(min_factor + brute_force)) is probed by brute force
-
-    Returns:
-        Smallest factor of n that is greater or equal min_factor
-    """
-    assert min_factor <= n
-
-    # this shortcut force shortcut costs 1us max
-    for factor in range(min_factor, min(min_factor + brute_force, n)):
-        if n % factor == 0:
-            return factor
-    else:
-        return min(filter(partial(le, min_factor),
-                          sympy.ntheory.divisors(n, generator=True)))
-
-
 def _approximate_int(alpha_num: int, d_num: int, den: int) -> Tuple[int, int]:
     """Find the best fraction approximation of alpha_num / den with an error smaller d_num / den. Best means the
     fraction with the smallest denominator.

problem_statement: RepetitionWaveform sample error due to TimeType time
```python
from qupulse.pulses.plotting import render
from qupulse._program._loop import Loop
from qupulse._program.waveforms import FunctionWaveform
from qupulse.expressions import ExpressionScalar


program = Loop(waveform= FunctionWaveform.from_expression(ExpressionScalar('(0.1)*sin(t / 16.) - 0.3'), 64., channel='a'),
               repetition_count=2)

r = render(program, sample_rate=3)
```

fails with

```
  File "<lambdifygenerated-1>", line 2, in _lambdifygenerated
    return 0.1*sin(0.0625*t) - 0.3

TypeError: loop of ufunc does not support argument 0 of type TimeType which has no callable sin method
```

because RepetitionWaveform changes the sample time to TimeType.
repo: qutech/qupulse
test_cmd: pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
test_patch: diff --git a/tests/_program/loop_tests.py b/tests/_program/loop_tests.py
index 40d1c266..69e36385 100644
--- a/tests/_program/loop_tests.py
+++ b/tests/_program/loop_tests.py
@@ -1,10 +1,7 @@
-import copy
 import unittest
 from unittest import mock
 import itertools
 
-import numpy as np
-
 from string import ascii_uppercase
 
 from qupulse.expressions import ExpressionScalar
@@ -13,10 +10,9 @@ from qupulse.parameter_scope import DictScope
 from qupulse.utils.types import TimeType, time_from_float
 from qupulse._program.volatile import VolatileRepetitionCount
 from qupulse._program._loop import Loop, _make_compatible, _is_compatible, _CompatibilityLevel,\
-    RepetitionWaveform, SequenceWaveform, make_compatible, MakeCompatibleWarning, DroppedMeasurementWarning,\
-    VolatileModificationWarning, roll_constant_waveforms
+    RepetitionWaveform, SequenceWaveform, make_compatible, MakeCompatibleWarning, DroppedMeasurementWarning, VolatileModificationWarning
 from qupulse._program._loop import Loop, _make_compatible, _is_compatible, _CompatibilityLevel,\
-    RepetitionWaveform, SequenceWaveform, make_compatible, MakeCompatibleWarning, ConstantWaveform
+    RepetitionWaveform, SequenceWaveform, make_compatible, MakeCompatibleWarning
 from tests.pulses.sequencing_dummies import DummyWaveform
 from qupulse.pulses.multi_channel_pulse_template import MultiChannelWaveform
 
@@ -482,59 +478,3 @@ class ProgramWaveformCompatibilityTest(unittest.TestCase):
 
                 is_compat.assert_called_once_with(program, **priv_kwargs)
                 make_compat.assert_called_once_with(program, **priv_kwargs)
-
-    def test_roll_constant_waveforms(self):
-        root = Loop(waveform=ConstantWaveform.from_mapping(16, {'A': 1., 'B': 0.5}), repetition_count=4)
-        expected = copy.deepcopy(root)
-        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
-
-        root = Loop(waveform=ConstantWaveform.from_mapping(32, {'A': 1., 'B': 0.5}), repetition_count=4)
-        expected = Loop(waveform=ConstantWaveform.from_mapping(16, {'A': 1., 'B': 0.5}), repetition_count=8)
-        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
-
-        root = Loop(waveform=ConstantWaveform.from_mapping(16*3, {'A': 1., 'B': 0.5}), repetition_count=4)
-        expected = Loop(waveform=ConstantWaveform.from_mapping(16, {'A': 1., 'B': 0.5}), repetition_count=12)
-        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
-
-        root = Loop(waveform=ConstantWaveform.from_mapping(16*3, {'A': 1., 'B': 0.5}), repetition_count=4)
-        expected = copy.deepcopy(root)
-        roll_constant_waveforms(root, 2, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
-
-        root = Loop(waveform=ConstantWaveform.from_mapping(16*5, {'A': 1., 'B': 0.5}), repetition_count=4)
-        expected = copy.deepcopy(root)
-        roll_constant_waveforms(root, 2, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
-
-        root = Loop(children=[
-            Loop(waveform=ConstantWaveform.from_mapping(32, {'A': 1., 'B': 0.5}), repetition_count=4),
-            Loop(waveform=ConstantWaveform.from_mapping(16, {'A': 1., 'B': 0.3}), repetition_count=4),
-            Loop(waveform=ConstantWaveform.from_mapping(128, {'A': .1, 'B': 0.5}), repetition_count=2)
-        ])
-        expected = Loop(children=[
-            Loop(waveform=ConstantWaveform.from_mapping(16, {'A': 1., 'B': 0.5}), repetition_count=8),
-            Loop(waveform=ConstantWaveform.from_mapping(16, {'A': 1., 'B': 0.3}), repetition_count=4),
-            Loop(waveform=ConstantWaveform.from_mapping(16, {'A': .1, 'B': 0.5}), repetition_count=2*128//16)
-        ])
-        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
-
-        not_constant_wf = DummyWaveform(sample_output=np.array([.1, .2, .3]),
-                                        duration=TimeType.from_fraction(32, 1),
-                                        defined_channels={'A', 'B'})
-
-        root = Loop(waveform=not_constant_wf, repetition_count=4)
-        expected = copy.deepcopy(root)
-        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
-
-        scope = DictScope.from_mapping({'a': 4, 'b': 3}, volatile={'a'})
-        rep_count = VolatileRepetitionCount(expression=ExpressionScalar('a+b'), scope=scope)
-        root = Loop(waveform=ConstantWaveform.from_mapping(32, {'A': 1., 'B': 0.5}), repetition_count=rep_count)
-        expected = Loop(waveform=ConstantWaveform.from_mapping(16, {'A': 1., 'B': 0.5}), repetition_count=rep_count * 2)
-        self.assertNotEqual(root.repetition_count, expected.repetition_count)
-        roll_constant_waveforms(root, 1, 16, TimeType.from_fraction(1, 1))
-        self.assertEqual(root, expected)
diff --git a/tests/_program/seqc_tests.py b/tests/_program/seqc_tests.py
index 5d2f2385..27b2d1ab 100644
--- a/tests/_program/seqc_tests.py
+++ b/tests/_program/seqc_tests.py
@@ -6,7 +6,6 @@ import sys
 import tempfile
 import pathlib
 import hashlib
-import random
 
 import numpy as np
 
@@ -14,7 +13,6 @@ from qupulse.expressions import ExpressionScalar
 from qupulse.parameter_scope import DictScope
 
 from qupulse._program._loop import Loop
-from qupulse._program.waveforms import ConstantWaveform
 from qupulse._program.seqc import BinaryWaveform, loop_to_seqc, WaveformPlayback, Repeat, SteppingRepeat, Scope,\
     to_node_clusters, find_sharable_waveforms, mark_sharable_waveforms, UserRegisterManager, HDAWGProgramManager,\
     UserRegister, WaveformFileSystem
@@ -33,33 +31,6 @@ def take(n, iterable):
     return list(islice(iterable, n))
 
 
-class BinaryWaveformTest(unittest.TestCase):
-    MAX_RATE = 14
-
-    def test_dynamic_rate_reduction(self):
-
-        ones = np.ones(2**(self.MAX_RATE + 2) * 3, np.uint16)
-
-        for n in (2, 3, 5):
-            self.assertEqual(BinaryWaveform(ones[:n * 16 * 3]).dynamic_rate(), 0, f"Reducing {n}")
-        for n in (4, 6):
-            self.assertEqual(BinaryWaveform(ones[:16 * n * 3]).dynamic_rate(), 1)
-
-        irreducibles = [
-            np.array([0, 0, 1, 1, 0, 1] * 16, dtype=np.uint16),
-            np.array([0, 0, 0] * 16 + [0, 1, 0] + [0, 0, 0] * 15, dtype=np.uint16),
-            np.array([0, 0, 0] * 16 + [1, 0, 0] + [0, 0, 0] * 15, dtype=np.uint16),
-        ]
-        for max_rate in range(self.MAX_RATE):
-            for n in range(self.MAX_RATE):
-                for irreducible in irreducibles:
-                    data = np.tile(np.tile(irreducible.reshape(-1, 1, 3), (1, 2**n, 1)).ravel(), (16,))
-
-                    dyn_n = BinaryWaveform(data).dynamic_rate(max_rate=max_rate)
-
-                    self.assertEqual(min(max_rate, n), dyn_n)
-
-
 def make_binary_waveform(waveform):
     if waveform.duration == 0:
         data = np.asarray(3 * [1, 2, 3, 4, 5], dtype=np.uint16)
@@ -74,21 +45,15 @@ def make_binary_waveform(waveform):
         return (BinaryWaveform.from_sampled(ch1, ch2, markers),)
 
 
-def _key_to_int(n: int, duration: int, defined_channels: frozenset):
-    key_bytes = str((n, duration, sorted(defined_channels))).encode('ascii')
-    key_int64 = int(hashlib.sha256(key_bytes).hexdigest()[:2*8], base=16) // 2
-    return key_int64
-
-
 def get_unique_wfs(n=10000, duration=32, defined_channels=frozenset(['A'])):
     if not hasattr(get_unique_wfs, 'cache'):
         get_unique_wfs.cache = {}
 
-    key = (n, duration, defined_channels)
+    key = (n, duration)
 
     if key not in get_unique_wfs.cache:
         # positive deterministic int64
-        h = _key_to_int(n, duration, defined_channels)
+        h = int(hashlib.sha256(str(key).encode('ascii')).hexdigest()[:2*8], base=16) // 2
         base = np.bitwise_xor(np.linspace(-h, h, num=duration + n, dtype=np.int64), h)
         base = base / np.max(np.abs(base))
 
@@ -100,27 +65,6 @@ def get_unique_wfs(n=10000, duration=32, defined_channels=frozenset(['A'])):
     return get_unique_wfs.cache[key]
 
 
-def get_constant_unique_wfs(n=10000, duration=192, defined_channels=frozenset(['A'])):
-    if not hasattr(get_unique_wfs, 'cache'):
-        get_unique_wfs.cache = {}
-
-    key = (n, duration, defined_channels)
-
-    if key not in get_unique_wfs.cache:
-        bit_gen = np.random.PCG64(_key_to_int(n, duration, defined_channels))
-        rng = np.random.Generator(bit_gen)
-
-        random_values = rng.random(size=(n, len(defined_channels)))
-
-        sorted_channels = sorted(defined_channels)
-        get_unique_wfs.cache[key] = [
-            ConstantWaveform.from_mapping(duration, {ch: ch_value
-                                                     for ch, ch_value in zip(sorted_channels, wf_values)})
-            for wf_values in random_values
-        ]
-    return get_unique_wfs.cache[key]
-
-
 def complex_program_as_loop(unique_wfs, wf_same):
     root = Loop(repetition_count=12)
 
@@ -826,7 +770,7 @@ const PLAYBACK_FINISHED_MASK = 0b1000000000000000000000000000000;
 const PROG_SEL_MASK = 0b111111111111111111111111111111;
 const INVERTED_PROG_SEL_MASK = 0b11000000000000000000000000000000;
 const IDLE_WAIT_CYCLES = 300;
-wave test_concatenated_waveform_0 = "c45d955d9dc472d46bf74f7eb9ae2ed4d159adea7d6fe9ce3f48c95423535333";
+wave test_concatenated_waveform_0 = "c583e709957ec1536986ae1c7a6ad6311c89e052405d2a5c786760fc2fcdf6e3";
 wave test_shared_waveform_121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518 = "121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518";
 
 // function used by manually triggered programs
@@ -914,135 +858,3 @@ while (true) {
 }"""
         self.assertEqual(expected_program, seqc_program)
 
-    @unittest.skipIf(sys.version_info.minor < 6, "This test requires dict to be ordered.")
-    def test_full_run_with_dynamic_rate_reduction(self):
-        defined_channels = frozenset(['A', 'B', 'C'])
-
-        unique_n = 1000
-        unique_duration = 192
-
-        unique_wfs = get_constant_unique_wfs(n=unique_n, duration=unique_duration,
-                                             defined_channels=defined_channels)
-        same_wf = DummyWaveform(duration=48, sample_output=np.ones(48), defined_channels=defined_channels)
-
-        channels = ('A', 'B')
-        markers = ('C', None, 'A', None)
-        amplitudes = (1., 1.)
-        offsets = (0., 0.)
-        volatage_transformations = (lambda x: x, lambda x: x)
-        sample_rate = 1
-
-        old_value, WaveformPlayback.ENABLE_DYNAMIC_RATE_REDUCTION = WaveformPlayback.ENABLE_DYNAMIC_RATE_REDUCTION, True
-        try:
-            root = complex_program_as_loop(unique_wfs, wf_same=same_wf)
-            seqc_nodes = complex_program_as_seqc(unique_wfs, wf_same=same_wf)
-
-            manager = HDAWGProgramManager()
-
-            manager.add_program('test', root, channels, markers, amplitudes, offsets, volatage_transformations,
-                                sample_rate)
-        finally:
-            WaveformPlayback.ENABLE_DYNAMIC_RATE_REDUCTION = old_value
-
-
-
-        # 0: Program selection
-        # 1: Trigger
-        self.assertEqual({UserRegister(zero_based_value=2): 7}, manager.get_register_values('test'))
-        seqc_program = manager.to_seqc_program()
-        expected_program = """const PROG_SEL_REGISTER = 0;
-const TRIGGER_REGISTER = 1;
-const TRIGGER_RESET_MASK = 0b10000000000000000000000000000000;
-const PROG_SEL_NONE = 0;
-const NO_RESET_MASK = 0b10000000000000000000000000000000;
-const PLAYBACK_FINISHED_MASK = 0b1000000000000000000000000000000;
-const PROG_SEL_MASK = 0b111111111111111111111111111111;
-const INVERTED_PROG_SEL_MASK = 0b11000000000000000000000000000000;
-const IDLE_WAIT_CYCLES = 300;
-wave test_concatenated_waveform_0 = "7fd412eb866ad371f717857ea33b309ec458c6c3469c7e51dcffcdce9a8c2679";
-wave test_shared_waveform_121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518 = "121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518";
-
-// function used by manually triggered programs
-void waitForSoftwareTrigger() {
-  while (true) {
-    var trigger_register = getUserReg(TRIGGER_REGISTER);
-    if (trigger_register & TRIGGER_RESET_MASK) setUserReg(TRIGGER_REGISTER, 0);
-    if (trigger_register) return;
-  }
-}
-
-
-// program definitions
-void test_function() {
-  var pos = 0;
-  var user_reg_2 = getUserReg(2);
-  waitForSoftwareTrigger();
-  var init_pos_1 = pos;
-  repeat(12) {
-    pos = init_pos_1;
-    repeat(1000) { // stepping repeat
-      repeat(10) {
-        repeat(42) {
-          playWaveIndexed(test_concatenated_waveform_0, pos, 48, 2); // advance disabled do to parent repetition
-        }
-        repeat(98) {
-          playWave(test_shared_waveform_121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518, 0);
-        }
-      }
-      pos = pos + 48;
-    }
-    repeat(21) {
-      playWaveIndexed(test_concatenated_waveform_0, pos, 48, 2); // advance disabled do to parent repetition
-    }
-    pos = pos + 48;
-    repeat(23) {
-      playWaveIndexed(test_concatenated_waveform_0, pos, 48, 0); // advance disabled do to parent repetition
-    }
-    pos = pos + 48;
-    var idx_2;
-    for(idx_2 = 0; idx_2 < user_reg_2; idx_2 = idx_2 + 1) {
-      playWaveIndexed(test_concatenated_waveform_0, pos, 48, 0); // advance disabled do to parent repetition
-    }
-    pos = pos + 48;
-  }
-}
-
-// Declare and initialize global variables
-// Selected program index (0 -> None)
-var prog_sel = 0;
-
-// Value that gets written back to program selection register.
-// Used to signal that at least one program was played completely.
-var new_prog_sel = 0;
-
-// Is OR'ed to new_prog_sel.
-// Set to PLAYBACK_FINISHED_MASK if a program was played completely.
-var playback_finished = 0;
-
-
-// runtime block
-while (true) {
-  // read program selection value
-  prog_sel = getUserReg(PROG_SEL_REGISTER);
-  
-  // calculate value to write back to PROG_SEL_REGISTER
-  new_prog_sel = prog_sel | playback_finished;
-  if (!(prog_sel & NO_RESET_MASK)) new_prog_sel &= INVERTED_PROG_SEL_MASK;
-  setUserReg(PROG_SEL_REGISTER, new_prog_sel);
-  
-  // reset playback flag
-  playback_finished = 0;
-  
-  // only use part of prog sel that does not mean other things to select the program.
-  prog_sel &= PROG_SEL_MASK;
-  
-  switch (prog_sel) {
-    case 1:
-      test_function();
-      waitWave();
-      playback_finished = PLAYBACK_FINISHED_MASK;
-    default:
-      wait(IDLE_WAIT_CYCLES);
-  }
-}"""
-        self.assertEqual(expected_program, seqc_program)
\ No newline at end of file
diff --git a/tests/_program/waveforms_tests.py b/tests/_program/waveforms_tests.py
index bb01c084..94179625 100644
--- a/tests/_program/waveforms_tests.py
+++ b/tests/_program/waveforms_tests.py
@@ -357,6 +357,16 @@ class RepetitionWaveformTest(unittest.TestCase):
         self.assertIs(output_expected, output_received)
         np.testing.assert_equal(output_received, inner_sample_times)
 
+    def test_float_sample_time(self):
+        # issue 624
+        body_wf = FunctionWaveform.from_expression(ExpressionScalar('sin(t)'), 1./3., channel='a')
+        rwf = RepetitionWaveform(body_wf, 2)
+
+        sample_times = np.arange(160) / 80. / 3.
+        sampled = rwf.unsafe_sample(sample_times=sample_times, channel='a')
+        inner_sample_times = np.concatenate((sample_times[:80], sample_times[80:] - 1./3.))
+        np.testing.assert_equal(sampled, np.sin(inner_sample_times))
+
     def test_repr(self):
         body_wf = ConstantWaveform(amplitude=1.1, duration=1.3, channel='3')
         wf = RepetitionWaveform(body_wf, 3)
diff --git a/tests/utils/numeric_tests.py b/tests/utils/numeric_tests.py
index b632f058..dae89a0c 100644
--- a/tests/utils/numeric_tests.py
+++ b/tests/utils/numeric_tests.py
@@ -5,7 +5,7 @@ from fractions import Fraction
 from collections import deque
 from itertools import islice
 
-from qupulse.utils.numeric import approximate_rational, approximate_double, smallest_factor_ge
+from qupulse.utils.numeric import approximate_rational, approximate_double
 
 
 def stern_brocot_sequence() -> Iterator[int]:
@@ -109,14 +109,3 @@ class ApproximationTests(unittest.TestCase):
                                                    'which is not the expected {expected}'.format(x=x, err=err,
                                                                                                  result=result,
                                                                                                  expected=expected))
-
-
-class FactorizationTests(unittest.TestCase):
-    def test_smallest_factor_ge(self):
-        for brute_force in range(100):
-            self.assertEqual(smallest_factor_ge(15, 2), 3, brute_force)
-            self.assertEqual(smallest_factor_ge(15, 4), 5, brute_force)
-            self.assertEqual(smallest_factor_ge(45, 2), 3, brute_force)
-            self.assertEqual(smallest_factor_ge(45, 4), 5, brute_force)
-            self.assertEqual(smallest_factor_ge(45, 5), 5, brute_force)
-            self.assertEqual(smallest_factor_ge(36, 8), 9, brute_force)