Skip to main content

tract_core/
plan.rs

1use std::borrow::Borrow;
2use std::fmt::{Debug, Display};
3
4use multithread::Executor;
5
6use crate::internal::*;
7use crate::model::{Fact, Graph, OutletId};
8use crate::ops::konst::Const;
9use crate::runtime::RunOptions;
10
11use self::order::{build_flush_list, eval_order_for_nodes, eval_order_opt_ram_for_nodes};
12
13/// Identifies one running state, so an op can key resources it manages itself
14/// per session and per node. Unique for the process; a state gets one at
15/// construction and keeps it until it is dropped, at which point the plan calls
16/// [`EvalOp::drop_session`] on every node.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub struct SessionId(u64);
19
20static NEXT_SESSION_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
21
22impl SessionId {
23    /// For evaluating an op outside any plan -- const folding, shape inference.
24    /// No state is ever built against it, so nothing keys scratch on it.
25    pub const NONE: SessionId = SessionId(u64::MAX);
26
27    fn next() -> SessionId {
28        SessionId(NEXT_SESSION_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
29    }
30}
31
32/// Where one stream's state lives inside a state shared by several streams. An
33/// index into the lane axis the state's per-lane buffers are sized at.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
35pub struct LaneId(pub usize);
36
37/// Which lane each seat of a turn's batch carries: the index is the seat, the
38/// value is the lane. `max_lanes` is the extent of the lane axis, fixed for the
39/// life of the state, so an op sizing a buffer on first eval knows how wide to
40/// make it -- a turn seating one lane of many still needs the full width.
41///
42/// A lane appears at most once, which is what makes a stream's state sequential:
43/// two seats of one turn cannot both advance the same lane. A model with no
44/// session-scoped state has nothing to address, so its seating is inert.
45///
46/// Seats and lanes both index axis 0 -- of the turn's tensors and of a laned
47/// state's buffers respectively. A runtime seating more than one lane is what
48/// must have checked that axis 0 of every stateful node is the model's batch
49/// axis; ops only assert that their input carries one stream per seat.
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct Seating {
52    lanes: Vec<LaneId>,
53    max_lanes: usize,
54}
55
56impl Seating {
57    pub fn new(max_lanes: usize, lanes: impl IntoIterator<Item = LaneId>) -> TractResult<Seating> {
58        let lanes: Vec<LaneId> = lanes.into_iter().collect();
59        for (seat, lane) in lanes.iter().enumerate() {
60            ensure!(lane.0 < max_lanes, "Seat {seat} takes lane {} of {max_lanes}", lane.0);
61            ensure!(!lanes[..seat].contains(lane), "Lane {} takes two seats in one turn", lane.0);
62        }
63        Ok(Seating { lanes, max_lanes })
64    }
65
66    /// The seating of a state one stream owns: one lane wide, that lane seated.
67    /// Every turn has a seating, and this is what an unbatched turn is.
68    pub fn single() -> Seating {
69        Seating { lanes: vec![LaneId(0)], max_lanes: 1 }
70    }
71
72    pub fn max_lanes(&self) -> usize {
73        self.max_lanes
74    }
75
76    pub fn lanes(&self) -> &[LaneId] {
77        &self.lanes
78    }
79
80    /// Seats filled this turn, i.e. the extent of the batch axis of the tensors
81    /// flowing through it.
82    pub fn occupancy(&self) -> usize {
83        self.lanes.len()
84    }
85
86    /// Where seat `ix` reads its stream and writes its state: the seat on axis 0
87    /// of the turn's tensors, the lane on axis 0 of the state's buffers. Both are
88    /// absent when the state is one lane wide, as its buffers then have no lane
89    /// axis and axis 0 of the tensors carries data rather than streams.
90    pub fn address(&self, ix: usize) -> (Option<usize>, Option<usize>) {
91        if self.max_lanes == 1 { (None, None) } else { (Some(ix), Some(self.lanes[ix].0)) }
92    }
93}
94
95/// Resources a [`TurnStateHandler`] installs for the turn and ops read while
96/// evaluating, keyed by the stored type. This is the extension point: ops see it
97/// read-only, only a handler mutates it.
98pub type TurnShared = anymap3::Map<dyn std::any::Any + Send>;
99
100/// Everything an op is given about where and when it is being evaluated. Ops
101/// receive it by shared reference: they can read the turn's symbols and the
102/// installed shared resources, and they can identify themselves with
103/// `(session, node_id)`, but they cannot reach another node's values or rebind a
104/// symbol.
105#[derive(Debug, Clone, Copy)]
106pub struct EvalContext<'a> {
107    pub session: SessionId,
108    pub node_id: usize,
109    pub symbols: &'a SymbolValues,
110    pub scenario: Option<usize>,
111    pub shared: Option<&'a TurnShared>,
112    /// Which lane each seat of this turn carries. A turn one stream owns is
113    /// [`Seating::single`], so an op reads the same field either way.
114    pub seating: &'a Seating,
115}
116
117pub struct TurnState {
118    pub resolved_symbols: SymbolValues,
119    pub scenario: Option<usize>,
120    pub values: Vec<Option<TVec<TValue>>>,
121    /// Resources installed for the turn by a [`TurnStateHandler`], reachable by
122    /// ops through [`EvalContext::shared`]. Entries outlive the turn --
123    /// `reset_turn` does not touch them -- so what a handler installs is reused
124    /// turn after turn unless it drops it in `after_plan_eval`.
125    pub shared: TurnShared,
126    /// Which lane each seat of this turn carries. A laned runtime sets it before
127    /// each turn; a state one stream owns keeps [`Seating::single`].
128    pub seating: Seating,
129}
130
131impl EvalContext<'static> {
132    /// Context for evaluating outside any plan -- const folding and shape
133    /// inference. What [`EvalOp::eval_out_of_plan`] hands the op: no symbol is
134    /// bound and no shared resource is reachable.
135    pub fn out_of_plan() -> EvalContext<'static> {
136        static SYMBOLS: std::sync::OnceLock<SymbolValues> = std::sync::OnceLock::new();
137        static SEATING: std::sync::OnceLock<Seating> = std::sync::OnceLock::new();
138        EvalContext {
139            session: SessionId::NONE,
140            node_id: usize::MAX,
141            symbols: SYMBOLS.get_or_init(SymbolValues::default),
142            scenario: None,
143            shared: None,
144            seating: SEATING.get_or_init(Seating::single),
145        }
146    }
147}
148
149impl TurnState {
150    pub fn context(&self, session: SessionId, node_id: usize) -> EvalContext<'_> {
151        EvalContext {
152            session,
153            node_id,
154            symbols: &self.resolved_symbols,
155            scenario: self.scenario,
156            shared: Some(&self.shared),
157            seating: &self.seating,
158        }
159    }
160}
161
162impl Default for TurnState {
163    fn default() -> Self {
164        TurnState {
165            resolved_symbols: SymbolValues::default(),
166            scenario: None,
167            values: vec![],
168            shared: TurnShared::new(),
169            seating: Seating::single(),
170        }
171    }
172}
173
174impl Clone for TurnState {
175    fn clone(&self) -> Self {
176        TurnState {
177            resolved_symbols: self.resolved_symbols.clone(),
178            scenario: self.scenario,
179            values: vec![],
180            shared: TurnShared::new(),
181            seating: self.seating.clone(),
182        }
183    }
184}
185
186pub trait TurnStateHandler: Send + Sync + Debug {
187    fn before_plan_eval(&self, turn: &mut TurnState) -> TractResult<()>;
188    fn after_plan_eval(&self, turn: &mut TurnState) -> TractResult<()>;
189}
190
191impl Debug for TurnState {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        write!(f, "TurnState({:?})", self.resolved_symbols)
194    }
195}
196
197#[derive(Debug, Clone)]
198pub struct SimplePlan<F, O>
199where
200    F: Fact + Clone + 'static,
201    O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
202{
203    pub(crate) model: Arc<Graph<F, O>>,
204    outputs: Vec<OutletId>,
205    order: Vec<usize>,
206    flush_lists: Vec<TVec<usize>>,
207    has_unresolved_symbols: bool,
208    symbols: Vec<Symbol>,
209    executor: Option<Executor>,
210    turn_handler: Option<Arc<dyn TurnStateHandler + 'static>>,
211}
212
213impl<F, O> SimplePlan<F, O>
214where
215    F: Fact + Clone + 'static,
216    O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
217{
218    /// This contructor returns a plan that will compute all the model default outputs in one pass.
219    pub fn new(model: impl Into<Arc<Graph<F, O>>>) -> TractResult<Arc<SimplePlan<F, O>>> {
220        let model = model.into();
221        Self::build(model, &RunOptions::default()).map(Arc::new)
222    }
223
224    /// This contructor returns a plan that will compute all the model default outputs in one pass.
225    pub fn new_with_options(
226        model: impl Into<Arc<Graph<F, O>>>,
227        options: &RunOptions,
228    ) -> TractResult<Arc<SimplePlan<F, O>>> {
229        let model = model.into();
230        Self::build(model, options).map(Arc::new)
231    }
232
233    /// This contructor returns a plan that will compute the specified output.
234    #[deprecated]
235    pub fn new_for_output(
236        model: Graph<F, O>,
237        output: OutletId,
238    ) -> TractResult<Arc<SimplePlan<F, O>>> {
239        #[allow(deprecated)]
240        Self::build_with_outputs_and_deps(model, &[output], &[], &RunOptions::default())
241            .map(Arc::new)
242    }
243
244    /// This contructor returns a plan that will compute all specified outputs in one pass.
245    #[deprecated]
246    pub fn new_for_outputs(
247        model: impl Into<Arc<Graph<F, O>>>,
248        outputs: &[OutletId],
249    ) -> TractResult<Arc<SimplePlan<F, O>>> {
250        #[allow(deprecated)]
251        Self::build_with_outputs_and_deps(model, outputs, &[], &RunOptions::default()).map(Arc::new)
252    }
253
254    pub fn with_turn_handler<H: TurnStateHandler + 'static>(mut self, turn_handler: H) -> Self {
255        self.turn_handler = Some(Arc::new(turn_handler));
256        self
257    }
258
259    #[deprecated]
260    pub fn new_for_outputs_and_deps(
261        model: impl Into<Arc<Graph<F, O>>>,
262        outputs: &[OutletId],
263        deps: &[(usize, usize)],
264    ) -> TractResult<Arc<SimplePlan<F, O>>> {
265        #[allow(deprecated)]
266        Self::build_with_outputs_and_deps(model, outputs, deps, &RunOptions::default())
267            .map(Arc::new)
268    }
269
270    pub fn build(
271        model: impl Into<Arc<Graph<F, O>>>,
272        options: &RunOptions,
273    ) -> TractResult<SimplePlan<F, O>> {
274        let model = model.into();
275        let outputs = model.outputs.clone();
276        #[allow(deprecated)]
277        Self::build_with_outputs_and_deps(model, &outputs, &[], options)
278    }
279
280    #[deprecated]
281    pub fn build_with_outputs_and_deps(
282        model: impl Into<Arc<Graph<F, O>>>,
283        outputs: &[OutletId],
284        deps: &[(usize, usize)],
285        options: &RunOptions,
286    ) -> TractResult<SimplePlan<F, O>> {
287        let model = model.into();
288        let inputs = model.input_outlets()?.iter().map(|n| n.node).collect::<Vec<usize>>();
289        let outputs_nodes = outputs.iter().map(|n| n.node).collect::<Vec<usize>>();
290        let mut order = if options.skip_order_opt_ram {
291            eval_order_for_nodes(model.nodes(), &inputs, &outputs_nodes, deps)?
292        } else {
293            eval_order_opt_ram_for_nodes(model.nodes(), &inputs, &outputs_nodes, deps)?
294        };
295        order.retain(|node| !model.node(*node).op_is::<Const>());
296        let flush_lists = build_flush_list(&*model, &order, outputs, |n| !n.op_is::<Const>());
297
298        #[allow(clippy::mutable_key_type)]
299        let mut symbols: std::collections::HashSet<Symbol> = Default::default();
300        for node in &model.nodes {
301            for output in &node.outputs {
302                if let Ok(fact) = output.fact.to_typed_fact() {
303                    symbols.extend(fact.shape.iter().flat_map(|d| d.symbols()))
304                }
305            }
306        }
307        Ok(SimplePlan {
308            model,
309            order,
310            flush_lists,
311            outputs: outputs.to_vec(),
312            has_unresolved_symbols: !symbols.is_empty(),
313            symbols: symbols.into_iter().collect(),
314            executor: options.executor.clone(),
315            turn_handler: None,
316        })
317    }
318
319    pub fn order_without_consts(&self) -> &[usize] {
320        &self.order
321    }
322
323    pub fn run(self: &Arc<Self>, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
324        let mut state = self.spawn()?;
325        state.run(inputs)
326    }
327
328    pub fn model(&self) -> &Graph<F, O> {
329        self.model.borrow()
330    }
331
332    pub fn spawn(self: &Arc<Self>) -> TractResult<SimpleState<F, O>> {
333        SimpleState::new(self)
334    }
335}
336
337#[derive(Debug)]
338pub struct SimpleState<F, O>
339where
340    F: Fact + Clone + 'static,
341    O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
342{
343    pub(crate) plan: Arc<SimplePlan<F, O>>,
344    pub op_states: Vec<Option<Box<dyn OpState>>>,
345    pub turn_state: TurnState,
346    session: SessionId,
347}
348
349/// A clone is a distinct session: it gets its own [`SessionId`], so whatever the
350/// ops key on `(session, node_id)` stays separate from the original's.
351impl<F, O> Clone for SimpleState<F, O>
352where
353    F: Fact + Clone + 'static,
354    O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
355{
356    fn clone(&self) -> Self {
357        SimpleState {
358            plan: self.plan.clone(),
359            op_states: self.op_states.clone(),
360            turn_state: self.turn_state.clone(),
361            session: SessionId::next(),
362        }
363    }
364}
365
366impl<F, O> Drop for SimpleState<F, O>
367where
368    F: Fact + Clone + 'static,
369    O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
370{
371    fn drop(&mut self) {
372        for (ix, node) in self.plan.model.nodes.iter().enumerate() {
373            node.op().drop_session(self.session, ix);
374        }
375    }
376}
377
378impl<F, O> SimpleState<F, O>
379where
380    F: Fact + Clone + 'static,
381    O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
382{
383    pub fn new(plan: &Arc<SimplePlan<F, O>>) -> TractResult<SimpleState<F, O>> {
384        let plan = Arc::clone(plan);
385        let turn = TurnState::default();
386        let model = plan.model();
387        let states: Vec<Option<Box<dyn OpState>>> = vec![None; model.nodes.len()];
388        let mut state =
389            SimpleState { plan, op_states: states, turn_state: turn, session: SessionId::next() };
390        state.reset_op_states()?;
391        Ok(state)
392    }
393
394    pub fn new_from_inputs(
395        plan: &Arc<SimplePlan<F, O>>,
396        inputs: TVec<TValue>,
397    ) -> TractResult<SimpleState<F, O>> {
398        let mut state = SimpleState::new(plan)?;
399        state.set_inputs(inputs)?;
400        state.resolve_symbols_with_states()?;
401
402        Ok(state)
403    }
404
405    fn ready_turn(&mut self) {
406        if self.turn_state.values.len() == 0 {
407            self.turn_state.values = vec![None; self.plan.model.nodes().len()];
408            for node in &self.plan.model.nodes {
409                if let Some(k) = node.op_as::<Const>() {
410                    self.turn_state.values[node.id] = Some(tvec!(k.val().clone().into_tvalue()));
411                }
412            }
413        }
414    }
415    /// Reset wires state.
416    pub fn reset_turn(&mut self) -> TractResult<()> {
417        self.reset_turn_keep_symbols();
418        self.turn_state.resolved_symbols = SymbolValues::default();
419        Ok(())
420    }
421
422    /// Like [`reset_turn`] but keeps the resolved symbols (and scenario). Used by
423    /// `Scan`/`Loop` bodies, whose shapes are constant across iterations: it lets
424    /// the body resolve its symbols once and skip the per-iteration re-resolution
425    /// the full `reset_turn` + `run` cycle would otherwise force.
426    pub(crate) fn reset_turn_keep_symbols(&mut self) {
427        for node in &self.plan.order {
428            self.turn_state.values[*node] = None;
429        }
430    }
431
432    /// Clear resolved symbols (and scenario) without touching node values. Used at
433    /// the start of a fresh `Scan` evaluation, since the body state persists across
434    /// outer calls and a previous call may have left stale symbol resolutions.
435    pub(crate) fn clear_resolved_symbols(&mut self) {
436        self.turn_state.resolved_symbols = SymbolValues::default();
437        self.turn_state.scenario = None;
438    }
439
440    /// Seat the lanes carrying the coming turn's streams, one lane per row of
441    /// axis 0 of its tensors.
442    pub fn seat(&mut self, seating: Seating) {
443        self.turn_state.seating = seating;
444    }
445
446    /// Drop the session state `lanes` hold, handing them to new streams.
447    pub fn reset_lanes(&mut self, lanes: &[LaneId]) -> TractResult<()> {
448        for op_state in self.op_states.iter_mut().flatten() {
449            op_state.reset_lanes(lanes)?;
450        }
451        Ok(())
452    }
453
454    /// Reset op inner state.
455    fn reset_op_states(&mut self) -> TractResult<()> {
456        let &mut SimpleState {
457            ref plan, ref turn_state, op_states: ref mut states, session, ..
458        } = self;
459        for (ix, n) in plan.model.nodes.iter().enumerate() {
460            states[ix] = n.op().state(&turn_state.context(session, ix))?;
461        }
462        Ok(())
463    }
464
465    pub(crate) fn resolve_symbols_with_states(&mut self) -> TractResult<()> {
466        for state in self
467            .op_states
468            .iter_mut()
469            .filter_map(Option::as_mut)
470            .filter(|s| s.has_init_tensor_fact())
471        {
472            state.resolve_symbols(&mut self.turn_state)?;
473        }
474        Ok(())
475    }
476
477    pub fn run(&mut self, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
478        self.run_plan_with_eval(inputs, self::eval)
479    }
480
481    pub fn exec(&mut self) -> TractResult<()> {
482        self.exec_plan_with_eval(self::eval)
483    }
484
485    pub fn run_plan_with_eval<Eval, E>(
486        &mut self,
487        inputs: TVec<TValue>,
488        eval: Eval,
489    ) -> TractResult<TVec<TValue>>
490    where
491        Eval: for<'a, 'b, 'c> FnMut(
492            &'a EvalContext<'a>,
493            Option<&'b mut (dyn OpState + 'static)>,
494            &'c Node<F, O>,
495            TVec<TValue>,
496        ) -> Result<TVec<TValue>, E>,
497        E: Into<anyhow::Error> + Send + Sync + 'static,
498    {
499        self.set_inputs(inputs)?;
500        self.resolve_symbols_with_states()?;
501        self.exec_plan_with_eval(eval)?;
502        let outputs = self.outputs()?;
503        self.reset_turn()?;
504        Ok(outputs)
505    }
506
507    pub fn exec_plan_with_eval<Eval, E>(&mut self, eval: Eval) -> TractResult<()>
508    where
509        Eval: for<'a, 'b, 'c> FnMut(
510            &'a EvalContext<'a>,
511            Option<&'b mut (dyn OpState + 'static)>,
512            &'c Node<F, O>,
513            TVec<TValue>,
514        ) -> Result<TVec<TValue>, E>,
515        E: Into<anyhow::Error> + Send + Sync + 'static,
516    {
517        if let Some(executor) = self.plan().executor.as_ref() {
518            tract_linalg::multithread::multithread_tract_scope(executor.clone(), || {
519                self.do_exec_plan_with_eval(eval)
520            })
521        } else {
522            self.do_exec_plan_with_eval(eval)
523        }
524    }
525
526    fn do_exec_plan_with_eval<Eval, E>(&mut self, mut eval: Eval) -> TractResult<()>
527    where
528        Eval: for<'a, 'b, 'c> FnMut(
529            &'a EvalContext<'a>,
530            Option<&'b mut (dyn OpState + 'static)>,
531            &'c Node<F, O>,
532            TVec<TValue>,
533        ) -> Result<TVec<TValue>, E>,
534        E: Into<anyhow::Error> + Send + Sync + 'static,
535    {
536        {
537            self.ready_turn();
538            self.plan
539                .turn_handler
540                .as_ref()
541                .map(|it| it.before_plan_eval(&mut self.turn_state))
542                .transpose()?;
543
544            let mut syms_done = !self.plan.has_unresolved_symbols
545                || self
546                    .plan
547                    .symbols
548                    .iter()
549                    .all(|s| self.turn_state.resolved_symbols.get(s).is_some());
550
551            for (step, n) in self.plan.order.iter().enumerate() {
552                let node = self.plan.model.node(*n);
553                trace!("Running step {step}, node {node}");
554                let mut inputs: TVec<TValue> = tvec![];
555                for i in &node.inputs {
556                    trace!("  use input {i:?}");
557                    let prec_node = self.plan.model.node(i.node);
558                    let prec = self.turn_state.values[i.node].as_ref().ok_or_else(|| {
559                        format_err!("Computing {}, precursor {} not done:", node, prec_node)
560                    })?;
561                    inputs.push(prec[i.slot].clone())
562                }
563                for flush in &self.plan.flush_lists[step] {
564                    trace!("  Ran {} can now flush {}", node, self.plan.model.node(*flush));
565                    self.turn_state.values[*flush] = None;
566                }
567
568                if cfg!(debug_assertions) {
569                    let facts = self.plan.model.node_input_facts(node.id)?;
570                    if facts.len() != inputs.len() {
571                        bail!(
572                            "Evaluating {}: expected {} inputs, got {}",
573                            node,
574                            facts.len(),
575                            inputs.len()
576                        );
577                    }
578                    for (ix, (v, f)) in inputs.iter().zip(facts.iter()).enumerate() {
579                        if !f.matches(v, Some(&self.turn_state.resolved_symbols))? {
580                            bail!(
581                                "Evaluating {}: input {:?}, expected {:?}, got {:?}",
582                                node,
583                                ix,
584                                f,
585                                v
586                            );
587                        }
588                    }
589                }
590
591                // A node with no precursors whose value is already set is a model
592                // input: `set_inputs` wrote it and `reset_turn` clears everything
593                // else, so hand it in rather than have the op reach for it. After
594                // the checks above, which are stated against the node's declared
595                // inputs -- a source declares none.
596                if node.inputs.is_empty()
597                    && let Some(preset) = self.turn_state.values[*n].as_ref()
598                {
599                    inputs = preset.clone();
600                }
601
602                let ctx = self.turn_state.context(self.session, node.id);
603                let vs = eval(&ctx, self.op_states[node.id].as_deref_mut(), node, inputs)
604                    .map_err(|e| e.into())?;
605
606                if !syms_done && self.plan.has_unresolved_symbols {
607                    for (o, v) in node.outputs.iter().zip(vs.iter()) {
608                        if let Ok(f) = o.fact.to_typed_fact() {
609                            for (dim_abstract, dim_concrete) in f.shape.iter().zip(v.shape()) {
610                                Self::resolve(
611                                    &mut self.turn_state,
612                                    dim_abstract,
613                                    *dim_concrete as i64,
614                                )?;
615                            }
616                        }
617                    }
618                    if self
619                        .plan
620                        .symbols
621                        .iter()
622                        .all(|s| self.turn_state.resolved_symbols.get(s).is_some())
623                    {
624                        syms_done = true;
625                    }
626                }
627                if cfg!(debug_assertions) {
628                    let facts = self.plan.model.node_output_facts(node.id)?;
629                    if facts.len() != vs.len() {
630                        bail!(
631                            "Evaluating {}: expected {} outputs, got {}",
632                            node,
633                            facts.len(),
634                            vs.len()
635                        );
636                    }
637                    for (ix, (v, f)) in vs.iter().zip(facts.iter()).enumerate() {
638                        if node.outputs[ix].successors.len() == 0 {
639                            continue;
640                        }
641                        if !f.matches(v, Some(&self.turn_state.resolved_symbols))? {
642                            bail!(
643                                "Evaluating {}: output {:?}, expected {:?}, got {:?}",
644                                node,
645                                ix,
646                                f,
647                                v
648                            );
649                        }
650                    }
651                }
652
653                self.turn_state.values[node.id] = Some(vs);
654            }
655            self.plan
656                .turn_handler
657                .as_ref()
658                .map(|it| it.after_plan_eval(&mut self.turn_state))
659                .transpose()?;
660        }
661        Ok(())
662    }
663
664    pub fn set_inputs(&mut self, inputs: TVec<TValue>) -> TractResult<()> {
665        ensure!(
666            inputs.len() == self.model().inputs.len(),
667            "Wrong number of inputs for model. Expected {} got {}",
668            self.model().inputs.len(),
669            inputs.len()
670        );
671
672        for (ix, t) in inputs.into_iter().enumerate() {
673            self.set_input(ix, t)?
674        }
675        Ok(())
676    }
677
678    /// Like [`set_inputs`] but drains the caller's buffer (leaving it empty with
679    /// its capacity intact) instead of consuming it, so a repeated caller (a
680    /// `Scan` body loop) can reuse one allocation across iterations.
681    pub(crate) fn set_inputs_drain(&mut self, inputs: &mut TVec<TValue>) -> TractResult<()> {
682        ensure!(
683            inputs.len() == self.model().inputs.len(),
684            "Wrong number of inputs for model. Expected {} got {}",
685            self.model().inputs.len(),
686            inputs.len()
687        );
688        for (ix, t) in inputs.drain(..).enumerate() {
689            self.set_input(ix, t)?
690        }
691        Ok(())
692    }
693
694    fn resolve(state: &mut TurnState, expression: &TDim, provided: i64) -> TractResult<()> {
695        if let TDim::Sym(sym) = expression
696            && state.resolved_symbols.get(sym).is_none()
697        {
698            state.resolved_symbols.set(sym, provided);
699            if state.scenario.is_none() {
700                let scope = sym.scope().with_context(|| {
701                    format!(
702                        "Symbol {sym:?} points to an invalid (dead ?) SymbolScope. \
703                         Make sure to create symbols using the model-managed SymbolScope."
704                    )
705                })?;
706                state.scenario = scope.guess_scenario(&state.resolved_symbols)?;
707            }
708            return Ok(());
709        }
710        let expected = expression.eval(&state.resolved_symbols);
711        if let Some(x) = expected.as_i64()
712            && x != provided
713        {
714            bail!("Clashing resolution for expression. {expression}={x} != {provided}. ({state:?})")
715        }
716        if expected.symbols().len() == 1 {
717            let sym = expected.symbols().into_iter().next().unwrap();
718            if let Some(v) = solve_for(&sym, &expected, &provided.to_dim()) {
719                debug!("Determined symbol {sym}={v}");
720                state.resolved_symbols.set(&sym, v.to_i64().unwrap());
721            }
722            if state.scenario.is_none() {
723                let scope = sym
724                    .scope()
725                    .with_context(|| format!("Symbol {sym:?} points to an invalid (dead ?) SymbolScope. Make sure to create symbols using the model-managed SymbolScope."))?;
726                state.scenario = scope.guess_scenario(&state.resolved_symbols)?;
727            }
728        }
729        Ok(())
730    }
731
732    pub fn set_input(&mut self, input: usize, t: TValue) -> TractResult<()> {
733        let outlet: OutletId = *self
734            .model()
735            .input_outlets()?
736            .get(input)
737            .with_context(|| format!("Invalid input id for model ({input})."))?;
738        if let Ok(fact) = self.plan.model.outlet_fact(outlet)?.to_typed_fact() {
739            for (expected, provided) in fact.shape.iter().zip(t.shape()) {
740                Self::resolve(&mut self.turn_state, expected, *provided as i64)?;
741            }
742        }
743        let fact = self.plan.model.outlet_fact(outlet)?;
744        ensure!(
745            fact.matches(&t, Some(&self.turn_state.resolved_symbols))
746                .with_context(|| format!("Setting input {input}"))?,
747            "Input at index {input} has incorrect dtype or shape (got {t:?}, expected to match fact {fact:?})",
748        );
749        self.ready_turn();
750        self.turn_state.values[outlet.node] = Some(tvec!(t));
751        Ok(())
752    }
753
754    pub fn output(&self, id: usize) -> TractResult<&TValue> {
755        let outlet = self.model().output_outlets()?.get(id).with_context(|| {
756            format!(
757                "Required output {}, only have {}",
758                id,
759                self.model().output_outlets().unwrap().len()
760            )
761        })?;
762        let value: &TValue = self
763            .turn_state
764            .values
765            .get(outlet.node)
766            .context("node id for output beyond node values array")?
767            .as_ref()
768            .context("node is not an output")?
769            .get(outlet.slot)
770            .context("slot id too high")?;
771        Ok(value)
772    }
773
774    pub fn outputs(&mut self) -> TractResult<TVec<TValue>> {
775        let &mut SimpleState { ref plan, ref mut turn_state, .. } = self;
776        let mut v = tvec![];
777        for o in plan.outputs.iter() {
778            let vs = turn_state.values[o.node].as_mut().ok_or_else(|| {
779                format_err!("Outputs of {:?} are not computed", plan.model.nodes()[o.node])
780            })?;
781            v.push(vs[o.slot].clone())
782        }
783        Ok(v)
784    }
785
786    pub fn set_values(&mut self, id: usize, values: TVec<TValue>) -> TractResult<()> {
787        self.turn_state.values[id] = Some(values);
788        Ok(())
789    }
790
791    pub fn set_value(&mut self, id: usize, value: TValue) -> TractResult<()> {
792        self.set_values(id, tvec!(value))
793    }
794
795    pub fn prepare_inputs(&self, node: usize) -> TractResult<TVec<TValue>> {
796        let SimpleState { plan, turn_state, .. } = self;
797        let nodes = plan.model.nodes();
798        let node = &nodes[node];
799        let mut inputs: TVec<TValue> = tvec![];
800        for i in &node.inputs {
801            let prec_node = &nodes[i.node];
802            let prec = turn_state.values[i.node].as_ref().ok_or_else(|| {
803                format_err!("Computing {}, precursor {} not done.", node, prec_node)
804            })?;
805            inputs.push(prec[i.slot].clone())
806        }
807        if node.inputs.is_empty()
808            && let Some(preset) = turn_state.values[node.id].as_ref()
809        {
810            inputs = preset.clone();
811        }
812        Ok(inputs)
813    }
814
815    pub fn compute_one(&mut self, node: usize) -> TractResult<()> {
816        let inputs = self.prepare_inputs(node)?;
817        self.compute_one_with_inputs(node, inputs)
818    }
819
820    pub fn compute_one_with_inputs(
821        &mut self,
822        node: usize,
823        inputs: TVec<TValue>,
824    ) -> TractResult<()> {
825        let &mut SimpleState {
826            ref plan,
827            ref mut turn_state,
828            op_states: ref mut states,
829            session,
830            ..
831        } = self;
832        let nodes = plan.model.nodes();
833        let node = &nodes[node];
834        let ctx = turn_state.context(session, node.id);
835        let vs = eval(&ctx, states[node.id].as_deref_mut(), node, inputs)?;
836        turn_state.values[node.id] = Some(vs);
837        Ok(())
838    }
839
840    pub fn compute_recursively(&mut self, node: usize) -> TractResult<&[TValue]> {
841        let values = {
842            #[allow(clippy::needless_collect)] // clippy bug ?
843            let precs: Vec<usize> =
844                self.model().nodes()[node].inputs.iter().map(|i| i.node).collect();
845            for i in precs.into_iter() {
846                if self.turn_state.values[i].is_none() {
847                    let _ = self.compute_recursively(i)?;
848                }
849            }
850            let mut inputs: TVec<TValue> = tvec![];
851            {
852                let node = &self.model().nodes()[node];
853                for i in &node.inputs {
854                    inputs.push(self.turn_state.values[i.node].as_ref().unwrap()[i.slot].clone())
855                }
856                if node.inputs.is_empty()
857                    && let Some(preset) = self.turn_state.values[node.id].as_ref()
858                {
859                    inputs = preset.clone();
860                }
861            }
862            let &mut Self {
863                op_states: ref mut states,
864                turn_state: ref mut turn,
865                ref plan,
866                session,
867                ..
868            } = self;
869            let ctx = turn.context(session, node);
870            eval(&ctx, states[node].as_deref_mut(), &plan.model().nodes[node], inputs)?
871        };
872        self.turn_state.values[node] = Some(values);
873        Ok(self.turn_state.values[node].as_ref().unwrap())
874    }
875
876    pub fn take_by_name(&mut self, name: &str) -> TractResult<TVec<Tensor>> {
877        let id = self.model().node_by_name(name)?.id;
878        Self::take(self, id)
879    }
880
881    pub fn take(&mut self, id: usize) -> TractResult<TVec<Tensor>> {
882        Ok(self.turn_state.values[id]
883            .take()
884            .ok_or_else(|| format_err!("Node is not computed"))?
885            .into_iter()
886            .map(|v| v.into_tensor())
887            .collect())
888    }
889
890    pub fn plan(&self) -> &Arc<SimplePlan<F, O>> {
891        &self.plan
892    }
893
894    pub fn model(&self) -> &Graph<F, O> {
895        &self.plan.model
896    }
897}
898
899pub fn eval<F, O>(
900    ctx: &EvalContext,
901    mut state: Option<&mut (dyn OpState + 'static)>,
902    node: &Node<F, O>,
903    input: TVec<TValue>,
904) -> TractResult<TVec<TValue>>
905where
906    F: Fact + Clone + 'static,
907    O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
908{
909    match state {
910        Some(ref mut state) => state.eval(ctx, node.op(), input),
911        None => node.op().eval(ctx, input),
912    }
913    .with_context(|| format!("Evaluating {node}"))
914}
915
916#[cfg(test)]
917mod test {
918    use super::*;
919    fn is_send<T: Send>() {}
920    fn is_sync<T: Sync>() {}
921
922    #[test]
923    fn type_model_is_sync() {
924        is_sync::<TypedModel>();
925    }
926
927    #[test]
928    fn type_model_is_send() {
929        is_send::<TypedModel>();
930    }
931
932    #[test]
933    fn type_plan_is_send() {
934        is_send::<TypedSimplePlan>();
935    }
936
937    #[test]
938    fn type_plan_is_sync() {
939        is_sync::<TypedSimplePlan>();
940    }
941
942    #[test]
943    fn type_state_is_send() {
944        is_send::<TypedSimpleState>();
945    }
946}