def test_mathtools_cumulative_sums_pairwise_02(): r'''Yield pairwise cumulative sums of l from 0. ''' l = [1, 2, 3, 4, 5, 6] g = mathtools.cumulative_sums_pairwise(l) assert list(g) == [(0, 1), (1, 3), (3, 6), (6, 10), (10, 15), (15, 21)]
def test_mathtools_cumulative_sums_pairwise_01(): r'''Yield pairwise cumulative sums of l from 0. ''' l = [3, 1, 2, 1, 3, 3, 1] g = mathtools.cumulative_sums_pairwise(l) assert list(g) == [ (0, 3), (3, 4), (4, 6), (6, 7), (7, 10), (10, 13), (13, 14)]
def test_mathtools_cumulative_sums_pairwise_02(): r'''Yield pairwise cumulative sums of l from 0. ''' l = [1, 2, 3, 4, 5, 6] g = mathtools.cumulative_sums_pairwise(l) assert list(g) == [ (0, 1), (1, 3), (3, 6), (6, 10), (10, 15), (15, 21)]
def partition_sequence_by_counts( sequence, counts, cyclic=False, overhang=False, reversed_=False, ): r'''Partitions `sequence` by `counts`. .. container:: example **Example 1.** Partitions sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2]] **Example 2.** Partitions sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6]] .. container:: example **Example 3.** Partitions sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8]] **Example 4.** Partitions sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13]] .. container:: example **Example 5.** Partitions sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]] **Example 6.** Partitions sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]] .. container:: example **Example 7.** Partitions sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] **Example 8.** Partitions sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13], [14, 15]] .. container:: example **Example 9.** Partitions sequence once by counts and asserts that sequence partitions exactly (with no overhang): :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [2, 3, 5], ... cyclic=False, ... overhang=Exact, ... ) [[0, 1], [2, 3, 4], [5, 6, 7, 8, 9]] **Example 10.** Partitions sequence cyclically by counts and asserts that sequence partitions exactly (with no overhang): :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [2], ... cyclic=True, ... overhang=Exact, ... ) [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]] .. container:: example **Example 11.** Partitions list: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]] **Example 12.** Partitions tuple: :: >>> sequencetools.partition_sequence_by_counts( ... tuple(range(10)), ... [3], ... cyclic=False, ... overhang=True, ... ) [(0, 1, 2), (3, 4, 5, 6, 7, 8, 9)] **Example 13.** Partitions string: :: >>> sequencetools.partition_sequence_by_counts( ... 'some text', ... [3], ... cyclic=False, ... overhang=True, ... ) ['som', 'e text'] .. container:: example **Example 14.** Reverses cyclic partition with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], ... [3], ... cyclic=True, ... overhang=True, ... reversed_=True, ... ) [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] **Example 15.** Reverses cyclic partition without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], ... [3], ... cyclic=True, ... overhang=False, ... reversed_=True, ... ) [[1, 2, 3], [4, 5, 6], [7, 8, 9]] **Example 16.** Reverses acyclic partition with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], ... [3], ... cyclic=False, ... overhang=True, ... reversed_=True, ... ) [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9]] **Example 17.** Reverses acyclic partition without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], ... [3], ... cyclic=False, ... overhang=False, ... reversed_=True, ... ) [[7, 8, 9]] Returns list of objects with type equal to that of `sequence`. ''' from abjad.tools import sequencetools if not isinstance(sequence, collections.Sequence): message = 'must be sequence: {!r}.' message = message.format(sequence) raise TypeError(message) if not isinstance(counts, collections.Iterable): message = 'must be iterable: {!r}.' message = message.format(counts) raise TypeError(message) sequence_type = type(sequence) if reversed_: sequence = reversed(sequence) sequence = sequence_type(sequence) if overhang == Exact: result_with_overhang = partition_sequence_by_counts( sequence, counts, cyclic=cyclic, overhang=True, ) result_without_overhang = partition_sequence_by_counts( sequence, counts, cyclic=cyclic, overhang=False, ) if result_with_overhang == result_without_overhang: return result_without_overhang else: message = 'sequence does not partition exactly.' raise Exception(message) result = [] if cyclic: if overhang: counts = sequencetools.repeat_sequence_to_weight( counts, len(sequence), ) else: counts = sequencetools.repeat_sequence_to_weight( counts, len(sequence), allow_total=Less, ) elif overhang: weight_counts = mathtools.weight(counts) len_sequence = len(sequence) if weight_counts < len_sequence: counts = list(counts) counts.append(len(sequence) - weight_counts) for start, stop in mathtools.cumulative_sums_pairwise(counts): part = sequence[start:stop] result.append(part) if reversed_: result_ = [] for part in reversed(result): part_type = type(part) part = reversed(part) part = part_type(part) result_.append(part) result = result_ return result
def partition_sequence_by_counts( sequence, counts, cyclic=False, overhang=False, copy_elements=False, ): r'''Partitions sequence by counts. .. container:: example **Example 1a.** Partition sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2]] .. container:: example **Example 1b.** Partition sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6]] .. container:: example **Example 2a.** Partition sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8]] .. container:: example **Example 2b.** Partition sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13]] .. container:: example **Example 3a.** Partition sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]] .. container:: example **Example 3b.** Partition sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]] .. container:: example **Example 4a.** Partition sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] .. container:: example **Example 4b.** Partition sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13], [14, 15]] Returns list of sequence objects. ''' from abjad.tools import sequencetools if not isinstance(counts, (tuple, list)): message = 'must be list or tuple: {!r}.' message = message.format(counts) raise TypeError(message) result = [] if cyclic: if overhang: counts = sequencetools.repeat_sequence_to_weight( counts, len(sequence), ) else: counts = sequencetools.repeat_sequence_to_weight( counts, len(sequence), allow_total=Less, ) elif overhang: weight_counts = mathtools.weight(counts) len_sequence = len(sequence) if weight_counts < len_sequence: counts = list(counts) counts.append(len(sequence) - weight_counts) for start, stop in mathtools.cumulative_sums_pairwise(counts): result.append(type(sequence)(sequence[start:stop])) return result
def tessalate_by_ratio(self, ratio, invert_on_negative=False, reflect_on_negative=False, y_center=None, ): r'''Concatenate copies of a BreakPointFunction, stretched by the weights in `ratio`: :: >>> bpf = interpolationtools.BreakPointFunction( ... {0.: 0., 0.25: 0.9, 1.: 1.}) :: >>> bpf.tessalate_by_ratio((1, 2, 3)) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (0.9,), 3.0: (1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) Negative ratio values are still treated as weights. If `invert_on_negative` is True, copies corresponding to negative ratio values will be inverted: :: >>> bpf.tessalate_by_ratio((1, -2, 3), invert_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 1.5: (0.09999999999999998,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) If `y_center` is not None, inversion will take `y_center` as the axis of inversion: :: >>> bpf.tessalate_by_ratio((1, -2, 3), ... invert_on_negative=True, y_center=0) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (-0.9,), 3.0: (-1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) If `reflect_on_negative` is True, copies corresponding to negative ratio values will be reflectd: :: >>> bpf.tessalate_by_ratio((1, -2, 3), reflect_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 2.5: (0.9,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) Inversion may be combined reflecting. Emit new `BreakPointFunction` instance. ''' ratio = mathtools.Ratio(ratio) tessalated_bpf = None for i, pair in enumerate(mathtools.cumulative_sums_pairwise( [abs(x) for x in ratio])): sign = mathtools.sign(ratio[i]) start, stop = pair bpf = self.scale_x_axis(start, stop) if sign < 0: if invert_on_negative: bpf = bpf.invert(y_center) if reflect_on_negative: bpf = bpf.reflect() if i == 0: tessalated_bpf = bpf else: tessalated_bpf = tessalated_bpf.concatenate(bpf) return tessalated_bpf
def partition_sequence_by_backgrounded_weights(sequence, weights): r'''Partition `sequence` by backgrounded `weights`: :: >>> sequencetools.partition_sequence_by_backgrounded_weights( ... [-5, -15, -10], [20, 10]) [[-5, -15], [-10]] Further examples: :: >>> sequencetools.partition_sequence_by_backgrounded_weights( ... [-5, -15, -10], [5, 5, 5, 5, 5, 5]) [[-5], [-15], [], [], [-10], []] :: >>> sequencetools.partition_sequence_by_backgrounded_weights( ... [-5, -15, -10], [1, 29]) [[-5], [-15, -10]] :: >>> sequencetools.partition_sequence_by_backgrounded_weights( ... [-5, -15, -10], [2, 28]) [[-5], [-15, -10]] :: >>> sequencetools.partition_sequence_by_backgrounded_weights( ... [-5, -15, -10], [1, 1, 1, 1, 1, 25]) [[-5], [], [], [], [], [-15, -10]] The term `backgrounded` is a short-hand concocted specifically for this function; rely on the formal definition to understand the function actually does. Input constraint: the weight of `sequence` must equal the weight of `weights` exactly. The signs of the elements in `sequence` are ignored. Formal definition: partition `sequence` into `parts` such that (1.) the length of `parts` equals the length of `weights`; (2.) the elements in `sequence` appear in order in `parts`; and (3.) some final condition that is difficult to formalize. Notionally what's going on here is that the elements of `weights` are acting as a list of successive time intervals into which the elements of `sequence` are being fit in accordance with the start offset of each `sequence` element. The function models the grouping together of successive timespans according to which of an underlying sequence of time intervals it is in which each time span begins. Note that, for any input to this function, the flattened output of this function always equals `sequence` exactly. Note too that while `partition` is being used here in the sense of the other partitioning functions in the API, the distinguishing feature is this funciton is its ability to produce empty lists as output. Returns list of `sequence` objects. ''' from abjad.tools import sequencetools assert all(0 < x for x in weights) assert mathtools.weight(sequence) == mathtools.weight(weights) start_offsets = \ mathtools.cumulative_sums([abs(x) for x in sequence])[:-1] indicator = zip(start_offsets, sequence) result = [] for interval_start, interval_stop in \ mathtools.cumulative_sums_pairwise(weights): part = [] for pair in indicator[:]: if interval_start <= pair[0] < interval_stop: part.append(pair[1]) indicator.remove(pair) result.append(part) return result
def tessalate_by_ratio( self, ratio, invert_on_negative=False, reflect_on_negative=False, y_center=None, ): r'''Concatenate copies of a BreakPointFunction, stretched by the weights in `ratio`: :: >>> bpf = interpolationtools.BreakPointFunction( ... {0.: 0., 0.25: 0.9, 1.: 1.}) :: >>> bpf.tessalate_by_ratio((1, 2, 3)) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (0.9,), 3.0: (1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) Negative ratio values are still treated as weights. If `invert_on_negative` is True, copies corresponding to negative ratio values will be inverted: :: >>> bpf.tessalate_by_ratio((1, -2, 3), invert_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 1.5: (0.09999999999999998,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) If `y_center` is not None, inversion will take `y_center` as the axis of inversion: :: >>> bpf.tessalate_by_ratio((1, -2, 3), ... invert_on_negative=True, y_center=0) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0, 0.0), 1.5: (-0.9,), 3.0: (-1.0, 0.0), 3.75: (0.9,), 6.0: (1.0,) }) If `reflect_on_negative` is True, copies corresponding to negative ratio values will be reflected: :: >>> bpf.tessalate_by_ratio((1, -2, 3), reflect_on_negative=True) BreakPointFunction({ 0.0: (0.0,), 0.25: (0.9,), 1.0: (1.0,), 2.5: (0.9,), 3.0: (0.0,), 3.75: (0.9,), 6.0: (1.0,) }) Inversion may be combined reflecting. Emit new `BreakPointFunction` instance. ''' ratio = mathtools.Ratio(ratio) tessalated_bpf = None for i, pair in enumerate( mathtools.cumulative_sums_pairwise( [abs(x) for x in ratio.numbers])): sign = mathtools.sign(ratio.numbers[i]) start, stop = pair bpf = self.scale_x_axis(start, stop) if sign < 0: if invert_on_negative: bpf = bpf.invert(y_center) if reflect_on_negative: bpf = bpf.reflect() if i == 0: tessalated_bpf = bpf else: tessalated_bpf = tessalated_bpf.concatenate(bpf) return tessalated_bpf
def partition_sequence_by_counts( sequence, counts, cyclic=False, overhang=False, copy_elements=False, ): r'''Partitions sequence by counts. .. container:: example **Example 1a.** Partition sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2]] .. container:: example **Example 1b.** Partition sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6]] .. container:: example **Example 2a.** Partition sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8]] .. container:: example **Example 2b.** Partition sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13]] .. container:: example **Example 3a.** Partition sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]] .. container:: example **Example 3b.** Partition sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]] .. container:: example **Example 4a.** Partition sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(10)), ... [3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] .. container:: example **Example 4b.** Partition sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... list(range(16)), ... [4, 3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13], [14, 15]] Returns list of sequence objects. ''' from abjad.tools import sequencetools result = [] if cyclic: if overhang: counts = sequencetools.repeat_sequence_to_weight( counts, len(sequence)) else: counts = sequencetools.repeat_sequence_to_weight( counts, len(sequence), allow_total=Less) elif overhang: weight_counts = mathtools.weight(counts) len_sequence = len(sequence) if weight_counts < len_sequence: counts = list(counts) counts.append(len(sequence) - weight_counts) for start, stop in mathtools.cumulative_sums_pairwise(counts): result.append(type(sequence)(sequence[start:stop])) return result