def repeat_to_duration(self, duration): r'''Repeat start-positioned division payload expression to `duration`. :: >>> payload = [(6, 8), (6, 8), (3, 4)] >>> expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( ... payload, Offset(0)) :: >>> result = expression.repeat_to_duration(Duration(13, 4)) :: >>> print(format(result)) musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=musicexpressiontools.DivisionList( [ durationtools.Division(6, 8), durationtools.Division(6, 8), durationtools.Division(3, 4), durationtools.Division(6, 8), durationtools.Division(2, 8), ], start_offset=durationtools.Offset(0, 1), ), start_offset=durationtools.Offset(0, 1), ) Returns newly constructed start-positioned division payload expression. ''' divisions = sequencetools.repeat_sequence_to_weight(self.payload, duration) result = type(self)(payload=divisions, voice_name=self.voice_name, start_offset=self.start_offset) return result
def repeat_to_duration(self, duration): r'''Repeat payload expression to duration. :: >>> result = \ ... payload_expression.repeat_to_duration(Duration(13, 16)) :: >>> print(format(result)) musicexpressiontools.IterablePayloadExpression( payload=( durationtools.Division(4, 16), durationtools.Division(2, 16), durationtools.Division(4, 16), durationtools.Division(2, 16), durationtools.Division(1, 16), ), ) Returns newly constructed payload expression. ''' if not mathtools.all_are_numbers(self.payload): payload = [durationtools.Division(_) for _ in self.payload] else: payload = self.payload payload = sequencetools.repeat_sequence_to_weight(payload, duration) payload = [durationtools.Division(_) for _ in payload] result = new(self, payload=payload) return result
def evaluate(self): r"""Evaluate division region expression. Returns start-positioned division payload expression. """ from experimental.tools import musicexpressiontools divisions = self.source_expression[:] divisions = [durationtools.Division(x) for x in divisions] divisions = sequencetools.repeat_sequence_to_weight(divisions, self.total_duration) expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name ) return expression
def evaluate(self): r'''Evaluate division region expression. Returns start-positioned division payload expression. ''' from experimental.tools import musicexpressiontools divisions = self.source_expression[:] divisions = [durationtools.Division(x) for x in divisions] divisions = sequencetools.repeat_sequence_to_weight( divisions, self.total_duration) expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def evaluate(self): r'''Evaluate literal division region expression. Returns start-positioned division payload expression. ''' from experimental.tools import musicexpressiontools divisions = [] for x in self.source_expression: if hasattr(x, 'duration'): x = x.duration division = durationtools.Division(x) divisions.append(division) divisions = sequencetools.repeat_sequence_to_weight( divisions, self.total_duration) expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def evaluate(self): r'''Evaluate select expression division region expression. Returns none when nonevaluable. Returns start-positioned division payload expression when evaluable. ''' from experimental.tools import musicexpressiontools expression = self.source_expression.evaluate() if expression is not None: divisions = expression.elements divisions = [musicexpressiontools.Division(x) for x in divisions] divisions = sequencetools.repeat_sequence_to_weight( divisions, self.total_duration) expression = \ musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def evaluate(self): r'''Evaluate select expression division region expression. Returns none when nonevaluable. Returns start-positioned division payload expression when evaluable. ''' from experimental.tools import musicexpressiontools expression = self.source_expression.evaluate() if expression is not None: divisions = expression.elements divisions = [durationtools.Division(x) for x in divisions] divisions = sequencetools.repeat_sequence_to_weight( divisions, self.total_duration) expression = \ musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def repeat_to_duration(self, duration): r'''Repeat start-positioned division payload expression to `duration`. :: >>> payload = [(6, 8), (6, 8), (3, 4)] >>> expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( ... payload, Offset(0)) :: >>> result = expression.repeat_to_duration(Duration(13, 4)) :: >>> print(format(result)) musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=musicexpressiontools.DivisionList( [ durationtools.Division(6, 8), durationtools.Division(6, 8), durationtools.Division(3, 4), durationtools.Division(6, 8), durationtools.Division(2, 8), ], start_offset=durationtools.Offset(0, 1), ), start_offset=durationtools.Offset(0, 1), ) Returns newly constructed start-positioned division payload expression. ''' divisions = sequencetools.repeat_sequence_to_weight( self.payload, duration) result = type(self)(payload=divisions, voice_name=self.voice_name, start_offset=self.start_offset) return result
def split_sequence(sequence, weights, cyclic=False, overhang=False): """Splits sequence by weights. .. container:: example **Example 1.** Split sequence cyclically by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ) [(3,), (7, -8), (-2, 1), (3,), (6, -9), (-1,)] .. container:: example **Example 2.** Split sequence cyclically by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=False, ... ) [(3,), (7, -8), (-2, 1), (3,), (6, -9)] .. container:: example **Example 3.** Split sequence once by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=True, ... ) [(3,), (7, -8), (-2, 1), (9, -10)] .. container:: example **Example 4.** Split sequence once by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=False, ... ) [(3,), (7, -8), (-2, 1)] Returns list of sequence types. """ from abjad.tools import sequencetools result = [] current_index = 0 current_piece = [] if cyclic: weights = sequencetools.repeat_sequence_to_weight(weights, mathtools.weight(sequence), allow_total=Less) for weight in weights: current_piece_weight = mathtools.weight(current_piece) while current_piece_weight < weight: current_piece.append(sequence[current_index]) current_index += 1 current_piece_weight = mathtools.weight(current_piece) if current_piece_weight == weight: current_piece = type(sequence)(current_piece) result.append(current_piece) current_piece = [] elif weight < current_piece_weight: overage = current_piece_weight - weight current_last_element = current_piece.pop(-1) needed = abs(current_last_element) - overage needed *= mathtools.sign(current_last_element) current_piece.append(needed) current_piece = type(sequence)(current_piece) result.append(current_piece) overage *= mathtools.sign(current_last_element) current_piece = [overage] if overhang: last_piece = current_piece last_piece.extend(sequence[current_index:]) if last_piece: last_piece = type(sequence)(last_piece) result.append(last_piece) return result
def __call__(self, divisions=None): r'''Calls division-maker on `divisions`. .. container:: example Calls division-maker on division with no remainder: :: >>> maker = makertools.DivisionMaker( ... cyclic=True, ... pattern=[(1, 4)], ... ) >>> lists = maker([(3, 4)]) >>> for list_ in lists: ... list_ [Division(1, 4), Division(1, 4), Division(1, 4)] .. container:: example Calls division-maker cyclically on each division. Positions remainders to the right of each output list: :: >>> maker = makertools.DivisionMaker( ... pattern=[(1, 4)], ... ) >>> lists = maker([(7, 8)]) >>> for list_ in lists: ... list_ [Division(1, 4), Division(1, 4), Division(1, 4), Division(1, 8)] Positions remainder at right of output because divison-maker `remainder` defaults to right. .. container:: example Calls division-maker with pattern set to none: :: >>> maker = makertools.DivisionMaker() >>> lists = maker([(6, 32)]) >>> for list_ in lists: ... list_ [Division(6, 32)] Returns input division unchanged. .. container:: example Calls division-maker on nothing: :: >>> maker = makertools.DivisionMaker(pattern=[(1, 4)]) >>> maker() [] Returns empty list. .. container:: example Call division-maker on multiple divisions: :: >>> maker = makertools.DivisionMaker( ... cyclic=True, ... pattern=[(1, 4)], ... ) >>> lists = maker([(2, 4), (3, 4)]) >>> for list_ in lists: ... list_ [Division(1, 4), Division(1, 4)] [Division(1, 4), Division(1, 4), Division(1, 4)] Returns possibly empty list of division lists. ''' divisions = divisions or [] if not divisions: return [] division_lists = [] for i, division in enumerate(divisions): input_division = durationtools.Division(division) input_duration = durationtools.Duration(input_division) assert 0 < input_division, repr(input_division) if not self.pattern: division_list = [input_division] division_lists.append(division_list) continue division_list = list(self.pattern) pattern_rotation_index = self.pattern_rotation_index or 0 pattern_rotation_index *= i division_list = sequencetools.rotate_sequence( division_list, pattern_rotation_index, ) if self.cyclic: division_list = sequencetools.repeat_sequence_to_weight( division_list, input_division, allow_total=Less, ) total_duration = durationtools.Duration(sum(division_list)) if total_duration == input_duration: division_lists.append(division_list) continue if self.remainder is None: message = 'can not fill {} from {} exactly.' message = message.format(input_division, self.pattern) raise Exception(message) remainder = input_division - total_duration remainder = durationtools.Duration(remainder) remainder = durationtools.Division(remainder) if self.remainder == Left: if self.remainder_fuse_threshold is None: division_list.insert(0, remainder) elif remainder <= self.remainder_fuse_threshold: #division_list[0] += remainder fused_value = division_list[0] + remainder fused_value = durationtools.Division(fused_value) division_list[0] = fused_value else: division_list.insert(0, remainder) elif self.remainder == Right: if self.remainder_fuse_threshold is None: division_list.append(remainder) elif remainder <= self.remainder_fuse_threshold: #division_list[-1] += remainder fused_value = division_list[-1] + remainder fused_value = durationtools.Division(fused_value) division_list[-1] = fused_value else: division_list.append(remainder) else: raise ValueError((self.remainder, remainder)) total_duration = durationtools.Duration(sum(division_list)) pair = total_duration, input_duration assert total_duration == input_duration, pair division_lists.append(division_list) return division_lists
def __call__(self, divisions=None): r'''Calls division-maker on `divisions`. .. container:: example **Example 1.** Splits divisions without remainder: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... cyclic=True, ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(3, 4)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division((1, 4)), Division((1, 4)), Division((1, 4))] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 3/4 c'4 c'4 c'4 } } .. container:: example **Example 2.** Splits divisions with remainder: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(7, 8)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division((1, 4)), Division((1, 4)), Division((1, 4)), Division((1, 8))] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 7/8 c'4 c'4 c'4 c'8 } } Positions remainder at right of output because divison-maker `remainder` defaults to right. .. container:: example **Example 3.** Multiple divisions: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... cyclic=True, ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(2, 4), (3, 4)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division((1, 4)), Division((1, 4))] [Division((1, 4)), Division((1, 4)), Division((1, 4))] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 2/4 c'4 c'4 } { \time 3/4 c'4 c'4 c'4 } } .. container:: example **Example 4.** No durations: :: >>> maker = makertools.SplitByDurationsDivisionCallback() :: >>> time_signatures = [(6, 32)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [Division((6, 32))] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 6/32 c'8. } } Returns input division unchanged. .. container:: example **Example 5.** Empty input: :: >>> maker = makertools.SplitByDurationsDivisionCallback(durations=[(1, 4)]) >>> maker() [] Returns empty list. .. container:: example **Example 6.** Works with start offset: :: >>> callback = makertools.SplitByDurationsDivisionCallback( ... cyclic=True, ... durations=[(1, 4)], ... ) :: >>> divisions = [(2, 4), (3, 4)] >>> divisions = [durationtools.Division(_) for _ in divisions] >>> divisions[0]._start_offset = Offset(1, 4) >>> divisions [Division((2, 4), start_offset=Offset(1, 4)), Division((3, 4))] :: >>> division_lists = callback(divisions) >>> for division_list in division_lists: ... division_list [Division((1, 4), start_offset=Offset(1, 4)), Division((1, 4), start_offset=Offset(1, 2))] [Division((1, 4), start_offset=Offset(3, 4)), Division((1, 4), start_offset=Offset(1, 1)), Division((1, 4), start_offset=Offset(5, 4))] **Example 7.** Works with start offset: :: >>> division_maker = makertools.DivisionMaker() >>> division_maker = division_maker.fuse_by_counts( ... counts=mathtools.Infinity, ... ) >>> division_maker = division_maker.split_by_durations( ... durations=[Duration(3, 16)], ... remainder=Left, ... ) :: >>> divisions = [(2, 8), (2, 8), (4, 8), (4, 8), (2, 4)] >>> divisions = [durationtools.Division(_) for _ in divisions] >>> divisions[0]._start_offset = Offset(1, 4) :: >>> division_lists = division_maker(divisions) >>> len(division_lists) 1 :: >>> for division in division_lists[0]: ... division Division((1, 8), start_offset=Offset(1, 4)) Division((3, 16), start_offset=Offset(3, 8)) Division((3, 16), start_offset=Offset(9, 16)) Division((3, 16), start_offset=Offset(3, 4)) Division((3, 16), start_offset=Offset(15, 16)) Division((3, 16), start_offset=Offset(9, 8)) Division((3, 16), start_offset=Offset(21, 16)) Division((3, 16), start_offset=Offset(3, 2)) Division((3, 16), start_offset=Offset(27, 16)) Division((3, 16), start_offset=Offset(15, 8)) Division((3, 16), start_offset=Offset(33, 16)) Returns possibly empty list of division lists. ''' from experimental import makertools divisions = divisions or [] if not divisions: return divisions divisions, start_offset = makertools.DivisionMaker._to_divisions( divisions) start_offset = divisions[0].start_offset division_lists = [] for i, division in enumerate(divisions): input_division = durationtools.Division(division) input_duration = durationtools.Duration(input_division) input_meter = metertools.Meter(input_division) assert 0 < input_division, repr(input_division) if not self.durations: division_list = [input_division] division_lists.append(division_list) continue if input_meter.is_simple or not self.durations: durations = self.durations[:] elif input_meter.is_compound: multiplier = self.compound_meter_multiplier durations = [ durationtools.Duration(multiplier * _) for _ in self.durations ] division_list = list(durations) pattern_rotation_index = self.pattern_rotation_index or 0 pattern_rotation_index *= i division_list = sequencetools.rotate_sequence( division_list, pattern_rotation_index, ) if self.cyclic: division_list = sequencetools.repeat_sequence_to_weight( division_list, input_division, allow_total=Less, ) total_duration = durationtools.Duration(sum(division_list)) if total_duration == input_duration: division_lists.append(division_list) continue if self.remainder is None: message = 'can not fill {} from {} exactly.' message = message.format(input_division, durations) raise Exception(message) remainder = input_division - total_duration remainder = durationtools.Division(remainder) if self.remainder == Left: if self.remainder_fuse_threshold is None: division_list.insert(0, remainder) elif remainder <= self.remainder_fuse_threshold: fused_value = division_list[0] + remainder fused_value = durationtools.Division(fused_value) division_list[0] = fused_value else: division_list.insert(0, remainder) elif self.remainder == Right: if self.remainder_fuse_threshold is None: division_list.append(remainder) elif remainder <= self.remainder_fuse_threshold: fused_value = division_list[-1] + remainder fused_value = durationtools.Division(fused_value) division_list[-1] = fused_value else: division_list.append(remainder) else: raise ValueError((self.remainder, remainder)) total_duration = durationtools.Duration(sum(division_list)) pair = total_duration, input_duration assert total_duration == input_duration, pair division_lists.append(division_list) for _ in division_lists: assert isinstance(_, list), repr(_) division_lists, start_offset = makertools.DivisionMaker._to_divisions( division_lists, start_offset ) return division_lists
def _split( self, durations, cyclic=False, fracture_spanners=False, tie_split_notes=True, use_messiaen_style_ties=False, ): from abjad.tools import pitchtools from abjad.tools import selectiontools from abjad.tools import scoretools from abjad.tools import spannertools durations = [durationtools.Duration(x) for x in durations] if cyclic: durations = sequencetools.repeat_sequence_to_weight( durations, self._get_duration()) durations = [durationtools.Duration(x) for x in durations] if sum(durations) < self._get_duration(): last_duration = self._get_duration() - sum(durations) durations.append(last_duration) sequencetools.truncate_sequence( durations, weight=self._get_duration(), ) result = [] leaf_prolation = self._get_parentage(include_self=False).prolation timespan = self._get_timespan() start_offset = timespan.start_offset for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation shard = new_leaf._set_duration( preprolated_duration, use_messiaen_style_ties=use_messiaen_style_ties, ) for x in shard: if isinstance(x, scoretools.Leaf): x_duration = x.written_duration * leaf_prolation else: x_duration = x.multiplied_duration * leaf_prolation stop_offset = x_duration + start_offset x._start_offset = start_offset x._stop_offset = stop_offset x._timespan = timespantools.Timespan( start_offset=start_offset, stop_offset=stop_offset, ) start_offset = stop_offset shard = [x._get_parentage().root for x in shard] result.append(shard) flattened_result = sequencetools.flatten_sequence(result) flattened_result = selectiontools.SliceSelection(flattened_result) prototype = (spannertools.Tie,) parentage = self._get_parentage() if parentage._get_spanners(prototype=prototype): selection = select(flattened_result) for component in selection: # TODO: make top-level detach() work here for spanner in component._get_spanners(prototype): spanner._sever_all_components() #detach(prototype, component) # replace leaf with flattened result selection = selectiontools.SliceSelection(self) parent, start, stop = selection._get_parent_and_start_stop_indices() if parent: parent.__setitem__(slice(start, stop + 1), flattened_result) else: selection._give_dominant_spanners(flattened_result) selection._withdraw_from_crossing_spanners() # fracture spanners if fracture_spanners: first_shard = result[0] for spanner in first_shard[-1]._get_spanners(): index = spanner._index(first_shard[-1]) spanner._fracture(index, direction=Right) last_shard = result[-1] for spanner in last_shard[0]._get_spanners(): index = spanner._index(last_shard[0]) spanner._fracture(index, direction=Left) for middle_shard in result[1:-1]: for spanner in middle_shard[0]._get_spanners(): index = spanner._index(middle_shard[0]) spanner._fracture(index, direction=Left) for spanner in middle_shard[-1]._get_spanners(): index = spanner._index(middle_shard[-1]) spanner._fracture(index, direction=Right) # adjust first leaf first_leaf = flattened_result[0] self._detach_grace_containers(kind='after') # adjust any middle leaves for middle_leaf in flattened_result[1:-1]: middle_leaf._detach_grace_containers(kind='grace') self._detach_grace_containers(kind='after') detach(object, middle_leaf) # adjust last leaf last_leaf = flattened_result[-1] last_leaf._detach_grace_containers(kind='grace') detach(object, last_leaf) # tie split notes, rests and chords as specified if pitchtools.Pitch.is_pitch_carrier(self) and tie_split_notes: flattened_result_leaves = iterate(flattened_result).by_class( scoretools.Leaf) # TODO: implement SliceSelection._attach_tie_spanner_to_leaves() for leaf_pair in sequencetools.iterate_sequence_nwise( flattened_result_leaves): selection = selectiontools.ContiguousSelection(leaf_pair) selection._attach_tie_spanner_to_leaf_pair( use_messiaen_style_ties=use_messiaen_style_ties, ) # return 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 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 split_sequence(sequence, weights, cyclic=False, overhang=False): '''Splits sequence by weights. .. container:: example **Example 1.** Split sequence cyclically by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ) [(3,), (7, -8), (-2, 1), (3,), (6, -9), (-1,)] .. container:: example **Example 2.** Split sequence cyclically by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=False, ... ) [(3,), (7, -8), (-2, 1), (3,), (6, -9)] .. container:: example **Example 3.** Split sequence once by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=True, ... ) [(3,), (7, -8), (-2, 1), (9, -10)] .. container:: example **Example 4.** Split sequence once by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=False, ... ) [(3,), (7, -8), (-2, 1)] Returns list of sequence types. ''' from abjad.tools import sequencetools result = [] current_index = 0 current_piece = [] if cyclic: weights = sequencetools.repeat_sequence_to_weight( weights, mathtools.weight(sequence), allow_total=Less) for weight in weights: current_piece_weight = mathtools.weight(current_piece) while current_piece_weight < weight: current_piece.append(sequence[current_index]) current_index += 1 current_piece_weight = mathtools.weight(current_piece) if current_piece_weight == weight: current_piece = type(sequence)(current_piece) result.append(current_piece) current_piece = [] elif weight < current_piece_weight: overage = current_piece_weight - weight current_last_element = current_piece.pop(-1) needed = abs(current_last_element) - overage needed *= mathtools.sign(current_last_element) current_piece.append(needed) current_piece = type(sequence)(current_piece) result.append(current_piece) overage *= mathtools.sign(current_last_element) current_piece = [overage] if overhang: last_piece = current_piece last_piece.extend(sequence[current_index:]) if last_piece: last_piece = type(sequence)(last_piece) result.append(last_piece) return result
def _split( self, durations, cyclic=False, fracture_spanners=False, tie_split_notes=True, use_messiaen_style_ties=False, ): from abjad.tools import pitchtools from abjad.tools import selectiontools from abjad.tools import scoretools from abjad.tools import spannertools durations = [durationtools.Duration(x) for x in durations] if cyclic: durations = sequencetools.repeat_sequence_to_weight( durations, self._get_duration()) durations = [durationtools.Duration(x) for x in durations] if sum(durations) < self._get_duration(): last_duration = self._get_duration() - sum(durations) durations.append(last_duration) sequencetools.truncate_sequence( durations, weight=self._get_duration(), ) result = [] leaf_prolation = self._get_parentage(include_self=False).prolation timespan = self._get_timespan() start_offset = timespan.start_offset for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation shard = new_leaf._set_duration( preprolated_duration, use_messiaen_style_ties=use_messiaen_style_ties, ) for x in shard: if isinstance(x, scoretools.Leaf): x_duration = x.written_duration * leaf_prolation else: x_duration = x.multiplied_duration * leaf_prolation stop_offset = x_duration + start_offset x._start_offset = start_offset x._stop_offset = stop_offset x._timespan = timespantools.Timespan( start_offset=start_offset, stop_offset=stop_offset, ) start_offset = stop_offset shard = [x._get_parentage().root for x in shard] result.append(shard) flattened_result = sequencetools.flatten_sequence(result) flattened_result = selectiontools.Selection(flattened_result) prototype = (spannertools.Tie,) parentage = self._get_parentage() if parentage._get_spanners(prototype=prototype): selection = select(flattened_result) for component in selection: # TODO: make top-level detach() work here for spanner in component._get_spanners(prototype): spanner._sever_all_components() #detach(prototype, component) # replace leaf with flattened result selection = selectiontools.Selection(self) parent, start, stop = selection._get_parent_and_start_stop_indices() if parent: parent.__setitem__(slice(start, stop + 1), flattened_result) else: selection._give_dominant_spanners(flattened_result) selection._withdraw_from_crossing_spanners() # fracture spanners if fracture_spanners: first_shard = result[0] for spanner in first_shard[-1]._get_spanners(): index = spanner._index(first_shard[-1]) spanner._fracture(index, direction=Right) last_shard = result[-1] for spanner in last_shard[0]._get_spanners(): index = spanner._index(last_shard[0]) spanner._fracture(index, direction=Left) for middle_shard in result[1:-1]: for spanner in middle_shard[0]._get_spanners(): index = spanner._index(middle_shard[0]) spanner._fracture(index, direction=Left) for spanner in middle_shard[-1]._get_spanners(): index = spanner._index(middle_shard[-1]) spanner._fracture(index, direction=Right) # adjust first leaf first_leaf = flattened_result[0] self._detach_grace_containers(kind='after') # adjust any middle leaves for middle_leaf in flattened_result[1:-1]: middle_leaf._detach_grace_containers(kind='grace') self._detach_grace_containers(kind='after') detach(object, middle_leaf) # adjust last leaf last_leaf = flattened_result[-1] last_leaf._detach_grace_containers(kind='grace') detach(object, last_leaf) # tie split notes, rests and chords as specified if pitchtools.Pitch.is_pitch_carrier(self) and tie_split_notes: flattened_result_leaves = iterate(flattened_result).by_class( scoretools.Leaf) # TODO: implement Selection._attach_tie_spanner_to_leaves() for leaf_pair in sequencetools.iterate_sequence_nwise( flattened_result_leaves): selection = selectiontools.Selection(leaf_pair) selection._attach_tie_spanner_to_leaf_pair( use_messiaen_style_ties=use_messiaen_style_ties, ) # return result return result
def split_sequence(sequence, weights, cyclic=False, overhang=False): '''Splits sequence by weights. .. container:: example **Example 1.** Splits sequence cyclically by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ) ((3,), (7, -8), (-2, 1), (3,), (6, -9), (-1,)) .. container:: example **Example 2.** Splits sequence cyclically by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=True, ... overhang=False, ... ) ((3,), (7, -8), (-2, 1), (3,), (6, -9)) .. container:: example **Example 3.** Splits sequence once by weights with overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=True, ... ) ((3,), (7, -8), (-2, 1), (9, -10)) .. container:: example **Example 4.** Splits sequence once by weights without overhang: :: >>> sequencetools.split_sequence( ... (10, -10, 10, -10), ... (3, 15, 3), ... cyclic=False, ... overhang=False, ... ) ((3,), (7, -8), (-2, 1)) .. container:: example **Example 5.** Splits list once by weights without overhang: :: >>> sequencetools.split_sequence( ... [10, -10, 10, -10], ... (3, 15, 3), ... cyclic=False, ... overhang=False, ... ) [[3], [7, -8], [-2, 1]] Returns new object of `sequence` type with elements also of `sequence` type. ''' from abjad.tools import sequencetools if not isinstance(sequence, collections.Sequence): message = 'must by sequence {!r}.' message = message.format(sequence) raise Exception(message) sequence_type = type(sequence) result = [] current_index = 0 current_piece = [] if cyclic: weights = sequencetools.repeat_sequence_to_weight( weights, mathtools.weight(sequence), allow_total=Less, ) for weight in weights: current_piece_weight = mathtools.weight(current_piece) while current_piece_weight < weight: current_piece.append(sequence[current_index]) current_index += 1 current_piece_weight = mathtools.weight(current_piece) if current_piece_weight == weight: current_piece = type(sequence)(current_piece) result.append(current_piece) current_piece = [] elif weight < current_piece_weight: overage = current_piece_weight - weight current_last_element = current_piece.pop(-1) needed = abs(current_last_element) - overage needed *= mathtools.sign(current_last_element) current_piece.append(needed) current_piece = type(sequence)(current_piece) result.append(current_piece) overage *= mathtools.sign(current_last_element) current_piece = [overage] if overhang: last_piece = current_piece last_piece.extend(sequence[current_index:]) if last_piece: last_piece = type(sequence)(last_piece) result.append(last_piece) result = sequence_type(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 __call__(self, divisions=None): r'''Calls division-maker on `divisions`. .. container:: example **Example 1.** NonreducedFraction without remainder: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... cyclic=True, ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(3, 4)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [NonreducedFraction(1, 4), NonreducedFraction(1, 4), NonreducedFraction(1, 4)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 3/4 c'4 c'4 c'4 } } .. container:: example **Example 2.** NonreducedFraction with remainder: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(7, 8)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [NonreducedFraction(1, 4), NonreducedFraction(1, 4), NonreducedFraction(1, 4), NonreducedFraction(1, 8)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 7/8 c'4 c'4 c'4 c'8 } } Positions remainder at right of output because divison-maker `remainder` defaults to right. .. container:: example **Example 3.** Multiple divisions: :: >>> maker = makertools.SplitByDurationsDivisionCallback( ... cyclic=True, ... durations=[(1, 4)], ... ) :: >>> time_signatures = [(2, 4), (3, 4)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [NonreducedFraction(1, 4), NonreducedFraction(1, 4)] [NonreducedFraction(1, 4), NonreducedFraction(1, 4), NonreducedFraction(1, 4)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 2/4 c'4 c'4 } { \time 3/4 c'4 c'4 c'4 } } .. container:: example **Example 4.** No durations: :: >>> maker = makertools.SplitByDurationsDivisionCallback() :: >>> time_signatures = [(6, 32)] >>> division_lists = maker(time_signatures) >>> for division_list in division_lists: ... division_list [NonreducedFraction(6, 32)] :: >>> maker = rhythmmakertools.NoteRhythmMaker() >>> divisions = sequencetools.flatten_sequence(division_lists) >>> music = maker(divisions) >>> lilypond_file = rhythmmakertools.make_lilypond_file( ... music, ... divisions, ... time_signatures=time_signatures, ... ) >>> show(lilypond_file) # doctest: +SKIP .. doctest:: >>> staff = maker._get_rhythmic_staff(lilypond_file) >>> f(staff) \new RhythmicStaff { { \time 6/32 c'8. } } Returns input division unchanged. .. container:: example **Example 5.** Empty input: :: >>> maker = makertools.SplitByDurationsDivisionCallback(durations=[(1, 4)]) >>> maker() [] Returns empty list. Returns possibly empty list of division lists. ''' divisions = divisions or [] if not divisions: return divisions division_lists = [] for i, division in enumerate(divisions): input_division = mathtools.NonreducedFraction(division) input_duration = durationtools.Duration(input_division) input_meter = metertools.Meter(input_division) assert 0 < input_division, repr(input_division) if not self.durations: division_list = [input_division] division_lists.append(division_list) continue if input_meter.is_simple or not self.durations: durations = self.durations[:] elif input_meter.is_compound: multiplier = self.compound_meter_multiplier durations = [ mathtools.NonreducedFraction(multiplier * _) for _ in self.durations ] #division_list = list(self.durations) division_list = list(durations) pattern_rotation_index = self.pattern_rotation_index or 0 pattern_rotation_index *= i division_list = sequencetools.rotate_sequence( division_list, pattern_rotation_index, ) if self.cyclic: division_list = sequencetools.repeat_sequence_to_weight( division_list, input_division, allow_total=Less, ) total_duration = durationtools.Duration(sum(division_list)) if total_duration == input_duration: division_lists.append(division_list) continue if self.remainder is None: message = 'can not fill {} from {} exactly.' #message = message.format(input_division, self.durations) message = message.format(input_division, durations) raise Exception(message) remainder = input_division - total_duration remainder = durationtools.Duration(remainder) remainder = mathtools.NonreducedFraction(remainder) if self.remainder == Left: if self.remainder_fuse_threshold is None: division_list.insert(0, remainder) elif remainder <= self.remainder_fuse_threshold: fused_value = division_list[0] + remainder fused_value = mathtools.NonreducedFraction(fused_value) division_list[0] = fused_value else: division_list.insert(0, remainder) elif self.remainder == Right: if self.remainder_fuse_threshold is None: division_list.append(remainder) elif remainder <= self.remainder_fuse_threshold: fused_value = division_list[-1] + remainder fused_value = mathtools.NonreducedFraction(fused_value) division_list[-1] = fused_value else: division_list.append(remainder) else: raise ValueError((self.remainder, remainder)) total_duration = durationtools.Duration(sum(division_list)) pair = total_duration, input_duration assert total_duration == input_duration, pair division_lists.append(division_list) for _ in division_lists: assert isinstance(_, list), repr(_) return division_lists
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