Skip to main content

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}