feat: pres
This commit is contained in:
parent
76dc461d44
commit
54801d18f0
4 changed files with 989 additions and 0 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
#let breakl(b) = {
|
#let breakl(b) = {
|
||||||
raw(b.text.replace(regex(" *\\\\\n *"), " "), lang: b.lang, block: b.block)
|
raw(b.text.replace(regex(" *\\\\\n *"), " "), lang: b.lang, block: b.block)
|
||||||
}
|
}
|
||||||
|
#set raw(syntaxes: "zelus.sublime-syntax")
|
||||||
|
|
||||||
#show raw: set text(font: "CaskaydiaCove NF")
|
#show raw: set text(font: "CaskaydiaCove NF")
|
||||||
#show link: underline
|
#show link: underline
|
||||||
|
|
@ -495,3 +496,71 @@ simulation loop as follows:
|
||||||
implemented in an imperative language like C. Code generation for hybrid
|
implemented in an imperative language like C. Code generation for hybrid
|
||||||
models has much in common with code generation for synchronous languages.
|
models has much in common with code generation for synchronous languages.
|
||||||
])
|
])
|
||||||
|
|
||||||
|
== Miscellaneous
|
||||||
|
|
||||||
|
=== Signals
|
||||||
|
|
||||||
|
Superdense time defines signals as
|
||||||
|
$S u p e r d e n s e(bb(V)) = bb(R) * bb(N) -> bb(V)$, where given
|
||||||
|
$f : S u p e r d e n s e(bb(V))$, $f(t, n) = f(t + n partial)$ in the
|
||||||
|
non-standard semantics.
|
||||||
|
|
||||||
|
Our representation instead uses
|
||||||
|
$S i g n a l(bb(V)) = S t r e a m((h : bb(R)) * ([0, h] -> bb(V)))$. Can we
|
||||||
|
convert between the two as we want?
|
||||||
|
|
||||||
|
#breakl(```ocaml
|
||||||
|
let (@.) f g x = f (g x)
|
||||||
|
let splt f g x = f x, g x
|
||||||
|
|
||||||
|
type 'a superdense = float * int -> 'a
|
||||||
|
type 'a signal = (float * (float -> 'a)) stream
|
||||||
|
|
||||||
|
let h = fst @. hd
|
||||||
|
let u = snd @. hd
|
||||||
|
|
||||||
|
let compose : 'a signal -> 'a superdense * float stream =
|
||||||
|
let rec g v s n =
|
||||||
|
if n = 0 then v
|
||||||
|
else if h s <> 0. then u s 0.
|
||||||
|
else g (u s 0.) (tl s) (n - 1) in
|
||||||
|
let rec f s = fun (t, n) ->
|
||||||
|
if t < h s then u s t
|
||||||
|
else if t = h s then g (u s t) (tl s) n
|
||||||
|
else f (tl s) (t -. h s, n) in
|
||||||
|
splt f (map fst)
|
||||||
|
|
||||||
|
let decompose : 'a superdense * float stream -> 'a signal =
|
||||||
|
let rec f r n = fun (s, h) ->
|
||||||
|
if hd h = 0. then (0., fun _ -> s (r, n)) @: f r (n + 1) (s, tl h)
|
||||||
|
else (hd h, fun t -> s (t +. r, 0)) @: f (r +. hd h) 0 (s, tl h) in
|
||||||
|
f 0. 0
|
||||||
|
```)
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
=== Continuity of assertions
|
||||||
|
|
||||||
|
In Zélus, the following program is forbidden:
|
||||||
|
|
||||||
|
```zelus
|
||||||
|
let hybrid f x = x >= 0
|
||||||
|
```
|
||||||
|
|
||||||
|
One cannot then write
|
||||||
|
```zelus
|
||||||
|
let hybrid ball () = y where
|
||||||
|
rec der y = y' init y0
|
||||||
|
and der y' = -. g init 0.0 reset z -> (-0.8 *. last y')
|
||||||
|
and z = up (-. y)
|
||||||
|
and assert (f y)
|
||||||
|
```
|
||||||
|
even though we want the following, equivalent program to work:
|
||||||
|
```zelus
|
||||||
|
let hybrid ball () = y where
|
||||||
|
rec der y = y' init y0
|
||||||
|
and der y' = -. g init 0.0 reset z -> (-0.8 *. last y')
|
||||||
|
and z = up (-. y)
|
||||||
|
and assert (y >= 0)
|
||||||
|
```
|
||||||
|
Is this an issue?
|
||||||
|
|
|
||||||
868
doc/pres.typ
Normal file
868
doc/pres.typ
Normal file
|
|
@ -0,0 +1,868 @@
|
||||||
|
#import "@preview/cetz:0.3.2"
|
||||||
|
#import "@preview/cetz-plot:0.1.1"
|
||||||
|
#import "@preview/finite:0.4.1"
|
||||||
|
#import "@preview/touying:0.6.1": *
|
||||||
|
#import themes.simple: *
|
||||||
|
|
||||||
|
// TODO: clarify between
|
||||||
|
// `solver.step : time * 's -> (time * (time -> 'y) * zin option) * 's`
|
||||||
|
// and `sim.step : (time * (time -> 'a)) * 's -> (time * (time -> 'b)) * 's`
|
||||||
|
|
||||||
|
#show: simple-theme.with(
|
||||||
|
aspect-ratio: "16-9",
|
||||||
|
config-common(show-bibliography-as-footnote: bibliography("sources.bib")),
|
||||||
|
)
|
||||||
|
#let cetz-canvas = touying-reducer.with(
|
||||||
|
reduce: cetz.canvas,
|
||||||
|
cover: cetz.draw.hide.with(bounds: true)
|
||||||
|
)
|
||||||
|
#let arrow(start, ..steps, stroke: black) = cetz.draw.line(
|
||||||
|
..(steps.pos().fold((start,), (acc, step) => {
|
||||||
|
let ((.., (x, y)), (d, l)) = (acc, step)
|
||||||
|
let (nx, ny) = if d == "d" {
|
||||||
|
(x, y - l)
|
||||||
|
} else if d == "u" {
|
||||||
|
(x, y + l)
|
||||||
|
} else if d == "l" {
|
||||||
|
(x - l, y)
|
||||||
|
} else if d == "r" {
|
||||||
|
(x + l, y)
|
||||||
|
} else {
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
acc.push((nx, ny))
|
||||||
|
acc
|
||||||
|
})),
|
||||||
|
mark: (end: "straight"),
|
||||||
|
stroke: stroke
|
||||||
|
)
|
||||||
|
#let (d, u, l, r) = ("d", "u", "l", "r")
|
||||||
|
#let box(start, dim, double: false, content: none, ..rest) = {
|
||||||
|
let (x, y) = start
|
||||||
|
let (dx, dy) = dim
|
||||||
|
cetz.draw.rect((x, y), (x + dx, y + dy), ..rest)
|
||||||
|
if double {
|
||||||
|
cetz.draw.rect((x + 0.1, y + 0.1), (x + dx - 0.1, y + dy - 0.1), ..rest)
|
||||||
|
}
|
||||||
|
if content != none {
|
||||||
|
cetz.draw.content((x + dx / 2, y + dy / 2), content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#set raw(syntaxes: "zelus.sublime-syntax")
|
||||||
|
#show raw: set text(font: "CaskaydiaCove NF")
|
||||||
|
|
||||||
|
#let rbrace(in-block, out-block, out-width: auto, ..args) = {
|
||||||
|
math.lr([
|
||||||
|
#in-block
|
||||||
|
$mid(})$
|
||||||
|
#block(width: out-width, out-block)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
= Executing Hybrid Systems
|
||||||
|
|
||||||
|
Henri Saudubray
|
||||||
|
#footnote(link("https://codeberg.org/17maiga/hsim"), numbering: "*")
|
||||||
|
|
||||||
|
22/05/2025
|
||||||
|
|
||||||
|
|
||||||
|
= Hybrid Systems <touying:hidden>
|
||||||
|
|
||||||
|
== Hybrid systems, as seen in Zélus
|
||||||
|
|
||||||
|
Computation alternates between _discrete_ and _continuous_ phases.
|
||||||
|
#pause #linebreak()
|
||||||
|
Use of an ODE solver to approximate continuous behaviour.
|
||||||
|
#pause #linebreak()
|
||||||
|
Side effects and state jumps occur in discrete steps triggered by
|
||||||
|
zero-crossings.
|
||||||
|
|
||||||
|
#meanwhile
|
||||||
|
|
||||||
|
#align(horizon, {
|
||||||
|
columns(2, [
|
||||||
|
```zelus
|
||||||
|
let hybrid saw() = y where
|
||||||
|
rec der y = 1.0
|
||||||
|
init 0.0
|
||||||
|
reset z -> (0.0)
|
||||||
|
and z = up(y -. 1.0)
|
||||||
|
```
|
||||||
|
#align(center, {
|
||||||
|
import cetz: *
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
canvas({
|
||||||
|
plot(
|
||||||
|
size: (9, 4), axis-style: "school-book", legend: (7.25, 4.5),
|
||||||
|
x-tick-step: 1, x-label: none, y-tick-step: 1, y-label: none,
|
||||||
|
y-grid: true, y-min: -0.5, y-max: 1.5, x-min: 0, x-max: 4.5,
|
||||||
|
plot-style: (stroke: blue), {
|
||||||
|
for i in range(5) {
|
||||||
|
add(((i, 0), (i + 1, 1)))
|
||||||
|
}
|
||||||
|
add(((0, 0),), label: [`saw`])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
== Hybrid assertions
|
||||||
|
|
||||||
|
Assertions are themselves hybrid systems.
|
||||||
|
#pause #linebreak()
|
||||||
|
Adding assertions can change the final result, they are not _transparent_.
|
||||||
|
#pause #linebreak()
|
||||||
|
Solution: simulate assertions with their own solver.
|
||||||
|
#meanwhile
|
||||||
|
|
||||||
|
#columns(2, [
|
||||||
|
#align(horizon, [
|
||||||
|
```zelus
|
||||||
|
let hybrid saw() = y where
|
||||||
|
rec der y = 1.0
|
||||||
|
init 0.0
|
||||||
|
reset z -> (0.0)
|
||||||
|
and z = up(y -. 1.0)
|
||||||
|
and assert(0.0 <= y <= 1.0)
|
||||||
|
```
|
||||||
|
])
|
||||||
|
#colbreak()
|
||||||
|
#align(center + horizon, {
|
||||||
|
import cetz: *
|
||||||
|
canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (8, 4.5))
|
||||||
|
box((2, 0.25), (4, 1.5), content: ```zelus assert```)
|
||||||
|
box((2, 2.75), (4, 1.5), content: `...`)
|
||||||
|
arrow((-1, 3.5), (r, 3))
|
||||||
|
arrow((6, 3.5), (r, 3))
|
||||||
|
arrow((7, 3.5), (d, 1.25), (l, 6), (d, 1.25), (r, 1))
|
||||||
|
content((-1.5, 3.5), [`()`])
|
||||||
|
content((9.5, 3.5), [`y`])
|
||||||
|
content((1, 5), [`saw`])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
= Nodes <touying:hidden>
|
||||||
|
|
||||||
|
== Discrete nodes
|
||||||
|
|
||||||
|
Nodes take an additional reset parameter `'p` for their reset function:
|
||||||
|
|
||||||
|
#align(horizon)[
|
||||||
|
#grid(columns: (3fr, 2fr), align: (left, left),
|
||||||
|
```ocaml
|
||||||
|
type ('p, 'a, 'b) dnode =
|
||||||
|
DNode : {
|
||||||
|
state : 's;
|
||||||
|
step : 's -> 'a -> 'b * 's;
|
||||||
|
reset : 'p -> 's -> 's;
|
||||||
|
} -> ('p, 'a, 'b) dnode
|
||||||
|
```,
|
||||||
|
cetz.canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (4, 4), content: `DNode`)
|
||||||
|
arrow((-1, 2), (r, 1))
|
||||||
|
arrow((4, 2), (r, 1))
|
||||||
|
arrow((2, 5), (d, 1))
|
||||||
|
content((-1.6, 2), `'a`)
|
||||||
|
content((5.6, 2), `'b`)
|
||||||
|
content((2, 5.6), `'p`)
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
|
||||||
|
== Hybrid nodes
|
||||||
|
|
||||||
|
Hybrid nodes are a bit more complex:
|
||||||
|
#block([
|
||||||
|
```ocaml
|
||||||
|
type ('p, 'a, 'b, 'y, 'yder, 'zin, 'zout) hnode =
|
||||||
|
HNode : {
|
||||||
|
state : 's;
|
||||||
|
step : 's -> 'a -> 'b * 's;
|
||||||
|
reset : 'p -> 's -> 's;
|
||||||
|
fder : 's -> 'a -> 'y -> 'yder;
|
||||||
|
fzer : 's -> 'a -> 'y -> 'zout;
|
||||||
|
fout : 's -> 'a -> 'y -> 'b;
|
||||||
|
(* ... *)
|
||||||
|
} -> ('p, 'a, 'b, 'y, 'yder, 'zin, 'zout) hnode
|
||||||
|
```
|
||||||
|
#pause
|
||||||
|
#place(left + top, dx: 75%, dy: 36%, block(grid(
|
||||||
|
align: left + horizon, columns: (1fr, 10fr),
|
||||||
|
move(scale(y: 200%, $}$), dy: -2%), [Discrete behaviour]
|
||||||
|
), width: 40%))
|
||||||
|
#place(left + top, dx: 75%, dy: 62.5%, block(grid(
|
||||||
|
align: left + horizon, columns: (1fr, 10fr),
|
||||||
|
move(scale(y: 300%, $}$), dy: -2%), [Continuous behaviour]
|
||||||
|
), width: 40%))
|
||||||
|
])
|
||||||
|
|
||||||
|
= Solvers are nodes <touying:hidden>
|
||||||
|
|
||||||
|
== ODE solvers
|
||||||
|
|
||||||
|
#slide(self => [
|
||||||
|
#let (uncover, only) = utils.methods(self)
|
||||||
|
|
||||||
|
ODE solvers are discrete nodes producing streams of functions defined on
|
||||||
|
successive intervals:
|
||||||
|
$(h: bb(R)_+) -->^D (h': bb(R)_+) times (u: [0,h'] -> bb(V))$.
|
||||||
|
|
||||||
|
#linebreak()
|
||||||
|
#grid(columns: (1fr, 1fr), align: (center, left), cetz.canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (4, 4), content: `csolver`)
|
||||||
|
arrow((-1, 2), (r, 1))
|
||||||
|
arrow((4, 3), (r, 1))
|
||||||
|
arrow((4, 1), (r, 1))
|
||||||
|
content((-1.5, 2), $h$)
|
||||||
|
content((5.5, 3), $h'$)
|
||||||
|
content((5.5, 1), $u$)
|
||||||
|
arrow((2, 5), (d, 1))
|
||||||
|
content((2, 5.6), "IVP")
|
||||||
|
}), align(horizon, cetz-canvas({
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
only(1, plot(
|
||||||
|
size: (10, 4.5), axis-style: "school-book", legend: (9, 4.5),
|
||||||
|
y-tick-step: none, y-label: none, y-min: 0, y-max: 6,
|
||||||
|
x-tick-step: none, x-label: none, x-min: 0, x-max: 5,
|
||||||
|
x-ticks: ((5, $h$), (2, $h'$)), {
|
||||||
|
add(domain: (0, 2), t => calc.sin(t) + t, label: $u$)
|
||||||
|
}))
|
||||||
|
only(2, plot(
|
||||||
|
size: (10, 4.5), axis-style: "school-book", legend: (9, 4.5),
|
||||||
|
y-tick-step: none, y-label: none, y-min: 0, y-max: 6,
|
||||||
|
x-tick-step: none, x-label: none, x-min: 0, x-max: 5,
|
||||||
|
x-ticks: ((5, $h$), (2, ""), (4, $h'$)), {
|
||||||
|
add(domain: (2, 4), t => calc.sin(t) + t, label: $u$)
|
||||||
|
add(domain: (0, 2), t => calc.sin(t) + t)
|
||||||
|
}))
|
||||||
|
only(3, plot(
|
||||||
|
size: (10, 4.5), axis-style: "school-book", legend: (9, 4.5),
|
||||||
|
y-tick-step: none, y-label: none, y-min: 0, y-max: 6,
|
||||||
|
x-tick-step: none, x-label: none, x-min: 0, x-max: 5,
|
||||||
|
x-ticks: ((2, ""), (4, ""), (5, $h, h'$)), {
|
||||||
|
add(domain: (4, 5), t => calc.sin(t) + t, label: $u$)
|
||||||
|
add(domain: (0, 4), t => calc.sin(t) + t)
|
||||||
|
}))
|
||||||
|
})))
|
||||||
|
])
|
||||||
|
|
||||||
|
== Zero-crossing solvers
|
||||||
|
|
||||||
|
#slide(self => [
|
||||||
|
#let (uncover, only) = utils.methods(self)
|
||||||
|
|
||||||
|
Zero-crossing solvers are discrete nodes too, looking for zero-crossings on
|
||||||
|
functions:
|
||||||
|
$(h: bb(R)_+) times (u: [0, h] -> bb(V)) -->^D
|
||||||
|
(h': bb(R)_+) times (z: bb(Z)?)$.
|
||||||
|
|
||||||
|
#linebreak()
|
||||||
|
#grid(columns: (1fr, 1fr), align: (center, left), cetz.canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (4, 4), content: `zsolver`)
|
||||||
|
arrow((-1, 1), (r, 1))
|
||||||
|
arrow((-1, 3), (r, 1))
|
||||||
|
arrow((4, 1), (r, 1))
|
||||||
|
arrow((4, 3), (r, 1))
|
||||||
|
arrow((2, 5), (d, 1))
|
||||||
|
content((-1.5, 3), $h$)
|
||||||
|
content((-1.5, 1), $u$)
|
||||||
|
content((5.5, 3), $h'$)
|
||||||
|
content((5.5, 1), $z$)
|
||||||
|
content((2, 5.6), "ZC")
|
||||||
|
}), align(horizon, cetz-canvas({
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
only(1, plot(
|
||||||
|
size: (10, 4.5), axis-style: "school-book", legend: (9, 4.5),
|
||||||
|
y-tick-step: none, y-label: none, y-min: -2, y-max: 5,
|
||||||
|
x-tick-step: none, x-label: none, x-min: 0, x-max: 2,
|
||||||
|
x-ticks: ((2/3, $h, h'$),), {
|
||||||
|
add(domain: (0, 2/3), t => t * t - 1, label: "u")
|
||||||
|
}))
|
||||||
|
only(2, plot(
|
||||||
|
size: (10, 4.5), axis-style: "school-book", legend: (9, 4.5),
|
||||||
|
y-tick-step: none, y-label: none, y-min: -2, y-max: 5,
|
||||||
|
x-tick-step: none, x-label: none, x-min: 0, x-max: 2,
|
||||||
|
x-ticks: ((1, $h'$), (4/3, $h$)), {
|
||||||
|
add(domain: (2/3, 4/3), t => t * t - 1, label: "u")
|
||||||
|
add(domain: (0, 2/3), t => t * t - 1)
|
||||||
|
}))
|
||||||
|
only(3, plot(
|
||||||
|
size: (10, 4.5), axis-style: "school-book", legend: (9, 4.5),
|
||||||
|
y-tick-step: none, y-label: none, y-min: -2, y-max: 5,
|
||||||
|
x-tick-step: none, x-label: none, x-min: 0, x-max: 2,
|
||||||
|
x-ticks: ((2, $h, h'$),), {
|
||||||
|
add(domain: (1, 2), t => t * t - 1, label: "u")
|
||||||
|
add(domain: (0, 1), t => t * t - 1)
|
||||||
|
}))
|
||||||
|
only("1, 3", cetz.draw.content((2, 4), $z = bot$))
|
||||||
|
only("2", cetz.draw.content((2, 3.9), $z != bot$))
|
||||||
|
})))
|
||||||
|
])
|
||||||
|
|
||||||
|
== A full solver
|
||||||
|
|
||||||
|
Combine an ODE solver `csolver` with a zero-crossing solver `zsolver`:
|
||||||
|
|
||||||
|
#linebreak()
|
||||||
|
|
||||||
|
#align(center, {
|
||||||
|
import cetz: *
|
||||||
|
canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (12, 4))
|
||||||
|
content((1.5, 4.5), `solver`)
|
||||||
|
box((1, 0.5), (4, 3), content: `csolver`)
|
||||||
|
box((7, 1.5), (4, 2), content: `zsolver`)
|
||||||
|
arrow((-1, 2), (r, 2))
|
||||||
|
arrow((5, 3), (r, 2))
|
||||||
|
arrow((5, 1), (r, 8))
|
||||||
|
arrow((6, 1), (u, 1), ("r", 1))
|
||||||
|
arrow((11, 2), (r, 2))
|
||||||
|
arrow((11, 3), (r, 2))
|
||||||
|
content((-1.5, 2), [$h$])
|
||||||
|
content((6, 3.5), [$h'$])
|
||||||
|
content((6, 0.5), [$u$])
|
||||||
|
content((13.5, 1), [$u$])
|
||||||
|
content((13.75, 2), [$h''$])
|
||||||
|
content((13.5, 3), [$z$])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
= Signals <touying:hidden>
|
||||||
|
|
||||||
|
== Semantics
|
||||||
|
|
||||||
|
Two semantics are often used for hybrid systems:
|
||||||
|
- Superdense semantics @opsem_lee_zheng:
|
||||||
|
- signals are functions from $bb(R) times bb(N) -> bb(V)$;
|
||||||
|
- discrete instants are ordered lexicographically
|
||||||
|
- Non-standard semantics @ns_sem_benveniste_bourke_caillaud_pouzet:
|
||||||
|
- signals are functions from the hyperreals $""^*bb(R) -> bb(V)$;
|
||||||
|
- discrete instants are ordered using infinitesimals
|
||||||
|
|
||||||
|
== Optional values
|
||||||
|
|
||||||
|
The solver does not always reach the requested horizon in a single step.
|
||||||
|
#linebreak()
|
||||||
|
We _wait_ for it to finish computing:
|
||||||
|
|
||||||
|
#align(center, {
|
||||||
|
import cetz: *
|
||||||
|
canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
content((0, 1.5), "Input:")
|
||||||
|
content((0, 0), "Output:")
|
||||||
|
content((3.5, 1.5), $a_0,$)
|
||||||
|
content((3.5, 0), $bot,$)
|
||||||
|
content((5, 1.5), $bot,$)
|
||||||
|
content((5, 0), $b_0,$)
|
||||||
|
content((6.5, 1.5), $bot,$)
|
||||||
|
content((6.5, 0), $b_1,$)
|
||||||
|
content((8, 1.5), $bot,$)
|
||||||
|
content((8, 0), $b_2,$)
|
||||||
|
content((9.5, 1.5), $bot,$)
|
||||||
|
content((9.5, 0), $bot,$)
|
||||||
|
content((11, 1.5), $a_2,$)
|
||||||
|
content((11, 0), $bot,$)
|
||||||
|
content((12.5, 1.5), $bot,$)
|
||||||
|
content((12.5, 0), $b_3,$)
|
||||||
|
content((14, 1.5), $bot,$)
|
||||||
|
content((14, 0), $b_4,$)
|
||||||
|
content((15.5, 1.5), $...$)
|
||||||
|
content((15.5, 0), $...$)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
This is represented by optional values:
|
||||||
|
|
||||||
|
$ "Signal"(alpha) = "Stream"("Optional"((h:bb(R)_+) times ([0,h] -> alpha))) $
|
||||||
|
|
||||||
|
== Discrete instants
|
||||||
|
|
||||||
|
Discrete instants are represented by constant functions of length 0. Their
|
||||||
|
order is the order of apparition in the stream.
|
||||||
|
|
||||||
|
== Example
|
||||||
|
|
||||||
|
The stream
|
||||||
|
#align(center, ```ocaml
|
||||||
|
f = (1.0, λt -> t)::(0.0, λt -> 2.0)::(0.3, λt -> 3*t)::...
|
||||||
|
```)
|
||||||
|
defines the function $f : ""^*bb(R)_+ -> bb(R)$
|
||||||
|
#grid(columns: (1fr, 1fr), align: (center, center), $ & f(t) = cases(
|
||||||
|
"st"(t)#footnote([
|
||||||
|
$"st"(t)$ denotes the _standard part_ of $t$, that is, the closest real
|
||||||
|
number $r$ to $t$.
|
||||||
|
]) & "if" t in [0, 1],
|
||||||
|
2 & "if" t = 1 + partial,
|
||||||
|
3 dot "st"(t) & "if" t in [1 + 2 partial, 1.3],
|
||||||
|
...
|
||||||
|
) $, cetz.canvas({
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
plot(
|
||||||
|
size: (10, 3), axis-style: "school-book",
|
||||||
|
y-label: none, y-tick-step: none,
|
||||||
|
x-label: none, x-tick-step: none, x-ticks: (1, 2.3), x-grid: true,
|
||||||
|
{
|
||||||
|
add(domain: (0, 1), t => t)
|
||||||
|
add(((1, 2),), mark: "o", mark-size: 0.05,
|
||||||
|
mark-style: (stroke: blue, fill: blue))
|
||||||
|
add(domain: (1, 1.3), t => 3 * t, style: (stroke: blue))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
|
||||||
|
= Simulation <touying:hidden>
|
||||||
|
|
||||||
|
== Simulation
|
||||||
|
|
||||||
|
The simulation is itself a discrete node on signals:
|
||||||
|
|
||||||
|
#align(center, cetz.canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (12, 5), content: `???`)
|
||||||
|
arrow((-1, 2.5), (r, 1))
|
||||||
|
arrow((12, 2.5), (r, 1))
|
||||||
|
content((-3, 2.5), $"Signal"(alpha)$)
|
||||||
|
content((15, 2.5), $"Signal"(beta)$)
|
||||||
|
content((1, 5.5), `sim`)
|
||||||
|
box((0.5, 0.5), (4, 4), content: `model`)
|
||||||
|
box((7.5, 0.5), (4, 4), content: `solver`)
|
||||||
|
}))
|
||||||
|
|
||||||
|
It runs according to one of two modes: _discrete_ and _continuous_.
|
||||||
|
|
||||||
|
== (Re)initialization
|
||||||
|
|
||||||
|
When receiving a new value, store it
|
||||||
|
#alternatives([...], [and reset the solver.])
|
||||||
|
#align(center, cetz-canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
|
||||||
|
// sim
|
||||||
|
content((1, 6.5), `sim`)
|
||||||
|
box((0, 0), (14, 6))
|
||||||
|
|
||||||
|
// model
|
||||||
|
content((2.5, 5.25), `model`)
|
||||||
|
box((1, 1), (4, 3.75))
|
||||||
|
|
||||||
|
// model.fzer
|
||||||
|
box((1.25, 3), (3.5, 1.5), content: `fzer`)
|
||||||
|
|
||||||
|
// model.fder
|
||||||
|
box((1.25, 1.25), (3.5, 1.5), content: `fder`)
|
||||||
|
|
||||||
|
// sim state
|
||||||
|
box((10, 1.25), (3, 1.5), double: true, content: `state`)
|
||||||
|
|
||||||
|
// (h, u) -> state
|
||||||
|
content((-2.25, 3), $(h, u)$)
|
||||||
|
arrow((-1, 3), (r, 1.5), (d, 2.5), (r, 5), (u, 1.5), (r, 4.5))
|
||||||
|
|
||||||
|
// sim -> bot
|
||||||
|
arrow((14, 3), (r, 1))
|
||||||
|
content((15.5, 3), $bot$)
|
||||||
|
|
||||||
|
(pause,)
|
||||||
|
|
||||||
|
// computation
|
||||||
|
box((6, 3), (3, 1.5), content: `...`)
|
||||||
|
|
||||||
|
// solver
|
||||||
|
box((10, 3), (3, 1.5), content: `solver`)
|
||||||
|
|
||||||
|
// (h, u) -> computation
|
||||||
|
arrow((5.5, 2), (u, 1.5), (r, 0.5))
|
||||||
|
|
||||||
|
// fzer -> computation
|
||||||
|
arrow((4.75, 4), (r, 1.25))
|
||||||
|
|
||||||
|
// fder -> computation
|
||||||
|
arrow((4.75, 2), (r, 0.5), (u, 1.75), (r, 0.75))
|
||||||
|
|
||||||
|
// computation -> solver
|
||||||
|
arrow((9, 3.75), (r, 0.5), (u, 1.25), (r, 2), (d, 0.5))
|
||||||
|
content((10.5, 5.5), `ivp + zc`)
|
||||||
|
}))
|
||||||
|
|
||||||
|
== Discrete step
|
||||||
|
|
||||||
|
#alternatives(
|
||||||
|
[Use the model's `step` function and the stored input.],
|
||||||
|
[Build a constant function out of the output value.],
|
||||||
|
[Update the running mode as needed.]
|
||||||
|
)
|
||||||
|
#align(center, cetz-canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
// sim
|
||||||
|
content((1, 6.5), `sim`)
|
||||||
|
box((0, 0), (17, 6))
|
||||||
|
|
||||||
|
// sim state
|
||||||
|
box((1, 1), (3, 1.5), double: true, content: `state`)
|
||||||
|
|
||||||
|
// model
|
||||||
|
content((6.5, 5.5), `model`)
|
||||||
|
box((5, 0.5), (7, 4.5))
|
||||||
|
|
||||||
|
// model step
|
||||||
|
box((7, 1), (3, 1.5), content: `step`)
|
||||||
|
|
||||||
|
// model state
|
||||||
|
box((7, 2.75), (3, 1.5), double: true, content: `state`)
|
||||||
|
|
||||||
|
// bot -> sim
|
||||||
|
arrow((-1, 3), (r, 1))
|
||||||
|
content((-1.5, 3), $bot$)
|
||||||
|
|
||||||
|
// sim.state -> model.step
|
||||||
|
arrow((4, 1.5), (r, 3))
|
||||||
|
content((5.75, 1.25), $alpha$)
|
||||||
|
|
||||||
|
// model.state -> model.step
|
||||||
|
arrow((7, 3.5), (l, 0.5), (d, 1.5), (r, 0.5))
|
||||||
|
content((6, 2.875), $sigma$)
|
||||||
|
|
||||||
|
(pause,)
|
||||||
|
|
||||||
|
// constant signal
|
||||||
|
box((13, 1), (3, 1.5), content: `...`)
|
||||||
|
|
||||||
|
// model.step -> model.state
|
||||||
|
arrow((10, 2), (r, 0.5), (u, 1.5), (l, 0.5))
|
||||||
|
content((11, 2.875), $sigma$)
|
||||||
|
|
||||||
|
// model.step -> ...
|
||||||
|
arrow((10, 1.5), (r, 3))
|
||||||
|
content((11.25, 1), $beta$)
|
||||||
|
|
||||||
|
// constant signal -> signal(beta)
|
||||||
|
arrow((16, 1.75), (r, 0.5), (u, 1.25), (r, 1.5))
|
||||||
|
content((20, 3), $"Signal"(beta)$)
|
||||||
|
|
||||||
|
(pause,)
|
||||||
|
|
||||||
|
// computation for mode
|
||||||
|
box((1, 3.5), (3, 1.5), content: `...`)
|
||||||
|
|
||||||
|
// model.step -> mode computations
|
||||||
|
arrow((10.5, 3.5), (u, 1), (l, 6.5))
|
||||||
|
|
||||||
|
// sim.state -> mod.computations
|
||||||
|
arrow((4, 2), (r, 0.5), (u, 2), (l, 0.5))
|
||||||
|
|
||||||
|
// mod.computations -> sim.state
|
||||||
|
arrow((1, 4.25), (l, 0.5), (d, 2.5), (r, 0.5))
|
||||||
|
}))
|
||||||
|
|
||||||
|
== Continuous step
|
||||||
|
|
||||||
|
#alternatives(
|
||||||
|
[Call the solver with the current target horizon.],
|
||||||
|
[Use the current input and the result of the solver to build the output.],
|
||||||
|
[Update the model's continuous state with the new horizon.],
|
||||||
|
[Update the simulation mode in case of a zero-crossing.],
|
||||||
|
)
|
||||||
|
|
||||||
|
#align(center, cetz-canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
// sim
|
||||||
|
content((1, 6.5), `sim`)
|
||||||
|
box((0, 0), (17, 6))
|
||||||
|
|
||||||
|
// sim.state
|
||||||
|
box((1, 1), (3, 1.5), double: true, content: `state`)
|
||||||
|
|
||||||
|
// bot -> sim
|
||||||
|
arrow((-1, 3), (r, 1))
|
||||||
|
content((-1.5, 3), $bot$)
|
||||||
|
|
||||||
|
// solver
|
||||||
|
content((6.5, 5.25), `solver`)
|
||||||
|
box((5, 0.75), (6, 4))
|
||||||
|
|
||||||
|
// solver.step
|
||||||
|
box((6.5, 1), (3, 1.5), content: `step`)
|
||||||
|
|
||||||
|
// solver.state
|
||||||
|
box((6.5, 2.75), (3, 1.5), double: true, content: `state`)
|
||||||
|
|
||||||
|
// solver.state -> solver.step
|
||||||
|
arrow((6.5, 3.5), (l, 0.5), (d, 1.5), (r, 0.5))
|
||||||
|
content((5.5, 2.75), $sigma$)
|
||||||
|
|
||||||
|
// sim.state -> solver.step
|
||||||
|
arrow((4, 1.5), (r, 2.5))
|
||||||
|
|
||||||
|
(pause,)
|
||||||
|
|
||||||
|
// solver.step -> solver.state
|
||||||
|
arrow((9.5, 2.2), (r, 0.5), (u, 1.3), (l, 0.5))
|
||||||
|
|
||||||
|
// computation
|
||||||
|
box((13, 1), (3, 1.5), content: `...`)
|
||||||
|
|
||||||
|
// solver.step -> computation
|
||||||
|
arrow((9.5, 1.6), (r, 3.5))
|
||||||
|
arrow((9.5, 1.3), (r, 3.5))
|
||||||
|
|
||||||
|
// sim.state -> computation
|
||||||
|
arrow((4, 2), (r, 0.5), (d, 1.5), (r, 8), (u, 1.55), (r, 0.5))
|
||||||
|
|
||||||
|
// computation -> beta signal
|
||||||
|
arrow((16, 1.75), (r, 2.5))
|
||||||
|
content((20.5, 1.75), $"Signal"(beta)$)
|
||||||
|
|
||||||
|
(pause,)
|
||||||
|
|
||||||
|
// model
|
||||||
|
box((12.5, 2.75), (4, 2))
|
||||||
|
content((14, 5.25), `model`)
|
||||||
|
|
||||||
|
// model.state
|
||||||
|
box((13, 3), (3, 1.5), double: true, content: `state`)
|
||||||
|
|
||||||
|
// step -> model
|
||||||
|
arrow((12.25, 1.3), (u, 2.2), (r, 0.75))
|
||||||
|
arrow((12, 1.6), (u, 2.4), (r, 1))
|
||||||
|
|
||||||
|
(pause,)
|
||||||
|
|
||||||
|
// computation
|
||||||
|
box((1, 2.75), (3, 1.5), content: `...`)
|
||||||
|
|
||||||
|
// solver.step -> computation
|
||||||
|
arrow((9.5, 1.9), (r, 1), (u, 2.6), (l, 6), (d, 1), (l, 0.5))
|
||||||
|
|
||||||
|
// computation -> solver.state
|
||||||
|
arrow((1, 3.5), (l, 0.5), (d, 1.75), (r, 0.5))
|
||||||
|
}))
|
||||||
|
#pause
|
||||||
|
Is that it?
|
||||||
|
|
||||||
|
= States <touying:hidden>
|
||||||
|
|
||||||
|
== The state problem
|
||||||
|
|
||||||
|
#slide(self => [
|
||||||
|
#let (uncover, only) = utils.methods(self)
|
||||||
|
Some solvers modify their state in-place (e.g. #smallcaps("Sundials")).
|
||||||
|
#linebreak()
|
||||||
|
The function returned depends on this state.
|
||||||
|
#pause #linebreak()
|
||||||
|
It becomes invalid once we call the solver again.
|
||||||
|
|
||||||
|
#meanwhile
|
||||||
|
#align(center, cetz-canvas({
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
plot(
|
||||||
|
size: (10, 4), axis-style: "school-book",
|
||||||
|
x-label: none, x-tick-step: none, x-max: 6,
|
||||||
|
y-label: none, y-tick-step: none, y-max: 6,
|
||||||
|
x-grid: true, x-ticks: ((4.5, ""),),
|
||||||
|
{
|
||||||
|
only(1, add(domain: (0, 4.5), t => calc.sin(t) + t))
|
||||||
|
only(2, add(domain: (4.5, 6), t => calc.sin(t) + t))
|
||||||
|
only(2, add(domain: (0, 4.5), t => calc.sin(t) + t))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
])
|
||||||
|
|
||||||
|
== Solver steps
|
||||||
|
|
||||||
|
Most solvers start off with smaller steps, and increase the step length
|
||||||
|
progressively.
|
||||||
|
|
||||||
|
#align(center, cetz.canvas({
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
plot(
|
||||||
|
size: (10, 4), axis-style: "school-book", x-label: none, x-tick-step: none,
|
||||||
|
x-ticks: ((0,""), (0.001,""), (0.01,""), (0.1,""), (0.2,""), (0.4,""),
|
||||||
|
(1,""), (1.9,""), (3.9, "")), x-grid: true,
|
||||||
|
y-label: none, y-tick-step: none, {
|
||||||
|
add(domain: (0, 6), t => calc.sin(t) + t)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
|
||||||
|
Combined with in-place state modifications and resets, this could hinder
|
||||||
|
performance.
|
||||||
|
|
||||||
|
== State copies
|
||||||
|
|
||||||
|
Copying (part of) the state solves this issue.
|
||||||
|
#linebreak()
|
||||||
|
If the solver supports state copies, we can call it more than once per
|
||||||
|
simulation step.
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
type ('p, 'a, 'b) dnode_c =
|
||||||
|
DNodeC : {
|
||||||
|
state : 's;
|
||||||
|
step : 's -> 'a -> 'b * 's;
|
||||||
|
reset : 'p -> 's -> 's;
|
||||||
|
copy : 's -> 's;
|
||||||
|
} -> ('p, 'a, 'b) dnode_c
|
||||||
|
```
|
||||||
|
|
||||||
|
== Accelerating the simulation
|
||||||
|
|
||||||
|
We can then concatenate functions obtained from successive continuous steps.
|
||||||
|
If no zero-crossing is found, there is no discontinuity.
|
||||||
|
|
||||||
|
#linebreak()
|
||||||
|
|
||||||
|
#grid(align: center + horizon, columns: (10fr, 1fr, 10fr),
|
||||||
|
cetz.canvas({
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
plot(
|
||||||
|
size: (8, 4), axis-style: "school-book", x-label: none,
|
||||||
|
x-tick-step: none, x-ticks: ((3, $h_1$), (6, $h_2$)), x-grid: true,
|
||||||
|
y-label: none, y-tick-step: none, {
|
||||||
|
add(domain: (0, 3), t => calc.sin(t) + t)
|
||||||
|
add(domain: (3, 6), t => calc.sin(t) + t)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}), $==>$, cetz.canvas({
|
||||||
|
import cetz-plot.plot: *
|
||||||
|
plot(
|
||||||
|
size: (8, 4), axis-style: "school-book", x-label: none,
|
||||||
|
x-tick-step: none, x-ticks: ((6, $h_1$),), x-grid: true, y-label: none,
|
||||||
|
y-tick-step: none, { add(domain: (0, 6), t => calc.sin(t) + t) }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
= Composing simulations <touying:hidden>
|
||||||
|
|
||||||
|
== Pushing values through
|
||||||
|
|
||||||
|
#slide(self => [
|
||||||
|
#let (uncover, only) = utils.methods(self)
|
||||||
|
|
||||||
|
Unless we can copy the state, we need to use up all of a value before
|
||||||
|
producing the next. We need to push empty values ($bot$) as needed.
|
||||||
|
|
||||||
|
#align(center, cetz-canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (3, 3), content: $M_1$)
|
||||||
|
box((8, 0), (3, 3), content: $M_2$)
|
||||||
|
only("1, 3-4", arrow((-1, 1.5), (r, 1)))
|
||||||
|
only("2, 5-", arrow((-1, 1.5), (r, 1), stroke: red))
|
||||||
|
only("1, 2, 4-", arrow((3, 1.5), (r, 5)))
|
||||||
|
only("3", arrow((3, 1.5), (r, 5), stroke: red))
|
||||||
|
only("-3", arrow((11, 1.5), (r, 1)))
|
||||||
|
only("4-", arrow((11, 1.5), (r, 1), stroke: red))
|
||||||
|
content((-3, 1.5), $"Signal"(alpha)$)
|
||||||
|
content((5.5, 2), $"Signal"(beta)$)
|
||||||
|
content((14, 1.5), $"Signal"(gamma)$)
|
||||||
|
|
||||||
|
content((-4, -1), $alpha:$)
|
||||||
|
content((-4, -2.5), $beta:$)
|
||||||
|
content((-4, -4), $gamma:$)
|
||||||
|
(pause,)
|
||||||
|
content((-2, -4), $bot$)
|
||||||
|
content((-2, -2.5), $bot$)
|
||||||
|
content((-2, -1.1), $alpha_1$)
|
||||||
|
(pause,)
|
||||||
|
content((0, -1), $bot$)
|
||||||
|
content((0, -2.6), $beta_1$)
|
||||||
|
content((0, -4), $bot$)
|
||||||
|
(pause,)
|
||||||
|
content((2, -1), $bot$)
|
||||||
|
content((2, -2.5), $bot$)
|
||||||
|
content((2, -4.1), $gamma_1$)
|
||||||
|
(pause,)
|
||||||
|
content((4, -1.1), $alpha_2$)
|
||||||
|
content((4, -2.5), $bot$)
|
||||||
|
content((4, -4.1), $gamma_2$)
|
||||||
|
content((5.5, -1), $...$)
|
||||||
|
content((5.5, -2.5), $...$)
|
||||||
|
content((5.5, -4), $...$)
|
||||||
|
(pause,)
|
||||||
|
box((3.5, -3), (1, 1), stroke: red)
|
||||||
|
box((0.1, 0.1), (2.8, 2.8), stroke: red)
|
||||||
|
content((12, -2.6), "Possible early reset!")
|
||||||
|
}))
|
||||||
|
])
|
||||||
|
|
||||||
|
== Pulling values from
|
||||||
|
|
||||||
|
#slide(self => [
|
||||||
|
#let (uncover, only) = utils.methods(self)
|
||||||
|
|
||||||
|
The output signal stops at least as often as the input signal does.
|
||||||
|
#linebreak()
|
||||||
|
This calls for a "lazy" composition: we request rather than provide data.
|
||||||
|
|
||||||
|
#align(center, cetz-canvas({
|
||||||
|
import cetz.draw: *
|
||||||
|
box((0, 0), (3, 3), content: $M_1$)
|
||||||
|
box((8, 0), (3, 3), content: $M_2$)
|
||||||
|
only("1-3, 5-10, 12-", arrow((-1, 1.5), (r, 1)))
|
||||||
|
only("4, 11", arrow((-1, 1.5), (r, 1), stroke: red))
|
||||||
|
only("1-2, 4-5, 7-8, 10-11, 13-", arrow((3, 1.5), (r, 5)))
|
||||||
|
only("3, 6, 9, 12", arrow((3, 1.5), (r, 5), stroke: red))
|
||||||
|
only("1, 3-4, 6, 9, 11-12, 14-", arrow((11, 1.5), (r, 1)))
|
||||||
|
only("2, 5, 7, 8, 10, 13", arrow((11, 1.5), (r, 1), stroke: red))
|
||||||
|
content((-3, 1.5), $"Signal"(alpha)$)
|
||||||
|
content((5.5, 2), $"Signal"(beta)$)
|
||||||
|
content((14, 1.5), $"Signal"(gamma)$)
|
||||||
|
|
||||||
|
content((-4, -1), $alpha:$)
|
||||||
|
content((-4, -2.5), $beta:$)
|
||||||
|
content((-4, -4), $gamma:$)
|
||||||
|
(pause,)
|
||||||
|
content((-2, -4), $bot$)
|
||||||
|
(pause,)
|
||||||
|
content((-2, -2.5), $bot$)
|
||||||
|
(pause,)
|
||||||
|
content((-2, -1.1), $alpha_1$)
|
||||||
|
(pause,)
|
||||||
|
content((0, -4), $bot$)
|
||||||
|
(pause,)
|
||||||
|
content((0, -2.6), $beta_1$)
|
||||||
|
(pause,)
|
||||||
|
content((2, -4.1), $gamma_1$)
|
||||||
|
(pause,)
|
||||||
|
content((4, -4.1), $gamma_2$)
|
||||||
|
(pause,)
|
||||||
|
content((6, -2.6), $beta_2$)
|
||||||
|
content((6, -4), $bot$)
|
||||||
|
(pause,)
|
||||||
|
content((8, -4.1), $gamma_3$)
|
||||||
|
(pause,)
|
||||||
|
content((10, -4), $bot$)
|
||||||
|
content((10, -2.5), $bot$)
|
||||||
|
content((10, -1.1), $alpha_2$)
|
||||||
|
(pause,)
|
||||||
|
content((12, -2.6), $beta_3$)
|
||||||
|
content((12, -4), $bot$)
|
||||||
|
(pause,)
|
||||||
|
content((14, -4.1), $gamma_4$)
|
||||||
|
(pause,)
|
||||||
|
content((15.5, -1), $...$)
|
||||||
|
content((15.5, -2.5), $...$)
|
||||||
|
content((15.5, -4), $...$)
|
||||||
|
}))
|
||||||
|
])
|
||||||
|
|
||||||
|
= Conclusion <touying:hidden>
|
||||||
|
|
||||||
|
== What next?
|
||||||
|
|
||||||
|
#align(horizon, [
|
||||||
|
- Can we avoid pointless solver resets?
|
||||||
|
- Think about assertions: is the checking of an assertion a composition?
|
||||||
|
- Plug to Zélus' output
|
||||||
|
])
|
||||||
|
|
||||||
16
doc/sources.bib
Normal file
16
doc/sources.bib
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
@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}
|
||||||
|
}
|
||||||
|
@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}
|
||||||
|
}
|
||||||
36
doc/zelus.sublime-syntax
Normal file
36
doc/zelus.sublime-syntax
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
name: Zelus
|
||||||
|
file_extensions: [zls, zli]
|
||||||
|
scope: source
|
||||||
|
|
||||||
|
contexts:
|
||||||
|
main:
|
||||||
|
- match: \b(let|in|where|rec|and|local)\b
|
||||||
|
scope: keyword.control
|
||||||
|
|
||||||
|
- match: \b(if|then|else)\b
|
||||||
|
scope: keyword.control.conditional
|
||||||
|
|
||||||
|
- match: \b(hybrid|node)\b
|
||||||
|
scope: keyword.control
|
||||||
|
|
||||||
|
- match: \b(up|assert|der|init|reset|last)\b
|
||||||
|
scope: entity.name.constant
|
||||||
|
|
||||||
|
- match: '"'
|
||||||
|
push: string
|
||||||
|
- match: \(\*
|
||||||
|
push: comment
|
||||||
|
|
||||||
|
string:
|
||||||
|
- meta_scope: string.quoted.double
|
||||||
|
- match: \\.
|
||||||
|
scope: constant.character.escape
|
||||||
|
- match: '"'
|
||||||
|
pop: true
|
||||||
|
|
||||||
|
comment:
|
||||||
|
- meta_scope: comment
|
||||||
|
- match: \*\)
|
||||||
|
pop: true
|
||||||
Loading…
Add table
Add a link
Reference in a new issue