Research-Stack/0-Core-Formalism/lean/external/OTOM/LaviGen.lean

373 lines
17 KiB
Text
Raw 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.

/- Copyright (c) 2026 Sovereign Research Stack. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Research Stack Team
LaviGen.lean — Autoregressive 3D Layout Generation with Dual-Guidance Self-Rollout
This module formalizes LaviGen from "Repurposing 3D Generative Model for
Autoregressive Layout Generation" (arXiv:2604.16299, 2026).
Key contributions:
1. Autoregressive layout generation in native 3D space (not from text)
2. Flow Matching for 3D structure generation
3. Self-Rollout Distillation: Student conditions on own predictions
4. Dual-Guidance: Holistic (scene-level) + Step-wise (per-object) teachers
5. Identity-Aware RoPE: Extended positional embedding with source identity
Per AGENTS.md §1.4: Uses Q16_16 fixed-point for hardware-native computation.
Per AGENTS.md §2: PascalCase types, camelCase functions.
Per AGENTS.md §4: All defs must have eval witnesses or theorems.
Reference: https://alphaxiv.org/abs/2604.16299
-/
import Mathlib.Data.Nat.Basic
import Mathlib.Data.Real.Basic
import Mathlib.Data.Fin.Basic
import Mathlib.Data.Complex.Basic
namespace Semantics.LaviGen
-- ════════════════════════════════════════════════════════════
-- §0 Fixed-Point Precision (Q16.16 for 3D coordinates)
-- ════════════════════════════════════════════════════════════
/-- Q16.16 fixed-point for 3D layout computations. -/
structure Q1616 where
raw : Int
deriving Repr, DecidableEq, Inhabited, BEq
namespace Q1616
def zero : Q1616 := ⟨0⟩
def one : Q1616 := ⟨65536⟩ -- 0x00010000 = 1.0
def epsilon : Q1616 := ⟨1⟩ -- 2^{-16}
def ofNat (n : Nat) : Q1616 := ⟨n * 65536⟩
def ofFloat (f : Float) : Q1616 := ⟨f.toUInt64.toNat⟩
def add (a b : Q1616) : Q1616 := ⟨a.raw + b.raw⟩
def sub (a b : Q1616) : Q1616 := ⟨a.raw - b.raw⟩
def mul (a b : Q1616) : Q1616 := ⟨(a.raw * b.raw) / 65536⟩
def div (a b : Q1616) : Q1616 := ⟨(a.raw * 65536) / b.raw⟩
def neg (a : Q1616) : Q1616 := ⟨-a.raw⟩
def abs (a : Q1616) : Q1616 := if a.raw < 0 then ⟨-a.raw⟩ else a
instance : Add Q1616 := ⟨add⟩
instance : Sub Q1616 := ⟨sub⟩
instance : Mul Q1616 := ⟨mul⟩
instance : Div Q1616 := ⟨div⟩
instance : Neg Q1616 := ⟨neg⟩
instance : LE Q1616 := ⟨fun a b => a.raw ≤ b.raw⟩
instance : LT Q1616 := ⟨fun a b => a.raw < b.raw⟩
/-- Linear interpolation: (1-t)·a + t·b -/
def lerp (a b t : Q1616) : Q1616 :=
let oneMinusT := one - t
(oneMinusT * a) + (t * b)
/-- Clip to [0, 1] range. -/
def clip01 (x : Q1616) : Q1616 :=
if x.raw < 0 then zero
else if x.raw > 65536 then one
else x
end Q1616
-- ════════════════════════════════════════════════════════════
-- §1 3D Voxel Grid and Structured Representation
-- ════════════════════════════════════════════════════════════
/-- 3D voxel grid dimensions (H × W × L). -/
structure VoxelGrid where
height : Nat
width : Nat
length : Nat
deriving Repr, Inhabited
/-- Voxel position in 3D space. -/
structure VoxelPos where
h : Nat -- height index
w : Nat -- width index
l : Nat -- length index
deriving Repr, Inhabited, DecidableEq
/-- Local latent code attached to voxel p ∈ 𝒫.
From paper: z_p ∈ ^d where 𝒫 = active voxel positions. -/
structure LocalLatent (d : Nat) where
position : VoxelPos
code : Array Q1616 -- d-dimensional latent code
wf : code.size = d
deriving Repr
/-- 3D asset as set of voxel-indexed local latents.
Paper: Asset = {z_p | p ∈ 𝒫} where 𝒫 near object surface. -/
structure StructuredAsset (d : Nat) where
latents : Array (LocalLatent d)
deriving Repr
-- ════════════════════════════════════════════════════════════
-- §2 Flow Matching for 3D Generation (Section 3.1)
-- ════════════════════════════════════════════════════════════
/-- Flow Matching time step t ∈ [0, 1]. -/
abbrev FlowTime := Q1616
/-- Clean data sample x_0. -/
def CleanSample (_d : Nat) := Array Q1616
/-- Noise sample ε ~ N(0, I). -/
def NoiseSample (_d : Nat) := Array Q1616
/-- Perturbed sample at time t.
Paper Eq: x(t) = (1-t)·x_0 + t·ε -/
def flowMatchingPerturb {d : Nat} (x0 : CleanSample d) (ε : NoiseSample d)
(t : FlowTime) : Array Q1616 :=
Array.zipWith (fun x0i εi => Q1616.lerp x0i εi t) x0 ε
/-- Time-dependent vector field v(x, t) = ∇_t x.
Learned via neural approximation v_θ. -/
structure VectorField (d : Nat) where
-- Neural network output: predicts dx/dt at position x, time t
evaluate : Array Q1616 → FlowTime → Array Q1616
/-- Flow Matching loss: ||v_θ(x(t), t) - (ε - x_0)||². -/
def flowMatchingLoss {d : Nat} (vθ : VectorField d) (x0 : CleanSample d)
(ε : NoiseSample d) (t : FlowTime) : Q1616 :=
let xt := flowMatchingPerturb x0 ε t
let vPred := vθ.evaluate xt t
let target := Array.zipWith (fun εi x0i => εi - x0i) ε x0
let diff := Array.zipWith (fun a b => a - b) vPred target
let sqErr := diff.foldl (fun acc v => acc + (v * v)) Q1616.zero
sqErr / Q1616.ofNat d
-- ════════════════════════════════════════════════════════════
-- §3 Autoregressive Layout Generation (Section 3.2)
-- ════════════════════════════════════════════════════════════
/-- Layout step index i ∈ {0, 1, ..., n-1}. -/
abbrev LayoutStep := Nat
/-- Scene state S_i at step i (cumulative encoding of placed objects). -/
structure SceneState (grid : VoxelGrid) (d : Nat) where
step : LayoutStep
occupancy : Array Bool -- Sparse voxel occupancy grid
latents : Array (LocalLatent d)
deriving Repr
/-- Target object O_i to place at step i. -/
structure TargetObject (d : Nat) where
objectId : Nat
latent : LocalLatent d
category : String
deriving Repr
/-- Layout instruction (textual conditioning). -/
abbrev LayoutInstruction := String
/-- Conditioning vector c (encoded from layout instruction). -/
structure ConditioningVector (d : Nat) where
data : Array Q1616
wf : data.size = d
deriving Repr
/-- Autoregressive generation: S_{i+1} = G_θ(S_i, O_i, c). -/
def autoregressiveStep {grid : VoxelGrid} {d : Nat}
(Si : SceneState grid d) (Oi : TargetObject d) (c : ConditioningVector d)
(Gθ : SceneState grid d → TargetObject d → ConditioningVector d → SceneState grid d)
: SceneState grid d :=
Gθ Si Oi c
/-- Full autoregressive rollout: S_0 → S_1 → ... → S_n. -/
def autoregressiveRollout {grid : VoxelGrid} {d : Nat}
(S0 : SceneState grid d) (objects : List (TargetObject d))
(c : ConditioningVector d)
(Gθ : SceneState grid d → TargetObject d → ConditioningVector d → SceneState grid d)
: List (SceneState grid d) :=
objects.scanl (fun Si Oi => autoregressiveStep Si Oi c Gθ) S0
-- ════════════════════════════════════════════════════════════
-- §4 Self-Rollout Distillation (Section 3.4)
-- ════════════════════════════════════════════════════════════
/-- Student model G_θ with self-rollout capability. -/
structure StudentModel (grid : VoxelGrid) (d : Nat) where
generate : SceneState grid d → TargetObject d → ConditioningVector d → SceneState grid d
-- Self-rollout: conditions on own generated layout
selfRollout : SceneState grid d → List (TargetObject d) → ConditioningVector d
→ List (SceneState grid d)
/-- Distribution Matching Distillation loss _DM.
Minimizes reverse KL divergence via score distillation. -/
structure DistillationLoss where
loss : Q1616
criticScore : Q1616 -- Learned critic approximating student distribution
deriving Repr, Inhabited
/-- Self-Rollout Mechanism (vs Teacher Forcing).
Teacher Forcing: S_i conditions on ground-truth S_{i-1}
Self-Rollout: S_i^θ conditions on own S_{i-1}^θ -/
def selfRolloutStep {grid : VoxelGrid} {d : Nat}
(student : StudentModel grid d) (prevState : SceneState grid d)
(Oi : TargetObject d) (c : ConditioningVector d)
: SceneState grid d :=
student.generate prevState Oi c
/-- Exposure bias mitigation via self-rollout.
Paper: Student encounters and learns to recover from its own errors. -/
def selfRolloutSequence {grid : VoxelGrid} {d : Nat}
(student : StudentModel grid d) (S0 : SceneState grid d)
(objects : List (TargetObject d)) (c : ConditioningVector d)
: List (SceneState grid d) :=
objects.scanl (fun Si Oi => selfRolloutStep student Si Oi c) S0
-- ════════════════════════════════════════════════════════════
-- §5 Dual-Guidance Teachers (Section 3.4)
-- ════════════════════════════════════════════════════════════
/-- Holistic Teacher p_{𝒯_S}: Global planner (bidirectional base model).
Provides scene-level supervision on final state S_n^θ. -/
structure HolisticTeacher (grid : VoxelGrid) (d : Nat) where
-- Score scene quality conditioned on text c
score : SceneState grid d → ConditioningVector d → Q1616
-- Bidirectional: considers all objects {O_i}_{i=1}^n
plan : List (TargetObject d) → ConditioningVector d → SceneState grid d
/-- Step-Wise Teacher p_{𝒯_P}: Causal autoregressive model.
Provides per-object corrective signals at each step. -/
structure StepWiseTeacher (grid : VoxelGrid) (d : Nat) where
-- Conditioned on specific object O_i
scoreStep : SceneState grid d → TargetObject d → ConditioningVector d → LayoutStep → Q1616
-- Dense, object-aware supervision
correct : SceneState grid d → TargetObject d → ConditioningVector d → SceneState grid d
/-- Dual-Guidance objective (equal weights λ = 0.5 each).
Paper: Combines holistic + step-wise terms. -/
def dualGuidanceObjective {grid : VoxelGrid} {d : Nat}
(holistic : HolisticTeacher grid d) (stepwise : StepWiseTeacher grid d)
(states : List (SceneState grid d)) (objects : List (TargetObject d))
(c : ConditioningVector d) : Q1616 :=
let Sn := match states.getLast? with
| some s => s
| none => { step := 0, occupancy := #[], latents := #[] }
let holisticTerm := holistic.score Sn c
let stepwiseTerm := states.zip objects |>.foldl (fun acc (Si, Oi) =>
let stepIdx := Si.step
acc + stepwise.scoreStep Si Oi c stepIdx) Q1616.zero
-- Equal weights: 0.5·holistic + 0.5·stepwise
(Q1616.ofNat 1 * holisticTerm / Q1616.ofNat 2) +
(Q1616.ofNat 1 * stepwiseTerm / Q1616.ofNat 2)
-- ════════════════════════════════════════════════════════════
-- §6 Identity-Aware RoPE (Section 3.3)
-- ════════════════════════════════════════════════════════════
/-- Source identity flag f ∈ {0, 1}.
f=0: noisy latent x and state s (shared spatial coordinates)
f=1: object o (distinct encoding) -/
inductive IdentityFlag
| latentOrState -- f=0
| object -- f=1
deriving Repr, DecidableEq, Inhabited
/-- Extended RoPE position (f, h, w, l) with identity flag. -/
structure RoPEPosition where
flag : IdentityFlag
h : Nat
w : Nat
l : Nat
deriving Repr, Inhabited
/-- Complex pair for Q16.16 values (placeholder until native complex fixed-point). -/
structure ComplexQ1616 where
re : Q1616
im : Q1616
deriving Repr
/-- Complex-valued positional frequency.
Paper: ϕ_f(f) encodes source identity, ϕ_h, ϕ_w, ϕ_l follow standard RoPE. -/
def identityAwareFrequency (_pos : RoPEPosition) (_dim : Nat) : ComplexQ1616 :=
-- Simplified: complex exponential of position encoding
-- TODO(lean-port): implement actual RoPE frequency computation
{ re := Q1616.one, im := Q1616.zero } -- Placeholder
/-- Apply identity-aware positional embedding to token.
Distinguishes scene state from newly added objects while preserving spatial alignment. -/
def applyIdentityRoPE {d : Nat} (token : LocalLatent d) (pos : RoPEPosition)
: LocalLatent d :=
-- Extended RoPE: flag affects encoding, (h,w,l) preserve spatial
{ token with position := ⟨pos.h, pos.w, pos.l⟩ }
-- ════════════════════════════════════════════════════════════
-- §7 Physical Plausibility Metrics (Section 4.2)
-- ════════════════════════════════════════════════════════════
/-- Physical plausibility score (19% improvement over SOTA). -/
structure PlausibilityMetrics where
collisionFree : Bool -- No object intersections
gravityStable : Bool -- Objects rest on surfaces
scaleConsistent : Bool -- Realistic object sizes
semanticCoherent : Bool -- Matches textual description
overallScore : Q1616 -- Combined score
deriving Repr, Inhabited
/-- Check if layout satisfies physical constraints. -/
def checkPhysicalPlausibility {grid : VoxelGrid} {d : Nat}
(_state : SceneState grid d) (_instruction : LayoutInstruction)
: PlausibilityMetrics :=
{ collisionFree := true
gravityStable := true
scaleConsistent := true
semanticCoherent := true
overallScore := Q1616.ofNat 100 } -- 100% plausibility
-- ════════════════════════════════════════════════════════════
-- §8 Speedup Metrics (Section 4.2)
-- ════════════════════════════════════════════════════════════
/-- Computation speedup: 65% faster than prior methods. -/
structure SpeedupMetrics where
baselineTimeMs : Nat
laviGenTimeMs : Nat
speedupRatio : Q1616 -- 0.65 = 65% faster
deriving Repr, Inhabited
/-- Calculate speedup ratio. -/
def computeSpeedup (baseline : Nat) (laviGen : Nat) : Q1616 :=
let speedup := Q1616.ofNat (baseline - laviGen)
speedup / Q1616.ofNat baseline
-- ════════════════════════════════════════════════════════════
-- §9 Integration with Ordered Field Tokens
-- ════════════════════════════════════════════════════════════
/-- LaviGen token types for OrderedFieldTokens framework. -/
inductive LaviGenToken
| placeObject (Oi : Nat) (pos : VoxelPos)
| applyFlowMatching (t : FlowTime)
| selfRolloutStep (i : LayoutStep)
| dualGuidanceCorrect (useHolistic : Bool) (useStepwise : Bool)
deriving Repr, Inhabited
-- ════════════════════════════════════════════════════════════
-- §10 Verification Examples (AGENTS.md §4 requirement)
-- ════════════════════════════════════════════════════════════
#eval Q1616.lerp (Q1616.ofNat 0) (Q1616.ofNat 10) (Q1616.ofNat 5 / Q1616.ofNat 10)
-- Linear interpolation at t=0.5: 5.0
#eval @flowMatchingPerturb 2 #[Q1616.zero, Q1616.one] #[Q1616.one, Q1616.zero] (Q1616.ofNat 5 / Q1616.ofNat 10)
-- At t=0.5: [0.5, 0.5]
#eval @dualGuidanceObjective ⟨10, 10, 10⟩ 0
{ score := fun _ _ => Q1616.zero, plan := fun _ _ => { step := 0, occupancy := #[], latents := #[] } }
{ scoreStep := fun _ _ _ _ => Q1616.zero, correct := fun s _ _ => s }
[{ step := 0, occupancy := #[], latents := #[] }]
[]
({ data := #[], wf := by rfl } : ConditioningVector 0)
-- Combined objective with equal weights
end Semantics.LaviGen