Skip to main content

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}