Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/Checklists/PARITY_VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ Full upstream-vs-port diff of every world model against its `config.json` / sour
| **TRELLIS.2 stages (structure decoder, shape decode, mesher, texture flow, texture decode)** | 🔧 (fp32 CPU, 512 bust) | Against upstream `microsoft/TRELLIS.2` `75fbf018` run as is on the CPU with stand-ins for the CUDA libraries, 2026-11-12, one real C# CUDA run, each stage on the C# stage's own input: structure decoder logits max abs **4.4e-4**, corr 1.0 (seeded latent) · shape decode dual-vertex offsets corr **0.99982** (mean 4.8e-4), split weights corr 0.99980, child logits corr 1.0000000 to 0.9999859 over four levels, 736,420 of 736,637 voxels in both (near-zero child logits flip: 1, 3, 50, 370 keep decisions) · dual-grid mesher on the same field **identical** (1,487,440 triangles) · texture flow, one forward, corr **0.9999998** (max abs 9.6e-3) · texture decode corr **0.9999999** (max abs 2.1e-2). Upstream CPU 30 to 52 s (shape decode), 46 to 57 s (texture decode), 40.8 s (flow forward), run to run. Not compared: the structure flow, the 1024/1536 flows, the full texture sampler, post-processing; the sparse-conv stand-in assumes the C# engine's own kernel-axis order. `tests/python-reference/trellis2_stage_parity`. |
| **Hunyuan3D-2** | 🔧 | Against upstream `hy3dgen` (`Tencent-Hunyuan/Hunyuan3D-2` f8db6309) with `tencent/Hunyuan3D-2` 9cd649ba, fp32 CPU reference, 2026-11-10, input `assets/demo.png` recentred on white, seed 0, 20 steps: **DiT velocity corr 1.00000000** (relative L2 4.6e-5 at step 10, 1.3e-6 at step 5) at the schedule timestep; **ShapeVAE occupancy corr 0.99999999** (relative L2 7.2e-5, 4000 grid points; processed latents corr 0.99999586). Mesh at grid 128: 77,328 triangles, 1 component, watertight, signed volume +0.978 against the reference mesh's 81,024 / 1 / +0.980 (different noise), rendered from four angles. The previous ✅ here was wrong: the sampler passed `sigma·1000` to a DiT that scales by 1000 itself (velocity corr 0.73 and 0.53), and marching cubes wound the triangles inward. | Not yet ✅: conditioner not re-compared on this input, default 50 steps/grid 256 not re-run, one subject, no cuDNN on this machine, raw RGBA preprocessing not ported. Tests: `Hunyuan3DShapeParityTests`. Detail: [MODEL_STATUS_3D.md](MODEL_STATUS_3D.md#hunyuan3d-2), [HUNYUAN3D_ARCHITECTURE.md](../Research/HUNYUAN3D_ARCHITECTURE.md). |
| **Hunyuan3D-2 variants** (Fast, Turbo + FlashVDM, mini, 2mv) | 🔧 | Against upstream `hy3dgen` `f8db6309` with `tencent/Hunyuan3D-2` 9cd649ba, `-2mini` f90a0f7d and `-2mv` 3a761b53, fp32 CPU, 2026-11-10, `assets/demo.png` (2mv: `example_mv_images/1`, three views), seed 0. DiT velocity corr **1.00000000** for every build (relative L2 Fast 3.0e-5, Turbo 3.9e-5 at consistency sigma 0.3994, mini 3.6e-6, 2mv 6.1e-5); ShapeVAE occupancy corr 0.99999999 to 1.00000000 (relative L2 4e-5 to 7e-5). FlashVDM field (turbo VAE) corr **0.99999979**, relative L2 6.4e-4, 194,648 shared points, 8 and 4 unshared. C# conditioner on GPU vs Python: 0.9999926 (single image), 0.9999957 (2mv, three views). Meshes through the CLI on an RTX 3060, rendered from four angles: Fast 85,340 triangles (2 components), Turbo 284,827 (1, 3 boundary edges), mini 74,692 (1, watertight), 2mv 68,196 (3, watertight; Python 68,886). | Not yet ✅: one subject and seed per build, 2mv with three views at 8 steps and no engine path, mini-Fast, 2mv-Fast, 2mv-Turbo untested, mini-Turbo refused, FlashVDM `merge` mode and `dmc` not implemented, default step counts not run, no cuDNN. Tests: `Hunyuan3DShapeParityTests`, `Hunyuan3DConfigFileTests`, `Hunyuan3DFlowScheduleTests`, `Hunyuan3DViewEmbeddingTests`, `Hunyuan3DCatalogTests`. Detail: [MODEL_STATUS_3D.md](MODEL_STATUS_3D.md#hunyuan3d-2-variants). |
| **Hunyuan3D Paint 2.1 (UNet, VAE)** | 🔧 | Against upstream `hunyuanpaintpbr/unet` (`Tencent-Hunyuan/Hunyuan3D-2.1` `hy3dpaint`) with `tencent/Hunyuan3D-2.1` 0b946776 UNet and VAE pickles (restricted unpickler), fp32 CPU, 2026-11-11, 6 views x 2 materials, 16 x 16 latents, timestep 500, seed 0, oracle `tests/python-reference/dump_paint21_unet.py`, C# `HunyuanPaintUnet21ReferenceTests` (Integration): reference-stream features relRMS 1e-6 to 4e-4, DINO projection 3e-7, UNet full branch **4.9e-4** (maxAbs 1.4e-3), unconditional branch **3.2e-4** (1.2e-3), rotary voxel indices 0 of 6120 differ, VAE decode **1.3e-6** (5.9e-6); all corr 1.000000 | Bugs found: unconditional DINO tokens were zeros, upstream projects zeros first (branch 22 % RMS off); voxel pooling must round in F16 (1.6 % RMS off). GPU (RTX 3060, 2026-11-12, same fixture, `HARTSY_PAINT21_BACKEND=cuda`): with the default TF32 GEMMs and TF32 attention the UNet full branch is relRMS **1.4e-3** (maxAbs 4.4e-3, corr 0.999999), the unconditional branch **9.6e-4**, reference-stream features at most 1.7e-3, DINO projection 2.1e-4; with `HighPrecisionGemm` the full branch is **7.8e-4**, the unconditional **5.3e-4**, the DINO projection 3.5e-7. Exact attention changes nothing: with `HighPrecisionGemm` plus `numerics.sdpaNoTf32` (attention F32), and with `numerics.noTf32` as well, the full branch stays at **7.8e-4** and the unconditional at **5.3e-4** (flags logged `Tf32Gemm=False Tf32Sdpa=False`). The UNet is therefore not limited by TF32 and no exact-GEMM default is needed; the CPU run is 4.9e-4, and the remaining difference is probably F16 weights and activations. The full-branch forward took 3.3 to 11.5 s across the runs (the first includes warm-up), peak 5.6 to 6.1 GB. Not compared: UniPC sampler, guidance, bake, the DINO encoder, a full run, the VAE on the GPU. Paint 2.0 has no parity run. |
| **Hunyuan3D Paint 2.1 (UNet, VAE, sampler, guidance)** | 🔧 | Against upstream `hunyuanpaintpbr/unet` (`Tencent-Hunyuan/Hunyuan3D-2.1` `hy3dpaint`) with `tencent/Hunyuan3D-2.1` 0b946776 UNet and VAE pickles (restricted unpickler), fp32 CPU, 2026-11-11, 6 views x 2 materials, 16 x 16 latents, timestep 500, seed 0, oracle `tests/python-reference/dump_paint21_unet.py`, C# `HunyuanPaintUnet21ReferenceTests` (Integration): reference-stream features relRMS 1e-6 to 4e-4, DINO projection 3e-7, UNet full branch **4.9e-4** (maxAbs 1.4e-3), unconditional branch **3.2e-4** (1.2e-3), rotary voxel indices 0 of 6120 differ, VAE decode **1.3e-6** (5.9e-6); all corr 1.000000 | Bugs found: unconditional DINO tokens were zeros, upstream projects zeros first (branch 22 % RMS off); voxel pooling must round in F16 (1.6 % RMS off). GPU (RTX 3060, 2026-11-12, same fixture, `HARTSY_PAINT21_BACKEND=cuda`): with the default TF32 GEMMs and TF32 attention the UNet full branch is relRMS **1.4e-3** (maxAbs 4.4e-3, corr 0.999999), the unconditional branch **9.6e-4**, reference-stream features at most 1.7e-3, DINO projection 2.1e-4; with `HighPrecisionGemm` the full branch is **7.8e-4**, the unconditional **5.3e-4**, the DINO projection 3.5e-7. Exact attention changes nothing: with `HighPrecisionGemm` plus `numerics.sdpaNoTf32` (attention F32), and with `numerics.noTf32` as well, the full branch stays at **7.8e-4** and the unconditional at **5.3e-4** (flags logged `Tf32Gemm=False Tf32Sdpa=False`). The UNet is therefore not limited by TF32 and no exact-GEMM default is needed; the CPU run is 4.9e-4, and the remaining difference is probably F16 weights and activations. The full-branch forward took 3.3 to 11.5 s across the runs (the first includes warm-up), peak 5.6 to 6.1 GB. Sampler and guidance (2026-11-12, diffusers 0.42.1 `UniPCMultistepScheduler` and `EulerAncestralDiscreteScheduler` built from the released `scheduler_config.json`, oracle `tests/python-reference/dump_paint_sampler.py`, C# `HunyuanPaintSamplerReferenceTests`, CPU, a fixed synthetic model so only the scheduler arithmetic is compared): UniPC 15 and 30 steps follow diffusers over the whole trajectory to relRMS **2.3e-7** and **4.6e-7**; Euler ancestral (Paint 2.0) 15 and 30 steps with injected noise **8.7e-7** and **1.1e-6** (model input 1e-6, initial sigma 4096 both); trailing timesteps equal, sigmas 8e-7; `alphas_cumprod` within 3.8e-5 relative (diffusers builds the table in float32, the largest difference is at timestep 998 where alpha is 2e-7); the view scale of upstream's `cam_mapping` equal for azimuths 0 to 345 and the three-way guidance equals `u + g s (full - u)` to 8e-8 relRMS. Not compared: the bake, the DINO encoder, a full run, the VAE on the GPU. Paint 2.0's UNet has no parity run. |
| **Hunyuan3D Part P3-SAM (Sonata features, heads)** | 🔧 | Against upstream `XPart/partgen/models/sonata` (`Tencent-Hunyuan/Hunyuan3D-Part` e96be065) with `tencent/Hunyuan3D-Part` 67717446 `p3sam.safetensors`, fp32 CPU, 2026-11-12, 6000 ellipsoid points, oracle `tests/python-reference/dump_p3sam_features.py` (stand-ins for spconv, torch_scatter, timm, addict; pooling shuffle pinned off), C# `P3SamRealWeightsTests` (Integration): grid sample exact, pooled grids identical, Sonata stages relRMS 2e-6 to 1.1e-4, features **4.2e-5**, mask probabilities **5.7e-7**, IoU 8.2e-7; all corr 1.000000 | Findings: upstream shuffles the serialisation orders at every pooling even at inference (its features are random from stage 1). The spconv `SubMConv3d` convention is an assumption of the oracle (library not installed). GPU (RTX 3060, 2026-11-12, same fixture): heads on CUDA match upstream at max diff 2.4e-6 (IoU 3.3e-7). Sonata features on CUDA with default TF32 GEMMs and TF32 attention are relRMS **3.6e-2** (corr 0.99935); with `HighPrecisionGemm` and `numerics.sdpaNoTf32` (the `reference` profile gives the same) they are relRMS **7.1e-5**, corr 1.000000, at the same 1.83 s. Either knob alone leaves 2.1e-2 to 2.9e-2. Not compared: the auto-mask geometry, surface sampling and prompts. |
| **Hunyuan3D-2.1 shape** | 🔧 | Against upstream `hy3dshape` `82920d64` with `tencent/Hunyuan3D-2.1` 0b946776, fp32 CPU, 2026-11-11, `demo.png`, seed 0, sigma 0.35: timestep token, latent embedding, blocks 0, 10, 15 (first MoE) and 20, and the final velocity all corr **1.00000000** (relative L2 2.9e-7, 1.6e-7, 1.2e-5, 2.0e-5, 6.1e-5, 2.0e-5, velocity 2.4e-5); bundled VAE occupancy corr 0.99999392. Before the QKV-regrouping fix the velocity corr was 0.65; before the exact-erf GELU fix block 15 was at relative L2 1.2e-2. GPU (RTX 3060, F16 matrices): 10 steps, grid 128 in 34 s, 76,656 triangles, watertight, rendered. | Not yet ✅: first sigma only (one forward, 32 min on CPU; unconditional pass and second sigma not re-run), one subject and seed, no catalog id (the upstream checkpoint is a pickle). Tests: `Hunyuan3D21ShapeParityTests`, `Hunyuan3DConfigFileTests`. Detail: [MODEL_STATUS_3D.md](MODEL_STATUS_3D.md#hunyuan3d-21-shape). |
| **SAM 3D Objects (occupancy decoder, Gaussian decoder, structure flow, SLAT flow)** | 🔧 (fp32 CPU; occupancy decoder on CUDA) | Against upstream `facebookresearch/sam-3d-objects` `f91db411` run as is on the CPU with stub packages (no `spconv`, `pytorch3d`, `xformers`; attention `sdpa`; a pure-torch submanifold convolution for the SLAT flow), torch 2.6, the real `apozz/sam-3d-objects-safetensors` `cbb995af` weights, seeded injected inputs, one forward each, 2026-11-12: occupancy decoder `[1,8,16,16,16]` to `[1,1,64,64,64]` max abs 1.8e-4, relative L2 1.6e-6 (C# CUDA); Gaussian decoder (3,000 voxels, 448 outputs) max abs 6.5e-4, relative L2 3.5e-6; structure flow (4,096 shape tokens, 7,528 condition tokens) max abs 1.5e-4, relative L2 1.3e-5, pose outputs 3.7e-6 to 3.0e-5; SLAT flow (1,500 voxels, 5,496 condition tokens) max abs 5.9e-4, relative L2 7.6e-5, with F16 block linears 3.5e-4. Not compared: the sampler, DINO towers, point-patch embedder, fuser, preprocessing, pose decode, the whole pipeline, photographs. Details and times in [the dossier](../Research/SAM3D_OBJECTS_ARCHITECTURE.md#cpu-parity-status-2026-11-12). |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,21 @@ private Tensor Concatenate(float[] sample, float[] normal, float[] position, int

// u + g s (b - u) per view with s from the azimuth (2.1: b is the full prediction, see Diffuse) and s = 1 for 2.0.
private float[] Guide(float[][] branch, IReadOnlyList<PaintViewCandidate> views, float guidance, int n, int latent)
{
float[] scales = new float[n];
for (int v = 0; v < n; v++)
scales[v] = _config.UsePoseRope ? ViewScale(views[v].Azimuth) : 1f;
return Guide(branch, scales, guidance, n, latent);
}

// Per view v, result = u + guidance * viewScales[v] * (b - u) over the view's latent plane.
internal static float[] Guide(float[][] branch, float[] viewScales, float guidance, int n, int latent)
{
int plane = latent * latent * 4;
float[] result = new float[branch[0].Length];
for (int v = 0; v < n; v++)
{
float s = _config.UsePoseRope ? guidance * ViewScale(views[v].Azimuth) : guidance;
float s = guidance * viewScales[v];
for (int i = v * plane; i < (v + 1) * plane; i++)
result[i] = branch[0][i] + s * (branch[1][i] - branch[0][i]);
}
Expand Down
Loading
Loading