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#[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 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
35pub struct LaneId(pub usize);
36
37#[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 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 pub fn occupancy(&self) -> usize {
83 self.lanes.len()
84 }
85
86 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
95pub type TurnShared = anymap3::Map<dyn std::any::Any + Send>;
99
100#[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 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 pub shared: TurnShared,
126 pub seating: Seating,
129}
130
131impl EvalContext<'static> {
132 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 const_values: Vec<(usize, TValue)>,
214}
215
216impl<F, O> SimplePlan<F, O>
217where
218 F: Fact + Clone + 'static,
219 O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
220{
221 pub fn new(model: impl Into<Arc<Graph<F, O>>>) -> TractResult<Arc<SimplePlan<F, O>>> {
223 let model = model.into();
224 Self::build(model, &RunOptions::default()).map(Arc::new)
225 }
226
227 pub fn new_with_options(
229 model: impl Into<Arc<Graph<F, O>>>,
230 options: &RunOptions,
231 ) -> TractResult<Arc<SimplePlan<F, O>>> {
232 let model = model.into();
233 Self::build(model, options).map(Arc::new)
234 }
235
236 #[deprecated]
238 pub fn new_for_output(
239 model: Graph<F, O>,
240 output: OutletId,
241 ) -> TractResult<Arc<SimplePlan<F, O>>> {
242 #[allow(deprecated)]
243 Self::build_with_outputs_and_deps(model, &[output], &[], &RunOptions::default())
244 .map(Arc::new)
245 }
246
247 #[deprecated]
249 pub fn new_for_outputs(
250 model: impl Into<Arc<Graph<F, O>>>,
251 outputs: &[OutletId],
252 ) -> TractResult<Arc<SimplePlan<F, O>>> {
253 #[allow(deprecated)]
254 Self::build_with_outputs_and_deps(model, outputs, &[], &RunOptions::default()).map(Arc::new)
255 }
256
257 pub fn with_turn_handler<H: TurnStateHandler + 'static>(mut self, turn_handler: H) -> Self {
258 self.turn_handler = Some(Arc::new(turn_handler));
259 self
260 }
261
262 #[deprecated]
263 pub fn new_for_outputs_and_deps(
264 model: impl Into<Arc<Graph<F, O>>>,
265 outputs: &[OutletId],
266 deps: &[(usize, usize)],
267 ) -> TractResult<Arc<SimplePlan<F, O>>> {
268 #[allow(deprecated)]
269 Self::build_with_outputs_and_deps(model, outputs, deps, &RunOptions::default())
270 .map(Arc::new)
271 }
272
273 pub fn build(
274 model: impl Into<Arc<Graph<F, O>>>,
275 options: &RunOptions,
276 ) -> TractResult<SimplePlan<F, O>> {
277 let model = model.into();
278 let outputs = model.outputs.clone();
279 #[allow(deprecated)]
280 Self::build_with_outputs_and_deps(model, &outputs, &[], options)
281 }
282
283 #[allow(clippy::mutable_key_type)]
290 fn symbol_deps(
291 nodes: &[Node<F, O>],
292 inputs: &[usize],
293 outputs: &[usize],
294 deps: &[(usize, usize)],
295 ) -> TractResult<Vec<(usize, usize)>> {
296 fn tensor_value_symbols(t: &Tensor, acc: &mut std::collections::HashSet<Symbol>) {
297 if t.datum_type() == TDim::datum_type()
298 && let Some(slice) =
299 t.try_as_plain_ram().ok().and_then(|v| v.as_slice::<TDim>().ok())
300 {
301 acc.extend(slice.iter().flat_map(|d| d.symbols()));
302 }
303 }
304 let base_order = eval_order_for_nodes(nodes, inputs, outputs, &[])?;
308 let mut definer: std::collections::HashMap<Symbol, usize> = Default::default();
311 for &id in inputs {
316 for output in &nodes[id].outputs {
317 if let Ok(fact) = output.fact.to_typed_fact() {
318 for sym in fact.shape.iter().flat_map(|d| d.symbols()) {
319 definer.entry(sym).or_insert(id);
320 }
321 }
322 }
323 }
324 for &id in &base_order {
325 let node = &nodes[id];
326 if !node.op.as_ref().mints_runtime_symbols() {
327 continue;
328 }
329 for output in &node.outputs {
330 if let Ok(fact) = output.fact.to_typed_fact() {
331 for sym in fact.shape.iter().flat_map(|d| d.symbols()) {
332 definer.entry(sym).or_insert(id);
333 }
334 }
335 }
336 }
337 let mut derived = vec![];
338 let mut seen: std::collections::HashSet<(usize, usize)> = deps.iter().copied().collect();
339 let mut definer_ancestors: std::collections::HashMap<
341 usize,
342 std::collections::HashSet<usize>,
343 > = Default::default();
344 for &id in &base_order {
345 let node = &nodes[id];
346 let mut mentioned: std::collections::HashSet<Symbol> = Default::default();
352 for output in &node.outputs {
353 if let Ok(fact) = output.fact.to_typed_fact() {
354 mentioned.extend(fact.shape.iter().flat_map(|d| d.symbols()));
355 for t in [fact.konst.as_deref(), fact.uniform.as_deref()].into_iter().flatten()
356 {
357 tensor_value_symbols(t, &mut mentioned);
358 }
359 mentioned.extend(fact.uniform_tdim.iter().flat_map(TDim::symbols));
360 }
361 }
362 let input_facts: Vec<Cow<'_, TypedFact>> = node
363 .inputs
364 .iter()
365 .filter_map(|i| nodes[i.node].outputs[i.slot].fact.to_typed_fact().ok())
366 .collect();
367 let input_facts: Vec<&TypedFact> = input_facts.iter().map(|f| f.as_ref()).collect();
368 mentioned.extend(node.op.as_ref().required_bound_symbols(&input_facts));
369 let mut mentioned: Vec<_> = mentioned.into_iter().collect();
372 mentioned.sort_unstable();
373 for sym in mentioned {
374 let Some(&d) = definer.get(&sym) else { continue };
375 if d == id || inputs.contains(&d) || !seen.insert((id, d)) {
377 continue;
378 }
379 if node.inputs.iter().any(|i| i.node == d) {
383 continue;
384 }
385 let ancestors = definer_ancestors.entry(d).or_insert_with(|| {
386 let mut stack: TVec<usize> = nodes[d].inputs.iter().map(|i| i.node).collect();
387 let mut set: std::collections::HashSet<usize> = Default::default();
388 while let Some(n) = stack.pop() {
389 if set.insert(n) {
390 stack.extend(nodes[n].inputs.iter().map(|i| i.node));
391 }
392 }
393 set
394 });
395 if !ancestors.contains(&id) {
396 derived.push((id, d));
398 }
399 }
400 }
401 Ok(derived)
402 }
403
404 #[deprecated]
405 pub fn build_with_outputs_and_deps(
406 model: impl Into<Arc<Graph<F, O>>>,
407 outputs: &[OutletId],
408 deps: &[(usize, usize)],
409 options: &RunOptions,
410 ) -> TractResult<SimplePlan<F, O>> {
411 let model = model.into();
412 let inputs = model.input_outlets()?.iter().map(|n| n.node).collect::<Vec<usize>>();
413 let outputs_nodes = outputs.iter().map(|n| n.node).collect::<Vec<usize>>();
414 let mut all_deps = deps.to_vec();
415 all_deps.extend(Self::symbol_deps(model.nodes(), &inputs, &outputs_nodes, deps)?);
416 let mut order = if options.skip_order_opt_ram {
417 eval_order_for_nodes(model.nodes(), &inputs, &outputs_nodes, &all_deps)?
418 } else {
419 eval_order_opt_ram_for_nodes(model.nodes(), &inputs, &outputs_nodes, &all_deps)?
420 };
421 order.retain(|node| !model.node(*node).op_is::<Const>());
422 let flush_lists = build_flush_list(&*model, &order, outputs, |n| !n.op_is::<Const>());
423
424 #[allow(clippy::mutable_key_type)]
425 let mut symbols: std::collections::HashSet<Symbol> = Default::default();
426 for node in &model.nodes {
427 for output in &node.outputs {
428 if let Ok(fact) = output.fact.to_typed_fact() {
429 symbols.extend(fact.shape.iter().flat_map(|d| d.symbols()))
430 }
431 }
432 }
433 let const_values = model
434 .nodes
435 .iter()
436 .filter_map(|n| n.op_as::<Const>().map(|k| (n.id, k.val().clone().into_tvalue())))
437 .collect();
438 Ok(SimplePlan {
439 model,
440 order,
441 flush_lists,
442 outputs: outputs.to_vec(),
443 has_unresolved_symbols: !symbols.is_empty(),
444 symbols: symbols.into_iter().collect(),
445 executor: options.executor.clone(),
446 turn_handler: None,
447 const_values,
448 })
449 }
450
451 pub fn order_without_consts(&self) -> &[usize] {
452 &self.order
453 }
454
455 pub fn run(self: &Arc<Self>, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
456 let mut state = self.spawn()?;
457 state.run(inputs)
458 }
459
460 pub fn model(&self) -> &Graph<F, O> {
461 self.model.borrow()
462 }
463
464 pub fn spawn(self: &Arc<Self>) -> TractResult<SimpleState<F, O>> {
465 SimpleState::new(self)
466 }
467}
468
469#[derive(Debug)]
470pub struct SimpleState<F, O>
471where
472 F: Fact + Clone + 'static,
473 O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
474{
475 pub(crate) plan: Arc<SimplePlan<F, O>>,
476 pub op_states: Vec<Option<Box<dyn OpState>>>,
477 pub turn_state: TurnState,
478 session: SessionId,
479}
480
481impl<F, O> Clone for SimpleState<F, O>
484where
485 F: Fact + Clone + 'static,
486 O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
487{
488 fn clone(&self) -> Self {
489 SimpleState {
490 plan: self.plan.clone(),
491 op_states: self.op_states.clone(),
492 turn_state: self.turn_state.clone(),
493 session: SessionId::next(),
494 }
495 }
496}
497
498impl<F, O> Drop for SimpleState<F, O>
499where
500 F: Fact + Clone + 'static,
501 O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
502{
503 fn drop(&mut self) {
504 for (ix, node) in self.plan.model.nodes.iter().enumerate() {
505 node.op().drop_session(self.session, ix);
506 }
507 }
508}
509
510impl<F, O> SimpleState<F, O>
511where
512 F: Fact + Clone + 'static,
513 O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
514{
515 pub fn new(plan: &Arc<SimplePlan<F, O>>) -> TractResult<SimpleState<F, O>> {
516 let plan = Arc::clone(plan);
517 let turn = TurnState::default();
518 let model = plan.model();
519 let states: Vec<Option<Box<dyn OpState>>> = vec![None; model.nodes.len()];
520 let mut state =
521 SimpleState { plan, op_states: states, turn_state: turn, session: SessionId::next() };
522 state.reset_op_states()?;
523 Ok(state)
524 }
525
526 pub fn new_from_inputs(
527 plan: &Arc<SimplePlan<F, O>>,
528 inputs: TVec<TValue>,
529 ) -> TractResult<SimpleState<F, O>> {
530 let mut state = SimpleState::new(plan)?;
531 state.set_inputs(inputs)?;
532 state.resolve_symbols_with_states()?;
533
534 Ok(state)
535 }
536
537 fn ready_turn(&mut self) {
538 if self.turn_state.values.len() == 0 {
539 self.turn_state.values = vec![None; self.plan.model.nodes().len()];
540 for (id, value) in &self.plan.const_values {
541 self.turn_state.values[*id] = Some(tvec!(value.clone()));
542 }
543 }
544 }
545 pub fn reset_turn(&mut self) -> TractResult<()> {
547 self.reset_turn_keep_symbols();
548 self.turn_state.resolved_symbols = SymbolValues::default();
549 Ok(())
550 }
551
552 pub(crate) fn reset_turn_keep_symbols(&mut self) {
557 for node in &self.plan.order {
558 self.turn_state.values[*node] = None;
559 }
560 }
561
562 pub(crate) fn clear_resolved_symbols(&mut self) {
566 self.turn_state.resolved_symbols = SymbolValues::default();
567 self.turn_state.scenario = None;
568 }
569
570 pub fn seat(&mut self, seating: Seating) {
573 self.turn_state.seating = seating;
574 }
575
576 pub fn reset_lanes(&mut self, lanes: &[LaneId]) -> TractResult<()> {
578 for op_state in self.op_states.iter_mut().flatten() {
579 op_state.reset_lanes(lanes)?;
580 }
581 Ok(())
582 }
583
584 fn reset_op_states(&mut self) -> TractResult<()> {
586 let &mut SimpleState {
587 ref plan, ref turn_state, op_states: ref mut states, session, ..
588 } = self;
589 for (ix, n) in plan.model.nodes.iter().enumerate() {
590 states[ix] = n.op().state(&turn_state.context(session, ix))?;
591 }
592 Ok(())
593 }
594
595 pub(crate) fn resolve_symbols_with_states(&mut self) -> TractResult<()> {
596 for state in self
597 .op_states
598 .iter_mut()
599 .filter_map(Option::as_mut)
600 .filter(|s| s.has_init_tensor_fact())
601 {
602 state.resolve_symbols(&mut self.turn_state)?;
603 }
604 Ok(())
605 }
606
607 pub fn run(&mut self, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
608 self.run_plan_with_eval(inputs, self::eval)
609 }
610
611 pub fn exec(&mut self) -> TractResult<()> {
612 self.exec_plan_with_eval(self::eval)
613 }
614
615 pub fn run_plan_with_eval<Eval, E>(
616 &mut self,
617 inputs: TVec<TValue>,
618 eval: Eval,
619 ) -> TractResult<TVec<TValue>>
620 where
621 Eval: for<'a, 'b, 'c> FnMut(
622 &'a EvalContext<'a>,
623 Option<&'b mut (dyn OpState + 'static)>,
624 &'c Node<F, O>,
625 TVec<TValue>,
626 ) -> Result<TVec<TValue>, E>,
627 E: Into<anyhow::Error> + Send + Sync + 'static,
628 {
629 self.set_inputs(inputs)?;
630 self.resolve_symbols_with_states()?;
631 self.exec_plan_with_eval(eval)?;
632 let outputs = self.outputs()?;
633 self.reset_turn()?;
634 Ok(outputs)
635 }
636
637 pub fn exec_plan_with_eval<Eval, E>(&mut self, eval: Eval) -> TractResult<()>
638 where
639 Eval: for<'a, 'b, 'c> FnMut(
640 &'a EvalContext<'a>,
641 Option<&'b mut (dyn OpState + 'static)>,
642 &'c Node<F, O>,
643 TVec<TValue>,
644 ) -> Result<TVec<TValue>, E>,
645 E: Into<anyhow::Error> + Send + Sync + 'static,
646 {
647 if let Some(executor) = self.plan().executor.as_ref() {
648 tract_linalg::multithread::multithread_tract_scope(executor.clone(), || {
649 self.do_exec_plan_with_eval(eval)
650 })
651 } else {
652 self.do_exec_plan_with_eval(eval)
653 }
654 }
655
656 fn do_exec_plan_with_eval<Eval, E>(&mut self, mut eval: Eval) -> TractResult<()>
657 where
658 Eval: for<'a, 'b, 'c> FnMut(
659 &'a EvalContext<'a>,
660 Option<&'b mut (dyn OpState + 'static)>,
661 &'c Node<F, O>,
662 TVec<TValue>,
663 ) -> Result<TVec<TValue>, E>,
664 E: Into<anyhow::Error> + Send + Sync + 'static,
665 {
666 {
667 self.ready_turn();
668 self.plan
669 .turn_handler
670 .as_ref()
671 .map(|it| it.before_plan_eval(&mut self.turn_state))
672 .transpose()?;
673
674 let mut syms_done = !self.plan.has_unresolved_symbols
675 || self
676 .plan
677 .symbols
678 .iter()
679 .all(|s| self.turn_state.resolved_symbols.get(s).is_some());
680
681 for (step, n) in self.plan.order.iter().enumerate() {
682 let node = self.plan.model.node(*n);
683 trace!("Running step {step}, node {node}");
684 let mut inputs: TVec<TValue> = tvec![];
685 for i in &node.inputs {
686 trace!(" use input {i:?}");
687 let prec_node = self.plan.model.node(i.node);
688 let prec = self.turn_state.values[i.node].as_ref().ok_or_else(|| {
689 format_err!("Computing {}, precursor {} not done:", node, prec_node)
690 })?;
691 inputs.push(prec[i.slot].clone())
692 }
693 for flush in &self.plan.flush_lists[step] {
694 trace!(" Ran {} can now flush {}", node, self.plan.model.node(*flush));
695 self.turn_state.values[*flush] = None;
696 }
697
698 if cfg!(debug_assertions) {
699 let facts = self.plan.model.node_input_facts(node.id)?;
700 if facts.len() != inputs.len() {
701 bail!(
702 "Evaluating {}: expected {} inputs, got {}",
703 node,
704 facts.len(),
705 inputs.len()
706 );
707 }
708 for (ix, (v, f)) in inputs.iter().zip(facts.iter()).enumerate() {
709 if !f.matches(v, Some(&self.turn_state.resolved_symbols))? {
710 bail!(
711 "Evaluating {}: input {:?}, expected {:?}, got {:?}",
712 node,
713 ix,
714 f,
715 v
716 );
717 }
718 }
719 }
720
721 if node.inputs.is_empty()
727 && let Some(preset) = self.turn_state.values[*n].as_ref()
728 {
729 inputs = preset.clone();
730 }
731
732 let ctx = self.turn_state.context(self.session, node.id);
733 let vs = eval(&ctx, self.op_states[node.id].as_deref_mut(), node, inputs)
734 .map_err(|e| e.into())?;
735
736 if !syms_done && self.plan.has_unresolved_symbols {
737 for (o, v) in node.outputs.iter().zip(vs.iter()) {
738 if let Ok(f) = o.fact.to_typed_fact() {
739 for (dim_abstract, dim_concrete) in f.shape.iter().zip(v.shape()) {
740 Self::resolve(
741 &mut self.turn_state,
742 dim_abstract,
743 *dim_concrete as i64,
744 )?;
745 }
746 }
747 }
748 if self
749 .plan
750 .symbols
751 .iter()
752 .all(|s| self.turn_state.resolved_symbols.get(s).is_some())
753 {
754 syms_done = true;
755 }
756 }
757 if cfg!(debug_assertions) {
758 let facts = self.plan.model.node_output_facts(node.id)?;
759 if facts.len() != vs.len() {
760 bail!(
761 "Evaluating {}: expected {} outputs, got {}",
762 node,
763 facts.len(),
764 vs.len()
765 );
766 }
767 for (ix, (v, f)) in vs.iter().zip(facts.iter()).enumerate() {
768 if node.outputs[ix].successors.len() == 0 {
769 continue;
770 }
771 if !f.matches(v, Some(&self.turn_state.resolved_symbols))? {
772 bail!(
773 "Evaluating {}: output {:?}, expected {:?}, got {:?}",
774 node,
775 ix,
776 f,
777 v
778 );
779 }
780 }
781 }
782
783 self.turn_state.values[node.id] = Some(vs);
784 }
785 self.plan
786 .turn_handler
787 .as_ref()
788 .map(|it| it.after_plan_eval(&mut self.turn_state))
789 .transpose()?;
790 }
791 Ok(())
792 }
793
794 pub fn set_inputs(&mut self, inputs: TVec<TValue>) -> TractResult<()> {
795 ensure!(
796 inputs.len() == self.model().inputs.len(),
797 "Wrong number of inputs for model. Expected {} got {}",
798 self.model().inputs.len(),
799 inputs.len()
800 );
801
802 for (ix, t) in inputs.into_iter().enumerate() {
803 self.set_input(ix, t)?
804 }
805 Ok(())
806 }
807
808 pub(crate) fn set_inputs_drain(&mut self, inputs: &mut TVec<TValue>) -> TractResult<()> {
812 ensure!(
813 inputs.len() == self.model().inputs.len(),
814 "Wrong number of inputs for model. Expected {} got {}",
815 self.model().inputs.len(),
816 inputs.len()
817 );
818 for (ix, t) in inputs.drain(..).enumerate() {
819 self.set_input(ix, t)?
820 }
821 Ok(())
822 }
823
824 fn resolve(state: &mut TurnState, expression: &TDim, provided: i64) -> TractResult<()> {
825 if let TDim::Sym(sym) = expression
826 && state.resolved_symbols.get(sym).is_none()
827 {
828 state.resolved_symbols.set(sym, provided);
829 if state.scenario.is_none() {
830 let scope = sym.scope().with_context(|| {
831 format!(
832 "Symbol {sym:?} points to an invalid (dead ?) SymbolScope. \
833 Make sure to create symbols using the model-managed SymbolScope."
834 )
835 })?;
836 state.scenario = scope.guess_scenario(&state.resolved_symbols)?;
837 }
838 return Ok(());
839 }
840 let expected = expression.eval(&state.resolved_symbols);
841 if let Some(x) = expected.as_i64()
842 && x != provided
843 {
844 bail!("Clashing resolution for expression. {expression}={x} != {provided}. ({state:?})")
845 }
846 if expected.symbols().len() == 1 {
847 let sym = expected.symbols().into_iter().next().unwrap();
848 if let Some(v) = solve_for(&sym, &expected, &provided.to_dim()) {
849 debug!("Determined symbol {sym}={v}");
850 state.resolved_symbols.set(&sym, v.to_i64().unwrap());
851 }
852 if state.scenario.is_none() {
853 let scope = sym
854 .scope()
855 .with_context(|| format!("Symbol {sym:?} points to an invalid (dead ?) SymbolScope. Make sure to create symbols using the model-managed SymbolScope."))?;
856 state.scenario = scope.guess_scenario(&state.resolved_symbols)?;
857 }
858 }
859 Ok(())
860 }
861
862 pub fn set_input(&mut self, input: usize, t: TValue) -> TractResult<()> {
863 let outlet: OutletId = *self
864 .model()
865 .input_outlets()?
866 .get(input)
867 .with_context(|| format!("Invalid input id for model ({input})."))?;
868 if let Ok(fact) = self.plan.model.outlet_fact(outlet)?.to_typed_fact() {
869 for (expected, provided) in fact.shape.iter().zip(t.shape()) {
870 Self::resolve(&mut self.turn_state, expected, *provided as i64)?;
871 }
872 }
873 let fact = self.plan.model.outlet_fact(outlet)?;
874 ensure!(
875 fact.matches(&t, Some(&self.turn_state.resolved_symbols))
876 .with_context(|| format!("Setting input {input}"))?,
877 "Input at index {input} has incorrect dtype or shape (got {t:?}, expected to match fact {fact:?})",
878 );
879 self.ready_turn();
880 self.turn_state.values[outlet.node] = Some(tvec!(t));
881 Ok(())
882 }
883
884 pub fn output(&self, id: usize) -> TractResult<&TValue> {
885 let outlet = self.model().output_outlets()?.get(id).with_context(|| {
886 format!(
887 "Required output {}, only have {}",
888 id,
889 self.model().output_outlets().unwrap().len()
890 )
891 })?;
892 let value: &TValue = self
893 .turn_state
894 .values
895 .get(outlet.node)
896 .context("node id for output beyond node values array")?
897 .as_ref()
898 .context("node is not an output")?
899 .get(outlet.slot)
900 .context("slot id too high")?;
901 Ok(value)
902 }
903
904 pub fn outputs(&mut self) -> TractResult<TVec<TValue>> {
905 let &mut SimpleState { ref plan, ref mut turn_state, .. } = self;
906 let mut v = tvec![];
907 for o in plan.outputs.iter() {
908 let vs = turn_state.values[o.node].as_mut().ok_or_else(|| {
909 format_err!("Outputs of {:?} are not computed", plan.model.nodes()[o.node])
910 })?;
911 v.push(vs[o.slot].clone())
912 }
913 Ok(v)
914 }
915
916 pub fn set_values(&mut self, id: usize, values: TVec<TValue>) -> TractResult<()> {
917 self.turn_state.values[id] = Some(values);
918 Ok(())
919 }
920
921 pub fn set_value(&mut self, id: usize, value: TValue) -> TractResult<()> {
922 self.set_values(id, tvec!(value))
923 }
924
925 pub fn prepare_inputs(&self, node: usize) -> TractResult<TVec<TValue>> {
926 let SimpleState { plan, turn_state, .. } = self;
927 let nodes = plan.model.nodes();
928 let node = &nodes[node];
929 let mut inputs: TVec<TValue> = tvec![];
930 for i in &node.inputs {
931 let prec_node = &nodes[i.node];
932 let prec = turn_state.values[i.node].as_ref().ok_or_else(|| {
933 format_err!("Computing {}, precursor {} not done.", node, prec_node)
934 })?;
935 inputs.push(prec[i.slot].clone())
936 }
937 if node.inputs.is_empty()
938 && let Some(preset) = turn_state.values[node.id].as_ref()
939 {
940 inputs = preset.clone();
941 }
942 Ok(inputs)
943 }
944
945 pub fn compute_one(&mut self, node: usize) -> TractResult<()> {
946 let inputs = self.prepare_inputs(node)?;
947 self.compute_one_with_inputs(node, inputs)
948 }
949
950 pub fn compute_one_with_inputs(
951 &mut self,
952 node: usize,
953 inputs: TVec<TValue>,
954 ) -> TractResult<()> {
955 let &mut SimpleState {
956 ref plan,
957 ref mut turn_state,
958 op_states: ref mut states,
959 session,
960 ..
961 } = self;
962 let nodes = plan.model.nodes();
963 let node = &nodes[node];
964 let ctx = turn_state.context(session, node.id);
965 let vs = eval(&ctx, states[node.id].as_deref_mut(), node, inputs)?;
966 turn_state.values[node.id] = Some(vs);
967 Ok(())
968 }
969
970 pub fn compute_recursively(&mut self, node: usize) -> TractResult<&[TValue]> {
971 let values = {
972 #[allow(clippy::needless_collect)] let precs: Vec<usize> =
974 self.model().nodes()[node].inputs.iter().map(|i| i.node).collect();
975 for i in precs.into_iter() {
976 if self.turn_state.values[i].is_none() {
977 let _ = self.compute_recursively(i)?;
978 }
979 }
980 let mut inputs: TVec<TValue> = tvec![];
981 {
982 let node = &self.model().nodes()[node];
983 for i in &node.inputs {
984 inputs.push(self.turn_state.values[i.node].as_ref().unwrap()[i.slot].clone())
985 }
986 if node.inputs.is_empty()
987 && let Some(preset) = self.turn_state.values[node.id].as_ref()
988 {
989 inputs = preset.clone();
990 }
991 }
992 let &mut Self {
993 op_states: ref mut states,
994 turn_state: ref mut turn,
995 ref plan,
996 session,
997 ..
998 } = self;
999 let ctx = turn.context(session, node);
1000 eval(&ctx, states[node].as_deref_mut(), &plan.model().nodes[node], inputs)?
1001 };
1002 self.turn_state.values[node] = Some(values);
1003 Ok(self.turn_state.values[node].as_ref().unwrap())
1004 }
1005
1006 pub fn take_by_name(&mut self, name: &str) -> TractResult<TVec<Tensor>> {
1007 let id = self.model().node_by_name(name)?.id;
1008 Self::take(self, id)
1009 }
1010
1011 pub fn take(&mut self, id: usize) -> TractResult<TVec<Tensor>> {
1012 Ok(self.turn_state.values[id]
1013 .take()
1014 .ok_or_else(|| format_err!("Node is not computed"))?
1015 .into_iter()
1016 .map(|v| v.into_tensor())
1017 .collect())
1018 }
1019
1020 pub fn plan(&self) -> &Arc<SimplePlan<F, O>> {
1021 &self.plan
1022 }
1023
1024 pub fn model(&self) -> &Graph<F, O> {
1025 &self.plan.model
1026 }
1027}
1028
1029pub fn eval<F, O>(
1030 ctx: &EvalContext,
1031 mut state: Option<&mut (dyn OpState + 'static)>,
1032 node: &Node<F, O>,
1033 input: TVec<TValue>,
1034) -> TractResult<TVec<TValue>>
1035where
1036 F: Fact + Clone + 'static,
1037 O: Debug + Display + AsRef<dyn Op> + AsMut<dyn Op> + Clone + 'static,
1038{
1039 match state {
1040 Some(ref mut state) => state.eval(ctx, node.op(), input),
1041 None => node.op().eval(ctx, input),
1042 }
1043 .with_context(|| format!("Evaluating {node}"))
1044}
1045
1046#[cfg(test)]
1047mod test {
1048 use super::*;
1049 fn is_send<T: Send>() {}
1050 fn is_sync<T: Sync>() {}
1051
1052 #[test]
1053 fn type_model_is_sync() {
1054 is_sync::<TypedModel>();
1055 }
1056
1057 #[test]
1058 fn type_model_is_send() {
1059 is_send::<TypedModel>();
1060 }
1061
1062 #[test]
1063 fn type_plan_is_send() {
1064 is_send::<TypedSimplePlan>();
1065 }
1066
1067 #[test]
1068 fn type_plan_is_sync() {
1069 is_sync::<TypedSimplePlan>();
1070 }
1071
1072 #[test]
1073 fn type_state_is_send() {
1074 is_send::<TypedSimpleState>();
1075 }
1076
1077 #[test]
1083 fn symbol_consumers_run_after_their_definer() -> TractResult<()> {
1084 use crate::ops::array::MultiBroadcastTo;
1085 use crate::ops::array::Range;
1086
1087 let mut model = TypedModel::default();
1088 let r = model.symbols.sym("r").to_dim();
1089 let limit = model.add_source("limit", i64::datum_type().scalar_fact())?;
1090 let start = model.add_const("start", tensor0(0i64))?;
1091 let step = model.add_const("step", tensor0(1i64))?;
1092 let range = model.wire_node("range", Range::new(r.clone()), &[start, limit, step])?[0];
1093 let zero = model.add_const("zero", tensor0(0i64))?;
1095 let expand = model.wire_node(
1096 "expand",
1097 MultiBroadcastTo::new(ShapeFact::from_dims(tvec![r, 2usize.to_dim()])),
1098 &[zero],
1099 )?[0];
1100 model.select_output_outlets(&[expand, range])?;
1102
1103 let found = TypedSimplePlan::new(model)?.run(tvec!(tensor0(5i64).into_tvalue()))?;
1104 assert_eq!(*found[0], tensor2(&[[0i64, 0], [0, 0], [0, 0], [0, 0], [0, 0]]));
1105 assert_eq!(*found[1], tensor1(&[0i64, 1, 2, 3, 4]));
1106 Ok(())
1107 }
1108 #[test]
1114 fn symbol_dep_skipped_when_consumer_is_ancestor() -> TractResult<()> {
1115 use crate::ops::array::MultiBroadcastTo;
1116 use crate::ops::array::Range;
1117
1118 let mut model = TypedModel::default();
1119 let r_a = model.symbols.sym("ra").to_dim();
1120 let r_b = model.symbols.sym("rb").to_dim();
1121 let limit = model.add_source("limit_in", i64::datum_type().scalar_fact())?;
1122 let start = model.add_const("start", tensor0(0i64))?;
1123 let step = model.add_const("step", tensor0(1i64))?;
1124 let range_a =
1125 model.wire_node("range_a", Range::new(r_a.clone()), &[start, limit, step])?[0];
1126 let zero = model.add_const("zero", tensor0(0i64))?;
1127 let expand = model.wire_node(
1128 "expand",
1129 MultiBroadcastTo::new(ShapeFact::from_dims(tvec![r_a.clone(), r_b.clone()])),
1130 &[zero],
1131 )?[0];
1132 let summed = model.wire_node(
1135 "summed",
1136 crate::ops::nn::Reduce::new(tvec!(0, 1), crate::ops::nn::Reducer::Sum),
1137 &[expand],
1138 )?[0];
1139 let summed =
1140 model.wire_node("summed_rm0", crate::ops::change_axes::AxisOp::Rm(0), &[summed])?[0];
1141 let summed =
1142 model.wire_node("summed_rm1", crate::ops::change_axes::AxisOp::Rm(0), &[summed])?[0];
1143 let range_b = model.wire_node("range_b", Range::new(r_b), &[start, summed, step])?;
1144 model.select_output_outlets(&[range_b[0], range_a])?;
1145 TypedSimplePlan::new(model)?;
1151 Ok(())
1152 }
1153
1154 #[test]
1158 fn input_defined_symbol_needs_no_dep() -> TractResult<()> {
1159 use crate::ops::array::MultiBroadcastTo;
1160
1161 let mut model = TypedModel::default();
1162 let s = model.symbols.sym("s").to_dim();
1163 let len = model.add_source("len", i64::datum_type().fact([s.clone()]))?;
1164 let zero = model.add_const("zero", tensor0(0i64))?;
1165 let expand = model.wire_node(
1166 "expand",
1167 MultiBroadcastTo::new(ShapeFact::from_dims(tvec![s, 2usize.to_dim()])),
1168 &[zero],
1169 )?[0];
1170 model.select_output_outlets(&[expand, len])?;
1171 let found = TypedSimplePlan::new(model)?.run(tvec!(tensor1(&[7i64; 7]).into_tvalue()))?;
1172 assert_eq!(found[0].shape(), &[7, 2]);
1173 Ok(())
1174 }
1175
1176 #[test]
1183 fn symbolic_konst_values_consumer_ordered_after_definer() -> TractResult<()> {
1184 use crate::ops::array::Range;
1185 use crate::ops::cast::cast;
1186 use crate::runtime::RunOptions;
1187
1188 let mut model = TypedModel::default();
1189 let r = model.symbols.sym("rk").to_dim();
1190 let limit = model.add_source("limit_in", i64::datum_type().scalar_fact())?;
1191 let start = model.add_const("start", tensor0(0i64))?;
1192 let step = model.add_const("step", tensor0(1i64))?;
1193 let range = model.wire_node("range", Range::new(r.clone()), &[start, limit, step])?[0];
1194 let shape_konst = model.add_const("shape_konst", tensor1(&[r]))?;
1195 let casted = model.wire_node("cast_konst", cast(i64::datum_type()), &[shape_konst])?;
1196 model.select_output_outlets(&[casted[0], range])?;
1197 let options = RunOptions { skip_order_opt_ram: true, ..Default::default() };
1198 let found = TypedSimplePlan::new_with_options(model, &options)?
1199 .run(tvec!(tensor0(4i64).into_tvalue()))?;
1200 assert_eq!(*found[0], tensor1(&[4i64]));
1201 assert_eq!(*found[1], tensor1(&[0i64, 1, 2, 3]));
1202 Ok(())
1203 }
1204}
1205
1206#[cfg(test)]
1207mod symbol_deps_tests {
1208 use super::*;
1209 use crate::ops::array::MultiBroadcastTo;
1210 use crate::ops::array::Range;
1211
1212 fn symbol_deps_of(model: &TypedModel) -> Vec<(usize, usize)> {
1213 let inputs = model.input_outlets().unwrap().iter().map(|n| n.node).collect::<Vec<_>>();
1214 let outputs = model.output_outlets().unwrap().iter().map(|n| n.node).collect::<Vec<_>>();
1215 SimplePlan::<TypedFact, Box<dyn TypedOp>>::symbol_deps(
1216 model.nodes(),
1217 &inputs,
1218 &outputs,
1219 &[],
1220 )
1221 .unwrap()
1222 }
1223
1224 fn range_over(model: &mut TypedModel, sym: &str) -> TractResult<TVec<OutletId>> {
1225 let r = model.symbols.sym(sym).to_dim();
1226 let start = model.add_const("start", tensor0(0i64))?;
1227 let step = model.add_const("step", tensor0(1i64))?;
1228 let limit = model.add_source("limit", i64::datum_type().scalar_fact())?;
1229 model.wire_node("range", Range::new(r), &[start, limit, step])
1230 }
1231
1232 #[test]
1233 fn no_edge_consumer_gets_a_dep() -> TractResult<()> {
1234 let mut model = TypedModel::default();
1235 let range = range_over(&mut model, "r")?[0];
1236 let zero = model.add_const("zero", tensor0(0i64))?;
1237 let r = model.symbols.sym("r").to_dim();
1238 let expand = model.wire_node(
1239 "expand",
1240 MultiBroadcastTo::new(ShapeFact::from_dims(tvec![r, 2usize.to_dim()])),
1241 &[zero],
1242 )?[0];
1243 model.select_output_outlets(&[expand, range])?;
1244 assert_eq!(symbol_deps_of(&model), vec![(expand.node, range.node)]);
1246 Ok(())
1247 }
1248
1249 #[test]
1250 fn input_definers_yield_no_dep() -> TractResult<()> {
1251 let mut model = TypedModel::default();
1252 let s = model.symbols.sym("s").to_dim();
1253 let len = model.add_source("len", i64::datum_type().fact([s.clone()]))?;
1254 let zero = model.add_const("zero", tensor0(0i64))?;
1255 let expand = model.wire_node(
1256 "expand",
1257 MultiBroadcastTo::new(ShapeFact::from_dims(tvec![s, 2usize.to_dim()])),
1258 &[zero],
1259 )?[0];
1260 model.select_output_outlets(&[expand, len])?;
1261 assert!(symbol_deps_of(&model).is_empty());
1262 Ok(())
1263 }
1264
1265 #[test]
1266 fn tensor_ancestor_consumer_gets_no_dep() -> TractResult<()> {
1267 let mut model = TypedModel::default();
1268 let range = range_over(&mut model, "r")?[0];
1269 let casted =
1272 model.wire_node("downstream", crate::ops::cast::cast(f32::datum_type()), &[range])?;
1273 model.select_output_outlets(&[casted[0]])?;
1274 assert!(symbol_deps_of(&model).is_empty());
1275 Ok(())
1276 }
1277
1278 #[test]
1279 fn scan_forwards_body_requirements() -> TractResult<()> {
1280 use crate::ops::array::Range;
1281 use crate::ops::cast::cast;
1282 use crate::ops::konst::Const;
1283 use crate::ops::scan::{InputMapping, OutputMapping, Scan, ScanInfo};
1284 let mut model = TypedModel::default();
1285 let r = model.symbols.sym("b");
1286
1287 let limit = model.add_source("limit", i64::datum_type().scalar_fact())?;
1289 let limit = model.wire_node("limit_tdim", cast(TDim::datum_type()), &[limit])?;
1290 let start =
1291 model.wire_node("start", Const::new(tensor0(TDim::Val(0)).into_arc_tensor())?, &[])?;
1292 let step =
1293 model.wire_node("step", Const::new(tensor0(TDim::Val(1)).into_arc_tensor())?, &[])?;
1294 let range =
1295 model.wire_node("range", Range::new(r.to_dim()), &[start[0], limit[0], step[0]])?;
1296
1297 let mut body = TypedModel { symbols: model.symbols.clone(), ..TypedModel::default() };
1300 let state = body.add_source("state_in", i64::datum_type().fact([1usize]))?;
1301 let scan_in = body.add_source("scan_in", i64::datum_type().fact([1usize]))?;
1302 let konst = body.wire_node(
1303 "body_konst",
1304 Const::new(tensor0(TDim::Sym(r)).into_arc_tensor())?,
1305 &[],
1306 )?;
1307 let _casted = body.wire_node("body_cast", cast(f32::datum_type()), &[konst[0]])?;
1308 let sum = body.wire_node("sum", crate::ops::math::add(), &[state, scan_in])?;
1309 body.select_output_outlets(&[sum[0]])?;
1310 let init = model.add_const("init", tensor1(&[0i64]))?;
1311 let scanned = model.add_source("scanned", i64::datum_type().fact([3usize]))?;
1312 let scan = model.wire_node(
1313 "scan",
1314 Scan::new(
1315 body,
1316 vec![InputMapping::Scan(ScanInfo { axis: 0, chunk: 1 }), InputMapping::State],
1317 vec![OutputMapping {
1318 scan: Some((0, ScanInfo { axis: 0, chunk: 1 })),
1319 full_dim_hint: None,
1320 last_value_slot: None,
1321 state: true,
1322 }],
1323 0,
1324 )?,
1325 &[scanned, init],
1326 )?;
1327 model.select_output_outlets(&[scan[0], range[0]])?;
1328 let deps = symbol_deps_of(&model);
1329 assert!(
1330 deps.contains(&(scan[0].node, range[0].node)),
1331 "expected the scan ordered after the top-level binder, got {deps:?}"
1332 );
1333 Ok(())
1334 }
1335
1336 #[test]
1337 fn hidden_input_konst_mention_yields_dep() -> TractResult<()> {
1338 use crate::ops::array::Range;
1339 use crate::ops::cast::cast;
1340 let mut model = TypedModel::default();
1341 let limit = model.add_source("limit", i64::datum_type().scalar_fact())?;
1342 let limit = model.wire_node("limit_tdim", cast(TDim::datum_type()), &[limit])?;
1343 let start = model.add_const("start", tensor0(TDim::Val(0)))?;
1344 let step = model.add_const("step", tensor0(TDim::Val(1)))?;
1345 let r = model.symbols.sym("h");
1346 let range = model.wire_node("range", Range::new(r.to_dim()), &[start, limit[0], step])?;
1347 let konst = model.wire_node(
1351 "konst",
1352 crate::ops::konst::Const::new(
1353 tensor1(&[TDim::Sym(r.clone()), TDim::Sym(r)]).into_arc_tensor(),
1354 )?,
1355 &[],
1356 )?;
1357 let casted = model.wire_node("cast", cast(f32::datum_type()), &[konst[0]])?;
1358 model.select_output_outlets(&[casted[0], range[0]])?;
1359 let deps = symbol_deps_of(&model);
1360 assert!(
1361 deps.contains(&(casted[0].node, range[0].node)),
1362 "expected the cast ordered after the binder, got {deps:?}"
1363 );
1364 Ok(())
1365 }
1366
1367 #[test]
1368 fn dead_nodes_contribute_nothing() -> TractResult<()> {
1369 let mut model = TypedModel::default();
1370 let range = range_over(&mut model, "r")?[0];
1371 let zero = model.add_const("zero", tensor0(0i64))?;
1372 let r = model.symbols.sym("r").to_dim();
1373 let expand = model.wire_node(
1374 "expand",
1375 MultiBroadcastTo::new(ShapeFact::from_dims(tvec![r.clone(), 2usize.to_dim()])),
1376 &[zero],
1377 )?[0];
1378 model.select_output_outlets(&[expand, range])?;
1379 let before = symbol_deps_of(&model);
1380 model.wire_node(
1382 "dead",
1383 MultiBroadcastTo::new(ShapeFact::from_dims(tvec![r, 3usize.to_dim()])),
1384 &[zero],
1385 )?;
1386 assert_eq!(symbol_deps_of(&model), before);
1387 Ok(())
1388 }
1389}
1390
1391#[cfg(test)]
1392mod late_input_definer_tests {
1393 use super::*;
1394 use crate::ops::array::Range;
1395
1396 #[test]
1402 fn late_input_definer_wins() -> TractResult<()> {
1403 let mut model = TypedModel::default();
1404 let s = model.symbols.sym("S");
1405 let definer = model.add_source("definer", f32::datum_type().fact([s.to_dim()]))?;
1406 let start = model.add_const("start", tensor0(TDim::Val(0)))?;
1407 let step = model.add_const("step", tensor0(TDim::Val(1)))?;
1408 let end = model.add_const("end", tensor0(TDim::Sym(s.clone())))?;
1409 let range = model.wire_node("range", Range::new(s.to_dim()), &[start, end, step])?;
1410 model.select_output_outlets(&[range[0], definer])?;
1411 crate::internal::TypedSimplePlan::new(model)?;
1412 Ok(())
1413 }
1414}