tshort / FunctionalModels.jl

Equation-based modeling and simulations in Julia
https://tshort.github.io/FunctionalModels.jl/stable/
Other
112 stars 19 forks source link

Use of Reactive.jl #17

Closed tshort closed 9 years ago

tshort commented 9 years ago

Our support for Discrete's is currently weak. They don't support any data flow. You cannot easily do the following:

on = Discrete(true)
off = !on

There's no easy way to propagate changes to Discretes. The user has to manually perform updates like this in Events.

Reactive.jl is one option for adding better data flow support. SimJulia is another option to explore.

iraikov commented 9 years ago

How would Inputs from Reactive interact with Unknowns? Could they be treated as a subtype of Unknown? I do agree that it might be useful to distinguish between state variables, parameters, and inputs that come from 'outside' the continuous system.

tshort commented 9 years ago

My first thought is that we would use Reactive as part of Discrete variables. They would work the same but changes in Discrete variables would propagate. I haven't thought through details any more than that,

tshort commented 9 years ago

I haven't tried Reactive, yet, but after fixing a couple of bugs (just pushed), it's possible to use global Arrays as Parameters. Here's an example:

module X

const myglobal = [2.0]

using Sims

function Vanderpol()
    y = Unknown(1.0, "y")
    x = Unknown("x")    
    @equations begin
        der(x, -1.0) = myglobal * (1 - y^2) * x - y 
        der(y) = x
    end
end
m = Vanderpol()
s = create_simstate(create_sim(elaborate(m)))

# First run
v1 = sim(s, 50.0)
@show v1.y[end-5:end,:]

# Change the global
myglobal[1] = 1.0

# Second run
v2 = sim(s, 50.0)
@show v2.y[end-5:end,:]

end

The results are reflected by the different coefficients. The arrays carry through into the residual function.

ViralBShah commented 9 years ago

@shashi - Are the Signals in Reactive.jl useful in this context?

tshort commented 9 years ago

It looks like Reactive.jl can be used for both Parameters and Discretes. We just need to figure out an API. Here are examples:

Use as Parameters

module X
using Sims, Reactive

Sims.value(x::Input) = x.value

function Vanderpol(mu)
    y = Unknown(1.0, "y")
    x = Unknown("x")    
    @equations begin
        der(x, -1.0) = mexpr(:call, :value, mu) * (1 - y^2) * x - y 
        der(y) = x
    end
end

mu = Input(1.0)

m = Vanderpol(mu)
e = elaborate(m)
s = create_simstate(create_sim(elaborate(m)))

v1 = sim(s, 50.0)
@show v1.y[end-5:end,:]

push!(mu, 2.0)
v2 = sim(s, 50.0)
@show v2.y[end-5:end,:]

end

Use as Discretes with data flow

module Y

using Sims, Reactive, Winston

Sims.value(x::Input) = x.value
Sims.value(x::Reactive.Lift) = x.value

function VanderpolWithEvents(mu::Input)
    y = Unknown(1.0, "y")   
    x = Unknown("x")       
    alpha = lift(a -> 0.8 * a, Float64, mu)
    beta = lift(a -> a^2, Float64, alpha)
    mu_u = Unknown(value(mu), "mu_u") 
    alpha_u = Unknown(value(alpha), "alpha_u") 
    beta_u = Unknown(value(beta), "beta_u") 
    @equations begin
        # The -1.0 in der(x, -1.0) is the initial value for the derivative 
        der(x, -1.0) = mexpr(:call, :value, mu) * (1 - y^2) * x - y
        der(y) = x
        mu_u    = mexpr(:call, :value, mu)
        alpha_u = mexpr(:call, :value, alpha)
        beta_u  = mexpr(:call, :value, beta)
        Event(sin(pi/2 * MTime),     # Initiate an event every 2 sec.
              Equation[
                  mexpr(:call, :push!, mu, mexpr(:call, :value, mu) * 0.7)
              ],
              Equation[
                  mexpr(:call, :push!, mu, mexpr(:call, :value, mu) * 1.8)
              ])
    end
end

mu = Input(1.0)
m = VanderpolWithEvents(mu)
e = elaborate(m)
s = create_simstate(create_sim(elaborate(m)))
v1 = sim(s, 50.0)

wplot(v1)

end

It looks like we can rip out our Discrete and Parameter support and replace both with light wrappers around the Reactive types.

tshort commented 9 years ago

I created a reactive branch here: https://github.com/tshort/Sims.jl/tree/reactive

It's got basic support for RDiscretes, a Discrete variable that uses Reactive.jl signals.

Compare Sims.Examples.Basics.VanderpolWithEventsReactive() and Sims.Examples.Basics.VanderpolWithEventsOriginal() here.

push! and reinit are the only methods implemented so far. Initialization and use as Parameters also needs some thinking.

iraikov commented 9 years ago

What is the difference between push! and reinit? It does look from this example that parameters and discrete variables could be replaced with reactive signals.

shashi commented 9 years ago

Sims.jl is very cool!

Signals seem to be a good way to represent time-varying discrete values. However it is more useful to keep Reactive and other libraries that use signals completely orthogonal. I would suggest implementing the simulation methods for Signal{Equation} and Signal{Vector{Equation}} values instead of something that uses signal.value as in VanderpolWithEventsReactive. For example, this will allow a user to interact with a running simulation using @manipulate. Right now Reactive forbids calling push! from inside a call to another push! - this means code that uses push! cannot be put inside an @manipulate expression.

One more thing to note is that timing functions in Reactive like fps / every actually relate to real-world time, which may not always be the case when you want to run Sims.

tshort commented 9 years ago

@shashi, thanks for the input, and thanks for writing Reactive!

I'm new to data flow programming, and I'm trying to figure out what the issue is with the way I've implemented it in the first cut. I looked at the Interact code to see how it was different, but I can't figure it out. Interact widgets have a signal and they use push!. Also, I don't think a Signal{Equation} does what we want. If I'm interpretting that, if I change an input, it flows and auto updates an Equation. But, during simulations, Equations have already been compiled into a residual function. I want signals that live inside the residual function.

@iraikov, push! and reinit serve the same purpose. reinit can update Unknowns, so I thought that for now we can leave them separate and use one for Unknowns and the other for updating Discretes.

tshort commented 9 years ago

Still looking good. I'll probably plow ahead with changes and rip out the old Discretes and Parameters.

shashi commented 9 years ago

@tshort Nice! #24 looks good! :)

The problem I was talking about is that Reactive cannot allow you to call push! inside another call to push! (this is to avoid infinite loops in a signal update A updating B which updates A and so on...). Since Interact is using push! to update signal of UI values you cannot use Sims to create interactive output. But I think this is a problem Reactive should address! I will prototype something that lets you have multiple signal graphs soon.

tshort commented 9 years ago

Overall, things came together much faster than expected. More features, more flexibility, and less code! Thanks @shashi.

I'll merge this sometime this week.

tshort commented 9 years ago

Features added in #31.