Skip to main content

aces/
context.rs

1use std::{
2    cmp, fmt,
3    collections::BTreeMap,
4    sync::{Arc, Mutex},
5};
6use crate::{
7    PartialContent, Port, Link, Harc, Fork, Join, Face, ID, NodeID, AtomID, PortID, LinkID, ForkID,
8    JoinID, Semantics, Capacity, Weight, AcesError,
9    name::NameSpace,
10    atom::{AtomSpace, Atom},
11    sat, solver, runner, vis,
12};
13
14/// A handle to a [`Context`] instance.
15///
16/// All [`Context`] handles used in _aces_ have type
17/// `Arc<Mutex<Context>>`.  They are stored permanently in the
18/// following structs: [`CEStructure`], [`sat::Formula`],
19/// [`solver::Solver`], and [`solver::Solution`].
20///
21/// For another way of binding [`Context`] to data see [`Contextual`]
22/// trait and [`InContext`] struct.
23///
24/// [`CEStructure`]: crate::CEStructure
25/// [`sat::Formula`]: crate::sat::Formula
26/// [`solver::Solver`]: crate::solver::Solver
27/// [`solver::Solution`]: crate::solver::Solution
28pub type ContextHandle = Arc<Mutex<Context>>;
29
30/// A representation of shared state.
31///
32/// This is an umbrella type which, currently, includes a collection
33/// of [`Atom`]s, two symbol tables (one for structure names, and
34/// another for node names), node capacities, harc weights, and
35/// [`PartialContent`] of any c-e structure created in this `Context`.
36///
37/// For usage, see [`ContextHandle`] type, [`Contextual`] trait and
38/// [`InContext`] struct.
39///
40/// [`Atom`]: crate::atom::Atom
41#[derive(Debug)]
42pub struct Context {
43    magic_id:     u64, // group ID
44    name_id:      ID,  // given name
45    globals:      NameSpace,
46    nodes:        NameSpace,
47    atoms:        AtomSpace,
48    content:      BTreeMap<ID, PartialContent>,
49    capacities:   BTreeMap<NodeID, Capacity>,
50    weights:      BTreeMap<AtomID, Weight>,
51    solver_props: solver::Props,
52    runner_props: runner::Props,
53    vis_props:    vis::Props,
54}
55
56impl Context {
57    /// Creates a new toplevel `Context` instance and returns the
58    /// corresponding [`ContextHandle`].
59    ///
60    /// Calling this method is the only public way of creating
61    /// toplevel `Context` instances.
62    pub fn new_toplevel<S: AsRef<str>>(name: S) -> ContextHandle {
63        let magic_id = rand::random();
64
65        let mut globals = NameSpace::default();
66        let name_id = globals.share_name(name);
67
68        let ctx = Self {
69            magic_id,
70            name_id,
71            globals,
72            nodes: Default::default(),
73            atoms: Default::default(),
74            content: Default::default(),
75            capacities: Default::default(),
76            weights: Default::default(),
77            solver_props: Default::default(),
78            runner_props: Default::default(),
79            vis_props: Default::default(),
80        };
81
82        Arc::new(Mutex::new(ctx))
83    }
84
85    /// Clears content map, capacities, weights and runtime
86    /// attributes, but doesn't destroy shared resources.
87    ///
88    /// This method preserves the collection of [`Atom`]s, the two
89    /// symbol tables, and the `Context`'s own given name and group
90    /// ID.
91    pub fn reset(&mut self) {
92        // Fields unchanged: magic_id, name_id, globals, nodes, atoms.
93        self.content.clear();
94        self.capacities.clear();
95        self.weights.clear();
96        self.solver_props.clear();
97        self.runner_props.clear();
98        self.vis_props.clear();
99    }
100
101    /// Creates a new derived `Context` instance and returns a
102    /// corresponding [`ContextHandle`].
103    ///
104    /// Calling this method is the only public way of creating derived
105    /// `Context` instances.
106    pub fn new_derived<S: AsRef<str>>(name: S, parent: &ContextHandle) -> ContextHandle {
107        let ctx = {
108            let mut parent = parent.lock().unwrap();
109
110            let magic_id = parent.magic_id;
111
112            // ID of child name in parent and child is the same (but
113            // not in further ancestors).  See `partial_cmp`.
114            let name_id = parent.globals.share_name(name);
115
116            let globals = parent.globals.clone();
117            let nodes = parent.nodes.clone();
118            let atoms = parent.atoms.clone();
119            let content = parent.content.clone();
120            let capacities = parent.capacities.clone();
121            let weights = parent.weights.clone();
122            let solver_props = parent.solver_props.clone();
123            let runner_props = parent.runner_props.clone();
124            let vis_props = parent.vis_props.clone();
125
126            Self {
127                magic_id,
128                name_id,
129                globals,
130                nodes,
131                atoms,
132                content,
133                capacities,
134                weights,
135                solver_props,
136                runner_props,
137                vis_props,
138            }
139        };
140
141        Arc::new(Mutex::new(ctx))
142    }
143
144    pub fn is_toplevel(&self) -> bool {
145        self.name_id == unsafe { ID::new_unchecked(1) }
146    }
147
148    pub fn is_derived(&self) -> bool {
149        self.name_id > unsafe { ID::new_unchecked(1) }
150    }
151
152    pub fn get_name(&self) -> &str {
153        self.globals.get_name(self.name_id).expect("Invalid context.")
154    }
155
156    // Nodes
157
158    // FIXME support node iteration (define a generic NameSpace iterator)
159
160    #[inline]
161    pub fn share_node_name<S: AsRef<str>>(&mut self, node_name: S) -> NodeID {
162        NodeID(self.nodes.share_name(node_name))
163    }
164
165    #[inline]
166    pub fn get_node_name(&self, node_id: NodeID) -> Option<&str> {
167        self.nodes.get_name(node_id.get())
168    }
169
170    #[inline]
171    pub fn get_node_id<S: AsRef<str>>(&self, node_name: S) -> Option<NodeID> {
172        self.nodes.get_id(node_name).map(NodeID)
173    }
174
175    // Atoms
176
177    #[inline]
178    pub(crate) fn get_atom(&self, atom_id: AtomID) -> Option<&Atom> {
179        self.atoms.get_atom(atom_id)
180    }
181
182    #[allow(dead_code)]
183    #[inline]
184    pub(crate) fn get_atom_id(&self, atom: &Atom) -> Option<AtomID> {
185        self.atoms.get_atom_id(atom)
186    }
187
188    #[inline]
189    pub fn is_port(&self, atom_id: AtomID) -> bool {
190        self.atoms.is_port(atom_id)
191    }
192
193    #[inline]
194    pub fn is_link(&self, atom_id: AtomID) -> bool {
195        self.atoms.is_link(atom_id)
196    }
197
198    #[inline]
199    pub fn is_harc(&self, atom_id: AtomID) -> bool {
200        self.atoms.is_harc(atom_id)
201    }
202
203    #[inline]
204    pub fn is_fork(&self, atom_id: AtomID) -> bool {
205        self.atoms.is_fork(atom_id)
206    }
207
208    #[inline]
209    pub fn is_join(&self, atom_id: AtomID) -> bool {
210        self.atoms.is_join(atom_id)
211    }
212
213    #[inline]
214    pub fn share_port(&mut self, port: &mut Port) -> PortID {
215        self.atoms.share_port(port)
216    }
217
218    #[inline]
219    pub fn share_link(&mut self, link: &mut Link) -> LinkID {
220        self.atoms.share_link(link)
221    }
222
223    #[inline]
224    pub fn share_fork(&mut self, fork: &mut Fork) -> ForkID {
225        self.atoms.share_fork(fork)
226    }
227
228    #[inline]
229    pub fn share_join(&mut self, join: &mut Join) -> JoinID {
230        self.atoms.share_join(join)
231    }
232
233    #[inline]
234    pub fn get_port(&self, port_id: PortID) -> Option<&Port> {
235        self.atoms.get_port(port_id)
236    }
237
238    #[inline]
239    pub fn get_link(&self, link_id: LinkID) -> Option<&Link> {
240        self.atoms.get_link(link_id)
241    }
242
243    #[inline]
244    pub fn get_harc(&self, atom_id: AtomID) -> Option<&Harc> {
245        self.atoms.get_harc(atom_id)
246    }
247
248    #[inline]
249    pub fn get_fork(&self, fork_id: ForkID) -> Option<&Fork> {
250        self.atoms.get_fork(fork_id)
251    }
252
253    #[inline]
254    pub fn get_join(&self, join_id: JoinID) -> Option<&Join> {
255        self.atoms.get_join(join_id)
256    }
257
258    #[inline]
259    pub fn get_antiport_id(&self, port_id: PortID) -> Option<PortID> {
260        self.atoms.get_antiport_id(port_id)
261    }
262
263    // Content
264
265    pub fn add_content<S: AsRef<str>>(
266        &mut self,
267        name: S,
268        content: PartialContent,
269    ) -> Option<PartialContent> {
270        let name_id = self.globals.share_name(name);
271
272        self.content.insert(name_id, content)
273    }
274
275    pub fn get_content<S: AsRef<str>>(&self, name: S) -> Option<&PartialContent> {
276        self.globals.get_id(name).and_then(|id| self.content.get(&id))
277    }
278
279    pub fn has_content<S: AsRef<str>>(&self, name: S) -> bool {
280        self.globals.get_id(name).map_or(false, |id| self.content.contains_key(&id))
281    }
282
283    // Capacities
284
285    pub fn set_capacity_by_name<S: AsRef<str>>(
286        &mut self,
287        node_name: S,
288        cap: Capacity,
289    ) -> Option<Capacity> {
290        let node_id = self.share_node_name(node_name.as_ref());
291
292        self.capacities.insert(node_id, cap)
293    }
294
295    #[inline]
296    pub fn get_capacity(&self, node_id: NodeID) -> Capacity {
297        self.capacities.get(&node_id).copied().unwrap_or_else(Capacity::one)
298    }
299
300    // Weights
301
302    pub fn set_weight_by_name<S, I>(
303        &mut self,
304        face: Face,
305        host_name: S,
306        suit_names: I,
307        weight: Weight,
308    ) -> Option<Weight>
309    where
310        S: AsRef<str>,
311        I: IntoIterator,
312        I::Item: AsRef<str>,
313    {
314        let host_id = self.share_node_name(host_name.as_ref());
315        let suit_ids = suit_names.into_iter().map(|n| self.share_node_name(n.as_ref()));
316
317        let atom_id = match face {
318            Face::Tx => {
319                let mut fork = Harc::new_fork(host_id, suit_ids);
320                let fork_id = self.share_fork(&mut fork);
321
322                fork_id.get()
323            }
324            Face::Rx => {
325                let mut join = Harc::new_join(host_id, suit_ids);
326                let join_id = self.share_join(&mut join);
327
328                join_id.get()
329            }
330        };
331
332        self.weights.insert(atom_id, weight)
333    }
334
335    #[inline]
336    pub fn set_inhibitor_by_name<S, I>(
337        &mut self,
338        face: Face,
339        host_name: S,
340        suit_names: I,
341    ) -> Option<Weight>
342    where
343        S: AsRef<str>,
344        I: IntoIterator,
345        I::Item: AsRef<str>,
346    {
347        self.set_weight_by_name(face, host_name, suit_names, Weight::omega())
348    }
349
350    #[inline]
351    pub fn set_holder_by_name<S, I>(
352        &mut self,
353        face: Face,
354        host_name: S,
355        suit_names: I,
356    ) -> Option<Weight>
357    where
358        S: AsRef<str>,
359        I: IntoIterator,
360        I::Item: AsRef<str>,
361    {
362        self.set_weight_by_name(face, host_name, suit_names, Weight::zero())
363    }
364
365    #[inline]
366    pub fn get_weight(&self, atom_id: AtomID) -> Weight {
367        self.weights.get(&atom_id).copied().unwrap_or_else(Weight::one)
368    }
369
370    // Solver props
371
372    pub fn set_encoding(&mut self, encoding: sat::Encoding) {
373        self.solver_props.sat_encoding = Some(encoding);
374    }
375
376    pub fn get_encoding(&self) -> Option<sat::Encoding> {
377        self.solver_props.sat_encoding
378    }
379
380    pub fn set_search(&mut self, search: sat::Search) {
381        self.solver_props.sat_search = Some(search);
382    }
383
384    pub fn get_search(&self) -> Option<sat::Search> {
385        self.solver_props.sat_search
386    }
387
388    // Runner props
389
390    pub fn set_semantics(&mut self, semantics: Semantics) {
391        self.runner_props.semantics = Some(semantics);
392    }
393
394    pub fn get_semantics(&self) -> Option<Semantics> {
395        self.runner_props.semantics
396    }
397
398    pub fn set_max_steps(&mut self, max_steps: usize) {
399        self.runner_props.max_steps = Some(max_steps);
400    }
401
402    pub fn get_max_steps(&self) -> Option<usize> {
403        self.runner_props.max_steps
404    }
405
406    // Vis props
407
408    pub fn set_title<S: AsRef<str>>(&mut self, title: S) {
409        self.vis_props.title = Some(title.as_ref().to_owned());
410    }
411
412    pub fn get_title(&self) -> Option<&str> {
413        self.vis_props.title.as_deref()
414    }
415
416    pub fn set_label<S: AsRef<str>>(&mut self, node_id: NodeID, label: S) {
417        self.vis_props.labels.insert(node_id, label.as_ref().to_owned());
418    }
419
420    pub fn get_label(&self, node_id: NodeID) -> Option<&str> {
421        self.vis_props.labels.get(&node_id).map(|t| t.as_str())
422    }
423}
424
425impl PartialEq for Context {
426    fn eq(&self, other: &Self) -> bool {
427        self.magic_id == other.magic_id && self.name_id == other.name_id
428    }
429}
430
431impl Eq for Context {}
432
433// Ancestors are _greater_ than their offspring.
434impl PartialOrd for Context {
435    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
436        if self.magic_id == other.magic_id {
437            if other.globals.get_id(self.get_name()) == Some(self.name_id) {
438                // Since ID of self's name in self and other is the
439                // same, self is either an ancestor of other, or
440                // a direct child of other, or equals the other.
441                Some(other.name_id.cmp(&self.name_id))
442            } else if self.globals.get_id(other.get_name()) == Some(other.name_id) {
443                // ID of other's name in self and other is the same...
444                match other.name_id.cmp(&self.name_id) {
445                    // ...other is an ancestor of self,
446                    cmp::Ordering::Less => Some(cmp::Ordering::Less),
447                    // ...self is the parent of other: this case
448                    // should have already been covered,
449                    cmp::Ordering::Greater => unreachable!(),
450                    // ...they are equal: this case should have
451                    // already been covered.
452                    cmp::Ordering::Equal => unreachable!(),
453                }
454            } else if self.name_id == other.name_id {
455                // This case should have already been covered.
456                unreachable!()
457            } else {
458                None
459            }
460        } else {
461            None
462        }
463    }
464}
465
466/// A trait for binding objects to [`Context`] temporarily, without
467/// permanently storing (and synchronizing) context references inside
468/// the objects.
469///
470/// See [`InContext`] for more details.
471pub trait Contextual: Sized {
472    fn format(&self, ctx: &ContextHandle) -> Result<String, AcesError>;
473
474    #[inline]
475    fn with(&self, ctx: &ContextHandle) -> InContext<Self> {
476        InContext { context: ctx.clone(), thing: self }
477    }
478
479    #[inline]
480    fn with_mut(&mut self, ctx: &ContextHandle) -> InContextMut<Self> {
481        InContextMut { context: ctx.clone(), thing: self }
482    }
483}
484
485/// A version of the [`Contextual`] trait to be used for fine-grained
486/// access to [`Context`].
487pub trait ExclusivelyContextual: Sized {
488    fn format_locked(&self, ctx: &Context) -> Result<String, AcesError>;
489}
490
491impl<T: ExclusivelyContextual> Contextual for T {
492    #[inline]
493    fn format(&self, ctx: &ContextHandle) -> Result<String, AcesError> {
494        self.format_locked(&ctx.lock().unwrap())
495    }
496}
497
498impl<T: ExclusivelyContextual> Contextual for Vec<T> {
499    fn format(&self, ctx: &ContextHandle) -> Result<String, AcesError> {
500        let mut elts = self.iter();
501
502        if let Some(elt) = elts.next() {
503            let ctx = ctx.lock().unwrap();
504            let mut elts_repr = elt.format_locked(&ctx)?;
505
506            for elt in elts {
507                elts_repr.push_str(&format!(", {}", elt.format_locked(&ctx)?));
508            }
509
510            Ok(format!("{{{}}}", elts_repr))
511        } else {
512            Ok("{{}}".to_owned())
513        }
514    }
515}
516
517/// A short-term binding of [`Context`] and any immutable object of a
518/// type that implements the [`Contextual`] trait.
519///
520/// The purpose of this type is to allow a transparent read access to
521/// shared data, like names etc.  To prevent coarse-grained locking,
522/// [`Context`] is internally represented by a [`ContextHandle`];
523/// however, [`Context`] can't be modified through `InContext`.
524pub struct InContext<'a, D: Contextual> {
525    context: ContextHandle,
526    thing:   &'a D,
527}
528
529impl<D: Contextual> InContext<'_, D> {
530    pub fn using_context<T, F>(&self, f: F) -> T
531    where
532        F: FnOnce(&D, &Context) -> T,
533    {
534        let ctx = self.context.lock().unwrap();
535
536        f(self.thing, &ctx)
537    }
538
539    #[inline]
540    pub fn same_context(&self, other: &Self) -> bool {
541        Arc::ptr_eq(&self.context, &other.context)
542    }
543
544    #[inline]
545    pub fn get_context(&self) -> &ContextHandle {
546        &self.context
547    }
548
549    #[inline]
550    pub fn get_thing(&self) -> &D {
551        self.thing
552    }
553}
554
555impl<'a, D: Contextual> fmt::Display for InContext<'a, D> {
556    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
557        write!(f, "{}", self.thing.format(&self.context).expect("Can't display"))
558    }
559}
560
561/// A short-term binding of [`Context`] and any mutable object of a
562/// type that implements the [`Contextual`] trait.
563///
564/// The purpose of this type is to allow a transparent read access to
565/// shared data, like names etc.  To prevent coarse-grained locking,
566/// [`Context`] is internally represented by a [`ContextHandle`];
567/// however, [`Context`] can't be modified through `InContextMut`.
568pub struct InContextMut<'a, D: Contextual> {
569    context: ContextHandle,
570    thing:   &'a mut D,
571}
572
573impl<D: Contextual> InContextMut<'_, D> {
574    pub fn using_context<T, F>(&mut self, f: F) -> T
575    where
576        F: FnOnce(&mut D, &Context) -> T,
577    {
578        let ctx = self.context.lock().unwrap();
579
580        f(self.thing, &ctx)
581    }
582
583    #[inline]
584    pub fn same_context(&self, other: &Self) -> bool {
585        Arc::ptr_eq(&self.context, &other.context)
586    }
587
588    #[inline]
589    pub fn get_context(&self) -> &ContextHandle {
590        &self.context
591    }
592
593    #[inline]
594    pub fn get_thing(&self) -> &D {
595        self.thing
596    }
597
598    #[inline]
599    pub fn get_thing_mut(&mut self) -> &mut D {
600        self.thing
601    }
602}
603
604impl<'a, D: Contextual> fmt::Display for InContextMut<'a, D> {
605    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
606        write!(f, "{}", self.thing.format(&self.context).expect("Can't display"))
607    }
608}
609
610#[cfg(test)]
611mod tests {
612    use super::*;
613
614    fn new_port(ctx: &ContextHandle, face: Face, host_name: &str) -> (Port, PortID) {
615        let mut ctx = ctx.lock().unwrap();
616        let node_id = ctx.share_node_name(host_name);
617        let mut port = Port::new(face, node_id);
618        let port_id = ctx.share_port(&mut port);
619
620        (port, port_id)
621    }
622
623    #[test]
624    fn test_partial_order() {
625        let toplevel = Context::new_toplevel("toplevel");
626        let derived = Context::new_derived("derived", &toplevel);
627
628        assert_eq!(
629            toplevel.lock().unwrap().partial_cmp(&derived.lock().unwrap()),
630            Some(cmp::Ordering::Greater)
631        );
632    }
633
634    #[test]
635    fn test_derivation() {
636        let toplevel = Context::new_toplevel("toplevel");
637        let (a_port, a_port_id) = new_port(&toplevel, Face::Tx, "a");
638
639        {
640            let derived = Context::new_derived("derived", &toplevel);
641            let (_, z_port_id) = new_port(&derived, Face::Rx, "z");
642
643            assert_eq!(derived.lock().unwrap().get_port(a_port_id), Some(&a_port));
644            assert_eq!(toplevel.lock().unwrap().get_port(z_port_id), None);
645        }
646
647        let (b_port, b_port_id) = new_port(&toplevel, Face::Tx, "b");
648
649        assert_eq!(toplevel.lock().unwrap().get_port(b_port_id), Some(&b_port));
650    }
651}