Skip to main content

sva_formula/
hash.rs

1// Concern: content-addresses a SpectralSum under the table version, and draws a seed at a step | Non-concern: what a hash keys (sva-engine) | IO: (&SpectralSum) -> Hash
2
3use std::fmt;
4
5use crate::closed_form::{
6    Body, Bound, ClosedForm, Edge, Excitation, Fold, IndexId, ModalBank, Mode, Part, Rational,
7    Series, Unary, Var,
8};
9use crate::complex::{C64, canonical};
10use crate::env::NodeId;
11use crate::lanes::Lanes;
12use crate::run::Mirror;
13use crate::spectral_sum::atom::{Singular, SpectralAtom};
14use crate::spectral_sum::{Lane, SpectralSum};
15use crate::table::TABLE_VERSION;
16
17/// Two independently primed FNV-1a lanes: serving one closed form's samples for another is silent
18/// corruption, so the width is set against birthday collisions rather than speed.
19#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
20pub struct Hash(pub u64, pub u64);
21
22impl fmt::Display for Hash {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        write!(f, "{:016x}{:016x}", self.0, self.1)
25    }
26}
27
28pub fn hash_spectral_sum(n: &SpectralSum) -> Hash {
29    hash_spectral_sum_under(n, TABLE_VERSION)
30}
31
32pub fn hash_closed_form(t: &ClosedForm) -> Hash {
33    hash_closed_form_under(t, TABLE_VERSION)
34}
35
36/// A spectral sum whose series terms read other nodes, each ref hashed as what `node` names it:
37/// the node's own content, never the number a graph gave it. A sum reading no node hashes as
38/// `hash_spectral_sum` does.
39pub fn hash_spectral_sum_with(n: &SpectralSum, node: &mut dyn FnMut(NodeId) -> Hash) -> Hash {
40    let mut s = Sink::new(0x01, TABLE_VERSION);
41    s.node = Some(node);
42    s.var(n.var);
43    s.u64(n.lanes.len() as u64);
44    for lane in &n.lanes {
45        s.lane(lane);
46    }
47    s.finish()
48}
49
50/// The version is the first field after the tag, so a table bump retires every entry keyed
51/// by one of these.
52pub fn hash_spectral_sum_under(n: &SpectralSum, table_version: u64) -> Hash {
53    let mut s = Sink::new(0x01, table_version);
54    s.var(n.var);
55    s.u64(n.lanes.len() as u64);
56    for lane in &n.lanes {
57        s.lane(lane);
58    }
59    s.finish()
60}
61
62pub fn hash_closed_form_under(t: &ClosedForm, table_version: u64) -> Hash {
63    let mut s = Sink::new(0x02, table_version);
64    s.var(t.var);
65    s.formula(&t.body);
66    s.finish()
67}
68
69/// Two operands of a sum or product, which IEEE commutes bit for bit; three never associate so.
70pub fn either_order(operands: &mut [Hash]) {
71    if let [a, b] = operands
72        && b < a
73    {
74        std::mem::swap(a, b);
75    }
76}
77
78/// A closed form as written, each subterm by its own hash and each ref as `node` names it, so a
79/// node named by its form's hash reads alike by ref or written in place.
80pub fn hash_written_with(t: &ClosedForm, node: &mut dyn FnMut(NodeId) -> Hash) -> Hash {
81    let mut s = Sink {
82        lanes: Lanes::default(),
83        node: Some(node),
84        bound: Vec::new(),
85        free: true,
86        merkle: true,
87    };
88    let held = s.part(&t.body);
89    match t.var {
90        Var::T => held,
91        Var::F => {
92            let mut f = Sink::new(0x06, 0);
93            f.u64(held.0);
94            f.u64(held.1);
95            f.finish()
96        }
97    }
98}
99
100/// A time a ref is read at, as a reading of that ref is kept under: an index it holds is one
101/// series' own, named by its number.
102pub fn hash_time(at: &Body) -> Hash {
103    let mut s = Sink::new(0x05, TABLE_VERSION);
104    s.free = true;
105    s.formula(at);
106    s.finish()
107}
108
109/// The unit-interval value one seed draws at one whole step, wherever the pair is written.
110pub fn draw(seed: u64, step: i64) -> f64 {
111    mix(mix(seed ^ DRAWN) ^ step as u64) as f64 / u64::MAX as f64
112}
113
114const DRAWN: u64 = 0x9e37_79b9_7f4a_7c15;
115
116/// The draw at the whole step nearest `key`, ties to even; `None` past any `i64` step.
117pub fn draw_nearest(seed: u64, key: f64) -> Option<f64> {
118    let step = key.round_ties_even();
119    let held = step >= i64::MIN as f64 && step < i64::MAX as f64;
120    held.then(|| draw(seed, step as i64))
121}
122
123struct Sink<'a> {
124    lanes: Lanes<0>,
125    node: Option<&'a mut dyn FnMut(NodeId) -> Hash>,
126    /// The series indices bound around the term being hashed, innermost last: an index is
127    /// hashed by which binder it names, never by the number a typing drew for it.
128    bound: Vec<IndexId>,
129    /// Whether an index no binder here names is named by its own number.
130    free: bool,
131    /// Each subterm named by its own hash, as a ref is by its node's.
132    merkle: bool,
133}
134
135impl<'a> Sink<'a> {
136    fn new(tag: u8, table_version: u64) -> Sink<'a> {
137        let mut s = Sink {
138            lanes: Lanes::default(),
139            node: None,
140            bound: Vec::new(),
141            free: false,
142            merkle: false,
143        };
144        s.byte(tag);
145        s.u64(table_version);
146        s
147    }
148
149    fn byte(&mut self, b: u8) {
150        self.lanes.word(u64::from(b));
151    }
152
153    fn u64(&mut self, v: u64) {
154        for b in v.to_le_bytes() {
155            self.byte(b);
156        }
157    }
158
159    fn i64(&mut self, v: i64) {
160        self.u64(v as u64);
161    }
162
163    fn f64(&mut self, v: f64) {
164        self.u64(canonical(v));
165    }
166
167    fn c64(&mut self, v: C64) {
168        let (re, im) = v.bits();
169        self.u64(re);
170        self.u64(im);
171    }
172
173    fn var(&mut self, v: Var) {
174        self.byte(match v {
175            Var::T => 0,
176            Var::F => 1,
177        });
178    }
179
180    fn edge(&mut self, e: Edge) {
181        match e {
182            Edge::NegInf => self.byte(0),
183            Edge::At(bits) => {
184                self.byte(1);
185                self.u64(bits);
186            }
187            Edge::PosInf => self.byte(2),
188        }
189    }
190
191    fn lane(&mut self, lane: &Lane) {
192        self.u64(lane.atoms.len() as u64);
193        for a in &lane.atoms {
194            self.atom(a);
195        }
196        self.u64(lane.series.len() as u64);
197        for s in &lane.series {
198            self.series(s);
199        }
200        self.u64(lane.modal.len() as u64);
201        for m in &lane.modal {
202            self.modal(m);
203        }
204    }
205
206    /// `Origin` is excluded: two spellings from different files must hash alike.
207    fn atom(&mut self, a: &SpectralAtom) {
208        self.c64(a.c);
209        self.u64(u64::from(a.poly.degree));
210        self.f64(a.poly.at);
211        match a.exp {
212            None => self.byte(0),
213            Some(e) => {
214                self.byte(1);
215                self.f64(e.sigma);
216                self.f64(e.omega);
217            }
218        }
219        match a.gauss {
220            None => self.byte(0),
221            Some(g) => {
222                self.byte(1);
223                self.f64(g.a);
224                self.f64(g.mu);
225            }
226        }
227        match a.ind {
228            None => self.byte(0),
229            Some(i) => {
230                self.byte(1);
231                self.edge(i.l);
232                self.edge(i.r);
233            }
234        }
235        match a.pole {
236            None => self.byte(0),
237            Some(p) => {
238                self.byte(1);
239                self.c64(p.at);
240                self.u64(u64::from(p.order));
241                self.byte(u8::from(p.pv));
242            }
243        }
244        match a.sing {
245            Singular::Regular => self.byte(0),
246            Singular::Delta { at, order } => {
247                self.byte(1);
248                self.f64(at);
249                self.u64(u64::from(order));
250            }
251        }
252    }
253
254    fn series(&mut self, s: &Series) {
255        self.i64(s.lo);
256        match s.hi {
257            Bound::Finite(n) => {
258                self.byte(0);
259                self.i64(n);
260            }
261            Bound::Infinite => self.byte(1),
262        }
263        self.bound.push(s.index);
264        self.formula(&s.term.body);
265        self.bound.pop();
266    }
267
268    fn amps(&mut self, amps: &[C64]) {
269        self.u64(amps.len() as u64);
270        for a in amps {
271            self.c64(*a);
272        }
273    }
274
275    fn modal(&mut self, m: &ModalBank) {
276        self.u64(m.modes.len() as u64);
277        for Mode {
278            omega,
279            tau,
280            amp,
281            phase,
282        } in &m.modes
283        {
284            self.f64(*omega);
285            self.f64(*tau);
286            self.f64(*amp);
287            self.f64(*phase);
288        }
289        match m.excite {
290            Excitation::HammerPulse { f0, t0, contact } => {
291                self.byte(0);
292                self.f64(f0);
293                self.f64(t0);
294                self.f64(contact);
295            }
296            Excitation::Impulse { t0 } => {
297                self.byte(1);
298                self.f64(t0);
299            }
300        }
301    }
302
303    fn parts(&mut self, parts: &[Part]) {
304        self.u64(parts.len() as u64);
305        for p in parts {
306            self.child(&p.body);
307        }
308    }
309
310    fn commuting(&mut self, parts: &[Part]) {
311        let [a, b] = parts else {
312            return self.parts(parts);
313        };
314        if !self.merkle {
315            return self.parts(parts);
316        }
317        let mut held = [self.part(&a.body), self.part(&b.body)];
318        either_order(&mut held);
319        self.u64(2);
320        for Hash(x, y) in held {
321            self.u64(x);
322            self.u64(y);
323        }
324    }
325
326    fn child(&mut self, f: &Body) {
327        match self.merkle {
328            true => {
329                let Hash(x, y) = self.part(f);
330                self.u64(x);
331                self.u64(y);
332            }
333            false => self.formula(f),
334        }
335    }
336
337    fn part(&mut self, f: &Body) -> Hash {
338        if let Body::Node(n) = f
339            && let Some(named) = self.node.as_mut()
340        {
341            return named(*n);
342        }
343        let mut sub = Sink {
344            lanes: Lanes::default(),
345            node: self.node.take(),
346            bound: std::mem::take(&mut self.bound),
347            free: self.free,
348            merkle: true,
349        };
350        sub.formula(f);
351        self.node = sub.node.take();
352        self.bound = std::mem::take(&mut sub.bound);
353        sub.finish()
354    }
355
356    fn rational(&mut self, r: &Rational) {
357        self.u64(r.zeros.len() as u64);
358        for z in &r.zeros {
359            self.c64(*z);
360        }
361        self.u64(r.poles.len() as u64);
362        for p in &r.poles {
363            self.c64(*p);
364        }
365        self.c64(r.gain);
366    }
367
368    fn formula(&mut self, f: &Body) {
369        match f {
370            Body::Const(c) => {
371                self.byte(0x10);
372                self.c64(*c);
373            }
374            Body::Line => self.byte(0x11),
375            Body::Index(i) => match self.bound.iter().rev().position(|b| b == i) {
376                Some(depth) => {
377                    self.byte(0x12);
378                    self.u64(depth as u64);
379                }
380                None => {
381                    assert!(self.free, "an index inside the series binding it");
382                    self.byte(0x29);
383                    self.u64(u64::from(i.0));
384                }
385            },
386            Body::Param(p) => {
387                self.byte(0x13);
388                self.u64(u64::from(p.0));
389            }
390            Body::Node(n) => {
391                self.byte(0x14);
392                match self.node.as_mut().map(|named| named(*n)) {
393                    Some(held) => {
394                        self.u64(held.0);
395                        self.u64(held.1);
396                    }
397                    None => self.u64(u64::from(n.0)),
398                }
399            }
400            Body::Add(parts) => {
401                self.byte(0x15);
402                self.commuting(parts);
403            }
404            Body::Mul(parts) => {
405                self.byte(0x16);
406                self.commuting(parts);
407            }
408            Body::Div(a, b) => {
409                self.byte(0x17);
410                self.child(&a.body);
411                self.child(&b.body);
412            }
413            Body::Pow(base, n) => {
414                self.byte(0x18);
415                self.child(&base.body);
416                self.i64(i64::from(*n));
417            }
418            Body::Apply(op, arg) => {
419                self.byte(0x19);
420                self.byte(unary_tag(*op));
421                self.child(&arg.body);
422            }
423            Body::Fold(op, args) => {
424                self.byte(0x1a);
425                self.byte(match op {
426                    Fold::Max => 0,
427                    Fold::Min => 1,
428                    Fold::Mod => 2,
429                });
430                self.parts(args);
431            }
432            Body::Delta { at, order } => {
433                self.byte(0x1b);
434                self.child(&at.body);
435                self.u64(u64::from(*order));
436            }
437            Body::Pv(at) => {
438                self.byte(0x1c);
439                self.child(&at.body);
440            }
441            Body::Warp { at, of } => {
442                self.byte(0x27);
443                self.child(&at.body);
444                self.child(&of.body);
445            }
446            Body::Shift { by, of } => {
447                self.byte(0x1d);
448                self.f64(*by);
449                self.child(&of.body);
450            }
451            Body::Deriv { order, of } => {
452                self.byte(0x1e);
453                self.u64(u64::from(*order));
454                self.child(&of.body);
455            }
456            Body::Crop {
457                of,
458                l,
459                r,
460                rise,
461                fall,
462            } => {
463                self.byte(0x1f);
464                self.child(&of.body);
465                self.edge(*l);
466                self.edge(*r);
467                self.f64(*rise);
468                self.f64(*fall);
469            }
470            Body::Join(parts) => {
471                self.byte(0x21);
472                self.parts(parts);
473            }
474            Body::Channel(of, k) => {
475                self.byte(0x22);
476                self.child(&of.body);
477                self.byte(*k);
478            }
479            Body::Rational(r) => {
480                self.byte(0x23);
481                self.rational(r);
482            }
483            Body::Series(s) => {
484                self.byte(0x24);
485                self.series(s);
486            }
487            Body::Modal(m) => {
488                self.byte(0x25);
489                self.modal(m);
490            }
491            Body::Run(run) => {
492                self.byte(0x28);
493                self.f64(run.offset);
494                self.f64(run.step);
495                self.i64(run.first);
496                self.amps(&run.amps);
497                match &run.mirror {
498                    Mirror::None => self.byte(0),
499                    Mirror::Conjugate => self.byte(1),
500                    Mirror::Held(amps) => {
501                        self.byte(2);
502                        self.amps(amps);
503                    }
504                }
505            }
506            Body::Banded(b) => {
507                self.byte(0x29);
508                self.series(&b.series);
509                for sum in [&b.slope, &b.offset] {
510                    self.u64(sum.lanes.len() as u64);
511                    sum.lanes.iter().for_each(|lane| self.lane(lane));
512                }
513                for x in [b.omega, b.reach, b.dropped_db] {
514                    self.f64(x);
515                }
516                self.i64(b.most);
517                self.i64(b.widest);
518            }
519            Body::Keyed { seed, of } => {
520                self.byte(0x26);
521                self.u64(*seed);
522                self.child(&of.body);
523            }
524        }
525    }
526
527    /// One avalanche past the lanes, so a short input still fills both words.
528    fn finish(&self) -> Hash {
529        let Hash(a, b) = self.lanes.finish();
530        Hash(mix(a), mix(b))
531    }
532}
533
534fn unary_tag(op: Unary) -> u8 {
535    match op {
536        Unary::Sin => 0,
537        Unary::Cos => 1,
538        Unary::Exp => 2,
539        Unary::Tanh => 3,
540        Unary::Sat => 4,
541        Unary::Abs => 5,
542        Unary::Log => 6,
543        Unary::Sqrt => 7,
544        Unary::Step => 8,
545    }
546}
547
548fn mix(mut z: u64) -> u64 {
549    z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
550    z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
551    z ^ (z >> 31)
552}