Reference implementation of stepwise inference for CADENA, a model that reconstructs a 3D mesh as a parametric CAD program by emitting one operation at a time and comparing the target with the currently built geometry at every step.
- Paper: arXiv:2608.00799
- Model weights: https://huggingface.co/kulibinai/cadena
- Benchmark: https://huggingface.co/datasets/kulibinai/cadena-bench
| Path | Contents |
|---|---|
cadgen/ |
The DSL runtime — the operations a CADENA program calls when it executes |
inference/ |
Stepwise rollout: vLLM serving, per-operation expansion, prefix selection |
utils/ |
Geometry helpers and the metrics, including GMS (utils/gms.py) |
rl/ |
Sandboxed prefix rendering (cad_pool.py) and the reward/scoring used to rank prefixes |
dataset/ |
Building an inference dataset from a directory of meshes |
visualization.py |
Rendering the multi-view input images |
The rule-based program generator that synthesised the training corpus is not
part of this release. cadgen/ contains the execution half of the DSL only.
Requires Python 3.10+, a CUDA GPU for serving, and OpenCASCADE via CadQuery.
conda env create -f environment.yml -n cadena && conda activate cadenaServing the policy needs an NVIDIA GPU (vLLM). The DSL runtime, the metrics and
dataset building are CPU-only. To reproduce the paper's numbers bit-for-bit on
Linux/x86-64, use the fully pinned environment.lock.yml instead.
Both stages live in one repository, as subfolders:
| Subfolder | Stage |
|---|---|
sft |
Supervised, final checkpoint of the second stage |
rl |
Reinforcement learning against executed geometry — the paper's CADENA-RL |
hf download kulibinai/cadena --include 'rl/*' --local-dir ./ckptOr load directly:
from transformers import AutoModelForVision2Seq
model = AutoModelForVision2Seq.from_pretrained("kulibinai/cadena", subfolder="rl")Build a dataset from a directory of target meshes, then roll out:
MESH_ROOT=data/meshes OUT_DIR=data/stepwise_hf python dataset/create_hf_dataset.pyMODEL_PATH=./ckpt/rl DATASET_PATH=data/stepwise_hf/meshes ./inference/run.shrun.sh starts vLLM, waits for it, rolls out, and shuts the servers down. Its
defaults reproduce the main-results setting of the paper: greedy decoding and an
operation budget of 20. A step is kept only if it improves IoU against the
target, so the returned program is the best prefix and extra steps cannot make
it worse. For each part it writes the program, the input image and the built STL
at every step. Every parameter is an environment variable — see the header of
the script.
For the sampling configuration reported alongside greedy, set MODE=beam_topk
with TEMPERATURE=1.0 and NUM_EXPANSIONS=12 (E, the candidates drawn per
operation; the best is kept by IoU).
A generated program opens with the prelude in utils.pipeline.CODE_PREFIX,
which imports the operations it may call:
import cadquery as cq
from cadgen.selectors import PointOnEdgeSelector
from cadgen.extrude import extrude
from cadgen.shell import shell
from cadgen.hole import hole
from cadgen.revolve import revolve
from cadgen.orto_cut import orto_cut
from cadgen.sweep import sweep
from cadgen.gear import gear
from cadgen.spring import spring
from cadgen.sweep_adv import sweep_adv
from cadgen.loft import loft
r = NoneEach emitted line applies one operation to r. Executing a prefix yields the
solid built so far, which is what the model is conditioned on at the next step.
utils/gms.py implements the metric used in the paper. A predicted point
matches a target point only when it is both close enough and has a consistent
normal — ‖p − q‖ ≤ τ and n_p · n_q ≥ cos α — so the score reflects whether
the surface itself was reconstructed, not merely whether volume was filled.
GMS is the normalised area under the F1 curve as the angular tolerance sweeps
to α_max = 25°, at fixed τ = 0.05 with 8192 sampled points.
- A prediction is invalid if it fails to build or is not watertight. Invalid predictions count toward the invalid rate and are excluded from metric means.
@article{cadena2026,
title = {CADENA: Stepwise CAD Reverse Engineering},
author = {Kabisov, Soslan and Savrasov, Gennadiy and Elistratov, Maksim and
Rodriguez, Antonio and Ignatiev, Daniil and Gavrilov, Nikita and
Uzdenov, Rustam and Boyko, Alexey I. and Pasechnik, Igor and
Konushin, Anton and Kuznetsov, Andrey and Zhemchuzhnikov, Dmitrii},
year = {2026},
eprint = {2608.00799},
archivePrefix = {arXiv},
primaryClass = {cs.CV},
url = {https://arxiv.org/abs/2608.00799}
}MIT — see LICENSE.