Skip to main content

sc_neurocore_engine/ir/
printer.rs

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