"""Algorithms which are related to German-Dutch composer G. M. Koenig."""importnumpyasnp# type: ignoreimportranges# type: ignorefrommutwoimportcommon_utilitiesfrommutwoimportcore_events__all__=("Tendency",)
[docs]classTendency(object):"""Tendency offers an interface for dynamically changing minima / maxima areas. :param minima_curve: The curve which describes the smallest allowed value over the time axis. :type minima_curve: core_events.Envelope :param maxima_curve: The curve which describes the biggest allowed value over the time axis. :type maxima_curve: core_events.Envelope :param random_seed: The random seed which shall be set. :type random_seed: int The class is based on Gottfried Michael Koenigs algorithm of "Tendenz-Masken" in his program "Projekt 2" where those minima / maxima areas represent probability fields. **Example:** >>> import core_events >>> from mutwo.generators import koenig >>> minima_curve = core_events.Envelope.from_points((0, 0), (1, 1), (2, 0)) >>> maxima_curve = core_events.Envelope.from_points((0, 1), (1, 2), (2, 3)) >>> my_tendency = koenig.Tendency(minima_curve, maxima_curve) >>> my_tendency.value_at(0.5) 0.6456692551041303 >>> my_tendency.value_at(0.5) 0.9549270045140213 """def__init__(self,minima_curve:core_events.Envelope,maxima_curve:core_events.Envelope,random_seed:int=100,):self._assert_curves_are_valid(minima_curve,maxima_curve)self._minima_curve=minima_curveself._maxima_curve=maxima_curveself._random=np.random.default_rng(random_seed)def__repr__(self)->str:returnf"{type(self).__name__}({self.minima_curve}, {self.maxima_curve})"@staticmethoddef_assert_curves_are_valid(minima_curve:core_events.Envelope,maxima_curve:core_events.Envelope):"""Helper method that asserts the curves are a valid min/max pair."""# make sure both curves have equal durationifminima_curve.duration!=maxima_curve.duration:raisecommon_utilities.UnequalEnvelopeDurationError(minima_curve,maxima_curve)# It would be better if we could compare all local extrema.# But there is no public function in core_events.Envelope yet.# Even if there would be a function to find local extrema it# is uncertain if this would be faster.# We want to make sure that at any time point minima_curve# < maxima_curve.point_to_compare_tuple=(minima_curve.absolute_time_tuple+maxima_curve.absolute_time_tuple+(0,minima_curve.duration))fortimeinpoint_to_compare_tuple:ifnotminima_curve.value_at(time)<maxima_curve.value_at(time):raisecommon_utilities.InvalidMinimaCurveAndMaximaCurveCombination(f"At time '{time}' 'minima_curve' isn't smaller ""than 'maxima_curve'!")@propertydefminima_curve(self)->core_events.Envelope:returnself._minima_curve@minima_curve.setterdefminima_curve(self,minima_curve:core_events.Envelope)->core_events.Envelope:self._assert_curves_are_valid(minima_curve,self.maxima_curve)self._minima_curve=minima_curve@propertydefmaxima_curve(self)->core_events.Envelope:returnself._maxima_curve@maxima_curve.setterdefmaxima_curve(self,maxima_curve:core_events.Envelope)->core_events.Envelope:self._assert_curves_are_valid(self.minima_curve,maxima_curve)self._maxima_curve=maxima_curve
[docs]defrange_at(self,time:float)->ranges.Range:"""Get minima / maxima range at requested time."""returnranges.Range(self.minima_curve.value_at(time),self.maxima_curve.value_at(time))
[docs]defvalue_at(self,time:float)->float:"""Get value at requested time."""range_at_time=self.range_at(time)returnself._random.uniform(range_at_time.start,range_at_time.end)