Skip to content

Commit

Permalink
events/Envelope.intergrate_interval: ↑ performance x8
Browse files Browse the repository at this point in the history
This patch improves the performance of 'Envelope.intergrate_interval' by
a factor of 8-10 and the performance of the tempo converter by a factor
of 5. By no longer using scipy, but our own integration calculation, we
also no longer need scipy as a dependency, which is another great
improvement.
  • Loading branch information
levinericzimmermann committed Nov 7, 2023
1 parent f42c5fa commit ae8343c
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 5 deletions.
2 changes: 1 addition & 1 deletion mutwo/core_converters/tempos.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def convert(self, event_to_convert: core_events.abc.Event) -> core_events.abc.Ev
>>> my_tempo_converter = core_converters.TempoConverter(tempo_envelope)
>>> my_events = core_events.SequentialEvent([core_events.SimpleEvent(d) for d in (3, 2, 5)])
>>> my_tempo_converter.convert(my_events)
SequentialEvent([SimpleEvent(duration = DirectDuration(duration = 3)), SimpleEvent(duration = DirectDuration(duration = 7205759403792795/2251799813685248)), SimpleEvent(duration = DirectDuration(duration = 6))])
SequentialEvent([SimpleEvent(duration = DirectDuration(duration = 3)), SimpleEvent(duration = DirectDuration(duration = 3602879701896397/1125899906842624)), SimpleEvent(duration = DirectDuration(duration = 6))])
"""
copied_event_to_convert = event_to_convert.destructive_copy()
self._convert_event(copied_event_to_convert, core_parameters.DirectDuration(0))
Expand Down
34 changes: 32 additions & 2 deletions mutwo/core_events/envelopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

import bisect
import functools
import math
import typing

from scipy import integrate
import ranges

from mutwo import core_constants
Expand Down Expand Up @@ -648,7 +648,37 @@ def integrate_interval(
:param end: End of integration interval.
:type end: core_parameters.abc.Duration
"""
return integrate.quad(lambda x: self.value_at(x), start, end)[0]
start, end = (
core_events.configurations.UNKNOWN_OBJECT_TO_DURATION(t)
for t in (start, end)
)
if start == end:
return 0

point_tuple = self.time_range_to_point_tuple(ranges.Range(start, end))
integral = 0
for p0, p1 in zip(point_tuple, point_tuple[1:]):
t0, v0, cs0 = p0 # (absolute_time, value, curve_shape)
t1, v1, _ = p1
if (d0 := float(t1 - t0)) > 0:
if cs0 != 0:
# See https://git.sr.ht/~marcevanstein/expenvelope/tree/cd4a3710/item/expenvelope/envelope_segment.py#L102-103
A = v0 - (v1 - v0) / (math.exp(cs0) - 1)
B = (v1 - v0) / (cs0 * (math.exp(cs0) - 1))

def antiderivative(tn):
# See https://git.sr.ht/~marcevanstein/expenvelope/tree/cd4a3710/item/expenvelope/envelope_segment.py#L239
return A * tn + B * math.exp(cs0 * tn)

a0, a1 = (antiderivative(i) for i in (0, 1))
integral += d0 * (a1 - a0)
else: # linear interpolation
diff = v1 - v0 if v1 > v0 else v0 - v1
square = d0 * min((v0, v1))
triangle = 0.5 * d0 * diff
integral += square + triangle

return float(integral)

def get_average_value(
self,
Expand Down
2 changes: 1 addition & 1 deletion performance_tests/performance_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_SimultaneousEvent_split_at(self):
split_time_list = sorted([random.uniform(0, duration) for _ in range(25)])
e.split_at(*split_time_list)

@t(0.8, 100)
@t(0.17, 100)
def test_metrize(self):
e = sim(
[seq([s(random.uniform(0.9, 1.2)) for _ in range(20)]) for _ in range(3)]
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
setup_requires=[],
install_requires=[
"numpy>=1.18, <2.00",
"scipy>=1.4.1, <2.0.0",
"python-ranges>=1.2.0, <2.0.0",
"quicktions>=1.10, <2.0",
],
Expand Down

0 comments on commit ae8343c

Please sign in to comment.