1use std::fmt;
11
12#[derive(Debug, Clone, PartialEq)]
16pub enum ScType {
17 Bitstream { length: usize },
19 FixedPoint { width: u32, frac: u32 },
21 Rate,
23 UInt { width: u32 },
25 SInt { width: u32 },
27 Bool,
29 Vec { element: Box<ScType>, count: usize },
31}
32
33impl ScType {
34 pub fn bit_width(&self) -> usize {
36 match self {
37 Self::Bool => 1,
38 Self::Rate => 16, Self::UInt { width } | Self::SInt { width } => *width as usize,
40 Self::FixedPoint { width, .. } => *width as usize,
41 Self::Bitstream { .. } => 1, Self::Vec { element, count } => element.bit_width() * count,
43 }
44 }
45}
46
47impl fmt::Display for ScType {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Self::Bitstream { length } => write!(f, "bitstream<{length}>"),
51 Self::FixedPoint { width, frac } => write!(f, "fixed<{width},{frac}>"),
52 Self::Rate => write!(f, "rate"),
53 Self::UInt { width } => write!(f, "u{width}"),
54 Self::SInt { width } => write!(f, "i{width}"),
55 Self::Bool => write!(f, "bool"),
56 Self::Vec { element, count } => write!(f, "vec<{element},{count}>"),
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
65pub struct ValueId(pub u32);
66
67impl fmt::Display for ValueId {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "%{}", self.0)
70 }
71}
72
73#[derive(Debug, Clone, PartialEq)]
77pub enum ScConst {
78 F64(f64),
80 I64(i64),
82 U64(u64),
84 F64Vec(Vec<f64>),
86 I64Vec(Vec<i64>),
88}
89
90#[derive(Debug, Clone, PartialEq)]
95pub struct LifParams {
96 pub data_width: u32,
97 pub fraction: u32,
98 pub v_rest: i64,
99 pub v_reset: i64,
100 pub v_threshold: i64,
101 pub refractory_period: u32,
102}
103
104impl Default for LifParams {
105 fn default() -> Self {
106 Self {
107 data_width: 16,
108 fraction: 8,
109 v_rest: 0,
110 v_reset: 0,
111 v_threshold: 256, refractory_period: 2,
113 }
114 }
115}
116
117#[derive(Debug, Clone, PartialEq)]
122pub struct DenseParams {
123 pub n_inputs: usize,
124 pub n_neurons: usize,
125 pub data_width: u32,
126 pub stream_length: usize,
128 pub input_seed_base: u16,
130 pub weight_seed_base: u16,
132 pub y_min: i64,
134 pub y_max: i64,
136}
137
138impl Default for DenseParams {
139 fn default() -> Self {
140 Self {
141 n_inputs: 3,
142 n_neurons: 7,
143 data_width: 16,
144 stream_length: 1024,
145 input_seed_base: 0xACE1,
146 weight_seed_base: 0xBEEF,
147 y_min: 0,
148 y_max: 256, }
150 }
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum ReduceMode {
156 Sum,
157 Max,
158}
159
160impl fmt::Display for ReduceMode {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 Self::Sum => write!(f, "sum"),
164 Self::Max => write!(f, "max"),
165 }
166 }
167}
168
169#[derive(Debug, Clone, PartialEq)]
176pub enum ScOp {
177 Input {
180 id: ValueId,
181 name: String,
182 ty: ScType,
183 },
184
185 Output {
188 id: ValueId,
189 name: String,
190 source: ValueId,
191 },
192
193 Constant {
195 id: ValueId,
196 value: ScConst,
197 ty: ScType,
198 },
199
200 Encode {
204 id: ValueId,
205 prob: ValueId,
207 length: usize,
209 seed: u16,
211 },
212
213 BitwiseAnd {
216 id: ValueId,
217 lhs: ValueId,
218 rhs: ValueId,
219 },
220
221 Popcount { id: ValueId, input: ValueId },
224
225 LifStep {
229 id: ValueId,
230 current: ValueId,
232 leak: ValueId,
234 gain: ValueId,
236 noise: ValueId,
238 params: LifParams,
240 },
241
242 DenseForward {
246 id: ValueId,
247 inputs: ValueId,
249 weights: ValueId,
251 leak: ValueId,
253 gain: ValueId,
255 params: DenseParams,
257 },
258
259 BitwiseXor {
262 id: ValueId,
263 lhs: ValueId,
264 rhs: ValueId,
265 },
266
267 Reduce {
270 id: ValueId,
271 input: ValueId,
272 mode: ReduceMode,
273 },
274
275 GraphForward {
278 id: ValueId,
279 features: ValueId,
280 adjacency: ValueId,
281 n_nodes: usize,
282 n_features: usize,
283 },
284
285 SoftmaxAttention {
288 id: ValueId,
289 q: ValueId,
290 k: ValueId,
291 v: ValueId,
292 dim_k: usize,
293 },
294
295 KuramotoStep {
298 id: ValueId,
299 phases: ValueId,
300 omega: ValueId,
301 coupling: ValueId,
302 dt: f64,
303 },
304
305 Scale {
308 id: ValueId,
309 input: ValueId,
310 factor: f64,
311 },
312
313 Offset {
315 id: ValueId,
316 input: ValueId,
317 offset: f64,
318 },
319
320 DivConst {
322 id: ValueId,
323 input: ValueId,
324 divisor: u64,
325 },
326}
327
328impl ScOp {
329 pub fn result_id(&self) -> ValueId {
331 match self {
332 Self::Input { id, .. }
333 | Self::Output { id, .. }
334 | Self::Constant { id, .. }
335 | Self::Encode { id, .. }
336 | Self::BitwiseAnd { id, .. }
337 | Self::BitwiseXor { id, .. }
338 | Self::Popcount { id, .. }
339 | Self::Reduce { id, .. }
340 | Self::LifStep { id, .. }
341 | Self::DenseForward { id, .. }
342 | Self::GraphForward { id, .. }
343 | Self::SoftmaxAttention { id, .. }
344 | Self::KuramotoStep { id, .. }
345 | Self::Scale { id, .. }
346 | Self::Offset { id, .. }
347 | Self::DivConst { id, .. } => *id,
348 }
349 }
350
351 pub fn operands(&self) -> Vec<ValueId> {
353 match self {
354 Self::Input { .. } | Self::Constant { .. } => vec![],
355 Self::Output { source, .. } => vec![*source],
356 Self::Encode { prob, .. } => vec![*prob],
357 Self::BitwiseAnd { lhs, rhs, .. } | Self::BitwiseXor { lhs, rhs, .. } => {
358 vec![*lhs, *rhs]
359 }
360 Self::Popcount { input, .. } | Self::Reduce { input, .. } => vec![*input],
361 Self::LifStep {
362 current,
363 leak,
364 gain,
365 noise,
366 ..
367 } => vec![*current, *leak, *gain, *noise],
368 Self::DenseForward {
369 inputs,
370 weights,
371 leak,
372 gain,
373 ..
374 } => vec![*inputs, *weights, *leak, *gain],
375 Self::GraphForward {
376 features,
377 adjacency,
378 ..
379 } => vec![*features, *adjacency],
380 Self::SoftmaxAttention { q, k, v, .. } => vec![*q, *k, *v],
381 Self::KuramotoStep {
382 phases,
383 omega,
384 coupling,
385 ..
386 } => vec![*phases, *omega, *coupling],
387 Self::Scale { input, .. }
388 | Self::Offset { input, .. }
389 | Self::DivConst { input, .. } => {
390 vec![*input]
391 }
392 }
393 }
394
395 pub fn op_name(&self) -> &'static str {
397 match self {
398 Self::Input { .. } => "sc.input",
399 Self::Output { .. } => "sc.output",
400 Self::Constant { .. } => "sc.constant",
401 Self::Encode { .. } => "sc.encode",
402 Self::BitwiseAnd { .. } => "sc.and",
403 Self::BitwiseXor { .. } => "sc.xor",
404 Self::Popcount { .. } => "sc.popcount",
405 Self::Reduce { .. } => "sc.reduce",
406 Self::LifStep { .. } => "sc.lif_step",
407 Self::DenseForward { .. } => "sc.dense_forward",
408 Self::GraphForward { .. } => "sc.graph_forward",
409 Self::SoftmaxAttention { .. } => "sc.softmax_attention",
410 Self::KuramotoStep { .. } => "sc.kuramoto_step",
411 Self::Scale { .. } => "sc.scale",
412 Self::Offset { .. } => "sc.offset",
413 Self::DivConst { .. } => "sc.div_const",
414 }
415 }
416}
417
418#[derive(Debug, Clone, PartialEq)]
425pub struct ScGraph {
426 pub name: String,
428 pub ops: Vec<ScOp>,
430 pub(crate) next_id: u32,
432}
433
434impl ScGraph {
435 pub fn new(name: impl Into<String>) -> Self {
437 Self {
438 name: name.into(),
439 ops: Vec::new(),
440 next_id: 0,
441 }
442 }
443
444 pub fn fresh_id(&mut self) -> ValueId {
446 let id = ValueId(self.next_id);
447 self.next_id += 1;
448 id
449 }
450
451 pub fn push(&mut self, op: ScOp) -> ValueId {
453 let id = op.result_id();
454 self.ops.push(op);
455 id
456 }
457
458 pub fn len(&self) -> usize {
460 self.ops.len()
461 }
462
463 pub fn is_empty(&self) -> bool {
465 self.ops.is_empty()
466 }
467
468 pub fn inputs(&self) -> Vec<&ScOp> {
470 self.ops
471 .iter()
472 .filter(|op| matches!(op, ScOp::Input { .. }))
473 .collect()
474 }
475
476 pub fn outputs(&self) -> Vec<&ScOp> {
478 self.ops
479 .iter()
480 .filter(|op| matches!(op, ScOp::Output { .. }))
481 .collect()
482 }
483}