diff --git a/README.md b/README.md index c71f28c4f4960f2d978553ea75db83d72c65acf0..479fe21a33daea70a560597572829a1bad2f52bd 100644 --- a/README.md +++ b/README.md @@ -134,3 +134,8 @@ Or you can check for strict inclusion Range('<-1;1>') in Range('<-2;2>') ``` +## TimeProviders + +**EXPERIMENTAL** + +Can be imported from _sai.timeproviders_. diff --git a/firanka/series.py b/firanka/series.py index c1bb0cc140d1d7c6f4210b38ce31a9e983c32a5f..606a187686a004011b739c4e6e234664f1533e8c 100644 --- a/firanka/series.py +++ b/firanka/series.py @@ -13,6 +13,8 @@ __all__ = [ 'DiscreteSeries', 'ModuloSeries', 'Series', + 'SCALAR_LINEAR_INTERPOLATOR', + 'LinearInterpolationSeries', ] @@ -314,3 +316,44 @@ class ModuloSeries(Series): item = 0 return self.series._get_for(self.series.domain.start + item) + + +def SCALAR_LINEAR_INTERPOLATOR(t0, v0, t1, v1, tt): + """ + Good intepolator if our values can be added, subtracted, multiplied and divided + """ + return v0 + (tt-t0) * (t1-t0)/(v1-v0) + + +class LinearInterpolationSeries(DiscreteSeries): + + def __init__(self, data, domain=None, + interpolator=lambda t0, v0, t1, v1, tt: v0 + , *args, **kwargs): + """ + :param interpolator: callable(t0: float, v0: any, t1: float, v1: any, tt: float) -> any + This, given intepolation points (t0, v0) and (t1, v1) such that t0 <= tt <= t1, + return a value for index tt + """ + self.interpolator = interpolator + if isinstance(data, DiscreteSeries): + self.data = data.data + self.domain = domain or data.domain + else: + super(LinearInterpolationSeries, self).__init__(data, domain, *args, **kwargs) + + def _get_for(self, item): + if item == self.domain.start: + return self.data[0][1] + + if len(self.data) == 1: + return super(LinearInterpolationSeries, self).__getitem__(item) + + for i in six.moves.range(0, len(self.data)-1): + cur_i, cur_v = self.data[i] + next_i, next_v = self.data[i+1] + + if cur_i <= item <= next_i: + return self.interpolator(cur_i, cur_v, next_i, next_v, item) + + return self.data[-1][1] diff --git a/tests/test_series.py b/tests/test_series.py index 8b8a0221fd20a967034bdcc9458c60ad4bd5fe12..c5396638282e8557832ba5d2ab07222363dcf1b0 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -3,7 +3,8 @@ from __future__ import print_function, absolute_import, division import six import math import unittest -from firanka.series import DiscreteSeries, FunctionSeries, ModuloSeries +from firanka.series import DiscreteSeries, FunctionSeries, ModuloSeries, \ + LinearInterpolationSeries, SCALAR_LINEAR_INTERPOLATOR from firanka.ranges import Range from firanka.exceptions import NotInDomainError @@ -174,3 +175,16 @@ class TestModuloSeries(unittest.TestCase): ser2 = FunctionSeries(NOOP, '<0;3)') ser3 = ser1.join(ser2, lambda x, y: x * y) + + +class TestLinearInterpolation(unittest.TestCase): + def test_lin(self): + series = LinearInterpolationSeries( + DiscreteSeries([(0, 1), (1, 2), (2, 3)], '<0;3)'), + None, SCALAR_LINEAR_INTERPOLATOR) + + self.assertEqual(series[0], 1) + self.assertEqual(series[0.5], 1.5) + self.assertEqual(series[1], 2) + self.assertEqual(series[2.3], 3) +