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 bitwise XOR (HDC binding).
116    pub fn bitwise_xor(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
117        let id = self.graph.fresh_id();
118        self.graph.push(ScOp::BitwiseXor { id, lhs, rhs })
119    }
120
121    /// Add a reduce operation (Sum or Max).
122    pub fn reduce(&mut self, input: ValueId, mode: ReduceMode) -> ValueId {
123        let id = self.graph.fresh_id();
124        self.graph.push(ScOp::Reduce { id, input, mode })
125    }
126
127    /// Add a graph forward pass.
128    pub fn graph_forward(
129        &mut self,
130        features: ValueId,
131        adjacency: ValueId,
132        n_nodes: usize,
133        n_features: usize,
134    ) -> ValueId {
135        let id = self.graph.fresh_id();
136        self.graph.push(ScOp::GraphForward {
137            id,
138            features,
139            adjacency,
140            n_nodes,
141            n_features,
142        })
143    }
144
145    /// Add a softmax attention operation.
146    pub fn softmax_attention(
147        &mut self,
148        q: ValueId,
149        k: ValueId,
150        v: ValueId,
151        dim_k: usize,
152    ) -> ValueId {
153        let id = self.graph.fresh_id();
154        self.graph
155            .push(ScOp::SoftmaxAttention { id, q, k, v, dim_k })
156    }
157
158    /// Add a Kuramoto integration step.
159    pub fn kuramoto_step(
160        &mut self,
161        phases: ValueId,
162        omega: ValueId,
163        coupling: ValueId,
164        dt: f64,
165    ) -> ValueId {
166        let id = self.graph.fresh_id();
167        self.graph.push(ScOp::KuramotoStep {
168            id,
169            phases,
170            omega,
171            coupling,
172            dt,
173        })
174    }
175
176    /// Add a scale (multiply by constant) operation.
177    pub fn scale(&mut self, input: ValueId, factor: f64) -> ValueId {
178        let id = self.graph.fresh_id();
179        self.graph.push(ScOp::Scale { id, input, factor })
180    }
181
182    /// Add an offset (add constant) operation.
183    pub fn offset(&mut self, input: ValueId, offset_val: f64) -> ValueId {
184        let id = self.graph.fresh_id();
185        self.graph.push(ScOp::Offset {
186            id,
187            input,
188            offset: offset_val,
189        })
190    }
191
192    /// Add a constant integer division.
193    pub fn div_const(&mut self, input: ValueId, divisor: u64) -> ValueId {
194        let id = self.graph.fresh_id();
195        self.graph.push(ScOp::DivConst { id, input, divisor })
196    }
197
198    /// Consume the builder and return the completed graph.
199    pub fn build(self) -> ScGraph {
200        self.graph
201    }
202}