tenferro_ext_tropical/scalar.rs
1//! Tropical scalar wrapper types.
2//!
3//! Each newtype redefines `Add` and `Mul` so that the [`Scalar`](tenferro_algebra::Scalar)
4//! blanket impl is satisfied with tropical semantics:
5//!
6//! | Type | `+` (⊕) | `*` (⊗) | Zero | One |
7//! |------|---------|---------|------|-----|
8//! | [`MaxPlus<T>`] | max | + | −∞ | 0 |
9//! | [`MinPlus<T>`] | min | + | +∞ | 0 |
10//! | [`MaxMul<T>`] | max | × | 0 | 1 |
11//!
12//! All arithmetic operations are fully implemented.
13
14use std::fmt;
15use std::ops;
16
17use tenferro_algebra::Conjugate;
18
19/// Max-plus tropical scalar: ⊕ = max, ⊗ = +.
20///
21/// The most common tropical semiring, used for shortest-path and
22/// optimal-alignment problems. Wraps any ordered numeric type `T`.
23///
24/// # Examples
25///
26/// ```
27/// use tenferro_ext_tropical::MaxPlus;
28///
29/// let a = MaxPlus(3.0_f64);
30/// let b = MaxPlus(5.0_f64);
31///
32/// // Tropical addition = max
33/// let c = a + b;
34/// assert_eq!(c, MaxPlus(5.0));
35///
36/// // Tropical multiplication = ordinary addition
37/// let d = a * b;
38/// assert_eq!(d, MaxPlus(8.0));
39/// ```
40#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
41#[repr(transparent)]
42pub struct MaxPlus<T>(pub T);
43
44/// Min-plus tropical scalar: ⊕ = min, ⊗ = +.
45///
46/// Used for shortest-path problems (Dijkstra, Bellman–Ford).
47///
48/// # Examples
49///
50/// ```
51/// use tenferro_ext_tropical::MinPlus;
52///
53/// let a = MinPlus(3.0_f64);
54/// let b = MinPlus(5.0_f64);
55///
56/// // Tropical addition = min
57/// let c = a + b;
58/// assert_eq!(c, MinPlus(3.0));
59///
60/// // Tropical multiplication = ordinary addition
61/// let d = a * b;
62/// assert_eq!(d, MinPlus(8.0));
63/// ```
64#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
65#[repr(transparent)]
66pub struct MinPlus<T>(pub T);
67
68/// Max-times tropical scalar: ⊕ = max, ⊗ = ×.
69///
70/// Used for probabilistic models where you want the most likely
71/// path (Viterbi algorithm with probabilities in [0, 1]).
72///
73/// # Examples
74///
75/// ```
76/// use tenferro_ext_tropical::MaxMul;
77///
78/// let a = MaxMul(0.3_f64);
79/// let b = MaxMul(0.7_f64);
80///
81/// // Tropical addition = max
82/// let c = a + b;
83/// assert_eq!(c, MaxMul(0.7));
84///
85/// // Tropical multiplication = ordinary multiplication
86/// let d = a * b;
87/// assert!((d.0 - 0.21).abs() < 1e-15);
88/// ```
89#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
90#[repr(transparent)]
91pub struct MaxMul<T>(pub T);
92
93impl<T: Copy> Conjugate for MaxPlus<T> {}
94
95impl<T: Copy> Conjugate for MinPlus<T> {}
96
97impl<T: Copy> Conjugate for MaxMul<T> {}
98
99// ---------------------------------------------------------------------------
100// Display
101// ---------------------------------------------------------------------------
102
103impl<T: fmt::Display> fmt::Display for MaxPlus<T> {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 write!(f, "MaxPlus({})", self.0)
106 }
107}
108
109impl<T: fmt::Display> fmt::Display for MinPlus<T> {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "MinPlus({})", self.0)
112 }
113}
114
115impl<T: fmt::Display> fmt::Display for MaxMul<T> {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 write!(f, "MaxMul({})", self.0)
118 }
119}
120
121// ---------------------------------------------------------------------------
122// MaxPlus<T>: Add (= max), Mul (= +), Zero (= −∞), One (= 0)
123// ---------------------------------------------------------------------------
124
125impl<T: Copy + PartialOrd> ops::Add for MaxPlus<T> {
126 type Output = Self;
127
128 /// Tropical addition: max(a, b).
129 fn add(self, rhs: Self) -> Self {
130 if self.0 >= rhs.0 {
131 self
132 } else {
133 rhs
134 }
135 }
136}
137
138impl<T: Copy + ops::Add<Output = T>> ops::Mul for MaxPlus<T> {
139 type Output = Self;
140
141 /// Tropical multiplication: a + b (ordinary addition).
142 fn mul(self, rhs: Self) -> Self {
143 MaxPlus(self.0 + rhs.0)
144 }
145}
146
147impl<T: num_traits::Float> num_traits::Zero for MaxPlus<T> {
148 /// Additive identity: −∞ (negative infinity).
149 fn zero() -> Self {
150 MaxPlus(T::neg_infinity())
151 }
152
153 fn is_zero(&self) -> bool {
154 self.0 == T::neg_infinity()
155 }
156}
157
158impl<T: num_traits::Float> num_traits::One for MaxPlus<T> {
159 /// Multiplicative identity: 0.
160 fn one() -> Self {
161 MaxPlus(T::zero())
162 }
163}
164
165// ---------------------------------------------------------------------------
166// MinPlus<T>: Add (= min), Mul (= +), Zero (= +∞), One (= 0)
167// ---------------------------------------------------------------------------
168
169impl<T: Copy + PartialOrd> ops::Add for MinPlus<T> {
170 type Output = Self;
171
172 /// Tropical addition: min(a, b).
173 fn add(self, rhs: Self) -> Self {
174 if self.0 <= rhs.0 {
175 self
176 } else {
177 rhs
178 }
179 }
180}
181
182impl<T: Copy + ops::Add<Output = T>> ops::Mul for MinPlus<T> {
183 type Output = Self;
184
185 /// Tropical multiplication: a + b (ordinary addition).
186 fn mul(self, rhs: Self) -> Self {
187 MinPlus(self.0 + rhs.0)
188 }
189}
190
191impl<T: num_traits::Float> num_traits::Zero for MinPlus<T> {
192 /// Additive identity: +∞ (positive infinity).
193 fn zero() -> Self {
194 MinPlus(T::infinity())
195 }
196
197 fn is_zero(&self) -> bool {
198 self.0 == T::infinity()
199 }
200}
201
202impl<T: num_traits::Float> num_traits::One for MinPlus<T> {
203 /// Multiplicative identity: 0.
204 fn one() -> Self {
205 MinPlus(T::zero())
206 }
207}
208
209// ---------------------------------------------------------------------------
210// MaxMul<T>: Add (= max), Mul (= ×), Zero (= 0), One (= 1)
211// ---------------------------------------------------------------------------
212
213impl<T: Copy + PartialOrd> ops::Add for MaxMul<T> {
214 type Output = Self;
215
216 /// Tropical addition: max(a, b).
217 fn add(self, rhs: Self) -> Self {
218 if self.0 >= rhs.0 {
219 self
220 } else {
221 rhs
222 }
223 }
224}
225
226impl<T: Copy + ops::Mul<Output = T>> ops::Mul for MaxMul<T> {
227 type Output = Self;
228
229 /// Tropical multiplication: a × b (ordinary multiplication).
230 fn mul(self, rhs: Self) -> Self {
231 MaxMul(self.0 * rhs.0)
232 }
233}
234
235impl<T: num_traits::Float> num_traits::Zero for MaxMul<T> {
236 /// Additive identity: 0 (for max, 0 is the identity when values are non-negative).
237 fn zero() -> Self {
238 MaxMul(T::zero())
239 }
240
241 fn is_zero(&self) -> bool {
242 self.0 == T::zero()
243 }
244}
245
246impl<T: num_traits::Float> num_traits::One for MaxMul<T> {
247 /// Multiplicative identity: 1.
248 fn one() -> Self {
249 MaxMul(T::one())
250 }
251}
252
253// ---------------------------------------------------------------------------
254// Unsafe marker traits — MaxPlus/MinPlus/MaxMul are Send + Sync if T is.
255// These are automatically derived from the inner T via #[repr(transparent)].
256// ---------------------------------------------------------------------------
257
258// Send and Sync are auto-derived since the wrapper is transparent over T.