Extends "conformance is a hash" from codecs (replicate) to GENERATION (create). Every other generative path (wai.semantic.*) is float + similarity-judged, so its output varies by hardware and can never go in a receipt. This is the opposite: a generator whose ENTIRE pipeline is integer/fixed-point, pinned to (model, prompt, seed, steps), so the GENERATED image is byte-identical on every machine — its hash is portable and receiptable even though there is no original. Generation-equivalence. - src/generate.rs (feature `generate`, pure i64, no float/ONNX, wasm-ready): fixed-point spectral synthesis — prompt+seed fold into a splitmix64 PRNG that draws K = base + steps·step integer sinusoid components (fx, fy, amp, phase); each pixel sums amp·sine_lut[(fx·x+fy·y+phase+colour_phase) mod T]; the field is normalized to [0,255] by integer min/max. The float sin() that bakes the LUT runs once at model-build (tools/wai_generate_model.py), NEVER at generation. - ReplicateClass::GenerativeDeterministic — the create-side quadrant, distinct from Behavioral (similarity, the semantic path) and the neural-decode classes. Wired wai.generate.spectral (Lossless tier, royalty-free) + SPEC §6 + capabilities table. - Committed pinned vector (tests/vectors/generate/): the model bundle + generate.json pin. NEVER-skip test: the pin reproduces fnv1a 10b690999f6c5616 byte-for-byte, regeneration is identical (determinism), and prompt/seed/steps each change the output (it is conditioned, not constant). examples/gen_preview.rs dumps a preview. Co-Authored-By: Claude Opus 4.8 <[email protected]>
50 lines
2.2 KiB
Rust
50 lines
2.2 KiB
Rust
//! `wai.generate.spectral` — **generation-equivalence**: a deterministic integer
|
|
//! generator produces byte-identical bytes on every machine from the same
|
|
//! `(model, prompt, seed, steps)`, so its hash is portable (the create-side of
|
|
//! "conformance is a hash"). Committed vector under `tests/vectors/generate/`;
|
|
//! never skips.
|
|
#![cfg(feature = "generate")]
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use wai::generate::{fnv1a, generate, SpectralModel};
|
|
|
|
fn dir() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/vectors/generate")
|
|
}
|
|
fn jload(name: &str) -> serde_json::Value {
|
|
serde_json::from_slice(&std::fs::read(dir().join(name)).unwrap()).unwrap()
|
|
}
|
|
fn hash(rgb: &[u8]) -> String {
|
|
format!("{:016x}", fnv1a(rgb))
|
|
}
|
|
|
|
#[test]
|
|
fn generation_is_deterministic_and_pinned() {
|
|
let m = SpectralModel::from_value(&jload("generate_spectral_model.json"));
|
|
let pin = jload("generate.json");
|
|
let (prompt, seed, steps, h, w) = (
|
|
pin["prompt"].as_str().unwrap(),
|
|
pin["seed"].as_u64().unwrap(),
|
|
pin["steps"].as_u64().unwrap() as usize,
|
|
pin["h"].as_u64().unwrap() as usize,
|
|
pin["w"].as_u64().unwrap() as usize,
|
|
);
|
|
|
|
// 1. PIN: the committed (prompt, seed, steps) reproduces the committed hash,
|
|
// byte-for-byte — the portable receipt of a generated image.
|
|
let rgb = generate(&m, prompt, seed, steps, h, w);
|
|
assert_eq!(rgb.len(), h * w * 3);
|
|
let got = hash(&rgb);
|
|
println!("generate.spectral: {h}x{w} '{prompt}' seed={seed} steps={steps} -> fnv1a {got}");
|
|
assert_eq!(got, pin["rgb_hash"].as_str().unwrap(), "pinned generation must reproduce the committed hash");
|
|
|
|
// 2. DETERMINISM: regenerating the same inputs is byte-identical.
|
|
assert_eq!(generate(&m, prompt, seed, steps, h, w), rgb, "regeneration must be byte-identical");
|
|
|
|
// 3. SENSITIVITY: a different prompt or seed draws a different image (the
|
|
// generation is conditioned, not constant).
|
|
assert_ne!(hash(&generate(&m, "a different prompt", seed, steps, h, w)), got, "prompt must change the output");
|
|
assert_ne!(hash(&generate(&m, prompt, seed ^ 1, steps, h, w)), got, "seed must change the output");
|
|
assert_ne!(hash(&generate(&m, prompt, seed, steps + 1, h, w)), got, "steps must change the output");
|
|
}
|