eml-stdlib 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. eml_stdlib/__init__.py +81 -0
  2. eml_stdlib/ballistics/air_density.eml +73 -0
  3. eml_stdlib/ballistics/ballistic_solver.eml +102 -0
  4. eml_stdlib/ballistics/coriolis.eml +60 -0
  5. eml_stdlib/ballistics/drag.eml +44 -0
  6. eml_stdlib/ballistics/gravity.eml +52 -0
  7. eml_stdlib/ballistics/muzzle_velocity.eml +42 -0
  8. eml_stdlib/ballistics/spin_drift.eml +60 -0
  9. eml_stdlib/ballistics/time_of_flight.eml +79 -0
  10. eml_stdlib/ballistics/wind.eml +58 -0
  11. eml_stdlib/biology/abilities/ballistic_armor.eml +61 -0
  12. eml_stdlib/biology/abilities/bite_force.eml +48 -0
  13. eml_stdlib/biology/abilities/chromatophore.eml +50 -0
  14. eml_stdlib/biology/abilities/discharge.eml +45 -0
  15. eml_stdlib/biology/abilities/elastic_energy.eml +42 -0
  16. eml_stdlib/biology/abilities/locomotion.eml +50 -0
  17. eml_stdlib/biology/abilities/muscle_power.eml +47 -0
  18. eml_stdlib/biology/abilities/sprint.eml +36 -0
  19. eml_stdlib/biology/abilities/texture_match.eml +49 -0
  20. eml_stdlib/biology/abilities/toxin.eml +44 -0
  21. eml_stdlib/biology/cone_l.eml +36 -0
  22. eml_stdlib/biology/cone_s.eml +32 -0
  23. eml_stdlib/biology/cryptochrome.eml +44 -0
  24. eml_stdlib/biology/geomagnetic_field.eml +32 -0
  25. eml_stdlib/biology/goldman_equation.eml +41 -0
  26. eml_stdlib/biology/hill_equation.eml +65 -0
  27. eml_stdlib/biology/hodgkin_huxley.eml +108 -0
  28. eml_stdlib/biology/michaelis_menten.eml +65 -0
  29. eml_stdlib/biology/plume_diffusion.eml +47 -0
  30. eml_stdlib/biology/receptor_binding.eml +35 -0
  31. eml_stdlib/biology/rod_sensitivity.eml +38 -0
  32. eml_stdlib/biology/tapetum.eml +70 -0
  33. eml_stdlib/carriers/acoustic_cloak.eml +56 -0
  34. eml_stdlib/carriers/ferron_emission.eml +55 -0
  35. eml_stdlib/carriers/ferron_logic.eml +45 -0
  36. eml_stdlib/carriers/ferron_propagation.eml +69 -0
  37. eml_stdlib/carriers/mach_zehnder.eml +52 -0
  38. eml_stdlib/carriers/magnon_dispersion.eml +57 -0
  39. eml_stdlib/carriers/magnon_logic.eml +45 -0
  40. eml_stdlib/carriers/optical_neuron.eml +55 -0
  41. eml_stdlib/carriers/phonon_bandgap.eml +54 -0
  42. eml_stdlib/carriers/ring_resonator.eml +48 -0
  43. eml_stdlib/carriers/spin_torque.eml +61 -0
  44. eml_stdlib/carriers/thermal_rectifier.eml +63 -0
  45. eml_stdlib/catalog.json +4209 -0
  46. eml_stdlib/circuits/buck_converter.eml +74 -0
  47. eml_stdlib/circuits/cmos_inverter.eml +75 -0
  48. eml_stdlib/circuits/h_bridge.eml +69 -0
  49. eml_stdlib/circuits/ldo.eml +66 -0
  50. eml_stdlib/circuits/mosfet_iv.eml +59 -0
  51. eml_stdlib/circuits/pll_loop.eml +63 -0
  52. eml_stdlib/circuits/rc_filter.eml +98 -0
  53. eml_stdlib/circuits/voltage_divider.eml +68 -0
  54. eml_stdlib/control/bangbang.eml +34 -0
  55. eml_stdlib/control/deadband.eml +28 -0
  56. eml_stdlib/control/hysteresis.eml +28 -0
  57. eml_stdlib/control/kalman_1d.eml +53 -0
  58. eml_stdlib/control/lqr_1d.eml +47 -0
  59. eml_stdlib/control/pid.eml +38 -0
  60. eml_stdlib/gaming/animation/bounce.eml +39 -0
  61. eml_stdlib/gaming/animation/ease_in.eml +27 -0
  62. eml_stdlib/gaming/animation/ease_in_out.eml +29 -0
  63. eml_stdlib/gaming/animation/ease_out.eml +27 -0
  64. eml_stdlib/gaming/animation/ik_2bone.eml +57 -0
  65. eml_stdlib/gaming/animation/spring.eml +39 -0
  66. eml_stdlib/gaming/audio/distance_attenuation.eml +50 -0
  67. eml_stdlib/gaming/audio/doppler.eml +39 -0
  68. eml_stdlib/gaming/audio/lowpass_audio.eml +32 -0
  69. eml_stdlib/gaming/audio/reverb_delay.eml +41 -0
  70. eml_stdlib/gaming/camera/dof_blur.eml +30 -0
  71. eml_stdlib/gaming/camera/fov_projection.eml +47 -0
  72. eml_stdlib/gaming/camera/orbit.eml +48 -0
  73. eml_stdlib/gaming/camera/shake.eml +20 -0
  74. eml_stdlib/gaming/lighting/ambient_occlusion.eml +40 -0
  75. eml_stdlib/gaming/lighting/area_light.eml +50 -0
  76. eml_stdlib/gaming/lighting/fog_exp.eml +28 -0
  77. eml_stdlib/gaming/lighting/fog_exp2.eml +30 -0
  78. eml_stdlib/gaming/lighting/point_light.eml +33 -0
  79. eml_stdlib/gaming/lighting/spot_light.eml +43 -0
  80. eml_stdlib/gaming/noise/fbm.eml +65 -0
  81. eml_stdlib/gaming/noise/perlin_2d.eml +65 -0
  82. eml_stdlib/gaming/noise/perlin_3d.eml +63 -0
  83. eml_stdlib/gaming/noise/simplex_2d.eml +67 -0
  84. eml_stdlib/gaming/noise/turbulence.eml +46 -0
  85. eml_stdlib/gaming/noise/value_noise.eml +45 -0
  86. eml_stdlib/gaming/noise/voronoi_2d.eml +83 -0
  87. eml_stdlib/gaming/noise/white_noise.eml +47 -0
  88. eml_stdlib/gaming/noise/worley.eml +79 -0
  89. eml_stdlib/gaming/particles/drag.eml +30 -0
  90. eml_stdlib/gaming/particles/emitter_radial.eml +35 -0
  91. eml_stdlib/gaming/particles/fade.eml +36 -0
  92. eml_stdlib/gaming/particles/gravity_particle.eml +32 -0
  93. eml_stdlib/gaming/particles/size_over_life.eml +28 -0
  94. eml_stdlib/gaming/physics/aabb_overlap.eml +38 -0
  95. eml_stdlib/gaming/physics/buoyancy.eml +43 -0
  96. eml_stdlib/gaming/physics/circle_collision.eml +46 -0
  97. eml_stdlib/gaming/physics/impulse_resolve.eml +39 -0
  98. eml_stdlib/gaming/physics/rigid_body_2d.eml +61 -0
  99. eml_stdlib/gaming/physics/spring_damper.eml +30 -0
  100. eml_stdlib/gaming/physics/verlet.eml +29 -0
  101. eml_stdlib/gaming/shading/fresnel.eml +57 -0
  102. eml_stdlib/gaming/shading/matcap.eml +34 -0
  103. eml_stdlib/gaming/shading/pbr_diffuse.eml +41 -0
  104. eml_stdlib/gaming/shading/pbr_specular.eml +75 -0
  105. eml_stdlib/gaming/shading/subsurface.eml +52 -0
  106. eml_stdlib/gaming/shading/toon.eml +30 -0
  107. eml_stdlib/gaming/terrain/biome_select.eml +41 -0
  108. eml_stdlib/gaming/terrain/cliff_detect.eml +35 -0
  109. eml_stdlib/gaming/terrain/erosion_hydraulic.eml +57 -0
  110. eml_stdlib/gaming/terrain/erosion_thermal.eml +44 -0
  111. eml_stdlib/gaming/terrain/heightmap.eml +33 -0
  112. eml_stdlib/gaming/textures/brick.eml +27 -0
  113. eml_stdlib/gaming/textures/caustics.eml +25 -0
  114. eml_stdlib/gaming/textures/checkerboard.eml +27 -0
  115. eml_stdlib/gaming/textures/fire.eml +26 -0
  116. eml_stdlib/gaming/textures/marble.eml +24 -0
  117. eml_stdlib/gaming/textures/rust.eml +23 -0
  118. eml_stdlib/gaming/textures/water_surface.eml +49 -0
  119. eml_stdlib/gaming/textures/wood.eml +41 -0
  120. eml_stdlib/math/abs.eml +45 -0
  121. eml_stdlib/math/atan.eml +32 -0
  122. eml_stdlib/math/atan2.eml +61 -0
  123. eml_stdlib/math/bessel_j0.eml +65 -0
  124. eml_stdlib/math/cos.eml +42 -0
  125. eml_stdlib/math/cosh.eml +24 -0
  126. eml_stdlib/math/erf.eml +66 -0
  127. eml_stdlib/math/exp.eml +49 -0
  128. eml_stdlib/math/gamma.eml +55 -0
  129. eml_stdlib/math/gaussian.eml +18 -0
  130. eml_stdlib/math/log.eml +59 -0
  131. eml_stdlib/math/pow.eml +51 -0
  132. eml_stdlib/math/relu.eml +63 -0
  133. eml_stdlib/math/sigmoid.eml +21 -0
  134. eml_stdlib/math/sin.eml +37 -0
  135. eml_stdlib/math/sinh.eml +24 -0
  136. eml_stdlib/math/softmax.eml +87 -0
  137. eml_stdlib/math/sqrt.eml +46 -0
  138. eml_stdlib/math/tan.eml +36 -0
  139. eml_stdlib/math/tanh.eml +54 -0
  140. eml_stdlib/ml/attention.eml +70 -0
  141. eml_stdlib/ml/cross_entropy.eml +85 -0
  142. eml_stdlib/ml/gelu.eml +51 -0
  143. eml_stdlib/ml/layernorm.eml +49 -0
  144. eml_stdlib/ml/leaky_relu.eml +51 -0
  145. eml_stdlib/ml/relu.eml +63 -0
  146. eml_stdlib/ml/rotary_embed.eml +64 -0
  147. eml_stdlib/ml/silu.eml +40 -0
  148. eml_stdlib/ml/softmax.eml +87 -0
  149. eml_stdlib/physics/coulomb.eml +48 -0
  150. eml_stdlib/physics/damped_oscillator.eml +20 -0
  151. eml_stdlib/physics/diffusion.eml +45 -0
  152. eml_stdlib/physics/gravity.eml +60 -0
  153. eml_stdlib/physics/hooke.eml +56 -0
  154. eml_stdlib/physics/navier_stokes_1d.eml +57 -0
  155. eml_stdlib/physics/planck_radiation.eml +77 -0
  156. eml_stdlib/physics/stefan_boltzmann.eml +75 -0
  157. eml_stdlib/physics/wave_propagation.eml +52 -0
  158. eml_stdlib/quantum/cnot.eml +72 -0
  159. eml_stdlib/quantum/grover_oracle.eml +69 -0
  160. eml_stdlib/quantum/hadamard.eml +73 -0
  161. eml_stdlib/quantum/pauli_x.eml +29 -0
  162. eml_stdlib/quantum/pauli_z.eml +29 -0
  163. eml_stdlib/quantum/phase_gate.eml +76 -0
  164. eml_stdlib/quantum/rotation_rz.eml +92 -0
  165. eml_stdlib/sensors/accelerometer.eml +59 -0
  166. eml_stdlib/sensors/hall_effect.eml +42 -0
  167. eml_stdlib/sensors/photodetector.eml +58 -0
  168. eml_stdlib/sensors/rtd.eml +57 -0
  169. eml_stdlib/sensors/strain_gauge.eml +63 -0
  170. eml_stdlib/sensors/thermistor.eml +71 -0
  171. eml_stdlib/signal/bandpass.eml +39 -0
  172. eml_stdlib/signal/chirp.eml +42 -0
  173. eml_stdlib/signal/dft_single.eml +30 -0
  174. eml_stdlib/signal/fft_butterfly.eml +36 -0
  175. eml_stdlib/signal/highpass.eml +45 -0
  176. eml_stdlib/signal/lowpass.eml +49 -0
  177. eml_stdlib/signal/matched_filter.eml +48 -0
  178. eml_stdlib/signal/notch_filter.eml +47 -0
  179. eml_stdlib/signal/window_hamming.eml +47 -0
  180. eml_stdlib/signal/window_hann.eml +50 -0
  181. eml_stdlib-0.4.0.dist-info/METADATA +453 -0
  182. eml_stdlib-0.4.0.dist-info/RECORD +185 -0
  183. eml_stdlib-0.4.0.dist-info/WHEEL +5 -0
  184. eml_stdlib-0.4.0.dist-info/licenses/LICENSE +21 -0
  185. eml_stdlib-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,37 @@
1
+ // sin.eml -- sine, with an explicit chain-order-1 contract.
2
+ //
3
+ // Pythagorean witness function included so downstream proofs of
4
+ // energy conservation / unitarity can inline it directly.
5
+
6
+ module sin_module;
7
+
8
+ const PI: Real = 3.141592653589793
9
+ const TWO_PI: Real = 6.283185307179586
10
+
11
+ @verify(lean, theorem = "sin_in_unit_interval")
12
+ fn sin_kernel(x: Real) -> Real
13
+ where chain_order <= 1
14
+ ensures (result >= -1.0)
15
+ ensures (result <= 1.0)
16
+ {
17
+ sin(x)
18
+ }
19
+
20
+ @verify(lean, theorem = "sin_zero_is_zero")
21
+ fn sin_at_zero() -> Real
22
+ where chain_order <= 0
23
+ ensures (result == 0.0)
24
+ {
25
+ sin(0.0)
26
+ }
27
+
28
+ // Squared sine -- chain order 1 (the chain budget folds in the
29
+ // double-angle identity). Used by Mach-Zehnder / coupler power
30
+ // ratios so it gets a name + a Lean obligation of its own.
31
+ @verify(lean, theorem = "sin_sq_nonneg")
32
+ fn sin_sq(x: Real) -> Real
33
+ where chain_order <= 1
34
+ ensures (result >= 0.0)
35
+ {
36
+ sin(x) * sin(x)
37
+ }
@@ -0,0 +1,24 @@
1
+ // sinh.eml -- hyperbolic sine.
2
+ //
3
+ // sinh(x) = (e^x - e^-x) / 2. Chain order 1 -- the two exps fold
4
+ // through one shared evaluator on most backends.
5
+
6
+ module sinh_module;
7
+
8
+ const SINH_X_MAX: Real = 30.0
9
+
10
+ @verify(lean, theorem = "sinh_zero_is_zero")
11
+ fn sinh_kernel(x: Real) -> Real
12
+ where chain_order <= 1
13
+ requires (abs(x) < SINH_X_MAX)
14
+ {
15
+ sinh(x)
16
+ }
17
+
18
+ @verify(lean, theorem = "sinh_zero_is_zero_at_zero")
19
+ fn sinh_at_zero() -> Real
20
+ where chain_order <= 0
21
+ ensures (result == 0.0)
22
+ {
23
+ sinh(0.0)
24
+ }
@@ -0,0 +1,87 @@
1
+ // softmax.eml -- numerically-stable softmax kernels.
2
+ //
3
+ // Softmax over an N-vector requires (a) subtracting the max for
4
+ // numerical stability and (b) reducing across the vector. EML's
5
+ // scalar-kernel surface naturally splits this into three pure
6
+ // gates that the host loop composes:
7
+ //
8
+ // softmax_shift_exp(logit, max_logit) per-element exp(x - m)
9
+ // softmax_normalize(numer, denom_sum) per-element / sum
10
+ // softmax_two_logit(a, b) closed form for N=2
11
+ //
12
+ // The FPGA allocator (Patent #14) maps the per-element gates to
13
+ // SIMD lanes and the reduction to a tree adder; cross-target
14
+ // equivalence (Patent #22) then guarantees the C / Rust / Lean
15
+ // reference matches the Verilog bit-for-bit at IEEE-754
16
+ // rounding.
17
+
18
+ module softmax;
19
+
20
+ const SOFTMAX_X_MAX: Real = 30.0
21
+
22
+ type SoftmaxNumerator = Real where chain_order <= 1
23
+ type SoftmaxValue = Real where chain_order <= 1
24
+
25
+ // Per-element exp(x - max) -- the numerically-stable softmax
26
+ // numerator. `max_logit` is the running maximum of the logit
27
+ // vector, supplied by the host (computed in a separate reduce).
28
+ // Subtracting it before exp() guarantees the largest numerator
29
+ // equals 1.0 and the smallest doesn't underflow.
30
+ @target(fpga, clock_mhz = 250, precision = float32)
31
+ @verify(lean, theorem = "softmax_numerator_bounded_by_one")
32
+ fn softmax_shift_exp(logit: Real, max_logit: Real) -> SoftmaxNumerator
33
+ where chain_order <= 1
34
+ requires (abs(logit) < SOFTMAX_X_MAX)
35
+ requires (abs(max_logit) < SOFTMAX_X_MAX)
36
+ requires (logit <= max_logit)
37
+ ensures (result >= 0.0)
38
+ ensures (result <= 1.0)
39
+ {
40
+ exp(logit - max_logit)
41
+ }
42
+
43
+ // Per-element normaliser. `denom_sum` is the sum of the shifted
44
+ // numerators; supplied by the host's reduction tree.
45
+ @target(fpga, clock_mhz = 400, precision = float32)
46
+ @verify(lean, theorem = "softmax_value_in_unit_interval")
47
+ fn softmax_normalize(numer: Real, denom_sum: Real) -> SoftmaxValue
48
+ where chain_order <= 0
49
+ requires (numer >= 0.0)
50
+ requires (denom_sum > 0.0)
51
+ requires (numer <= denom_sum)
52
+ ensures (result >= 0.0)
53
+ ensures (result <= 1.0)
54
+ {
55
+ numer / denom_sum
56
+ }
57
+
58
+ // Closed-form softmax for N=2 -- the binary-classifier head.
59
+ // Equivalent to sigmoid(a - b); useful as a sanity-check
60
+ // for the sigmoid-vs-softmax-for-binary debate. Chain order 1.
61
+ @target(fpga, clock_mhz = 250, precision = float32)
62
+ @verify(lean, theorem = "softmax_two_equals_sigmoid_diff")
63
+ fn softmax_two_logit(a: Real, b: Real) -> SoftmaxValue
64
+ where chain_order <= 1
65
+ requires (abs(a) < SOFTMAX_X_MAX)
66
+ requires (abs(b) < SOFTMAX_X_MAX)
67
+ ensures (result >= 0.0)
68
+ ensures (result <= 1.0)
69
+ {
70
+ 1.0 / (1.0 + exp(b - a))
71
+ }
72
+
73
+ // Log-sum-exp (LSE) -- stable computation of log(sum exp(x_i)).
74
+ // The host loop accumulates `running_lse` and `next_logit`
75
+ // pair-wise via this gate. Chain order 2 (ln composes through
76
+ // exp). Used as the cross-entropy denominator in transformer
77
+ // language-model heads.
78
+ @target(fpga, clock_mhz = 200, precision = float32)
79
+ @verify(lean, theorem = "log_sum_exp_pair_associative")
80
+ fn log_sum_exp_pair(a: Real, b: Real) -> Real
81
+ where chain_order <= 2
82
+ requires (abs(a) < SOFTMAX_X_MAX)
83
+ requires (abs(b) < SOFTMAX_X_MAX)
84
+ {
85
+ let m = clamp(a, b, SOFTMAX_X_MAX);
86
+ m + ln(exp(a - m) + exp(b - m))
87
+ }
@@ -0,0 +1,46 @@
1
+ // sqrt.eml -- square root + 2D/3D Euclidean distance.
2
+ //
3
+ // `sqrt` is a Forge builtin. This module ships sqrt with an
4
+ // explicit positivity contract plus the two most-used compositions
5
+ // (Euclidean distance) so callers can drop them into kernels
6
+ // without re-deriving the chain-order budget.
7
+
8
+ module sqrt_module;
9
+
10
+ @verify(lean, theorem = "sqrt_nonneg")
11
+ fn sqrt_kernel(x: Real) -> Real
12
+ where chain_order <= 1,
13
+ domain: x >= 0.0
14
+ ensures (result >= 0.0)
15
+ {
16
+ sqrt(x)
17
+ }
18
+
19
+ // 2D Euclidean distance.
20
+ @verify(lean, theorem = "hypot2_nonneg")
21
+ fn hypot2(x: Real, y: Real) -> Real
22
+ where chain_order <= 1
23
+ ensures (result >= 0.0)
24
+ {
25
+ sqrt(x * x + y * y)
26
+ }
27
+
28
+ // 3D Euclidean distance.
29
+ @verify(lean, theorem = "hypot3_nonneg")
30
+ fn hypot3(x: Real, y: Real, z: Real) -> Real
31
+ where chain_order <= 1
32
+ ensures (result >= 0.0)
33
+ {
34
+ sqrt(x * x + y * y + z * z)
35
+ }
36
+
37
+ // Inverse square root -- shows up in normal-vector renormalisation,
38
+ // 1/sqrt(2pi) Gaussian prefactors, and 1/r^2 force laws.
39
+ @verify(lean, theorem = "rsqrt_positive")
40
+ fn rsqrt(x: Real) -> Real
41
+ where chain_order <= 1,
42
+ domain: x > 0.0
43
+ ensures (result > 0.0)
44
+ {
45
+ 1.0 / sqrt(x)
46
+ }
@@ -0,0 +1,36 @@
1
+ // tan.eml -- tangent.
2
+ //
3
+ // Chain order 2: tan = sin / cos -- one transcendental in the
4
+ // numerator and one in the denominator. Caller is responsible for
5
+ // keeping x away from (k + 1/2) * pi where cos vanishes.
6
+
7
+ module tan_module;
8
+
9
+ const PI: Real = 3.141592653589793
10
+ const HALF_PI: Real = 1.5707963267948966
11
+ const SAFE_MARGIN: Real = 0.01
12
+
13
+ @verify(lean, theorem = "tan_zero_is_zero")
14
+ fn tan_kernel(x: Real) -> Real
15
+ where chain_order <= 2,
16
+ domain: abs(cos(x)) > SAFE_MARGIN
17
+ {
18
+ tan(x)
19
+ }
20
+
21
+ @verify(lean, theorem = "tan_at_zero_is_zero")
22
+ fn tan_at_zero() -> Real
23
+ where chain_order <= 0
24
+ ensures (result == 0.0)
25
+ {
26
+ tan(0.0)
27
+ }
28
+
29
+ // Double-tangent identity numerator: 2*tan(x) -- shows up enough
30
+ // in trig-rationalisation passes to get its own kernel.
31
+ fn two_tan(x: Real) -> Real
32
+ where chain_order <= 2,
33
+ domain: abs(cos(x)) > SAFE_MARGIN
34
+ {
35
+ 2.0 * tan(x)
36
+ }
@@ -0,0 +1,54 @@
1
+ // tanh.eml -- hyperbolic-tangent activation.
2
+ //
3
+ // Canonical bipolar squashing function. Range (-1, 1), zero
4
+ // mean for symmetric input distributions, smooth and analytic
5
+ // everywhere. Chain order 1 (single transcendental).
6
+ //
7
+ // We also expose `hard_tanh` (a piecewise-linear
8
+ // FPGA-friendly approximation) and `tanh_from_sigmoid` -- the
9
+ // 2*sigmoid(2x) - 1 identity that SuperBEST canonicalises to a
10
+ // single tanh call (saving one extra exp under the hood).
11
+
12
+ module tanh_activation;
13
+
14
+ const TANH_X_MAX: Real = 30.0
15
+
16
+ type BipolarSignal = Real where chain_order <= 1
17
+
18
+ @target(fpga, clock_mhz = 250, precision = float32)
19
+ @verify(lean, theorem = "tanh_monotone_in_x")
20
+ fn tanh_activation(x: Real) -> BipolarSignal
21
+ where chain_order <= 1
22
+ requires (abs(x) < TANH_X_MAX)
23
+ ensures (result >= -1.0)
24
+ ensures (result <= 1.0)
25
+ {
26
+ tanh(x)
27
+ }
28
+
29
+ // Sigmoid-form identity: tanh(x) = 2*sigmoid(2x) - 1. SuperBEST
30
+ // rewrites this exact pattern back to the single tanh call above,
31
+ // recovering the chain-1 cost despite the surface chain-2 form.
32
+ @target(fpga, clock_mhz = 250, precision = float32)
33
+ @verify(lean, theorem = "tanh_from_sigmoid_equals_canonical")
34
+ fn tanh_from_sigmoid(x: Real) -> BipolarSignal
35
+ where chain_order <= 2
36
+ requires (abs(x) < TANH_X_MAX)
37
+ ensures (result >= -1.0)
38
+ ensures (result <= 1.0)
39
+ {
40
+ 2.0 / (1.0 + exp(-2.0 * x)) - 1.0
41
+ }
42
+
43
+ // Hard tanh -- piecewise linear, chain order 0. Used in
44
+ // quantised inference (int8 / int16) where the saturation
45
+ // nonlinearity is a single clamp instead of a transcendental.
46
+ @target(fpga, clock_mhz = 400, precision = float32)
47
+ @verify(lean, theorem = "hard_tanh_bounded")
48
+ fn hard_tanh(x: Real) -> BipolarSignal
49
+ where chain_order <= 0
50
+ ensures (result >= -1.0)
51
+ ensures (result <= 1.0)
52
+ {
53
+ clamp(x, -1.0, 1.0)
54
+ }
@@ -0,0 +1,70 @@
1
+ // attention.eml -- scaled dot-product attention kernel.
2
+ //
3
+ // One query-key pair contributes a score:
4
+ //
5
+ // score = (q . k) / sqrt(d_k)
6
+ //
7
+ // The softmax over scores produces the attention weights; the
8
+ // output is the weighted sum of values:
9
+ //
10
+ // out = sum_j softmax(score_j) * v_j
11
+ //
12
+ // EML kernels operate on scalars; the host threads in the dot
13
+ // product, the LSE reduction, and the value sum. This module
14
+ // provides:
15
+ //
16
+ // scale_factor(d_k) -- 1 / sqrt(d_k)
17
+ // score_qk(q_dot_k, scale) -- chain order 0 multiply
18
+ // attention_step(...) -- per-(j) contribution to output
19
+ //
20
+ // Plus the mask kernels for causal / padding masking.
21
+
22
+ module attention;
23
+
24
+ @verify(lean, theorem = "attn_scale_positive")
25
+ fn scale_factor(d_k: Real) -> Real
26
+ where chain_order <= 1,
27
+ domain: d_k > 0.0
28
+ ensures (result > 0.0)
29
+ {
30
+ 1.0 / sqrt(d_k)
31
+ }
32
+
33
+ @verify(lean, theorem = "attn_score_signed")
34
+ fn score_qk(q_dot_k: Real, scale: Real) -> Real
35
+ where chain_order <= 0,
36
+ domain: scale > 0.0
37
+ {
38
+ q_dot_k * scale
39
+ }
40
+
41
+ // Per-position contribution to the output. softmax_weight is the
42
+ // pre-computed exp(score_j - lse) value from the host. v is the
43
+ // j-th value.
44
+ @verify(lean, theorem = "attn_step_signed")
45
+ fn attention_step(softmax_weight: Real, v: Real) -> Real
46
+ where chain_order <= 0
47
+ requires (softmax_weight >= 0.0)
48
+ requires (softmax_weight <= 1.0)
49
+ {
50
+ softmax_weight * v
51
+ }
52
+
53
+ // Causal mask: returns -1e30 for masked positions, 0 otherwise.
54
+ // Caller adds this to the score before softmax.
55
+ @verify(lean, theorem = "causal_mask_signed")
56
+ fn causal_mask(query_pos: Real, key_pos: Real) -> Real
57
+ where chain_order <= 0
58
+ {
59
+ let allowed = clamp((query_pos - key_pos) * 1.0e30 + 1.0, 0.0, 1.0);
60
+ (1.0 - allowed) * -1.0e30
61
+ }
62
+
63
+ // Padding mask -- positions where pad == 1 get -inf.
64
+ @verify(lean, theorem = "pad_mask_signed")
65
+ fn padding_mask(pad: Real) -> Real
66
+ where chain_order <= 0
67
+ requires (pad == 0.0 || pad == 1.0)
68
+ {
69
+ pad * -1.0e30
70
+ }
@@ -0,0 +1,85 @@
1
+ // cross_entropy.eml -- cross-entropy loss kernels.
2
+ //
3
+ // The standard classifier loss. For binary targets:
4
+ //
5
+ // bce(p, y) = -(y * ln(p) + (1-y) * ln(1-p))
6
+ //
7
+ // The numerically-stable form is to compute the loss in
8
+ // logit-space (the input to the final sigmoid) instead of
9
+ // probability-space, since ln(sigmoid(z)) collapses to
10
+ // -softplus(-z) which avoids the catastrophic-cancellation
11
+ // region around p ≈ 0 or p ≈ 1.
12
+ //
13
+ // Both forms ship here; SuperBEST's cross_entropy rewrite rule
14
+ // (E-193 calibration) detects the prob-space form and rewrites
15
+ // it to the logit-space form, saving ~2.4 decimal digits at the
16
+ // saturated tails.
17
+
18
+ module cross_entropy;
19
+
20
+ const MAX_LOGIT: Real = 30.0
21
+ const EPS: Real = 1.0e-7
22
+
23
+ type LossSignal = Real where chain_order <= 2
24
+
25
+ // Probability-space binary cross-entropy. Hand-written form;
26
+ // SuperBEST rewrites to the logit-space form below before any
27
+ // backend emits.
28
+ @target(fpga, clock_mhz = 200, precision = float32)
29
+ @verify(lean, theorem = "bce_loss_nonnegative")
30
+ fn bce_loss(prediction: Real, target: Real) -> LossSignal
31
+ where chain_order <= 2,
32
+ domain: 0.0 < prediction && prediction < 1.0
33
+ requires (target >= 0.0)
34
+ requires (target <= 1.0)
35
+ ensures (result >= 0.0)
36
+ {
37
+ -(target * ln(prediction) + (1.0 - target) * ln(1.0 - prediction))
38
+ }
39
+
40
+ // Logit-space (numerically stable) form. Equivalent to
41
+ // max(z, 0) - z * y + ln(1 + exp(-|z|))
42
+ // the form torch.nn.BCEWithLogitsLoss compiles to. Chain order 2
43
+ // (ln composes through exp).
44
+ @target(fpga, clock_mhz = 200, precision = float32)
45
+ @verify(lean, theorem = "bce_with_logits_equals_bce")
46
+ fn bce_with_logits(logit: Real, target: Real) -> LossSignal
47
+ where chain_order <= 2
48
+ requires (abs(logit) < MAX_LOGIT)
49
+ requires (target >= 0.0)
50
+ requires (target <= 1.0)
51
+ ensures (result >= 0.0)
52
+ {
53
+ let pos = clamp(logit, 0.0, MAX_LOGIT);
54
+ pos - logit * target + ln(1.0 + exp(-abs(logit)))
55
+ }
56
+
57
+ // Categorical cross-entropy contribution from one (logit, target)
58
+ // pair, given the host has already supplied log_sum_exp(logits)
59
+ // from the LSE reduction. Chain order 1 (single ln baked into
60
+ // log_sum_exp; this gate is just subtract-and-multiply).
61
+ @target(fpga, clock_mhz = 400, precision = float32)
62
+ @verify(lean, theorem = "categorical_ce_pair_nonnegative")
63
+ fn categorical_ce_pair(logit: Real, log_sum_exp_val: Real, target_one_hot: Real) -> LossSignal
64
+ where chain_order <= 0
65
+ requires (abs(logit) < MAX_LOGIT)
66
+ requires (target_one_hot >= 0.0)
67
+ requires (target_one_hot <= 1.0)
68
+ {
69
+ target_one_hot * (log_sum_exp_val - logit)
70
+ }
71
+
72
+ // Gradient of BCE-with-logits w.r.t. the logit. Equals
73
+ // sigmoid(z) - y
74
+ // SuperBEST recognises -ln(sigmoid(-z))-shaped patterns in the
75
+ // surrounding code and hoists this gradient when the forward
76
+ // pass and backward pass share the same logit.
77
+ @target(fpga, clock_mhz = 250, precision = float32)
78
+ fn bce_with_logits_grad(logit: Real, target: Real) -> LossSignal
79
+ where chain_order <= 1
80
+ requires (abs(logit) < MAX_LOGIT)
81
+ requires (target >= 0.0)
82
+ requires (target <= 1.0)
83
+ {
84
+ 1.0 / (1.0 + exp(-logit)) - target
85
+ }
eml_stdlib/ml/gelu.eml ADDED
@@ -0,0 +1,51 @@
1
+ // gelu.eml -- Gaussian Error Linear Unit, the activation BERT
2
+ // and GPT use.
3
+ //
4
+ // The exact GELU is
5
+ //
6
+ // gelu(x) = x * Phi(x)
7
+ //
8
+ // where Phi is the standard normal CDF. Phi has no closed-form
9
+ // elementary expression, so production code uses one of two
10
+ // approximations:
11
+ //
12
+ // gelu_tanh chain 1: 0.5*x*(1 + tanh(sqrt(2/pi) * (x + 0.044715*x^3)))
13
+ // gelu_erf chain 1: 0.5*x*(1 + erf(x / sqrt(2)))
14
+ //
15
+ // erf is a Forge builtin in the f64 path; for f32 the tanh form
16
+ // is the canonical FPGA-friendly choice (one transcendental
17
+ // instead of one + special function).
18
+ //
19
+ // Numeric range: bounded |x| < 30 keeps the cubic term inside
20
+ // float32 dynamic range and lets the Lean monotonicity proof
21
+ // avoid +/-inf edge cases.
22
+
23
+ module gelu;
24
+
25
+ const GELU_X_MAX: Real = 30.0
26
+ const SQRT_2_OVER_PI: Real = 0.7978845608028654
27
+ const GELU_CUBIC: Real = 0.044715
28
+ const HALF: Real = 0.5
29
+
30
+ type GeluSignal = Real where chain_order <= 1
31
+
32
+ @target(fpga, clock_mhz = 200, precision = float32)
33
+ @verify(lean, theorem = "gelu_tanh_monotone_in_x")
34
+ fn gelu_tanh(x: Real) -> GeluSignal
35
+ where chain_order <= 1
36
+ requires (abs(x) < GELU_X_MAX)
37
+ {
38
+ HALF * x * (1.0 + tanh(SQRT_2_OVER_PI * (x + GELU_CUBIC * x * x * x)))
39
+ }
40
+
41
+ // QuickGELU -- the OpenAI CLIP variant: x * sigmoid(1.702 * x).
42
+ // Cheaper than gelu_tanh (one exp instead of one tanh + cubic);
43
+ // matches gelu_tanh to ~3 decimal digits on N(0,1) inputs.
44
+ @target(fpga, clock_mhz = 250, precision = float32)
45
+ @verify(lean, theorem = "quick_gelu_monotone_in_x")
46
+ fn quick_gelu(x: Real) -> GeluSignal
47
+ where chain_order <= 1
48
+ requires (abs(x) < GELU_X_MAX)
49
+ {
50
+ x / (1.0 + exp(-1.702 * x))
51
+ }
@@ -0,0 +1,49 @@
1
+ // layernorm.eml -- per-feature layer normalization kernel.
2
+ //
3
+ // Given a feature vector x, the LayerNorm transform is:
4
+ //
5
+ // mu = mean(x)
6
+ // var = mean((x - mu)^2)
7
+ // x_norm = (x - mu) / sqrt(var + eps)
8
+ // y = gamma * x_norm + beta
9
+ //
10
+ // EML kernels operate per-element -- the host program threads the
11
+ // per-vector statistics in. Chain order 1 (single sqrt). The
12
+ // closed proofs cover unit-variance + zero-mean of `x_norm` in the
13
+ // no-noise limit.
14
+
15
+ module layernorm;
16
+
17
+ const DEFAULT_EPS: Real = 1.0e-5
18
+
19
+ // Normalisation step for a single element.
20
+ @verify(lean, theorem = "layernorm_unit_variance_in_limit")
21
+ fn layernorm_step(x: Real, mu: Real, var: Real, eps: Real,
22
+ gamma: Real, beta: Real) -> Real
23
+ where chain_order <= 1,
24
+ domain: var >= 0.0 && eps > 0.0
25
+ {
26
+ let x_norm = (x - mu) / sqrt(var + eps);
27
+ gamma * x_norm + beta
28
+ }
29
+
30
+ // Default-eps variant.
31
+ @verify(lean, theorem = "layernorm_default_eps_unit_variance_in_limit")
32
+ fn layernorm_default(x: Real, mu: Real, var: Real,
33
+ gamma: Real, beta: Real) -> Real
34
+ where chain_order <= 1,
35
+ domain: var >= 0.0
36
+ {
37
+ let x_norm = (x - mu) / sqrt(var + DEFAULT_EPS);
38
+ gamma * x_norm + beta
39
+ }
40
+
41
+ // RMSNorm variant -- skips the mean subtraction (used in LLaMA).
42
+ // y = x / sqrt(mean(x^2) + eps) * gamma
43
+ @verify(lean, theorem = "rmsnorm_signed_in_input_band")
44
+ fn rmsnorm_step(x: Real, mean_sq: Real, eps: Real, gamma: Real) -> Real
45
+ where chain_order <= 1,
46
+ domain: mean_sq >= 0.0 && eps > 0.0
47
+ {
48
+ gamma * x / sqrt(mean_sq + eps)
49
+ }
@@ -0,0 +1,51 @@
1
+ // leaky_relu.eml -- Leaky ReLU activation.
2
+ //
3
+ // leaky_relu(x) = x if x >= 0 else alpha * x
4
+ //
5
+ // Standard alpha = 0.01 prevents the dying-ReLU problem at the
6
+ // cost of a small negative-tail contribution to the gradient.
7
+ // Chain order 0; branch-free via clamp + linear blend.
8
+
9
+ module leaky_relu;
10
+
11
+ const DEFAULT_ALPHA: Real = 0.01
12
+
13
+ @verify(lean, theorem = "leaky_relu_at_zero_is_zero")
14
+ fn leaky_relu(x: Real, alpha: Real) -> Real
15
+ where chain_order <= 0
16
+ requires (alpha >= 0.0)
17
+ {
18
+ let pos = clamp(x, 0.0, 1.0e30);
19
+ let neg = clamp(x, -1.0e30, 0.0);
20
+ pos + alpha * neg
21
+ }
22
+
23
+ @verify(lean, theorem = "default_leaky_relu_at_zero_is_zero")
24
+ fn leaky_relu_default(x: Real) -> Real
25
+ where chain_order <= 0
26
+ {
27
+ let pos = clamp(x, 0.0, 1.0e30);
28
+ let neg = clamp(x, -1.0e30, 0.0);
29
+ pos + DEFAULT_ALPHA * neg
30
+ }
31
+
32
+ // PReLU: alpha is a learnable parameter (per channel).
33
+ @verify(lean, theorem = "prelu_at_zero_is_zero")
34
+ fn prelu(x: Real, alpha: Real) -> Real
35
+ where chain_order <= 0
36
+ {
37
+ let pos = clamp(x, 0.0, 1.0e30);
38
+ let neg = clamp(x, -1.0e30, 0.0);
39
+ pos + alpha * neg
40
+ }
41
+
42
+ // Gradient of leaky_relu: 1 for x >= 0, alpha for x < 0.
43
+ @verify(lean, theorem = "leaky_relu_grad_in_alpha_band")
44
+ fn leaky_relu_grad(x: Real, alpha: Real) -> Real
45
+ where chain_order <= 0
46
+ requires (alpha >= 0.0)
47
+ ensures (result >= 0.0)
48
+ {
49
+ let s = clamp(x * 1.0e30, 0.0, 1.0);
50
+ s + alpha * (1.0 - s)
51
+ }
eml_stdlib/ml/relu.eml ADDED
@@ -0,0 +1,63 @@
1
+ // relu.eml -- rectified linear unit family.
2
+ //
3
+ // All four entries in this module are chain order 0 -- ReLU is
4
+ // branch-free in EML because we lower max(0, x) to clamp(x, 0, +inf)
5
+ // which a backend can either emit as a CMOV / select or as a
6
+ // single MAX instruction. No transcendental.
7
+ //
8
+ // On FPGA this is the cheapest activation per layer and the
9
+ // reason quantised inference cores (Patent #14 allocator) bias
10
+ // toward ReLU + variants over sigmoid / tanh.
11
+
12
+ module relu;
13
+
14
+ const RELU_UPPER: Real = 1.0e30
15
+ const ALPHA_LEAKY: Real = 0.01
16
+
17
+ type RectifiedSignal = Real where chain_order <= 0
18
+
19
+ @target(fpga, clock_mhz = 500, precision = float32)
20
+ @verify(lean, theorem = "relu_nonnegative")
21
+ fn relu(x: Real) -> RectifiedSignal
22
+ where chain_order <= 0
23
+ ensures (result >= 0.0)
24
+ ensures (result >= x)
25
+ {
26
+ clamp(x, 0.0, RELU_UPPER)
27
+ }
28
+
29
+ // Leaky ReLU -- non-zero gradient for negative inputs, helps
30
+ // dead-neuron problem. `alpha` is typically 0.01.
31
+ @target(fpga, clock_mhz = 500, precision = float32)
32
+ @verify(lean, theorem = "leaky_relu_monotone_in_x")
33
+ fn leaky_relu(x: Real, alpha: Real) -> RectifiedSignal
34
+ where chain_order <= 0
35
+ requires (alpha >= 0.0)
36
+ requires (alpha <= 1.0)
37
+ {
38
+ clamp(x, alpha * x, RELU_UPPER)
39
+ }
40
+
41
+ // ReLU6 (mobile inference favourite). Clamped to [0, 6]
42
+ // so int8 quantisation has a sane fixed range.
43
+ @target(fpga, clock_mhz = 500, precision = float32)
44
+ @verify(lean, theorem = "relu6_bounded")
45
+ fn relu6(x: Real) -> RectifiedSignal
46
+ where chain_order <= 0
47
+ ensures (result >= 0.0)
48
+ ensures (result <= 6.0)
49
+ {
50
+ clamp(x, 0.0, 6.0)
51
+ }
52
+
53
+ // Parametric ReLU with a fixed-but-loaded slope -- here `alpha`
54
+ // is the per-channel learned slope shipped as a kernel argument
55
+ // (the surface form a quantised PReLU layer takes).
56
+ @target(fpga, clock_mhz = 500, precision = float32)
57
+ fn prelu(x: Real, alpha: Real) -> RectifiedSignal
58
+ where chain_order <= 0
59
+ requires (alpha >= 0.0)
60
+ requires (alpha <= 2.0)
61
+ {
62
+ clamp(x, alpha * x, RELU_UPPER)
63
+ }