Skip to main content

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