Skip to main content

sc_neurocore_engine/ir/
printer.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// SC-NeuroCore — Text-format printer for SC IR graphs
8
9//! Text-format printer for SC IR graphs.
10//!
11//! # Format
12//!
13//! ```text
14//! sc.graph @module_name {
15//!   %0 = sc.input "x_in" : rate
16//!   %1 = sc.constant 0.5 : rate
17//!   %2 = sc.encode %0, length=1024, seed=0xACE1 : bitstream<1024>
18//!   %3 = sc.encode %1, length=1024, seed=0xBEEF : bitstream<1024>
19//!   %4 = sc.and %2, %3 : bitstream<1024>
20//!   %5 = sc.popcount %4 : u64
21//!   sc.output "result" %5
22//! }
23//! ```
24
25use crate::ir::graph::*;
26
27/// Print a graph to its text representation.
28pub fn print(graph: &ScGraph) -> String {
29    let mut out = String::new();
30    out.push_str(&format!("sc.graph @{} {{\n", graph.name));
31
32    for op in &graph.ops {
33        out.push_str("  ");
34        match op {
35            ScOp::Input { id, name, ty } => {
36                out.push_str(&format!("{} = sc.input \"{}\" : {}\n", id, name, ty));
37            }
38            ScOp::Output { name, source, .. } => {
39                out.push_str(&format!("sc.output \"{}\" {}\n", name, source));
40            }
41            ScOp::Constant { id, value, ty } => {
42                let val_str = match value {
43                    ScConst::F64(v) => format!("{v}"),
44                    ScConst::I64(v) => format!("{v}"),
45                    ScConst::U64(v) => format!("{v}"),
46                    ScConst::F64Vec(v) => format!(
47                        "[{}]",
48                        v.iter()
49                            .map(|x| format!("{x}"))
50                            .collect::<Vec<_>>()
51                            .join(", ")
52                    ),
53                    ScConst::I64Vec(v) => format!(
54                        "[{}]",
55                        v.iter()
56                            .map(|x| format!("{x}"))
57                            .collect::<Vec<_>>()
58                            .join(", ")
59                    ),
60                };
61                out.push_str(&format!("{} = sc.constant {} : {}\n", id, val_str, ty));
62            }
63            ScOp::Encode {
64                id,
65                prob,
66                length,
67                seed,
68            } => {
69                out.push_str(&format!(
70                    "{} = sc.encode {}, length={}, seed=0x{:04X} : bitstream<{}>\n",
71                    id, prob, length, seed, length
72                ));
73            }
74            ScOp::BitwiseAnd { id, lhs, rhs } => {
75                out.push_str(&format!("{} = sc.and {}, {} : bitstream\n", id, lhs, rhs));
76            }
77            ScOp::BitwiseXor { id, lhs, rhs } => {
78                out.push_str(&format!("{} = sc.xor {}, {} : bitstream\n", id, lhs, rhs));
79            }
80            ScOp::Popcount { id, input } => {
81                out.push_str(&format!("{} = sc.popcount {} : u64\n", id, input));
82            }
83            ScOp::Reduce { id, input, mode } => {
84                out.push_str(&format!(
85                    "{} = sc.reduce {}, mode={} : rate\n",
86                    id, input, mode
87                ));
88            }
89            ScOp::LifStep {
90                id,
91                current,
92                leak,
93                gain,
94                noise,
95                params,
96            } => {
97                out.push_str(&format!(
98                    "{} = sc.lif_step {}, leak={}, gain={}, noise={}, \
99                     dw={}, frac={}, vt={}, rp={} : (bool, fixed<{},{}>)\n",
100                    id,
101                    current,
102                    leak,
103                    gain,
104                    noise,
105                    params.data_width,
106                    params.fraction,
107                    params.v_threshold,
108                    params.refractory_period,
109                    params.data_width,
110                    params.fraction
111                ));
112            }
113            ScOp::DenseForward {
114                id,
115                inputs,
116                weights,
117                leak,
118                gain,
119                params,
120            } => {
121                out.push_str(&format!(
122                    "{} = sc.dense_forward {}, weights={}, leak={}, gain={}, \
123                     ni={}, nn={}, len={} : vec<bool,{}>\n",
124                    id,
125                    inputs,
126                    weights,
127                    leak,
128                    gain,
129                    params.n_inputs,
130                    params.n_neurons,
131                    params.stream_length,
132                    params.n_neurons
133                ));
134            }
135            ScOp::GraphForward {
136                id,
137                features,
138                adjacency,
139                n_nodes,
140                n_features,
141            } => {
142                out.push_str(&format!(
143                    "{} = sc.graph_forward {}, adj={}, nodes={}, features={} : rate\n",
144                    id, features, adjacency, n_nodes, n_features
145                ));
146            }
147            ScOp::SoftmaxAttention { id, q, k, v, dim_k } => {
148                out.push_str(&format!(
149                    "{} = sc.softmax_attention {}, {}, {}, dim_k={} : rate\n",
150                    id, q, k, v, dim_k
151                ));
152            }
153            ScOp::KuramotoStep {
154                id,
155                phases,
156                omega,
157                coupling,
158                dt,
159            } => {
160                out.push_str(&format!(
161                    "{} = sc.kuramoto_step {}, omega={}, K={}, dt={} : rate\n",
162                    id, phases, omega, coupling, dt
163                ));
164            }
165            ScOp::Scale { id, input, factor } => {
166                out.push_str(&format!(
167                    "{} = sc.scale {}, factor={} : rate\n",
168                    id, input, factor
169                ));
170            }
171            ScOp::Offset { id, input, offset } => {
172                out.push_str(&format!(
173                    "{} = sc.offset {}, offset={} : rate\n",
174                    id, input, offset
175                ));
176            }
177            ScOp::DivConst { id, input, divisor } => {
178                out.push_str(&format!(
179                    "{} = sc.div_const {}, divisor={} : u64\n",
180                    id, input, divisor
181                ));
182            }
183        }
184    }
185
186    out.push_str("}\n");
187    out
188}