Skip to main content

sc_neurocore_engine/ir/
builder.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 — Fluent builder for `ScGraph`
8
9//! Fluent builder for `ScGraph`.
10
11use crate::ir::graph::*;
12
13/// Builder for constructing `ScGraph` instances.
14pub struct ScGraphBuilder {
15    graph: ScGraph,
16}
17
18impl ScGraphBuilder {
19    /// Start building a new graph with the given module name.
20    pub fn new(name: impl Into<String>) -> Self {
21        Self {
22            graph: ScGraph::new(name),
23        }
24    }
25
26    /// Add a module input port.
27    pub fn input(&mut self, name: impl Into<String>, ty: ScType) -> ValueId {
28        let id = self.graph.fresh_id();
29        self.graph.push(ScOp::Input {
30            id,
31            name: name.into(),
32            ty,
33        })
34    }
35
36    /// Add a module output port.
37    pub fn output(&mut self, name: impl Into<String>, source: ValueId) -> ValueId {
38        let id = self.graph.fresh_id();
39        self.graph.push(ScOp::Output {
40            id,
41            name: name.into(),
42            source,
43        })
44    }
45
46    /// Add a compile-time constant.
47    pub fn constant(&mut self, value: ScConst, ty: ScType) -> ValueId {
48        let id = self.graph.fresh_id();
49        self.graph.push(ScOp::Constant { id, value, ty })
50    }
51
52    /// Add a bitstream encode operation.
53    pub fn encode(&mut self, prob: ValueId, length: usize, seed: u16) -> ValueId {
54        let id = self.graph.fresh_id();
55        self.graph.push(ScOp::Encode {
56            id,
57            prob,
58            length,
59            seed,
60        })
61    }
62
63    /// Add a bitwise AND (stochastic multiply).
64    pub fn bitwise_and(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
65        let id = self.graph.fresh_id();
66        self.graph.push(ScOp::BitwiseAnd { id, lhs, rhs })
67    }
68
69    /// Add a popcount operation.
70    pub fn popcount(&mut self, input: ValueId) -> ValueId {
71        let id = self.graph.fresh_id();
72        self.graph.push(ScOp::Popcount { id, input })
73    }
74
75    /// Add a LIF neuron step.
76    pub fn lif_step(
77        &mut self,
78        current: ValueId,
79        leak: ValueId,
80        gain: ValueId,
81        noise: ValueId,
82        params: LifParams,
83    ) -> ValueId {
84        let id = self.graph.fresh_id();
85        self.graph.push(ScOp::LifStep {
86            id,
87            current,
88            leak,
89            gain,
90            noise,
91            params,
92        })
93    }
94
95    /// Add a dense SC layer forward pass.
96    pub fn dense_forward(
97        &mut self,
98        inputs: ValueId,
99        weights: ValueId,
100        leak: ValueId,
101        gain: ValueId,
102        params: DenseParams,
103    ) -> ValueId {
104        let id = self.graph.fresh_id();
105        self.graph.push(ScOp::DenseForward {
106            id,
107            inputs,
108            weights,
109            leak,
110            gain,
111            params,
112        })
113    }
114
115    /// Add a delay-coded learnable-spike layer forward pass.
116    pub fn dcls_layer(
117        &mut self,
118        spike: ValueId,
119        weights: ValueId,
120        centre: ValueId,
121        sigma: ValueId,
122        params: DclsParams,
123    ) -> ValueId {
124        let id = self.graph.fresh_id();
125        self.graph.push(ScOp::DclsLayer {
126            id,
127            spike,
128            weights,
129            centre,
130            sigma,
131            params,
132        })
133    }
134
135    /// Add a bitwise XOR (HDC binding).
136    pub fn bitwise_xor(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
137        let id = self.graph.fresh_id();
138        self.graph.push(ScOp::BitwiseXor { id, lhs, rhs })
139    }
140
141    /// Add a reduce operation (Sum or Max).
142    pub fn reduce(&mut self, input: ValueId, mode: ReduceMode) -> ValueId {
143        let id = self.graph.fresh_id();
144        self.graph.push(ScOp::Reduce { id, input, mode })
145    }
146
147    /// Add a graph forward pass.
148    pub fn graph_forward(
149        &mut self,
150        features: ValueId,
151        adjacency: ValueId,
152        n_nodes: usize,
153        n_features: usize,
154    ) -> ValueId {
155        let id = self.graph.fresh_id();
156        self.graph.push(ScOp::GraphForward {
157            id,
158            features,
159            adjacency,
160            n_nodes,
161            n_features,
162        })
163    }
164
165    /// Add a softmax attention operation.
166    pub fn softmax_attention(
167        &mut self,
168        q: ValueId,
169        k: ValueId,
170        v: ValueId,
171        dim_k: usize,
172    ) -> ValueId {
173        let id = self.graph.fresh_id();
174        self.graph
175            .push(ScOp::SoftmaxAttention { id, q, k, v, dim_k })
176    }
177
178    /// Add a Kuramoto integration step.
179    pub fn kuramoto_step(
180        &mut self,
181        phases: ValueId,
182        omega: ValueId,
183        coupling: ValueId,
184        dt: f64,
185    ) -> ValueId {
186        let id = self.graph.fresh_id();
187        self.graph.push(ScOp::KuramotoStep {
188            id,
189            phases,
190            omega,
191            coupling,
192            dt,
193        })
194    }
195
196    /// Add a scale (multiply by constant) operation.
197    pub fn scale(&mut self, input: ValueId, factor: f64) -> ValueId {
198        let id = self.graph.fresh_id();
199        self.graph.push(ScOp::Scale { id, input, factor })
200    }
201
202    /// Add an offset (add constant) operation.
203    pub fn offset(&mut self, input: ValueId, offset_val: f64) -> ValueId {
204        let id = self.graph.fresh_id();
205        self.graph.push(ScOp::Offset {
206            id,
207            input,
208            offset: offset_val,
209        })
210    }
211
212    /// Add a constant integer division.
213    pub fn div_const(&mut self, input: ValueId, divisor: u64) -> ValueId {
214        let id = self.graph.fresh_id();
215        self.graph.push(ScOp::DivConst { id, input, divisor })
216    }
217
218    /// Consume the builder and return the completed graph.
219    pub fn build(self) -> ScGraph {
220        self.graph
221    }
222}