1use rlx_ir::{DType, Dim, Graph, GraphExt, NodeId, Shape};
8
9use crate::Tensor;
10use crate::handle::GraphHandle;
11use crate::scalar::promote_scalar;
12
13#[derive(Debug)]
15pub struct GraphScope {
16 handle: GraphHandle,
17 next_dyn: u32,
19}
20
21impl GraphScope {
22 pub fn new(name: impl Into<String>) -> Self {
23 Self {
24 handle: GraphHandle::new(Graph::new(name)),
25 next_dyn: 0,
26 }
27 }
28
29 pub fn graph(&self) -> Graph {
30 self.handle.with_graph(|g| g.clone())
31 }
32
33 pub fn fresh_symbol(&mut self) -> u32 {
35 let s = self.next_dyn;
36 self.next_dyn += 1;
37 s
38 }
39
40 pub fn input(&mut self, name: impl Into<String>, shape: impl Into<Shape>) -> Tensor {
41 let id = self.handle.with_graph(|g| g.input(name, shape.into()));
42 Tensor::new(self.handle.clone(), id)
43 }
44
45 pub fn input_dyn(&mut self, name: impl Into<String>, dims: &[Dim], dtype: DType) -> Tensor {
47 let shape = Shape::from_dims(dims, dtype);
48 self.input(name, shape)
49 }
50
51 pub fn param(&mut self, name: impl Into<String>, shape: impl Into<Shape>) -> Tensor {
52 let id = self.handle.with_graph(|g| g.param(name, shape.into()));
53 Tensor::new(self.handle.clone(), id)
54 }
55
56 pub fn scalar(&mut self, value: f64) -> Tensor {
58 let id = self
59 .handle
60 .with_graph(|g| promote_scalar(g, value.into(), DType::F32));
61 Tensor::new(self.handle.clone(), id)
62 }
63
64 pub fn constant(&mut self, value: f64, dtype: DType) -> Tensor {
65 let id = self.handle.with_graph(|g| g.constant(value, dtype));
66 Tensor::new(self.handle.clone(), id)
67 }
68
69 pub fn constant_nd(&mut self, data: Vec<f64>, dims: Vec<usize>, dtype: DType) -> Tensor {
74 let n: usize = dims.iter().product();
75 assert_eq!(
76 data.len(),
77 n,
78 "constant_nd: {} values for shape {:?} ({n} expected)",
79 data.len(),
80 dims
81 );
82 let bytes: Vec<u8> = match dtype {
83 DType::F32 => data
84 .iter()
85 .flat_map(|v| (*v as f32).to_le_bytes())
86 .collect(),
87 DType::F64 => data.iter().flat_map(|v| v.to_le_bytes()).collect(),
88 DType::I32 => data
89 .iter()
90 .flat_map(|v| (*v as i32).to_le_bytes())
91 .collect(),
92 DType::I64 => data
93 .iter()
94 .flat_map(|v| (*v as i64).to_le_bytes())
95 .collect(),
96 DType::U8 => data.iter().map(|v| *v as u8).collect(),
97 other => panic!("constant_nd: unsupported dtype {other:?} (use F32/F64/I32/I64/U8)"),
98 };
99 let shape = Shape::new(&dims, dtype);
100 let id = self
101 .handle
102 .with_graph(|g| g.add_node(rlx_ir::Op::Constant { data: bytes }, vec![], shape));
103 Tensor::new(self.handle.clone(), id)
104 }
105
106 pub fn scan_block(
124 &mut self,
125 init: &Tensor,
126 bcasts: &[&Tensor],
127 length: u32,
128 body: impl FnOnce(&Tensor, &[Tensor]) -> Tensor,
129 ) -> Tensor {
130 let init_id = self.handle.adopt(&init.handle, init.id);
132 let bcast_ids: Vec<NodeId> = bcasts
133 .iter()
134 .map(|b| self.handle.adopt(&b.handle, b.id))
135 .collect();
136
137 let mut inner = GraphScope::new("scan_body");
141 let carry = inner.input("carry", init.shape());
142 let bcast_ins: Vec<Tensor> = bcasts
143 .iter()
144 .enumerate()
145 .map(|(i, b)| inner.input(format!("bcast_{i}"), b.shape()))
146 .collect();
147 let next = body(&carry, &bcast_ins);
148 inner.set_outputs([next.id]);
149 let body_graph = inner.finish();
150
151 let id = self.handle.with_graph(|g| {
152 g.scan_with_bcasts_and_xs(init_id, &bcast_ids, &[], body_graph, length)
153 });
154 Tensor::new(self.handle.clone(), id)
155 }
156
157 pub fn cat(&mut self, tensors: &[&Tensor], axis: usize) -> Tensor {
159 let ids: Vec<NodeId> = tensors.iter().map(|t| t.id).collect();
160 let id = self.handle.with_graph(|g| g.concat_(ids, axis));
161 Tensor::new(self.handle.clone(), id)
162 }
163
164 pub fn set_outputs(&mut self, outputs: impl IntoIterator<Item = impl Into<NodeId>>) {
165 let ids: Vec<NodeId> = outputs.into_iter().map(Into::into).collect();
166 self.handle.with_graph(|g| g.set_outputs(ids));
167 }
168
169 pub fn finish(self) -> Graph {
170 self.handle.with_graph(|g| g.clone())
171 }
172}
173
174pub fn graph<F>(name: impl Into<String>, build: F) -> Graph
188where
189 F: FnOnce(&mut GraphScope) -> Tensor,
190{
191 let mut scope = GraphScope::new(name);
192 let out = build(&mut scope);
193 scope.set_outputs([out]);
194 scope.finish()
195}
196
197pub fn graph_with<F, R>(name: impl Into<String>, build: F) -> (Graph, R)
199where
200 F: FnOnce(&mut GraphScope) -> R,
201{
202 let mut scope = GraphScope::new(name);
203 let result = build(&mut scope);
204 (scope.finish(), result)
205}