openinfer_simulator/simulator.rs
1//! Simulator entry point for graph validation and execution.
2//!
3//! The simulator validates the graph against a model and then produces an
4//! `Executor` that runs the graph deterministically on the chosen device.
5use std::sync::Arc;
6
7use anyhow::{anyhow, Result};
8
9use crate::graph::Graph;
10use crate::runtime::ModelLoader;
11pub use crate::runtime::{Executor, Fetchable, TraceEvent, TraceEventKind};
12
13/// Execution device selection for a simulation run.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum Device {
16 /// CPU-only execution.
17 Cpu,
18 /// Vulkan execution (requires `vulkan` feature).
19 Vulkan,
20}
21
22impl Device {
23 /// Returns true if the device is available in the current build.
24 pub fn is_supported(&self) -> bool {
25 match self {
26 Device::Cpu => true,
27 Device::Vulkan => cfg!(feature = "vulkan"),
28 }
29 }
30}
31
32/// High-level entry point for validating and executing a graph.
33#[derive(Debug)]
34pub struct Simulator {
35 model: Arc<ModelLoader>,
36 graph: Graph,
37 device: Device,
38 trace_enabled: bool,
39 timer_enabled: bool,
40}
41
42impl Simulator {
43 /// Create a new simulator for a graph and model on a device.
44 ///
45 /// This validates the graph, initializes the op registry, and warms kernels
46 /// for the chosen device.
47 ///
48 /// # Example
49 /// ```no_run
50 /// # use openinfer::{ModelLoader, Simulator, Device, graph};
51 /// # fn main() -> anyhow::Result<()> {
52 /// let model = ModelLoader::open("model.oinf")?;
53 /// let g = graph! { block entry { return; } };
54 /// let sim = Simulator::new(&model, &g, Device::Cpu)?;
55 /// let _exec = sim.make_executor()?;
56 /// # Ok(()) }
57 /// ```
58 pub fn new(model: &ModelLoader, graph: &Graph, device: Device) -> Result<Self> {
59 if !device.is_supported() {
60 return Err(anyhow!("device {:?} not supported for this build", device));
61 }
62 crate::op_defs::init_ops_registry();
63 crate::runtime::validation::validate_graph(model, graph)?;
64 Self::warm_kernels_for_device(device);
65 Ok(Self {
66 model: Arc::new(model.clone()),
67 graph: graph.clone(),
68 device,
69 trace_enabled: false,
70 timer_enabled: false,
71 })
72 }
73
74 /// Enable execution tracing for the next executor.
75 pub fn with_trace(mut self) -> Self {
76 self.trace_enabled = true;
77 self
78 }
79
80 /// Enable timing capture for the next executor.
81 pub fn with_timer(mut self) -> Self {
82 self.timer_enabled = true;
83 self
84 }
85
86 /// Build an executor with the current simulator configuration.
87 ///
88 /// # Example
89 /// ```no_run
90 /// # use openinfer::{ModelLoader, Simulator, Device, graph};
91 /// # fn main() -> anyhow::Result<()> {
92 /// let model = ModelLoader::open("model.oinf")?;
93 /// let g = graph! { block entry { return; } };
94 /// let sim = Simulator::new(&model, &g, Device::Cpu)?;
95 /// let mut exec = sim.make_executor()?;
96 /// exec.step()?;
97 /// # Ok(()) }
98 /// ```
99 pub fn make_executor(&self) -> Result<Executor> {
100 Executor::new(
101 self.model.clone(),
102 self.graph.clone(),
103 self.device,
104 self.trace_enabled,
105 self.timer_enabled,
106 )
107 }
108
109 fn warm_kernels_for_device(_device: Device) {
110 crate::ops::cpu::registry::warm_kernels();
111
112 #[cfg(feature = "vulkan")] {
113 if _device == Device::Vulkan {
114 crate::ops::vulkan::registry::warm_kernels();
115 }
116 }
117
118
119 }
120}