def test_get(self): ordered_list = [(10, 100), (5, 20), (7, 70), (2, 50)] om = OrderedMap(ordered_list) a = om.get(5) assert a == 20 from structure.time_signature import TimeSignature from timemodel.time_signature_event import TimeSignatureEvent tse = TimeSignatureEvent(TimeSignature(3, Duration(1, 4)), Position(0)) d = OrderedMap([(tse.time, tse)]) x = d.get(0) assert x == tse
def test_remove(self): ordered_list = [(10, 100), (5, 20), (7, 70), (2, 50)] om = OrderedMap(ordered_list) assert 5 in om assert om.get(5) == 20 om.remove_key(5) assert 5 not in om
def test_merge(self): ordered_list = [(10, 100), (5, 20), (7, 70), (2, 50)] om = OrderedMap(ordered_list) sup = [(3, 60), (15, 2)] om.merge(sup) self.assertTrue(3 in om.keys()) self.assertTrue(om.get(3) == 60) self.assertTrue(15 in om.keys()) self.assertTrue(om.get(15) == 2) self.assertTrue(5 in om.keys()) sup = {3: 60, 15: 2} om.merge(sup) self.assertTrue(3 in om.keys()) self.assertTrue(om.get(3) == 60) self.assertTrue(15 in om.keys()) self.assertTrue(om.get(15) == 2) self.assertTrue(5 in om.keys()) sup = OrderedMap({3: 60, 15: 2}) om.merge(sup) self.assertTrue(3 in om.keys()) self.assertTrue(om.get(3) == 60) self.assertTrue(15 in om.keys()) self.assertTrue(om.get(15) == 2) self.assertTrue(5 in om.keys())
class EventSequence(object): """ A class to collect a sequence of Event's ordered (increasing) by the Event's time value. The class contains the following event accounting structures: 1) OrderedMap: ordering the events by time in a map that provides a floor() function. 2) successor: a dict that maps events to successors. 3) predecessor: a dict that maps events to predecessors. 4) first: first event in the event sequence. 5) last: last event in the event sequence. """ def __init__(self, event_list=None): """ Constructor. Args: event_list: Any of None, a single Event, or a list of Events. """ self.ordered_map = OrderedMap() self._successor = {} self._predecessor = {} self.__first = None self.__last = None if event_list: self.add(event_list) @property def sequence_list(self): return list(self.ordered_map.get(x) for x in self.ordered_map.keys()) @property def is_empty(self): return self.ordered_map.is_empty() def floor(self, time): return self.ordered_map.floor(time) def event(self, index): return self.ordered_map.get(index) def floor_event(self, time): floor_position = self.floor(time) return self.event(floor_position) if floor_position else None @property def first(self): return self.__first @property def last(self): return self.__last def add(self, new_members): """ Add any of a single Event or a list of Events. Args: new_members: Any of a single Event or a list of events """ if isinstance(new_members, list): mem_set = new_members inputt = [(e.time, e) for e in new_members] else: mem_set = [new_members] inputt = [(new_members.time, new_members)] for m in mem_set: if self.ordered_map.has_reverse(m): raise Exception('{0} already a member of sequence.'.format(m)) if not isinstance(m, Event): raise Exception('{0} is not an event.'.format(m)) for i in inputt: if i[1].time not in self.ordered_map: self._add_successor_predecessor_maps(i[1]) else: self._update_successor_predecessor_maps(i[1]) self.ordered_map.insert(i[0], i[1]) def remove(self, members): """ Remove any of a single Event or a list of Events already in the sequence. Args: members: Any of a single Event or a list of Events already in the sequence. """ if isinstance(members, list): for member in members: self.remove(member) else: if not self.ordered_map.has_reverse(members): raise Exception('{0} not a member of sequence'.format(members)) self._remove_successor_predecessor_maps(members) self.ordered_map.remove_key(self.ordered_map.reverse_get(members)) def move_event(self, event, new_time): """ Method to move event in sequence to a new time. Args: event: (Event) to move new_time: the new time setting for the event """ if self.event(event.time) != event: raise Exception('Given event at time {0} not in sequence'.format(event.time)) self.remove(event) event.time = new_time self.add(event) def _add_successor_predecessor_maps(self, event): fl_key = self.floor(event.time) if fl_key: a = self.event(fl_key) b = self._successor[a] # could be None event is between a and b self._successor[a] = event self._successor[event] = b self._predecessor[event] = a if b: self._predecessor[b] = event else: self.__last = event else: # this event has to come first if self.__first: self._successor[event] = self.__first self._predecessor[self.__first] = event self._predecessor[event] = None self.__first = event else: self.__first = self.__last = event self._successor[event] = None self._predecessor[event] = None def _update_successor_predecessor_maps(self, event): e = self.event(event.time) self.remove(e) self._add_successor_predecessor_maps(event) pass def _remove_successor_predecessor_maps(self, event): a = self._predecessor[event] b = self._successor[event] del self._successor[event] del self._predecessor[event] if a: self._successor[a] = b else: self.__first = b if b: self._predecessor[b] = a else: self.__last = a def clear(self): self.ordered_map.clear() self._successor.clear() self._predecessor.clear() def successor(self, event): return self._successor[event] if event in self._successor else None def predecessor(self, event): return self._predecessor[event] if event in self._predecessor else None def __str__(self): return ', '.join(str(x) for x in self.sequence_list) def print_maps(self): print('---------') if self.__first: print('first={0}'.format(self.__first)) else: print('first=None') if self.__first: print('last={0}'.format(self.__last)) else: print('last=None') print('Successor:') for i in self._successor.items(): print(' {0} --> {1}'.format(i[0].object if i[0] else 'None', i[1].object if i[1] else 'None')) print('Predecessor:') for i in self._predecessor.items(): print(' {0} --> {1}'.format(i[0].object if i[0] else 'None', i[1].object if i[1] else 'None'))
class PiecewiseLinearFunction(UnivariateFunction): """ Piecewise linear function, steps defined by a set of transition points, where values between the ordinates of adjacent transitions points, are based on a linear interpolation of the transition points' values. For example, (3, 5), (7, 10), (10, 14), (12, 2) has the following steps: (-3, 5 (3-7, 5), (7-10, 10), (10-12, 14), (12-, 2) if restrict_domain is specified (True), evaluation points must be within domain bounds. """ def __init__(self, transition_points=list(), restrict_domain=False): """ Constructor. Args: transition_points: non-empty list of ordered pairs (x, y), x is the domain, y the range. restrict_domain: boolean indicating if evaluation points must be in defined domain of transition points. default is False. """ if transition_points is None or not isinstance(transition_points, list): assert Exception('Illegal argument to SetwiseLinearFunction {0}'.format(transition_points)) self.__restrict_domain = restrict_domain self._setup(transition_points) def _setup(self, transition_points): self.__transition_points = sorted(transition_points, key=lambda x: x[0]) self.__domain_start = self.__transition_points[0][0] self.__domain_end = self.__transition_points[len(self.__transition_points) - 1][0] lin_segs = [] for i in range(0, len(self.transition_points) - 1): lin_segs.append((self.transition_points[i][0], LinearSegment(self.transition_points[i], self.transition_points[i + 1]))) self.ordered_map = OrderedMap(lin_segs) @property def transition_points(self): return self.__transition_points @property def restrict_domain(self): return self.__restrict_domain @property def domain_start(self): return self.__domain_start @property def domain_end(self): return self.__domain_end def __call__(self, x): return self.eval(x) def eval(self, x): if len(self.transition_points) == 0: raise Exception("The function is undefined due to lack of transition points.") if self.restrict_domain: if x < self.domain_start or x > self.domain_end: raise Exception('Input {0} out of range [{1}, {2}]'.format(x, self.domain_start, self.domain_end)) if x <= self.domain_start: return self.transition_points[0][1] if x >= self.domain_end: return self.transition_points[len(self.transition_points) - 1][1] key = self.ordered_map.floor(x) lin_seg = self.ordered_map.get(key) return lin_seg.eval(x) def add(self, transition_point): """ Add a transition point to the piecewise function. Args: transition_point: Pair (x, y) x, y are numerics. """ new_points = list(self.transition_points) new_points.append(transition_point) self._setup(new_points) def add_and_clear_forward(self, transition_point): """ Add a transition point to the piecewise function AND clear out higher (domain value) transition points. Args: transition_point: Pair (x, y) x, y are numerics. """ new_points = [] elimination_value = transition_point[0] for p in self.transition_points: if p[0] < elimination_value: new_points.append(p) new_points.append(transition_point) self._setup(new_points)
class StepwiseFunction(UnivariateFunction): """ Stepwise function, steps defined by a set of transition points For example, (5, 1), (7, 3), (10, 6), (12, 8) has the following linear segments: (5, 1) to (7, 3) (7, 3) to (10, 6) (10, 6) to 12, 8) if restrict_domain is specified (True), evaluation points must be within domain bounds. """ def __init__(self, transition_points=None, restrict_domain=False): """ Constructor. Args: transition_points: non-empty list of ordered pairs (x, y) restrict_domain: boolean indicating if evaluation points must be in defined domain of transition points. default is False. """ if transition_points is None: transition_points = list() if transition_points is None or not isinstance(transition_points, list): assert Exception( 'Illegal argument to SetwiseLinearFunction {0}'.format( transition_points)) self.__restrict_domain = restrict_domain self._setup(transition_points) def _setup(self, transition_points): self.__transition_points = sorted(transition_points, key=lambda x: x[0]) self.__domain_start = self.__transition_points[0][0] self.__domain_end = self.__transition_points[ len(self.__transition_points) - 1][0] self.ordered_map = OrderedMap(self.transition_points) @property def transition_points(self): return self.__transition_points @property def domain_start(self): return self.__domain_start @property def domain_end(self): return self.__domain_end @property def restrict_domain(self): return self.__restrict_domain def eval(self, x): if len(self.transition_points) == 0: raise Exception( "The function is undefined due to lack of transition points.") if self.restrict_domain: if x < self.domain_start or x > self.domain_end: raise Exception('Input {0} out of range [{1}, {2}]'.format( x, self.domain_start, self.domain_end)) key = self.ordered_map.floor(x) if key is None: return self.ordered_map.get(self.domain_start) if key == self.domain_end: return self.ordered_map.get(self.domain_end) else: return self.ordered_map.get(key) def add(self, transition_point): """ Add a transition point to the stepwise function. Args: transition_point: Pair (x, y) x, y are numerics. """ new_points = list(self.transition_points) new_points.append(transition_point) self._setup(new_points) def add_and_clear_forward(self, transition_point): """ Add a transition point to the stepwise function AND clear out higher (domain value) transition points. Args: transition_point: Pair (x, y) x, y are numerics. """ new_points = [] elimination_value = transition_point[0] for p in self.transition_points: if p[0] < elimination_value: new_points.append(p) new_points.append(transition_point) self._setup(new_points)