понедельник, 12 марта 2012 г.

Real time (stream) integration with Simpson's rule

Turn on MathML plugin
About this method of numerical integration you can read here. To do it in streaming manner we should do little formal mathematics:

Composite Simpson's rule:

a b f x x h 3 k = 1 , 2 N - 1 f x k - 1 + 4 f x k + f x k + 1

where k=1,2 means from 1 step by 2, N is even intervals number.

The sum will be: h 3 k = 1 , 2 N - 1 S k

Simpsone's method requires 3 points least so we can integrate not every point (sample) but through one, so neighboring samples are IN-2 and IN. Different between them is:

I N - I N - 2 = h 3 k = 1 , 2 N - 1 S k - k = 1 , 2 N - 3 S k = h 3 S N - 1 + k = 1 , 2 N - 3 S k - k = 1 , 2 N - 3 S k

no SN-2 member bcz of step k=2.

So difference is: h 3 S N - 1

For example, I 4 - I 2 = h 3 y 2 + 4 y 3 + y 4 , where I4 is 4th interval but 5th sample.

And resulting formula of Ii=
0 , i 1
h 3 y i - 2 + 4 y i - 1 + y i + I i - 1 , i mod 2 = 0
I i - 1 , i mod 2 0

where i is a number of input sample. We can not get values of I on each sample, but through one (skipping odds and 0-th, 1-th). On odds samples, value will repeat previous one.

Example in SWIG:
some.h file:
// Simpsone's rule integrator
typedef struct CSIntr {
        double I; // last value of integral
        double y_1; // y[-1]
        double y_2; // y[-2]
        double h; // step of subinterval
        double h_d; // step of subinterval/3
        long long i; // iteration
} CSIntr;
some.i file:
%include "some.h"
%extend CSIntr {
    // FIXME how is more right to do this?
    void reset() {
        $self->I = 0.;
        $self->y_1 = $self->y_2 = 0.;
        $self->i = 0;
    }

    void setup(double h) {
        $self->h = h;
        $self->h_d = h/3.;
    }

    CSIntr(double h) {
        CSIntr *obj;

        if (NULL == (obj=(CSIntr*) malloc(sizeof (CSIntr))))
            return (NULL);
        CSIntr_reset(obj);
        CSIntr_setup(obj, h);
        return (obj);
    }

    ~CSIntr() {
        if ($self) free($self);
    }

    double calculate(double y) {
        if ($self->i <= 1)
            $self->I = 0;
        else if (0 == $self->i % 2)
            $self->I += $self->h_d * ($self->y_2 + 4. * $self->y_1 + y);

        $self->y_2 = $self->y_1;
        $self->y_1 = y;
        $self->i++;
        return ($self->I);
    }
};
So, if we use Tcl, then we can build and test our class:
Makefile's fragment:
some_wrap.c: some.i some.h some.c
 swig -tcl -pkgversion 1.0 some.i

some.dll: some_wrap.c
 gcc -Ic:/tcl/include -I./ -DUSE_TCL_STUBS -c some_wrap.c -o some_wrap.o
 gcc -shared -o some.dll some/some_wrap.o -L c:/tcl/lib -ltclstub85 -lm
test on linear function with yi = {1, 2, 3, 4, 5}:
load some
% CSIntr cs 1
_f85f7601_p_CSIntr
% cs calculate 1
0.0
% cs calculate 2
0.0
% cs calculate 3
4.0
% cs calculate 4
4.0
% cs calculate 5
12.0
I4 is really 12.0

Комментариев нет:

Отправить комментарий

Thanks for your posting!