summaryrefslogtreecommitdiff
path: root/pyspike/function.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyspike/function.py')
-rw-r--r--pyspike/function.py165
1 files changed, 156 insertions, 9 deletions
diff --git a/pyspike/function.py b/pyspike/function.py
index 662606c..e0dadf6 100644
--- a/pyspike/function.py
+++ b/pyspike/function.py
@@ -136,15 +136,6 @@ class PieceWiseConstFunc(object):
a /= int_length
return a
- def avrg_function_value(self):
- """ Computes the average function value of the piece-wise const
- function: :math:`a = 1/N sum_i f_i` where N is the number of intervals.
-
- :returns: the average a.
- :rtype: float
- """
- return sum(self.y)/(len(self.y))
-
def add(self, f):
""" Adds another PieceWiseConst function to this function.
Note: only functions defined on the same interval can be summed.
@@ -364,6 +355,162 @@ that PySpike is installed by running\n 'python setup.py build_ext --inplace'! \
self.y2 *= fac
+##############################################################
+# DiscreteFunction
+##############################################################
+class DiscreteFunction(object):
+ """ A class representing values defined on a discrete set of points.
+ """
+
+ def __init__(self, x, y, multiplicity):
+ """ Constructs the discrete function.
+
+ :param x: array of length N defining the points at which the values are
+ defined.
+ :param y: array of length N degining the values at the points x.
+ :param multiplicity: array of length N defining the multiplicity of the
+ values.
+ """
+ # convert parameters to arrays, also ensures copying
+ self.x = np.array(x)
+ self.y = np.array(y)
+ self.mp = np.array(multiplicity)
+
+ def copy(self):
+ """ Returns a copy of itself
+
+ :rtype: :class:`DiscreteFunction`
+ """
+ return DiscreteFunction(self.x, self.y, self.mp)
+
+ def almost_equal(self, other, decimal=14):
+ """ Checks if the function is equal to another function up to `decimal`
+ precision.
+
+ :param other: another :class:`DiscreteFunction`
+ :returns: True if the two functions are equal up to `decimal` decimals,
+ False otherwise
+ :rtype: bool
+ """
+ eps = 10.0**(-decimal)
+ return np.allclose(self.x, other.x, atol=eps, rtol=0.0) and \
+ np.allclose(self.y, other.y, atol=eps, rtol=0.0) and \
+ np.allclose(self.mp, other.mp, atol=eps, rtol=0.0)
+
+ def get_plottable_data(self, k=0):
+ """ Returns two arrays containing x- and y-coordinates for immeditate
+ plotting of the interval sequence.
+
+ :returns: (x_plot, y_plot) containing plottable data
+ :rtype: pair of np.array
+
+ Example::
+
+ x, y = f.get_plottable_data()
+ plt.plot(x, y, '-o', label="Discrete function")
+ """
+
+ if k > 0:
+ raise NotImplementedError()
+
+ return 1.0*self.x, 1.0*self.y/self.mp
+
+ def integral(self, interval=None):
+ """ Returns the integral over the given interval. For the discrete
+ function, this amounts to the sum over all values divided by the total
+ multiplicity.
+
+ :param interval: integration interval given as a pair of floats, or a
+ sequence of pairs in case of multiple intervals, if
+ None the integral over the whole function is computed.
+ :type interval: Pair, sequence of pairs, or None.
+ :returns: the integral
+ :rtype: float
+ """
+
+ def get_indices(ival):
+ """ Retuns the indeces surrounding the given interval"""
+ start_ind = np.searchsorted(self.x, ival[0], side='right')
+ end_ind = np.searchsorted(self.x, ival[1], side='left')
+ assert start_ind > 0 and end_ind < len(self.x), \
+ "Invalid averaging interval"
+ return start_ind, end_ind
+
+ if interval is None:
+ # no interval given, integrate over the whole spike train
+ # don't count the first value, which is zero by definition
+ return 1.0 * np.sum(self.y[1:-1]) / np.sum(self.mp[1:-1])
+
+ # check if interval is as sequence
+ assert isinstance(interval, collections.Sequence), \
+ "Invalid value for `interval`. None, Sequence or Tuple expected."
+ # check if interval is a sequence of intervals
+ if not isinstance(interval[0], collections.Sequence):
+ # find the indices corresponding to the interval
+ start_ind, end_ind = get_indices(interval)
+ return (np.sum(self.y[start_ind:end_ind]) /
+ np.sum(self.mp[start_ind:end_ind]))
+ else:
+ value = 0.0
+ multiplicity = 0.0
+ for ival in interval:
+ # find the indices corresponding to the interval
+ start_ind, end_ind = get_indices(ival)
+ value += np.sum(self.y[start_ind:end_ind])
+ multiplicity += np.sum(self.mp[start_ind:end_ind])
+ return value/multiplicity
+
+ def avrg(self, interval=None):
+ """ Computes the average of the interval sequence:
+ :math:`a = 1/N sum f_n ` where N is the number of intervals.
+
+ :param interval: averaging interval given as a pair of floats, a
+ sequence of pairs for averaging multiple intervals, or
+ None, if None the average over the whole function is
+ computed.
+ :type interval: Pair, sequence of pairs, or None.
+ :returns: the average a.
+ :rtype: float
+ """
+ return self.integral(interval)
+
+ def add(self, f):
+ """ Adds another `DiscreteFunction` function to this function.
+ Note: only functions defined on the same interval can be summed.
+
+ :param f: :class:`DiscreteFunction` function to be added.
+ :rtype: None
+ """
+ assert self.x[0] == f.x[0], "The functions have different intervals"
+ assert self.x[-1] == f.x[-1], "The functions have different intervals"
+
+ # cython version
+ try:
+ from cython_add import add_discrete_function_cython as \
+ add_discrete_function_impl
+ except ImportError:
+ print("Warning: add_discrete_function_cython not found. Make \
+sure that PySpike is installed by running\n\
+'python setup.py build_ext --inplace'! \
+\n Falling back to slow python backend.")
+ # use python backend
+ from python_backend import add_discrete_function_python as \
+ add_discrete_function_impl
+
+ self.x, self.y, self.mp = \
+ add_discrete_function_impl(self.x, self.y, self.mp,
+ f.x, f.y, f.mp)
+
+ def mul_scalar(self, fac):
+ """ Multiplies the function with a scalar value
+
+ :param fac: Value to multiply
+ :type fac: double
+ :rtype: None
+ """
+ self.y *= fac
+
+
def average_profile(profiles):
""" Computes the average profile from the given ISI- or SPIKE-profiles.