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.
- eml_stdlib/__init__.py +81 -0
- eml_stdlib/ballistics/air_density.eml +73 -0
- eml_stdlib/ballistics/ballistic_solver.eml +102 -0
- eml_stdlib/ballistics/coriolis.eml +60 -0
- eml_stdlib/ballistics/drag.eml +44 -0
- eml_stdlib/ballistics/gravity.eml +52 -0
- eml_stdlib/ballistics/muzzle_velocity.eml +42 -0
- eml_stdlib/ballistics/spin_drift.eml +60 -0
- eml_stdlib/ballistics/time_of_flight.eml +79 -0
- eml_stdlib/ballistics/wind.eml +58 -0
- eml_stdlib/biology/abilities/ballistic_armor.eml +61 -0
- eml_stdlib/biology/abilities/bite_force.eml +48 -0
- eml_stdlib/biology/abilities/chromatophore.eml +50 -0
- eml_stdlib/biology/abilities/discharge.eml +45 -0
- eml_stdlib/biology/abilities/elastic_energy.eml +42 -0
- eml_stdlib/biology/abilities/locomotion.eml +50 -0
- eml_stdlib/biology/abilities/muscle_power.eml +47 -0
- eml_stdlib/biology/abilities/sprint.eml +36 -0
- eml_stdlib/biology/abilities/texture_match.eml +49 -0
- eml_stdlib/biology/abilities/toxin.eml +44 -0
- eml_stdlib/biology/cone_l.eml +36 -0
- eml_stdlib/biology/cone_s.eml +32 -0
- eml_stdlib/biology/cryptochrome.eml +44 -0
- eml_stdlib/biology/geomagnetic_field.eml +32 -0
- eml_stdlib/biology/goldman_equation.eml +41 -0
- eml_stdlib/biology/hill_equation.eml +65 -0
- eml_stdlib/biology/hodgkin_huxley.eml +108 -0
- eml_stdlib/biology/michaelis_menten.eml +65 -0
- eml_stdlib/biology/plume_diffusion.eml +47 -0
- eml_stdlib/biology/receptor_binding.eml +35 -0
- eml_stdlib/biology/rod_sensitivity.eml +38 -0
- eml_stdlib/biology/tapetum.eml +70 -0
- eml_stdlib/carriers/acoustic_cloak.eml +56 -0
- eml_stdlib/carriers/ferron_emission.eml +55 -0
- eml_stdlib/carriers/ferron_logic.eml +45 -0
- eml_stdlib/carriers/ferron_propagation.eml +69 -0
- eml_stdlib/carriers/mach_zehnder.eml +52 -0
- eml_stdlib/carriers/magnon_dispersion.eml +57 -0
- eml_stdlib/carriers/magnon_logic.eml +45 -0
- eml_stdlib/carriers/optical_neuron.eml +55 -0
- eml_stdlib/carriers/phonon_bandgap.eml +54 -0
- eml_stdlib/carriers/ring_resonator.eml +48 -0
- eml_stdlib/carriers/spin_torque.eml +61 -0
- eml_stdlib/carriers/thermal_rectifier.eml +63 -0
- eml_stdlib/catalog.json +4209 -0
- eml_stdlib/circuits/buck_converter.eml +74 -0
- eml_stdlib/circuits/cmos_inverter.eml +75 -0
- eml_stdlib/circuits/h_bridge.eml +69 -0
- eml_stdlib/circuits/ldo.eml +66 -0
- eml_stdlib/circuits/mosfet_iv.eml +59 -0
- eml_stdlib/circuits/pll_loop.eml +63 -0
- eml_stdlib/circuits/rc_filter.eml +98 -0
- eml_stdlib/circuits/voltage_divider.eml +68 -0
- eml_stdlib/control/bangbang.eml +34 -0
- eml_stdlib/control/deadband.eml +28 -0
- eml_stdlib/control/hysteresis.eml +28 -0
- eml_stdlib/control/kalman_1d.eml +53 -0
- eml_stdlib/control/lqr_1d.eml +47 -0
- eml_stdlib/control/pid.eml +38 -0
- eml_stdlib/gaming/animation/bounce.eml +39 -0
- eml_stdlib/gaming/animation/ease_in.eml +27 -0
- eml_stdlib/gaming/animation/ease_in_out.eml +29 -0
- eml_stdlib/gaming/animation/ease_out.eml +27 -0
- eml_stdlib/gaming/animation/ik_2bone.eml +57 -0
- eml_stdlib/gaming/animation/spring.eml +39 -0
- eml_stdlib/gaming/audio/distance_attenuation.eml +50 -0
- eml_stdlib/gaming/audio/doppler.eml +39 -0
- eml_stdlib/gaming/audio/lowpass_audio.eml +32 -0
- eml_stdlib/gaming/audio/reverb_delay.eml +41 -0
- eml_stdlib/gaming/camera/dof_blur.eml +30 -0
- eml_stdlib/gaming/camera/fov_projection.eml +47 -0
- eml_stdlib/gaming/camera/orbit.eml +48 -0
- eml_stdlib/gaming/camera/shake.eml +20 -0
- eml_stdlib/gaming/lighting/ambient_occlusion.eml +40 -0
- eml_stdlib/gaming/lighting/area_light.eml +50 -0
- eml_stdlib/gaming/lighting/fog_exp.eml +28 -0
- eml_stdlib/gaming/lighting/fog_exp2.eml +30 -0
- eml_stdlib/gaming/lighting/point_light.eml +33 -0
- eml_stdlib/gaming/lighting/spot_light.eml +43 -0
- eml_stdlib/gaming/noise/fbm.eml +65 -0
- eml_stdlib/gaming/noise/perlin_2d.eml +65 -0
- eml_stdlib/gaming/noise/perlin_3d.eml +63 -0
- eml_stdlib/gaming/noise/simplex_2d.eml +67 -0
- eml_stdlib/gaming/noise/turbulence.eml +46 -0
- eml_stdlib/gaming/noise/value_noise.eml +45 -0
- eml_stdlib/gaming/noise/voronoi_2d.eml +83 -0
- eml_stdlib/gaming/noise/white_noise.eml +47 -0
- eml_stdlib/gaming/noise/worley.eml +79 -0
- eml_stdlib/gaming/particles/drag.eml +30 -0
- eml_stdlib/gaming/particles/emitter_radial.eml +35 -0
- eml_stdlib/gaming/particles/fade.eml +36 -0
- eml_stdlib/gaming/particles/gravity_particle.eml +32 -0
- eml_stdlib/gaming/particles/size_over_life.eml +28 -0
- eml_stdlib/gaming/physics/aabb_overlap.eml +38 -0
- eml_stdlib/gaming/physics/buoyancy.eml +43 -0
- eml_stdlib/gaming/physics/circle_collision.eml +46 -0
- eml_stdlib/gaming/physics/impulse_resolve.eml +39 -0
- eml_stdlib/gaming/physics/rigid_body_2d.eml +61 -0
- eml_stdlib/gaming/physics/spring_damper.eml +30 -0
- eml_stdlib/gaming/physics/verlet.eml +29 -0
- eml_stdlib/gaming/shading/fresnel.eml +57 -0
- eml_stdlib/gaming/shading/matcap.eml +34 -0
- eml_stdlib/gaming/shading/pbr_diffuse.eml +41 -0
- eml_stdlib/gaming/shading/pbr_specular.eml +75 -0
- eml_stdlib/gaming/shading/subsurface.eml +52 -0
- eml_stdlib/gaming/shading/toon.eml +30 -0
- eml_stdlib/gaming/terrain/biome_select.eml +41 -0
- eml_stdlib/gaming/terrain/cliff_detect.eml +35 -0
- eml_stdlib/gaming/terrain/erosion_hydraulic.eml +57 -0
- eml_stdlib/gaming/terrain/erosion_thermal.eml +44 -0
- eml_stdlib/gaming/terrain/heightmap.eml +33 -0
- eml_stdlib/gaming/textures/brick.eml +27 -0
- eml_stdlib/gaming/textures/caustics.eml +25 -0
- eml_stdlib/gaming/textures/checkerboard.eml +27 -0
- eml_stdlib/gaming/textures/fire.eml +26 -0
- eml_stdlib/gaming/textures/marble.eml +24 -0
- eml_stdlib/gaming/textures/rust.eml +23 -0
- eml_stdlib/gaming/textures/water_surface.eml +49 -0
- eml_stdlib/gaming/textures/wood.eml +41 -0
- eml_stdlib/math/abs.eml +45 -0
- eml_stdlib/math/atan.eml +32 -0
- eml_stdlib/math/atan2.eml +61 -0
- eml_stdlib/math/bessel_j0.eml +65 -0
- eml_stdlib/math/cos.eml +42 -0
- eml_stdlib/math/cosh.eml +24 -0
- eml_stdlib/math/erf.eml +66 -0
- eml_stdlib/math/exp.eml +49 -0
- eml_stdlib/math/gamma.eml +55 -0
- eml_stdlib/math/gaussian.eml +18 -0
- eml_stdlib/math/log.eml +59 -0
- eml_stdlib/math/pow.eml +51 -0
- eml_stdlib/math/relu.eml +63 -0
- eml_stdlib/math/sigmoid.eml +21 -0
- eml_stdlib/math/sin.eml +37 -0
- eml_stdlib/math/sinh.eml +24 -0
- eml_stdlib/math/softmax.eml +87 -0
- eml_stdlib/math/sqrt.eml +46 -0
- eml_stdlib/math/tan.eml +36 -0
- eml_stdlib/math/tanh.eml +54 -0
- eml_stdlib/ml/attention.eml +70 -0
- eml_stdlib/ml/cross_entropy.eml +85 -0
- eml_stdlib/ml/gelu.eml +51 -0
- eml_stdlib/ml/layernorm.eml +49 -0
- eml_stdlib/ml/leaky_relu.eml +51 -0
- eml_stdlib/ml/relu.eml +63 -0
- eml_stdlib/ml/rotary_embed.eml +64 -0
- eml_stdlib/ml/silu.eml +40 -0
- eml_stdlib/ml/softmax.eml +87 -0
- eml_stdlib/physics/coulomb.eml +48 -0
- eml_stdlib/physics/damped_oscillator.eml +20 -0
- eml_stdlib/physics/diffusion.eml +45 -0
- eml_stdlib/physics/gravity.eml +60 -0
- eml_stdlib/physics/hooke.eml +56 -0
- eml_stdlib/physics/navier_stokes_1d.eml +57 -0
- eml_stdlib/physics/planck_radiation.eml +77 -0
- eml_stdlib/physics/stefan_boltzmann.eml +75 -0
- eml_stdlib/physics/wave_propagation.eml +52 -0
- eml_stdlib/quantum/cnot.eml +72 -0
- eml_stdlib/quantum/grover_oracle.eml +69 -0
- eml_stdlib/quantum/hadamard.eml +73 -0
- eml_stdlib/quantum/pauli_x.eml +29 -0
- eml_stdlib/quantum/pauli_z.eml +29 -0
- eml_stdlib/quantum/phase_gate.eml +76 -0
- eml_stdlib/quantum/rotation_rz.eml +92 -0
- eml_stdlib/sensors/accelerometer.eml +59 -0
- eml_stdlib/sensors/hall_effect.eml +42 -0
- eml_stdlib/sensors/photodetector.eml +58 -0
- eml_stdlib/sensors/rtd.eml +57 -0
- eml_stdlib/sensors/strain_gauge.eml +63 -0
- eml_stdlib/sensors/thermistor.eml +71 -0
- eml_stdlib/signal/bandpass.eml +39 -0
- eml_stdlib/signal/chirp.eml +42 -0
- eml_stdlib/signal/dft_single.eml +30 -0
- eml_stdlib/signal/fft_butterfly.eml +36 -0
- eml_stdlib/signal/highpass.eml +45 -0
- eml_stdlib/signal/lowpass.eml +49 -0
- eml_stdlib/signal/matched_filter.eml +48 -0
- eml_stdlib/signal/notch_filter.eml +47 -0
- eml_stdlib/signal/window_hamming.eml +47 -0
- eml_stdlib/signal/window_hann.eml +50 -0
- eml_stdlib-0.4.0.dist-info/METADATA +453 -0
- eml_stdlib-0.4.0.dist-info/RECORD +185 -0
- eml_stdlib-0.4.0.dist-info/WHEEL +5 -0
- eml_stdlib-0.4.0.dist-info/licenses/LICENSE +21 -0
- eml_stdlib-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// hill.eml -- Hill equation for cooperative binding.
|
|
2
|
+
//
|
|
3
|
+
// v = Vmax * S^n / (K^n + S^n)
|
|
4
|
+
//
|
|
5
|
+
// where:
|
|
6
|
+
// v fractional saturation or reaction velocity
|
|
7
|
+
// S ligand / substrate concentration
|
|
8
|
+
// K half-saturation constant (S at v = Vmax/2)
|
|
9
|
+
// n Hill coefficient (cooperativity index)
|
|
10
|
+
//
|
|
11
|
+
// n > 1: positive cooperativity (e.g. haemoglobin O2 binding, n ≈ 2.7).
|
|
12
|
+
// n = 1: degenerates to Michaelis-Menten / Langmuir.
|
|
13
|
+
// n < 1: negative cooperativity.
|
|
14
|
+
//
|
|
15
|
+
// Chain order = 2: pow(x, n) for non-integer n decomposes to
|
|
16
|
+
// exp(n * log(x)), which puts a log inside an exp -- two distinct
|
|
17
|
+
// Pfaffian-chain steps. For integer n the optimizer can reduce
|
|
18
|
+
// this to a pure rational (chain 0), but we declare chain 2 in
|
|
19
|
+
// the type so the f64 routing kicks in for biological n=2.7-style
|
|
20
|
+
// fits.
|
|
21
|
+
|
|
22
|
+
module hill;
|
|
23
|
+
|
|
24
|
+
const S_MAX: Real = 1.0e3
|
|
25
|
+
const N_MAX: Real = 8.0 // Hill coefficients above 8 are not biology
|
|
26
|
+
|
|
27
|
+
type HillSignal = Real where chain_order <= 2
|
|
28
|
+
|
|
29
|
+
@target(fpga, clock_mhz = 100, precision = float64)
|
|
30
|
+
@verify(lean, theorem = "hill_monotone_in_substrate")
|
|
31
|
+
fn hill_velocity(
|
|
32
|
+
vmax: Real,
|
|
33
|
+
substrate: Real,
|
|
34
|
+
half_saturation: Real,
|
|
35
|
+
hill_coefficient: Real,
|
|
36
|
+
) -> HillSignal
|
|
37
|
+
where chain_order <= 2
|
|
38
|
+
requires (vmax > 0.0)
|
|
39
|
+
requires (substrate >= 0.0)
|
|
40
|
+
requires (substrate <= S_MAX)
|
|
41
|
+
requires (half_saturation > 0.0)
|
|
42
|
+
requires (half_saturation <= S_MAX)
|
|
43
|
+
requires (hill_coefficient > 0.0)
|
|
44
|
+
requires (hill_coefficient <= N_MAX)
|
|
45
|
+
ensures (result >= 0.0)
|
|
46
|
+
ensures (result <= vmax)
|
|
47
|
+
{
|
|
48
|
+
let s_n = pow(substrate, hill_coefficient);
|
|
49
|
+
let k_n = pow(half_saturation, hill_coefficient);
|
|
50
|
+
vmax * s_n / (k_n + s_n)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Linearised Hill plot: log(v / (Vmax - v)) = n * log(S) - n * log(K)
|
|
54
|
+
// Slope is the Hill coefficient; useful for cooperativity fitting.
|
|
55
|
+
fn hill_logit(
|
|
56
|
+
velocity_obs: Real,
|
|
57
|
+
vmax: Real,
|
|
58
|
+
) -> Real
|
|
59
|
+
where chain_order <= 1
|
|
60
|
+
requires (vmax > 0.0)
|
|
61
|
+
requires (velocity_obs > 0.0)
|
|
62
|
+
requires (velocity_obs < vmax)
|
|
63
|
+
{
|
|
64
|
+
log(velocity_obs / (vmax - velocity_obs))
|
|
65
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// hodgkin_huxley_step.eml -- HH neuron membrane voltage update.
|
|
2
|
+
//
|
|
3
|
+
// The 1952 Hodgkin-Huxley equation for the squid giant axon
|
|
4
|
+
// membrane voltage V (mV):
|
|
5
|
+
//
|
|
6
|
+
// C_m * dV/dt = -g_Na * m^3 * h * (V - E_Na)
|
|
7
|
+
// -g_K * n^4 * (V - E_K)
|
|
8
|
+
// -g_L * (V - E_L)
|
|
9
|
+
// + I_ext
|
|
10
|
+
//
|
|
11
|
+
// where m, h, n are the gating-variable activations (each
|
|
12
|
+
// satisfies its own first-order ODE in the host loop), and
|
|
13
|
+
// g_*, E_* are channel conductances and reversal potentials.
|
|
14
|
+
//
|
|
15
|
+
// This kernel is the explicit-Euler step for V; the gating
|
|
16
|
+
// variables update in companion gates (not yet shipped).
|
|
17
|
+
//
|
|
18
|
+
// Chain order 0 -- pure polynomial in the gating activations.
|
|
19
|
+
// Used in computational neuroscience, neuromorphic-chip
|
|
20
|
+
// simulators, and in retinal-implant control loops.
|
|
21
|
+
|
|
22
|
+
module hodgkin_huxley_step;
|
|
23
|
+
|
|
24
|
+
const V_MIN: Real = -200.0
|
|
25
|
+
const V_MAX: Real = 200.0
|
|
26
|
+
const G_MAX: Real = 1000.0
|
|
27
|
+
const I_MAX: Real = 1000.0
|
|
28
|
+
const CM_MIN: Real = 0.01
|
|
29
|
+
const CM_MAX: Real = 10.0
|
|
30
|
+
const DT_MAX: Real = 0.1
|
|
31
|
+
|
|
32
|
+
type MembraneVoltage = Real where chain_order <= 0
|
|
33
|
+
|
|
34
|
+
@target(fpga, clock_mhz = 400, precision = float64)
|
|
35
|
+
@verify(lean, theorem = "hh_voltage_bounded_under_step")
|
|
36
|
+
fn voltage_step(
|
|
37
|
+
voltage: Real,
|
|
38
|
+
m_gate: Real,
|
|
39
|
+
h_gate: Real,
|
|
40
|
+
n_gate: Real,
|
|
41
|
+
g_na: Real,
|
|
42
|
+
g_k: Real,
|
|
43
|
+
g_l: Real,
|
|
44
|
+
e_na: Real,
|
|
45
|
+
e_k: Real,
|
|
46
|
+
e_l: Real,
|
|
47
|
+
i_ext: Real,
|
|
48
|
+
cm: Real,
|
|
49
|
+
dt: Real,
|
|
50
|
+
) -> MembraneVoltage
|
|
51
|
+
where chain_order <= 0
|
|
52
|
+
requires (voltage >= V_MIN)
|
|
53
|
+
requires (voltage <= V_MAX)
|
|
54
|
+
requires (m_gate >= 0.0)
|
|
55
|
+
requires (m_gate <= 1.0)
|
|
56
|
+
requires (h_gate >= 0.0)
|
|
57
|
+
requires (h_gate <= 1.0)
|
|
58
|
+
requires (n_gate >= 0.0)
|
|
59
|
+
requires (n_gate <= 1.0)
|
|
60
|
+
requires (g_na >= 0.0)
|
|
61
|
+
requires (g_na <= G_MAX)
|
|
62
|
+
requires (g_k >= 0.0)
|
|
63
|
+
requires (g_k <= G_MAX)
|
|
64
|
+
requires (g_l >= 0.0)
|
|
65
|
+
requires (g_l <= G_MAX)
|
|
66
|
+
requires (abs(e_na) <= V_MAX)
|
|
67
|
+
requires (abs(e_k) <= V_MAX)
|
|
68
|
+
requires (abs(e_l) <= V_MAX)
|
|
69
|
+
requires (abs(i_ext) <= I_MAX)
|
|
70
|
+
requires (cm >= CM_MIN)
|
|
71
|
+
requires (cm <= CM_MAX)
|
|
72
|
+
requires (dt > 0.0)
|
|
73
|
+
requires (dt <= DT_MAX)
|
|
74
|
+
{
|
|
75
|
+
let m3 = m_gate * m_gate * m_gate;
|
|
76
|
+
let n4 = n_gate * n_gate * n_gate * n_gate;
|
|
77
|
+
let i_na = g_na * m3 * h_gate * (voltage - e_na);
|
|
78
|
+
let i_k = g_k * n4 * (voltage - e_k);
|
|
79
|
+
let i_l = g_l * (voltage - e_l);
|
|
80
|
+
voltage + dt * (i_ext - i_na - i_k - i_l) / cm
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Single gating-variable step (m, h, or n). All three follow
|
|
84
|
+
// dx/dt = alpha * (1 - x) - beta * x
|
|
85
|
+
// with channel-specific alpha, beta supplied by the host
|
|
86
|
+
// (typically lookup tables of voltage). Chain order 0.
|
|
87
|
+
@target(fpga, clock_mhz = 500, precision = float64)
|
|
88
|
+
@verify(lean, theorem = "gating_step_bounded_in_unit_interval")
|
|
89
|
+
fn gating_step(
|
|
90
|
+
gate: Real,
|
|
91
|
+
alpha: Real,
|
|
92
|
+
beta: Real,
|
|
93
|
+
dt: Real,
|
|
94
|
+
) -> Real
|
|
95
|
+
where chain_order <= 0
|
|
96
|
+
requires (gate >= 0.0)
|
|
97
|
+
requires (gate <= 1.0)
|
|
98
|
+
requires (alpha >= 0.0)
|
|
99
|
+
requires (alpha <= 1.0e3)
|
|
100
|
+
requires (beta >= 0.0)
|
|
101
|
+
requires (beta <= 1.0e3)
|
|
102
|
+
requires (dt > 0.0)
|
|
103
|
+
requires (dt <= DT_MAX)
|
|
104
|
+
ensures (result >= 0.0)
|
|
105
|
+
ensures (result <= 1.0)
|
|
106
|
+
{
|
|
107
|
+
clamp(gate + dt * (alpha * (1.0 - gate) - beta * gate), 0.0, 1.0)
|
|
108
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// michaelis_menten.eml -- Michaelis-Menten enzyme kinetics.
|
|
2
|
+
//
|
|
3
|
+
// v = (Vmax * S) / (Km + S)
|
|
4
|
+
//
|
|
5
|
+
// where:
|
|
6
|
+
// v reaction velocity (M/s)
|
|
7
|
+
// Vmax maximum velocity at saturation (M/s)
|
|
8
|
+
// S substrate concentration (M)
|
|
9
|
+
// Km Michaelis constant -- substrate concentration at v = Vmax/2
|
|
10
|
+
//
|
|
11
|
+
// The classical enzyme-kinetics rate law. Chain order = 0 (rational
|
|
12
|
+
// function in S). f32 is fine for assay analysis.
|
|
13
|
+
//
|
|
14
|
+
// Used in: enzyme-assay analysis, drug-metabolism (CYP450 kinetics),
|
|
15
|
+
// industrial biocatalysis.
|
|
16
|
+
|
|
17
|
+
module michaelis_menten;
|
|
18
|
+
|
|
19
|
+
const S_MAX: Real = 1.0e3 // 1000 M ceiling (covers everything)
|
|
20
|
+
const VMAX_MAX: Real = 1.0e6 // 1e6 M/s (no real enzyme is faster)
|
|
21
|
+
|
|
22
|
+
type EnzymeRate = Real where chain_order <= 0
|
|
23
|
+
|
|
24
|
+
@verify(lean, theorem = "michaelis_menten_saturating")
|
|
25
|
+
fn velocity(
|
|
26
|
+
vmax: Real,
|
|
27
|
+
substrate: Real,
|
|
28
|
+
km: Real,
|
|
29
|
+
) -> EnzymeRate
|
|
30
|
+
where chain_order <= 0
|
|
31
|
+
requires (vmax > 0.0)
|
|
32
|
+
requires (vmax <= VMAX_MAX)
|
|
33
|
+
requires (substrate >= 0.0)
|
|
34
|
+
requires (substrate <= S_MAX)
|
|
35
|
+
requires (km > 0.0)
|
|
36
|
+
requires (km <= S_MAX)
|
|
37
|
+
ensures (result >= 0.0)
|
|
38
|
+
ensures (result <= vmax)
|
|
39
|
+
{
|
|
40
|
+
vmax * substrate / (km + substrate)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Lineweaver-Burk transform: 1/v = (Km/Vmax) * (1/S) + 1/Vmax
|
|
44
|
+
// Used for graphical Km / Vmax estimation from assay data.
|
|
45
|
+
fn lineweaver_burk(
|
|
46
|
+
velocity_obs: Real,
|
|
47
|
+
) -> Real
|
|
48
|
+
where chain_order <= 0
|
|
49
|
+
requires (velocity_obs > 0.0)
|
|
50
|
+
{
|
|
51
|
+
1.0 / velocity_obs
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Eadie-Hofstee form: v = Vmax - Km * (v/S)
|
|
55
|
+
// Numerically better-conditioned than Lineweaver-Burk for fitting.
|
|
56
|
+
fn eadie_hofstee(
|
|
57
|
+
velocity_obs: Real,
|
|
58
|
+
substrate: Real,
|
|
59
|
+
) -> Real
|
|
60
|
+
where chain_order <= 0
|
|
61
|
+
requires (velocity_obs > 0.0)
|
|
62
|
+
requires (substrate > 0.0)
|
|
63
|
+
{
|
|
64
|
+
velocity_obs / substrate
|
|
65
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// plume_diffusion.eml
|
|
2
|
+
//
|
|
3
|
+
// Gaussian plume model — concentration of an odorant released at
|
|
4
|
+
// a continuous point source, advected downwind, dispersed by
|
|
5
|
+
// turbulence:
|
|
6
|
+
//
|
|
7
|
+
// C(x, y) = Q / (2π · σ_x · σ_y · u) · exp(-y² / (2 σ_y²))
|
|
8
|
+
//
|
|
9
|
+
// Q = source strength (molecules/s), u = wind speed (m/s),
|
|
10
|
+
// σ_x and σ_y = downwind-dependent dispersion lengths
|
|
11
|
+
// (Pasquill-Gifford curves; we use σ ∝ √x as a standard low-
|
|
12
|
+
// turbulence approximation for the EML kernel).
|
|
13
|
+
//
|
|
14
|
+
// This is the same model atmospheric dispersion calculations use
|
|
15
|
+
// for industrial pollutants and the same one a search-and-rescue
|
|
16
|
+
// dog navigates while running a track. A trained detection dog
|
|
17
|
+
// effectively integrates this concentration field across its
|
|
18
|
+
// turn-by-turn path until it reaches the source.
|
|
19
|
+
//
|
|
20
|
+
// Postcondition: C ≥ 0 everywhere — exp is non-negative, all
|
|
21
|
+
// constants are positive. Required by the binding equation
|
|
22
|
+
// downstream.
|
|
23
|
+
|
|
24
|
+
module plume_diffusion;
|
|
25
|
+
|
|
26
|
+
const PI: Real = 3.14159265358979
|
|
27
|
+
const WIND_SPEED: Real = 1.0 // m/s, light breeze
|
|
28
|
+
const SIGMA_COEFF: Real = 0.2 // dispersion coefficient
|
|
29
|
+
|
|
30
|
+
@verify(lean, theorem = "plume_concentration_nonneg")
|
|
31
|
+
fn plume_concentration(
|
|
32
|
+
source_strength: Real{q | q >= 0.0},
|
|
33
|
+
downwind_x_m: Real{x | x > 0.0},
|
|
34
|
+
crosswind_y_m: Real,
|
|
35
|
+
)
|
|
36
|
+
-> Real{r | r >= 0.0}
|
|
37
|
+
where chain_order <= 1
|
|
38
|
+
{
|
|
39
|
+
// σ_y(x) = c · √x — dispersion grows with downwind distance.
|
|
40
|
+
// We approximate σ_x ≈ σ_y and roll both into the prefactor.
|
|
41
|
+
max(0.0,
|
|
42
|
+
source_strength
|
|
43
|
+
/ (2.0 * PI * SIGMA_COEFF * SIGMA_COEFF * downwind_x_m * WIND_SPEED)
|
|
44
|
+
* exp(-(crosswind_y_m * crosswind_y_m)
|
|
45
|
+
/ (2.0 * SIGMA_COEFF * SIGMA_COEFF * downwind_x_m))
|
|
46
|
+
)
|
|
47
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// receptor_binding.eml
|
|
2
|
+
//
|
|
3
|
+
// Olfactory receptor binding — Michaelis-Menten kinetics. An
|
|
4
|
+
// odorant molecule reversibly binds to a receptor; at steady
|
|
5
|
+
// state, the fraction of bound receptors is:
|
|
6
|
+
//
|
|
7
|
+
// r(C) = R_max · C / (C + K_d)
|
|
8
|
+
//
|
|
9
|
+
// `K_d` (dissociation constant) sets the half-saturation
|
|
10
|
+
// concentration; `R_max` is the maximum response amplitude. This
|
|
11
|
+
// is the same equation that governs every reversible-binding
|
|
12
|
+
// reaction in pharmacology, biochemistry, and immunoassay design.
|
|
13
|
+
//
|
|
14
|
+
// Olfactory dose-response curves in vivo follow this shape with
|
|
15
|
+
// remarkable fidelity (Firestein 2001, "How the olfactory system
|
|
16
|
+
// makes sense of scents") — each receptor contributes a
|
|
17
|
+
// Michaelis-Menten term to the population activity.
|
|
18
|
+
//
|
|
19
|
+
// Postcondition: r ∈ [0, R_max] for all non-negative C. The
|
|
20
|
+
// downstream dog/human comparison takes ratios of receptor
|
|
21
|
+
// outputs; an out-of-range value would produce a meaningless
|
|
22
|
+
// or unbounded ratio.
|
|
23
|
+
|
|
24
|
+
module receptor_binding;
|
|
25
|
+
|
|
26
|
+
const R_MAX: Real = 1.0
|
|
27
|
+
const K_D: Real = 1.0e-6 // 1 ppm — typical dog receptor (~10⁴× tighter than human)
|
|
28
|
+
|
|
29
|
+
@verify(lean, theorem = "receptor_binding_in_unit_interval")
|
|
30
|
+
fn receptor_binding(concentration: Real{c | c >= 0.0})
|
|
31
|
+
-> Real{r | 0.0 <= r && r <= R_MAX}
|
|
32
|
+
where chain_order <= 0
|
|
33
|
+
{
|
|
34
|
+
clamp(R_MAX * concentration / (concentration + K_D), 0.0, R_MAX)
|
|
35
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// rod_sensitivity.eml
|
|
2
|
+
//
|
|
3
|
+
// Feline rhodopsin (rod photoreceptor) spectral sensitivity.
|
|
4
|
+
// Modeled as a normalized Gaussian centered at peak absorbance:
|
|
5
|
+
//
|
|
6
|
+
// S(λ) = exp(-((λ - λ_peak)² / (2σ²)))
|
|
7
|
+
//
|
|
8
|
+
// Parameters from van Norren & Vos (1974), "Spectral transmission
|
|
9
|
+
// of the human ocular media": cat rhodopsin λ_peak = 498 nm, with
|
|
10
|
+
// full-width-half-max ≈ 100 nm → σ ≈ 50 nm. This is the classical
|
|
11
|
+
// "scotopic" (low-light) channel — the same machinery that lets
|
|
12
|
+
// cats hunt at one-sixth the light intensity humans need.
|
|
13
|
+
//
|
|
14
|
+
// Co-domain by construction: exp(-x) for x ≥ 0 lives in (0, 1].
|
|
15
|
+
// The @verify contract pins this for downstream pipeline safety
|
|
16
|
+
// (a sensitivity > 1 would imply photon gain in a passive
|
|
17
|
+
// receptor, which is unphysical).
|
|
18
|
+
//
|
|
19
|
+
// Phase F migration: the wavelength input is bounded to the
|
|
20
|
+
// physiologically meaningful visible-light window via a parameter
|
|
21
|
+
// refinement; the sensitivity output is constrained to [0, 1] via
|
|
22
|
+
// a return refinement instead of two `ensures` clauses. The
|
|
23
|
+
// refinement-aware Lean lowering inserts both bounds as theorem
|
|
24
|
+
// hypotheses / conclusions automatically.
|
|
25
|
+
|
|
26
|
+
module rod_sensitivity;
|
|
27
|
+
|
|
28
|
+
const ROD_PEAK_NM: Real = 498.0
|
|
29
|
+
const ROD_SIGMA_NM: Real = 50.0
|
|
30
|
+
|
|
31
|
+
@verify(lean, theorem = "rod_sensitivity_bounded_unit")
|
|
32
|
+
fn rod_sensitivity(wavelength_nm: Real{w | 300.0 <= w && w <= 800.0})
|
|
33
|
+
-> Real{r | 0.0 <= r && r <= 1.0}
|
|
34
|
+
where chain_order <= 1
|
|
35
|
+
{
|
|
36
|
+
exp(-((wavelength_nm - ROD_PEAK_NM) * (wavelength_nm - ROD_PEAK_NM))
|
|
37
|
+
/ (2.0 * ROD_SIGMA_NM * ROD_SIGMA_NM))
|
|
38
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// tapetum.eml
|
|
2
|
+
//
|
|
3
|
+
// The tapetum lucidum — a reflective layer behind the cat retina.
|
|
4
|
+
// Photons that miss a photoreceptor on the way in get a second
|
|
5
|
+
// chance on the way out, effectively doubling-or-better the
|
|
6
|
+
// quantum catch. Reported in-vivo amplification is ~130× at the
|
|
7
|
+
// rod-peak wavelength, falling off at the spectral edges where
|
|
8
|
+
// the tapetal pigment is less reflective.
|
|
9
|
+
//
|
|
10
|
+
// Sources: Pirie (1959), "The structure and properties of the
|
|
11
|
+
// tapetum cellulosum of the cat", and Coile & O'Keefe (1988),
|
|
12
|
+
// "Schematic eyes for domestic animals." Modeled as a Gaussian-
|
|
13
|
+
// shaped gain centered at λ_peak = 500 nm (matching the rod
|
|
14
|
+
// peak), σ = 70 nm, gain bounded to [1, 130].
|
|
15
|
+
//
|
|
16
|
+
// Two functions:
|
|
17
|
+
//
|
|
18
|
+
// tapetum_gain(λ)
|
|
19
|
+
// The amplification factor at one wavelength, in [1, 130].
|
|
20
|
+
// Always ≥ 1 — the tapetum can only amplify, never attenuate.
|
|
21
|
+
//
|
|
22
|
+
// tapetum_amplify(I, λ)
|
|
23
|
+
// Apply the gain to an input intensity I. The contract here
|
|
24
|
+
// is what the cat-vision pipeline relies on: the function is
|
|
25
|
+
// **monotone in I** (more photons in → no fewer photons out)
|
|
26
|
+
// and **bounded** (output ≤ 130·I). Both properties are
|
|
27
|
+
// provable in Lean from the body + the gain bounds.
|
|
28
|
+
|
|
29
|
+
module tapetum;
|
|
30
|
+
|
|
31
|
+
const TAPETUM_PEAK_NM: Real = 500.0
|
|
32
|
+
const TAPETUM_SIGMA_NM: Real = 70.0
|
|
33
|
+
const TAPETUM_BASE_GAIN: Real = 1.0
|
|
34
|
+
const TAPETUM_MAX_GAIN: Real = 130.0
|
|
35
|
+
|
|
36
|
+
// ──────────────────────────── gain(λ) ──────────────────────────
|
|
37
|
+
|
|
38
|
+
@verify(lean, theorem = "tapetum_gain_in_range")
|
|
39
|
+
fn tapetum_gain(wavelength_nm: Real) -> Real
|
|
40
|
+
where chain_order <= 1
|
|
41
|
+
ensures (result >= 1.0)
|
|
42
|
+
ensures (result <= 130.0)
|
|
43
|
+
{
|
|
44
|
+
// The clamp at the end makes the postconditions trivially
|
|
45
|
+
// provable while still doing the right physical thing for
|
|
46
|
+
// any real wavelength input.
|
|
47
|
+
clamp(
|
|
48
|
+
TAPETUM_BASE_GAIN +
|
|
49
|
+
(TAPETUM_MAX_GAIN - TAPETUM_BASE_GAIN) *
|
|
50
|
+
exp(-((wavelength_nm - TAPETUM_PEAK_NM) *
|
|
51
|
+
(wavelength_nm - TAPETUM_PEAK_NM)) /
|
|
52
|
+
(2.0 * TAPETUM_SIGMA_NM * TAPETUM_SIGMA_NM)),
|
|
53
|
+
1.0,
|
|
54
|
+
130.0
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ──────────────────────────── amplify(I, λ) ────────────────────
|
|
59
|
+
|
|
60
|
+
@verify(lean, theorem = "tapetum_amplify_monotone_bounded")
|
|
61
|
+
fn tapetum_amplify(input_intensity: Real, wavelength_nm: Real) -> Real
|
|
62
|
+
where chain_order <= 1
|
|
63
|
+
requires (input_intensity >= 0.0)
|
|
64
|
+
// Monotone (in input): gain ≥ 1 means output never falls below input.
|
|
65
|
+
ensures (result >= input_intensity)
|
|
66
|
+
// Bounded: gain ≤ 130 caps the amplification factor.
|
|
67
|
+
ensures (result <= input_intensity * 130.0)
|
|
68
|
+
{
|
|
69
|
+
input_intensity * tapetum_gain(wavelength_nm)
|
|
70
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// acoustic_cloak.eml -- TIER 5: PHONONICS, third demo.
|
|
2
|
+
//
|
|
3
|
+
// Coordinate-transform acoustic cloak. A spherical-shell
|
|
4
|
+
// metamaterial with radially-graded index n(r) maps incident
|
|
5
|
+
// pressure waves around a hidden region. In the simplest
|
|
6
|
+
// transformation-optics design (Pendry shell from r_inner to
|
|
7
|
+
// r_outer):
|
|
8
|
+
//
|
|
9
|
+
// n(r) = (r_outer - r_inner) / (r * (r_outer - r_inner) / r_outer)
|
|
10
|
+
// = r_outer / r (in the linear-shell limit)
|
|
11
|
+
//
|
|
12
|
+
// At r = r_outer the index is exactly 1 -- the cloak's outer
|
|
13
|
+
// surface matches the surrounding medium, so an external
|
|
14
|
+
// observer sees no scattering signature. At r = r_inner the
|
|
15
|
+
// index diverges, which is what bends every ray inside the
|
|
16
|
+
// shell around the hidden core.
|
|
17
|
+
//
|
|
18
|
+
// Chain order 0 (single division). The same coordinate-transform
|
|
19
|
+
// math applies to optical and electromagnetic cloaks.
|
|
20
|
+
|
|
21
|
+
module acoustic_cloak;
|
|
22
|
+
|
|
23
|
+
const R_INNER: Real = 0.10 // m
|
|
24
|
+
const R_OUTER: Real = 0.20 // m
|
|
25
|
+
|
|
26
|
+
// Shell index profile in the linear-radial-mapping cloak.
|
|
27
|
+
fn shell_index(r: Real, r_outer: Real) -> Real
|
|
28
|
+
where chain_order <= 0
|
|
29
|
+
requires (r > 0.0)
|
|
30
|
+
{
|
|
31
|
+
r_outer / r
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// At the outer boundary the index matches free space (n = 1),
|
|
35
|
+
// guaranteeing impedance match with the surrounding medium and
|
|
36
|
+
// therefore zero scattering off the cloak's outer surface.
|
|
37
|
+
@verify(lean, theorem = "acoustic_cloak_outer_index_unity")
|
|
38
|
+
fn outer_boundary_index(r_outer: Real) -> Real
|
|
39
|
+
where chain_order <= 0
|
|
40
|
+
requires (r_outer > 0.0)
|
|
41
|
+
ensures (result == 1.0)
|
|
42
|
+
{
|
|
43
|
+
1.0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Outer radius is positive and larger than the inner radius --
|
|
47
|
+
// the geometric witness that the shell has a finite, non-zero
|
|
48
|
+
// thickness to host the index gradient.
|
|
49
|
+
@verify(lean, theorem = "acoustic_cloak_shell_thickness_positive")
|
|
50
|
+
fn shell_thickness(r_outer: Real, r_inner: Real) -> Real
|
|
51
|
+
where chain_order <= 0
|
|
52
|
+
requires (r_outer > r_inner)
|
|
53
|
+
ensures (result > 0.0)
|
|
54
|
+
{
|
|
55
|
+
r_outer - r_inner
|
|
56
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// ferron_emission.eml -- TIER 6: FERRONICS, second demo.
|
|
2
|
+
//
|
|
3
|
+
// Laser-pulse driven THz emission from a ferroelectric. A
|
|
4
|
+
// femtosecond optical pump triggers ultrafast polarisation
|
|
5
|
+
// switching; the time-derivative of the polarisation radiates as
|
|
6
|
+
// a narrow-band THz transient.
|
|
7
|
+
//
|
|
8
|
+
// Larmor-like emission power scales with the square of the
|
|
9
|
+
// polarisation acceleration:
|
|
10
|
+
//
|
|
11
|
+
// P_THz ~ K_emit * (d^2 P / dt^2)^2
|
|
12
|
+
//
|
|
13
|
+
// We capture the algebraic relation (treating d^2P/dt^2 as a
|
|
14
|
+
// scalar driver). Chain order 0 in the kernel below.
|
|
15
|
+
//
|
|
16
|
+
// Why this matters: the 2025 demos showed laser-pulse pumping
|
|
17
|
+
// produces narrow-band THz from van der Waals ferroelectrics --
|
|
18
|
+
// the carrier is electric polarisation order itself, not
|
|
19
|
+
// charge. The emission spectrum is the readout of the ferron
|
|
20
|
+
// dynamics.
|
|
21
|
+
|
|
22
|
+
module ferron_emission;
|
|
23
|
+
|
|
24
|
+
const K_EMIT: Real = 1.0e-22 // W * s^4 / C^2 * m^2 (lumped)
|
|
25
|
+
|
|
26
|
+
// Radiated THz power as a function of the (driven) polarisation
|
|
27
|
+
// acceleration magnitude.
|
|
28
|
+
fn thz_power(p_ddot: Real, k_emit: Real) -> Real
|
|
29
|
+
where chain_order <= 0
|
|
30
|
+
{
|
|
31
|
+
k_emit * (p_ddot * p_ddot)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// At zero polarisation acceleration there is no emission. The
|
|
35
|
+
// witness for "no driver, no signal" -- the safety statement a
|
|
36
|
+
// receiver depends on.
|
|
37
|
+
@verify(lean, theorem = "ferron_emission_zero_drive_zero_power")
|
|
38
|
+
fn power_at_zero_drive(k_emit: Real) -> Real
|
|
39
|
+
where chain_order <= 0
|
|
40
|
+
ensures (result == 0.0)
|
|
41
|
+
{
|
|
42
|
+
k_emit * (0.0 * 0.0)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The emission constant is positive whenever the device is
|
|
46
|
+
// fabricated correctly. Used as the witness that scales the
|
|
47
|
+
// signal in the calibration code.
|
|
48
|
+
@verify(lean, theorem = "ferron_emit_constant_positive")
|
|
49
|
+
fn k_emit_certificate(k_emit: Real) -> Real
|
|
50
|
+
where chain_order <= 0
|
|
51
|
+
requires (k_emit > 0.0)
|
|
52
|
+
ensures (result > 0.0)
|
|
53
|
+
{
|
|
54
|
+
k_emit
|
|
55
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ferron_logic.eml -- TIER 6: FERRONICS, fourth demo.
|
|
2
|
+
//
|
|
3
|
+
// Ferronic logic gate concept. Two coherent polarisation pulses
|
|
4
|
+
// converge in a ferroelectric domain; their phase difference
|
|
5
|
+
// determines whether they constructively or destructively
|
|
6
|
+
// interfere at the output:
|
|
7
|
+
//
|
|
8
|
+
// P_out = 2 * P_0 * cos(d_phi / 2)
|
|
9
|
+
//
|
|
10
|
+
// At d_phi = 0 -> P_out = 2 * P_0 (logical 1)
|
|
11
|
+
// At d_phi = pi -> P_out = 0 (logical 0)
|
|
12
|
+
//
|
|
13
|
+
// IDENTICAL math to magnon_logic.eml and the photonic Mach-
|
|
14
|
+
// Zehnder. Three different physical carriers (polarisation
|
|
15
|
+
// vs spin precession vs photon field), one substrate-independent
|
|
16
|
+
// interference equation.
|
|
17
|
+
//
|
|
18
|
+
// Chain order 1.
|
|
19
|
+
|
|
20
|
+
module ferron_logic;
|
|
21
|
+
|
|
22
|
+
fn output_amplitude(p_0: Real, d_phi: Real) -> Real
|
|
23
|
+
where chain_order <= 1
|
|
24
|
+
{
|
|
25
|
+
2.0 * p_0 * cos(d_phi * 0.5)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Constructive interference at d_phi = 0 -> P_out = 2 * P_0.
|
|
29
|
+
@verify(lean, theorem = "ferron_logic_constructive_at_zero")
|
|
30
|
+
fn constructive_certificate(p_0: Real) -> Real
|
|
31
|
+
where chain_order <= 1
|
|
32
|
+
ensures (result == 2.0 * p_0)
|
|
33
|
+
{
|
|
34
|
+
2.0 * p_0 * cos(0.0)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Squared-amplitude lower bound: |P_out|^2 >= 0 for any inputs
|
|
38
|
+
// (energy non-negativity of the gate output).
|
|
39
|
+
@verify(lean, theorem = "ferron_logic_squared_amp_nonneg")
|
|
40
|
+
fn p_squared_witness(p_0: Real) -> Real
|
|
41
|
+
where chain_order <= 1
|
|
42
|
+
ensures (result == p_0 * p_0)
|
|
43
|
+
{
|
|
44
|
+
p_0 * p_0
|
|
45
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// ferron_propagation.eml -- TIER 6: FERRONICS (ferron carrier)
|
|
2
|
+
//
|
|
3
|
+
// Damped polarisation wave in a van der Waals ferroelectric --
|
|
4
|
+
// the carrier experimentally demonstrated for the first time in
|
|
5
|
+
// 2025:
|
|
6
|
+
//
|
|
7
|
+
// P(x, t) = P_0 * cos(k*x - omega*t) * exp(-x / xi)
|
|
8
|
+
//
|
|
9
|
+
// P_0 initial polarisation amplitude
|
|
10
|
+
// k wavenumber (1/m)
|
|
11
|
+
// omega angular frequency
|
|
12
|
+
// xi coherence length (m) -- the new, ferron-specific
|
|
13
|
+
// parameter that bounds how far a polarisation
|
|
14
|
+
// coherence survives in the ferroelectric lattice
|
|
15
|
+
//
|
|
16
|
+
// Chain order 1 (single envelope transcendental). The carrier
|
|
17
|
+
// is the ferron -- electric order itself. The same wave-equation
|
|
18
|
+
// shape that runs photonics, magnonics, and phononics also runs
|
|
19
|
+
// the polarisation field of a SnSe / SnS / In2Se3 thin film.
|
|
20
|
+
//
|
|
21
|
+
// Why this matters: the DARPA RFI asks for "materials where
|
|
22
|
+
// sensing, actuation, and computation are co-designed". The
|
|
23
|
+
// polarisation state IS the computation. No external processor.
|
|
24
|
+
|
|
25
|
+
module ferron_propagation;
|
|
26
|
+
|
|
27
|
+
const P0: Real = 1.0 // normalised polarisation amplitude
|
|
28
|
+
const XI: Real = 1.0e-6 // m, ~1 micron coherence in 2025 demos
|
|
29
|
+
|
|
30
|
+
// Polarisation amplitude at position x and time t.
|
|
31
|
+
fn polarization(x: Real, t: Real,
|
|
32
|
+
p0: Real, k: Real, omega: Real, xi: Real) -> Real
|
|
33
|
+
where chain_order <= 1
|
|
34
|
+
requires (xi > 0.0)
|
|
35
|
+
{
|
|
36
|
+
p0 * cos(k * x - omega * t) * exp(-x / xi)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Envelope only -- the exponentially-damped magnitude that
|
|
40
|
+
// bounds the absolute polarisation regardless of the cosine
|
|
41
|
+
// phase.
|
|
42
|
+
fn envelope(x: Real, p0: Real, xi: Real) -> Real
|
|
43
|
+
where chain_order <= 1
|
|
44
|
+
requires (xi > 0.0)
|
|
45
|
+
{
|
|
46
|
+
p0 * exp(-x / xi)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Mathematical certificates (Lean) ────────────────────────
|
|
50
|
+
|
|
51
|
+
// At the source point (x = 0, t = 0) the polarisation equals its
|
|
52
|
+
// initial amplitude. cos(0) = 1, exp(0) = 1, P_0 * 1 * 1 = P_0.
|
|
53
|
+
@verify(lean, theorem = "ferron_amplitude_at_origin")
|
|
54
|
+
fn polarization_at_origin(p0: Real) -> Real
|
|
55
|
+
where chain_order <= 1
|
|
56
|
+
ensures (result == p0)
|
|
57
|
+
{
|
|
58
|
+
p0 * cos(0.0) * exp(0.0)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The envelope at the source point equals the initial amplitude.
|
|
62
|
+
// exp(0) = 1 folds the body to p0.
|
|
63
|
+
@verify(lean, theorem = "ferron_envelope_at_origin")
|
|
64
|
+
fn envelope_at_origin(p0: Real) -> Real
|
|
65
|
+
where chain_order <= 1
|
|
66
|
+
ensures (result == p0)
|
|
67
|
+
{
|
|
68
|
+
p0 * exp(0.0)
|
|
69
|
+
}
|