Active object

From Rosetta Code
Task
Active object
You are encouraged to solve this task according to the task description, using any language you may know.

In object-oriented programming an object is active when its state depends on clock. Usually an active object encapsulates a task that updates the object's state. To the outer world the object looks like a normal object with methods that can be called from outside. Implementation of such methods must have a certain synchronization mechanism with the encapsulated task in order to prevent object's state corruption.

A typical instance of an active object is an animation widget. The widget state changes with the time, while as an object it has all properties of a normal widget.

The task

Implement an active integrator object. The object has an input and output. The input can be set using the method Input. The input is a function of time. The output can be queried using the method Output. The object integrates its input over the time and the result becomes the object's output. So if the input is K(t) and the output is S, the object state S is changed to S + (K(t1) + K(t0)) * (t1 - t0) / 2, i.e. it integrates K using the trapeze method. Initially K is constant 0 and S is 0.

In order to test the object:

  1. set its input to sin (2π f t), where the frequency f=0.5Hz. The phase is irrelevant.
  2. wait 2s
  3. set the input to constant 0
  4. wait 0.5s

Verify that now the object's output is approximately 0 (the sine has the period of 2s). The accuracy of the result will depend on the OS scheduler time slicing and the accuracy of the clock.

Ada

<lang ada> with Ada.Calendar; use Ada.Calendar; with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; with Ada.Text_IO; use Ada.Text_IO;

procedure Test_Integrator is

  type Func is access function (T : Time) return Float;
  function Zero (T : Time) return Float is
  begin
     return 0.0;
  end Zero;
  Epoch : constant Time := Clock;
  function Sine (T : Time) return Float is
  begin
     return Sin (Pi * Float (T - Epoch));
  end Sine;
  task type Integrator is
     entry Input  (Value : Func);
     entry Output (Value : out Float);
     entry Shut_Down;
  end Integrator;

  task body Integrator is
     K  : Func  := Zero'Access;
     S  : Float := 0.0;
     F0 : Float := 0.0;
     F1 : Float;
     T0 : Time  := Clock;
     T1 : Time;
  begin
     loop
        select
           accept Input (Value : Func) do
              K := Value;
           end Input;
        or accept Output (Value : out Float) do
              Value := S;
           end Output;
        or accept Shut_Down;
           exit;
        else
           T1 := Clock;
           F1 := K (T1);
           S  := S + 0.5 * (F1 + F0) * Float (T1 - T0);
           T0 := T1;
           F0 := F1;
        end select;
     end loop;
  end Integrator;

  I : Integrator;
  S : Float;

begin

  I.Input (Sine'Access);
  delay 2.0;
  I.Input (Zero'Access);
  delay 0.5;
  I.Output (S);
  Put_Line ("Integrated" & Float'Image (S) & "s");
  I.Shut_Down;

end Test_Integrator; </lang> Sample output:

Integrated-5.34100E-05s

C

Works with: POSIX

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <unistd.h>
  3. include <math.h>
  4. include <pthread.h>

double nullfunc(double t) {

 return 0.0;

}

double func1(double t) {

  return sin(2.0*M_PI*0.5*t);

}

typedef double (*objfunc)(double); struct objhandle_s {

   int terminate;
   pthread_mutex_t access_S;
   pthread_mutex_t access_I;
   pthread_mutex_t access_t;
   objfunc I;
   pthread_t th;
   double S;
   double t;
   double freq;

}; typedef struct objhandle_s objhandle_t; typedef struct objhandle_s *objhandle;

void *the_integrator(void *n) {

   objhandle oh = (objhandle)n;
   double ts = 1.0/oh->freq;
   while(oh->terminate == 0)
   {
     usleep((useconds_t)(ts*1000000.0));
     pthread_mutex_lock(&oh->access_S);
     pthread_mutex_lock(&oh->access_t);
     pthread_mutex_lock(&oh->access_I);
     oh->S += ( oh->I(oh->t+ts) - oh->I(oh->t) )*ts/2.0;
     oh->t += ts;
     pthread_mutex_unlock(&oh->access_I);
     pthread_mutex_unlock(&oh->access_t);
     pthread_mutex_unlock(&oh->access_S);
   }

}

void destroy_integrator(objhandle integrator) {

  integrator->terminate = 1;
  pthread_join(integrator->th, NULL);
  free(integrator);

}

double integrator_getsum(objhandle integrator) {

  return integrator->S;

}

void integrator_setinput(objhandle integrator, objfunc nif) {

  pthread_mutex_lock(&integrator->access_I);
  integrator->I = nif;
  pthread_mutex_unlock(&integrator->access_I);

}

objhandle create_integrator(double freq) {

   objhandle r;
   
   r = malloc(sizeof(objhandle_t));
   if ( r != NULL )
   {
      pthread_mutex_init(&r->access_S, NULL);
      pthread_mutex_init(&r->access_I, NULL);
      pthread_mutex_init(&r->access_t, NULL);
      r->terminate = 0;
      r->t = 0.0;
      r->S = 0.0;
      r->I = nullfunc;
      r->freq = freq;
      pthread_create(&r->th, NULL, the_integrator, (void *)r);
   }
   return r;

}


int main() {

  objhandle integrator;
  double sint;
  
  integrator = create_integrator(10.0);
  integrator_setinput(integrator, func1);
  sleep(2);
  integrator_setinput(integrator, nullfunc);
  usleep(500000);
  sint = integrator_getsum(integrator);
  printf("S = %lf\n", sint);
  destroy_integrator(integrator);
  return 0;

}</lang>

Mutex for S and t (time) exists in case one wants to implement functions to reset/set S (sum) and time (t) i.e. to reset or set the integrator to a particular state.

Output:

S = -0.015451

By increasing the precision of the integrator by increasing the "sampling frequency" you can take a better result; e.g. with create_integrator(100.0) I've obtained S = -0.000157.

E

def makeIntegrator() {
    var value := 0.0
    var input := fn { 0.0 }
    
    var input1 := input()
    var t1 := timer.now()
    
    def update() {
        def t2 := timer.now()
        def input2 :float64 := input()
        def dt := (t2 - t1) / 1000
        
        value += (input1 + input2) * dt / 2
        
        t1 := t2
        input1 := input2
    }
    
    var task() {
        update <- ()
        task <- ()
    }
    task()
    
    def integrator {
        to input(new) :void  { input := new }
        to output() :float64 { return value }
        to shutdown()        { task := fn {} }
    }
    return integrator
}
def test() {
    def result
    
    def pi := (-1.0).acos()
    def freq := pi / 1000
    
    def base := timer.now()
    def i := makeIntegrator()
    
    i.input(fn { (freq * timer.now()).sin() })
    timer.whenPast(base + 2000, fn {
        i.input(fn {0})
    })
    timer.whenPast(base + 2500, fn {
        bind result := i.output()
        i.shutdown()
    })
    return result
}

Haskell

<lang haskell>module Integrator (

 newIntegrator, input, output, stop,
 Time, timeInterval

) where import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent.MVar (MVar, newMVar, modifyMVar_, modifyMVar, readMVar) import Control.Exception (evaluate) import Data.Time (UTCTime) import Data.Time.Clock (getCurrentTime, diffUTCTime)

-- RC task main = do let f = 0.5 {- Hz -}

         t0 <- getCurrentTime
         i <- newIntegrator
         input i (\t -> sin(2*pi * f * timeInterval t0 t)) -- task step 1
         threadDelay 2000000 {- µs -}                      -- task step 2
         input i (const 0)                                 -- task step 3
         threadDelay 500000 {- µs -}                       -- task step 4
         result <- output i
         stop i
         print result

Implementation ------------------------------------------------------

-- Utilities for working with the time type type Time = UTCTime type Func a = Time -> a timeInterval t0 t1 = fromRational $ toRational $ diffUTCTime t1 t0

-- Type signatures of the module's interface newIntegrator :: Fractional a => IO (Integrator a) -- Create an integrator input  :: Integrator a -> Func a -> IO () -- Set the input function output :: Integrator a -> IO a -- Get the current value stop  :: Integrator a -> IO () -- Stop integration, don't waste CPU

-- Data structures data Integrator a = Integrator (MVar (IntState a)) -- MVar is a thread-safe mutable cell

 deriving Eq

data IntState a = IntState { func  :: Func a, -- The current function

                            run   :: Bool,        -- Whether to keep going
                            value :: a,           -- The current accumulated value
                            time  :: Time }       -- The time of the previous update

newIntegrator = do

 now <- getCurrentTime
 state <- newMVar $ IntState { func  = const 0,
                               run   = True,
                               value = 0,
                               time  = now }
 thread <- forkIO (intThread state)  -- The state variable is shared between the thread
 return (Integrator state)           --   and the client interface object.     
                  

input (Integrator stv) f = modifyMVar_ stv (\st -> return st { func = f }) output (Integrator stv) = fmap value $ readMVar stv stop (Integrator stv) = modifyMVar_ stv (\st -> return st { run = False })

 -- modifyMVar_ takes an MVar and replaces its contents according to the provided function.
 -- a { b = c } is record-update syntax: "the record a, except with field b changed to c"

-- Integration thread intThread :: Fractional a => MVar (IntState a) -> IO () intThread stv = whileM $ modifyMVar stv updateAndCheckRun

 -- modifyMVar is like modifyMVar_ but the function returns a tuple of the new value
 -- and an arbitrary extra value, which in this case ends up telling whileM whether
 -- to keep looping.
 where updateAndCheckRun st = do
         now <- getCurrentTime
         let value' = integrate (func st) (value st) (time st) now
         evaluate value'                             -- avoid undesired laziness
         return (st { value = value', time  = now }, -- updated state
                 run st)                             -- whether to continue

integrate :: Fractional a => Func a -> a -> Time -> Time -> a integrate f value t0 t1 = value + (f t0 + f t1)/2 * dt

 where dt = timeInterval t0 t1

-- Execute 'action' until it returns false. whileM action = do b <- action; if b then whileM action else return ()</lang>

JavaScript

Translation of: E

<lang javascript>function Integrator(sampleIntervalMS) {

   var inputF = function () { return 0.0 };
   var sum = 0.0;
 
   var t1 = new Date().getTime();
   var input1 = inputF(t1 / 1000);
 
   function update() {
       var t2 = new Date().getTime();
       var input2 = inputF(t2 / 1000);
       var dt = (t2 - t1) / 1000;
       
       sum += (input1 + input2) * dt / 2;
       
       t1 = t2;
       input1 = input2;
   }
   
   var updater = setInterval(update, sampleIntervalMS);
 
   return ({
       input: function (newF) { inputF = newF },
       output: function () { return sum },
       shutdown: function () { clearInterval(updater) },
   });

}</lang>

Test program as a HTML fragment:

<lang html>

Test running... -

<script type="text/javascript">

   var f = 0.5;
   var i = new Integrator(1);
   var displayer = setInterval(function () { document.getElementById("b").firstChild.data = i.output() }, 100)
 
   setTimeout(function () {
       i.input(function (t) { return Math.sin(2*Math.PI*f*t) }); // test step 1
       setTimeout(function () { // test step 2
           i.input(function (t) { return 0 }); // test step 3
           setTimeout(function () { // test step 3
               i.shutdown();
               clearInterval(displayer);
               document.getElementById("a").firstChild.data = "Done, should be about 0: "
           }, 500);      
       }, 2000);
   }, 1)

</script></lang>

Python

<lang python> from time import time, sleep

class ActiveIntegrator():

   def __init__(self, interval=1e-5):
       self.interval  = interval
       self.t0        = time() # seconds
       self.inputK    = lambda t: 0.0
       self.k0        = 0.0
       self.outputS   = 0.0
       
   def inp(self, K=lambda t: 0.0):
       t1            = time()
       interval = self.interval
       tp0, tp1 = self.t0, self.t0+interval
       kp0 = self.k0
       Kp0 = self.inputK
       S   = self.outputS
       while tp1 < t1:
           kp1 = K(tp1)
           S  += (kp0 + kp1)*(tp1 - tp0)/2.0
           kp0 = kp1
           tp0, tp1 = tp1, tp1+interval
       self.outputS  = S
       self.t0       = t1
       self.inputK   = K
       self.k0       = self.inputK(t1)
       
   def outp(self):
       self.inp(self.inputK)
       return self.outputS

if __name__ == '__main__':

   from math import sin, pi
   
   ai = ActiveIntegrator()
   f = 0.5
   ai.inp(lambda t: sin(2*pi*f*t))
   sleep(2)
   ai.inp()
   print (ai.outp())
   sleep(0.5)
   print (ai.outp())</lang>

Tcl

Works with: Tcl version 8.6
Works with: Tcl version 8.5

and the TclOO package

This implementation Tcl 8.6 for object support (for the active integrator object) and coroutine support (for the controller task). It could be rewritten to only use 8.5 plus the TclOO library. <lang Tcl>package require Tcl 8.6 oo::class create integrator {

   variable e sum delay tBase t0 k0 aid
   constructor Template:Interval 1 {

set delay $interval set tBase [clock microseconds] set t0 0 set e { 0.0 } set k0 0.0 set sum 0.0 set aid [after $delay [namespace code {my Step}]]

   }
   destructor {

after cancel $aid

   }
   method input expression {

set e $expression

   }
   method output {} {

return $sum

   }
   method Eval t {

expr $e

   }
   method Step {} {

set aid [after $delay [namespace code {my Step}]] set t [expr {([clock microseconds] - $tBase) / 1e6}] set k1 [my Eval $t] set sum [expr {$sum + ($k1 + $k0) * ($t - $t0) / 2.}] set t0 $t set k0 $k1

   }

}

set pi 3.14159265 proc pause {time} {

   yield [after [expr {int($time * 1000)}] [info coroutine]]

} proc task {script} {

   coroutine task_ apply [list {} "$script;set ::done ok"]
   vwait done

} task {

   integrator create i
   i input {sin(2*$::pi * 0.5 * $t)}
   pause 2
   i input { 0.0 }
   pause 0.5
   puts [format %.15f [i output]]

}</lang> Sample output:

-0.000000168952702

Visual Basic .NET

Since this object is CPU intensive, shutting it down when done is crucial. To facilitate this, the IDisposable pattern was used.

<lang vbnet>Module Module1

   Sub Main()
       Using active As New Integrator
           active.Operation = Function(t As Double) Math.Sin(2 * Math.PI * 0.5 * t)
           Threading.Thread.Sleep(TimeSpan.FromSeconds(2))
           Console.WriteLine(active.Value)
           active.Operation = Function(t As Double) 0
           Threading.Thread.Sleep(TimeSpan.FromSeconds(0.5))
           Console.WriteLine(active.Value)
       End Using
       Console.ReadLine()
   End Sub

End Module

Class Integrator

   Implements IDisposable

   Private m_Operation As Func(Of Double, Double)
   Private m_Disposed As Boolean
   Private m_SyncRoot As New Object
   Private m_Value As Double

   Public Sub New()
       m_Operation = Function(void) 0.0
       Dim t As New Threading.Thread(AddressOf MainLoop)
       t.Start()
   End Sub

   Private Sub MainLoop()
       Dim epoch = Now
       Dim t0 = 0.0
       Do
           SyncLock m_SyncRoot
               Dim t1 = (Now - epoch).TotalSeconds
               m_Value = m_Value + (Operation(t1) + Operation(t0)) * (t1 - t0) / 2
               t0 = t1
           End SyncLock
           Threading.Thread.Sleep(10)
       Loop Until m_Disposed
   End Sub

   Public Property Operation() As Func(Of Double, Double)
       Get
           SyncLock m_SyncRoot
               Return m_Operation
           End SyncLock
       End Get
       Set(ByVal value As Func(Of Double, Double))
           SyncLock m_SyncRoot
               m_Operation = value
           End SyncLock
       End Set
   End Property

   Public ReadOnly Property Value() As Double
       Get
           SyncLock m_SyncRoot
               Return m_Value
           End SyncLock
       End Get
   End Property

   Protected Overridable Sub Dispose(ByVal disposing As Boolean)
       m_Disposed = True
   End Sub

   Public Sub Dispose() Implements IDisposable.Dispose
       Dispose(True)
       GC.SuppressFinalize(Me)
   End Sub

End Class</lang>


Output: 0.000241446762282308