SimpleDet Docs

Developer guide

Repository structure, registry contracts, tensor contracts, detector composition, and validation rules for extending the native architecture.

Use this page when you are modifying the repo itself rather than only consuming the public API.

Repository structure

  • simpledet/simpledet/suite/ for specs, builders, and compiler
  • simpledet/simpledet/_model_resolution.py for runtime adaptation
  • simpledet/simpledet/api.py for the pipeline wrapper
  • simpledet/simpledet/detectors/ for compatibility helpers
  • simpledet/simpledet/extensions/registry.py for component metadata and alias resolution
  • simpledet/simpledet/native/ for registry-backed modules, reusable CNN blocks, assemblers, tensor ops, losses, and decoders
  • tests/ for unit tests and repo audit
  • docs/ for this static HTML site

For the current baseline of modules, APIs, tests, docs, optional extras, and detector alias status, see the package surface audit.

Registry contracts

Native support starts with an explicit registry entry, not a loose import side effect. The shared ExtensionRegistry stores the factory plus metadata that is surfaced by builders, CLI discovery, docs, and support tests.

  • Use the existing registries from simpledet.extensions: ENCODERS, NECKS, HEADS, DETECTORS, LOSSES, ASSIGNERS, and POSTPROCESSORS.
  • Register aliases deliberately. Alias resolution is case-insensitive and punctuation-insensitive, and duplicate normalized aliases are rejected.
  • Set family to the boundary the assembler enforces, such as dense, roi, proposal, transformer, neck, or the loss family.
  • Set validation_status truthfully. Use runtime_validated only after real construction and tensor/runtime tests pass. Use compatibility_alias or unvalidated when the route exists but architecture-faithful support is not proven.
  • Fill tensor_contracts, required_dependencies, and summary because those fields explain what was validated and why a missing extra fails.
@HEADS.register(
    "MyDenseHead",
    aliases=("my_dense_head",),
    required_dependencies=(("torch", "cpu"),),
    tensor_contracts=("feature_pyramid", "dense_anchor_free_outputs"),
    validation_status="runtime_validated",
    family="dense",
    summary="Native dense head with construction, forward, loss, and decode coverage.",
)
class MyDenseHead(nn.Module):
    ...

A config row, notebook import, or alias-only route is not support. Do not describe a detector family as supported until the registered native modules have CPU tensor validation and the docs identify the exact validation boundary.

Tensor contracts

Tensor contracts are the runtime shape and key agreements between modules. They keep detector families extensible without copying fragile assumptions from one head into another.

BoundaryExpected contractValidation location
Feature pyramidA sequence of NCHW tensors, one tensor per level, with consistent batch size and downstream channel expectations.tests/native_tensor_contracts.py
Dense head forwardA mapping whose per-level outputs preserve the feature-map level count and spatial shape. Standard keys include cls_logits and bbox_regression; family-specific heads may add centerness, objectness_logits, heatmaps, embeddings, or offsets.assert_dense_head_output_contract
Loss pathLoss callables consume images, targets, feature pyramids, and head outputs, then return finite scalar losses including loss_total when the family test expects aggregation.tests/test_native_dense_heads.py, tests/test_native_two_stage.py, tests/test_native_query_detector.py
Decode pathPostprocessors return detection payloads with boxes, scores, and labels, where boxes are shaped N x 4.Dense, ROI, keypoint, and query detector tests
ROI and proposal pathRPN, ROI bbox, mask, grid, and sparse heads use family-specific contracts such as proposals, ROI features, mask predictions, grid offsets, and learned proposal features.tests/test_native_roi.py, tests/test_native_two_stage.py

Detector composition layers

Detector construction is layered so the public suite API, build-plan compiler, component builders, and runtime modules each own one concern.

build_detector(...)
  -> compile_native_detector_plan(...)
  -> build_native_components(...)
  -> family-specific assembler
  -> native torch.nn.Module runtime
  • simpledet.suite owns user-facing specs and plan compilation.
  • build_native_components imports requested modules, builds backbone, neck, and heads, and validates family boundaries before assembly.
  • Dense detectors own backbone, neck, dense head, loss, and decoder through the single-stage model path.
  • Proposal and ROI detectors own RPN, bbox, mask, grid, sparse, or cascade pieces through explicit ROI validation.
  • Transformer detectors own query heads, positional settings, set losses, and postprocessing through the query-detector path.

Runtime dependency direction points into the native PyTorch and Lightning stack. New support must not depend on MMDetection, MMEngine, or MMCV as a fallback execution path.

Head implementation expectations

  • Implement heads as real torch.nn.Module objects with trainable parameters owned through normal PyTorch module attributes.
  • Keep constructor validation local and actionable. For example, dense heads must reject non-positive num_classes, and RepPoints must reject missing or mismatched point-generation settings.
  • Keep forward output keys stable for the family. If a new key is required for loss or decode, document it in tensor_contracts and test it explicitly.
  • Use shared geometry, assignment, target-builder, loss, and decoder helpers in simpledet/simpledet/native/ before adding another family-specific copy.
  • Make architecture gaps visible. A compatibility route may share a dense seam, but the docs and metadata must say when full family parity is not claimed.

Tests required for support claims

A support claim needs tests at the same boundary that users will rely on. Metadata tests prove discovery; tensor tests prove the runtime contract.

  • Registry and discovery: assert aliases resolve to the expected component, family, dependencies, tensor contracts, and validation status.
  • Construction: instantiate through public builders and native builders, not only by directly calling the class.
  • Forward: run deterministic CPU feature maps and assert output keys, levels, batch dimensions, channels, and spatial shapes.
  • Loss: run finite scalar loss smoke tests with generated boxes, labels, metadata, and targets.
  • Decode or prediction: run the decoder/postprocessor and assert detection payload shapes.
  • Detector assembly: build the detector family through build_native_detector or the relevant assembler and exercise loss and prediction paths when the story claims detector support.
  • Negative cases: assert unsupported combinations fail with actionable messages instead of silently routing to another family.
  • Docs: update Model Coverage, this guide, examples, or changelog entries when public support status changes.
PYTHONPATH=simpledet python3 -m unittest tests.test_native_dense_heads
PYTHONPATH=simpledet python3 -m unittest tests.test_native_dense_detectors
make docs-check

Release readiness gate

Before handoff, run make release-ready. The target rebuilds distributions, runs the unit, docs, and package artifact checks, then audits release claims so detector and head counts stay above 31 and installation docs stay aligned with package extras.

make release-ready

Optional dependency rules

  • Base imports must stay lightweight. Do not make import simpledet require torch, torchvision, timm, geospatial packages, plotting libraries, MMDetection, MMEngine, or MMCV.
  • Register required runtime modules with required_dependencies so require_dependencies can raise an install hint such as python -m pip install 'simpledet[cpu]'.
  • Use simpledet[cpu] for native PyTorch and TorchVision runtime modules, and simpledet[timm] only for TIMM-backed encoders or defaults that need TIMM feature metadata.
  • Guard optional imports inside the module or builder that needs them. Do not import optional runtime stacks from package top-level modules or docs-only discovery paths.

Checklist for adding a dense head

  1. Construction: add the native nn.Module, constructor validation, registry metadata, aliases, dependency requirements, family, validation status, and tensor contract names.
  2. Forward: accept a list of feature-pyramid tensors and return a mapping with stable per-level tensors for class logits, bbox regression, and any family-specific outputs.
  3. Loss: wire or add the target builder and loss callable, then test finite loss_total behavior with CPU smoke targets.
  4. Decode: wire or add the decoder/postprocessor and test that inference returns boxes, scores, and labels with coherent shapes.
  5. Detector route: connect the head to a detector assembler only when the full family boundary is validated; otherwise mark it as a head-level capability or compatibility alias.
  6. Docs: update model coverage, examples or changelog if support status changes, and keep any limitation text aligned with the actual tests.

How to add a new detector family

  1. Add the architecture and family mapping in suite/catalog.py.
  2. Add the native component, head, decoder/loss, or ROI/query primitive needed for the family.
  3. Register a native detector assembler in native/assemblers.py.
  4. Extend compiler logic only if the family needs custom planning rules.
  5. Add suite, runtime, and tensor validation coverage. A catalog entry or legacy config alone is not support.

Testing

PYTHONPATH=simpledet python3 -m unittest discover -s tests -p 'test*.py'
python3 scripts/verify_docs.py
python3 scripts/docs_audit.py
make docs-check

Native detector-family stories should reuse tests/native_tensor_contracts.py. It builds deterministic CPU tensors for images, multiscale feature maps, boxes, labels, targets, and metadata so support claims are backed by runtime tensors rather than registry-only wiring.

from native_tensor_contracts import (
    assert_dense_head_output_contract,
    make_cpu_detector_smoke_batch,
)

batch = make_cpu_detector_smoke_batch(feature_channels=8)
outputs = head(batch.feature_maps)
assert_dense_head_output_contract(outputs, batch.feature_maps, batch_size=2)