feat (state): cleanup simulation state interface

This commit is contained in:
Henri Saudubray 2025-04-23 10:47:46 +02:00
parent 200152b769
commit cc099c02e7
Signed by: hms
GPG key ID: 7065F57ED8856128
3 changed files with 118 additions and 128 deletions

View file

@ -3,7 +3,7 @@ opam-version: "2.0"
synopsis: "An executable semantics for the simulation of hybrid systems"
maintainer: ["henri.saudubray@proton.me"]
authors: ["Henri Saudubray"]
license: "GPL-3.0-or-later"
license: "CeCILL-2.1-or-later"
homepage: "https://codeberg.org/17maiga/hsim"
doc: "https://url/to/documentation"
bug-reports: "https://codeberg.org/17maiga/hsim/issues"

View file

@ -26,27 +26,25 @@ module LazySim (S : SimState) =
(HNode model : ('p, 'a, 'b, 'y, 'yder, 'zin, 'zout) hnode)
(DNode solver : ('y, 'yder, 'zin, 'zout) solver)
: ('p, 'a, 'b) lazy_sim
= let state = S.init ~mstate:model.state ~sstate:solver.state in
= let state = S.get_init model.state solver.state in
let step state input =
let mstate = S.mstate state and sstate = S.sstate state
and status = S.status state in
let mstate = S.get_mstate state and sstate = S.get_sstate state in
match input, S.is_running state with
| Some input, _ ->
let mode = Discrete and now = 0.0 and stop = input.length in
let status = S.running ~mode ~input ~now ~stop status in
let state = S.update ~status state in
let state = S.set_running ~mode ~input ~now ~stop state in
None, state
| None, false -> None, state
| None, true ->
let input = S.input state and now = S.now state
and stop = S.stop state in
match S.mode state with
let input = S.get_input state and now = S.get_now state
and stop = S.get_stop state in
match S.get_mode state with
| Discrete ->
let o, mstate = model.step mstate (input.u now) in
let state =
let h = model.horizon mstate in
if h <= 0.0 then S.update ~mstate state
if h <= 0.0 then S.set_mstate state mstate
else if now >= stop then
(* TODO: Build an initial state with the initial states for
both the solver and model - an equivalent of [s] in
@ -59,11 +57,9 @@ module LazySim (S : SimState) =
let ivp = { fder; stop = stop -. now; init = y } in
let zc = { yc = y; fzer } in
let sstate = solver.reset (ivp, zc) sstate in
let status = S.running ~mode:Continuous status in
S.update ~status ~mstate ~sstate state
else
let status = S.running ~mode:Continuous status in
S.update ~status state in
let state = S.set_running ~mode:Continuous state in
S.update state mstate sstate
else S.set_running ~mode:Continuous state in
let start = input.start +. now in
Some { start; length = 0.0; u = fun _ -> o }, state
| Continuous ->
@ -76,28 +72,27 @@ module LazySim (S : SimState) =
{ start = input.start +. now; length = h -. now; u = fout } in
let state = match z with
| None ->
let status =
if h >= stop then S.running ~mode:Discrete ~now:h' status
else S.running ~now:h' status in
S.update ~status ~mstate ~sstate state
let state =
if h >= stop then
S.set_running ~mode:Discrete ~now:h' state
else S.set_running ~now:h' state in
S.update state mstate sstate
| Some z ->
let status = S.running ~mode:Discrete ~now:h' status in
let mstate = model.zset mstate z in
S.update ~status ~mstate ~sstate state in
let state = S.set_running ~mode:Discrete ~now:h' state in
S.update state (model.zset mstate z) sstate in
Some out, state in
let reset p s =
(* TODO: does [model.cget mstate] make sense before the first discrete
step - can we use [mstate] to reinitialize [sstate] ? *)
let mstate = model.reset p (S.mstate s) in
let mstate = model.reset p (S.get_mstate s) in
let y = model.cget mstate in
(* TODO: what initial stop time do we use ? *)
let stop = raise Common.Utils.TODO in
let ivp = { fder = model.fder mstate; stop; init = y } in
let zc = { fzer = model.fzer mstate; yc = y } in
let sstate = solver.reset (ivp, zc) (S.sstate s) in
let status = S.idle (S.status s) in
S.update ~status ~mstate ~sstate s in
let sstate = solver.reset (ivp, zc) (S.get_sstate s) in
S.update s mstate sstate in
DNode { state; step; reset }
@ -113,40 +108,37 @@ module GreedySim (S : SimState) =
(HNode model : ('p, 'a, 'b, 'y, 'yder, 'zin, 'zout) hnode)
(DNode solver : ('y, 'yder, 'zin, 'zout) solver)
: ('p, 'a, 'b) greedy_sim
= let state = S.init ~mstate:model.state ~sstate:solver.state in
= let state = S.get_init model.state solver.state in
let rec step state input =
let status = S.status state and mstate = S.mstate state
and sstate = S.sstate state in
let mstate = S.get_mstate state and sstate = S.get_sstate state in
if not (S.is_running state) then
let mode = Discrete and now = 0.0 and stop = input.length in
let status = S.running ~mode ~input ~now ~stop (S.status state) in
let state = S.update ~status state in
let state = S.set_running ~mode ~input ~now ~stop state in
step state input
else let now = S.now state and stop = S.stop state in
match S.mode state with
else let now = S.get_now state and stop = S.get_stop state in
match S.get_mode state with
| Discrete ->
let o, mstate = model.step mstate (input.u now) in
let h = model.horizon mstate in
let rest, state =
if h <= 0.0 then step (S.update ~mstate state) input
if h <= 0.0 then step (S.set_mstate state mstate) input
else if now >= stop then [], state
else if model.jump mstate then
let y = model.cget mstate in
(* FIXME: copy the state here *)
let fder t = model.fder mstate (offset input now t) in
let fzer t = model.fzer mstate (offset input now t) in
let ivp = { fder; stop = stop -. now; init = y } in
let zc = { yc = y; fzer } in
let sstate = solver.reset (ivp, zc) sstate in
let status = S.running ~mode:Continuous status in
step (S.update ~status ~mstate ~sstate state) input
else
let status = S.running ~mode:Continuous status in
step (S.update ~status state) input in
let state = S.set_running ~mode:Continuous state in
step (S.update state mstate sstate) input
else step (S.set_running ~mode:Continuous state) input in
let start = input.start +. now in
{ start; length = 0.0; u = fun _ -> o }::rest, state
| Continuous ->
let (h, f, z), _sstate = solver.step sstate stop in
let (h, f, z), sstate = solver.step sstate stop in
let mstate = model.cset mstate (f h) in
let h' = input.start +. h in
let fout t =
@ -156,22 +148,19 @@ module GreedySim (S : SimState) =
match z with
| None ->
if h >= stop then
let status = S.running ~mode:Discrete ~now:h' status in
let rest, state =
step (S.update ~status ~mstate ~sstate state) input in
let state = S.set_running ~mode:Discrete ~now:h' state in
let rest, state = step (S.update state mstate sstate) input in
out::rest, state
else
let status = S.running ~now:h' status in
let rest, state =
step (S.update ~status ~mstate ~sstate state) input in
let state = S.set_running ~now:h' state in
let rest, state = step (S.update state mstate sstate) input in
(match rest with
| [] -> [out], state
| f::rest -> compose [out;f] :: rest, state)
| Some z ->
let status = S.running ~mode:Discrete ~now:h' status in
let state = S.set_running ~mode:Discrete ~now:h' state in
let mstate = model.zset mstate z in
let rest, state =
step (S.update ~status ~mstate ~sstate state) input in
let rest, state = step (S.update state mstate sstate) input in
out::rest, state in
let reset = assert false in

View file

@ -8,58 +8,59 @@ type mode = Discrete | Continuous
module type SimState =
sig
(** Simulation status:
- Idle: waiting for input
- Running: currently integrating; in this case, we have access to the
step mode, current input, timestamp and stop time. *)
type 'a status
(** Internal state of the simulation. This contains the state of the model
and solver, as well as the current simulation status. *)
and solver, as well as the current simulation status.
The simulation status should take two forms:
- Idle: waiting for input;
- Running: currently integrating; in this case, we have access to the
step mode, current input, timestamp and stop time. *)
type ('a, 'ms, 'ss) state
(** Get the current simulation status. *)
val status : ('a, 'ms, 'ss) state -> 'a status
(** Get the model state. *)
val mstate : ('a, 'ms, 'ss) state -> 'ms
val get_mstate : ('a, 'ms, 'ss) state -> 'ms
(** Get the solver state. *)
val sstate : ('a, 'ms, 'ss) state -> 'ss
val get_sstate : ('a, 'ms, 'ss) state -> 'ss
(** Get the current step mode.
Should only be called when running (see [is_running]). *)
val get_mode : ('a, 'ms, 'ss) state -> mode
(** Get the current input.
Should only be called when running (see [is_running]). *)
val get_input : ('a, 'ms, 'ss) state -> 'a value
(** Get the current timestamp.
Should only be called when running (see [is_running]). *)
val get_now : ('a, 'ms, 'ss) state -> time
(** Get the current stop time.
Should only be called when running (see [is_running]). *)
val get_stop : ('a, 'ms, 'ss) state -> time
(** Build an initial state. *)
val get_init : 'ms -> 'ss -> ('a, 'ms, 'ss) state
(** Is the simulation running or idle ? *)
val is_running : ('a, 'ms, 'ss) state -> bool
(** Update the state. *)
val update : ?status:'a status -> ?mstate:'ms -> ?sstate:'ss ->
(** Update the model state. *)
val set_mstate : ('a, 'ms, 'ss) state -> 'ms -> ('a, 'ms, 'ss) state
(** Update the solver state. *)
val set_sstate : ('a, 'ms, 'ss) state -> 'ss -> ('a, 'ms, 'ss) state
(** Update both the solver and model states. *)
val update : ('a, 'ms, 'ss) state -> 'ms -> 'ss -> ('a, 'ms, 'ss) state
(** Update the status to running. *)
val set_running :
?mode:mode -> ?input:'a value -> ?now:time -> ?stop:time ->
('a, 'ms, 'ss) state -> ('a, 'ms, 'ss) state
(** Update the status to idle. *)
val idle : 'a status -> 'a status
(** Update the status to running. *)
val running :
?mode:mode -> ?input:'a value ->
?now:time -> ?stop:time -> 'a status -> 'a status
(** Get the current step mode.
Should only be called when running (see [is_running]). *)
val mode : ('a, 'ms, 'ss) state -> mode
(** Get the current input.
Should only be called when running (see [is_running]). *)
val input : ('a, 'ms, 'ss) state -> 'a value
(** Get the current timestamp.
Should only be called when running (see [is_running]). *)
val now : ('a, 'ms, 'ss) state -> time
(** Get the current stop time.
Should only be called when running (see [is_running]). *)
val stop : ('a, 'ms, 'ss) state -> time
(** Build an initial state. *)
val init : mstate:'ms -> sstate:'ss -> ('a, 'ms, 'ss) state
val set_idle : ('a, 'ms, 'ss) state -> ('a, 'ms, 'ss) state
end
module FunctionalSimState : SimState =
@ -87,21 +88,20 @@ module FunctionalSimState : SimState =
exception Not_running
let status s = s.status
let mstate s = s.mstate
let sstate s = s.sstate
let get_mstate state = state.mstate
let get_sstate state = state.sstate
let is_running s =
match s.status with Running _ -> true | Idle -> false
let is_running state =
match state.status with Running _ -> true | Idle -> false
let idle _ = Idle
let set_idle state = { state with status = Idle }
let running ?mode ?input ?now ?stop s =
match s with
let set_running ?mode ?input ?now ?stop state =
match state.status with
| Idle ->
begin match mode, input, now, stop with
| Some mode, Some input, Some now, Some stop ->
Running { mode; input; now; stop }
{ state with status = Running { mode; input; now; stop } }
| _ -> raise (Invalid_argument "")
end
| Running { mode=m; input=i; now=n; stop=s } ->
@ -109,24 +109,23 @@ module FunctionalSimState : SimState =
let input = Option.value input ~default:i in
let now = Option.value now ~default:n in
let stop = Option.value stop ~default:s in
Running { mode; input; now; stop }
{ state with status = Running { mode; input; now; stop } }
let update ?status ?mstate ?sstate { status=st; mstate=ms; sstate=ss } =
let status = Option.value status ~default:st in
let mstate = Option.value mstate ~default:ms in
let sstate = Option.value sstate ~default:ss in
{ status; mstate; sstate }
let set_mstate state mstate = { state with mstate }
let set_sstate state sstate = { state with sstate }
let mode s =
let update state mstate sstate = { state with mstate; sstate }
let get_mode s =
match s.status with Running r -> r.mode | Idle -> raise Not_running
let input s =
let get_input s =
match s.status with Running r -> r.input | Idle -> raise Not_running
let now s =
let get_now s =
match s.status with Running r -> r.now | Idle -> raise Not_running
let stop s =
let get_stop s =
match s.status with Running r -> r.stop | Idle -> raise Not_running
let init ~mstate ~sstate = { status = Idle; mstate; sstate }
let get_init mstate sstate = { status = Idle; mstate; sstate }
end
module InPlaceSimState : SimState =
@ -147,21 +146,23 @@ module InPlaceSimState : SimState =
exception Not_running
let status s = s.status
let mstate s = s.mstate
let sstate s = s.sstate
let get_mstate state = state.mstate
let get_sstate state = state.sstate
let is_running s =
match s.status with Running _ -> true | Idle -> false
let is_running state =
match state.status with Running _ -> true | Idle -> false
let idle _ = Idle
let set_idle state =
state.status <- Idle;
state
let running ?mode ?input ?now ?stop status =
match status with
let set_running ?mode ?input ?now ?stop state =
match state.status with
| Idle ->
begin match mode, input, now, stop with
| Some mode, Some input, Some now, Some stop ->
Running { mode; input; now; stop }
state.status <- Running { mode; input; now; stop };
state
| _ -> raise (Invalid_argument "")
end
| Running ({ mode=m; input=i; now=n; stop=s } as r) ->
@ -169,23 +170,23 @@ module InPlaceSimState : SimState =
let input = Option.value input ~default:i in r.input <- input;
let now = Option.value now ~default:n in r.now <- now;
let stop = Option.value stop ~default:s in r.stop <- stop;
status
state
let update ?status ?mstate ?sstate
({ status=st; mstate=ms; sstate=ss } as s) =
let status = Option.value status ~default:st in
let mstate = Option.value mstate ~default:ms in
let sstate = Option.value sstate ~default:ss in
s.status <- status; s.mstate <- mstate; s.sstate <- sstate; s
let set_mstate state mstate = state.mstate <- mstate; state
let set_sstate state sstate = state.sstate <- sstate; state
let mode s =
let update state mstate sstate =
state.mstate <- mstate; state.sstate <- sstate; state
let get_mode s =
match s.status with Running r -> r.mode | Idle -> raise Not_running
let input s =
let get_input s =
match s.status with Running r -> r.input | Idle -> raise Not_running
let now s =
let get_now s =
match s.status with Running r -> r.now | Idle -> raise Not_running
let stop s =
let get_stop s =
match s.status with Running r -> r.stop | Idle -> raise Not_running
let init ~mstate ~sstate = { status = Idle; mstate; sstate }
let get_init mstate sstate = { status = Idle; mstate; sstate }
end