def __init__(self, sub_waveforms: List[Waveform]) -> None: """Create a new MultiChannelWaveform instance. Use `MultiChannelWaveform.from_parallel` for optimal construction. Requires a list of subwaveforms in the form (Waveform, List(int)) where the list defines the channel mapping, i.e., a value y at index x in the list means that channel x of the subwaveform will be mapped to channel y of this MultiChannelWaveform object. Args: sub_waveforms: The list of sub waveforms of this MultiChannelWaveform. List might get sorted! Raises: ValueError, if a channel mapping is out of bounds of the channels defined by this MultiChannelWaveform ValueError, if several subwaveform channels are assigned to a single channel of this MultiChannelWaveform ValueError, if subwaveforms have inconsistent durations """ if not sub_waveforms: raise ValueError( "MultiChannelWaveform cannot be constructed without channel waveforms." ) # sort the waveforms with their defined channels to make compare key reproducible if not isinstance(sub_waveforms, list): sub_waveforms = list(sub_waveforms) sub_waveforms.sort(key=lambda wf: wf._sort_key_for_channels()) super().__init__(duration=sub_waveforms[0].duration) self._sub_waveforms = tuple(sub_waveforms) defined_channels = set() for waveform in self._sub_waveforms: if waveform.defined_channels & defined_channels: raise ValueError( 'Channel may not be defined in multiple waveforms', waveform.defined_channels & defined_channels) defined_channels |= waveform.defined_channels self._defined_channels = frozenset(defined_channels) if not all( isclose(waveform.duration, self.duration) for waveform in self._sub_waveforms[1:]): # meaningful error message: durations = {} for waveform in self._sub_waveforms: for duration, channels in durations.items(): if isclose(waveform.duration, duration): channels.update(waveform.defined_channels) break else: durations[waveform.duration] = set( waveform.defined_channels) raise ValueError( "MultiChannelWaveform cannot be constructed from channel waveforms of different durations.", durations)
def build_waveform( self, parameters: Dict[str, numbers.Real], channel_mapping: Dict[ChannelID, Optional[ChannelID]] ) -> Optional[Waveform]: self.validate_parameter_constraints(parameters=parameters) sub_waveforms = [] for subtemplate in self.subtemplates: sub_waveform = subtemplate.build_waveform( parameters, channel_mapping=channel_mapping) if sub_waveform is not None: sub_waveforms.append(sub_waveform) if len(sub_waveforms) == 0: return None if len(sub_waveforms) == 1: waveform = sub_waveforms[0] else: waveform = MultiChannelWaveform(sub_waveforms) if self._duration: expected_duration = self._duration.evaluate_numeric(**parameters) if not isclose(expected_duration, waveform.duration): raise ValueError( 'The duration does not ' 'equal the expected duration', expected_duration, waveform.duration) return waveform
def test_issue_584_uninitialized_table_sample(self): """issue 584""" d = 598.3333333333334 - 480 tpt = TablePulseTemplate( entries={'P': [(0, 1.0, 'hold'), (d, 1.0, 'hold')]}) with mock.patch('qupulse._program.waveforms.PULSE_TO_WAVEFORM_ERROR', 1e-6): wf = to_waveform(tpt.create_program()) self.assertTrue(isclose(d, wf.duration, abs_tol=1e-6)) start_time = 0. end_time = wf.duration sample_rate = 3. sample_count = (end_time - start_time) * sample_rate + 1 times = np.linspace(float(start_time), float(wf.duration), num=int(sample_count), dtype=float) times[-1] = np.nextafter(times[-1], times[-2]) out = np.full_like(times, fill_value=np.nan) sampled = wf.get_sampled(channel='P', sample_times=times, output_array=out) expected = np.full_like(times, fill_value=1.) np.testing.assert_array_equal(expected, sampled)
def from_operator(cls, lhs: Waveform, arithmetic_operator: str, rhs: Waveform): # one could optimize rhs_cv to being only created if lhs_cv is not None but this makes the code harder to read lhs_cv = lhs.constant_value_dict() rhs_cv = rhs.constant_value_dict() if lhs_cv is None or rhs_cv is None: return cls(lhs, arithmetic_operator, rhs) else: constant_values = dict(lhs_cv) op = cls.operator_map[arithmetic_operator] rhs_op = cls.rhs_only_map[arithmetic_operator] for ch, rhs_val in rhs_cv.items(): if ch in constant_values: constant_values[ch] = op(constant_values[ch], rhs_val) else: constant_values[ch] = rhs_op(rhs_val) duration = lhs.duration assert isclose(duration, rhs.duration) return ConstantWaveform.from_mapping(duration, constant_values)
def __init__(self, sub_waveforms: Iterable[Waveform]) -> None: """Create a new MultiChannelWaveform instance. Requires a list of subwaveforms in the form (Waveform, List(int)) where the list defines the channel mapping, i.e., a value y at index x in the list means that channel x of the subwaveform will be mapped to channel y of this MultiChannelWaveform object. Args: sub_waveforms (Iterable( Waveform )): The list of sub waveforms of this MultiChannelWaveform Raises: ValueError, if a channel mapping is out of bounds of the channels defined by this MultiChannelWaveform ValueError, if several subwaveform channels are assigned to a single channel of this MultiChannelWaveform ValueError, if subwaveforms have inconsistent durations """ super().__init__() if not sub_waveforms: raise ValueError( "MultiChannelWaveform cannot be constructed without channel waveforms." ) # avoid unnecessary multi channel nesting def flatten_sub_waveforms(to_flatten): for sub_waveform in to_flatten: if isinstance(sub_waveform, MultiChannelWaveform): yield from sub_waveform._sub_waveforms else: yield sub_waveform # sort the waveforms with their defined channels to make compare key reproducible def get_sub_waveform_sort_key(waveform): return tuple( sorted( tuple('{}_stringified_numeric_channel'. format(ch) if isinstance(ch, int) else ch for ch in waveform.defined_channels))) self._sub_waveforms = tuple( sorted(flatten_sub_waveforms(sub_waveforms), key=get_sub_waveform_sort_key)) self.__defined_channels = set() for waveform in self._sub_waveforms: if waveform.defined_channels & self.__defined_channels: raise ValueError( 'Channel may not be defined in multiple waveforms', waveform.defined_channels & self.__defined_channels) self.__defined_channels |= waveform.defined_channels if not all( isclose(waveform.duration, self._sub_waveforms[0].duration) for waveform in self._sub_waveforms[1:]): # meaningful error message: durations = {} for waveform in self._sub_waveforms: for duration, channels in durations.items(): if isclose(waveform.duration, duration): channels.update(waveform.defined_channels) break else: durations[waveform.duration] = set( waveform.defined_channels) raise ValueError( "MultiChannelWaveform cannot be constructed from channel waveforms of different durations.", durations)