feat: lift runtime into language, start of zelus 2024 compatibility

This commit is contained in:
Henri Saudubray 2025-07-11 11:21:07 +02:00
parent dc8d941b84
commit ffc583985a
Signed by: hms
GPG key ID: 7065F57ED8856128
37 changed files with 1154 additions and 143 deletions

View file

@ -751,3 +751,20 @@ let hybrid ball () = y where
and assert (y >= 0)
```
Is this an issue?
= Ideas
== Continuous assertions with lifted runtime
Given a synchronous node simulating a hybrid one :
```zelus
let hybrid sincos () = (sin, cos) where
rec der sin = cos init 0.0
and der cos = -. sin init 1.0
let node sincos_sim = Solve.solve_ode45 sincos
```
We could create a primitive `Solve.assert_continuous` which would take as input
an `'a value` and a function `'a -> Solve.cond`, where `Solve.cond` is a
zero-crossing expression, for instance `fun v -> Solve.up(-. v)`. This could be
passed along to a zero-crossing solver during continuous phases.

View file

@ -11,10 +11,12 @@
#let simulink = smallcaps[Simulink]
#let sundials = smallcaps[Sundials CVODE]
#let zelus = smallcaps[Zélus]
#let TODO(..what) = {
#let note(color, prefix, ..what) = {
let msg = if what.pos().len() == 0 { "" } else { ": " + what.pos().join("") }
block(fill: red, width: 100%, inset: 5pt, align(center, raw("TODO" + msg)))
block(fill: color, width: 100%, inset: 5pt, align(center, raw(prefix + msg)))
}
#let TODO(..what) = note(red, "TODO", ..what)
#let MENTION(..what) = note(gray, "MENTION", ..what)
#let adot(s) = $accent(#s, dot)$
#let addot(s) = $accent(#s, dot.double)$
@ -97,14 +99,14 @@ physical systems. Continuous phases are described using ordinary differential
equations (ODEs), while discrete phases can be represented as a reactive
program in a synchronous language such as #lustre or #esterel.
As a first example, say we wish to model a bouncing ball. We could start by
describing its distance from the ground $y$ with a second-order differential
equation
$ addot(y) = -9.81 $
As an illustration, say we wished to model an extensively studied system: a
bouncing ball. We could start by describing its distance from the ground $y$ as
a function of time, with a second-order differential equation
$ addot(y) = -9.81, $
where $addot(y)$ denotes the second order derivative of $y$ with
respect to time (the acceleration of the ball), and $9.81$ is the gravitational
constant $g$: the acceleration of the ball is its negation. We now give the
initial position and speed of the ball:
respect to time $(d^2y)/(d t^2)$ (the acceleration of the ball), and $9.81$ is
the gravitational constant $g$: the acceleration of the ball is its negation. We
now give the initial position and speed of the ball:
$ y(0) = 50 space space space adot(y)(0) = 0 $
We have just described an initial value problem: given an ODE and an initial
value for its dependent variable, its solution is a function $y(t)$ returning
@ -113,17 +115,20 @@ this function using an ODE solver, such as #sundials.
As of right now, our ball will fall until the end of time; we have not said
anything about how it bounces when it hits the floor. To do so, we need a notion
of discrete _events_. These are modelled by zero-crossings: we monitor a certain
value and stop when it goes from strictly negative to positive or null. For our
purposes, we choose $-y$ as the monitored value, and call the zero-crossing
event $z$. When $z$ occurs (i.e., when the ball touches the ground), we set the
speed $adot(y)$ to $-k dot "last"(adot(y))$, where $"last"(y)$ denotes the left
limit of $y$ (we cannot specify $adot(y)$ in terms of itself), and $k$ is a
factor modelling the loss of inertia due to the collision (say, $0.8$). We can
then resume the approximation of the solution.
of _events_: we need to identify exactly when the ball hits the ground, so that
we may take action to make it bounce. These events are modelled by
zero-crossings: we monitor a certain value and stop when it goes from strictly
negative to positive or null. For our purposes, we choose $-y$ as the monitored
value, and call the zero-crossing event $z$. When $z$ occurs (i.e., when the
ball touches the ground), we set the speed $adot(y)$ to
$-k dot #raw(lang: "zelus", "last")\(adot(y))$, where
$#raw(lang: "zelus", "last")\(y)$ denotes the left limit of $y$ (we cannot
specify $adot(y)$ in terms of itself), and $k$ is a factor modelling the loss of
inertia due to the collision (say, $0.8$). We can then resume the approximation
of the solution.
@lst:ball.zls shows how such a model might be expressed in the concrete syntax
of #zelus.
of #zelus @cit:zelus_sync_lng_with_ode.
#figure(placement: top, gap: 2em,
```zelus
@ -135,22 +140,68 @@ of #zelus.
caption: [The bouncing ball in #zelus]
) <lst:ball.zls>
More formally, a hybrid system can be described as an automaton
More formally, and as done in @cit:alg_ana_hyb_sys, a hybrid system $cal(H)$ can
be defined as a graph whose nodes represent continuous behaviour, and whose
edges represent discrete transitions:
$ cal(H) = (L o c, V a r, E d g, A c t, I n v, I n i) $
where:
- $L o c$ is a finite set of _locations_;
- $V a r$ is a finite set of _variables_;
- $E d g subset.eq L o c times F times L o c$ is a finite set of _transitions_
== Executing models
Executing such a model is quite simple. There are two modes of execution:
discrete and continuous. In continuous mode, we call the ODE solver in order to
obtain an approximation of the variables defined through ODEs, and monitor for
zero-crossings. If a zero-crossing occurs, we enter the discrete mode, in which
we perform computation steps as needed, until no other zero-crossing occurs, in
which case we go back to the continuous mode, and repeat, as seen in @automaton.
To execute such a model, we first compile it into a synchronous function, as
described in @cit:sync_based_codegen_hyb_sys_lng. The details of this
compilation step are not particularly relevant to our purposes, and can be
ignored. What is more interesting is the output of this compilation step: a
single synchronous function. The simulation loop is then itself described as a
synchronous function operating on
#figure(finite.automaton(
(D: (D: "cascade", C: "no cascade"),
C: (C: "no zero-crossing", D: "zero-crossing")),
initial: "D", final: (), layout: finite.layout.linear.with(spacing: 3)
), caption: [High-level overview of the runtime], placement: top) <automaton>
#MENTION("Use of a single solver")
#pagebreak()
// The compilation of a hybrid model into a synchronous function is described in
// detail in @cit:sync_based_codegen_hyb_sys_lng, but can be summarized quite
// succintly as follows. By pairing this synchronous function with an
// off-the-shelf ODE solver like #sundials, we can then simulate the dynamics of
// the system. This is done by repeatedly performing execution steps according to
// two different modes: discrete and continuous.
// The continuous mode operates as follows: we first call the ODE solver in order
// to approximate the dynamics of the model's continuous state.
// Continuous steps first call the ODE solver to approximate the dynamics of the
// model's continuous variables. The solver will return a function defined on a
// time interval, which we then provide as input to the zero-crossing solver, which
// will monitor the evolution of zero-crossing values along this interval. After
// both solvers have been called, we choose what the next step's mode will be:
// - if no zero-crossings have been detected, we output the entire solution
// provided by the ODE solver, and the next step remains continuous;
// - if a zero-crossing occurs, we return the solution provided by the ODE solver
// up to the zero-crossing instant, and the next step becomes a discrete step.
// Discrete steps perform state changes and side effects. We first call the model's
// step function, which updates the state and outputs a value. We then decide what
// the next step is. If a zero-crossing event occured due to the current step, the
// next step is another discrete step. If no new event occured, we perform a
// continuous step.
// Executing such a model is quite simple. There are two modes of execution:
// discrete and continuous. In continuous mode, we call the ODE solver in order
// to obtain an approximation of the variables defined through ODEs, and monitor
// for zero-crossings. If a zero-crossing occurs, we enter the discrete mode, in
// which we perform computation steps as needed, until no other zero-crossing
// occurs, in which case we go back to the continuous mode, and repeat, as seen
// in @automaton.
// #figure(finite.automaton(
// (D: (D: "cascade", C: "no cascade"),
// C: (C: "no zero-crossing", D: "zero-crossing")),
// initial: "D", final: (), layout: finite.layout.linear.with(spacing: 3)
// ), caption: [High-level overview of the runtime], placement: top) <automaton>
= Runtime
To solve this issue, we need to redefine what the runtime of our hybrid system
@ -180,10 +231,10 @@ required by the assertion becomes a state variable.
== Solvers as synchronous nodes
== Simulations as synchronous nodes
#TODO("talk about the new runtime")
#MENTION("the new runtime")
= Assertions
#TODO("talk about how assertions are done")
#MENTION("how assertions are done")
#pagebreak()
= Annex

View file

@ -1,16 +1,137 @@
@article{
ns_sem_benveniste_bourke_caillaud_pouzet,
title={Non-Standard Semantics of Hybrid Systems Modelers},
author={Benveniste, Albert and Bourke, Timothy and
Caillaud, Benoıt and Pouzet, Marc},
year={2011},
language={en}
@article{cit:nonstd_sem_hyb_sys_mod,
title = {Non-standard semantics of hybrid systems modelers},
journal = {Journal of Computer and System Sciences},
volume = {78},
number = {3},
pages = {877-910},
year = {2012},
note = {In Commemoration of Amir Pnueli},
issn = {0022-0000},
doi = {https://doi.org/10.1016/j.jcss.2011.08.009},
url = {https://www.sciencedirect.com/science/article/pii/S0022000011001061},
author = {Albert Benveniste and Timothy Bourke and Benoît Caillaud and Marc
Pouzet},
keywords = {Hybrid systems, Hybrid systems modelers, Non-standard analysis,
Non-standard semantics, Constructive semantics, Kahn process
networks, Compilation of hybrid systems},
abstract = {Hybrid system modelers have become a corner stone of complex
embedded system development. Embedded systems include not only
control components and software, but also physical devices. In
this area, Simulink is a de facto standard design framework, and
Modelica a new player. However, such tools raise several issues
related to the lack of reproducibility of simulations
(sensitivity to simulation parameters and to the choice of a
simulation engine). In this paper we propose using techniques
from non-standard analysis to define a semantic domain for hybrid
systems. Non-standard analysis is an extension of classical
analysis in which infinitesimal (the ε and η in the celebrated
generic sentence ∀ε∃η… of college maths) can be manipulated as
first class citizens. This approach allows us to define both a
denotational semantics, a constructive semantics, and a Kahn
Process Network semantics for hybrid systems, thus establishing
simulation engines on a sound but flexible mathematical
foundation. These semantics offer a clear distinction between the
concerns of the numerical analyst (solving differential
equations) and those of the computer scientist (generating
execution schemes). We also discuss a number of practical and
fundamental issues in hybrid system modelers that give rise to
non-reproducibility of results, non-determinism, and undesirable
side effects. Of particular importance are cascaded mode changes
(also called “zero-crossings” in the context of hybrid systems
modelers).},
}
@inbook{
opsem_lee_zheng,
title={Operational Semantics of Hybrid Systems},
ISBN={978-3-540-25108-8},
author={Lee, Edward A. and Zheng, Haiyang},
year={2005},
language={en}
@inbook{cit:op_sem_hyb_sys,
address = {Berlin, Heidelberg},
series = {Lecture Notes in Computer Science},
title = {Operational Semantics of Hybrid Systems},
volume = {3414},
ISBN = {978-3-540-25108-8},
url = {http://link.springer.com/10.1007/978-3-540-31954-2_2},
DOI = {10.1007/978-3-540-31954-2_2},
abstractNote = {This paper discusses an interpretation of hybrid systems as
executable models. A specification of a hybrid system for this
purpose can be viewed as a program in a domain-specific
programming language. We describe the semantics of HyVisual,
which is such a domain-specific programming language. The
semantic properties of such a language affect our ability to
understand, execute, and analyze a model. We discuss several
semantic issues that come in defining such a programming
language, such as the interpretation of discontinuities in
continuous-time signals, and the interpretation of
discrete-event signals in hybrid systems, and the
consequences of numerical ODE solver techniques. We describe
the solution in HyVisual by giving its operational semantics.
},
booktitle = {Hybrid Systems: Computation and Control},
publisher = {Springer Berlin Heidelberg},
author = {Lee, Edward A. and Zheng, Haiyang},
editor = {Morari, Manfred and Thiele, Lothar},
year = {2005},
pages = {2553},
collection = {Lecture Notes in Computer Science},
language = {en},
}
@inproceedings{cit:zelus_sync_lng_with_ode,
address = {Philadelphia Pennsylvania USA},
title = {Zélus: a synchronous language with ODEs},
ISBN = {978-1-4503-1567-8},
url = {https://dl.acm.org/doi/10.1145/2461328.2461348},
DOI = {10.1145/2461328.2461348},
abstractNote = { Z´elus is a new programming language for modeling systems
that mix discrete logical time and continuous time behaviors.
From a users perspective, its main originality is to extend
an existing Lustre-like synchronous language with Ordinary
Differential Equations (ODEs). The extension is conservative:
any synchronous program expressed as dataflow equations and
hierarchical automata can be composed arbitrarily with ODEs
in the same source code. },
booktitle = { Proceedings of the 16th international conference on Hybrid
systems: computation and control },
publisher = {ACM},
author = {Bourke, Timothy and Pouzet, Marc},
year = {2013},
month = apr,
pages = {113118},
language = {en},
}
@inbook{cit:sync_based_codegen_hyb_sys_lng,
address = {Berlin, Heidelberg},
series = {Lecture Notes in Computer Science},
title = {A Synchronous-Based Code Generator for Explicit Hybrid Systems
Languages},
volume = {9031},
rights = {http://www.springer.com/tdm},
ISBN = {978-3-662-46662-9},
url = {http://link.springer.com/10.1007/978-3-662-46663-6_4},
DOI = {10.1007/978-3-662-46663-6_4},
abstractNote = {Modeling languages for hybrid systems are cornerstones of
embedded systems development in which software interacts with
a physical environment. Sequential code generation from such
languages is important for simulation efficiency and for
producing code for embedded targets. Despite being routinely
used in industrial compilers, code generation is rarely, if
ever, described in full detail, much less formalized. Yet
formalization is an essential step in building trustable
compilers for critical embedded software development.},
booktitle = {Compiler Construction},
publisher = {Springer Berlin Heidelberg},
author = {Bourke, Timothy and Colaço, Jean-Louis and Pagano, Bruno and
Pasteur, Cédric and Pouzet, Marc},
editor = {Franke, Björn},
year = {2015},
pages = {6988},
collection = {Lecture Notes in Computer Science},
language = {en},
}
@article{cit:alg_ana_hyb_sys,
title = {The algorithmic analysis of hybrid systems},
author = {Alur, Rajeev and Courcoubetis, Costas and Halbwachs, Nicolas and
Henzinger, Thomas A and Ho, P-H and Nicollin, Xavier and Olivero,
Alfredo and Sifakis, Joseph and Yovine, Sergio},
journal = {Theoretical computer science},
volume = {138},
number = {1},
pages = {3--34},
year = {1995},
publisher = {Elsevier},
}