scan_core/channel_system.rs
1//! Implementation of the CS model of computation.
2//!
3//! Channel systems comprises multiple program graphs executing asynchronously
4//! while sending and retrieving messages from channels.
5//!
6//! A channel system is given by:
7//!
8//! - A finite set of PGs.
9//! - A finite set of channels, each of which has:
10//! - a given type;
11//! - a FIFO queue that can contain values of the channel's type;
12//! - a queue capacity limit: from zero (handshake communication) to infinite.
13//! - Some PG actions are communication actions:
14//! - `send` actions push the computed value of an expression to the rear of the channel queue;
15//! - `receive` actions pop the value in front of the channel queue and write it onto a given PG variable;
16//! - `probe_empty_queue` actions can only be executed if the given channel has an empty queue;
17//! - `probe_full_queue` actions can only be executed if the given channel has a full queue;
18//!
19//! Analogously to PGs, a CS is defined through a [`ChannelSystemBuilder`],
20//! by adding new PGs and channels.
21//! Each PG in the CS can be given new locations, actions, effects, guards and transitions.
22//! Then, a [`ChannelSystem`] is built from the [`ChannelSystemBuilder`]
23//! and can be executed by performing transitions,
24//! though the definition of the CS itself can no longer be altered.
25//!
26//! ```
27//! # use scan_core::*;
28//! # use scan_core::channel_system::*;
29//! // Create a new CS builder
30//! let mut cs_builder = ChannelSystemBuilder::new();
31//!
32//! // Add a new PG to the CS
33//! let pg_1 = cs_builder.new_program_graph();
34//!
35//! // Get initial location of pg_1
36//! let initial_1 = cs_builder
37//! .new_initial_location(pg_1)
38//! .expect("every PG has an initial location");
39//!
40//! // Create new channel
41//! let chn = cs_builder.new_channel(vec![Type::Integer], Some(1));
42//!
43//! // Create new send communication action
44//! let send = cs_builder
45//! .new_send(pg_1, chn, vec![CsExpression::from(1i64)])
46//! .expect("always possible to add new actions");
47//!
48//! // Add transition sending a message to the channel
49//! cs_builder.add_transition(pg_1, initial_1, send, initial_1, None)
50//! .expect("transition is well-defined");
51//!
52//! // Add a new PG to the CS
53//! let pg_2 = cs_builder.new_program_graph();
54//!
55//! // Get initial location of pg_2
56//! let initial_2 = cs_builder
57//! .new_initial_location(pg_2)
58//! .expect("every PG has an initial location");
59//!
60//! // Add new variable to pg_2
61//! let var = cs_builder
62//! .new_var(pg_2, Val::from(0i64))
63//! .expect("always possible to add new variable");
64//!
65//! // Create new receive communication action
66//! let receive = cs_builder
67//! .new_receive(pg_2, chn, vec![var])
68//! .expect("always possible to add new actions");
69//!
70//! // Add transition sending a message to the channel
71//! cs_builder.add_transition(pg_2, initial_2, receive, initial_2, None)
72//! .expect("transition is well-defined");
73//!
74//! // Build the CS from its builder
75//! // The builder is always guaranteed to build a well-defined CS and building cannot fail
76//! let cs = cs_builder.build();
77//! let mut instance = cs.new_instance();
78//!
79//! // Since the channel is empty, only pg_1 can transition (with send)
80//! {
81//! let mut iter = instance.possible_transitions();
82//! let (pg, action, mut trans) = iter.next().unwrap();
83//! assert_eq!(pg, pg_1);
84//! assert_eq!(action, send);
85//! let post_locs: Vec<Location> = trans.next().unwrap().collect();
86//! assert_eq!(post_locs, vec![initial_1]);
87//! assert!(iter.next().is_none());
88//! }
89//!
90//! // Perform the transition, which sends a value to the channel queue
91//! // After this, the channel is full
92//! instance.transition(pg_1, send, &[initial_1])
93//! .expect("transition is possible");
94//!
95//! // Since the channel is now full, only pg_2 can transition (with receive)
96//! {
97//! let mut iter = instance.possible_transitions();
98//! let (pg, action, mut trans) = iter.next().unwrap();
99//! assert_eq!(pg, pg_2);
100//! assert_eq!(action, receive);
101//! let post_locs: Vec<Location> = trans.next().unwrap().collect();
102//! assert_eq!(post_locs, vec![initial_2]);
103//! assert!(iter.next().is_none());
104//! }
105//!
106//! // Perform the transition, which receives a value to the channel queue
107//! // After this, the channel is empty
108//! instance.transition(pg_2, receive, &[initial_2])
109//! .expect("transition is possible");
110//! ```
111
112mod builder;
113mod run;
114
115use crate::grammar::*;
116use crate::program_graph::{
117 Action as PgAction, Clock as PgClock, Location as PgLocation, Var as PgVar, *,
118};
119pub use builder::*;
120use get_size2::GetSize;
121pub use run::ChannelSystemRun;
122use thiserror::Error;
123
124type PgIndex = u16;
125
126/// An indexing object for PGs in a CS.
127///
128/// These cannot be directly created or manipulated,
129/// but have to be generated and/or provided by a [`ChannelSystemBuilder`] or [`ChannelSystem`].
130#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, GetSize)]
131pub struct PgId(PgIndex);
132
133impl From<PgId> for PgIndex {
134 #[inline]
135 fn from(val: PgId) -> Self {
136 val.0
137 }
138}
139
140/// An indexing object for channels in a CS.
141///
142/// These cannot be directly created or manipulated,
143/// but have to be generated and/or provided by a [`ChannelSystemBuilder`] or [`ChannelSystem`].
144#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, GetSize)]
145pub struct Channel(u16);
146
147impl From<Channel> for u16 {
148 #[inline]
149 fn from(val: Channel) -> Self {
150 val.0
151 }
152}
153
154/// An indexing object for locations in a CS.
155///
156/// These cannot be directly created or manipulated,
157/// but have to be generated and/or provided by a [`ChannelSystemBuilder`] or [`ChannelSystem`].
158#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
159pub struct Location(PgId, PgLocation);
160
161/// An indexing object for actions in a CS.
162///
163/// These cannot be directly created or manipulated,
164/// but have to be generated and/or provided by a [`ChannelSystemBuilder`] or [`ChannelSystem`].
165#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
166pub struct Action(PgId, PgAction);
167
168/// An indexing object for typed variables in a CS.
169///
170/// These cannot be directly created or manipulated,
171/// but have to be generated and/or provided by a [`ChannelSystemBuilder`] or [`ChannelSystem`].
172#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
173pub struct Var(PgId, PgVar);
174
175/// An indexing object for clocks in a CS.
176///
177/// These cannot be directly created or manipulated,
178/// but have to be generated and/or provided by a [`ChannelSystemBuilder`] or [`ChannelSystem`].
179///
180/// See also [`PgClock`].
181#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, GetSize)]
182pub struct Clock(PgId, PgClock);
183
184/// A message to be sent through a CS's channel.
185#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, GetSize)]
186pub enum Message {
187 /// Sending the computed value of an expression to a channel.
188 Send,
189 /// Retrieving a value out of a channel and associating it to a variable.
190 Receive,
191 /// Checking whether a channel is empty.
192 ProbeEmptyQueue,
193 /// Checking whether a channel is full.
194 ProbeFullQueue,
195}
196
197/// The error type for operations with [`ChannelSystemBuilder`]s and [`ChannelSystem`]s.
198#[derive(Debug, Clone, Copy, Error)]
199pub enum CsError {
200 /// A PG within the CS returned an error of its own.
201 #[error("error from program graph {0:?}")]
202 ProgramGraph(PgId, #[source] PgError),
203 /// There is no such PG in the CS.
204 #[error("program graph {0:?} does not belong to the channel system")]
205 MissingPg(PgId),
206 /// The channel is at full capacity and can accept no more incoming messages.
207 #[error("channel {0:?} is at full capacity")]
208 OutOfCapacity(Channel),
209 /// Channel is not full
210 #[error("the channel still has free space {0:?}")]
211 NotFull(Channel),
212 /// The channel is empty and there is no message to be retrieved.
213 #[error("channel {0:?} is empty")]
214 Empty(Channel),
215 /// The channel is not empty.
216 #[error("channel {0:?} is not empty")]
217 NotEmpty(Channel),
218 /// There is no such communication action in the CS.
219 #[error("communication {0:?} has not been defined")]
220 NoCommunication(Action),
221 /// The action does not belong to the PG.
222 #[error("action {0:?} does not belong to program graph {1:?}")]
223 ActionNotInPg(Action, PgId),
224 /// The variable does not belong to the PG.
225 #[error("variable {0:?} does not belong to program graph {1:?}")]
226 VarNotInPg(Var, PgId),
227 /// The location does not belong to the PG.
228 #[error("location {0:?} does not belong to program graph {1:?}")]
229 LocationNotInPg(Location, PgId),
230 /// The clock does not belong to the PG.
231 #[error("clock {0:?} does not belong to program graph {1:?}")]
232 ClockNotInPg(Clock, PgId),
233 /// The given PGs do not match.
234 #[error("program graphs {0:?} and {1:?} do not match")]
235 DifferentPgs(PgId, PgId),
236 /// Action is a communication.
237 ///
238 /// Is returned when trying to associate an effect to a communication action.
239 #[error("action {0:?} is a communication")]
240 ActionIsCommunication(Action),
241 /// There is no such channel in the CS.
242 #[error("channel {0:?} does not exists")]
243 MissingChannel(Channel),
244 /// Cannot probe an handshake channel
245 #[error("cannot probe handshake {0:?}")]
246 ProbingHandshakeChannel(Channel),
247 /// Cannot probe for fullness an infinite capacity channel
248 #[error("cannot probe for fullness the infinite capacity {0:?}")]
249 ProbingInfiniteQueue(Channel),
250 /// A type error
251 #[error("type error")]
252 Type(#[source] TypeError),
253}
254
255/// A Channel System event related to a channel.
256#[derive(Debug, Clone, PartialEq, GetSize)]
257pub struct Event {
258 /// The PG producing the event in the course of a transition.
259 pub pg_id: PgId,
260 /// The channel involved in the event.
261 pub channel: Channel,
262 /// The type of event produced.
263 pub event_type: EventType,
264}
265
266/// A Channel System event type related to a channel.
267#[derive(Debug, Clone, PartialEq, GetSize)]
268pub enum EventType {
269 /// Sending a value to a channel.
270 Send(Vec<Val>),
271 /// Retrieving a value out of a channel.
272 Receive(Vec<Val>),
273 /// Checking whether a channel is empty.
274 ProbeEmptyQueue,
275 /// Checking whether a channel is full.
276 ProbeFullQueue,
277}
278
279/// The capacity type of a channel:
280#[derive(Debug, Clone, Copy, GetSize)]
281pub enum ChannelCapacity {
282 /// A (in)finite-capacity FIFO queue.
283 Queue(Option<usize>),
284 /// A channel that receives messages but never returns them.
285 Sink,
286}
287
288/// A definition object for a CS.
289/// It represents the abstract definition of a CS.
290///
291/// The only way to produce a [`ChannelSystem`] is through a [`ChannelSystemBuilder`].
292/// This guarantees that there are no type errors involved in the definition of its PGs,
293/// and thus the CS will always be in a consistent state.
294///
295/// The only way to execute the [`ChannelSystem`] is to generate a new [`ChannelSystemRun`] through [`ChannelSystem::new_instance`].
296/// The [`ChannelSystemRun`] cannot outlive its [`ChannelSystem`], as it holds references to it.
297/// This allows to cheaply generate multiple [`ChannelSystemRun`]s from the same [`ChannelSystem`].
298///
299/// Example:
300///
301/// ```
302/// # use scan_core::channel_system::ChannelSystemBuilder;
303/// // Create and populate a CS builder object
304/// let mut cs_builder = ChannelSystemBuilder::new();
305/// let pg_id = cs_builder.new_program_graph();
306/// let initial = cs_builder.new_initial_location(pg_id).expect("create new location");
307/// cs_builder.add_autonomous_transition(pg_id, initial, initial, None).expect("add transition");
308///
309/// // Build the builder object to get a CS definition object.
310/// let cs_def = cs_builder.build();
311///
312/// // Instantiate a CS with the previously built definition.
313/// let mut cs = cs_def.new_instance();
314///
315/// // Perform the (unique) active transition available.
316/// let (pg_id_trans, e, mut post_locs) = cs.possible_transitions().last().expect("autonomous transition");
317/// assert_eq!(pg_id_trans, pg_id);
318/// let post_loc = post_locs.last().expect("post location").last().expect("post location");
319/// assert_eq!(post_loc, initial);
320/// cs.transition(pg_id, e, &[initial]).expect("transition is active");
321/// ```
322#[derive(Debug, Clone, GetSize)]
323pub struct ChannelSystem {
324 channels: Vec<(Vec<Type>, ChannelCapacity)>,
325 communications: Vec<Option<(Channel, Message)>>,
326 communications_pg_idxs: Vec<usize>,
327 program_graphs: Vec<ProgramGraph>,
328}
329
330impl ChannelSystem {
331 /// Creates a new [`ChannelSystemRun`] which allows to execute the CS as defined.
332 ///
333 /// The new instance borrows the caller to refer to the CS definition without copying its data,
334 /// so that spawning instances is (relatively) inexpensive.
335 ///
336 /// See also [`ProgramGraph::new_instance`].
337 pub fn new_instance<'def>(&'def self) -> ChannelSystemRun<'def> {
338 ChannelSystemRun::new(self)
339 }
340
341 #[inline]
342 fn communication(&self, pg_id: PgId, pg_action: PgAction) -> Option<(Channel, Message)> {
343 if pg_action == EPSILON {
344 None
345 } else {
346 let start = self.communications_pg_idxs[pg_id.0 as usize];
347 self.communications[start + ActionIdx::from(pg_action) as usize]
348 }
349 }
350
351 /// Returns an immutable reference to the list of [`ProgramGraph`]s of the Channel System.
352 #[inline]
353 pub fn program_graphs(&self) -> &[ProgramGraph] {
354 &self.program_graphs
355 }
356
357 /// Returns the list of [`PgId`]s associated to the Program Graphs of the Channel System.
358 #[inline]
359 pub fn program_graph_ids(&self) -> impl Iterator<Item = PgId> {
360 (0..self.program_graphs.len()).map(|idx| PgId(idx as PgIndex))
361 }
362
363 /// Returns the list of defined channels, given as the pair of their type and capacity
364 /// (where `None` denotes channels with infinite capacity, and `Some` denotes channels with finite capacity).
365 #[inline]
366 pub fn channels(&self) -> &[(Vec<Type>, ChannelCapacity)] {
367 &self.channels
368 }
369
370 /// Returns the type and capacity of the given channel
371 /// (where `None` denotes channels with infinite capacity, and `Some` denotes channels with finite capacity).
372 #[inline]
373 pub fn channel(&self, channel: Channel) -> Result<(&[Type], ChannelCapacity), CsError> {
374 self.channels
375 .get(channel.0 as usize)
376 .map(|(types, cap)| (types.as_slice(), *cap))
377 .ok_or(CsError::MissingChannel(channel))
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn builder() {
387 let _cs: ChannelSystemBuilder = ChannelSystemBuilder::new();
388 }
389
390 #[test]
391 fn new_pg() {
392 let mut cs = ChannelSystemBuilder::new();
393 let _ = cs.new_program_graph();
394 }
395
396 #[test]
397 fn new_action() -> Result<(), CsError> {
398 let mut cs = ChannelSystemBuilder::new();
399 let pg = cs.new_program_graph();
400 let _action = cs.new_action(pg)?;
401 Ok(())
402 }
403
404 #[test]
405 fn new_var() -> Result<(), CsError> {
406 let mut cs = ChannelSystemBuilder::new();
407 let pg = cs.new_program_graph();
408 let _var1 = cs.new_var(pg, Val::from(false))?;
409 let _var2 = cs.new_var(pg, Val::from(0i64))?;
410 Ok(())
411 }
412
413 #[test]
414 fn add_effect() -> Result<(), CsError> {
415 let mut cs = ChannelSystemBuilder::new();
416 let pg = cs.new_program_graph();
417 let action = cs.new_action(pg)?;
418 let var1 = cs.new_var(pg, Val::from(false))?;
419 let var2 = cs.new_var(pg, Val::from(0i64))?;
420 let effect_1 = CsExpression::from(2i64);
421 cs.add_effect(pg, action, var1, effect_1.clone())
422 .expect_err("type mismatch");
423 let effect_2 = CsExpression::from(true);
424 cs.add_effect(pg, action, var1, effect_2.clone())?;
425 cs.add_effect(pg, action, var2, effect_2)
426 .expect_err("type mismatch");
427 cs.add_effect(pg, action, var2, effect_1)?;
428 Ok(())
429 }
430
431 #[test]
432 fn new_location() -> Result<(), CsError> {
433 let mut cs = ChannelSystemBuilder::new();
434 let pg = cs.new_program_graph();
435 let initial = cs.new_initial_location(pg)?;
436 let location = cs.new_location(pg)?;
437 assert_ne!(initial, location);
438 Ok(())
439 }
440
441 #[test]
442 fn add_transition() -> Result<(), CsError> {
443 let mut cs = ChannelSystemBuilder::new();
444 let pg = cs.new_program_graph();
445 let initial = cs.new_initial_location(pg)?;
446 let action = cs.new_action(pg)?;
447 let var1 = cs.new_var(pg, Val::from(false))?;
448 let var2 = cs.new_var(pg, Val::from(0i64))?;
449 let effect_1 = CsExpression::from(0i64);
450 let effect_2 = CsExpression::from(true);
451 cs.add_effect(pg, action, var1, effect_2)?;
452 cs.add_effect(pg, action, var2, effect_1)?;
453 let post = cs.new_location(pg)?;
454 cs.add_transition(pg, initial, action, post, None)?;
455 Ok(())
456 }
457
458 #[test]
459 fn add_communication() -> Result<(), CsError> {
460 let mut cs = ChannelSystemBuilder::new();
461 let ch = cs.new_channel(vec![Type::Boolean], Some(1));
462
463 let pg1 = cs.new_program_graph();
464 let initial1 = cs.new_initial_location(pg1)?;
465 let post1 = cs.new_location(pg1)?;
466 let effect = CsExpression::from(true);
467 let send = cs.new_send(pg1, ch, vec![effect.clone()])?;
468 let _ = cs.new_send(pg1, ch, vec![effect])?;
469 cs.add_transition(pg1, initial1, send, post1, None)?;
470
471 let var1 = cs.new_var(pg1, Val::from(0i64))?;
472 let effect = CsExpression::from(0i64);
473 cs.add_effect(pg1, send, var1, effect)
474 .expect_err("send is a message so it cannot have effects");
475
476 let pg2 = cs.new_program_graph();
477 let initial2 = cs.new_initial_location(pg2)?;
478 let post2 = cs.new_location(pg2)?;
479 let var2 = cs.new_var(pg2, Val::from(false))?;
480 let receive = cs.new_receive(pg2, ch, vec![var2])?;
481 let _ = cs.new_receive(pg2, ch, vec![var2])?;
482 let _ = cs.new_receive(pg2, ch, vec![var2])?;
483 cs.add_transition(pg2, initial2, receive, post2, None)?;
484
485 let cs_def = cs.build();
486 let mut cs = cs_def.new_instance();
487 // assert_eq!(cs.possible_transitions().count(), 1);
488 assert_eq!(cs.def().communications_pg_idxs, vec![0, 2, 5]);
489
490 cs.transition(pg1, send, &[post1])?;
491 cs.transition(pg2, receive, &[post2])?;
492 // assert_eq!(cs.possible_transitions().count(), 0);
493 Ok(())
494 }
495}