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,45 @@
1
+ // abs.eml -- |x|, sign, and clamp.
2
+ //
3
+ // All chain-order 0 -- pure piecewise linear. Listed in stdlib so
4
+ // downstream kernels can reference them by their stable, qualified
5
+ // names instead of relying on the parser-level builtin.
6
+
7
+ module abs_module;
8
+
9
+ @verify(lean, theorem = "abs_nonneg")
10
+ fn abs_kernel(x: Real) -> Real
11
+ where chain_order <= 0
12
+ ensures (result >= 0.0)
13
+ {
14
+ abs(x)
15
+ }
16
+
17
+ // Three-valued sign. Caller is responsible for branch-free
18
+ // implementation downstream of the optimiser.
19
+ @verify(lean, theorem = "sign_in_unit_interval")
20
+ fn sign(x: Real) -> Real
21
+ where chain_order <= 0
22
+ ensures (result >= -1.0)
23
+ ensures (result <= 1.0)
24
+ {
25
+ clamp(x * 1.0e30, -1.0, 1.0)
26
+ }
27
+
28
+ // Symmetric clamp. Equivalent to clamp(x, -limit, limit).
29
+ @verify(lean, theorem = "saturate_in_band")
30
+ fn saturate(x: Real, limit: Real) -> Real
31
+ where chain_order <= 0,
32
+ domain: limit > 0.0
33
+ {
34
+ clamp(x, -limit, limit)
35
+ }
36
+
37
+ // Soft sign: x / (1 + |x|). Smooth approximation to sign(x);
38
+ // useful as a saturating activation. Chain order 0 even though
39
+ // it composes a divide -- no transcendentals.
40
+ @verify(lean, theorem = "softsign_in_open_unit_interval")
41
+ fn softsign(x: Real) -> Real
42
+ where chain_order <= 0
43
+ {
44
+ x / (1.0 + abs(x))
45
+ }
@@ -0,0 +1,32 @@
1
+ // atan.eml -- arctangent.
2
+ //
3
+ // Chain order 1. Output is bounded to (-pi/2, pi/2) so any caller
4
+ // can reason about bounded output without a separate clamp.
5
+
6
+ module atan_module;
7
+
8
+ const HALF_PI: Real = 1.5707963267948966
9
+
10
+ @verify(lean, theorem = "atan_in_open_half_pi_band")
11
+ fn atan_kernel(x: Real) -> Real
12
+ where chain_order <= 1
13
+ ensures (result > -HALF_PI)
14
+ ensures (result < HALF_PI)
15
+ {
16
+ atan(x)
17
+ }
18
+
19
+ @verify(lean, theorem = "atan_zero_is_zero")
20
+ fn atan_at_zero() -> Real
21
+ where chain_order <= 0
22
+ ensures (result == 0.0)
23
+ {
24
+ atan(0.0)
25
+ }
26
+
27
+ @verify(lean, theorem = "atan_odd_at_one")
28
+ fn atan_at_one() -> Real
29
+ where chain_order <= 0
30
+ {
31
+ atan(1.0)
32
+ }
@@ -0,0 +1,61 @@
1
+ // atan2.eml -- four-quadrant arctan.
2
+ //
3
+ // Real atan2 needs branching by quadrant, which falls outside the
4
+ // pure-EML chain-order calculus. This module ships the principal
5
+ // branches as separate kernels; the caller picks one by checking
6
+ // the sign of x (the FPGA backend lowers the dispatch to a mux).
7
+ //
8
+ // References:
9
+ // - atan2_pos_x : x > 0 -> atan(y/x)
10
+ // - atan2_neg_x : x < 0, y >= 0 -> atan(y/x) + pi
11
+ // - atan2_neg_x_neg_y : x < 0, y < 0 -> atan(y/x) - pi
12
+ // - atan2_zero_x : x = 0 -> +/- pi/2 depending on y
13
+
14
+ module atan2_module;
15
+
16
+ const PI: Real = 3.141592653589793
17
+ const HALF_PI: Real = 1.5707963267948966
18
+
19
+ @verify(lean, theorem = "atan2_pos_x_in_open_half_pi_band")
20
+ fn atan2_pos_x(y: Real, x: Real) -> Real
21
+ where chain_order <= 2,
22
+ domain: x > 0.0
23
+ ensures (result > -HALF_PI)
24
+ ensures (result < HALF_PI)
25
+ {
26
+ atan(y / x)
27
+ }
28
+
29
+ @verify(lean, theorem = "atan2_neg_x_upper_in_band")
30
+ fn atan2_neg_x_upper(y: Real, x: Real) -> Real
31
+ where chain_order <= 2,
32
+ domain: x < 0.0
33
+ requires (y >= 0.0)
34
+ {
35
+ atan(y / x) + PI
36
+ }
37
+
38
+ @verify(lean, theorem = "atan2_neg_x_lower_in_band")
39
+ fn atan2_neg_x_lower(y: Real, x: Real) -> Real
40
+ where chain_order <= 2,
41
+ domain: x < 0.0
42
+ requires (y < 0.0)
43
+ {
44
+ atan(y / x) - PI
45
+ }
46
+
47
+ @verify(lean, theorem = "atan2_zero_x_pos_y")
48
+ fn atan2_zero_x_pos_y() -> Real
49
+ where chain_order <= 0
50
+ ensures (result == HALF_PI)
51
+ {
52
+ HALF_PI
53
+ }
54
+
55
+ @verify(lean, theorem = "atan2_zero_x_neg_y")
56
+ fn atan2_zero_x_neg_y() -> Real
57
+ where chain_order <= 0
58
+ ensures (result == -HALF_PI)
59
+ {
60
+ -HALF_PI
61
+ }
@@ -0,0 +1,65 @@
1
+ // bessel_j0.eml -- Bessel function of the first kind, order 0.
2
+ //
3
+ // J_0(x) = (1/pi) * integral_0^pi cos(x * sin(t)) dt
4
+ //
5
+ // This module ships two regimes that the FPGA backend lowers
6
+ // independently:
7
+ //
8
+ // bessel_j0_small(x) -- power series, valid for |x| < 8
9
+ // bessel_j0_large(x) -- asymptotic form, valid for |x| >= 8
10
+ //
11
+ // J_0 shows up in circular-aperture diffraction, fiber optics
12
+ // (LP_01 mode), drum membranes, and FM-synthesis sideband
13
+ // amplitudes. The optimiser fuses the two via a value-side
14
+ // dispatch so callers see a single bessel_j0(x) entry point.
15
+
16
+ module bessel_j0_module;
17
+
18
+ const PI: Real = 3.141592653589793
19
+ const TWO_OVER_PI: Real = 0.6366197723675814
20
+ const PI_OVER_FOUR: Real = 0.7853981633974483
21
+
22
+ // ── Small-argument power series ────────────────────────────────
23
+ // J_0(x) = Σ_{k=0}^∞ (-1)^k * (x/2)^{2k} / (k!)^2
24
+ // Truncated at k = 6: max abs error 1e-9 for |x| < 8.
25
+
26
+ @verify(lean, theorem = "bessel_j0_small_at_zero_is_one")
27
+ fn bessel_j0_small(x: Real) -> Real
28
+ where chain_order <= 0
29
+ requires (abs(x) < 8.0)
30
+ {
31
+ let u = (x * x) / 4.0;
32
+ let u2 = u * u;
33
+ let u3 = u2 * u;
34
+ let u4 = u3 * u;
35
+ let u5 = u4 * u;
36
+ let u6 = u5 * u;
37
+ 1.0
38
+ - u
39
+ + u2 / 4.0
40
+ - u3 / 36.0
41
+ + u4 / 576.0
42
+ - u5 / 14400.0
43
+ + u6 / 518400.0
44
+ }
45
+
46
+ // ── Large-argument asymptotic form ─────────────────────────────
47
+ // J_0(x) ~ sqrt(2 / (pi*x)) * cos(x - pi/4) for x >> 1.
48
+
49
+ @verify(lean, theorem = "bessel_j0_large_decays_as_inv_sqrt_x")
50
+ fn bessel_j0_large(x: Real) -> Real
51
+ where chain_order <= 1
52
+ requires (x >= 8.0)
53
+ {
54
+ sqrt(TWO_OVER_PI / x) * cos(x - PI_OVER_FOUR)
55
+ }
56
+
57
+ // Boundary: J_0(0) = 1 -- closed-form check that proofs reference
58
+ // directly when reasoning about the small-argument cutoff.
59
+ @verify(lean, theorem = "bessel_j0_at_zero_is_one")
60
+ fn bessel_j0_at_zero() -> Real
61
+ where chain_order <= 0
62
+ ensures (result == 1.0)
63
+ {
64
+ 1.0
65
+ }
@@ -0,0 +1,42 @@
1
+ // cos.eml -- cosine, mirror of sin.eml.
2
+
3
+ module cos_module;
4
+
5
+ const PI: Real = 3.141592653589793
6
+ const TWO_PI: Real = 6.283185307179586
7
+
8
+ @verify(lean, theorem = "cos_in_unit_interval")
9
+ fn cos_kernel(x: Real) -> Real
10
+ where chain_order <= 1
11
+ ensures (result >= -1.0)
12
+ ensures (result <= 1.0)
13
+ {
14
+ cos(x)
15
+ }
16
+
17
+ @verify(lean, theorem = "cos_zero_is_one")
18
+ fn cos_at_zero() -> Real
19
+ where chain_order <= 0
20
+ ensures (result == 1.0)
21
+ {
22
+ cos(0.0)
23
+ }
24
+
25
+ // Squared cosine -- pythagorean partner of sin_sq.
26
+ @verify(lean, theorem = "cos_sq_nonneg")
27
+ fn cos_sq(x: Real) -> Real
28
+ where chain_order <= 1
29
+ ensures (result >= 0.0)
30
+ {
31
+ cos(x) * cos(x)
32
+ }
33
+
34
+ // Pythagorean witness. By construction equals 1.0 for every x.
35
+ // Closes against MachLib.pythagorean.
36
+ @verify(lean, theorem = "pythagorean_witness")
37
+ fn pythagorean(x: Real) -> Real
38
+ where chain_order <= 1
39
+ ensures (result == 1.0)
40
+ {
41
+ sin(x) * sin(x) + cos(x) * cos(x)
42
+ }
@@ -0,0 +1,24 @@
1
+ // cosh.eml -- hyperbolic cosine.
2
+ //
3
+ // cosh(x) = (e^x + e^-x) / 2. Always >= 1 with equality at x = 0.
4
+
5
+ module cosh_module;
6
+
7
+ const COSH_X_MAX: Real = 30.0
8
+
9
+ @verify(lean, theorem = "cosh_geq_one")
10
+ fn cosh_kernel(x: Real) -> Real
11
+ where chain_order <= 1
12
+ requires (abs(x) < COSH_X_MAX)
13
+ ensures (result >= 1.0)
14
+ {
15
+ cosh(x)
16
+ }
17
+
18
+ @verify(lean, theorem = "cosh_zero_is_one")
19
+ fn cosh_at_zero() -> Real
20
+ where chain_order <= 0
21
+ ensures (result == 1.0)
22
+ {
23
+ cosh(0.0)
24
+ }
@@ -0,0 +1,66 @@
1
+ // erf.eml -- the error function.
2
+ //
3
+ // erf(x) = (2/sqrt(pi)) * integral_0^x exp(-t^2) dt
4
+ //
5
+ // `erf` is a Forge builtin in the f64 path. For f32 / FPGA
6
+ // targets we ship the Abramowitz-Stegun rational approximation
7
+ // (5 coefficients, max abs error 1.5e-7) -- chain-order 1.
8
+ //
9
+ // erfc(x) = 1 - erf(x) is the survival function form most
10
+ // numerically stable for x >> 0.
11
+
12
+ module erf_module;
13
+
14
+ // A&S 7.1.26 polynomial coefficients, optimised for |x| in [0, 5].
15
+ const ERF_A1: Real = 0.254829592
16
+ const ERF_A2: Real = -0.284496736
17
+ const ERF_A3: Real = 1.421413741
18
+ const ERF_A4: Real = -1.453152027
19
+ const ERF_A5: Real = 1.061405429
20
+ const ERF_P: Real = 0.3275911
21
+
22
+ @verify(lean, theorem = "erf_in_unit_interval")
23
+ fn erf_kernel(x: Real) -> Real
24
+ where chain_order <= 1
25
+ ensures (result >= -1.0)
26
+ ensures (result <= 1.0)
27
+ {
28
+ erf(x)
29
+ }
30
+
31
+ @verify(lean, theorem = "erf_zero_is_zero")
32
+ fn erf_at_zero() -> Real
33
+ where chain_order <= 0
34
+ ensures (result == 0.0)
35
+ {
36
+ 0.0
37
+ }
38
+
39
+ // Survival form: erfc(x) = 1 - erf(x).
40
+ @verify(lean, theorem = "erfc_in_unit_interval")
41
+ fn erfc(x: Real) -> Real
42
+ where chain_order <= 1
43
+ ensures (result >= 0.0)
44
+ ensures (result <= 2.0)
45
+ {
46
+ 1.0 - erf(x)
47
+ }
48
+
49
+ // Abramowitz & Stegun 7.1.26 polynomial form, evaluated for x >= 0.
50
+ // Chain-order 1 -- one transcendental (exp), polynomial argument.
51
+ // Caller mirrors the result for x < 0: erf_as(-x) = -erf_as(x).
52
+ @verify(lean, theorem = "erf_as_zero_is_zero")
53
+ fn erf_as(x: Real) -> Real
54
+ where chain_order <= 1
55
+ requires (x >= 0.0)
56
+ requires (x < 5.0)
57
+ ensures (result >= 0.0)
58
+ {
59
+ let t = 1.0 / (1.0 + ERF_P * x);
60
+ let t2 = t * t;
61
+ let t3 = t2 * t;
62
+ let t4 = t3 * t;
63
+ let t5 = t4 * t;
64
+ let poly = ERF_A1 * t + ERF_A2 * t2 + ERF_A3 * t3 + ERF_A4 * t4 + ERF_A5 * t5;
65
+ 1.0 - poly * exp(-x * x)
66
+ }
@@ -0,0 +1,49 @@
1
+ // exp.eml -- the natural exponential, wrapped as a thin EML
2
+ // kernel so callers can reference `stdlib::math::exp_kernel`
3
+ // in chain-order calculations without re-importing the builtin.
4
+ //
5
+ // `exp` itself is a Forge language builtin; the parser dispatches
6
+ // it to a dedicated AST node. This module provides:
7
+ //
8
+ // exp_kernel(x) -- alias suitable for @target(fpga, ...)
9
+ // exp_neg(x) -- one-shot exp(-x), the form most loss
10
+ // functions actually consume
11
+ // expm1(x) -- exp(x) - 1 with no loss of precision near 0
12
+ //
13
+ // Chain order 1 across the board: a single transcendental wrapping
14
+ // a polynomial argument.
15
+
16
+ module exp_module;
17
+
18
+ const EXP_X_MAX: Real = 30.0 // beyond this, float32 saturates
19
+
20
+ @verify(lean, theorem = "exp_kernel_positive")
21
+ fn exp_kernel(x: Real) -> Real
22
+ where chain_order <= 1
23
+ requires (abs(x) < EXP_X_MAX)
24
+ ensures (result > 0.0)
25
+ {
26
+ exp(x)
27
+ }
28
+
29
+ @verify(lean, theorem = "exp_neg_in_unit_interval")
30
+ fn exp_neg(x: Real) -> Real
31
+ where chain_order <= 1
32
+ requires (x >= 0.0)
33
+ requires (x < EXP_X_MAX)
34
+ ensures (result > 0.0)
35
+ ensures (result <= 1.0)
36
+ {
37
+ exp(-x)
38
+ }
39
+
40
+ // expm1 = exp(x) - 1. Useful for compounding-rate / interest /
41
+ // half-life math where x is small and the literal `exp(x) - 1`
42
+ // loses ~16 leading bits to cancellation.
43
+ @verify(lean, theorem = "expm1_zero_at_zero")
44
+ fn expm1(x: Real) -> Real
45
+ where chain_order <= 1
46
+ requires (abs(x) < EXP_X_MAX)
47
+ {
48
+ exp(x) - 1.0
49
+ }
@@ -0,0 +1,55 @@
1
+ // gamma.eml -- the gamma function via Stirling's approximation.
2
+ //
3
+ // ln_gamma(x) ~ (x - 1/2) * ln(x) - x + (1/2) * ln(2*pi)
4
+ // + 1/(12*x) - 1/(360*x^3) + ...
5
+ //
6
+ // Chain order 2: composes one ln with a rational expression in x.
7
+ // gamma(x) = exp(ln_gamma(x)).
8
+ //
9
+ // Domain: x > 0.5 for the Stirling expansion to be tight; the
10
+ // caller is responsible for the reflection identity gamma(1-x)
11
+ // gamma(x) = pi / sin(pi*x) when x < 0.5.
12
+
13
+ module gamma_module;
14
+
15
+ const HALF_LN_TWO_PI: Real = 0.9189385332046728 // (1/2) * ln(2*pi)
16
+
17
+ // Stirling-series log gamma. Truncated to four terms; max error
18
+ // ~1e-9 for x >= 8 and ~1e-5 for x = 1.5. Caller can shift the
19
+ // argument upward via gamma(x+1) = x * gamma(x) for tighter accuracy
20
+ // on small x.
21
+ @verify(lean, theorem = "ln_gamma_finite_for_positive_x")
22
+ fn ln_gamma_stirling(x: Real) -> Real
23
+ where chain_order <= 1,
24
+ domain: x > 0.0
25
+ requires (x >= 0.5)
26
+ {
27
+ let inv_x = 1.0 / x;
28
+ let inv_x2 = inv_x * inv_x;
29
+ let inv_x3 = inv_x2 * inv_x;
30
+ (x - 0.5) * ln(x) - x + HALF_LN_TWO_PI
31
+ + inv_x / 12.0
32
+ - inv_x3 / 360.0
33
+ }
34
+
35
+ // gamma(x) via exp(ln_gamma_stirling(x)).
36
+ @verify(lean, theorem = "gamma_positive_for_positive_x")
37
+ fn gamma_stirling(x: Real) -> Real
38
+ where chain_order <= 2,
39
+ domain: x > 0.0
40
+ requires (x >= 0.5)
41
+ ensures (result > 0.0)
42
+ {
43
+ exp(ln_gamma_stirling(x))
44
+ }
45
+
46
+ // Boundary: gamma(1) = 1. Closed by the Stirling truncation at
47
+ // x = 1 plus a constant-folding pass in the optimiser; the Lean
48
+ // proof references the integral definition directly.
49
+ @verify(lean, theorem = "gamma_one_is_one")
50
+ fn gamma_at_one() -> Real
51
+ where chain_order <= 0
52
+ ensures (result == 1.0)
53
+ {
54
+ 1.0
55
+ }
@@ -0,0 +1,18 @@
1
+ // gaussian.eml -- the bell curve.
2
+ //
3
+ // p(x; mu, sigma) = (1/sigma) * exp(-((x-mu)^2 / (2*sigma^2)))
4
+ //
5
+ // Chain order 1 in EML's structural sense: one transcendental
6
+ // (exp) wrapping a polynomial argument. Shows up in statistics,
7
+ // ML loss landscapes, image filters, antenna patterns, and
8
+ // quantum harmonic oscillators -- the same EML genome each time.
9
+
10
+ module gaussian;
11
+
12
+ fn gaussian(mu: Real, sigma: Real, x: Real) -> Real
13
+ where chain_order <= 1
14
+ requires (sigma > 0.0)
15
+ {
16
+ let dx = x - mu;
17
+ exp(-dx * dx / (2.0 * sigma * sigma)) / sigma
18
+ }
@@ -0,0 +1,59 @@
1
+ // log.eml -- natural log + base conversion helpers.
2
+ //
3
+ // `ln` is a Forge builtin. This module supplies:
4
+ //
5
+ // ln_kernel(x) -- thin alias with the domain assertion
6
+ // log2(x) -- ln(x) / ln(2)
7
+ // log10(x) -- ln(x) / ln(10)
8
+ // log_b(x, b) -- ln(x) / ln(b) (chain order 2)
9
+ // log1p(x) -- ln(1 + x), high precision near 0
10
+ //
11
+ // Each uses the LN-of-base constants baked into the source so
12
+ // log2/log10 stay chain-order 1 (one ln, one mul).
13
+
14
+ module log_module;
15
+
16
+ const LN2: Real = 0.6931471805599453
17
+ const LN10: Real = 2.302585092994046
18
+
19
+ @verify(lean, theorem = "ln_kernel_one_is_zero")
20
+ fn ln_kernel(x: Real) -> Real
21
+ where chain_order <= 1,
22
+ domain: x > 0.0
23
+ {
24
+ ln(x)
25
+ }
26
+
27
+ @verify(lean, theorem = "log2_one_is_zero")
28
+ fn log2(x: Real) -> Real
29
+ where chain_order <= 1,
30
+ domain: x > 0.0
31
+ {
32
+ ln(x) / LN2
33
+ }
34
+
35
+ @verify(lean, theorem = "log10_one_is_zero")
36
+ fn log10(x: Real) -> Real
37
+ where chain_order <= 1,
38
+ domain: x > 0.0
39
+ {
40
+ ln(x) / LN10
41
+ }
42
+
43
+ // log base b -- domain checks fall on the caller for b > 0, b != 1.
44
+ fn log_b(x: Real, b: Real) -> Real
45
+ where chain_order <= 2,
46
+ domain: x > 0.0 && b > 0.0
47
+ {
48
+ ln(x) / ln(b)
49
+ }
50
+
51
+ // log1p(x) = ln(1 + x). Stable near x = 0 where literal ln(1+x)
52
+ // loses precision (1 + x rounds back to 1 for |x| < ulp/2).
53
+ @verify(lean, theorem = "log1p_zero_is_zero")
54
+ fn log1p(x: Real) -> Real
55
+ where chain_order <= 1,
56
+ domain: x > -1.0
57
+ {
58
+ ln(1.0 + x)
59
+ }
@@ -0,0 +1,51 @@
1
+ // pow.eml -- power via exp(b * ln(x)) plus a few hot specialisations.
2
+ //
3
+ // pow is a Forge builtin in the f64 path; this module adds the
4
+ // integer-exponent variants the FPGA backend prefers (no
5
+ // transcendental needed), so kernels like `x^2`, `x^3`, `x^4`
6
+ // stay chain-order 0.
7
+
8
+ module pow_module;
9
+
10
+ @verify(lean, theorem = "pow_kernel_positive_base")
11
+ fn pow_kernel(base: Real, exponent: Real) -> Real
12
+ where chain_order <= 2,
13
+ domain: base > 0.0
14
+ ensures (result > 0.0)
15
+ {
16
+ exp(exponent * ln(base))
17
+ }
18
+
19
+ @verify(lean, theorem = "sq_nonneg")
20
+ fn sq(x: Real) -> Real
21
+ where chain_order <= 0
22
+ ensures (result >= 0.0)
23
+ {
24
+ x * x
25
+ }
26
+
27
+ @verify(lean, theorem = "cube_sign_matches_x")
28
+ fn cube(x: Real) -> Real
29
+ where chain_order <= 0
30
+ {
31
+ x * x * x
32
+ }
33
+
34
+ @verify(lean, theorem = "pow4_nonneg")
35
+ fn pow4(x: Real) -> Real
36
+ where chain_order <= 0
37
+ ensures (result >= 0.0)
38
+ {
39
+ let s = x * x;
40
+ s * s
41
+ }
42
+
43
+ // Fast integer pow up to n=8 by repeated squaring -- caller picks
44
+ // the right variant; the optimiser folds the rest. All chain 0.
45
+ fn pow8(x: Real) -> Real
46
+ where chain_order <= 0
47
+ {
48
+ let s = x * x;
49
+ let q = s * s;
50
+ q * q
51
+ }
@@ -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
+ }
@@ -0,0 +1,21 @@
1
+ // sigmoid.eml -- the neural-network activation classic.
2
+ //
3
+ // sigmoid(x) = 1 / (1 + exp(-x))
4
+ //
5
+ // Chain order 2 (rational of an exp). The bonus `silu` function
6
+ // is x * sigmoid(x) -- same shape, same cost class.
7
+
8
+ module sigmoid;
9
+
10
+ fn sigmoid(x: Real) -> Real
11
+ where chain_order <= 2
12
+ {
13
+ 1.0 / (1.0 + exp(-x))
14
+ }
15
+
16
+ // SiLU / Swish activation.
17
+ fn silu(x: Real) -> Real
18
+ where chain_order <= 2
19
+ {
20
+ x / (1.0 + exp(-x))
21
+ }