Skip to main content

primase_core/layered/
expression.rs

1//! [`Expression`]: a weight channel as a small expression over sources, for what the named [`Channel`] forms do not
2//! spell, and a layer's colour lane over the lanes of its own colour ([`Layer::color`](super::Layer::color)).
3//!
4//! An expression is a list of [`Term`]s, each a constant, a source or one f32 operation on earlier terms; the last is
5//! the channel's value. Shared values are one term, so a value used twice is computed once.
6//!
7//! # Numeric rules
8//!
9//! Every term is an f32 and every operation is rounded to f32 on its own: nothing is fused (no multiply-add) or
10//! reassociated, so a program making the same operations in the same order makes the same bits. These rules are the
11//! named forms' too ([`Channel::evaluate`]):
12//!
13//! - `add`, `subtract`, `multiply`: IEEE 754 binary32, rounding to nearest even.
14//! - `min`, `max`: a NaN operand gives the other operand (IEEE 754's minNum and maxNum).
15//! - `saturate`: [`saturate`], NaN gives 0, anything else is clamped to 0..=1.
16//! - Lerp, power and product are spelled in these operations: [`lerp`] is `a + (b - a) * t` (the difference, then
17//!   the product, then the sum); [`power`](super::power) squares for a power of two and otherwise multiplies `v` in
18//!   from the left; a product multiplies from the left.
19//! - The two zeros are one value: a constant `-0` is `0`, and no operation here tells them apart.
20//!
21//! # One spelling
22//!
23//! An expression is stored in its canonical spelling ([`Expression::canonical`]): every term and source used, equal
24//! terms and equal sources one, the operands of `add`, `multiply`, `min` and `max` (which give the same bits in either
25//! order) in a stated order, and the terms in the order a walk from the value first reaches them. Two expressions that
26//! make the same operations on the same sources are then the same expression. An expression that makes exactly a named
27//! form's operations ([`Channel::expression`]) is that form ([`Channel::from_expression`]), so a consumer that maps
28//! the named forms keeps receiving them. The named forms are not canonical among themselves: a product of `a` and `b`
29//! is also the product of `b` and `a`.
30use std::cmp::Ordering;
31use std::collections::HashMap;
32
33use serde::Serialize;
34
35use super::{Channel, POWER_LIMIT, PRODUCT_LIMIT, Source};
36
37/// The most terms an expression has, in its canonical spelling.
38pub const TERM_LIMIT: usize = 64;
39
40/// The most terms a spelling may have before it is made canonical (its operands index them as bytes).
41const RAW_LIMIT: usize = u8::MAX as usize;
42
43/// `saturate(x)`: 0 for NaN, else `x` clamped to 0..=1.
44pub fn saturate(x: f32) -> f32 {
45    if x.is_nan() { 0.0 } else { x.clamp(0.0, 1.0) }
46}
47
48/// `lerp(a, b, t)`: `a + (b - a) * t`, in that order.
49pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
50    a + (b - a) * t
51}
52
53/// One value of an [`Expression`]: a constant, a source, or one operation on earlier terms (by their index).
54#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
55#[serde(rename_all = "snake_case")]
56pub enum Term {
57    /// A finite constant.
58    Constant(f32),
59    /// The value of [`Expression::sources`]' source at this index.
60    Source(u8),
61    Add([u8; 2]),
62    /// The first minus the second.
63    Subtract([u8; 2]),
64    Multiply([u8; 2]),
65    Min([u8; 2]),
66    Max([u8; 2]),
67    /// [`saturate`].
68    Saturate(u8),
69}
70
71impl Term {
72    /// The terms it reads, in its order.
73    pub fn operands(&self) -> &[u8] {
74        match self {
75            Term::Constant(_) | Term::Source(_) => &[],
76            Term::Add(o) | Term::Subtract(o) | Term::Multiply(o) | Term::Min(o) | Term::Max(o) => o,
77            Term::Saturate(o) => std::slice::from_ref(o),
78        }
79    }
80
81    /// Whether its operands give the same bits in either order.
82    fn commutative(&self) -> bool {
83        matches!(
84            self,
85            Term::Add(_) | Term::Multiply(_) | Term::Min(_) | Term::Max(_)
86        )
87    }
88
89    /// Its kind's place in the canonical order.
90    fn rank(&self) -> u8 {
91        match self {
92            Term::Constant(_) => 0,
93            Term::Source(_) => 1,
94            Term::Add(_) => 2,
95            Term::Subtract(_) => 3,
96            Term::Multiply(_) => 4,
97            Term::Min(_) => 5,
98            Term::Max(_) => 6,
99            Term::Saturate(_) => 7,
100        }
101    }
102
103    /// The same operation on other terms (`operands` as many as it reads).
104    fn with_operands(&self, operands: &[u8]) -> Term {
105        let two = || [operands[0], operands[1]];
106        match self {
107            Term::Constant(_) | Term::Source(_) => *self,
108            Term::Add(_) => Term::Add(two()),
109            Term::Subtract(_) => Term::Subtract(two()),
110            Term::Multiply(_) => Term::Multiply(two()),
111            Term::Min(_) => Term::Min(two()),
112            Term::Max(_) => Term::Max(two()),
113            Term::Saturate(_) => Term::Saturate(operands[0]),
114        }
115    }
116
117    /// Whether it is the same term as `other` with commutative operands in either order.
118    fn same(&self, other: &Term) -> bool {
119        let sorted = |t: &Term| {
120            let mut o = t.operands().to_vec();
121            if t.commutative() {
122                o.sort_unstable();
123            }
124            t.with_operands(&o)
125        };
126        match (self, other) {
127            (Term::Constant(a), Term::Constant(b)) => a.to_bits() == b.to_bits(),
128            _ => sorted(self) == sorted(other),
129        }
130    }
131}
132
133/// A value per pixel as f32 operations on its sources (see the module): a weight channel's over [`Source`]s, a layer's
134/// colour lane over the lanes of its own colour ([`ColorLane`](super::ColorLane)).
135#[derive(Clone, Debug, PartialEq, Serialize)]
136pub struct Expression<S = Source> {
137    /// The sources its [`Term::Source`] terms read, each once.
138    pub sources: Vec<S>,
139    /// Its terms, each after the terms it reads; the last is the value.
140    pub terms: Vec<Term>,
141}
142
143impl<S: Clone + PartialEq + Serialize> Expression<S> {
144    /// Its value from its sources' values (in [`Expression::sources`] order), under the module's numeric rules.
145    pub fn evaluate(&self, values: &[f32]) -> f32 {
146        let mut v = [0.0f32; TERM_LIMIT];
147        for (i, term) in self.terms.iter().enumerate().take(TERM_LIMIT) {
148            let at = |k: u8| v[usize::from(k)];
149            v[i] = match *term {
150                Term::Constant(x) => x,
151                Term::Source(s) => values[usize::from(s)],
152                Term::Add([a, b]) => at(a) + at(b),
153                Term::Subtract([a, b]) => at(a) - at(b),
154                Term::Multiply([a, b]) => at(a) * at(b),
155                Term::Min([a, b]) => at(a).min(at(b)),
156                Term::Max([a, b]) => at(a).max(at(b)),
157                Term::Saturate(a) => saturate(at(a)),
158            };
159        }
160        self.terms
161            .len()
162            .checked_sub(1)
163            .and_then(|last| v.get(last))
164            .copied()
165            .unwrap_or(f32::NAN)
166    }
167
168    /// Its canonical spelling (see the module), or why it has none: a term reading itself or a later term, a source
169    /// that is not there, a constant that is not finite, no terms, or more than [`TERM_LIMIT`] once canonical.
170    pub fn canonical(&self) -> Result<Expression<S>, String> {
171        let root = self
172            .terms
173            .len()
174            .checked_sub(1)
175            .ok_or("an expression without terms")?;
176        canonical(&self.sources, &self.terms, root)
177    }
178
179    /// Its own checks (its sources' apart): it is its canonical spelling. That it is no named form is
180    /// [`Channel::check`]'s.
181    pub fn check(&self) -> Result<(), String> {
182        let canonical = self.canonical()?;
183        if canonical != *self {
184            return Err(format!(
185                "expression is not canonical: {:?} is spelled {:?}",
186                self.terms, canonical.terms
187            ));
188        }
189        Ok(())
190    }
191}
192
193/// A term of an [`ExpressionBuilder`].
194#[derive(Clone, Copy, Debug, PartialEq, Eq)]
195pub struct TermId(usize);
196
197/// Spells an expression term by term, in any order and with any repetition; [`ExpressionBuilder::expression`] makes
198/// it canonical, and [`ExpressionBuilder::channel`] the named form it is.
199#[derive(Clone, Debug)]
200pub struct ExpressionBuilder<S = Source> {
201    sources: Vec<S>,
202    terms: Vec<Term>,
203    /// Past [`RAW_LIMIT`] terms: the spelling is refused.
204    overflow: bool,
205}
206
207impl<S> Default for ExpressionBuilder<S> {
208    fn default() -> Self {
209        ExpressionBuilder {
210            sources: Vec::new(),
211            terms: Vec::new(),
212            overflow: false,
213        }
214    }
215}
216
217impl<S: Clone + PartialEq + Serialize> ExpressionBuilder<S> {
218    pub fn new() -> Self {
219        Self::default()
220    }
221
222    fn push(&mut self, t: Term) -> TermId {
223        if self.terms.len() >= RAW_LIMIT {
224            self.overflow = true;
225            return TermId(0);
226        }
227        self.terms.push(t);
228        TermId(self.terms.len() - 1)
229    }
230
231    fn index(&mut self, v: TermId) -> u8 {
232        u8::try_from(v.0).unwrap_or_else(|_| {
233            self.overflow = true;
234            0
235        })
236    }
237
238    pub fn source(&mut self, s: S) -> TermId {
239        self.sources.push(s);
240        let i = self.sources.len() - 1;
241        let i = u8::try_from(i).unwrap_or_else(|_| {
242            self.overflow = true;
243            0
244        });
245        self.push(Term::Source(i))
246    }
247
248    pub fn constant(&mut self, x: f32) -> TermId {
249        self.push(Term::Constant(x))
250    }
251
252    fn binary(&mut self, a: TermId, b: TermId, make: fn([u8; 2]) -> Term) -> TermId {
253        let o = [self.index(a), self.index(b)];
254        self.push(make(o))
255    }
256
257    pub fn add(&mut self, a: TermId, b: TermId) -> TermId {
258        self.binary(a, b, Term::Add)
259    }
260
261    /// `a - b`.
262    pub fn subtract(&mut self, a: TermId, b: TermId) -> TermId {
263        self.binary(a, b, Term::Subtract)
264    }
265
266    pub fn multiply(&mut self, a: TermId, b: TermId) -> TermId {
267        self.binary(a, b, Term::Multiply)
268    }
269
270    pub fn min(&mut self, a: TermId, b: TermId) -> TermId {
271        self.binary(a, b, Term::Min)
272    }
273
274    pub fn max(&mut self, a: TermId, b: TermId) -> TermId {
275        self.binary(a, b, Term::Max)
276    }
277
278    pub fn saturate(&mut self, a: TermId) -> TermId {
279        let o = self.index(a);
280        self.push(Term::Saturate(o))
281    }
282
283    /// [`lerp`]'s operations.
284    pub fn lerp(&mut self, a: TermId, b: TermId, t: TermId) -> TermId {
285        let d = self.subtract(b, a);
286        let s = self.multiply(d, t);
287        self.add(a, s)
288    }
289
290    /// [`power`](super::power)'s operations; an exponent of 0 is the constant 1.
291    pub fn power(&mut self, v: TermId, exponent: u8) -> TermId {
292        if exponent == 0 {
293            return self.constant(1.0);
294        }
295        if exponent.is_power_of_two() {
296            (0..exponent.trailing_zeros()).fold(v, |x, _| self.multiply(x, x))
297        } else {
298            (1..exponent).fold(v, |x, _| self.multiply(x, v))
299        }
300    }
301
302    /// The canonical expression whose value is `root`.
303    pub fn expression(&self, root: TermId) -> Result<Expression<S>, String> {
304        if self.overflow {
305            return Err(format!(
306                "an expression spelled in more than {RAW_LIMIT} terms or sources"
307            ));
308        }
309        if root.0 >= self.terms.len() {
310            return Err(format!("term {} is not in the expression", root.0));
311        }
312        canonical(&self.sources, &self.terms, root.0)
313    }
314}
315
316impl ExpressionBuilder {
317    /// The channel whose value is `root`: the named form it is, else the canonical expression.
318    pub fn channel(&self, root: TermId) -> Result<Channel, String> {
319        self.expression(root).map(Channel::from_expression_canonical)
320    }
321}
322
323/// That every operand reads an earlier term and every source term a source, and every constant is finite.
324fn well_formed<S>(sources: &[S], terms: &[Term], root: usize) -> Result<(), String> {
325    if terms.len() > RAW_LIMIT || sources.len() > RAW_LIMIT {
326        return Err(format!(
327            "an expression spelled in more than {RAW_LIMIT} terms or sources"
328        ));
329    }
330    for (i, t) in terms.iter().enumerate().take(root + 1) {
331        if let Some(&o) = t.operands().iter().find(|&&o| usize::from(o) >= i) {
332            return Err(format!("term {i} reads term {o}, which is not before it"));
333        }
334        match *t {
335            Term::Source(s) if usize::from(s) >= sources.len() => {
336                return Err(format!("term {i} reads source {s} of {}", sources.len()));
337            }
338            Term::Constant(x) if !x.is_finite() => {
339                return Err(format!("term {i}: constant {x} is not finite"));
340            }
341            _ => {}
342        }
343    }
344    Ok(())
345}
346
347/// The canonical spelling of term `root` of `terms` over `sources` (see the module).
348fn canonical<S: Clone + PartialEq + Serialize>(
349    sources: &[S],
350    terms: &[Term],
351    root: usize,
352) -> Result<Expression<S>, String> {
353    well_formed(sources, terms, root)?;
354    let mut needed = vec![false; root + 1];
355    needed[root] = true;
356    for i in (0..=root).rev() {
357        if needed[i] {
358            for &o in terms[i].operands() {
359                needed[usize::from(o)] = true;
360            }
361        }
362    }
363    // equal terms (over equal classes, commutative operands in either order) are one class; equal sources one source
364    let mut distinct: Vec<&S> = Vec::new();
365    let mut classes: Vec<Term> = Vec::new();
366    let mut class_of = vec![0u8; root + 1];
367    for i in (0..=root).filter(|&i| needed[i]) {
368        let operands: Vec<u8> = terms[i]
369            .operands()
370            .iter()
371            .map(|&o| class_of[usize::from(o)])
372            .collect();
373        let term = match terms[i] {
374            Term::Source(s) => {
375                let s = &sources[usize::from(s)];
376                let d = distinct.iter().position(|x| *x == s).unwrap_or_else(|| {
377                    distinct.push(s);
378                    distinct.len() - 1
379                });
380                Term::Source(d as u8)
381            }
382            // the two zeros are one value
383            Term::Constant(x) => Term::Constant(if x == 0.0 { 0.0 } else { x }),
384            t => t.with_operands(&operands),
385        };
386        let class = classes.iter().position(|c| c.same(&term)).unwrap_or_else(|| {
387            classes.push(term);
388            classes.len() - 1
389        });
390        class_of[i] = class as u8;
391    }
392    let keys = distinct
393        .iter()
394        .map(|s| serde_json::to_string(s).map_err(|e| format!("a source without a key: {e}")))
395        .collect::<Result<Vec<String>, String>>()?;
396    let mut order = Order {
397        classes: &classes,
398        keys: &keys,
399        memo: HashMap::new(),
400    };
401    let mut emit = Emit {
402        index: vec![None; classes.len()],
403        source_index: vec![None; distinct.len()],
404        sources: Vec::new(),
405        terms: Vec::new(),
406    };
407    emit.class(&mut order, &distinct, usize::from(class_of[root]));
408    if emit.terms.len() > TERM_LIMIT {
409        return Err(format!(
410            "an expression of {} terms, past {TERM_LIMIT}",
411            emit.terms.len()
412        ));
413    }
414    Ok(Expression {
415        sources: emit.sources,
416        terms: emit.terms,
417    })
418}
419
420/// The canonical order of classes: by kind, then constants by value, sources by their JSON, operations by their
421/// operands in their canonical order. Two classes are equal in it only when they are one class.
422struct Order<'a> {
423    classes: &'a [Term],
424    keys: &'a [String],
425    memo: HashMap<(usize, usize), Ordering>,
426}
427
428impl Order<'_> {
429    fn operands(&mut self, c: usize) -> Vec<usize> {
430        let t = self.classes[c];
431        let mut o: Vec<usize> = t.operands().iter().map(|&o| usize::from(o)).collect();
432        if t.commutative() && self.cmp(o[1], o[0]) == Ordering::Less {
433            o.swap(0, 1);
434        }
435        o
436    }
437
438    fn cmp(&mut self, a: usize, b: usize) -> Ordering {
439        if a == b {
440            return Ordering::Equal;
441        }
442        if let Some(&o) = self.memo.get(&(a, b)) {
443            return o;
444        }
445        let (x, y) = (self.classes[a], self.classes[b]);
446        let o = match (x, y) {
447            _ if x.rank() != y.rank() => x.rank().cmp(&y.rank()),
448            (Term::Constant(p), Term::Constant(q)) => p.total_cmp(&q),
449            (Term::Source(p), Term::Source(q)) => self.keys[usize::from(p)].cmp(&self.keys[usize::from(q)]),
450            _ => {
451                let (p, q) = (self.operands(a), self.operands(b));
452                p.iter()
453                    .zip(&q)
454                    .map(|(&i, &j)| self.cmp(i, j))
455                    .find(|o| o.is_ne())
456                    .unwrap_or(Ordering::Equal)
457            }
458        };
459        self.memo.insert((a, b), o);
460        o
461    }
462}
463
464/// The canonical terms, emitted by a walk from the value: operands first, in their canonical order.
465struct Emit<S> {
466    index: Vec<Option<u8>>,
467    source_index: Vec<Option<u8>>,
468    sources: Vec<S>,
469    terms: Vec<Term>,
470}
471
472impl<S: Clone> Emit<S> {
473    fn class(&mut self, order: &mut Order, distinct: &[&S], c: usize) -> u8 {
474        if let Some(i) = self.index[c] {
475            return i;
476        }
477        let operands: Vec<u8> = order
478            .operands(c)
479            .into_iter()
480            .map(|o| self.class(order, distinct, o))
481            .collect();
482        let term = match order.classes[c] {
483            Term::Source(s) => {
484                let s = usize::from(s);
485                let i = *self.source_index[s].get_or_insert_with(|| {
486                    self.sources.push(distinct[s].clone());
487                    (self.sources.len() - 1) as u8
488                });
489                Term::Source(i)
490            }
491            t => t.with_operands(&operands),
492        };
493        self.terms.push(term);
494        let i = (self.terms.len() - 1) as u8;
495        self.index[c] = Some(i);
496        i
497    }
498}
499
500impl Channel {
501    /// Its operations as an [`Expression`], in the canonical spelling: what it means under the numeric rules.
502    pub fn expression(&self) -> Result<Expression, String> {
503        self.check()?;
504        let mut b = ExpressionBuilder::new();
505        let root = match self {
506            Channel::Source(s) => b.source(s.clone()),
507            Channel::Lerp { a, b: to, t } => {
508                let (a, to, t) = (b.source(a.clone()), b.source(to.clone()), b.source(t.clone()));
509                b.lerp(a, to, t)
510            }
511            Channel::Threshold { v, m, k } => {
512                // ((k0 + k1 v) + k2 m) + (k3 v) m, as [`Channel::evaluate`] writes it
513                let (v, m) = (b.source(v.clone()), b.source(m.clone()));
514                let [k0, k1, k2, k3] = k.map(|x| b.constant(x));
515                let k1v = b.multiply(k1, v);
516                let x = b.add(k0, k1v);
517                let k2m = b.multiply(k2, m);
518                let x = b.add(x, k2m);
519                let k3v = b.multiply(k3, v);
520                let k3vm = b.multiply(k3v, m);
521                let x = b.add(x, k3vm);
522                b.saturate(x)
523            }
524            Channel::Product { sources } => {
525                let mut values = sources
526                    .iter()
527                    .map(|s| b.source(s.clone()))
528                    .collect::<Vec<_>>()
529                    .into_iter();
530                let first = values.next().ok_or("a product of no sources")?;
531                values.fold(first, |p, v| b.multiply(p, v))
532            }
533            Channel::Power { v, exponent } => {
534                let v = b.source(v.clone());
535                b.power(v, *exponent)
536            }
537            Channel::Expression(e) => return Ok(e.clone()),
538        };
539        b.expression(root)
540    }
541
542    /// The channel `e` spells: the named form whose operations it makes, else `e` in its canonical spelling.
543    pub fn from_expression(e: &Expression) -> Result<Channel, String> {
544        e.canonical().map(Self::from_expression_canonical)
545    }
546
547    /// [`Channel::from_expression`] of a canonical expression.
548    fn from_expression_canonical(e: Expression) -> Channel {
549        named(&e).unwrap_or(Channel::Expression(e))
550    }
551}
552
553/// The named form whose canonical expression is `e`, trying source, power, product, lerp and threshold in that order,
554/// each with its sources and constants taken from `e` in their order there.
555fn named(e: &Expression) -> Option<Channel> {
556    let constants: Vec<f32> = e
557        .terms
558        .iter()
559        .filter_map(|t| match t {
560            Term::Constant(x) => Some(*x),
561            _ => None,
562        })
563        .collect();
564    let s = &e.sources;
565    let mut candidates: Vec<Channel> = Vec::new();
566    match e.terms.last()? {
567        Term::Source(_) => candidates.push(Channel::Source(s.first()?.clone())),
568        Term::Multiply(_) if constants.is_empty() => {
569            if s.len() == 1 {
570                candidates.extend((2..=POWER_LIMIT).map(|exponent| Channel::Power {
571                    v: s[0].clone(),
572                    exponent,
573                }));
574            }
575            if s.len() <= PRODUCT_LIMIT {
576                for n in 2..=PRODUCT_LIMIT {
577                    candidates.extend(sequences(s, n).map(|sources| Channel::Product { sources }));
578                }
579            }
580        }
581        Term::Add(_) if constants.is_empty() && s.len() <= 3 => {
582            candidates.extend(sequences(s, 3).map(|mut abt| {
583                let (t, b, a) = (abt.remove(2), abt.remove(1), abt.remove(0));
584                Channel::Lerp { a, b, t }
585            }));
586        }
587        Term::Saturate(_) if s.len() <= 2 && constants.len() <= 4 && e.terms.len() <= 14 => {
588            let ks = (0..constants.len().pow(4)).map(|mut n| {
589                std::array::from_fn(|_| {
590                    let k = constants[n % constants.len()];
591                    n /= constants.len();
592                    k
593                })
594            });
595            let ks: Vec<[f32; 4]> = ks.collect();
596            for vm in sequences(s, 2) {
597                candidates.extend(ks.iter().map(|&k| Channel::Threshold {
598                    v: vm[0].clone(),
599                    m: vm[1].clone(),
600                    k,
601                }));
602            }
603        }
604        _ => {}
605    }
606    candidates
607        .into_iter()
608        .find(|c| c.expression().is_ok_and(|x| x == *e))
609}
610
611/// Every sequence of `n` of `sources` (repeats allowed) that uses each, in lexicographic order of their indices.
612fn sequences(sources: &[Source], n: usize) -> impl Iterator<Item = Vec<Source>> + '_ {
613    let k = sources.len();
614    let count = if k == 0 { 0 } else { k.pow(n as u32) };
615    (0..count).filter_map(move |mut x| {
616        let mut picks = vec![0; n];
617        for p in picks.iter_mut().rev() {
618            *p = x % k;
619            x /= k;
620        }
621        (0..k)
622            .all(|s| picks.contains(&s))
623            .then(|| picks.iter().map(|&p| sources[p].clone()).collect())
624    })
625}