open-standards/wai/wai-rs/src/int_world.rs
Transaction Science 24539d2be1 int_world gate 7 (WIP: committed before running): run_net, decode and the convolution dispatch leave production
With the world renderer through world/1, nothing in production calls
int_hyper_synth::run_net, decode, to_rgb8 or the fast/reference convolution
dispatch run_net used (wai-web included). They move into the test-only
int_hyper_synth::legacy, beside the guard: run_net and apply_layer on the
scalar kernels (legacy::reference), with no dispatch; decode and to_rgb8.
int_hyper_synth::kernel_equivalence goes with the dispatch it tested: its
convolution equivalences are int_ops' whm1_convolutions_equal_the_interpreter
(M4), its pad-factor test int_ops' padding_beyond_the_pad_factor_runs_on_the_
interpreter (gate 5), and its overflow and malformed-geometry cases cannot
reach a loaded graph.

The callers that were not production move to the lowering:
- wai_decode_bench: the int_hyper kernel cases, the g_s decoder case and the
  layer profile run whm1/1 graphs through int_ops, as the decoders do;
- tests/int_kernels_geometry.rs: the int_hyper edge geometry runs a WHM1
  layer through its lowering under every policy; strides past 32 bits and
  planes past the cap are refused, not panicked;
- tests/int_hyper_synth.rs: the pretrained synthesis and hyper-decoder hashes
  are checked through the lowering alone;
- int_mlicpp's and int_world's direct decodes take run_net, decode and
  to_rgb8 from legacy; docs in int_kernels and int_transformer.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 02:51:39 -04:00

474 lines
24 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Integer-exact INTERACTIVE WORLD MODEL — the byte-exact renderer for
//! `wai.world.model`: every frame is generated by integer arithmetic, so its hash is
//! the same on every machine (a float next-frame network's would not be).
//!
//! An action-conditioned next-frame model, fully integer:
//! ```text
//! frame_0 = g_s(z_0); z_{t+1} = clip( T( z_t ‖ action_t ) ); frame_{t+1} = g_s(z_{t+1})
//! ```
//! Both the transition `T` and the synthesis `g_s` are [`IntModel`]s (the int_hyper
//! WHM1 format); the action is folded in as `A` constant input planes, `A` being `T`'s
//! input channels less the latent's. The renderer runs through the integer operator set
//! (`extensions/integer-operators.md`): `T` and `g_s` lowered by `world/1` (§13.5,
//! informative) and loaded under `acc64` with their bounds certificates ([`ops`]), so
//! given `(T, g_s, z_0, action trace)` every frame is byte-identical on every machine,
//! and a value that could take a layer outside its exact integer range is refused
//! (`check` ranges) rather than computed. Paired with [`crate::world_model`]'s signed,
//! replay-equivalent action trace, both the trace AND the generated frames
//! hash-converge: a generative interactive world you can put in a receipt.
//!
//! The renderer is informative: `wai.world.model`'s payload is its action trace, and no
//! payload text yet binds a trace to a renderer's set. Its refusals ([`Refused`]) use the
//! integer payloads' codes: `model_mismatch` for a set that breaks `world/1`'s rules, a row
//! without one value per action plane, or a network that does not fit the latent;
//! `geometry_mismatch` for a latent that is not `C·lat·lat` values; `field_range` for a side
//! of 0, a move whose plane value leaves `i64`, or a value outside a `check` range;
//! `too_large` for a latent past `2^26` values.
use crate::int_hyper_synth::{fnv1a, IntModel, Refused};
pub use ops::{load, World};
/// The reference action set (must match the export tool): stay, +x, −x, +y, −y.
/// `(dx, dy)` are folded into the transition as constant planes (scaled to `2^afrac`).
pub const ACTIONS: [(i64, i64); 5] = [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)];
/// Number of action planes appended to the latent for the transition (dx, dy, btn).
pub const A: usize = 3;
/// A decoded interactive clip: one HWC `u8` RGB frame per (initial + per-action) step.
pub struct WorldClip {
pub frames: Vec<Vec<u8>>,
pub frame_px: usize,
}
impl WorldClip {
/// FNV-1a over one frame's RGB bytes — the per-frame portable hash.
pub fn frame_hash(&self, i: usize) -> u64 {
fnv1a(&self.frames[i])
}
/// The clip hash = FNV-1a over the concatenated per-frame hash bytes.
pub fn clip_hash(&self) -> u64 {
let mut all = Vec::with_capacity(self.frames.len() * 8);
for f in &self.frames {
all.extend_from_slice(&fnv1a(f).to_be_bytes());
}
fnv1a(&all)
}
}
/// Render a clip from rows of action plane values at `2^afrac`, one value per plane of
/// `T` (§13.5's driver): the set loaded ([`load`]), then [`World::decode_planes`]. A
/// merged multi-actor log maps to one row per op (the acting agent's planes filled,
/// the others 0), so participants co-driving one world render the same frames.
pub fn decode_planes(t_model: &IntModel, gs_model: &IntModel, z0: &[i64], rows: &[Vec<i64>], c: usize, lat: usize) -> Result<WorldClip, Refused> {
ops::decode_planes(t_model, gs_model, z0, rows, c, lat)
}
/// Render a clip from a single agent's moves `(dx, dy)` ([`World::decode_deltas`]): the
/// path of a merged, canonically ordered multi-actor log from
/// [`crate::world_model::replay`], which renders to one byte-exact frame sequence on every
/// machine, whatever order the ops arrived in.
pub fn decode_deltas(t_model: &IntModel, gs_model: &IntModel, z0: &[i64], deltas: &[(i64, i64)], c: usize, lat: usize) -> Result<WorldClip, Refused> {
ops::decode_deltas(t_model, gs_model, z0, deltas, c, lat)
}
/// Render a clip from indices into [`ACTIONS`] (past its end, the last).
pub fn decode(t_model: &IntModel, gs_model: &IntModel, z0: &[i64], actions: &[usize], c: usize, lat: usize) -> Result<WorldClip, Refused> {
ops::decode(t_model, gs_model, z0, actions, c, lat)
}
/// The direct renderer, as `wai.world.model`'s integer renderer ran before it rendered
/// through the integer operator set: `T` and `g_s` run layer by layer by
/// [`crate::int_hyper_synth::legacy::run_net`], with no check of the values they are given.
/// Test-only (feature `legacy_int`): the tests hold the renderer to it, and the two differ
/// only on the planned breaks of `extensions/integer-operators.md` §15.3.
#[cfg(any(test, feature = "legacy_int"))]
pub mod legacy {
use super::*;
use crate::int_hyper_synth::legacy::{decode as synth_decode, run_net};
/// One integer transition step from an explicit set of action **planes**:
/// `z_{t+1} = clip( T( z_t ‖ plane₀ ‖ … ‖ plane_{A-1} ) )`. Each `planes_q[k]` is a
/// constant value (already at `2^afrac`) broadcast to one `lat·lat` input plane, so
/// the number of planes (`A`) is whatever the model expects — `A=3` for a single
/// agent (dx, dy, btn), `A=4` for two agents (dxA, dyA, dxB, dyB), etc. The next
/// latent is clamped to `[-2^afrac, 2^afrac]` (the bound the model trained under).
/// The generalized core every higher-level step path calls.
pub fn step_planes(t_model: &IntModel, z: &[i64], planes_q: &[i64], c: usize, lat: usize) -> Vec<i64> {
let one = 1i64 << t_model.afrac;
let plane = lat * lat;
let na = planes_q.len();
let mut inp = Vec::with_capacity((c + na) * plane);
inp.extend_from_slice(z);
for &v in planes_q {
inp.extend(std::iter::repeat_n(v, plane));
}
let (mut zn, _, _, _) = run_net(t_model, &inp, c + na, lat, lat);
for v in &mut zn {
*v = (*v).clamp(-one, one);
}
zn
}
/// One integer transition step from raw `(dx, dy)` deltas (single agent, `A=3`).
pub fn step_delta(t_model: &IntModel, z: &[i64], dx: i64, dy: i64, c: usize, lat: usize) -> Vec<i64> {
let one = 1i64 << t_model.afrac;
step_planes(t_model, z, &[dx * one, dy * one, 0], c, lat)
}
/// One integer transition step from an action index into [`ACTIONS`].
pub fn step(t_model: &IntModel, z: &[i64], action: usize, c: usize, lat: usize) -> Vec<i64> {
let (dx, dy) = ACTIONS[action.min(ACTIONS.len() - 1)];
step_delta(t_model, z, dx, dy, c, lat)
}
/// Decode an interactive world clip from a list of `(dx, dy)` deltas: from `z0`,
/// render `g_s(z0)`, then for each delta advance the integer transition and render.
/// The convergence path — a **merged, canonically-ordered multi-actor** action log
/// (from [`crate::world_model::replay`]) decodes to one byte-exact frame sequence on
/// every machine, regardless of network arrival order. Pure i64.
pub fn decode_deltas(t_model: &IntModel, gs_model: &IntModel, z0: &[i64], deltas: &[(i64, i64)], c: usize, lat: usize) -> WorldClip {
let mut z = z0.to_vec();
let (f0, frame_px, _) = synth_decode(gs_model, &z, c, lat, lat);
let mut frames = Vec::with_capacity(deltas.len() + 1);
frames.push(f0);
for &(dx, dy) in deltas {
z = step_delta(t_model, &z, dx, dy, c, lat);
let (f, _, _) = synth_decode(gs_model, &z, c, lat, lat);
frames.push(f);
}
WorldClip { frames, frame_px }
}
/// Decode an interactive world clip from an explicit sequence of action **plane**
/// rows (each row `planes_q` at `2^afrac`, length = the model's `A`). The general
/// path — single- or multi-agent. A merged multi-actor log maps to one plane row per
/// op (the acting agent's slot filled, others zero), so two participants co-drive one
/// world and every peer renders the IDENTICAL frames. Pure i64.
pub fn decode_planes(t_model: &IntModel, gs_model: &IntModel, z0: &[i64], rows: &[Vec<i64>], c: usize, lat: usize) -> WorldClip {
let mut z = z0.to_vec();
let (f0, frame_px, _) = synth_decode(gs_model, &z, c, lat, lat);
let mut frames = Vec::with_capacity(rows.len() + 1);
frames.push(f0);
for row in rows {
z = step_planes(t_model, &z, row, c, lat);
let (f, _, _) = synth_decode(gs_model, &z, c, lat, lat);
frames.push(f);
}
WorldClip { frames, frame_px }
}
/// Decode an interactive world clip from action indices (single-actor convenience).
pub fn decode(t_model: &IntModel, gs_model: &IntModel, z0: &[i64], actions: &[usize], c: usize, lat: usize) -> WorldClip {
let deltas: Vec<(i64, i64)> = actions.iter().map(|&a| ACTIONS[a.min(ACTIONS.len() - 1)]).collect();
decode_deltas(t_model, gs_model, z0, &deltas, c, lat)
}
/// The direct renderer with the direct guard the other integer decoders ran
/// ([`crate::int_hyper_synth::legacy::run_net_checked`]): before each layer of `T` and
/// of `g_s`, the values present are checked against the bounds of that layer's
/// arithmetic, and a layer that could leave the exact integer range is not run. The
/// direct renderer itself never checked; this is what the `check` ranges of `world/1`
/// restate (`extensions/integer-operators.md` §13.2), and what the tests hold the
/// renderer through the operator set to. Test-only (`legacy_int`).
#[cfg(any(test, feature = "legacy_int"))]
pub mod checked {
use super::*;
use crate::int_hyper_synth::legacy::run_net_checked;
use crate::int_hyper_synth::legacy::to_rgb8;
/// Where the guard refused: the network (`"T"` or `"g_s"`), the step (0 for frame
/// 0, `k` for the `k`-th row's transition and the frame after it), the layer whose
/// check failed, and whether that check was about the values (`out_of_range`) or
/// about the layer itself.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Refusal {
pub net: &'static str,
pub step: usize,
pub layer: usize,
pub out_of_range: bool,
}
/// [`super::decode_planes`], with each network run by `run_net_checked`.
pub fn decode_planes(t_model: &IntModel, gs_model: &IntModel, z0: &[i64], rows: &[Vec<i64>], c: usize, lat: usize) -> Result<WorldClip, Refusal> {
let at = |net, step| move |r: crate::int_hyper_synth::NetRefusal| Refusal { net, step, layer: r.layer, out_of_range: r.out_of_range };
let render = |z: &[i64], step: usize| -> Result<(Vec<u8>, usize), Refusal> {
let (x, oc, oh, ow) = run_net_checked(gs_model, z, c, lat, lat).map_err(at("g_s", step))?;
Ok((to_rgb8(&x, oc, oh, ow, gs_model.afrac), oh))
};
let one = 1i64 << t_model.afrac;
let plane = lat * lat;
let mut z = z0.to_vec();
let (f0, frame_px) = render(&z, 0)?;
let mut frames = vec![f0];
for (k, row) in rows.iter().enumerate() {
let mut inp = z;
for &v in row {
inp.extend(std::iter::repeat_n(v, plane));
}
let (mut zn, ..) = run_net_checked(t_model, &inp, c + row.len(), lat, lat).map_err(at("T", k + 1))?;
for v in &mut zn {
*v = (*v).clamp(-one, one);
}
z = zn;
frames.push(render(&z, k + 1)?.0);
}
Ok(WorldClip { frames, frame_px })
}
}
}
/// The renderer through the integer operator set (`extensions/integer-operators.md`):
/// the transition `T` and the synthesis `g_s` lowered by `world/1` (§13.5, informative;
/// §13.2 for each network's layers), each loaded under `acc64` with its bounds
/// certificate ([`crate::int_ops`]).
pub mod ops {
use super::*;
use crate::int_hyper_synth::{refused, RefusalKind, Refused, MAX_TENSOR};
use wai_ops::{Code, Graph, Loaded, Refusal, Tensor};
/// A refusal of the operator set as the renderer reports it: a value outside a `check`
/// range is `field_range`; the other codes keep their names (integer-payloads §8).
fn kind(r: Refusal, what: &'static str) -> Refused {
let k = match r.code {
Code::FieldRange => RefusalKind::FieldRange,
Code::TooLarge => RefusalKind::TooLarge,
Code::GeometryMismatch => RefusalKind::GeometryMismatch,
Code::StreamExhausted => RefusalKind::StreamExhausted,
Code::StreamNoncanonical => RefusalKind::StreamNoncanonical,
Code::ModelMismatch => RefusalKind::ModelMismatch,
};
refused(k, what)
}
const SET: &str = "T and g_s do not lower by world/1 to integer-operator graphs that load under acc64";
/// Both networks in the form `wai-ops` lowers them from ([`crate::int_ops::whm1_net`]),
/// and `C` as the lowering takes it.
fn nets(t: &IntModel, g_s: &IntModel, c: usize) -> Result<(wai_ops::lower::Net, wai_ops::lower::Net, u32), Refused> {
let c = u32::try_from(c).map_err(|_| refused(RefusalKind::ModelMismatch, SET))?;
let tn = crate::int_ops::whm1_net(t).map_err(|r| kind(r, SET))?;
Ok((tn, crate::int_ops::whm1_net(g_s).map_err(|r| kind(r, SET))?, c))
}
/// The set's two graphs, `T` then `g_s` (with the RGB8 step), as `world/1` writes them
/// from the models this crate parsed, for a latent of `c` channels
/// ([`wai_ops::lower::world_net`]).
pub fn lower(t: &IntModel, g_s: &IntModel, c: usize) -> Result<(Graph, Graph), Refused> {
let (tn, gn, c) = nets(t, g_s, c)?;
let mut roles = wai_ops::lower::world_net(&tn, &gn, c).map_err(|r| kind(r, SET))?.roles.into_iter().map(|(_, g)| g);
match (roles.next(), roles.next()) {
(Some(tg), Some(gg)) => Ok((tg, gg)),
_ => Err(refused(RefusalKind::ModelMismatch, SET)),
}
}
/// The set as the renderer holds it: `T` and `g_s` lowered by `world/1` and loaded
/// under `acc64`, with the latent's channel count `C` and the number of action planes
/// `A`, which is `T`'s input channel count less `C` (§13.5).
pub struct World {
pub(super) t: Loaded,
pub(super) g_s: Loaded,
pub(super) c: usize,
pub(super) a: usize,
pub(super) afrac: u32,
}
impl World {
/// `T`'s graph, loaded.
pub fn t(&self) -> &Loaded {
&self.t
}
/// `g_s`'s graph (ending with the RGB8 step), loaded.
pub fn g_s(&self) -> &Loaded {
&self.g_s
}
/// `C`, the latent's channel count.
pub fn channels(&self) -> usize {
self.c
}
/// `A`, the action planes a row carries: `T`'s input channels less `C`.
pub fn planes(&self) -> usize {
self.a
}
/// The fixed-point scale both networks share.
pub fn afrac(&self) -> u32 {
self.afrac
}
/// A row carries one value per action plane (§13.5's load checks, `model_mismatch`).
fn row_ok(&self, row: &[i64]) -> Result<(), Refused> {
if row.len() != self.a {
return Err(refused(RefusalKind::ModelMismatch, "a row does not carry one value per action plane of T"));
}
Ok(())
}
/// The elements of a latent `[C, lat, lat]`: a side of 0 is `field_range`, and a
/// latent past [`MAX_TENSOR`] elements `too_large`.
fn latent_len(&self, lat: usize) -> Result<usize, Refused> {
if lat == 0 {
return Err(refused(RefusalKind::FieldRange, "the latent's side is zero"));
}
let n = self.c.checked_mul(lat).and_then(|v| v.checked_mul(lat)).filter(|&n| n <= MAX_TENSOR);
n.ok_or(refused(RefusalKind::TooLarge, "the latent exceeds 2^26 elements"))
}
/// The latent `z` as a tensor `[C, lat, lat]` ([`World::latent_len`]); `z` of
/// another length is `geometry_mismatch`.
fn latent(&self, z: &[i64], lat: usize) -> Result<Tensor, Refused> {
if z.len() != self.latent_len(lat)? {
return Err(refused(RefusalKind::GeometryMismatch, "the latent is not C·lat·lat values"));
}
// C and lat are within 2^26, so each fits u32.
Ok(Tensor::new(self.c as u32, lat as u32, lat as u32, z.to_vec()))
}
/// The frame a latent `[C, lat, lat]` gives, `(channels, h, w)`, planned before
/// any value is read (`extensions/integer-operators.md` §4): `T` planned on that
/// shape must give it back, so that every transition reads what the one before it
/// wrote; then `g_s` must plan on it. A network that does not (it reads another
/// channel count, a size passes a cap, or a layer leaves no output) does not fit the
/// latent: `model_mismatch`, as a set that does not fit a payload's shapes is.
pub fn plan(&self, lat: usize) -> Result<(usize, usize, usize), Refused> {
use wai_ops::plan::run_plan;
self.latent_len(lat)?;
let shape = [(self.c as u64, lat as u64, lat as u64)];
let t = self.t.graph();
let misfit = |what| move |_| refused(RefusalKind::ModelMismatch, what);
let hw = run_plan(t, &shape, &vec![0; self.a]).map_err(misfit("T does not plan on the latent's shape"))?;
let o = t.outputs[0] as usize;
if (t.tensors[o].channels as u64, hw[o].0, hw[o].1) != shape[0] {
return Err(refused(RefusalKind::ModelMismatch, "T does not give the latent's shape back"));
}
let g = self.g_s.graph();
let hw = run_plan(g, &shape, &[]).map_err(misfit("g_s does not plan on the latent's shape"))?;
let o = g.outputs[0] as usize;
Ok((g.tensors[o].channels as usize, hw[o].0 as usize, hw[o].1 as usize))
}
/// `T(z)` with `row`'s values as its scalars, then the clamp to `[−2^afrac,
/// 2^afrac]` that ends `T`'s graph. A value outside a `check` range is `field_range`.
fn run_t(&self, z: Tensor, row: &[i64]) -> Result<Tensor, Refused> {
let shape = (z.c, z.h, z.w);
let out = crate::int_ops::run1(&self.t, z, row).map_err(|r| kind(r, "a latent or plane value is outside a check range of T"))?;
if (out.c, out.h, out.w) != shape {
return Err(refused(RefusalKind::ModelMismatch, "T does not give the latent's shape back"));
}
Ok(out)
}
/// `g_s(z)`, which ends with the RGB8 step, as row-major HWC bytes, with the
/// frame's height and width. A value outside a `check` range is `field_range`.
fn run_g(&self, z: Tensor) -> Result<(Vec<u8>, usize, usize), Refused> {
let out = crate::int_ops::run1(&self.g_s, z, &[]).map_err(|r| kind(r, "a latent value is outside a check range of g_s"))?;
let (c, h, w) = (out.c as usize, out.h as usize, out.w as usize);
let plane = h * w;
let mut hwc = vec![0u8; c * plane];
for (k, &v) in out.data.iter().enumerate() {
hwc[(k % plane) * c + k / plane] = v as u8;
}
Ok((hwc, h, w))
}
/// One transition: `z` (`[C, lat, lat]`) becomes `T(z)`, with `row`'s values, one per
/// action plane at `2^afrac`, as `T`'s scalars (§13.5).
pub fn step(&self, z: &[i64], row: &[i64], lat: usize) -> Result<Vec<i64>, Refused> {
self.row_ok(row)?;
let x = self.latent(z, lat)?;
Ok(self.run_t(x, row)?.data)
}
/// [`World::step`] for a single agent's move `(dx, dy)` ([`World::move_row`]).
pub fn step_delta(&self, z: &[i64], dx: i64, dy: i64, lat: usize) -> Result<Vec<i64>, Refused> {
let row = self.move_row(dx, dy)?;
self.step(z, &row, lat)
}
/// [`World::step_delta`] for an index into [`ACTIONS`] (past its end, the last).
pub fn step_action(&self, z: &[i64], action: usize, lat: usize) -> Result<Vec<i64>, Refused> {
let (dx, dy) = ACTIONS[action.min(ACTIONS.len() - 1)];
self.step_delta(z, dx, dy, lat)
}
/// One frame: `g_s(z)` as row-major HWC bytes, with its height and width.
pub fn render(&self, z: &[i64], lat: usize) -> Result<(Vec<u8>, usize, usize), Refused> {
let x = self.latent(z, lat)?;
self.run_g(x)
}
/// The row of a single agent's move `(dx, dy)` (§13.5): `dequantize(dx, afrac, 0)`,
/// `dequantize(dy, afrac, 0)` and 0. A set whose `A` is not 3 takes no such row
/// (`model_mismatch`), and a move whose value leaves `i64` is `field_range`.
pub fn move_row(&self, dx: i64, dy: i64) -> Result<Vec<i64>, Refused> {
if self.a != 3 {
return Err(refused(RefusalKind::ModelMismatch, "a single agent's move is a row of three planes, and T takes another count"));
}
let q = |d: i64| wai_ops::stage::dequantize(d, self.afrac, 0).map_err(|_| refused(RefusalKind::FieldRange, "a move's plane value leaves i64"));
Ok(vec![q(dx)?, q(dy)?, 0])
}
/// A clip (§13.5's driver): frame 0 is `g_s(z_0)`; for each row in order, `z`
/// becomes `T(z)` with the row's values as the scalars, and the next frame is
/// `g_s(z)`. Before the first frame: every row carries `A` values
/// (`model_mismatch`), `z_0` is `[C, lat, lat]`, and both graphs plan on it
/// ([`World::plan`]).
pub fn decode_planes(&self, z0: &[i64], rows: &[Vec<i64>], lat: usize) -> Result<WorldClip, Refused> {
for row in rows {
self.row_ok(row)?;
}
let mut z = self.latent(z0, lat)?;
self.plan(lat)?;
let (mut frames, mut frame_px) = (Vec::with_capacity(rows.len() + 1), 0);
for k in 0..=rows.len() {
if k > 0 {
z = self.run_t(z, &rows[k - 1])?;
}
// The last frame takes the latent, which no transition reads after it.
let x = if k == rows.len() { std::mem::replace(&mut z, Tensor::new(0, 0, 0, Vec::new())) } else { z.clone() };
let (f, h, _) = self.run_g(x)?;
if k == 0 {
frame_px = h;
}
frames.push(f);
}
Ok(WorldClip { frames, frame_px })
}
/// [`World::decode_planes`] for a single agent's moves, each a row
/// ([`World::move_row`]).
pub fn decode_deltas(&self, z0: &[i64], deltas: &[(i64, i64)], lat: usize) -> Result<WorldClip, Refused> {
let rows = deltas.iter().map(|&(dx, dy)| self.move_row(dx, dy)).collect::<Result<Vec<_>, _>>()?;
self.decode_planes(z0, &rows, lat)
}
}
/// [`World::decode_planes`] of the set `T`, `g_s` for a latent of `c` channels, loaded
/// first ([`load`]).
pub fn decode_planes(t: &IntModel, g_s: &IntModel, z0: &[i64], rows: &[Vec<i64>], c: usize, lat: usize) -> Result<WorldClip, Refused> {
load(t, g_s, c)?.decode_planes(z0, rows, lat)
}
/// [`World::decode_deltas`] of the set, loaded first ([`load`]).
pub fn decode_deltas(t: &IntModel, g_s: &IntModel, z0: &[i64], deltas: &[(i64, i64)], c: usize, lat: usize) -> Result<WorldClip, Refused> {
load(t, g_s, c)?.decode_deltas(z0, deltas, lat)
}
/// [`decode_deltas`] for indices into [`ACTIONS`] (past its end, the last).
pub fn decode(t: &IntModel, g_s: &IntModel, z0: &[i64], actions: &[usize], c: usize, lat: usize) -> Result<WorldClip, Refused> {
let deltas: Vec<(i64, i64)> = actions.iter().map(|&a| ACTIONS[a.min(ACTIONS.len() - 1)]).collect();
decode_deltas(t, g_s, z0, &deltas, c, lat)
}
/// The load checks of `world/1` (§13.5, `model_mismatch`): both networks' `afrac` in
/// `1..=30` and equal, `A ≥ 1`, and each network's rules (§13.2); then `T`, then `g_s`,
/// lowered and loaded under `acc64` ([`wai_ops::lower::world_net_loaded`]: the result of
/// loading the lowered graphs' bytes, without writing and parsing them again).
pub fn load(t: &IntModel, g_s: &IntModel, c: usize) -> Result<World, Refused> {
let (tn, gn, c32) = nets(t, g_s, c)?;
let (tl, gl) = wai_ops::lower::world_net_loaded(tn, gn, c32, crate::int_ops::PROFILE).map_err(|r| kind(r, SET))?;
let a = tl.graph().scalars.len();
Ok(World { t: tl, g_s: gl, c, a, afrac: t.afrac })
}
}