1use std::fmt;
12
13#[derive(Debug, Clone, PartialEq)]
17pub enum ScType {
18 Bitstream { length: usize },
20 FixedPoint { width: u32, frac: u32 },
22 Rate,
24 UInt { width: u32 },
26 SInt { width: u32 },
28 Bool,
30 Vec { element: Box<ScType>, count: usize },
32}
33
34impl ScType {
35 pub fn bit_width(&self) -> usize {
37 match self {
38 Self::Bool => 1,
39 Self::Rate => 16, Self::UInt { width } | Self::SInt { width } => *width as usize,
41 Self::FixedPoint { width, .. } => *width as usize,
42 Self::Bitstream { .. } => 1, Self::Vec { element, count } => element.bit_width() * count,
44 }
45 }
46}
47
48impl fmt::Display for ScType {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::Bitstream { length } => write!(f, "bitstream<{length}>"),
52 Self::FixedPoint { width, frac } => write!(f, "fixed<{width},{frac}>"),
53 Self::Rate => write!(f, "rate"),
54 Self::UInt { width } => write!(f, "u{width}"),
55 Self::SInt { width } => write!(f, "i{width}"),
56 Self::Bool => write!(f, "bool"),
57 Self::Vec { element, count } => write!(f, "vec<{element},{count}>"),
58 }
59 }
60}
61
62#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
66pub struct ValueId(pub u32);
67
68impl fmt::Display for ValueId {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "%{}", self.0)
71 }
72}
73
74#[derive(Debug, Clone, PartialEq)]
78pub enum ScConst {
79 F64(f64),
81 I64(i64),
83 U64(u64),
85 F64Vec(Vec<f64>),
87 I64Vec(Vec<i64>),
89}
90
91#[derive(Debug, Clone, PartialEq)]
96pub struct LifParams {
97 pub data_width: u32,
98 pub fraction: u32,
99 pub v_rest: i64,
100 pub v_reset: i64,
101 pub v_threshold: i64,
102 pub refractory_period: u32,
103}
104
105impl Default for LifParams {
106 fn default() -> Self {
107 Self {
108 data_width: 16,
109 fraction: 8,
110 v_rest: 0,
111 v_reset: 0,
112 v_threshold: 256, refractory_period: 2,
114 }
115 }
116}
117
118#[derive(Debug, Clone, PartialEq)]
123pub struct DenseParams {
124 pub n_inputs: usize,
125 pub n_neurons: usize,
126 pub data_width: u32,
127 pub stream_length: usize,
129 pub input_seed_base: u16,
131 pub weight_seed_base: u16,
133 pub y_min: i64,
135 pub y_max: i64,
137}
138
139impl Default for DenseParams {
140 fn default() -> Self {
141 Self {
142 n_inputs: 3,
143 n_neurons: 7,
144 data_width: 16,
145 stream_length: 1024,
146 input_seed_base: 0xACE1,
147 weight_seed_base: 0xBEEF,
148 y_min: 0,
149 y_max: 256, }
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum ReduceMode {
157 Sum,
158 Max,
159}
160
161impl fmt::Display for ReduceMode {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 match self {
164 Self::Sum => write!(f, "sum"),
165 Self::Max => write!(f, "max"),
166 }
167 }
168}
169
170#[derive(Debug, Clone, PartialEq)]
177pub enum ScOp {
178 Input {
181 id: ValueId,
182 name: String,
183 ty: ScType,
184 },
185
186 Output {
189 id: ValueId,
190 name: String,
191 source: ValueId,
192 },
193
194 Constant {
196 id: ValueId,
197 value: ScConst,
198 ty: ScType,
199 },
200
201 Encode {
205 id: ValueId,
206 prob: ValueId,
208 length: usize,
210 seed: u16,
212 },
213
214 BitwiseAnd {
217 id: ValueId,
218 lhs: ValueId,
219 rhs: ValueId,
220 },
221
222 Popcount { id: ValueId, input: ValueId },
225
226 LifStep {
230 id: ValueId,
231 current: ValueId,
233 leak: ValueId,
235 gain: ValueId,
237 noise: ValueId,
239 params: LifParams,
241 },
242
243 DenseForward {
247 id: ValueId,
248 inputs: ValueId,
250 weights: ValueId,
252 leak: ValueId,
254 gain: ValueId,
256 params: DenseParams,
258 },
259
260 BitwiseXor {
263 id: ValueId,
264 lhs: ValueId,
265 rhs: ValueId,
266 },
267
268 Reduce {
271 id: ValueId,
272 input: ValueId,
273 mode: ReduceMode,
274 },
275
276 GraphForward {
279 id: ValueId,
280 features: ValueId,
281 adjacency: ValueId,
282 n_nodes: usize,
283 n_features: usize,
284 },
285
286 SoftmaxAttention {
289 id: ValueId,
290 q: ValueId,
291 k: ValueId,
292 v: ValueId,
293 dim_k: usize,
294 },
295
296 KuramotoStep {
299 id: ValueId,
300 phases: ValueId,
301 omega: ValueId,
302 coupling: ValueId,
303 dt: f64,
304 },
305
306 Scale {
309 id: ValueId,
310 input: ValueId,
311 factor: f64,
312 },
313
314 Offset {
316 id: ValueId,
317 input: ValueId,
318 offset: f64,
319 },
320
321 DivConst {
323 id: ValueId,
324 input: ValueId,
325 divisor: u64,
326 },
327}
328
329impl ScOp {
330 pub fn result_id(&self) -> ValueId {
332 match self {
333 Self::Input { id, .. }
334 | Self::Output { id, .. }
335 | Self::Constant { id, .. }
336 | Self::Encode { id, .. }
337 | Self::BitwiseAnd { id, .. }
338 | Self::BitwiseXor { id, .. }
339 | Self::Popcount { id, .. }
340 | Self::Reduce { id, .. }
341 | Self::LifStep { id, .. }
342 | Self::DenseForward { id, .. }
343 | Self::GraphForward { id, .. }
344 | Self::SoftmaxAttention { id, .. }
345 | Self::KuramotoStep { id, .. }
346 | Self::Scale { id, .. }
347 | Self::Offset { id, .. }
348 | Self::DivConst { id, .. } => *id,
349 }
350 }
351
352 pub fn operands(&self) -> Vec<ValueId> {
354 match self {
355 Self::Input { .. } | Self::Constant { .. } => vec![],
356 Self::Output { source, .. } => vec![*source],
357 Self::Encode { prob, .. } => vec![*prob],
358 Self::BitwiseAnd { lhs, rhs, .. } | Self::BitwiseXor { lhs, rhs, .. } => {
359 vec![*lhs, *rhs]
360 }
361 Self::Popcount { input, .. } | Self::Reduce { input, .. } => vec![*input],
362 Self::LifStep {
363 current,
364 leak,
365 gain,
366 noise,
367 ..
368 } => vec![*current, *leak, *gain, *noise],
369 Self::DenseForward {
370 inputs,
371 weights,
372 leak,
373 gain,
374 ..
375 } => vec![*inputs, *weights, *leak, *gain],
376 Self::GraphForward {
377 features,
378 adjacency,
379 ..
380 } => vec![*features, *adjacency],
381 Self::SoftmaxAttention { q, k, v, .. } => vec![*q, *k, *v],
382 Self::KuramotoStep {
383 phases,
384 omega,
385 coupling,
386 ..
387 } => vec![*phases, *omega, *coupling],
388 Self::Scale { input, .. }
389 | Self::Offset { input, .. }
390 | Self::DivConst { input, .. } => {
391 vec![*input]
392 }
393 }
394 }
395
396 pub fn op_name(&self) -> &'static str {
398 match self {
399 Self::Input { .. } => "sc.input",
400 Self::Output { .. } => "sc.output",
401 Self::Constant { .. } => "sc.constant",
402 Self::Encode { .. } => "sc.encode",
403 Self::BitwiseAnd { .. } => "sc.and",
404 Self::BitwiseXor { .. } => "sc.xor",
405 Self::Popcount { .. } => "sc.popcount",
406 Self::Reduce { .. } => "sc.reduce",
407 Self::LifStep { .. } => "sc.lif_step",
408 Self::DenseForward { .. } => "sc.dense_forward",
409 Self::GraphForward { .. } => "sc.graph_forward",
410 Self::SoftmaxAttention { .. } => "sc.softmax_attention",
411 Self::KuramotoStep { .. } => "sc.kuramoto_step",
412 Self::Scale { .. } => "sc.scale",
413 Self::Offset { .. } => "sc.offset",
414 Self::DivConst { .. } => "sc.div_const",
415 }
416 }
417}
418
419#[derive(Debug, Clone, PartialEq)]
426pub struct ScGraph {
427 pub name: String,
429 pub ops: Vec<ScOp>,
431 pub(crate) next_id: u32,
433}
434
435impl ScGraph {
436 pub fn new(name: impl Into<String>) -> Self {
438 Self {
439 name: name.into(),
440 ops: Vec::new(),
441 next_id: 0,
442 }
443 }
444
445 pub fn fresh_id(&mut self) -> ValueId {
447 let id = ValueId(self.next_id);
448 self.next_id += 1;
449 id
450 }
451
452 pub fn push(&mut self, op: ScOp) -> ValueId {
454 let id = op.result_id();
455 self.ops.push(op);
456 id
457 }
458
459 pub fn len(&self) -> usize {
461 self.ops.len()
462 }
463
464 pub fn is_empty(&self) -> bool {
466 self.ops.is_empty()
467 }
468
469 pub fn inputs(&self) -> Vec<&ScOp> {
471 self.ops
472 .iter()
473 .filter(|op| matches!(op, ScOp::Input { .. }))
474 .collect()
475 }
476
477 pub fn outputs(&self) -> Vec<&ScOp> {
479 self.ops
480 .iter()
481 .filter(|op| matches!(op, ScOp::Output { .. }))
482 .collect()
483 }
484}