Trichromat with UV — pollinator vision
What does a bee see on a sunflower?
A UV cone at 350 nm, a blue cone at 440, a green cone at 540 — the bee's RGB is shifted into the ultraviolet. Flowers paint UV nectar guides we can't see; for the bee, every sunflower has a runway.
Three cones — UV at 350 nm, blue at 440, green at 540 — replace our RGB. The kernels above paint the same flower in both opponent spaces; the bee’s UV channel is mapped to violet on screen because there’s no honest way to display real UV on a human monitor.
The math behind it
Each function carries a @verify contract that compiles to a Lean theorem about its bounds. The same source compiles to C, Verilog, HLSL, and 28 more targets. View the experiment on GitHub →
bee_cones.emlchain 1module bee_cones;
// Three Gaussian cone sensitivities for Apis mellifera.
// Wavelengths in nanometres; sigma is FWHM/2.355 of the
// reported peak.
const LAMBDA_UV_NM: Real = 350.0
const LAMBDA_BLUE_NM: Real = 440.0
const LAMBDA_GREEN_NM:Real = 540.0
const SIGMA_NM: Real = 35.0
@verify(lean, theorem = "cone_response_in_unit_interval")
fn cone_response(lambda_nm: Real, lambda_peak_nm: Real) -> Real
requires (lambda_nm > 0.0)
ensures (0.0 <= result and result <= 1.0)
{
exp(-((lambda_nm - lambda_peak_nm) * (lambda_nm - lambda_peak_nm))
/ (2.0 * SIGMA_NM * SIGMA_NM))
}uv_excitation.emlchain 1module uv_excitation;
// "Nectar guide" excitation in the UV channel — the headline
// signature of bee vision. A typical sunflower has an outer-
// petal reflectance ≈ 0.05 in UV (low) and a centre-disc
// reflectance ≈ 0.30 (high), even though both look uniform
// yellow to us. The ratio is what the bee chases.
const SUNFLOWER_OUTER_UV_REFL: Real = 0.05
const SUNFLOWER_CENTRE_UV_REFL: Real = 0.30
const SUNFLOWER_OUTER_VIS_REFL: Real = 0.62 // human-yellow
const SUNFLOWER_CENTRE_VIS_REFL:Real = 0.62 // ~same in visible
@verify(lean, theorem = "uv_contrast_exceeds_visible_for_sunflower")
fn uv_to_visible_contrast_ratio() -> Real
ensures (result > 1.0)
{
((SUNFLOWER_CENTRE_UV_REFL - SUNFLOWER_OUTER_UV_REFL) /
(SUNFLOWER_OUTER_UV_REFL + 0.001))
/
((SUNFLOWER_CENTRE_VIS_REFL - SUNFLOWER_OUTER_VIS_REFL + 0.001) /
(SUNFLOWER_OUTER_VIS_REFL + 0.001))
}opponent_color.emlchain 1module opponent_color;
// Bee colour vision is opponent — a UV-vs-Blue+Green axis and a
// Blue-vs-Green axis come out of three cone responses, just as
// human L-M and S-(L+M) do for our three cones. The math is
// identical; only the cone peaks change.
@verify(lean, theorem = "opponent_uv_bg_in_signed_unit_interval")
fn opponent_uv_bg(uv: Real, blue: Real, green: Real) -> Real
requires (0.0 <= uv and uv <= 1.0 and
0.0 <= blue and blue <= 1.0 and
0.0 <= green and green <= 1.0)
ensures (-1.0 <= result and result <= 1.0)
{
clamp(uv - 0.5 * (blue + green), -1.0, 1.0)
}
@verify(lean, theorem = "opponent_b_g_in_signed_unit_interval")
fn opponent_b_g(blue: Real, green: Real) -> Real
requires (0.0 <= blue and blue <= 1.0 and
0.0 <= green and green <= 1.0)
ensures (-1.0 <= result and result <= 1.0)
{
clamp(blue - green, -1.0, 1.0)
}bee_color_chromaticity.emlchain 1module bee_color_chromaticity;
// Position on the bee's chromaticity diagram. A flower's (x, y)
// is its (opponent_uv_bg, opponent_b_g) coordinate; nearby
// points = colours the bee struggles to distinguish. The
// "perceptual distance" between two flowers is just Euclidean
// in this space.
@verify(lean, theorem = "perceptual_distance_nonneg")
fn perceptual_distance(x1: Real, y1: Real, x2: Real, y2: Real) -> Real
ensures (result >= 0.0)
{
sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
}Same EML pipeline. Different sense.
Eight more cross-species sensor models live alongside this one — bat sonar, owl 3D hearing, pit-viper infrared, dog olfaction, pigeon magnetoreception, shark electric sense, mantis shrimp 16-channel hyperspectral, spider web vibration.
See all nine senses →