1use std::collections::HashMap;
6use std::f64::consts::PI as PI_F64;
7
8use super::functions::scaled_dot_product_attention;
9
10#[derive(Debug, Clone)]
15pub struct BatchNormLayer {
16 pub running_mean: Vec<f32>,
18 pub running_var: Vec<f32>,
20 pub gamma: Vec<f32>,
22 pub beta: Vec<f32>,
24 pub epsilon: f32,
26 pub n_features: usize,
28}
29impl BatchNormLayer {
30 pub fn new(n_features: usize) -> Self {
32 Self {
33 running_mean: vec![0.0; n_features],
34 running_var: vec![1.0; n_features],
35 gamma: vec![1.0; n_features],
36 beta: vec![0.0; n_features],
37 epsilon: 1e-5,
38 n_features,
39 }
40 }
41 pub fn forward(&self, input: &[f32]) -> Vec<f32> {
45 assert_eq!(input.len(), self.n_features);
46 (0..self.n_features)
47 .map(|i| {
48 let normalized =
49 (input[i] - self.running_mean[i]) / (self.running_var[i] + self.epsilon).sqrt();
50 self.gamma[i] * normalized + self.beta[i]
51 })
52 .collect()
53 }
54 pub fn set_stats(&mut self, mean: &[f32], var: &[f32]) {
56 assert_eq!(mean.len(), self.n_features);
57 assert_eq!(var.len(), self.n_features);
58 self.running_mean.copy_from_slice(mean);
59 self.running_var.copy_from_slice(var);
60 }
61 pub fn set_affine(&mut self, gamma: &[f32], beta: &[f32]) {
63 assert_eq!(gamma.len(), self.n_features);
64 assert_eq!(beta.len(), self.n_features);
65 self.gamma.copy_from_slice(gamma);
66 self.beta.copy_from_slice(beta);
67 }
68}
69impl BatchNormLayer {
70 pub fn update_running_stats(&mut self, batch: &[Vec<f32>], momentum: f32) {
78 assert!(
79 !batch.is_empty(),
80 "update_running_stats: batch must not be empty"
81 );
82 let n = batch.len() as f32;
83 let mut batch_mean = vec![0.0_f32; self.n_features];
84 for sample in batch {
85 assert_eq!(sample.len(), self.n_features, "sample length mismatch");
86 for (k, &v) in sample.iter().enumerate() {
87 batch_mean[k] += v;
88 }
89 }
90 for m in &mut batch_mean {
91 *m /= n;
92 }
93 let mut batch_var = vec![0.0_f32; self.n_features];
94 for sample in batch {
95 for (k, &v) in sample.iter().enumerate() {
96 let d = v - batch_mean[k];
97 batch_var[k] += d * d;
98 }
99 }
100 for v in &mut batch_var {
101 *v /= n;
102 }
103 for k in 0..self.n_features {
104 self.running_mean[k] =
105 (1.0 - momentum) * self.running_mean[k] + momentum * batch_mean[k];
106 self.running_var[k] = (1.0 - momentum) * self.running_var[k] + momentum * batch_var[k];
107 }
108 }
109}
110#[derive(Debug, Clone)]
113pub struct RnnCell {
114 pub w_x: Vec<f64>,
116 pub w_h: Vec<f64>,
118 pub b: Vec<f64>,
120 pub input_size: usize,
122 pub hidden_size: usize,
124 pub activation: ExtActivation,
126}
127impl RnnCell {
128 pub fn new(input_size: usize, hidden_size: usize, activation: ExtActivation) -> Self {
130 Self {
131 w_x: vec![0.0_f64; hidden_size * input_size],
132 w_h: vec![0.0_f64; hidden_size * hidden_size],
133 b: vec![0.0_f64; hidden_size],
134 input_size,
135 hidden_size,
136 activation,
137 }
138 }
139 pub fn step(&self, x: &[f64], h_prev: &[f64]) -> Vec<f64> {
143 assert_eq!(x.len(), self.input_size);
144 assert_eq!(h_prev.len(), self.hidden_size);
145 (0..self.hidden_size)
146 .map(|o| {
147 let mut acc = self.b[o];
148 for (i, &xi) in x.iter().enumerate() {
149 acc += self.w_x[o * self.input_size + i] * xi;
150 }
151 for (i, &hi) in h_prev.iter().enumerate() {
152 acc += self.w_h[o * self.hidden_size + i] * hi;
153 }
154 self.activation.apply(acc)
155 })
156 .collect()
157 }
158 pub fn forward_sequence(&self, sequence: &[Vec<f64>]) -> Vec<Vec<f64>> {
162 let mut h = vec![0.0_f64; self.hidden_size];
163 let mut hidden_states = Vec::with_capacity(sequence.len());
164 for x in sequence {
165 h = self.step(x, &h);
166 hidden_states.push(h.clone());
167 }
168 hidden_states
169 }
170}
171#[derive(Debug, Clone)]
173pub struct InferencePipeline {
174 pub ops: Vec<InferenceOp>,
176}
177impl InferencePipeline {
178 pub fn new() -> Self {
180 Self { ops: Vec::new() }
181 }
182 pub fn add_op(&mut self, op: InferenceOp) {
184 self.ops.push(op);
185 }
186 pub fn forward(&self, input: &[f32]) -> Vec<f32> {
188 let mut current = input.to_vec();
189 for op in &self.ops {
190 current = match op {
191 InferenceOp::Dense(layer) => layer.forward(¤t),
192 InferenceOp::BatchNorm(bn) => bn.forward(¤t),
193 InferenceOp::Activation(act) => current.iter().map(|&x| act.apply(x)).collect(),
194 };
195 }
196 current
197 }
198 pub fn total_parameters(&self) -> usize {
200 self.ops
201 .iter()
202 .map(|op| match op {
203 InferenceOp::Dense(layer) => layer.parameter_count(),
204 InferenceOp::BatchNorm(bn) => 2 * bn.n_features,
205 InferenceOp::Activation(_) => 0,
206 })
207 .sum()
208 }
209}
210pub struct NetworkBuilder;
212impl NetworkBuilder {
213 pub fn simple_aann(
219 n_descriptors: usize,
220 hidden_sizes: &[usize],
221 _element: u8,
222 ) -> FeedForwardNet {
223 let mut net = FeedForwardNet::new();
224 let mut prev = n_descriptors;
225 for &h in hidden_sizes {
226 net.add_layer(DenseLayer::new(prev, h, ActivationFn::Tanh));
227 prev = h;
228 }
229 net.add_layer(DenseLayer::new(prev, 1, ActivationFn::Linear));
230 net
231 }
232}
233#[derive(Debug, Clone)]
239pub struct TransformerFfn {
240 pub d_model: usize,
242 pub d_ff: usize,
244 pub w1: Vec<f64>,
246 pub b1: Vec<f64>,
248 pub w2: Vec<f64>,
250 pub b2: Vec<f64>,
252}
253impl TransformerFfn {
254 pub fn new(d_model: usize, d_ff: usize) -> Self {
256 Self {
257 d_model,
258 d_ff,
259 w1: vec![0.0_f64; d_ff * d_model],
260 b1: vec![0.0_f64; d_ff],
261 w2: vec![0.0_f64; d_model * d_ff],
262 b2: vec![0.0_f64; d_model],
263 }
264 }
265 pub fn forward(&self, x: &[f64], seq_len: usize) -> Vec<f64> {
267 let dm = self.d_model;
268 let df = self.d_ff;
269 let mut out = vec![0.0_f64; seq_len * dm];
270 for t in 0..seq_len {
271 let mut hidden = vec![0.0_f64; df];
272 for (j, h_j) in hidden.iter_mut().enumerate() {
273 let mut acc = self.b1[j];
274 for (i, &xi) in x[t * dm..t * dm + dm].iter().enumerate() {
275 acc += xi * self.w1[j * dm + i];
276 }
277 *h_j = acc.max(0.0);
278 }
279 for (j, out_j) in out[t * dm..t * dm + dm].iter_mut().enumerate() {
280 let mut acc = self.b2[j];
281 for (i, &hi) in hidden.iter().enumerate() {
282 acc += hi * self.w2[j * df + i];
283 }
284 *out_j = acc;
285 }
286 }
287 out
288 }
289}
290#[derive(Debug, Clone)]
301pub struct Conv1DLayer {
302 pub in_channels: usize,
304 pub out_channels: usize,
306 pub kernel_size: usize,
308 pub weights: Vec<Vec<Vec<f64>>>,
310 pub biases: Vec<f64>,
312 pub activation: ExtActivation,
314}
315impl Conv1DLayer {
316 pub fn new(
318 in_channels: usize,
319 out_channels: usize,
320 kernel_size: usize,
321 activation: ExtActivation,
322 ) -> Self {
323 let weights = vec![vec![vec![0.0_f64; in_channels]; kernel_size]; out_channels];
324 let biases = vec![0.0_f64; out_channels];
325 Self {
326 in_channels,
327 out_channels,
328 kernel_size,
329 weights,
330 biases,
331 activation,
332 }
333 }
334 pub fn forward(&self, input: &[Vec<f64>]) -> Vec<Vec<f64>> {
339 let seq_len = input.len();
340 let mut output = vec![vec![0.0_f64; self.out_channels]; seq_len];
341 for (t, out_t) in output.iter_mut().enumerate() {
342 for (o, out_to) in out_t.iter_mut().enumerate() {
343 let mut acc = self.biases[o];
344 for k in 0..self.kernel_size {
345 let src_t = t as isize - k as isize;
346 if src_t < 0 {
347 continue;
348 }
349 let src_t = src_t as usize;
350 for (c, &inp) in input[src_t].iter().enumerate() {
351 acc += self.weights[o][k][c] * inp;
352 }
353 }
354 *out_to = self.activation.apply(acc);
355 }
356 }
357 output
358 }
359 pub fn num_params(&self) -> usize {
361 self.out_channels * self.kernel_size * self.in_channels + self.out_channels
362 }
363}
364#[derive(Debug, Clone)]
366pub enum InferenceOp {
367 Dense(DenseLayer),
369 BatchNorm(BatchNormLayer),
371 Activation(ActivationFn),
373}
374#[derive(Debug, Clone)]
378pub struct AdamOptimizer {
379 pub lr: f64,
381 pub beta1: f64,
383 pub beta2: f64,
385 pub epsilon: f64,
387 pub m: Vec<f64>,
389 pub v: Vec<f64>,
391 pub step: u64,
393}
394impl AdamOptimizer {
395 pub fn new(n_params: usize, lr: f64, beta1: f64, beta2: f64, epsilon: f64) -> Self {
397 Self {
398 lr,
399 beta1,
400 beta2,
401 epsilon,
402 m: vec![0.0; n_params],
403 v: vec![0.0; n_params],
404 step: 0,
405 }
406 }
407 pub fn default_params(n_params: usize) -> Self {
409 Self::new(n_params, 1e-3, 0.9, 0.999, 1e-8)
410 }
411 pub fn step_update(&mut self, params: &mut [f64], grads: &[f64]) {
415 assert_eq!(
416 params.len(),
417 self.m.len(),
418 "AdamOptimizer::step_update: params/m length mismatch"
419 );
420 assert_eq!(
421 grads.len(),
422 self.m.len(),
423 "AdamOptimizer::step_update: grads/m length mismatch"
424 );
425 self.step += 1;
426 let t = self.step as f64;
427 let bias_corr1 = 1.0 - self.beta1.powf(t);
428 let bias_corr2 = 1.0 - self.beta2.powf(t);
429 for i in 0..params.len() {
430 self.m[i] = self.beta1 * self.m[i] + (1.0 - self.beta1) * grads[i];
431 self.v[i] = self.beta2 * self.v[i] + (1.0 - self.beta2) * grads[i] * grads[i];
432 let m_hat = self.m[i] / bias_corr1;
433 let v_hat = self.v[i] / bias_corr2;
434 params[i] -= self.lr * m_hat / (v_hat.sqrt() + self.epsilon);
435 }
436 }
437 pub fn reset(&mut self) {
439 self.m.iter_mut().for_each(|x| *x = 0.0);
440 self.v.iter_mut().for_each(|x| *x = 0.0);
441 self.step = 0;
442 }
443}
444#[derive(Debug, Clone)]
451pub struct GnnLayer {
452 pub in_dim: usize,
454 pub out_dim: usize,
456 pub w_self: Vec<f64>,
458 pub w_neigh: Vec<f64>,
460 pub bias: Vec<f64>,
462 pub activation: ExtActivation,
464}
465impl GnnLayer {
466 pub fn new(in_dim: usize, out_dim: usize, activation: ExtActivation) -> Self {
468 Self {
469 in_dim,
470 out_dim,
471 w_self: vec![0.0_f64; out_dim * in_dim],
472 w_neigh: vec![0.0_f64; out_dim * in_dim],
473 bias: vec![0.0_f64; out_dim],
474 activation,
475 }
476 }
477 pub fn forward(&self, node_feats: &[f64], n_nodes: usize, adj: &[Vec<usize>]) -> Vec<f64> {
484 assert_eq!(node_feats.len(), n_nodes * self.in_dim);
485 assert_eq!(adj.len(), n_nodes);
486 let in_d = self.in_dim;
487 let out_d = self.out_dim;
488 let mut out = vec![0.0_f64; n_nodes * out_d];
489 for i in 0..n_nodes {
490 let h_self = &node_feats[i * in_d..(i + 1) * in_d];
491 let mut agg = vec![0.0_f64; in_d];
492 for &j in &adj[i] {
493 let h_j = &node_feats[j * in_d..(j + 1) * in_d];
494 for d in 0..in_d {
495 agg[d] += h_j[d];
496 }
497 }
498 for o in 0..out_d {
499 let mut acc = self.bias[o];
500 for d in 0..in_d {
501 acc += self.w_self[o * in_d + d] * h_self[d];
502 acc += self.w_neigh[o * in_d + d] * agg[d];
503 }
504 out[i * out_d + o] = self.activation.apply(acc);
505 }
506 }
507 out
508 }
509 pub fn num_params(&self) -> usize {
511 2 * self.out_dim * self.in_dim + self.out_dim
512 }
513}
514#[derive(Debug, Clone)]
516pub struct MessagePassingNet {
517 pub layers: Vec<GnnLayer>,
519}
520impl MessagePassingNet {
521 pub fn new() -> Self {
523 Self { layers: Vec::new() }
524 }
525 pub fn add_layer(&mut self, layer: GnnLayer) {
527 self.layers.push(layer);
528 }
529 pub fn forward(&self, node_feats: &[f64], n_nodes: usize, adj: &[Vec<usize>]) -> Vec<f64> {
533 let mut h = node_feats.to_vec();
534 for layer in &self.layers {
535 h = layer.forward(&h, n_nodes, adj);
536 }
537 h
538 }
539 pub fn global_mean_pool(&self, node_feats: &[f64], n_nodes: usize, out_dim: usize) -> Vec<f64> {
541 if n_nodes == 0 {
542 return vec![0.0_f64; out_dim];
543 }
544 let mut pooled = vec![0.0_f64; out_dim];
545 for i in 0..n_nodes {
546 for d in 0..out_dim {
547 pooled[d] += node_feats[i * out_dim + d];
548 }
549 }
550 let inv_n = 1.0 / n_nodes as f64;
551 for v in &mut pooled {
552 *v *= inv_n;
553 }
554 pooled
555 }
556}
557#[derive(Debug, Clone)]
559pub struct GradAccumulator {
560 pub grad_weights: Vec<f64>,
562 pub grad_biases: Vec<f64>,
564 pub count: usize,
566}
567impl GradAccumulator {
568 pub fn new(n_weights: usize, n_biases: usize) -> Self {
570 Self {
571 grad_weights: vec![0.0; n_weights],
572 grad_biases: vec![0.0; n_biases],
573 count: 0,
574 }
575 }
576 pub fn accumulate(&mut self, gw: &[f64], gb: &[f64]) {
578 assert_eq!(gw.len(), self.grad_weights.len());
579 assert_eq!(gb.len(), self.grad_biases.len());
580 for (acc, &g) in self.grad_weights.iter_mut().zip(gw.iter()) {
581 *acc += g;
582 }
583 for (acc, &g) in self.grad_biases.iter_mut().zip(gb.iter()) {
584 *acc += g;
585 }
586 self.count += 1;
587 }
588 pub fn mean_grads(&self) -> (Vec<f64>, Vec<f64>) {
590 let n = self.count.max(1) as f64;
591 let gw: Vec<f64> = self.grad_weights.iter().map(|&g| g / n).collect();
592 let gb: Vec<f64> = self.grad_biases.iter().map(|&g| g / n).collect();
593 (gw, gb)
594 }
595 pub fn zero(&mut self) {
597 self.grad_weights.iter_mut().for_each(|x| *x = 0.0);
598 self.grad_biases.iter_mut().for_each(|x| *x = 0.0);
599 self.count = 0;
600 }
601}
602#[derive(Debug, Clone)]
609pub struct MultiHeadAttention {
610 pub d_model: usize,
612 pub n_heads: usize,
614 pub d_head: usize,
616 pub w_q: Vec<f64>,
618 pub w_k: Vec<f64>,
620 pub w_v: Vec<f64>,
622 pub w_o: Vec<f64>,
624 pub b_o: Vec<f64>,
626}
627impl MultiHeadAttention {
628 pub fn new(d_model: usize, n_heads: usize) -> Self {
630 assert_eq!(d_model % n_heads, 0, "d_model must be divisible by n_heads");
631 let d_head = d_model / n_heads;
632 let dm2 = d_model * d_model;
633 Self {
634 d_model,
635 n_heads,
636 d_head,
637 w_q: vec![0.0_f64; dm2],
638 w_k: vec![0.0_f64; dm2],
639 w_v: vec![0.0_f64; dm2],
640 w_o: vec![0.0_f64; dm2],
641 b_o: vec![0.0_f64; d_model],
642 }
643 }
644 pub fn init_identity(&mut self) {
646 let dm = self.d_model;
647 for row in 0..dm {
648 self.w_q[row * dm + row] = 1.0;
649 self.w_k[row * dm + row] = 1.0;
650 self.w_v[row * dm + row] = 1.0;
651 self.w_o[row * dm + row] = 1.0;
652 }
653 }
654 fn project(
656 input: &[f64],
657 w: &[f64],
658 seq_len: usize,
659 in_dim: usize,
660 out_dim: usize,
661 ) -> Vec<f64> {
662 let mut out = vec![0.0_f64; seq_len * out_dim];
663 for t in 0..seq_len {
664 for o in 0..out_dim {
665 let mut acc = 0.0_f64;
666 for i in 0..in_dim {
667 acc += input[t * in_dim + i] * w[o * in_dim + i];
668 }
669 out[t * out_dim + o] = acc;
670 }
671 }
672 out
673 }
674 pub fn forward(&self, x: &[f64], seq_len: usize) -> Vec<f64> {
679 let dm = self.d_model;
680 let dh = self.d_head;
681 let nh = self.n_heads;
682 let q_full = Self::project(x, &self.w_q, seq_len, dm, dm);
683 let k_full = Self::project(x, &self.w_k, seq_len, dm, dm);
684 let v_full = Self::project(x, &self.w_v, seq_len, dm, dm);
685 let mut concat = vec![0.0_f64; seq_len * dm];
686 for h in 0..nh {
687 let mut q_h = vec![0.0_f64; seq_len * dh];
688 let mut k_h = vec![0.0_f64; seq_len * dh];
689 let mut v_h = vec![0.0_f64; seq_len * dh];
690 for t in 0..seq_len {
691 for d in 0..dh {
692 q_h[t * dh + d] = q_full[t * dm + h * dh + d];
693 k_h[t * dh + d] = k_full[t * dm + h * dh + d];
694 v_h[t * dh + d] = v_full[t * dm + h * dh + d];
695 }
696 }
697 let head_out =
698 scaled_dot_product_attention(&q_h, &k_h, &v_h, seq_len, seq_len, dh, dh, None);
699 for t in 0..seq_len {
700 for d in 0..dh {
701 concat[t * dm + h * dh + d] = head_out[t * dh + d];
702 }
703 }
704 }
705 let projected = Self::project(&concat, &self.w_o, seq_len, dm, dm);
706 let mut output = projected;
707 for t in 0..seq_len {
708 for d in 0..dm {
709 output[t * dm + d] += self.b_o[d];
710 }
711 }
712 output
713 }
714 pub fn num_params(&self) -> usize {
716 4 * self.d_model * self.d_model + self.d_model
717 }
718}
719#[derive(Debug, Clone)]
721pub struct NeuralLayer {
722 pub weights: Vec<Vec<f64>>,
724 pub biases: Vec<f64>,
726 pub activation: ActivationFn64,
728}
729impl NeuralLayer {
730 pub fn new_xavier(in_features: usize, out_features: usize, activation: ActivationFn64) -> Self {
734 let limit = (6.0_f64 / (in_features + out_features) as f64).sqrt();
735 let mut state: u64 = 0x123456789abcdef0;
736 let lcg_next = |s: &mut u64| -> f64 {
737 *s = s
738 .wrapping_mul(6364136223846793005)
739 .wrapping_add(1442695040888963407);
740 let bits = (*s >> 33) as f64;
741 bits / (u64::MAX as f64) * 2.0 * limit - limit
742 };
743 let weights: Vec<Vec<f64>> = (0..out_features)
744 .map(|_| (0..in_features).map(|_| lcg_next(&mut state)).collect())
745 .collect();
746 let biases = vec![0.0_f64; out_features];
747 Self {
748 weights,
749 biases,
750 activation,
751 }
752 }
753 pub fn forward(&self, input: &[f64]) -> Vec<f64> {
755 let out_features = self.weights.len();
756 let mut output = Vec::with_capacity(out_features);
757 for o in 0..out_features {
758 let mut acc = self.biases[o];
759 for (i, &x) in input.iter().enumerate() {
760 acc += self.weights[o][i] * x;
761 }
762 output.push(self.activation.apply(acc));
763 }
764 output
765 }
766}
767#[derive(Debug, Clone)]
772pub struct AttentionReadout {
773 pub d_feat: usize,
775 pub w_attn: Vec<f64>,
777 pub b_attn: f64,
779}
780impl AttentionReadout {
781 pub fn new(d_feat: usize) -> Self {
783 Self {
784 d_feat,
785 w_attn: vec![0.0_f64; d_feat],
786 b_attn: 0.0,
787 }
788 }
789 pub fn forward(&self, node_feats: &[f64], n_nodes: usize) -> Vec<f64> {
793 let df = self.d_feat;
794 let mut out = vec![0.0_f64; df];
795 let mut attn_scores = Vec::with_capacity(n_nodes);
796 for i in 0..n_nodes {
797 let h = &node_feats[i * df..(i + 1) * df];
798 let raw: f64 = h
799 .iter()
800 .zip(self.w_attn.iter())
801 .map(|(&x, &w)| x * w)
802 .sum::<f64>()
803 + self.b_attn;
804 let score = 1.0 / (1.0 + (-raw).exp());
805 attn_scores.push(score);
806 }
807 for i in 0..n_nodes {
808 let h = &node_feats[i * df..(i + 1) * df];
809 for d in 0..df {
810 out[d] += attn_scores[i] * h[d];
811 }
812 }
813 out
814 }
815}
816#[derive(Debug, Clone)]
819pub struct TransformerBlock {
820 pub mha: MultiHeadAttention,
822 pub ffn: TransformerFfn,
824 pub ln1: LayerNorm,
826 pub ln2: LayerNorm,
828 pub d_model: usize,
830}
831impl TransformerBlock {
832 pub fn new(d_model: usize, n_heads: usize, d_ff: usize) -> Self {
834 Self {
835 mha: MultiHeadAttention::new(d_model, n_heads),
836 ffn: TransformerFfn::new(d_model, d_ff),
837 ln1: LayerNorm::new(d_model),
838 ln2: LayerNorm::new(d_model),
839 d_model,
840 }
841 }
842 pub fn forward(&self, x: &[f64], seq_len: usize) -> Vec<f64> {
846 let dm = self.d_model;
847 let mut normed1 = vec![0.0_f64; seq_len * dm];
848 for t in 0..seq_len {
849 let row = &x[t * dm..(t + 1) * dm];
850 let n = self.ln1.forward(row);
851 normed1[t * dm..(t + 1) * dm].copy_from_slice(&n);
852 }
853 let attn_out = self.mha.forward(&normed1, seq_len);
854 let mut x1 = vec![0.0_f64; seq_len * dm];
855 for i in 0..x1.len() {
856 x1[i] = x[i] + attn_out[i];
857 }
858 let mut normed2 = vec![0.0_f64; seq_len * dm];
859 for t in 0..seq_len {
860 let row = &x1[t * dm..(t + 1) * dm];
861 let n = self.ln2.forward(row);
862 normed2[t * dm..(t + 1) * dm].copy_from_slice(&n);
863 }
864 let ffn_out = self.ffn.forward(&normed2, seq_len);
865 let mut x2 = vec![0.0_f64; seq_len * dm];
866 for i in 0..x2.len() {
867 x2[i] = x1[i] + ffn_out[i];
868 }
869 x2
870 }
871}
872#[derive(Debug, Clone, PartialEq)]
874pub enum ActivationFn {
875 Tanh,
877 Relu,
879 Sigmoid,
881 Silu,
883 Gelu,
885 Linear,
887}
888impl ActivationFn {
889 pub fn apply(&self, x: f32) -> f32 {
891 match self {
892 ActivationFn::Tanh => x.tanh(),
893 ActivationFn::Relu => x.max(0.0),
894 ActivationFn::Sigmoid => 1.0 / (1.0 + (-x).exp()),
895 ActivationFn::Silu => x / (1.0 + (-x).exp()),
896 ActivationFn::Gelu => {
897 let cdf = 0.5
898 * (1.0
899 + (std::f32::consts::FRAC_2_SQRT_PI.sqrt() * (x + 0.044715 * x * x * x))
900 .tanh());
901 x * cdf
902 }
903 ActivationFn::Linear => x,
904 }
905 }
906 pub fn derivative(&self, x: f32) -> f32 {
908 match self {
909 ActivationFn::Tanh => {
910 let t = x.tanh();
911 1.0 - t * t
912 }
913 ActivationFn::Relu => {
914 if x > 0.0 {
915 1.0
916 } else {
917 0.0
918 }
919 }
920 ActivationFn::Sigmoid => {
921 let s = 1.0 / (1.0 + (-x).exp());
922 s * (1.0 - s)
923 }
924 ActivationFn::Silu => {
925 let s = 1.0 / (1.0 + (-x).exp());
926 s + x * s * (1.0 - s)
927 }
928 ActivationFn::Gelu => {
929 let eps = 1e-5_f32;
930 (self.apply(x + eps) - self.apply(x - eps)) / (2.0 * eps)
931 }
932 ActivationFn::Linear => 1.0,
933 }
934 }
935}
936#[derive(Debug, Clone)]
941pub struct LayerNormLayer {
942 pub n_features: usize,
944 pub gamma: Vec<f64>,
946 pub beta: Vec<f64>,
948 pub epsilon: f64,
950}
951impl LayerNormLayer {
952 pub fn new(n_features: usize) -> Self {
954 Self {
955 n_features,
956 gamma: vec![1.0; n_features],
957 beta: vec![0.0; n_features],
958 epsilon: 1e-5,
959 }
960 }
961 pub fn forward(&self, input: &[f64]) -> Vec<f64> {
965 assert_eq!(
966 input.len(),
967 self.n_features,
968 "LayerNorm: input size mismatch"
969 );
970 let n = self.n_features as f64;
971 let mean: f64 = input.iter().sum::<f64>() / n;
972 let var: f64 = input.iter().map(|&x| (x - mean) * (x - mean)).sum::<f64>() / n;
973 let std_inv = 1.0 / (var + self.epsilon).sqrt();
974 (0..self.n_features)
975 .map(|i| self.gamma[i] * (input[i] - mean) * std_inv + self.beta[i])
976 .collect()
977 }
978 pub fn backward(&self, input: &[f64], d_output: &[f64]) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
982 assert_eq!(input.len(), self.n_features);
983 assert_eq!(d_output.len(), self.n_features);
984 let n = self.n_features as f64;
985 let mean: f64 = input.iter().sum::<f64>() / n;
986 let var: f64 = input.iter().map(|&x| (x - mean) * (x - mean)).sum::<f64>() / n;
987 let std_inv = 1.0 / (var + self.epsilon).sqrt();
988 let x_hat: Vec<f64> = input.iter().map(|&x| (x - mean) * std_inv).collect();
989 let d_gamma: Vec<f64> = (0..self.n_features)
990 .map(|i| d_output[i] * x_hat[i])
991 .collect();
992 let d_beta: Vec<f64> = d_output.to_vec();
993 let d_x_hat: Vec<f64> = (0..self.n_features)
994 .map(|i| d_output[i] * self.gamma[i])
995 .collect();
996 let sum_d_x_hat: f64 = d_x_hat.iter().sum();
997 let sum_d_x_hat_xhat: f64 = d_x_hat.iter().zip(x_hat.iter()).map(|(&a, &b)| a * b).sum();
998 let d_input: Vec<f64> = (0..self.n_features)
999 .map(|i| std_inv * (d_x_hat[i] - (sum_d_x_hat + x_hat[i] * sum_d_x_hat_xhat) / n))
1000 .collect();
1001 (d_input, d_gamma, d_beta)
1002 }
1003}
1004#[derive(Debug, Clone)]
1008pub struct DataNormalizer {
1009 pub mean: Vec<f32>,
1011 pub std_dev: Vec<f32>,
1013}
1014impl DataNormalizer {
1015 pub fn fit(data: &[Vec<f32>]) -> Self {
1020 assert!(
1021 !data.is_empty(),
1022 "DataNormalizer::fit: data must be non-empty"
1023 );
1024 let n_features = data[0].len();
1025 let n = data.len() as f32;
1026 let mut mean = vec![0.0_f32; n_features];
1027 for sample in data {
1028 assert_eq!(
1029 sample.len(),
1030 n_features,
1031 "DataNormalizer::fit: inconsistent sample length"
1032 );
1033 for (k, &v) in sample.iter().enumerate() {
1034 mean[k] += v;
1035 }
1036 }
1037 for m in &mut mean {
1038 *m /= n;
1039 }
1040 let mut variance = vec![0.0_f32; n_features];
1041 for sample in data {
1042 for (k, &v) in sample.iter().enumerate() {
1043 let diff = v - mean[k];
1044 variance[k] += diff * diff;
1045 }
1046 }
1047 let std_dev: Vec<f32> = variance
1048 .iter()
1049 .map(|&v| {
1050 let s = (v / n).sqrt();
1051 if s < 1e-8 { 1.0 } else { s }
1052 })
1053 .collect();
1054 DataNormalizer { mean, std_dev }
1055 }
1056 pub fn transform(&self, x: &[f32]) -> Vec<f32> {
1058 x.iter()
1059 .zip(self.mean.iter())
1060 .zip(self.std_dev.iter())
1061 .map(|((&xi, &m), &s)| (xi - m) / s)
1062 .collect()
1063 }
1064 pub fn inverse_transform(&self, x: &[f32]) -> Vec<f32> {
1066 x.iter()
1067 .zip(self.mean.iter())
1068 .zip(self.std_dev.iter())
1069 .map(|((&xi, &m), &s)| xi * s + m)
1070 .collect()
1071 }
1072}
1073#[derive(Debug, Clone, PartialEq)]
1075pub enum ActivationFn64 {
1076 Relu,
1078 Sigmoid,
1080 Tanh,
1082 Linear,
1084}
1085impl ActivationFn64 {
1086 pub fn apply(&self, x: f64) -> f64 {
1088 match self {
1089 ActivationFn64::Relu => x.max(0.0),
1090 ActivationFn64::Sigmoid => 1.0 / (1.0 + (-x).exp()),
1091 ActivationFn64::Tanh => x.tanh(),
1092 ActivationFn64::Linear => x,
1093 }
1094 }
1095 pub fn apply_batch(&self, v: &mut [f64]) {
1097 for x in v.iter_mut() {
1098 *x = self.apply(*x);
1099 }
1100 }
1101}
1102#[derive(Debug, Clone)]
1104pub struct NeuralNetwork {
1105 pub layers: Vec<NeuralLayer>,
1107}
1108impl NeuralNetwork {
1109 pub fn new(layer_sizes: &[usize], activation: ActivationFn64) -> Self {
1113 assert!(
1114 layer_sizes.len() >= 2,
1115 "need at least input and output size"
1116 );
1117 let mut layers = Vec::new();
1118 for i in 0..layer_sizes.len() - 1 {
1119 let act = if i == layer_sizes.len() - 2 {
1120 ActivationFn64::Linear
1121 } else {
1122 activation.clone()
1123 };
1124 layers.push(NeuralLayer::new_xavier(
1125 layer_sizes[i],
1126 layer_sizes[i + 1],
1127 act,
1128 ));
1129 }
1130 Self { layers }
1131 }
1132 pub fn forward(&self, input: &[f64]) -> Vec<f64> {
1134 let mut current: Vec<f64> = input.to_vec();
1135 for layer in &self.layers {
1136 current = layer.forward(¤t);
1137 }
1138 current
1139 }
1140 pub fn input_dim(&self) -> usize {
1142 self.layers
1143 .first()
1144 .map_or(0, |l| l.weights.first().map_or(0, |r| r.len()))
1145 }
1146 pub fn output_dim(&self) -> usize {
1148 self.layers.last().map_or(0, |l| l.biases.len())
1149 }
1150}
1151#[derive(Debug, Clone)]
1153pub struct FeedForwardNet {
1154 pub layers: Vec<DenseLayer>,
1156}
1157impl FeedForwardNet {
1158 pub fn new() -> Self {
1160 FeedForwardNet { layers: Vec::new() }
1161 }
1162 pub fn add_layer(&mut self, layer: DenseLayer) {
1164 self.layers.push(layer);
1165 }
1166 pub fn forward(&self, input: &[f32]) -> Vec<f32> {
1168 let mut current: Vec<f32> = input.to_vec();
1169 for layer in &self.layers {
1170 current = layer.forward(¤t);
1171 }
1172 current
1173 }
1174 pub fn input_size(&self) -> Option<usize> {
1176 self.layers.first().map(|l| l.in_features)
1177 }
1178 pub fn output_size(&self) -> Option<usize> {
1180 self.layers.last().map(|l| l.out_features)
1181 }
1182 pub fn total_parameters(&self) -> usize {
1184 self.layers.iter().map(|l| l.parameter_count()).sum()
1185 }
1186}
1187impl FeedForwardNet {
1188 pub fn compute_gradient_norm(&self, layer_grads: &[Vec<f32>]) -> f32 {
1193 let sum_sq: f32 = layer_grads
1194 .iter()
1195 .flat_map(|g| g.iter())
1196 .map(|&v| v * v)
1197 .sum();
1198 sum_sq.sqrt()
1199 }
1200 pub fn clip_gradients(&self, layer_grads: &mut [Vec<f32>], max_norm: f32) -> f32 {
1203 let norm = self.compute_gradient_norm(layer_grads);
1204 if norm > max_norm && norm > 0.0 {
1205 let scale = max_norm / norm;
1206 for g in layer_grads.iter_mut() {
1207 for v in g.iter_mut() {
1208 *v *= scale;
1209 }
1210 }
1211 }
1212 norm
1213 }
1214}
1215#[derive(Debug, Clone)]
1220pub struct LayerNorm {
1221 pub n_features: usize,
1223 pub gamma: Vec<f64>,
1225 pub beta: Vec<f64>,
1227 pub epsilon: f64,
1229}
1230impl LayerNorm {
1231 pub fn new(n_features: usize) -> Self {
1233 Self {
1234 n_features,
1235 gamma: vec![1.0_f64; n_features],
1236 beta: vec![0.0_f64; n_features],
1237 epsilon: 1e-5,
1238 }
1239 }
1240 pub fn forward(&self, x: &[f64]) -> Vec<f64> {
1242 assert_eq!(x.len(), self.n_features);
1243 let n = self.n_features as f64;
1244 let mean = x.iter().sum::<f64>() / n;
1245 let var = x.iter().map(|&v| (v - mean) * (v - mean)).sum::<f64>() / n;
1246 let std = (var + self.epsilon).sqrt();
1247 x.iter()
1248 .enumerate()
1249 .map(|(i, &v)| self.gamma[i] * (v - mean) / std + self.beta[i])
1250 .collect()
1251 }
1252}
1253#[derive(Debug, Clone)]
1258pub struct GpuNeuralBuffer {
1259 pub batch_size: usize,
1261 pub input_dim: usize,
1263 pub output_dim: usize,
1265 pub data: Vec<f64>,
1267}
1268impl GpuNeuralBuffer {
1269 pub fn pack_positions(positions: &[[f64; 3]]) -> Self {
1273 let batch_size = positions.len();
1274 let input_dim = 3;
1275 let output_dim = 3;
1276 let mut data = Vec::with_capacity(batch_size * input_dim);
1277 for p in positions {
1278 data.push(p[0]);
1279 data.push(p[1]);
1280 data.push(p[2]);
1281 }
1282 Self {
1283 batch_size,
1284 input_dim,
1285 output_dim,
1286 data,
1287 }
1288 }
1289 pub fn unpack_forces(&self) -> Vec<[f64; 3]> {
1294 self.data.chunks(3).map(|c| [c[0], c[1], c[2]]).collect()
1295 }
1296}
1297#[derive(Debug, Clone)]
1304pub struct PositionalEncoding {
1305 pub d_model: usize,
1307 pub max_len: usize,
1309 pub table: Vec<Vec<f64>>,
1311}
1312impl PositionalEncoding {
1313 pub fn new(d_model: usize, max_len: usize) -> Self {
1315 let mut table = vec![vec![0.0_f64; d_model]; max_len];
1316 for (pos, row) in table.iter_mut().enumerate() {
1317 for i in 0..(d_model / 2) {
1318 let angle = (pos as f64) / (10000.0_f64).powf(2.0 * i as f64 / d_model as f64);
1319 row[2 * i] = angle.sin();
1320 if 2 * i + 1 < d_model {
1321 row[2 * i + 1] = angle.cos();
1322 }
1323 }
1324 }
1325 Self {
1326 d_model,
1327 max_len,
1328 table,
1329 }
1330 }
1331 pub fn add_to_sequence(&self, embeddings: &mut [Vec<f64>]) {
1335 for (t, emb) in embeddings.iter_mut().enumerate() {
1336 if t >= self.max_len {
1337 break;
1338 }
1339 for d in 0..emb.len().min(self.d_model) {
1340 emb[d] += self.table[t][d];
1341 }
1342 }
1343 }
1344 pub fn get(&self, pos: usize) -> &[f64] {
1346 &self.table[pos.min(self.max_len - 1)]
1347 }
1348}
1349#[derive(Debug, Clone)]
1352pub struct DenseLayer64 {
1353 pub weights: Vec<f64>,
1355 pub biases: Vec<f64>,
1357 pub in_features: usize,
1359 pub out_features: usize,
1361 pub activation: ExtActivation,
1363 pub last_pre_act: Vec<f64>,
1365 pub last_output: Vec<f64>,
1367 pub last_input: Vec<f64>,
1369}
1370impl DenseLayer64 {
1371 pub fn new(in_features: usize, out_features: usize, activation: ExtActivation) -> Self {
1373 Self {
1374 weights: vec![0.0_f64; out_features * in_features],
1375 biases: vec![0.0_f64; out_features],
1376 in_features,
1377 out_features,
1378 activation,
1379 last_pre_act: Vec::new(),
1380 last_output: Vec::new(),
1381 last_input: Vec::new(),
1382 }
1383 }
1384 pub fn forward(&mut self, input: &[f64]) -> Vec<f64> {
1387 assert_eq!(
1388 input.len(),
1389 self.in_features,
1390 "DenseLayer64::forward: input size mismatch"
1391 );
1392 self.last_input = input.to_vec();
1393 let pre_act: Vec<f64> = (0..self.out_features)
1394 .map(|o| {
1395 let row = o * self.in_features;
1396 let mut acc = self.biases[o];
1397 for (i, &inp) in input.iter().enumerate() {
1398 acc += self.weights[row + i] * inp;
1399 }
1400 acc
1401 })
1402 .collect();
1403 let output: Vec<f64> = pre_act.iter().map(|&z| self.activation.apply(z)).collect();
1404 self.last_pre_act = pre_act;
1405 self.last_output = output.clone();
1406 output
1407 }
1408 pub fn backward(&self, delta_out: &[f64]) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
1416 assert_eq!(
1417 delta_out.len(),
1418 self.out_features,
1419 "DenseLayer64::backward: delta_out size mismatch"
1420 );
1421 let delta_pre: Vec<f64> = delta_out
1422 .iter()
1423 .zip(self.last_pre_act.iter())
1424 .map(|(&d, &z)| d * self.activation.derivative(z))
1425 .collect();
1426 let mut grad_weights = vec![0.0_f64; self.out_features * self.in_features];
1427 for (o, &dp_o) in delta_pre.iter().enumerate() {
1428 let row = o * self.in_features;
1429 for (i, &li) in self.last_input.iter().enumerate() {
1430 grad_weights[row + i] = dp_o * li;
1431 }
1432 }
1433 let grad_biases = delta_pre.clone();
1434 let mut delta_in = vec![0.0_f64; self.in_features];
1435 for (o, &dp_o) in delta_pre.iter().enumerate() {
1436 let row = o * self.in_features;
1437 for (i, di) in delta_in.iter_mut().enumerate() {
1438 *di += self.weights[row + i] * dp_o;
1439 }
1440 }
1441 (grad_weights, grad_biases, delta_in)
1442 }
1443 pub fn apply_sgd(&mut self, grad_weights: &[f64], grad_biases: &[f64], lr: f64) {
1445 for (w, &gw) in self.weights.iter_mut().zip(grad_weights.iter()) {
1446 *w -= lr * gw;
1447 }
1448 for (b, &gb) in self.biases.iter_mut().zip(grad_biases.iter()) {
1449 *b -= lr * gb;
1450 }
1451 }
1452 pub fn num_params(&self) -> usize {
1454 self.out_features * self.in_features + self.out_features
1455 }
1456}
1457#[derive(Debug)]
1463pub struct AtomicNeuralNetwork {
1464 pub networks: HashMap<u8, FeedForwardNet>,
1466 pub descriptor: BehlerParrinelloDescriptor,
1468}
1469impl AtomicNeuralNetwork {
1470 pub fn new(descriptor: BehlerParrinelloDescriptor) -> Self {
1472 AtomicNeuralNetwork {
1473 networks: HashMap::new(),
1474 descriptor,
1475 }
1476 }
1477 pub fn add_element_network(&mut self, atomic_number: u8, net: FeedForwardNet) {
1479 self.networks.insert(atomic_number, net);
1480 }
1481 pub fn atomic_energy(&self, atomic_number: u8, descriptor: &[f32]) -> Option<f32> {
1485 self.networks
1486 .get(&atomic_number)
1487 .map(|net| net.forward(descriptor)[0])
1488 }
1489 pub fn total_energy(&self, positions: &[[f64; 3]], atomic_numbers: &[u8]) -> f64 {
1493 assert_eq!(
1494 positions.len(),
1495 atomic_numbers.len(),
1496 "total_energy: positions and atomic_numbers must have the same length"
1497 );
1498 let mut e_total = 0.0_f64;
1499 for (i, &z) in atomic_numbers.iter().enumerate() {
1500 let desc_f64 = self.descriptor.descriptor_vector(positions, i);
1501 let desc_f32: Vec<f32> = desc_f64.iter().map(|&v| v as f32).collect();
1502 if let Some(e) = self.atomic_energy(z, &desc_f32) {
1503 e_total += e as f64;
1504 }
1505 }
1506 e_total
1507 }
1508}
1509#[derive(Debug, Clone)]
1513pub struct DenseLayer {
1514 pub weights: Vec<f32>,
1516 pub biases: Vec<f32>,
1518 pub in_features: usize,
1520 pub out_features: usize,
1522 pub activation: ActivationFn,
1524}
1525impl DenseLayer {
1526 pub fn new(in_features: usize, out_features: usize, activation: ActivationFn) -> Self {
1528 DenseLayer {
1529 weights: vec![0.0_f32; out_features * in_features],
1530 biases: vec![0.0_f32; out_features],
1531 in_features,
1532 out_features,
1533 activation,
1534 }
1535 }
1536 pub fn forward(&self, input: &[f32]) -> Vec<f32> {
1541 assert_eq!(
1542 input.len(),
1543 self.in_features,
1544 "DenseLayer::forward: input length {} != in_features {}",
1545 input.len(),
1546 self.in_features
1547 );
1548 (0..self.out_features)
1549 .map(|o| {
1550 let row_offset = o * self.in_features;
1551 let mut acc = self.biases[o];
1552 for (i, &inp) in input.iter().enumerate() {
1553 acc += self.weights[row_offset + i] * inp;
1554 }
1555 self.activation.apply(acc)
1556 })
1557 .collect()
1558 }
1559 pub fn set_weights(&mut self, w: &[f32]) {
1564 assert_eq!(
1565 w.len(),
1566 self.out_features * self.in_features,
1567 "set_weights: expected {} elements, got {}",
1568 self.out_features * self.in_features,
1569 w.len()
1570 );
1571 self.weights.copy_from_slice(w);
1572 }
1573 pub fn set_biases(&mut self, b: &[f32]) {
1578 assert_eq!(
1579 b.len(),
1580 self.out_features,
1581 "set_biases: expected {} elements, got {}",
1582 self.out_features,
1583 b.len()
1584 );
1585 self.biases.copy_from_slice(b);
1586 }
1587 pub fn parameter_count(&self) -> usize {
1589 self.out_features * self.in_features + self.out_features
1590 }
1591}
1592#[derive(Debug, Clone)]
1598pub struct DropoutLayer {
1599 pub rate: f64,
1601 pub training: bool,
1603 pub last_mask: Vec<f64>,
1605 pub(super) seed: u64,
1607}
1608impl DropoutLayer {
1609 pub fn new(rate: f64, training: bool) -> Self {
1611 assert!(
1612 (0.0..=1.0).contains(&rate),
1613 "dropout rate must be in [0, 1]"
1614 );
1615 Self {
1616 rate,
1617 training,
1618 last_mask: Vec::new(),
1619 seed: 0xdeadbeefcafe1234,
1620 }
1621 }
1622 pub fn set_seed(&mut self, seed: u64) {
1624 self.seed = seed;
1625 }
1626 pub fn forward(&mut self, input: &[f64]) -> Vec<f64> {
1632 if !self.training || self.rate == 0.0 {
1633 self.last_mask = vec![1.0; input.len()];
1634 return input.to_vec();
1635 }
1636 if self.rate == 1.0 {
1637 self.last_mask = vec![0.0; input.len()];
1638 return vec![0.0; input.len()];
1639 }
1640 let scale = 1.0 / (1.0 - self.rate);
1641 let mut mask = Vec::with_capacity(input.len());
1642 let mut output = Vec::with_capacity(input.len());
1643 for &x in input {
1644 self.seed = self
1645 .seed
1646 .wrapping_mul(6364136223846793005)
1647 .wrapping_add(1442695040888963407);
1648 let u = (self.seed >> 11) as f64 / (1u64 << 53) as f64;
1649 let m = if u >= self.rate { scale } else { 0.0 };
1650 mask.push(m);
1651 output.push(x * m);
1652 }
1653 self.last_mask = mask;
1654 output
1655 }
1656 pub fn backward(&self, delta_out: &[f64]) -> Vec<f64> {
1658 delta_out
1659 .iter()
1660 .zip(self.last_mask.iter())
1661 .map(|(&d, &m)| d * m)
1662 .collect()
1663 }
1664}
1665#[derive(Debug, Clone)]
1669pub struct BehlerParrinelloDescriptor {
1670 pub eta: Vec<f64>,
1672 pub rs: Vec<f64>,
1674 pub cutoff: f64,
1676}
1677impl BehlerParrinelloDescriptor {
1678 pub fn cutoff_fn(r: f64, rc: f64) -> f64 {
1682 if r < rc {
1683 0.5 * ((PI_F64 * r / rc).cos() + 1.0)
1684 } else {
1685 0.0
1686 }
1687 }
1688 pub fn radial_g1(r: f64, rc: f64) -> f64 {
1690 Self::cutoff_fn(r, rc)
1691 }
1692 pub fn radial_g2(r: f64, eta: f64, rs: f64, rc: f64) -> f64 {
1694 let diff = r - rs;
1695 (-eta * diff * diff).exp() * Self::cutoff_fn(r, rc)
1696 }
1697 pub fn angular_g4(
1701 r_ij: f64,
1702 r_ik: f64,
1703 r_jk: f64,
1704 cos_theta: f64,
1705 eta: f64,
1706 zeta: f64,
1707 lambda: f64,
1708 rc: f64,
1709 ) -> f64 {
1710 let angular = (1.0 + lambda * cos_theta).powf(zeta);
1711 let radial = (-eta * (r_ij * r_ij + r_ik * r_ik + r_jk * r_jk)).exp();
1712 let fc = Self::cutoff_fn(r_ij, rc) * Self::cutoff_fn(r_ik, rc) * Self::cutoff_fn(r_jk, rc);
1713 2.0_f64.powf(1.0 - zeta) * angular * radial * fc
1714 }
1715 pub fn compute(r_ij: f64, eta: f64, rs: f64, cutoff: f64) -> f64 {
1717 Self::radial_g2(r_ij, eta, rs, cutoff)
1718 }
1719 pub fn descriptor_vector(&self, positions: &[[f64; 3]], center_idx: usize) -> Vec<f64> {
1724 let n_descriptors = self.eta.len();
1725 let mut desc = vec![0.0_f64; n_descriptors];
1726 let ci = positions[center_idx];
1727 for (j, pos_j) in positions.iter().enumerate() {
1728 if j == center_idx {
1729 continue;
1730 }
1731 let dx = pos_j[0] - ci[0];
1732 let dy = pos_j[1] - ci[1];
1733 let dz = pos_j[2] - ci[2];
1734 let r = (dx * dx + dy * dy + dz * dz).sqrt();
1735 if r >= self.cutoff {
1736 continue;
1737 }
1738 for (dk, (&eta_k, &rs_k)) in desc.iter_mut().zip(self.eta.iter().zip(self.rs.iter())) {
1739 *dk += Self::radial_g2(r, eta_k, rs_k, self.cutoff);
1740 }
1741 }
1742 desc
1743 }
1744}
1745#[derive(Debug, Clone, PartialEq)]
1747pub enum ExtActivation {
1748 LeakyRelu(f64),
1750 Swish(f64),
1752 Relu,
1754 Sigmoid,
1756 Tanh,
1758 Linear,
1760}
1761impl ExtActivation {
1762 pub fn apply(&self, x: f64) -> f64 {
1764 match self {
1765 ExtActivation::LeakyRelu(alpha) => {
1766 if x >= 0.0 {
1767 x
1768 } else {
1769 alpha * x
1770 }
1771 }
1772 ExtActivation::Swish(beta) => x / (1.0 + (-beta * x).exp()),
1773 ExtActivation::Relu => x.max(0.0),
1774 ExtActivation::Sigmoid => 1.0 / (1.0 + (-x).exp()),
1775 ExtActivation::Tanh => x.tanh(),
1776 ExtActivation::Linear => x,
1777 }
1778 }
1779 pub fn derivative(&self, x: f64) -> f64 {
1781 match self {
1782 ExtActivation::LeakyRelu(alpha) => {
1783 if x >= 0.0 {
1784 1.0
1785 } else {
1786 *alpha
1787 }
1788 }
1789 ExtActivation::Swish(beta) => {
1790 let sig = 1.0 / (1.0 + (-beta * x).exp());
1791 sig + beta * x * sig * (1.0 - sig)
1792 }
1793 ExtActivation::Relu => {
1794 if x > 0.0 {
1795 1.0
1796 } else {
1797 0.0
1798 }
1799 }
1800 ExtActivation::Sigmoid => {
1801 let s = 1.0 / (1.0 + (-x).exp());
1802 s * (1.0 - s)
1803 }
1804 ExtActivation::Tanh => {
1805 let t = x.tanh();
1806 1.0 - t * t
1807 }
1808 ExtActivation::Linear => 1.0,
1809 }
1810 }
1811 pub fn apply_vec(&self, v: &mut [f64]) {
1813 for x in v.iter_mut() {
1814 *x = self.apply(*x);
1815 }
1816 }
1817}