Skip to content

Model Card for Coarse-to-Fine

paper venue license base dataset dataset vendor-parity hub

This package ports Coarse-to-Fine, an AAAI 2022 hierarchy-first layout generator, into a 🤗transformers-style package that returns the shared layout schema.

Model Details

Model Description

Coarse-to-Fine generates page or UI layouts through a hierarchy-aware transformers model: it predicts coarse group structure before refining element boxes and labels for RICO25 or PubLayNet. It supports unconditional local inference from converted checkpoints and exposes hierarchy traces through intermediates when requested. Public outputs use normalized center xywh boxes in [0, 1], dataset-local integer labels, a valid-element mask, and id2label.

  • Developed by: Zhaoyun Jiang et al.
  • Shared by: creative-graphic-design.
  • Model type: content-agnostic; task: single-task; conditioning: unconditional.
  • Language(s) (NLP): not applicable.
  • License: MIT.

Model Sources

Supported Checkpoints

Checkpoint Hub ID Status
RICO25 creative-graphic-design/coarse-to-fine-rico25 not-published
PubLayNet creative-graphic-design/coarse-to-fine-publaynet not-published

Uses

Direct Use

Use this package for research inference, conversion checks, and agreement checks against the original implementation for generated layouts.

The released checkpoints support unconditional hierarchical generation only. Canonical condition_type names are normalized at the API boundary, and unsupported modes fail explicitly.

condition_type Required inputs Support
unconditional none supported
label, label_size, completion, refinement, text, content_image, relation, hierarchical, retrieval not applicable raises NotImplementedError

Downstream Use

Generated layouts may feed rendering, design tooling, layout evaluation, or downstream content placement systems after task-specific validation.

Out-of-Scope Use

Do not treat generated layouts as production accessibility annotations, OCR output, semantic scene understanding, or license-cleared design assets without separate review.

Bias, Risks, and Limitations

The converted behavior follows the upstream checkpoints, prompt fixtures, and datasets. Dataset coverage, label vocabularies, and layout quality inherit the limits of those sources.

Recommendations

Re-run the agreement-check suite against the original implementation before publishing converted checkpoints or comparing new results.

How to Get Started with the Model

Install the package directly from this repository. The command includes shared packages when they are not published on PyPI.

pip install \
  "laygen @ git+https://github.com/creative-graphic-design/design-generators.git#subdirectory=lib/laygen" \
  "coarse-to-fine @ git+https://github.com/creative-graphic-design/design-generators.git#subdirectory=models/coarse-to-fine"

Clone this repository, install the workspace member, and run the download and conversion steps in REPRODUCING.md. Those steps create .cache/coarse-to-fine/converted/rico25.

git clone https://github.com/creative-graphic-design/design-generators.git
cd design-generators
uv sync --package coarse-to-fine
uv run --package coarse-to-fine python
from coarse_to_fine import (
    CoarseToFineForLayoutGeneration,
    CoarseToFinePipeline,
    CoarseToFineProcessor,
)

path = ".cache/coarse-to-fine/converted/rico25"
# After Hub publication: from_pretrained("creative-graphic-design/coarse-to-fine-rico25")
model = CoarseToFineForLayoutGeneration.from_pretrained(path)
processor = CoarseToFineProcessor.from_pretrained(path)
pipe = CoarseToFinePipeline(model=model, processor=processor)

out = pipe(batch_size=2, seed=0, return_intermediates=True)
print(out.bbox.shape)
print(out.labels.shape)
print(out.id2label[int(out.labels[out.mask][0])])
print(out.intermediates["hierarchy"]["group_bbox"].shape)

Training Details

Training Data

Dataset Dataset ID Notes
RICO25 creative-graphic-design/Rico ui-screenshots-and-hierarchies-with-semantic-annotations
PubLayNet creative-graphic-design/PubLayNet default

RICO25 public labels are zero-based; original-implementation tensors use one-based labels with SOS/EOS ids. PubLayNet COCO-style document boxes are converted to normalized ltwh, discretized on the original 128-bin grid, and returned publicly as normalized center xywh.

Training Procedure

This package ports released behavior and does not retrain the method in this repository.

Preprocessing

Inputs and outputs are normalized to the public layout schema at package boundaries. Boxes, tokens, prompts, or analog bits that follow the original implementation stay inside package adapters and fixtures used for agreement checks.

Training Hyperparameters

  • Training regime: original upstream training; not rerun in this repository.

Speeds, Sizes, Times

Training-time and carbon measurements are unknown.

Evaluation

Testing Data, Factors & Metrics

Testing Data

Agreement checks use local-only generated fixtures and converted checkpoint directories. Large generated tensors, images, weights, and downloaded artifacts are not committed.

Factors

Agreement results are reported separately by dataset, checkpoint, condition mode, seed, or prompt fixture where the package has recorded evidence.

Metrics

Metrics are exact tensor equality, exact token or byte equality, or an explicitly stated numeric tolerance against the original implementation.

Parity Results

Dataset Comparison target Cases Agreement criterion Result
RICO25 Strict checkpoint conversion + from_pretrained 1 checkpoint Missing/unexpected keys fail strict load Pass
RICO25 Original implementation vs converted hierarchy logits batch 2, 20 groups/elements rtol=5e-5, atol=5e-5; greedy argmax exact Pass; max abs 3.004e-05
PubLayNet Strict checkpoint conversion + from_pretrained 1 checkpoint Missing/unexpected keys fail strict load Pass
PubLayNet Original implementation vs converted hierarchy logits batch 2, 20 groups/elements rtol=5e-5, atol=5e-5; greedy argmax exact Pass; max abs 7.75e-06

Detailed max-abs values for the logits checks were:

Dataset Group bbox Group label histogram Grouped bbox Grouped label Argmax
RICO25 3.004e-05 2.026e-06 2.098e-05 2.193e-05 exact
PubLayNet 3.814e-06 5.960e-07 5.600e-06 7.750e-06 exact

Reproducibility

See REPRODUCING.md for the commands that download original-implementation assets, generate reference outputs, run agreement checks, convert checkpoints, and smoke-test local loading.

Environmental Impact

No new model training is performed by these conversion packages. Conversion and parity costs depend on the selected checkpoint and local hardware.

Technical Specifications

Model Architecture and Objective

Coarse-to-Fine uses an autoregressive Transformer decoder with separate heads for element labels, group label histograms, and four coordinate bins. Generated hierarchy data is returned through intermediates when requested. There is no single unified token vocabulary for layout serialization, so Coarse-to-Fine uses a ProcessorMixin processor instead of a PreTrainedTokenizer.

Compute Infrastructure

Agreement-check commands are intended for one explicitly selected GPU when the original implementation requires CUDA.

Hardware

CPU is sufficient for import and most smoke tests. CUDA is required for heavyweight agreement checks when the original implementation requires it.

Software

Use uv run --package coarse-to-fine ... from the repository root so workspace dependency sources and extras resolve correctly.

License

Repository wrapper code is Apache-2.0. The original Microsoft LayoutGeneration repository is MIT licensed. Converted code in this package is for research conversion and evaluation of the released Coarse-to-Fine checkpoints.

Citation

@inproceedings{jiang2022coarse,
  title = {Coarse-to-Fine Generative Modeling for Graphic Layouts},
  author = {Jiang, Zhaoyun and Sun, Shizhao and Zhu, Jihua and Lou, Jian-Guang and Zhang, Dongmei},
  booktitle = {AAAI},
  year = {2022}
}

API Reference

Transformers-style Coarse-to-Fine layout generation components.

CoarseToFineConfig

Bases: PretrainedConfig

Stores architecture, discretization, and label metadata.

Parameters:

Name Type Description Default
dataset DatasetName | str

Dataset name for the converted checkpoint.

rico25
num_labels int | None

Optional label vocabulary size. Defaults to the dataset vocabulary length.

None
id2label Mapping[int | str, str] | None

Optional label id to display-label mapping.

None
label2id Mapping[str, int] | None

Optional display-label to id mapping.

None
max_num_elements int

Maximum flat element count used by the checkpoint model.

20
discrete_x_grid int

Number of x-axis bins.

128
discrete_y_grid int

Number of y-axis bins.

128
d_model int

Transformer hidden dimension.

512
d_z int

VAE latent dimension.

512
n_layers int

Number of encoder layers.

4
n_layers_decoder int

Number of decoder layers.

4
n_heads int

Number of attention heads.

8
dim_feedforward int

Transformer feed-forward dimension.

2048
dropout float

Dropout probability.

0.1
internal_box_format BoxFormat | str

Reference internal box format.

ltwh
public_box_format BoxFormat | str

Public output box format.

xywh
vendor_label_offset int

Offset from public label ids to internal ids.

1
**kwargs str | int | float | bool | None

Additional PretrainedConfig fields.

{}

Examples:

>>> CoarseToFineConfig(dataset="publaynet").num_labels
5
Source code in models/coarse-to-fine/src/coarse_to_fine/configuration_coarse_to_fine.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
class CoarseToFineConfig(PretrainedConfig):
    """Stores architecture, discretization, and label metadata.

    Args:
        dataset: Dataset name for the converted checkpoint.
        num_labels: Optional label vocabulary size. Defaults to the dataset
            vocabulary length.
        id2label: Optional label id to display-label mapping.
        label2id: Optional display-label to id mapping.
        max_num_elements: Maximum flat element count used by the checkpoint model.
        discrete_x_grid: Number of x-axis bins.
        discrete_y_grid: Number of y-axis bins.
        d_model: Transformer hidden dimension.
        d_z: VAE latent dimension.
        n_layers: Number of encoder layers.
        n_layers_decoder: Number of decoder layers.
        n_heads: Number of attention heads.
        dim_feedforward: Transformer feed-forward dimension.
        dropout: Dropout probability.
        internal_box_format: Reference internal box format.
        public_box_format: Public output box format.
        vendor_label_offset: Offset from public label ids to internal ids.
        **kwargs: Additional ``PretrainedConfig`` fields.

    Examples:
        >>> CoarseToFineConfig(dataset="publaynet").num_labels
        5
    """

    model_type = "coarse_to_fine"

    def __init__(
        self,
        dataset: DatasetName | str = DatasetName.rico25,
        num_labels: int | None = None,
        id2label: Mapping[int | str, str] | None = None,
        label2id: Mapping[str, int] | None = None,
        max_num_elements: int = 20,
        discrete_x_grid: int = 128,
        discrete_y_grid: int = 128,
        d_model: int = 512,
        d_z: int = 512,
        n_layers: int = 4,
        n_layers_decoder: int = 4,
        n_heads: int = 8,
        dim_feedforward: int = 2048,
        dropout: float = 0.1,
        internal_box_format: BoxFormat | str = BoxFormat.ltwh,
        public_box_format: BoxFormat | str = BoxFormat.xywh,
        vendor_label_offset: int = 1,
        eval_batch_size: int | None = None,
        **kwargs: str | int | float | bool | None,
    ) -> None:
        """Initialize Coarse-to-Fine config values."""
        normalized_dataset = normalize_dataset_name(dataset)
        if normalized_dataset not in SUPPORTED_DATASETS:
            raise ValueError(f"Unsupported Coarse-to-Fine dataset: {dataset}")

        resolved_id2label = (
            {int(key): str(value) for key, value in id2label.items()}
            if id2label is not None
            else id2label_for_dataset(normalized_dataset)
        )
        resolved_label2id = (
            {str(key): int(value) for key, value in label2id.items()}
            if label2id is not None
            else label2id_for_dataset(normalized_dataset)
        )
        resolved_num_labels = num_labels or len(resolved_id2label)
        super().__init__(id2label=resolved_id2label, label2id=resolved_label2id)
        for key, value in kwargs.items():
            setattr(self, key, value)

        self.dataset = str(normalized_dataset)
        self.num_labels = resolved_num_labels
        self.max_num_elements = max_num_elements
        self.discrete_x_grid = discrete_x_grid
        self.discrete_y_grid = discrete_y_grid
        self.d_model = d_model
        self.d_z = d_z
        self.n_layers = n_layers
        self.n_layers_decoder = n_layers_decoder
        self.n_heads = n_heads
        self.dim_feedforward = dim_feedforward
        self.dropout = dropout

        self.internal_box_format = str(normalize_box_format(internal_box_format))
        self.public_box_format = str(normalize_box_format(public_box_format))
        self.vendor_label_offset = vendor_label_offset
        self.eval_batch_size = eval_batch_size or max_num_elements

        self.element_sos_id = resolved_num_labels + 1
        self.element_eos_id = resolved_num_labels + 2
        self.group_sos_index = 0
        self.group_eos_index = resolved_num_labels + 1
        self.group_label_size = resolved_num_labels + 2
        self.element_label_size = resolved_num_labels + 3
        self.bbox_vocab_size = max(discrete_x_grid, discrete_y_grid)
        self.architectures = ["CoarseToFineForLayoutGeneration"]

__init__

__init__(
    dataset: DatasetName | str = DatasetName.rico25,
    num_labels: int | None = None,
    id2label: Mapping[int | str, str] | None = None,
    label2id: Mapping[str, int] | None = None,
    max_num_elements: int = 20,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    d_model: int = 512,
    d_z: int = 512,
    n_layers: int = 4,
    n_layers_decoder: int = 4,
    n_heads: int = 8,
    dim_feedforward: int = 2048,
    dropout: float = 0.1,
    internal_box_format: BoxFormat | str = BoxFormat.ltwh,
    public_box_format: BoxFormat | str = BoxFormat.xywh,
    vendor_label_offset: int = 1,
    eval_batch_size: int | None = None,
    **kwargs: str | int | float | bool | None,
) -> None

Initialize Coarse-to-Fine config values.

Source code in models/coarse-to-fine/src/coarse_to_fine/configuration_coarse_to_fine.py
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def __init__(
    self,
    dataset: DatasetName | str = DatasetName.rico25,
    num_labels: int | None = None,
    id2label: Mapping[int | str, str] | None = None,
    label2id: Mapping[str, int] | None = None,
    max_num_elements: int = 20,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    d_model: int = 512,
    d_z: int = 512,
    n_layers: int = 4,
    n_layers_decoder: int = 4,
    n_heads: int = 8,
    dim_feedforward: int = 2048,
    dropout: float = 0.1,
    internal_box_format: BoxFormat | str = BoxFormat.ltwh,
    public_box_format: BoxFormat | str = BoxFormat.xywh,
    vendor_label_offset: int = 1,
    eval_batch_size: int | None = None,
    **kwargs: str | int | float | bool | None,
) -> None:
    """Initialize Coarse-to-Fine config values."""
    normalized_dataset = normalize_dataset_name(dataset)
    if normalized_dataset not in SUPPORTED_DATASETS:
        raise ValueError(f"Unsupported Coarse-to-Fine dataset: {dataset}")

    resolved_id2label = (
        {int(key): str(value) for key, value in id2label.items()}
        if id2label is not None
        else id2label_for_dataset(normalized_dataset)
    )
    resolved_label2id = (
        {str(key): int(value) for key, value in label2id.items()}
        if label2id is not None
        else label2id_for_dataset(normalized_dataset)
    )
    resolved_num_labels = num_labels or len(resolved_id2label)
    super().__init__(id2label=resolved_id2label, label2id=resolved_label2id)
    for key, value in kwargs.items():
        setattr(self, key, value)

    self.dataset = str(normalized_dataset)
    self.num_labels = resolved_num_labels
    self.max_num_elements = max_num_elements
    self.discrete_x_grid = discrete_x_grid
    self.discrete_y_grid = discrete_y_grid
    self.d_model = d_model
    self.d_z = d_z
    self.n_layers = n_layers
    self.n_layers_decoder = n_layers_decoder
    self.n_heads = n_heads
    self.dim_feedforward = dim_feedforward
    self.dropout = dropout

    self.internal_box_format = str(normalize_box_format(internal_box_format))
    self.public_box_format = str(normalize_box_format(public_box_format))
    self.vendor_label_offset = vendor_label_offset
    self.eval_batch_size = eval_batch_size or max_num_elements

    self.element_sos_id = resolved_num_labels + 1
    self.element_eos_id = resolved_num_labels + 2
    self.group_sos_index = 0
    self.group_eos_index = resolved_num_labels + 1
    self.group_label_size = resolved_num_labels + 2
    self.element_label_size = resolved_num_labels + 3
    self.bbox_vocab_size = max(discrete_x_grid, discrete_y_grid)
    self.architectures = ["CoarseToFineForLayoutGeneration"]

CoarseToFineHierarchy dataclass

Decoded Coarse-to-Fine hierarchy returned in intermediates.

Source code in models/coarse-to-fine/src/coarse_to_fine/hierarchy.py
27
28
29
30
31
32
33
34
35
36
37
38
@dataclass
class CoarseToFineHierarchy:
    """Decoded Coarse-to-Fine hierarchy returned in ``intermediates``."""

    group_bbox: Float[torch.Tensor, "batch groups 4"]
    group_mask: Bool[torch.Tensor, "batch groups"]
    label_histogram: Float[torch.Tensor, "batch groups labels"]
    element_group_index: Int[torch.Tensor, "batch elements"]
    relative_bbox: Float[torch.Tensor, "batch groups elements 4"]
    relative_mask: Bool[torch.Tensor, "batch groups elements"]
    discrete_group_bbox: Int[torch.Tensor, "batch groups 4"] | None = None
    discrete_relative_bbox: Int[torch.Tensor, "batch groups elements 4"] | None = None

CoarseToFineHierarchyEncoding dataclass

Training/reference hierarchy tensors before padding.

Source code in models/coarse-to-fine/src/coarse_to_fine/hierarchy.py
41
42
43
44
45
46
47
48
@dataclass
class CoarseToFineHierarchyEncoding:
    """Training/reference hierarchy tensors before padding."""

    group_bounding_box: Int[torch.Tensor, "groups 4"]
    label_in_one_group: Float[torch.Tensor, "groups labels"]
    grouped_labels: list[Int[torch.Tensor, "elements"]]
    grouped_bbox: list[Int[torch.Tensor, "elements 4"]]

CoarseToFineForLayoutGeneration

Bases: PreTrainedModel

Transformers PreTrainedModel with checkpoint-compatible module names.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
class CoarseToFineForLayoutGeneration(PreTrainedModel):
    """Transformers ``PreTrainedModel`` with checkpoint-compatible module names."""

    config_class = CoarseToFineConfig
    base_model_prefix = "coarse_to_fine"
    main_input_name = "labels"
    _tied_weights_keys = {
        "encoder.embedding.label_embed.weight": "layout_embd.label_embed.weight",
        "group_decoder.layout_embd.label_embed.weight": "layout_embd.label_embed.weight",
        "ele_decoder.layout_embd.label_embed.weight": "layout_embd.label_embed.weight",
        "encoder.embedding.bbox_embed.weight": "layout_embd.bbox_embed.weight",
        "group_decoder.layout_embd.bbox_embed.weight": "layout_embd.bbox_embed.weight",
        "ele_decoder.layout_embd.bbox_embed.weight": "layout_embd.bbox_embed.weight",
        "encoder.embedding.proj_cat.weight": "layout_embd.proj_cat.weight",
        "group_decoder.layout_embd.proj_cat.weight": "layout_embd.proj_cat.weight",
        "ele_decoder.layout_embd.proj_cat.weight": "layout_embd.proj_cat.weight",
        "encoder.embedding.proj_cat.bias": "layout_embd.proj_cat.bias",
        "group_decoder.layout_embd.proj_cat.bias": "layout_embd.proj_cat.bias",
        "ele_decoder.layout_embd.proj_cat.bias": "layout_embd.proj_cat.bias",
        "encoder.embedding.group_label_embed.weight": (
            "layout_embd.group_label_embed.weight"
        ),
        "group_decoder.layout_embd.group_label_embed.weight": (
            "layout_embd.group_label_embed.weight"
        ),
        "ele_decoder.layout_embd.group_label_embed.weight": (
            "layout_embd.group_label_embed.weight"
        ),
        "encoder.embedding.group_label_embed.bias": (
            "layout_embd.group_label_embed.bias"
        ),
        "group_decoder.layout_embd.group_label_embed.bias": (
            "layout_embd.group_label_embed.bias"
        ),
        "ele_decoder.layout_embd.group_label_embed.bias": (
            "layout_embd.group_label_embed.bias"
        ),
    }

    def __init__(self, config: CoarseToFineConfig) -> None:
        """Initialize checkpoint-compatible modules."""
        super().__init__(config)
        self.layout_embd = LayoutEmbedding(config)
        self.encoder = Encoder(config, self.layout_embd)
        self.vae = VAE(config)
        self.group_decoder = GroupDecoder(config, self.layout_embd)
        self.ele_decoder = ElementDecoder(config, self.layout_embd)
        self.all_tied_weights_keys = dict(self._tied_weights_keys)

    @property
    def device(self) -> torch.device:
        """Return the current parameter device."""
        return next(self.parameters()).device

    def _sample_latent(
        self,
        *,
        batch_size: int,
        generator: torch.Generator | None,
        device: torch.device,
    ) -> Float[torch.Tensor, "1 batch latent"]:
        return cast(
            torch.FloatTensor,
            randn(
                (1, batch_size, self.config.d_z),
                generator=generator,
                device=device,
            ),
        )

    def forward(
        self,
        labels: Int[torch.Tensor, "batch elements"],
        bbox: Int[torch.Tensor, "batch elements 4"],
        mask: Bool[torch.Tensor, "batch elements"],
        group_bounding_box: Int[torch.Tensor, "batch seq 4"],
        label_in_one_group: Float[torch.Tensor, "batch seq vocab"],
        group_mask: Bool[torch.Tensor, "batch seq"],
        grouped_bbox: Int[torch.Tensor, "batch seq elements 4"],
        grouped_labels: Int[torch.Tensor, "batch seq elements"],
        grouped_mask: Bool[torch.Tensor, "batch seq elements"],
        latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
        use_teacher_forcing: bool = True,
        return_dict: bool | None = None,
    ) -> (
        dict[str, Shaped[torch.Tensor, "..."]] | tuple[Shaped[torch.Tensor, "..."], ...]
    ):
        """Run teacher-forced or greedy hierarchical decoding.

        Args:
            labels: Batch-first internal label ids.
            bbox: Batch-first discrete ``ltwh`` ids.
            mask: Batch-first valid element mask.
            group_bounding_box: Batch-first group discrete ``ltwh`` ids.
            label_in_one_group: Batch-first group label histograms.
            group_mask: Batch-first valid group mask.
            grouped_bbox: Batch-first group-relative discrete ``ltwh`` ids.
            grouped_labels: Batch-first group-relative internal labels.
            grouped_mask: Batch-first valid grouped-element mask.
            latent_z: Optional latent tensor to bypass stochastic sampling.
            use_teacher_forcing: Whether to use provided hierarchy tensors.
            return_dict: Return a dictionary when true.

        Returns:
            Dictionary of raw logits and latent tensors.
        """
        batch_size, groups, seq, dims = grouped_bbox.shape
        seq_bbox = make_seq_first(bbox)
        seq_group_bbox = make_seq_first(group_bounding_box)
        grouped_box = make_seq_first(
            grouped_bbox.reshape(batch_size, groups * seq, dims)
        )
        grouped_box = make_seq_first(
            grouped_box.reshape(groups, seq, batch_size * dims)
        ).reshape(seq, groups, batch_size, dims)
        seq_labels = make_seq_first(labels.unsqueeze(2)).squeeze(2)
        seq_group_labels = make_seq_first(label_in_one_group)
        grouped_label = make_seq_first(
            grouped_labels.reshape(batch_size, groups * seq, 1)
        )
        grouped_label = make_seq_first(
            grouped_label.reshape(groups, seq, batch_size)
        ).unsqueeze(3)
        memory = self.encoder(seq_labels, seq_bbox, mask)
        if latent_z is None:
            z, mu, logvar = self.vae(memory)
        else:
            z = (
                latent_z
                if latent_z.dim() == 3 and latent_z.size(0) == 1
                else make_seq_first(latent_z)
            )
            mu = torch.zeros_like(z)
            logvar = torch.zeros_like(z)
        if use_teacher_forcing:
            group_embd, rec_group_bbox, rec_group_label = self.group_decoder(
                seq_group_labels, seq_group_bbox, z, group_mask
            )
            rec_box, rec_label = self.ele_decoder(
                grouped_label,
                grouped_box,
                group_embd,
                z,
                grouped_mask,
            )
        else:
            group_embd, rec_group_bbox, rec_group_label = self.group_decoder.inference(
                z, max_group_num=seq_group_labels.shape[0], device=self.device
            )
            rec_box, rec_label = self.ele_decoder.inference(
                group_embd,
                z,
                max_num_elements=grouped_label.shape[0],
                device=self.device,
            )
        rec_box = make_group_first(rec_box)
        rec_label = make_group_first(rec_label)
        flat_box = make_batch_first(rec_box.reshape(groups * seq, batch_size, -1))
        flat_label = make_batch_first(rec_label.reshape(groups * seq, batch_size, -1))
        rec_group_bbox = make_batch_first(rec_group_bbox)
        rec_group_label = make_batch_first(rec_group_label)
        output = {
            "group_bounding_box_logits": rec_group_bbox.reshape(
                batch_size, rec_group_bbox.size(1), 4, self.config.bbox_vocab_size
            ),
            "label_in_one_group_logits": rec_group_label,
            "grouped_bbox_logits": flat_box.reshape(
                batch_size, groups, seq, 4, self.config.bbox_vocab_size
            ),
            "grouped_label_logits": flat_label.reshape(batch_size, groups, seq, -1),
            "mu": make_batch_first(mu),
            "logvar": make_batch_first(logvar),
            "latent_z": make_batch_first(z),
        }
        if return_dict is False:
            return tuple(output.values())
        return output

    @torch.no_grad()
    def _decode_hierarchy(
        self, latent_z: Float[torch.Tensor, "1 batch latent"]
    ) -> dict[str, Shaped[torch.Tensor, "..."]]:
        """Decode raw hierarchy logits from a seq-first latent tensor."""
        z = latent_z.to(self.device)
        group_embd, group_bbox, group_label = self.group_decoder.inference(
            z,
            max_group_num=self.config.max_num_elements + 2,
            device=self.device,
        )
        elem_bbox, elem_label = self.ele_decoder.inference(
            group_embd,
            z,
            max_num_elements=self.config.max_num_elements + 2,
            device=self.device,
        )
        elem_bbox = make_group_first(elem_bbox)
        elem_label = make_group_first(elem_label)
        groups, seq, batch_size, _ = elem_bbox.shape
        return {
            "group_bounding_box_logits": make_batch_first(group_bbox).reshape(
                batch_size,
                group_bbox.size(0),
                4,
                self.config.bbox_vocab_size,
            ),
            "label_in_one_group_logits": make_batch_first(group_label),
            "grouped_bbox_logits": make_batch_first(
                elem_bbox.reshape(groups * seq, batch_size, -1)
            ).reshape(batch_size, groups, seq, 4, self.config.bbox_vocab_size),
            "grouped_label_logits": make_batch_first(
                elem_label.reshape(groups * seq, batch_size, -1)
            ).reshape(batch_size, groups, seq, -1),
        }

device property

device: device

Return the current parameter device.

__init__

__init__(config: CoarseToFineConfig) -> None

Initialize checkpoint-compatible modules.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
496
497
498
499
500
501
502
503
504
def __init__(self, config: CoarseToFineConfig) -> None:
    """Initialize checkpoint-compatible modules."""
    super().__init__(config)
    self.layout_embd = LayoutEmbedding(config)
    self.encoder = Encoder(config, self.layout_embd)
    self.vae = VAE(config)
    self.group_decoder = GroupDecoder(config, self.layout_embd)
    self.ele_decoder = ElementDecoder(config, self.layout_embd)
    self.all_tied_weights_keys = dict(self._tied_weights_keys)

forward

forward(
    labels: Int[Tensor, "batch elements"],
    bbox: Int[Tensor, "batch elements 4"],
    mask: Bool[Tensor, "batch elements"],
    group_bounding_box: Int[Tensor, "batch seq 4"],
    label_in_one_group: Float[Tensor, "batch seq vocab"],
    group_mask: Bool[Tensor, "batch seq"],
    grouped_bbox: Int[Tensor, "batch seq elements 4"],
    grouped_labels: Int[Tensor, "batch seq elements"],
    grouped_mask: Bool[Tensor, "batch seq elements"],
    latent_z: Float[Tensor, "1 batch latent"] | None = None,
    use_teacher_forcing: bool = True,
    return_dict: bool | None = None,
) -> (
    dict[str, Shaped[torch.Tensor, "..."]]
    | tuple[Shaped[torch.Tensor, "..."], ...]
)

Run teacher-forced or greedy hierarchical decoding.

Parameters:

Name Type Description Default
labels Int[Tensor, 'batch elements']

Batch-first internal label ids.

required
bbox Int[Tensor, 'batch elements 4']

Batch-first discrete ltwh ids.

required
mask Bool[Tensor, 'batch elements']

Batch-first valid element mask.

required
group_bounding_box Int[Tensor, 'batch seq 4']

Batch-first group discrete ltwh ids.

required
label_in_one_group Float[Tensor, 'batch seq vocab']

Batch-first group label histograms.

required
group_mask Bool[Tensor, 'batch seq']

Batch-first valid group mask.

required
grouped_bbox Int[Tensor, 'batch seq elements 4']

Batch-first group-relative discrete ltwh ids.

required
grouped_labels Int[Tensor, 'batch seq elements']

Batch-first group-relative internal labels.

required
grouped_mask Bool[Tensor, 'batch seq elements']

Batch-first valid grouped-element mask.

required
latent_z Float[Tensor, '1 batch latent'] | None

Optional latent tensor to bypass stochastic sampling.

None
use_teacher_forcing bool

Whether to use provided hierarchy tensors.

True
return_dict bool | None

Return a dictionary when true.

None

Returns:

Type Description
dict[str, Shaped[Tensor, '...']] | tuple[Shaped[Tensor, '...'], ...]

Dictionary of raw logits and latent tensors.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
def forward(
    self,
    labels: Int[torch.Tensor, "batch elements"],
    bbox: Int[torch.Tensor, "batch elements 4"],
    mask: Bool[torch.Tensor, "batch elements"],
    group_bounding_box: Int[torch.Tensor, "batch seq 4"],
    label_in_one_group: Float[torch.Tensor, "batch seq vocab"],
    group_mask: Bool[torch.Tensor, "batch seq"],
    grouped_bbox: Int[torch.Tensor, "batch seq elements 4"],
    grouped_labels: Int[torch.Tensor, "batch seq elements"],
    grouped_mask: Bool[torch.Tensor, "batch seq elements"],
    latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
    use_teacher_forcing: bool = True,
    return_dict: bool | None = None,
) -> (
    dict[str, Shaped[torch.Tensor, "..."]] | tuple[Shaped[torch.Tensor, "..."], ...]
):
    """Run teacher-forced or greedy hierarchical decoding.

    Args:
        labels: Batch-first internal label ids.
        bbox: Batch-first discrete ``ltwh`` ids.
        mask: Batch-first valid element mask.
        group_bounding_box: Batch-first group discrete ``ltwh`` ids.
        label_in_one_group: Batch-first group label histograms.
        group_mask: Batch-first valid group mask.
        grouped_bbox: Batch-first group-relative discrete ``ltwh`` ids.
        grouped_labels: Batch-first group-relative internal labels.
        grouped_mask: Batch-first valid grouped-element mask.
        latent_z: Optional latent tensor to bypass stochastic sampling.
        use_teacher_forcing: Whether to use provided hierarchy tensors.
        return_dict: Return a dictionary when true.

    Returns:
        Dictionary of raw logits and latent tensors.
    """
    batch_size, groups, seq, dims = grouped_bbox.shape
    seq_bbox = make_seq_first(bbox)
    seq_group_bbox = make_seq_first(group_bounding_box)
    grouped_box = make_seq_first(
        grouped_bbox.reshape(batch_size, groups * seq, dims)
    )
    grouped_box = make_seq_first(
        grouped_box.reshape(groups, seq, batch_size * dims)
    ).reshape(seq, groups, batch_size, dims)
    seq_labels = make_seq_first(labels.unsqueeze(2)).squeeze(2)
    seq_group_labels = make_seq_first(label_in_one_group)
    grouped_label = make_seq_first(
        grouped_labels.reshape(batch_size, groups * seq, 1)
    )
    grouped_label = make_seq_first(
        grouped_label.reshape(groups, seq, batch_size)
    ).unsqueeze(3)
    memory = self.encoder(seq_labels, seq_bbox, mask)
    if latent_z is None:
        z, mu, logvar = self.vae(memory)
    else:
        z = (
            latent_z
            if latent_z.dim() == 3 and latent_z.size(0) == 1
            else make_seq_first(latent_z)
        )
        mu = torch.zeros_like(z)
        logvar = torch.zeros_like(z)
    if use_teacher_forcing:
        group_embd, rec_group_bbox, rec_group_label = self.group_decoder(
            seq_group_labels, seq_group_bbox, z, group_mask
        )
        rec_box, rec_label = self.ele_decoder(
            grouped_label,
            grouped_box,
            group_embd,
            z,
            grouped_mask,
        )
    else:
        group_embd, rec_group_bbox, rec_group_label = self.group_decoder.inference(
            z, max_group_num=seq_group_labels.shape[0], device=self.device
        )
        rec_box, rec_label = self.ele_decoder.inference(
            group_embd,
            z,
            max_num_elements=grouped_label.shape[0],
            device=self.device,
        )
    rec_box = make_group_first(rec_box)
    rec_label = make_group_first(rec_label)
    flat_box = make_batch_first(rec_box.reshape(groups * seq, batch_size, -1))
    flat_label = make_batch_first(rec_label.reshape(groups * seq, batch_size, -1))
    rec_group_bbox = make_batch_first(rec_group_bbox)
    rec_group_label = make_batch_first(rec_group_label)
    output = {
        "group_bounding_box_logits": rec_group_bbox.reshape(
            batch_size, rec_group_bbox.size(1), 4, self.config.bbox_vocab_size
        ),
        "label_in_one_group_logits": rec_group_label,
        "grouped_bbox_logits": flat_box.reshape(
            batch_size, groups, seq, 4, self.config.bbox_vocab_size
        ),
        "grouped_label_logits": flat_label.reshape(batch_size, groups, seq, -1),
        "mu": make_batch_first(mu),
        "logvar": make_batch_first(logvar),
        "latent_z": make_batch_first(z),
    }
    if return_dict is False:
        return tuple(output.values())
    return output

CoarseToFinePipeline

Bases: Pipeline

Orchestrate Coarse-to-Fine layout generation.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
class CoarseToFinePipeline(Pipeline):
    """Orchestrate Coarse-to-Fine layout generation."""

    model: CoarseToFineForLayoutGeneration
    processor: CoarseToFineProcessor

    def __init__(
        self,
        model: CoarseToFineForLayoutGeneration,
        processor: CoarseToFineProcessor,
    ) -> None:
        """Initialize the pipeline with a model and processor."""
        super().__init__(model=model, tokenizer=None)
        self.processor = processor

    def _sanitize_parameters(
        self, **kwargs: CoarseToFinePipelineParameter
    ) -> tuple[
        dict[str, CoarseToFinePipelineParameter],
        dict[str, CoarseToFinePipelineParameter],
        dict[str, CoarseToFinePipelineParameter],
    ]:
        return {}, kwargs, {}

    def preprocess(
        self,
        input_: CoarseToFinePipelineParameter,
        **preprocess_parameters: dict[str, CoarseToFinePipelineParameter],
    ) -> dict[str, GenericTensor]:
        """Satisfy the abstract pipeline API; direct calls bypass this path."""
        _ = (input_, preprocess_parameters)
        return {}

    def _forward(
        self,
        input_tensors: dict[str, GenericTensor],
        **forward_parameters: dict[str, CoarseToFinePipelineParameter],
    ) -> ModelOutput:
        _ = (input_tensors, forward_parameters)
        raise NotImplementedError("Use CoarseToFinePipeline.__call__ directly")

    def postprocess(
        self,
        model_outputs: LayoutGenerationOutput | CoarseToFineOutputDict,
        **kwargs: dict[str, CoarseToFinePipelineParameter],
    ) -> LayoutGenerationOutput | CoarseToFineOutputDict:
        """Return model outputs without additional formatting."""
        _ = kwargs
        return model_outputs

    def __call__(
        self,
        *,
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: ConditionType | str = ConditionType.unconditional,
        labels: Int[torch.Tensor, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        num_inference_steps: int | None = None,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
        latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
    ) -> LayoutGenerationOutput | CoarseToFineOutputDict:
        """Generate layouts through model decode and processor post-processing."""
        del labels, bbox, mask, num_elements
        del box_format, normalized, canvas_size, num_inference_steps
        condition = normalize_condition_type(condition_type)
        if condition is not ConditionType.unconditional:
            raise NotImplementedError(
                "Coarse-to-Fine released checkpoints support only unconditional generation"
            )

        generator = resolve_torch_generator(generator=generator, seed=seed)
        if latent_z is None:
            sampled_z = self.model._sample_latent(
                batch_size=batch_size,
                generator=generator,
                device=self.model.device,
            )
        else:
            sampled_z = cast(torch.FloatTensor, latent_z.to(self.model.device))
        raw = self.model._decode_hierarchy(sampled_z)
        hierarchy = decode_hierarchy_from_logits(
            group_bbox_logits=raw["group_bounding_box_logits"],
            group_label_logits=raw["label_in_one_group_logits"],
            grouped_bbox_logits=raw["grouped_bbox_logits"],
            grouped_label_logits=raw["grouped_label_logits"],
            num_labels=self.model.config.num_labels,
            group_eos_index=self.model.config.group_eos_index,
            element_eos_id=self.model.config.element_eos_id,
            discrete_x_grid=self.model.config.discrete_x_grid,
            discrete_y_grid=self.model.config.discrete_y_grid,
        )
        output = cast(
            LayoutGenerationOutput,
            self.processor.post_process_hierarchy(
                hierarchy,
                output_type=OutputType.dataclass,
                return_intermediates=return_intermediates,
            ),
        )
        output.sequences = cast(torch.Tensor, hierarchy.discrete_relative_bbox)
        output.scores = None
        output.trajectory = raw if return_intermediates else None
        if normalize_output_type(output_type) is OutputType.dict:
            return dict(output)
        return output

__init__

__init__(
    model: CoarseToFineForLayoutGeneration,
    processor: CoarseToFineProcessor,
) -> None

Initialize the pipeline with a model and processor.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
49
50
51
52
53
54
55
56
def __init__(
    self,
    model: CoarseToFineForLayoutGeneration,
    processor: CoarseToFineProcessor,
) -> None:
    """Initialize the pipeline with a model and processor."""
    super().__init__(model=model, tokenizer=None)
    self.processor = processor

preprocess

preprocess(
    input_: CoarseToFinePipelineParameter,
    **preprocess_parameters: dict[
        str, CoarseToFinePipelineParameter
    ],
) -> dict[str, GenericTensor]

Satisfy the abstract pipeline API; direct calls bypass this path.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
67
68
69
70
71
72
73
74
def preprocess(
    self,
    input_: CoarseToFinePipelineParameter,
    **preprocess_parameters: dict[str, CoarseToFinePipelineParameter],
) -> dict[str, GenericTensor]:
    """Satisfy the abstract pipeline API; direct calls bypass this path."""
    _ = (input_, preprocess_parameters)
    return {}

postprocess

postprocess(
    model_outputs: LayoutGenerationOutput
    | CoarseToFineOutputDict,
    **kwargs: dict[str, CoarseToFinePipelineParameter],
) -> LayoutGenerationOutput | CoarseToFineOutputDict

Return model outputs without additional formatting.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
84
85
86
87
88
89
90
91
def postprocess(
    self,
    model_outputs: LayoutGenerationOutput | CoarseToFineOutputDict,
    **kwargs: dict[str, CoarseToFinePipelineParameter],
) -> LayoutGenerationOutput | CoarseToFineOutputDict:
    """Return model outputs without additional formatting."""
    _ = kwargs
    return model_outputs

__call__

__call__(
    *,
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: ConditionType
    | str = ConditionType.unconditional,
    labels: Int[Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int
    | list[int]
    | Int[Tensor, "batch"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latent_z: Float[Tensor, "1 batch latent"] | None = None,
) -> LayoutGenerationOutput | CoarseToFineOutputDict

Generate layouts through model decode and processor post-processing.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def __call__(
    self,
    *,
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: ConditionType | str = ConditionType.unconditional,
    labels: Int[torch.Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
) -> LayoutGenerationOutput | CoarseToFineOutputDict:
    """Generate layouts through model decode and processor post-processing."""
    del labels, bbox, mask, num_elements
    del box_format, normalized, canvas_size, num_inference_steps
    condition = normalize_condition_type(condition_type)
    if condition is not ConditionType.unconditional:
        raise NotImplementedError(
            "Coarse-to-Fine released checkpoints support only unconditional generation"
        )

    generator = resolve_torch_generator(generator=generator, seed=seed)
    if latent_z is None:
        sampled_z = self.model._sample_latent(
            batch_size=batch_size,
            generator=generator,
            device=self.model.device,
        )
    else:
        sampled_z = cast(torch.FloatTensor, latent_z.to(self.model.device))
    raw = self.model._decode_hierarchy(sampled_z)
    hierarchy = decode_hierarchy_from_logits(
        group_bbox_logits=raw["group_bounding_box_logits"],
        group_label_logits=raw["label_in_one_group_logits"],
        grouped_bbox_logits=raw["grouped_bbox_logits"],
        grouped_label_logits=raw["grouped_label_logits"],
        num_labels=self.model.config.num_labels,
        group_eos_index=self.model.config.group_eos_index,
        element_eos_id=self.model.config.element_eos_id,
        discrete_x_grid=self.model.config.discrete_x_grid,
        discrete_y_grid=self.model.config.discrete_y_grid,
    )
    output = cast(
        LayoutGenerationOutput,
        self.processor.post_process_hierarchy(
            hierarchy,
            output_type=OutputType.dataclass,
            return_intermediates=return_intermediates,
        ),
    )
    output.sequences = cast(torch.Tensor, hierarchy.discrete_relative_bbox)
    output.scores = None
    output.trajectory = raw if return_intermediates else None
    if normalize_output_type(output_type) is OutputType.dict:
        return dict(output)
    return output

CoarseToFineProcessor

Bases: ProcessorMixin

Convert public layouts to Coarse-to-Fine hierarchy tensors.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
class CoarseToFineProcessor(ProcessorMixin):
    """Convert public layouts to Coarse-to-Fine hierarchy tensors."""

    attributes: list[str] = []
    processor_class = "CoarseToFineProcessor"

    def __init__(
        self,
        dataset: DatasetName | str = DatasetName.rico25,
        *,
        x_grid: int = 128,
        y_grid: int = 128,
        max_num_elements: int = 20,
        id2label: dict[int, str] | None = None,
    ) -> None:
        """Initialize label maps and discretization settings.

        Args:
            dataset: Canonical layout dataset.
            x_grid: Number of x/width bins.
            y_grid: Number of y/height bins.
            max_num_elements: Padded flat sequence length.
            id2label: Optional public label map.

        Examples:
            >>> CoarseToFineProcessor(dataset="publaynet").id2label[0]
            'text'
        """
        normalized_dataset = normalize_dataset_name(dataset)
        self.dataset = str(normalized_dataset)
        self.x_grid = x_grid
        self.y_grid = y_grid
        self.max_num_elements = max_num_elements
        self.id2label = id2label or id2label_for_dataset(normalized_dataset)
        self.label2id = label2id_for_dataset(normalized_dataset)

    @classmethod
    def from_config(
        cls,
        dataset: DatasetName | str = DatasetName.rico25,
        *,
        x_grid: int = 128,
        y_grid: int = 128,
        max_num_elements: int = 20,
        id2label: dict[int, str] | None = None,
    ) -> "CoarseToFineProcessor":
        """Construct a processor without external files."""
        return cls(
            dataset=dataset,
            x_grid=x_grid,
            y_grid=y_grid,
            max_num_elements=max_num_elements,
            id2label=id2label,
        )

    def to_dict(self) -> dict[str, str | int | dict[str, str]]:
        """Serialize processor state to a JSON-compatible dictionary."""
        return {
            "processor_class": self.processor_class,
            "dataset": self.dataset,
            "x_grid": self.x_grid,
            "y_grid": self.y_grid,
            "max_num_elements": self.max_num_elements,
            "id2label": {str(key): value for key, value in self.id2label.items()},
        }

    def save_pretrained(
        self,
        save_directory: str | PathLike[str],
        push_to_hub: bool = False,
        **kwargs: str | int | float | bool | None,
    ) -> list[str]:
        """Save processor metadata next to a converted checkpoint."""
        _ = (push_to_hub, kwargs)
        path = Path(save_directory)
        path.mkdir(parents=True, exist_ok=True)
        out_file = path / "preprocessor_config.json"
        out_file.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n")
        return [str(out_file)]

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | PathLike[str],
        cache_dir: str | PathLike[str] | None = None,
        force_download: bool = False,
        local_files_only: bool = False,
        token: str | bool | None = None,
        revision: str = "main",
        **kwargs: str | int | float | bool | None,
    ) -> "CoarseToFineProcessor":
        """Load processor metadata from a local ``save_pretrained`` directory."""
        _ = (cache_dir, force_download, local_files_only, token, revision, kwargs)
        path = Path(pretrained_model_name_or_path) / "preprocessor_config.json"
        data = json.loads(path.read_text())
        return cls(
            dataset=str(data["dataset"]),
            x_grid=int(data["x_grid"]),
            y_grid=int(data["y_grid"]),
            max_num_elements=int(data["max_num_elements"]),
            id2label={int(key): str(value) for key, value in data["id2label"].items()},
        )

    def _labels_to_vendor(
        self, labels: Int[torch.Tensor, "..."]
    ) -> Int[torch.Tensor, "..."]:
        return cast(torch.LongTensor, labels.long() + 1)

    def __call__(
        self,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | None = None,
        bbox: list[list[list[float]]]
        | Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        return_tensors: Literal["pt"] = "pt",
    ) -> BatchEncoding:
        """Build discrete flat tensors from public layout inputs.

        Args:
            labels: Public zero-based labels or label strings.
            bbox: Public normalized boxes.
            mask: Optional valid-element mask.
            box_format: Input box format.
            normalized: Must be true; pixel boxes are dataset-loader work.
            return_tensors: Only ``"pt"`` is supported.

        Returns:
            BatchEncoding with internal labels, discrete boxes, and masks.

        Raises:
            ValueError: If labels or boxes are missing.
        """
        if return_tensors != "pt":
            raise ValueError("Only return_tensors='pt' is supported")

        if not normalized:
            raise ValueError("CoarseToFineProcessor expects normalized boxes")

        if labels is None or bbox is None:
            raise ValueError("labels and bbox are required for processor encoding")

        label_tensor = self._coerce_labels(labels)
        bbox_tensor = torch.as_tensor(bbox, dtype=torch.float32)
        encoded_mask = (
            cast(torch.BoolTensor, torch.ones(label_tensor.shape, dtype=torch.bool))
            if mask is None
            else cast(torch.BoolTensor, torch.as_tensor(mask, dtype=torch.bool))
        )
        ltwh = public_to_ltwh(bbox_tensor, box_format=box_format)
        discrete = discretize_ltwh(ltwh, num_x_grid=self.x_grid, num_y_grid=self.y_grid)
        vendor_labels = self._labels_to_vendor(label_tensor)
        return BatchEncoding(
            {"labels": vendor_labels, "bbox": discrete, "mask": encoded_mask}
        )

    def _coerce_labels(
        self,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"],
    ) -> Int[torch.Tensor, "batch elements"]:
        if isinstance(labels, torch.Tensor | np.ndarray):
            return cast(torch.LongTensor, torch.as_tensor(labels, dtype=torch.long))
        rows: list[list[int]] = []
        for row in labels:
            values: list[int] = []
            for label in row:
                if isinstance(label, int):
                    values.append(label)
                else:
                    values.append(self.label2id[label])
            rows.append(values)
        max_len = max((len(row) for row in rows), default=1)
        padded = [row + [0] * (max_len - len(row)) for row in rows]
        return cast(torch.LongTensor, torch.tensor(padded, dtype=torch.long))

    def build_hierarchy_batch(
        self,
        labels: Int[torch.Tensor, "batch elements"],
        bbox: Float[torch.Tensor, "batch elements 4"],
        mask: Bool[torch.Tensor, "batch elements"],
    ) -> BatchEncoding:
        """Build padded hierarchy tensors for training/reference batches."""
        batch = labels.size(0)
        flat_enc = self(labels=labels, bbox=bbox, mask=mask, return_tensors="pt")
        encodings = []
        for idx in range(batch):
            valid = mask[idx].bool()
            encodings.append(
                build_cut_hierarchy(
                    public_to_ltwh(bbox[idx, valid]),
                    self._labels_to_vendor(labels[idx, valid]),
                    num_labels=len(self.id2label),
                    discrete_x_grid=self.x_grid,
                    discrete_y_grid=self.y_grid,
                )
            )
        max_groups = max(enc.group_bounding_box.size(0) for enc in encodings) + 2
        max_decode_groups = max_groups - 2
        max_group_elems = (
            max(max(group.size(0) for group in enc.grouped_labels) for enc in encodings)
            + 2
        )
        group_boxes = []
        group_labels = []
        group_masks = []
        grouped_boxes = []
        grouped_labels = []
        grouped_masks = []
        for enc in encodings:
            group_count = enc.group_bounding_box.size(0)
            sos_group_box = torch.zeros((1, 4), dtype=torch.long)
            eos_group_box = torch.zeros((1, 4), dtype=torch.long)
            padded_group_box = torch.cat(
                (sos_group_box, enc.group_bounding_box.long(), eos_group_box), dim=0
            )
            padded_group_box = F.pad(
                padded_group_box, (0, 0, 0, max_groups - padded_group_box.size(0))
            )
            hist = torch.zeros(
                (group_count + 2, len(self.id2label) + 2), dtype=torch.float32
            )
            hist[0, 0] = 1.0
            hist[1 : group_count + 1, 1:-1] = enc.label_in_one_group.float()
            hist[group_count + 1, -1] = 1.0
            hist = F.pad(hist, (0, 0, 0, max_groups - hist.size(0)))
            group_mask = torch.arange(max_groups) < group_count + 2
            per_group_boxes = []
            per_group_labels = []
            per_group_masks = []
            for group_idx in range(group_count):
                box = enc.grouped_bbox[group_idx].long()
                label = enc.grouped_labels[group_idx].long()
                label = torch.cat(
                    (
                        torch.tensor([len(self.id2label) + 1]),
                        label,
                        torch.tensor([len(self.id2label) + 2]),
                    )
                )
                box = torch.cat(
                    (
                        torch.zeros((1, 4), dtype=torch.long),
                        box,
                        torch.zeros((1, 4), dtype=torch.long),
                    )
                )
                valid_count = min(label.size(0), max_group_elems)
                per_group_boxes.append(
                    F.pad(
                        box[:max_group_elems], (0, 0, 0, max_group_elems - valid_count)
                    )
                )
                per_group_labels.append(
                    F.pad(label[:max_group_elems], (0, max_group_elems - valid_count))
                )
                per_group_masks.append(torch.arange(max_group_elems) < valid_count)
            while len(per_group_boxes) < max_decode_groups:
                per_group_boxes.append(
                    torch.zeros((max_group_elems, 4), dtype=torch.long)
                )
                per_group_labels.append(
                    torch.zeros((max_group_elems,), dtype=torch.long)
                )
                per_group_masks.append(
                    torch.zeros((max_group_elems,), dtype=torch.bool)
                )
            group_boxes.append(padded_group_box)
            group_labels.append(hist)
            group_masks.append(group_mask)
            grouped_boxes.append(torch.stack(per_group_boxes[:max_decode_groups]))
            grouped_labels.append(torch.stack(per_group_labels[:max_decode_groups]))
            grouped_masks.append(torch.stack(per_group_masks[:max_decode_groups]))
        flat_enc.update(
            {
                "group_bounding_box": torch.stack(group_boxes),
                "label_in_one_group": torch.stack(group_labels),
                "group_mask": torch.stack(group_masks),
                "grouped_bbox": torch.stack(grouped_boxes),
                "grouped_labels": torch.stack(grouped_labels),
                "grouped_mask": torch.stack(grouped_masks),
            }
        )
        return flat_enc

    def post_process_hierarchy(
        self,
        hierarchy: CoarseToFineHierarchy,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
    ) -> (
        LayoutGenerationOutput
        | dict[
            str,
            Float[torch.Tensor, "..."]
            | Int[torch.Tensor, "..."]
            | Bool[torch.Tensor, "..."]
            | dict[int, str]
            | None,
        ]
    ):
        """Convert decoded hierarchy tensors to the shared output schema."""
        output = flatten_hierarchy(
            hierarchy,
            id2label=dict(self.id2label),
            max_num_elements=self.max_num_elements,
        )
        if not return_intermediates:
            output.intermediates = None
        if normalize_output_type(output_type) is OutputType.dict:
            return dict(output)
        return output

__init__

__init__(
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> None

Initialize label maps and discretization settings.

Parameters:

Name Type Description Default
dataset DatasetName | str

Canonical layout dataset.

rico25
x_grid int

Number of x/width bins.

128
y_grid int

Number of y/height bins.

128
max_num_elements int

Padded flat sequence length.

20
id2label dict[int, str] | None

Optional public label map.

None

Examples:

>>> CoarseToFineProcessor(dataset="publaynet").id2label[0]
'text'
Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def __init__(
    self,
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> None:
    """Initialize label maps and discretization settings.

    Args:
        dataset: Canonical layout dataset.
        x_grid: Number of x/width bins.
        y_grid: Number of y/height bins.
        max_num_elements: Padded flat sequence length.
        id2label: Optional public label map.

    Examples:
        >>> CoarseToFineProcessor(dataset="publaynet").id2label[0]
        'text'
    """
    normalized_dataset = normalize_dataset_name(dataset)
    self.dataset = str(normalized_dataset)
    self.x_grid = x_grid
    self.y_grid = y_grid
    self.max_num_elements = max_num_elements
    self.id2label = id2label or id2label_for_dataset(normalized_dataset)
    self.label2id = label2id_for_dataset(normalized_dataset)

from_config classmethod

from_config(
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> "CoarseToFineProcessor"

Construct a processor without external files.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@classmethod
def from_config(
    cls,
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> "CoarseToFineProcessor":
    """Construct a processor without external files."""
    return cls(
        dataset=dataset,
        x_grid=x_grid,
        y_grid=y_grid,
        max_num_elements=max_num_elements,
        id2label=id2label,
    )

to_dict

to_dict() -> dict[str, str | int | dict[str, str]]

Serialize processor state to a JSON-compatible dictionary.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
81
82
83
84
85
86
87
88
89
90
def to_dict(self) -> dict[str, str | int | dict[str, str]]:
    """Serialize processor state to a JSON-compatible dictionary."""
    return {
        "processor_class": self.processor_class,
        "dataset": self.dataset,
        "x_grid": self.x_grid,
        "y_grid": self.y_grid,
        "max_num_elements": self.max_num_elements,
        "id2label": {str(key): value for key, value in self.id2label.items()},
    }

save_pretrained

save_pretrained(
    save_directory: str | PathLike[str],
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> list[str]

Save processor metadata next to a converted checkpoint.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def save_pretrained(
    self,
    save_directory: str | PathLike[str],
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> list[str]:
    """Save processor metadata next to a converted checkpoint."""
    _ = (push_to_hub, kwargs)
    path = Path(save_directory)
    path.mkdir(parents=True, exist_ok=True)
    out_file = path / "preprocessor_config.json"
    out_file.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n")
    return [str(out_file)]

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    cache_dir: str | PathLike[str] | None = None,
    force_download: bool = False,
    local_files_only: bool = False,
    token: str | bool | None = None,
    revision: str = "main",
    **kwargs: str | int | float | bool | None,
) -> "CoarseToFineProcessor"

Load processor metadata from a local save_pretrained directory.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | PathLike[str],
    cache_dir: str | PathLike[str] | None = None,
    force_download: bool = False,
    local_files_only: bool = False,
    token: str | bool | None = None,
    revision: str = "main",
    **kwargs: str | int | float | bool | None,
) -> "CoarseToFineProcessor":
    """Load processor metadata from a local ``save_pretrained`` directory."""
    _ = (cache_dir, force_download, local_files_only, token, revision, kwargs)
    path = Path(pretrained_model_name_or_path) / "preprocessor_config.json"
    data = json.loads(path.read_text())
    return cls(
        dataset=str(data["dataset"]),
        x_grid=int(data["x_grid"]),
        y_grid=int(data["y_grid"]),
        max_num_elements=int(data["max_num_elements"]),
        id2label={int(key): str(value) for key, value in data["id2label"].items()},
    )

__call__

__call__(
    labels: list[list[int | str]]
    | Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | None = None,
    bbox: list[list[list[float]]]
    | Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding

Build discrete flat tensors from public layout inputs.

Parameters:

Name Type Description Default
labels list[list[int | str]] | Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | None

Public zero-based labels or label strings.

None
bbox list[list[list[float]]] | Float[Tensor, 'batch elements 4'] | Float[ndarray, 'batch elements 4'] | None

Public normalized boxes.

None
mask Bool[Tensor, 'batch elements'] | Bool[ndarray, 'batch elements'] | None

Optional valid-element mask.

None
box_format BoxFormat | str

Input box format.

xywh
normalized bool

Must be true; pixel boxes are dataset-loader work.

True
return_tensors Literal['pt']

Only "pt" is supported.

'pt'

Returns:

Type Description
BatchEncoding

BatchEncoding with internal labels, discrete boxes, and masks.

Raises:

Type Description
ValueError

If labels or boxes are missing.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def __call__(
    self,
    labels: list[list[int | str]]
    | Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | None = None,
    bbox: list[list[list[float]]]
    | Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding:
    """Build discrete flat tensors from public layout inputs.

    Args:
        labels: Public zero-based labels or label strings.
        bbox: Public normalized boxes.
        mask: Optional valid-element mask.
        box_format: Input box format.
        normalized: Must be true; pixel boxes are dataset-loader work.
        return_tensors: Only ``"pt"`` is supported.

    Returns:
        BatchEncoding with internal labels, discrete boxes, and masks.

    Raises:
        ValueError: If labels or boxes are missing.
    """
    if return_tensors != "pt":
        raise ValueError("Only return_tensors='pt' is supported")

    if not normalized:
        raise ValueError("CoarseToFineProcessor expects normalized boxes")

    if labels is None or bbox is None:
        raise ValueError("labels and bbox are required for processor encoding")

    label_tensor = self._coerce_labels(labels)
    bbox_tensor = torch.as_tensor(bbox, dtype=torch.float32)
    encoded_mask = (
        cast(torch.BoolTensor, torch.ones(label_tensor.shape, dtype=torch.bool))
        if mask is None
        else cast(torch.BoolTensor, torch.as_tensor(mask, dtype=torch.bool))
    )
    ltwh = public_to_ltwh(bbox_tensor, box_format=box_format)
    discrete = discretize_ltwh(ltwh, num_x_grid=self.x_grid, num_y_grid=self.y_grid)
    vendor_labels = self._labels_to_vendor(label_tensor)
    return BatchEncoding(
        {"labels": vendor_labels, "bbox": discrete, "mask": encoded_mask}
    )

build_hierarchy_batch

build_hierarchy_batch(
    labels: Int[Tensor, "batch elements"],
    bbox: Float[Tensor, "batch elements 4"],
    mask: Bool[Tensor, "batch elements"],
) -> BatchEncoding

Build padded hierarchy tensors for training/reference batches.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
def build_hierarchy_batch(
    self,
    labels: Int[torch.Tensor, "batch elements"],
    bbox: Float[torch.Tensor, "batch elements 4"],
    mask: Bool[torch.Tensor, "batch elements"],
) -> BatchEncoding:
    """Build padded hierarchy tensors for training/reference batches."""
    batch = labels.size(0)
    flat_enc = self(labels=labels, bbox=bbox, mask=mask, return_tensors="pt")
    encodings = []
    for idx in range(batch):
        valid = mask[idx].bool()
        encodings.append(
            build_cut_hierarchy(
                public_to_ltwh(bbox[idx, valid]),
                self._labels_to_vendor(labels[idx, valid]),
                num_labels=len(self.id2label),
                discrete_x_grid=self.x_grid,
                discrete_y_grid=self.y_grid,
            )
        )
    max_groups = max(enc.group_bounding_box.size(0) for enc in encodings) + 2
    max_decode_groups = max_groups - 2
    max_group_elems = (
        max(max(group.size(0) for group in enc.grouped_labels) for enc in encodings)
        + 2
    )
    group_boxes = []
    group_labels = []
    group_masks = []
    grouped_boxes = []
    grouped_labels = []
    grouped_masks = []
    for enc in encodings:
        group_count = enc.group_bounding_box.size(0)
        sos_group_box = torch.zeros((1, 4), dtype=torch.long)
        eos_group_box = torch.zeros((1, 4), dtype=torch.long)
        padded_group_box = torch.cat(
            (sos_group_box, enc.group_bounding_box.long(), eos_group_box), dim=0
        )
        padded_group_box = F.pad(
            padded_group_box, (0, 0, 0, max_groups - padded_group_box.size(0))
        )
        hist = torch.zeros(
            (group_count + 2, len(self.id2label) + 2), dtype=torch.float32
        )
        hist[0, 0] = 1.0
        hist[1 : group_count + 1, 1:-1] = enc.label_in_one_group.float()
        hist[group_count + 1, -1] = 1.0
        hist = F.pad(hist, (0, 0, 0, max_groups - hist.size(0)))
        group_mask = torch.arange(max_groups) < group_count + 2
        per_group_boxes = []
        per_group_labels = []
        per_group_masks = []
        for group_idx in range(group_count):
            box = enc.grouped_bbox[group_idx].long()
            label = enc.grouped_labels[group_idx].long()
            label = torch.cat(
                (
                    torch.tensor([len(self.id2label) + 1]),
                    label,
                    torch.tensor([len(self.id2label) + 2]),
                )
            )
            box = torch.cat(
                (
                    torch.zeros((1, 4), dtype=torch.long),
                    box,
                    torch.zeros((1, 4), dtype=torch.long),
                )
            )
            valid_count = min(label.size(0), max_group_elems)
            per_group_boxes.append(
                F.pad(
                    box[:max_group_elems], (0, 0, 0, max_group_elems - valid_count)
                )
            )
            per_group_labels.append(
                F.pad(label[:max_group_elems], (0, max_group_elems - valid_count))
            )
            per_group_masks.append(torch.arange(max_group_elems) < valid_count)
        while len(per_group_boxes) < max_decode_groups:
            per_group_boxes.append(
                torch.zeros((max_group_elems, 4), dtype=torch.long)
            )
            per_group_labels.append(
                torch.zeros((max_group_elems,), dtype=torch.long)
            )
            per_group_masks.append(
                torch.zeros((max_group_elems,), dtype=torch.bool)
            )
        group_boxes.append(padded_group_box)
        group_labels.append(hist)
        group_masks.append(group_mask)
        grouped_boxes.append(torch.stack(per_group_boxes[:max_decode_groups]))
        grouped_labels.append(torch.stack(per_group_labels[:max_decode_groups]))
        grouped_masks.append(torch.stack(per_group_masks[:max_decode_groups]))
    flat_enc.update(
        {
            "group_bounding_box": torch.stack(group_boxes),
            "label_in_one_group": torch.stack(group_labels),
            "group_mask": torch.stack(group_masks),
            "grouped_bbox": torch.stack(grouped_boxes),
            "grouped_labels": torch.stack(grouped_labels),
            "grouped_mask": torch.stack(grouped_masks),
        }
    )
    return flat_enc

post_process_hierarchy

post_process_hierarchy(
    hierarchy: CoarseToFineHierarchy,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
) -> (
    LayoutGenerationOutput
    | dict[
        str,
        Float[torch.Tensor, "..."]
        | Int[torch.Tensor, "..."]
        | Bool[torch.Tensor, "..."]
        | dict[int, str]
        | None,
    ]
)

Convert decoded hierarchy tensors to the shared output schema.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
def post_process_hierarchy(
    self,
    hierarchy: CoarseToFineHierarchy,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
) -> (
    LayoutGenerationOutput
    | dict[
        str,
        Float[torch.Tensor, "..."]
        | Int[torch.Tensor, "..."]
        | Bool[torch.Tensor, "..."]
        | dict[int, str]
        | None,
    ]
):
    """Convert decoded hierarchy tensors to the shared output schema."""
    output = flatten_hierarchy(
        hierarchy,
        id2label=dict(self.id2label),
        max_num_elements=self.max_num_elements,
    )
    if not return_intermediates:
        output.intermediates = None
    if normalize_output_type(output_type) is OutputType.dict:
        return dict(output)
    return output

OutputType

Bases: StrEnum

Supported output containers.

Source code in models/coarse-to-fine/src/coarse_to_fine/types.py
10
11
12
13
14
class OutputType(StrEnum):
    """Supported output containers."""

    dataclass = auto()
    dict = auto()

configuration_coarse_to_fine

Configuration for Coarse-to-Fine checkpoints.

CoarseToFineConfig

Bases: PretrainedConfig

Stores architecture, discretization, and label metadata.

Parameters:

Name Type Description Default
dataset DatasetName | str

Dataset name for the converted checkpoint.

rico25
num_labels int | None

Optional label vocabulary size. Defaults to the dataset vocabulary length.

None
id2label Mapping[int | str, str] | None

Optional label id to display-label mapping.

None
label2id Mapping[str, int] | None

Optional display-label to id mapping.

None
max_num_elements int

Maximum flat element count used by the checkpoint model.

20
discrete_x_grid int

Number of x-axis bins.

128
discrete_y_grid int

Number of y-axis bins.

128
d_model int

Transformer hidden dimension.

512
d_z int

VAE latent dimension.

512
n_layers int

Number of encoder layers.

4
n_layers_decoder int

Number of decoder layers.

4
n_heads int

Number of attention heads.

8
dim_feedforward int

Transformer feed-forward dimension.

2048
dropout float

Dropout probability.

0.1
internal_box_format BoxFormat | str

Reference internal box format.

ltwh
public_box_format BoxFormat | str

Public output box format.

xywh
vendor_label_offset int

Offset from public label ids to internal ids.

1
**kwargs str | int | float | bool | None

Additional PretrainedConfig fields.

{}

Examples:

>>> CoarseToFineConfig(dataset="publaynet").num_labels
5
Source code in models/coarse-to-fine/src/coarse_to_fine/configuration_coarse_to_fine.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
class CoarseToFineConfig(PretrainedConfig):
    """Stores architecture, discretization, and label metadata.

    Args:
        dataset: Dataset name for the converted checkpoint.
        num_labels: Optional label vocabulary size. Defaults to the dataset
            vocabulary length.
        id2label: Optional label id to display-label mapping.
        label2id: Optional display-label to id mapping.
        max_num_elements: Maximum flat element count used by the checkpoint model.
        discrete_x_grid: Number of x-axis bins.
        discrete_y_grid: Number of y-axis bins.
        d_model: Transformer hidden dimension.
        d_z: VAE latent dimension.
        n_layers: Number of encoder layers.
        n_layers_decoder: Number of decoder layers.
        n_heads: Number of attention heads.
        dim_feedforward: Transformer feed-forward dimension.
        dropout: Dropout probability.
        internal_box_format: Reference internal box format.
        public_box_format: Public output box format.
        vendor_label_offset: Offset from public label ids to internal ids.
        **kwargs: Additional ``PretrainedConfig`` fields.

    Examples:
        >>> CoarseToFineConfig(dataset="publaynet").num_labels
        5
    """

    model_type = "coarse_to_fine"

    def __init__(
        self,
        dataset: DatasetName | str = DatasetName.rico25,
        num_labels: int | None = None,
        id2label: Mapping[int | str, str] | None = None,
        label2id: Mapping[str, int] | None = None,
        max_num_elements: int = 20,
        discrete_x_grid: int = 128,
        discrete_y_grid: int = 128,
        d_model: int = 512,
        d_z: int = 512,
        n_layers: int = 4,
        n_layers_decoder: int = 4,
        n_heads: int = 8,
        dim_feedforward: int = 2048,
        dropout: float = 0.1,
        internal_box_format: BoxFormat | str = BoxFormat.ltwh,
        public_box_format: BoxFormat | str = BoxFormat.xywh,
        vendor_label_offset: int = 1,
        eval_batch_size: int | None = None,
        **kwargs: str | int | float | bool | None,
    ) -> None:
        """Initialize Coarse-to-Fine config values."""
        normalized_dataset = normalize_dataset_name(dataset)
        if normalized_dataset not in SUPPORTED_DATASETS:
            raise ValueError(f"Unsupported Coarse-to-Fine dataset: {dataset}")

        resolved_id2label = (
            {int(key): str(value) for key, value in id2label.items()}
            if id2label is not None
            else id2label_for_dataset(normalized_dataset)
        )
        resolved_label2id = (
            {str(key): int(value) for key, value in label2id.items()}
            if label2id is not None
            else label2id_for_dataset(normalized_dataset)
        )
        resolved_num_labels = num_labels or len(resolved_id2label)
        super().__init__(id2label=resolved_id2label, label2id=resolved_label2id)
        for key, value in kwargs.items():
            setattr(self, key, value)

        self.dataset = str(normalized_dataset)
        self.num_labels = resolved_num_labels
        self.max_num_elements = max_num_elements
        self.discrete_x_grid = discrete_x_grid
        self.discrete_y_grid = discrete_y_grid
        self.d_model = d_model
        self.d_z = d_z
        self.n_layers = n_layers
        self.n_layers_decoder = n_layers_decoder
        self.n_heads = n_heads
        self.dim_feedforward = dim_feedforward
        self.dropout = dropout

        self.internal_box_format = str(normalize_box_format(internal_box_format))
        self.public_box_format = str(normalize_box_format(public_box_format))
        self.vendor_label_offset = vendor_label_offset
        self.eval_batch_size = eval_batch_size or max_num_elements

        self.element_sos_id = resolved_num_labels + 1
        self.element_eos_id = resolved_num_labels + 2
        self.group_sos_index = 0
        self.group_eos_index = resolved_num_labels + 1
        self.group_label_size = resolved_num_labels + 2
        self.element_label_size = resolved_num_labels + 3
        self.bbox_vocab_size = max(discrete_x_grid, discrete_y_grid)
        self.architectures = ["CoarseToFineForLayoutGeneration"]

__init__

__init__(
    dataset: DatasetName | str = DatasetName.rico25,
    num_labels: int | None = None,
    id2label: Mapping[int | str, str] | None = None,
    label2id: Mapping[str, int] | None = None,
    max_num_elements: int = 20,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    d_model: int = 512,
    d_z: int = 512,
    n_layers: int = 4,
    n_layers_decoder: int = 4,
    n_heads: int = 8,
    dim_feedforward: int = 2048,
    dropout: float = 0.1,
    internal_box_format: BoxFormat | str = BoxFormat.ltwh,
    public_box_format: BoxFormat | str = BoxFormat.xywh,
    vendor_label_offset: int = 1,
    eval_batch_size: int | None = None,
    **kwargs: str | int | float | bool | None,
) -> None

Initialize Coarse-to-Fine config values.

Source code in models/coarse-to-fine/src/coarse_to_fine/configuration_coarse_to_fine.py
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def __init__(
    self,
    dataset: DatasetName | str = DatasetName.rico25,
    num_labels: int | None = None,
    id2label: Mapping[int | str, str] | None = None,
    label2id: Mapping[str, int] | None = None,
    max_num_elements: int = 20,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    d_model: int = 512,
    d_z: int = 512,
    n_layers: int = 4,
    n_layers_decoder: int = 4,
    n_heads: int = 8,
    dim_feedforward: int = 2048,
    dropout: float = 0.1,
    internal_box_format: BoxFormat | str = BoxFormat.ltwh,
    public_box_format: BoxFormat | str = BoxFormat.xywh,
    vendor_label_offset: int = 1,
    eval_batch_size: int | None = None,
    **kwargs: str | int | float | bool | None,
) -> None:
    """Initialize Coarse-to-Fine config values."""
    normalized_dataset = normalize_dataset_name(dataset)
    if normalized_dataset not in SUPPORTED_DATASETS:
        raise ValueError(f"Unsupported Coarse-to-Fine dataset: {dataset}")

    resolved_id2label = (
        {int(key): str(value) for key, value in id2label.items()}
        if id2label is not None
        else id2label_for_dataset(normalized_dataset)
    )
    resolved_label2id = (
        {str(key): int(value) for key, value in label2id.items()}
        if label2id is not None
        else label2id_for_dataset(normalized_dataset)
    )
    resolved_num_labels = num_labels or len(resolved_id2label)
    super().__init__(id2label=resolved_id2label, label2id=resolved_label2id)
    for key, value in kwargs.items():
        setattr(self, key, value)

    self.dataset = str(normalized_dataset)
    self.num_labels = resolved_num_labels
    self.max_num_elements = max_num_elements
    self.discrete_x_grid = discrete_x_grid
    self.discrete_y_grid = discrete_y_grid
    self.d_model = d_model
    self.d_z = d_z
    self.n_layers = n_layers
    self.n_layers_decoder = n_layers_decoder
    self.n_heads = n_heads
    self.dim_feedforward = dim_feedforward
    self.dropout = dropout

    self.internal_box_format = str(normalize_box_format(internal_box_format))
    self.public_box_format = str(normalize_box_format(public_box_format))
    self.vendor_label_offset = vendor_label_offset
    self.eval_batch_size = eval_batch_size or max_num_elements

    self.element_sos_id = resolved_num_labels + 1
    self.element_eos_id = resolved_num_labels + 2
    self.group_sos_index = 0
    self.group_eos_index = resolved_num_labels + 1
    self.group_label_size = resolved_num_labels + 2
    self.element_label_size = resolved_num_labels + 3
    self.bbox_vocab_size = max(discrete_x_grid, discrete_y_grid)
    self.architectures = ["CoarseToFineForLayoutGeneration"]

conversion

Checkpoint conversion helpers for Coarse-to-Fine.

strip_module_prefix

strip_module_prefix(
    state_dict: dict[str, Shaped[Tensor, "..."]],
) -> dict[str, Shaped[torch.Tensor, "..."]]

Remove optional DDP module. prefixes from checkpoint keys.

Source code in models/coarse-to-fine/src/coarse_to_fine/conversion.py
17
18
19
20
21
def strip_module_prefix(
    state_dict: dict[str, Shaped[torch.Tensor, "..."]],
) -> dict[str, Shaped[torch.Tensor, "..."]]:
    """Remove optional DDP ``module.`` prefixes from checkpoint keys."""
    return {key.removeprefix("module."): value for key, value in state_dict.items()}

convert_checkpoint

convert_checkpoint(
    checkpoint: str | Path,
    *,
    dataset: DatasetName | str,
    output_dir: str | Path,
) -> CoarseToFineForLayoutGeneration

Convert a raw vendor state dict into save_pretrained format.

Parameters:

Name Type Description Default
checkpoint str | Path

Path to checkpoint.pth.tar.

required
dataset DatasetName | str

Dataset for config defaults.

required
output_dir str | Path

Destination directory.

required

Returns:

Type Description
CoarseToFineForLayoutGeneration

The loaded model.

Source code in models/coarse-to-fine/src/coarse_to_fine/conversion.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def convert_checkpoint(
    checkpoint: str | Path,
    *,
    dataset: DatasetName | str,
    output_dir: str | Path,
) -> CoarseToFineForLayoutGeneration:
    """Convert a raw vendor state dict into ``save_pretrained`` format.

    Args:
        checkpoint: Path to ``checkpoint.pth.tar``.
        dataset: Dataset for config defaults.
        output_dir: Destination directory.

    Returns:
        The loaded model.
    """
    config = CoarseToFineConfig(dataset=dataset)
    model = CoarseToFineForLayoutGeneration(config)
    raw_state = torch.load(checkpoint, map_location="cpu")
    state = strip_module_prefix(raw_state)
    model.load_state_dict(state, strict=True)
    output_path = Path(output_dir)
    model.save_pretrained(output_path, safe_serialization=True)
    id2label = {int(key): str(value) for key, value in (config.id2label or {}).items()}
    processor = CoarseToFineProcessor.from_config(
        dataset=config.dataset,
        x_grid=config.discrete_x_grid,
        y_grid=config.discrete_y_grid,
        max_num_elements=config.max_num_elements,
        id2label=id2label,
    )
    processor.save_pretrained(output_path)
    return model

geometry

Geometry helpers for Coarse-to-Fine discretization and hierarchy math.

public_to_ltwh

public_to_ltwh(
    bbox: Float[Tensor, "... 4"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
) -> Float[torch.Tensor, "... 4"]

Convert normalized public boxes to normalized left-top ltwh.

Parameters:

Name Type Description Default
bbox Float[Tensor, '... 4']

Box tensor with the selected input format.

required
box_format BoxFormat | str

Input box format.

xywh

Returns:

Type Description
Float[Tensor, '... 4']

Normalized ltwh tensor.

Raises:

Type Description
ValueError

If the box format is unsupported.

Examples:

>>> import torch
>>> public_to_ltwh(torch.tensor([[[0.5, 0.5, 0.2, 0.2]]])).shape
torch.Size([1, 1, 4])
Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def public_to_ltwh(
    bbox: Float[torch.Tensor, "... 4"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
) -> Float[torch.Tensor, "... 4"]:
    """Convert normalized public boxes to normalized left-top ``ltwh``.

    Args:
        bbox: Box tensor with the selected input format.
        box_format: Input box format.

    Returns:
        Normalized ``ltwh`` tensor.

    Raises:
        ValueError: If the box format is unsupported.

    Examples:
        >>> import torch
        >>> public_to_ltwh(torch.tensor([[[0.5, 0.5, 0.2, 0.2]]])).shape
        torch.Size([1, 1, 4])
    """
    fmt = normalize_box_format(box_format)
    boxes = bbox.to(dtype=torch.float32)
    if fmt is BoxFormat.xywh:
        return clamp_boxes(xywh_to_ltwh(boxes))
    if fmt is BoxFormat.ltwh:
        return clamp_boxes(boxes)
    if fmt is BoxFormat.ltrb:
        return clamp_boxes(xywh_to_ltwh(ltrb_to_xywh(boxes)))
    raise ValueError(f"Unsupported box_format: {box_format}")

ltwh_to_public_xywh

ltwh_to_public_xywh(
    bbox: Float[Tensor, "... 4"],
) -> Float[torch.Tensor, "... 4"]

Convert normalized ltwh boxes to public normalized center xywh.

Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
54
55
56
57
58
def ltwh_to_public_xywh(
    bbox: Float[torch.Tensor, "... 4"],
) -> Float[torch.Tensor, "... 4"]:
    """Convert normalized ``ltwh`` boxes to public normalized center ``xywh``."""
    return clamp_boxes(ltwh_to_xywh(bbox.to(dtype=torch.float32)))

discretize_ltwh

discretize_ltwh(
    bbox: Float[Tensor, "... 4"],
    *,
    num_x_grid: int,
    num_y_grid: int,
) -> Int[torch.Tensor, "... 4"]

Discretize normalized ltwh with the reference floor(coord*(grid-1)) rule.

Parameters:

Name Type Description Default
bbox Float[Tensor, '... 4']

Normalized ltwh tensor.

required
num_x_grid int

Number of x/width bins.

required
num_y_grid int

Number of y/height bins.

required

Returns:

Type Description
Int[Tensor, '... 4']

Integer ltwh bin ids.

Examples:

>>> import torch
>>> discretize_ltwh(torch.tensor([[1.0, 0.5, 0.0, 0.5]]), num_x_grid=128, num_y_grid=128)
tensor([[127,  63,   0,  63]])
Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def discretize_ltwh(
    bbox: Float[torch.Tensor, "... 4"], *, num_x_grid: int, num_y_grid: int
) -> Int[torch.Tensor, "... 4"]:
    """Discretize normalized ``ltwh`` with the reference ``floor(coord*(grid-1))`` rule.

    Args:
        bbox: Normalized ``ltwh`` tensor.
        num_x_grid: Number of x/width bins.
        num_y_grid: Number of y/height bins.

    Returns:
        Integer ``ltwh`` bin ids.

    Examples:
        >>> import torch
        >>> discretize_ltwh(torch.tensor([[1.0, 0.5, 0.0, 0.5]]), num_x_grid=128, num_y_grid=128)
        tensor([[127,  63,   0,  63]])
    """
    grids = torch.tensor(
        [num_x_grid - 1, num_y_grid - 1, num_x_grid - 1, num_y_grid - 1],
        device=bbox.device,
        dtype=bbox.dtype,
    )
    return cast(torch.LongTensor, torch.floor(bbox.clamp(0.0, 1.0) * grids).long())

continuize_ltwh

continuize_ltwh(
    ids: Int[Tensor, "... 4"],
    *,
    num_x_grid: int,
    num_y_grid: int,
) -> Float[torch.Tensor, "... 4"]

Convert discrete ltwh ids back to normalized coordinates.

Parameters:

Name Type Description Default
ids Int[Tensor, '... 4']

Integer ltwh ids.

required
num_x_grid int

Number of x/width bins.

required
num_y_grid int

Number of y/height bins.

required

Returns:

Type Description
Float[Tensor, '... 4']

Normalized ltwh tensor.

Examples:

>>> import torch
>>> continuize_ltwh(torch.tensor([[127, 63, 0, 63]]), num_x_grid=128, num_y_grid=128).shape
torch.Size([1, 4])
Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def continuize_ltwh(
    ids: Int[torch.Tensor, "... 4"], *, num_x_grid: int, num_y_grid: int
) -> Float[torch.Tensor, "... 4"]:
    """Convert discrete ``ltwh`` ids back to normalized coordinates.

    Args:
        ids: Integer ``ltwh`` ids.
        num_x_grid: Number of x/width bins.
        num_y_grid: Number of y/height bins.

    Returns:
        Normalized ``ltwh`` tensor.

    Examples:
        >>> import torch
        >>> continuize_ltwh(torch.tensor([[127, 63, 0, 63]]), num_x_grid=128, num_y_grid=128).shape
        torch.Size([1, 4])
    """
    values = ids.to(dtype=torch.float32)
    grids = torch.tensor(
        [num_x_grid - 1, num_y_grid - 1, num_x_grid - 1, num_y_grid - 1],
        device=values.device,
        dtype=values.dtype,
    )
    return cast(torch.FloatTensor, (values / grids).clamp(0.0, 1.0))

relative_ltwh_to_absolute_ltwh

relative_ltwh_to_absolute_ltwh(
    relative_bbox: Float[Tensor, "... 4"],
    group_bbox: Float[Tensor, "... 4"],
) -> Float[torch.Tensor, "... 4"]

Convert group-relative ltwh boxes to absolute normalized ltwh.

Parameters:

Name Type Description Default
relative_bbox Float[Tensor, '... 4']

Relative left, top, width, height inside the group.

required
group_bbox Float[Tensor, '... 4']

Absolute group ltwh boxes broadcastable to relative_bbox.

required

Returns:

Type Description
Float[Tensor, '... 4']

Absolute normalized ltwh boxes.

Examples:

>>> import torch
>>> relative_ltwh_to_absolute_ltwh(
...     torch.tensor([[0.5, 0.0, 0.5, 1.0]]),
...     torch.tensor([[0.0, 0.0, 0.4, 0.2]]),
... )
tensor([[0.2000, 0.0000, 0.2000, 0.2000]])
Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def relative_ltwh_to_absolute_ltwh(
    relative_bbox: Float[torch.Tensor, "... 4"],
    group_bbox: Float[torch.Tensor, "... 4"],
) -> Float[torch.Tensor, "... 4"]:
    """Convert group-relative ``ltwh`` boxes to absolute normalized ``ltwh``.

    Args:
        relative_bbox: Relative left, top, width, height inside the group.
        group_bbox: Absolute group ``ltwh`` boxes broadcastable to
            ``relative_bbox``.

    Returns:
        Absolute normalized ``ltwh`` boxes.

    Examples:
        >>> import torch
        >>> relative_ltwh_to_absolute_ltwh(
        ...     torch.tensor([[0.5, 0.0, 0.5, 1.0]]),
        ...     torch.tensor([[0.0, 0.0, 0.4, 0.2]]),
        ... )
        tensor([[0.2000, 0.0000, 0.2000, 0.2000]])
    """
    left, top, width, height = relative_bbox.unbind(dim=-1)
    group_left, group_top, group_width, group_height = group_bbox.unbind(dim=-1)
    return clamp_boxes(
        torch.stack(
            (
                group_left + left * group_width,
                group_top + top * group_height,
                width * group_width,
                height * group_height,
            ),
            dim=-1,
        )
    )

ltwh_to_ltrb

ltwh_to_ltrb(
    bbox: Float[Tensor, "... 4"],
) -> Float[torch.Tensor, "... 4"]

Convert ltwh boxes to ltrb boxes.

Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
151
152
153
154
def ltwh_to_ltrb(bbox: Float[torch.Tensor, "... 4"]) -> Float[torch.Tensor, "... 4"]:
    """Convert ``ltwh`` boxes to ``ltrb`` boxes."""
    left, top, width, height = bbox.unbind(dim=-1)
    return torch.stack((left, top, left + width, top + height), dim=-1)

ltrb_to_ltwh

ltrb_to_ltwh(
    bbox: Float[Tensor, "... 4"],
) -> Float[torch.Tensor, "... 4"]

Convert ltrb boxes to ltwh boxes.

Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
157
158
159
160
def ltrb_to_ltwh(bbox: Float[torch.Tensor, "... 4"]) -> Float[torch.Tensor, "... 4"]:
    """Convert ``ltrb`` boxes to ``ltwh`` boxes."""
    left, top, right, bottom = bbox.unbind(dim=-1)
    return torch.stack((left, top, right - left, bottom - top), dim=-1)

public_to_ltrb

public_to_ltrb(
    bbox: Float[Tensor, "... 4"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
) -> Float[torch.Tensor, "... 4"]

Convert normalized public boxes to normalized ltrb.

Source code in models/coarse-to-fine/src/coarse_to_fine/geometry.py
163
164
165
166
167
168
169
170
171
172
173
174
def public_to_ltrb(
    bbox: Float[torch.Tensor, "... 4"], *, box_format: BoxFormat | str = BoxFormat.xywh
) -> Float[torch.Tensor, "... 4"]:
    """Convert normalized public boxes to normalized ``ltrb``."""
    fmt = normalize_box_format(box_format)
    if fmt is BoxFormat.xywh:
        return clamp_boxes(xywh_to_ltrb(bbox.to(dtype=torch.float32)))
    if fmt is BoxFormat.ltwh:
        return clamp_boxes(ltwh_to_ltrb(bbox.to(dtype=torch.float32)))
    if fmt is BoxFormat.ltrb:
        return clamp_boxes(bbox.to(dtype=torch.float32))
    raise ValueError(f"Unsupported box_format: {box_format}")

hierarchy

Hierarchy carriers and CutHierarchy-compatible processing.

CoarseToFineHierarchy dataclass

Decoded Coarse-to-Fine hierarchy returned in intermediates.

Source code in models/coarse-to-fine/src/coarse_to_fine/hierarchy.py
27
28
29
30
31
32
33
34
35
36
37
38
@dataclass
class CoarseToFineHierarchy:
    """Decoded Coarse-to-Fine hierarchy returned in ``intermediates``."""

    group_bbox: Float[torch.Tensor, "batch groups 4"]
    group_mask: Bool[torch.Tensor, "batch groups"]
    label_histogram: Float[torch.Tensor, "batch groups labels"]
    element_group_index: Int[torch.Tensor, "batch elements"]
    relative_bbox: Float[torch.Tensor, "batch groups elements 4"]
    relative_mask: Bool[torch.Tensor, "batch groups elements"]
    discrete_group_bbox: Int[torch.Tensor, "batch groups 4"] | None = None
    discrete_relative_bbox: Int[torch.Tensor, "batch groups elements 4"] | None = None

CoarseToFineHierarchyEncoding dataclass

Training/reference hierarchy tensors before padding.

Source code in models/coarse-to-fine/src/coarse_to_fine/hierarchy.py
41
42
43
44
45
46
47
48
@dataclass
class CoarseToFineHierarchyEncoding:
    """Training/reference hierarchy tensors before padding."""

    group_bounding_box: Int[torch.Tensor, "groups 4"]
    label_in_one_group: Float[torch.Tensor, "groups labels"]
    grouped_labels: list[Int[torch.Tensor, "elements"]]
    grouped_bbox: list[Int[torch.Tensor, "elements 4"]]

build_cut_hierarchy

build_cut_hierarchy(
    bbox_ltwh: Float[Tensor, "elements 4"],
    labels_1based: Int[Tensor, "elements"],
    *,
    num_labels: int,
    discrete_x_grid: int,
    discrete_y_grid: int,
) -> CoarseToFineHierarchyEncoding

Build the checkpoint bottom-two hierarchy for one layout.

Parameters:

Name Type Description Default
bbox_ltwh Float[Tensor, 'elements 4']

Normalized ltwh boxes for valid elements.

required
labels_1based Int[Tensor, 'elements']

Internal one-based labels for valid elements.

required
num_labels int

Number of dataset labels.

required
discrete_x_grid int

Number of x bins.

required
discrete_y_grid int

Number of y bins.

required

Returns:

Type Description
CoarseToFineHierarchyEncoding

Unpadded hierarchy encoding with discrete group and relative boxes.

Raises:

Type Description
ValueError

If the layout has no valid elements.

Examples:

>>> import torch
>>> enc = build_cut_hierarchy(
...     torch.tensor([[0.0, 0.0, 0.2, 0.2], [0.5, 0.0, 0.2, 0.2]]),
...     torch.tensor([1, 2]),
...     num_labels=2,
...     discrete_x_grid=128,
...     discrete_y_grid=128,
... )
>>> enc.group_bounding_box.shape[-1]
4
Source code in models/coarse-to-fine/src/coarse_to_fine/hierarchy.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
def build_cut_hierarchy(
    bbox_ltwh: Float[torch.Tensor, "elements 4"],
    labels_1based: Int[torch.Tensor, "elements"],
    *,
    num_labels: int,
    discrete_x_grid: int,
    discrete_y_grid: int,
) -> CoarseToFineHierarchyEncoding:
    """Build the checkpoint bottom-two hierarchy for one layout.

    Args:
        bbox_ltwh: Normalized ``ltwh`` boxes for valid elements.
        labels_1based: Internal one-based labels for valid elements.
        num_labels: Number of dataset labels.
        discrete_x_grid: Number of x bins.
        discrete_y_grid: Number of y bins.

    Returns:
        Unpadded hierarchy encoding with discrete group and relative boxes.

    Raises:
        ValueError: If the layout has no valid elements.

    Examples:
        >>> import torch
        >>> enc = build_cut_hierarchy(
        ...     torch.tensor([[0.0, 0.0, 0.2, 0.2], [0.5, 0.0, 0.2, 0.2]]),
        ...     torch.tensor([1, 2]),
        ...     num_labels=2,
        ...     discrete_x_grid=128,
        ...     discrete_y_grid=128,
        ... )
        >>> enc.group_bounding_box.shape[-1]
        4
    """
    if bbox_ltwh.numel() == 0:
        raise ValueError("Cannot build a hierarchy for an empty layout")

    bbox_ltrb = ltwh_to_ltrb(bbox_ltwh.to(dtype=torch.float64))
    sorted_boxes = _sort_boxes(bbox_ltrb, labels_1based.long())
    sorted_bbox_with_idx = [(box, idx) for idx, (box, _) in enumerate(sorted_boxes)]
    group_tree = _group_bbox(sorted_bbox_with_idx, direction="y")
    if len(group_tree) == len(sorted_boxes):
        group_tree = _group_bbox(sorted_bbox_with_idx, direction="x")
    structure = _bottom_two_layers(group_tree, [])
    group_ltrb = _structure_group_boxes(structure, sorted_boxes)
    group_ltwh = ltrb_to_ltwh(group_ltrb).to(dtype=torch.float32)
    group_discrete = discretize_ltwh(
        group_ltwh, num_x_grid=discrete_x_grid, num_y_grid=discrete_y_grid
    )
    grouped_labels: list[Int[torch.Tensor, "elements"]] = []
    grouped_bbox: list[Int[torch.Tensor, "elements 4"]] = []
    label_histogram: list[Float[torch.Tensor, "labels"]] = []
    for group_idx, group in enumerate(structure):
        labels = torch.tensor(
            [sorted_boxes[idx][1] for idx in group],
            dtype=torch.long,
            device=bbox_ltwh.device,
        )
        boxes_ltrb = torch.stack([sorted_boxes[idx][0] for idx in group]).to(
            dtype=torch.float64
        )
        group_box = group_ltrb[group_idx].to(dtype=torch.float64)
        width = torch.clamp(group_box[2] - group_box[0], min=1e-8)
        height = torch.clamp(group_box[3] - group_box[1], min=1e-8)
        relative_ltrb = boxes_ltrb.clone()
        relative_ltrb[:, 0] = (boxes_ltrb[:, 0] - group_box[0]) / width
        relative_ltrb[:, 1] = (boxes_ltrb[:, 1] - group_box[1]) / height
        relative_ltrb[:, 2] = (boxes_ltrb[:, 2] - group_box[0]) / width
        relative_ltrb[:, 3] = (boxes_ltrb[:, 3] - group_box[1]) / height
        relative_ltwh = ltrb_to_ltwh(relative_ltrb).to(dtype=torch.float32)
        grouped_labels.append(cast(torch.LongTensor, labels))
        grouped_bbox.append(
            discretize_ltwh(
                relative_ltwh,
                num_x_grid=discrete_x_grid,
                num_y_grid=discrete_y_grid,
            )
        )
        hist = torch.zeros(num_labels, dtype=torch.float32, device=bbox_ltwh.device)
        for label in labels:
            hist[int(label) - 1] += 1.0
        label_histogram.append(hist)
    return CoarseToFineHierarchyEncoding(
        group_bounding_box=group_discrete,
        label_in_one_group=cast(torch.FloatTensor, torch.stack(label_histogram)),
        grouped_labels=grouped_labels,
        grouped_bbox=grouped_bbox,
    )

flatten_hierarchy

flatten_hierarchy(
    hierarchy: CoarseToFineHierarchy,
    *,
    id2label: dict[int, str],
    max_num_elements: int | None = None,
) -> LayoutGenerationOutput

Flatten a decoded hierarchy to the shared output schema.

Parameters:

Name Type Description Default
hierarchy CoarseToFineHierarchy

Decoded Coarse-to-Fine hierarchy.

required
id2label dict[int, str]

Public label mapping.

required
max_num_elements int | None

Optional output padding length.

None

Returns:

Type Description
LayoutGenerationOutput

Shared layout output with hierarchy metadata in intermediates.

Source code in models/coarse-to-fine/src/coarse_to_fine/hierarchy.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def flatten_hierarchy(
    hierarchy: CoarseToFineHierarchy,
    *,
    id2label: dict[int, str],
    max_num_elements: int | None = None,
) -> LayoutGenerationOutput:
    """Flatten a decoded hierarchy to the shared output schema.

    Args:
        hierarchy: Decoded Coarse-to-Fine hierarchy.
        id2label: Public label mapping.
        max_num_elements: Optional output padding length.

    Returns:
        Shared layout output with hierarchy metadata in ``intermediates``.
    """
    batch = hierarchy.group_bbox.size(0)
    rows_bbox: list[Float[torch.Tensor, "elements 4"]] = []
    rows_labels: list[Int[torch.Tensor, "elements"]] = []
    rows_mask: list[Bool[torch.Tensor, "elements"]] = []
    rows_group_index: list[Int[torch.Tensor, "elements"]] = []

    for batch_idx in range(batch):
        bbox_values: list[Float[torch.Tensor, "4"]] = []
        label_values: list[Int[torch.Tensor, ""]] = []
        group_values: list[int] = []
        for group_idx in range(hierarchy.group_bbox.size(1)):
            if not bool(hierarchy.group_mask[batch_idx, group_idx]):
                continue
            rel_mask = hierarchy.relative_mask[batch_idx, group_idx]
            rel_bbox = hierarchy.relative_bbox[batch_idx, group_idx, rel_mask]
            if rel_bbox.numel() == 0:
                continue
            group_bbox = hierarchy.group_bbox[batch_idx, group_idx].expand_as(rel_bbox)
            bbox_values.extend(relative_ltwh_to_absolute_ltwh(rel_bbox, group_bbox))
            hist = hierarchy.label_histogram[batch_idx, group_idx, : len(id2label)]
            group_labels = torch.arange(
                len(id2label), device=hist.device
            ).repeat_interleave(hist.clamp_min(0).round().long())
            if group_labels.numel() < rel_bbox.size(0):
                group_labels = F.pad(
                    group_labels, (0, rel_bbox.size(0) - group_labels.numel())
                )
            label_values.extend(group_labels[: rel_bbox.size(0)])
            group_values.extend([group_idx] * rel_bbox.size(0))
        if not bbox_values:
            bbox = torch.zeros(
                (0, 4), dtype=torch.float32, device=hierarchy.group_bbox.device
            )
            labels = torch.zeros(
                (0,), dtype=torch.long, device=hierarchy.group_bbox.device
            )
            group_index = torch.zeros(
                (0,), dtype=torch.long, device=hierarchy.group_bbox.device
            )
        else:
            bbox = ltwh_to_public_xywh(torch.stack(bbox_values))
            labels = torch.stack(label_values).long()
            group_index = torch.tensor(
                group_values, dtype=torch.long, device=bbox.device
            )
        rows_bbox.append(bbox)
        rows_labels.append(labels)
        rows_group_index.append(group_index)
    out_len = max_num_elements or max((row.size(0) for row in rows_bbox), default=1)
    out_len = max(out_len, 1)
    for idx, bbox in enumerate(rows_bbox):
        labels = rows_labels[idx]
        group_index = rows_group_index[idx]
        valid = min(bbox.size(0), out_len)
        rows_mask.append(torch.arange(out_len, device=bbox.device) < valid)
        rows_bbox[idx] = F.pad(bbox[:out_len], (0, 0, 0, out_len - valid))
        rows_labels[idx] = F.pad(labels[:out_len], (0, out_len - valid))
        rows_group_index[idx] = F.pad(
            group_index[:out_len], (0, out_len - valid), value=INVALID_GROUP_INDEX
        )
    return LayoutGenerationOutput(
        bbox=torch.stack(rows_bbox).float(),
        labels=torch.stack(rows_labels).long(),
        mask=torch.stack(rows_mask).bool(),
        id2label=dict(id2label),
        intermediates={
            "hierarchy": {
                "group_bbox": hierarchy.group_bbox,
                "group_mask": hierarchy.group_mask,
                "element_group_index": torch.stack(rows_group_index).long(),
                "relative_bbox": hierarchy.relative_bbox,
                "relative_mask": hierarchy.relative_mask,
                "label_histogram": hierarchy.label_histogram,
            }
        },
    )

decode_hierarchy_from_logits

decode_hierarchy_from_logits(
    *,
    group_bbox_logits: Float[
        Tensor, "batch group_tokens 4 bbox_vocab"
    ],
    group_label_logits: Float[
        Tensor, "batch group_tokens group_label_vocab"
    ],
    grouped_bbox_logits: Float[
        Tensor, "batch groups elements 4 bbox_vocab"
    ],
    grouped_label_logits: Float[
        Tensor, "batch groups elements element_label_vocab"
    ],
    num_labels: int,
    group_eos_index: int,
    element_eos_id: int,
    discrete_x_grid: int,
    discrete_y_grid: int,
) -> CoarseToFineHierarchy

Decode argmax group/element logits into hierarchy tensors.

Source code in models/coarse-to-fine/src/coarse_to_fine/hierarchy.py
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
def decode_hierarchy_from_logits(
    *,
    group_bbox_logits: Float[torch.Tensor, "batch group_tokens 4 bbox_vocab"],
    group_label_logits: Float[torch.Tensor, "batch group_tokens group_label_vocab"],
    grouped_bbox_logits: Float[torch.Tensor, "batch groups elements 4 bbox_vocab"],
    grouped_label_logits: Float[
        torch.Tensor, "batch groups elements element_label_vocab"
    ],
    num_labels: int,
    group_eos_index: int,
    element_eos_id: int,
    discrete_x_grid: int,
    discrete_y_grid: int,
) -> CoarseToFineHierarchy:
    """Decode argmax group/element logits into hierarchy tensors."""
    group_bbox_ids = group_bbox_logits.argmax(dim=-1)[:, :-2]
    group_label_scores = group_label_logits[:, :-2]
    grouped_bbox_ids = grouped_bbox_logits.argmax(dim=-1)
    grouped_label_ids = grouped_label_logits.argmax(dim=-1)
    group_bbox_ltwh = continuize_ltwh(
        group_bbox_ids, num_x_grid=discrete_x_grid, num_y_grid=discrete_y_grid
    )
    relative_ltwh = continuize_ltwh(
        grouped_bbox_ids, num_x_grid=discrete_x_grid, num_y_grid=discrete_y_grid
    )
    group_label_ids = group_label_scores.argmax(dim=-1)
    group_has_labels = group_label_scores[..., 1 : num_labels + 1].sum(dim=-1) > 0
    group_mask = group_has_labels & group_label_ids.ne(group_eos_index)
    relative_mask = grouped_label_ids.ge(1) & grouped_label_ids.le(num_labels)
    relative_mask = relative_mask & grouped_label_ids.ne(element_eos_id)
    label_histogram = group_label_scores[..., 1 : num_labels + 1].clamp_min(0)
    batch, groups, elems = relative_mask.shape
    element_group_index = torch.arange(groups, device=relative_mask.device).view(
        1, groups, 1
    )
    element_group_index = element_group_index.expand(batch, groups, elems).reshape(
        batch, groups * elems
    )
    return CoarseToFineHierarchy(
        group_bbox=cast(torch.FloatTensor, group_bbox_ltwh.float()),
        group_mask=cast(torch.BoolTensor, group_mask.bool()),
        label_histogram=cast(torch.FloatTensor, label_histogram.float()),
        element_group_index=cast(torch.LongTensor, element_group_index.long()),
        relative_bbox=cast(torch.FloatTensor, relative_ltwh.float()),
        relative_mask=cast(torch.BoolTensor, relative_mask.bool()),
        discrete_group_bbox=cast(torch.LongTensor, group_bbox_ids.long()),
        discrete_relative_bbox=cast(torch.LongTensor, grouped_bbox_ids.long()),
    )

modeling_coarse_to_fine

PyTorch model wrapper for Coarse-to-Fine layout generation.

LayoutEmbedding

Bases: Module

Checkpoint-compatible label, box, and group-label embeddings.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class LayoutEmbedding(nn.Module):
    """Checkpoint-compatible label, box, and group-label embeddings."""

    def __init__(self, config: CoarseToFineConfig) -> None:
        """Initialize embedding tables."""
        super().__init__()
        self.config = config
        self.label_embed = nn.Embedding(config.num_labels + 3, 128)
        self.bbox_embed = nn.Embedding(config.bbox_vocab_size, 128)
        self.proj_cat = nn.Linear(128 * 5, config.d_model)
        self.group_label_embed = nn.Linear(config.num_labels + 2, 128)
        self._init_embeddings()

    def _init_embeddings(self) -> None:
        nn.init.kaiming_normal_(self.label_embed.weight, mode="fan_in")
        nn.init.kaiming_normal_(self.bbox_embed.weight, mode="fan_in")
        nn.init.kaiming_normal_(self.proj_cat.weight, mode="fan_in")

    def get_label_embedding(
        self, label: Int[torch.Tensor, "..."]
    ) -> Float[torch.Tensor, "... channels"]:
        """Embed element labels."""
        return self.label_embed(label)

    def get_box_embedding(
        self, box: Int[torch.Tensor, "seq batch 4"]
    ) -> Float[torch.Tensor, "seq batch box_channels"]:
        """Embed four discrete box-coordinate ids and concatenate them."""
        bbox_vecs = self.bbox_embed(box)
        seq, batch, _, _ = bbox_vecs.shape
        return bbox_vecs.reshape(seq, batch, -1)

    def get_group_label_embedding(
        self, label: Float[torch.Tensor, "seq batch group_label_vocab"]
    ) -> Float[torch.Tensor, "seq batch channels"]:
        """Embed per-group label histograms."""
        return self.group_label_embed(label)

    def forward(
        self,
        label: Int[torch.Tensor, "seq batch"],
        box: Int[torch.Tensor, "seq batch 4"],
    ) -> Float[torch.Tensor, "seq batch channels"]:
        """Embed labels and boxes into transformer hidden states."""
        label_vecs = self.get_label_embedding(label)
        box_vecs = self.get_box_embedding(box)
        return self.proj_cat(torch.cat((label_vecs, box_vecs), dim=-1))

__init__

__init__(config: CoarseToFineConfig) -> None

Initialize embedding tables.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
86
87
88
89
90
91
92
93
94
def __init__(self, config: CoarseToFineConfig) -> None:
    """Initialize embedding tables."""
    super().__init__()
    self.config = config
    self.label_embed = nn.Embedding(config.num_labels + 3, 128)
    self.bbox_embed = nn.Embedding(config.bbox_vocab_size, 128)
    self.proj_cat = nn.Linear(128 * 5, config.d_model)
    self.group_label_embed = nn.Linear(config.num_labels + 2, 128)
    self._init_embeddings()

get_label_embedding

get_label_embedding(
    label: Int[Tensor, "..."],
) -> Float[torch.Tensor, "... channels"]

Embed element labels.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
101
102
103
104
105
def get_label_embedding(
    self, label: Int[torch.Tensor, "..."]
) -> Float[torch.Tensor, "... channels"]:
    """Embed element labels."""
    return self.label_embed(label)

get_box_embedding

get_box_embedding(
    box: Int[Tensor, "seq batch 4"],
) -> Float[torch.Tensor, "seq batch box_channels"]

Embed four discrete box-coordinate ids and concatenate them.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
107
108
109
110
111
112
113
def get_box_embedding(
    self, box: Int[torch.Tensor, "seq batch 4"]
) -> Float[torch.Tensor, "seq batch box_channels"]:
    """Embed four discrete box-coordinate ids and concatenate them."""
    bbox_vecs = self.bbox_embed(box)
    seq, batch, _, _ = bbox_vecs.shape
    return bbox_vecs.reshape(seq, batch, -1)

get_group_label_embedding

get_group_label_embedding(
    label: Float[Tensor, "seq batch group_label_vocab"],
) -> Float[torch.Tensor, "seq batch channels"]

Embed per-group label histograms.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
115
116
117
118
119
def get_group_label_embedding(
    self, label: Float[torch.Tensor, "seq batch group_label_vocab"]
) -> Float[torch.Tensor, "seq batch channels"]:
    """Embed per-group label histograms."""
    return self.group_label_embed(label)

forward

forward(
    label: Int[Tensor, "seq batch"],
    box: Int[Tensor, "seq batch 4"],
) -> Float[torch.Tensor, "seq batch channels"]

Embed labels and boxes into transformer hidden states.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
121
122
123
124
125
126
127
128
129
def forward(
    self,
    label: Int[torch.Tensor, "seq batch"],
    box: Int[torch.Tensor, "seq batch 4"],
) -> Float[torch.Tensor, "seq batch channels"]:
    """Embed labels and boxes into transformer hidden states."""
    label_vecs = self.get_label_embedding(label)
    box_vecs = self.get_box_embedding(box)
    return self.proj_cat(torch.cat((label_vecs, box_vecs), dim=-1))

Encoder

Bases: Module

Checkpoint-compatible layout encoder.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
class Encoder(nn.Module):
    """Checkpoint-compatible layout encoder."""

    def __init__(
        self, config: CoarseToFineConfig, layout_embd: LayoutEmbedding
    ) -> None:
        """Initialize the transformer encoder."""
        super().__init__()
        self.embedding = layout_embd
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=config.d_model,
            nhead=config.n_heads,
            dim_feedforward=config.dim_feedforward,
            dropout=config.dropout,
            batch_first=False,
        )
        encoder_norm = nn.LayerNorm(config.d_model)
        self.encoder = nn.TransformerEncoder(
            encoder_layer, num_layers=config.n_layers, norm=encoder_norm
        )

    def forward(
        self,
        labels: Int[torch.Tensor, "seq batch"],
        bboxes: Int[torch.Tensor, "seq batch 4"],
        masks: Bool[torch.Tensor, "batch seq"],
    ) -> Float[torch.Tensor, "1 batch channels"]:
        """Encode a seq-first padded layout and mean-pool valid states."""
        key_padding_mask = get_key_padding_mask(masks)
        src = self.embedding(labels, bboxes)
        memory = self.encoder(src=src, src_key_padding_mask=key_padding_mask)
        padding_mask = get_padding_mask(masks)
        return (memory * padding_mask).sum(dim=0, keepdim=True) / padding_mask.sum(
            dim=0, keepdim=True
        ).clamp_min(1)

__init__

__init__(
    config: CoarseToFineConfig, layout_embd: LayoutEmbedding
) -> None

Initialize the transformer encoder.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def __init__(
    self, config: CoarseToFineConfig, layout_embd: LayoutEmbedding
) -> None:
    """Initialize the transformer encoder."""
    super().__init__()
    self.embedding = layout_embd
    encoder_layer = nn.TransformerEncoderLayer(
        d_model=config.d_model,
        nhead=config.n_heads,
        dim_feedforward=config.dim_feedforward,
        dropout=config.dropout,
        batch_first=False,
    )
    encoder_norm = nn.LayerNorm(config.d_model)
    self.encoder = nn.TransformerEncoder(
        encoder_layer, num_layers=config.n_layers, norm=encoder_norm
    )

forward

forward(
    labels: Int[Tensor, "seq batch"],
    bboxes: Int[Tensor, "seq batch 4"],
    masks: Bool[Tensor, "batch seq"],
) -> Float[torch.Tensor, "1 batch channels"]

Encode a seq-first padded layout and mean-pool valid states.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def forward(
    self,
    labels: Int[torch.Tensor, "seq batch"],
    bboxes: Int[torch.Tensor, "seq batch 4"],
    masks: Bool[torch.Tensor, "batch seq"],
) -> Float[torch.Tensor, "1 batch channels"]:
    """Encode a seq-first padded layout and mean-pool valid states."""
    key_padding_mask = get_key_padding_mask(masks)
    src = self.embedding(labels, bboxes)
    memory = self.encoder(src=src, src_key_padding_mask=key_padding_mask)
    padding_mask = get_padding_mask(masks)
    return (memory * padding_mask).sum(dim=0, keepdim=True) / padding_mask.sum(
        dim=0, keepdim=True
    ).clamp_min(1)

VAE

Bases: Module

Checkpoint-compatible latent sampler.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
class VAE(nn.Module):
    """Checkpoint-compatible latent sampler."""

    def __init__(self, config: CoarseToFineConfig) -> None:
        """Initialize latent projections."""
        super().__init__()
        self.config = config
        self.enc_mu_fcn = nn.Linear(config.d_model, config.d_z)
        self.enc_sigma_fcn = nn.Linear(config.d_model, config.d_z)
        self.z_fcn = nn.Linear(config.d_z, config.d_model)
        self._init_embeddings()

    def _init_embeddings(self) -> None:
        nn.init.normal_(self.enc_mu_fcn.weight, std=0.001)
        nn.init.constant_(self.enc_mu_fcn.bias, 0)
        nn.init.normal_(self.enc_sigma_fcn.weight, std=0.001)
        nn.init.constant_(self.enc_sigma_fcn.bias, 0)

    def forward(
        self, memory: Float[torch.Tensor, "1 batch channels"]
    ) -> tuple[
        Float[torch.Tensor, "1 batch latent"],
        Float[torch.Tensor, "1 batch latent"],
        Float[torch.Tensor, "1 batch latent"],
    ]:
        """Sample latent ``z`` from encoded memory."""
        mu = self.enc_mu_fcn(memory)
        logvar = self.enc_sigma_fcn(memory)
        sigma = torch.exp(logvar / 2.0)
        z = mu + sigma * torch.randn_like(sigma)
        return z, mu, logvar

    def inference(
        self,
        z: Float[torch.Tensor, "1 batch latent"] | None,
        *,
        batch_size: int,
        device: torch.device,
    ) -> Float[torch.Tensor, "1 batch latent"]:
        """Return seq-first latent tensor for generation."""
        if z is None:
            return torch.randn(size=(1, batch_size, self.config.d_z), device=device)
        return (
            make_seq_first(z).to(device)
            if z.dim() == 3 and z.size(0) != 1
            else z.to(device)
        )

__init__

__init__(config: CoarseToFineConfig) -> None

Initialize latent projections.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
172
173
174
175
176
177
178
179
def __init__(self, config: CoarseToFineConfig) -> None:
    """Initialize latent projections."""
    super().__init__()
    self.config = config
    self.enc_mu_fcn = nn.Linear(config.d_model, config.d_z)
    self.enc_sigma_fcn = nn.Linear(config.d_model, config.d_z)
    self.z_fcn = nn.Linear(config.d_z, config.d_model)
    self._init_embeddings()

forward

forward(
    memory: Float[Tensor, "1 batch channels"],
) -> tuple[
    Float[torch.Tensor, "1 batch latent"],
    Float[torch.Tensor, "1 batch latent"],
    Float[torch.Tensor, "1 batch latent"],
]

Sample latent z from encoded memory.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
187
188
189
190
191
192
193
194
195
196
197
198
199
def forward(
    self, memory: Float[torch.Tensor, "1 batch channels"]
) -> tuple[
    Float[torch.Tensor, "1 batch latent"],
    Float[torch.Tensor, "1 batch latent"],
    Float[torch.Tensor, "1 batch latent"],
]:
    """Sample latent ``z`` from encoded memory."""
    mu = self.enc_mu_fcn(memory)
    logvar = self.enc_sigma_fcn(memory)
    sigma = torch.exp(logvar / 2.0)
    z = mu + sigma * torch.randn_like(sigma)
    return z, mu, logvar

inference

inference(
    z: Float[Tensor, "1 batch latent"] | None,
    *,
    batch_size: int,
    device: device,
) -> Float[torch.Tensor, "1 batch latent"]

Return seq-first latent tensor for generation.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def inference(
    self,
    z: Float[torch.Tensor, "1 batch latent"] | None,
    *,
    batch_size: int,
    device: torch.device,
) -> Float[torch.Tensor, "1 batch latent"]:
    """Return seq-first latent tensor for generation."""
    if z is None:
        return torch.randn(size=(1, batch_size, self.config.d_z), device=device)
    return (
        make_seq_first(z).to(device)
        if z.dim() == 3 and z.size(0) != 1
        else z.to(device)
    )

GroupDecoder

Bases: Module

Autoregressive group box and label-histogram decoder.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
class GroupDecoder(nn.Module):
    """Autoregressive group box and label-histogram decoder."""

    square_subsequent_mask: Float[torch.Tensor, "max_seq max_seq"]

    def __init__(
        self, config: CoarseToFineConfig, layout_embd: LayoutEmbedding
    ) -> None:
        """Initialize group decoder modules."""
        super().__init__()
        self.config = config
        self.layout_embd = layout_embd
        self.proj_cat_tgt = nn.Linear(128 * 5, config.d_model)
        self.register_buffer(
            "square_subsequent_mask",
            generate_square_subsequent_mask(
                config.max_num_elements + 2, torch.device("cpu")
            ),
        )
        decoder_layer = nn.TransformerDecoderLayer(
            d_model=config.d_model,
            nhead=config.n_heads,
            dim_feedforward=config.dim_feedforward,
            dropout=config.dropout,
            batch_first=False,
        )
        decoder_norm = nn.LayerNorm(config.d_model)
        self.decoder = nn.TransformerDecoder(
            decoder_layer, num_layers=config.n_layers_decoder, norm=decoder_norm
        )
        self.label_fcn = nn.Sequential(
            nn.Linear(config.d_model, 128),
            nn.Linear(128, config.num_labels + 2),
            nn.ReLU(inplace=True),
        )
        self.box_fcn = nn.Sequential(
            nn.Linear(config.d_model, 4 * config.bbox_vocab_size),
            nn.ReLU(inplace=True),
        )

    def forward(
        self,
        label: Float[torch.Tensor, "seq batch group_label_vocab"],
        box: Int[torch.Tensor, "seq batch 4"],
        z: Float[torch.Tensor, "1 batch channels"],
        mask: Bool[torch.Tensor, "batch seq"],
    ) -> tuple[
        Float[torch.Tensor, "seq_minus_eos batch channels"],
        Float[torch.Tensor, "seq batch box_logits"],
        Float[torch.Tensor, "seq batch group_label_vocab"],
    ]:
        """Teacher-forced group decoding."""
        key_padding_mask = get_key_padding_mask(mask)
        tgt_label_vecs = self.layout_embd.get_group_label_embedding(label)
        tgt_box_vecs = self.layout_embd.get_box_embedding(box)
        tgt = self.proj_cat_tgt(torch.cat((tgt_label_vecs, tgt_box_vecs), dim=-1))
        length = tgt.size(0)
        causal_mask = generate_square_subsequent_mask(length, tgt.device)
        out = self.decoder(
            tgt[:length],
            z,
            tgt_mask=causal_mask,
            tgt_key_padding_mask=key_padding_mask,
        )
        rec_box = self.box_fcn(out)
        rec_label = self.label_fcn(out)
        return out[:-2], rec_box, rec_label

    def inference(
        self,
        z: Float[torch.Tensor, "1 batch channels"],
        *,
        max_group_num: int,
        device: torch.device,
    ) -> tuple[
        Float[torch.Tensor, "seq_minus_eos batch channels"],
        Float[torch.Tensor, "seq batch box_logits"],
        Float[torch.Tensor, "seq batch group_label_vocab"],
    ]:
        """Greedy autoregressive group decoding."""
        tgt = torch.zeros(max_group_num, z.shape[1], self.config.d_model, device=device)
        rec_labels = torch.zeros(
            max_group_num, z.shape[1], self.config.num_labels + 2, device=device
        )
        rec_bboxes = torch.zeros(
            max_group_num, z.shape[1], 4 * self.config.bbox_vocab_size, device=device
        )
        sos_box = torch.zeros(z.shape[1], 4, dtype=torch.long, device=device)
        sos_label = torch.zeros(z.shape[1], self.config.num_labels + 2, device=device)
        sos_label[:, self.config.group_sos_index] = 1.0
        sos_box_vecs = self.layout_embd.get_box_embedding(sos_box.unsqueeze(0))
        sos_label_vecs = self.layout_embd.group_label_embed(sos_label.unsqueeze(0))
        tgt[0] = self.proj_cat_tgt(
            torch.cat((sos_label_vecs, sos_box_vecs), dim=-1)
        ).squeeze(0)
        out = torch.zeros_like(tgt)
        for idx in range(max_group_num):
            decoded = self.decoder(tgt[: idx + 1], z)
            out[idx] = decoded[idx]
            rec_box_i = self.box_fcn(out[idx])
            rec_label_i = self.label_fcn(out[idx])
            rec_bboxes[idx] = rec_box_i
            rec_labels[idx] = rec_label_i
            if idx < max_group_num - 1:
                next_box = rec_box_i.reshape(-1, 4, self.config.bbox_vocab_size).argmax(
                    -1
                )
                tgt_label_vecs = self.layout_embd.group_label_embed(rec_label_i)
                tgt_box_vecs = self.layout_embd.get_box_embedding(
                    next_box.unsqueeze(0)
                ).squeeze(0)
                tgt[idx + 1] = self.proj_cat_tgt(
                    torch.cat((tgt_label_vecs, tgt_box_vecs), dim=-1)
                )
        return out[:-2], rec_bboxes, rec_labels

__init__

__init__(
    config: CoarseToFineConfig, layout_embd: LayoutEmbedding
) -> None

Initialize group decoder modules.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
def __init__(
    self, config: CoarseToFineConfig, layout_embd: LayoutEmbedding
) -> None:
    """Initialize group decoder modules."""
    super().__init__()
    self.config = config
    self.layout_embd = layout_embd
    self.proj_cat_tgt = nn.Linear(128 * 5, config.d_model)
    self.register_buffer(
        "square_subsequent_mask",
        generate_square_subsequent_mask(
            config.max_num_elements + 2, torch.device("cpu")
        ),
    )
    decoder_layer = nn.TransformerDecoderLayer(
        d_model=config.d_model,
        nhead=config.n_heads,
        dim_feedforward=config.dim_feedforward,
        dropout=config.dropout,
        batch_first=False,
    )
    decoder_norm = nn.LayerNorm(config.d_model)
    self.decoder = nn.TransformerDecoder(
        decoder_layer, num_layers=config.n_layers_decoder, norm=decoder_norm
    )
    self.label_fcn = nn.Sequential(
        nn.Linear(config.d_model, 128),
        nn.Linear(128, config.num_labels + 2),
        nn.ReLU(inplace=True),
    )
    self.box_fcn = nn.Sequential(
        nn.Linear(config.d_model, 4 * config.bbox_vocab_size),
        nn.ReLU(inplace=True),
    )

forward

forward(
    label: Float[Tensor, "seq batch group_label_vocab"],
    box: Int[Tensor, "seq batch 4"],
    z: Float[Tensor, "1 batch channels"],
    mask: Bool[Tensor, "batch seq"],
) -> tuple[
    Float[torch.Tensor, "seq_minus_eos batch channels"],
    Float[torch.Tensor, "seq batch box_logits"],
    Float[torch.Tensor, "seq batch group_label_vocab"],
]

Teacher-forced group decoding.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
def forward(
    self,
    label: Float[torch.Tensor, "seq batch group_label_vocab"],
    box: Int[torch.Tensor, "seq batch 4"],
    z: Float[torch.Tensor, "1 batch channels"],
    mask: Bool[torch.Tensor, "batch seq"],
) -> tuple[
    Float[torch.Tensor, "seq_minus_eos batch channels"],
    Float[torch.Tensor, "seq batch box_logits"],
    Float[torch.Tensor, "seq batch group_label_vocab"],
]:
    """Teacher-forced group decoding."""
    key_padding_mask = get_key_padding_mask(mask)
    tgt_label_vecs = self.layout_embd.get_group_label_embedding(label)
    tgt_box_vecs = self.layout_embd.get_box_embedding(box)
    tgt = self.proj_cat_tgt(torch.cat((tgt_label_vecs, tgt_box_vecs), dim=-1))
    length = tgt.size(0)
    causal_mask = generate_square_subsequent_mask(length, tgt.device)
    out = self.decoder(
        tgt[:length],
        z,
        tgt_mask=causal_mask,
        tgt_key_padding_mask=key_padding_mask,
    )
    rec_box = self.box_fcn(out)
    rec_label = self.label_fcn(out)
    return out[:-2], rec_box, rec_label

inference

inference(
    z: Float[Tensor, "1 batch channels"],
    *,
    max_group_num: int,
    device: device,
) -> tuple[
    Float[torch.Tensor, "seq_minus_eos batch channels"],
    Float[torch.Tensor, "seq batch box_logits"],
    Float[torch.Tensor, "seq batch group_label_vocab"],
]

Greedy autoregressive group decoding.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
def inference(
    self,
    z: Float[torch.Tensor, "1 batch channels"],
    *,
    max_group_num: int,
    device: torch.device,
) -> tuple[
    Float[torch.Tensor, "seq_minus_eos batch channels"],
    Float[torch.Tensor, "seq batch box_logits"],
    Float[torch.Tensor, "seq batch group_label_vocab"],
]:
    """Greedy autoregressive group decoding."""
    tgt = torch.zeros(max_group_num, z.shape[1], self.config.d_model, device=device)
    rec_labels = torch.zeros(
        max_group_num, z.shape[1], self.config.num_labels + 2, device=device
    )
    rec_bboxes = torch.zeros(
        max_group_num, z.shape[1], 4 * self.config.bbox_vocab_size, device=device
    )
    sos_box = torch.zeros(z.shape[1], 4, dtype=torch.long, device=device)
    sos_label = torch.zeros(z.shape[1], self.config.num_labels + 2, device=device)
    sos_label[:, self.config.group_sos_index] = 1.0
    sos_box_vecs = self.layout_embd.get_box_embedding(sos_box.unsqueeze(0))
    sos_label_vecs = self.layout_embd.group_label_embed(sos_label.unsqueeze(0))
    tgt[0] = self.proj_cat_tgt(
        torch.cat((sos_label_vecs, sos_box_vecs), dim=-1)
    ).squeeze(0)
    out = torch.zeros_like(tgt)
    for idx in range(max_group_num):
        decoded = self.decoder(tgt[: idx + 1], z)
        out[idx] = decoded[idx]
        rec_box_i = self.box_fcn(out[idx])
        rec_label_i = self.label_fcn(out[idx])
        rec_bboxes[idx] = rec_box_i
        rec_labels[idx] = rec_label_i
        if idx < max_group_num - 1:
            next_box = rec_box_i.reshape(-1, 4, self.config.bbox_vocab_size).argmax(
                -1
            )
            tgt_label_vecs = self.layout_embd.group_label_embed(rec_label_i)
            tgt_box_vecs = self.layout_embd.get_box_embedding(
                next_box.unsqueeze(0)
            ).squeeze(0)
            tgt[idx + 1] = self.proj_cat_tgt(
                torch.cat((tgt_label_vecs, tgt_box_vecs), dim=-1)
            )
    return out[:-2], rec_bboxes, rec_labels

ElementDecoder

Bases: Module

Autoregressive element decoder conditioned on group memory.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
class ElementDecoder(nn.Module):
    """Autoregressive element decoder conditioned on group memory."""

    square_subsequent_mask: Float[torch.Tensor, "max_seq max_seq"]

    def __init__(
        self, config: CoarseToFineConfig, layout_embd: LayoutEmbedding
    ) -> None:
        """Initialize element decoder modules."""
        super().__init__()
        self.config = config
        self.layout_embd = layout_embd
        self.proj_cat_memory = nn.Sequential(
            nn.Linear(config.d_model + config.d_model, config.d_model)
        )
        self.register_buffer(
            "square_subsequent_mask",
            generate_square_subsequent_mask(
                config.max_num_elements + 2, torch.device("cpu")
            ),
        )
        decoder_layer = nn.TransformerDecoderLayer(
            d_model=config.d_model,
            nhead=config.n_heads,
            dim_feedforward=config.dim_feedforward,
            dropout=config.dropout,
            batch_first=False,
        )
        decoder_norm = nn.LayerNorm(config.d_model)
        self.decoder = nn.TransformerDecoder(
            decoder_layer, num_layers=config.n_layers_decoder, norm=decoder_norm
        )
        self.label_fcn = nn.Sequential(
            nn.Linear(config.d_model, 128),
            nn.Linear(128, config.num_labels + 3),
            nn.ReLU(inplace=True),
        )
        self.box_fcn = nn.Sequential(
            nn.Linear(config.d_model, 4 * config.bbox_vocab_size),
            nn.ReLU(inplace=True),
        )

    def forward(
        self,
        label: Int[torch.Tensor, "seq groups batch 1"],
        box: Int[torch.Tensor, "seq groups batch 4"],
        memory: Float[torch.Tensor, "groups batch channels"],
        z: Float[torch.Tensor, "1 batch channels"],
        mask: Bool[torch.Tensor, "batch groups seq"],
    ) -> tuple[
        Float[torch.Tensor, "seq groups batch box_logits"],
        Float[torch.Tensor, "seq groups batch label_logits"],
    ]:
        """Teacher-forced element decoding."""
        z = z.repeat(memory.size(0), 1, 1).unsqueeze(0)
        memory = memory.unsqueeze(0)
        memory, z, label, box = pack_group_batch(memory, z, label, box)
        memory = self.proj_cat_memory(torch.cat((memory, z), dim=-1))
        batch_size, _, _ = mask.shape
        tgt = self.layout_embd(label.squeeze(-1), box)
        length = tgt.size(0)
        causal_mask = generate_square_subsequent_mask(length, tgt.device)
        out = self.decoder(tgt[:length], memory, tgt_mask=causal_mask)
        rec_box = self.box_fcn(out)
        rec_label = self.label_fcn(out)
        rec_box, rec_label = unpack_group_batch(batch_size, rec_box, rec_label)
        return rec_box, rec_label

    def inference(
        self,
        memory: Float[torch.Tensor, "groups batch channels"],
        z: Float[torch.Tensor, "1 batch channels"],
        *,
        max_num_elements: int,
        device: torch.device,
    ) -> tuple[
        Float[torch.Tensor, "seq groups batch box_logits"],
        Float[torch.Tensor, "seq groups batch label_logits"],
    ]:
        """Greedy autoregressive element decoding."""
        groups, batch_size, _ = memory.shape
        z = z.repeat(memory.size(0), 1, 1).unsqueeze(0)
        memory = memory.unsqueeze(0)
        memory, z = pack_group_batch(memory, z)
        memory = self.proj_cat_memory(torch.cat((memory, z), dim=-1))
        tgt = torch.zeros(
            max_num_elements, z.shape[1], self.config.d_model, device=device
        )
        rec_label = torch.zeros(
            max_num_elements, z.shape[1], self.config.num_labels + 3, device=device
        )
        rec_box = torch.zeros(
            max_num_elements, z.shape[1], 4 * self.config.bbox_vocab_size, device=device
        )
        sos_box = torch.zeros(z.shape[1], 4, dtype=torch.long, device=device)
        sos_label = torch.full(
            (z.shape[1],), self.config.element_sos_id, dtype=torch.long, device=device
        )
        tgt[0] = self.layout_embd(sos_label.unsqueeze(0), sos_box.unsqueeze(0)).squeeze(
            0
        )
        out = torch.zeros_like(tgt)
        for idx in range(max_num_elements):
            decoded = self.decoder(tgt[: idx + 1], memory)
            out[idx] = decoded[idx]
            rec_box_i = self.box_fcn(out[idx])
            rec_label_i = self.label_fcn(out[idx])
            rec_box[idx] = rec_box_i
            rec_label[idx] = rec_label_i
            if idx < max_num_elements - 1:
                next_box = rec_box_i.reshape(-1, 4, self.config.bbox_vocab_size).argmax(
                    -1
                )
                next_label = rec_label_i.argmax(1)
                tgt[idx + 1] = self.layout_embd(
                    next_label.unsqueeze(0), next_box.unsqueeze(0)
                ).squeeze(0)
        rec_box, rec_label = unpack_group_batch(batch_size, rec_box, rec_label)
        _ = groups
        return rec_box, rec_label

__init__

__init__(
    config: CoarseToFineConfig, layout_embd: LayoutEmbedding
) -> None

Initialize element decoder modules.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
def __init__(
    self, config: CoarseToFineConfig, layout_embd: LayoutEmbedding
) -> None:
    """Initialize element decoder modules."""
    super().__init__()
    self.config = config
    self.layout_embd = layout_embd
    self.proj_cat_memory = nn.Sequential(
        nn.Linear(config.d_model + config.d_model, config.d_model)
    )
    self.register_buffer(
        "square_subsequent_mask",
        generate_square_subsequent_mask(
            config.max_num_elements + 2, torch.device("cpu")
        ),
    )
    decoder_layer = nn.TransformerDecoderLayer(
        d_model=config.d_model,
        nhead=config.n_heads,
        dim_feedforward=config.dim_feedforward,
        dropout=config.dropout,
        batch_first=False,
    )
    decoder_norm = nn.LayerNorm(config.d_model)
    self.decoder = nn.TransformerDecoder(
        decoder_layer, num_layers=config.n_layers_decoder, norm=decoder_norm
    )
    self.label_fcn = nn.Sequential(
        nn.Linear(config.d_model, 128),
        nn.Linear(128, config.num_labels + 3),
        nn.ReLU(inplace=True),
    )
    self.box_fcn = nn.Sequential(
        nn.Linear(config.d_model, 4 * config.bbox_vocab_size),
        nn.ReLU(inplace=True),
    )

forward

forward(
    label: Int[Tensor, "seq groups batch 1"],
    box: Int[Tensor, "seq groups batch 4"],
    memory: Float[Tensor, "groups batch channels"],
    z: Float[Tensor, "1 batch channels"],
    mask: Bool[Tensor, "batch groups seq"],
) -> tuple[
    Float[torch.Tensor, "seq groups batch box_logits"],
    Float[torch.Tensor, "seq groups batch label_logits"],
]

Teacher-forced element decoding.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
def forward(
    self,
    label: Int[torch.Tensor, "seq groups batch 1"],
    box: Int[torch.Tensor, "seq groups batch 4"],
    memory: Float[torch.Tensor, "groups batch channels"],
    z: Float[torch.Tensor, "1 batch channels"],
    mask: Bool[torch.Tensor, "batch groups seq"],
) -> tuple[
    Float[torch.Tensor, "seq groups batch box_logits"],
    Float[torch.Tensor, "seq groups batch label_logits"],
]:
    """Teacher-forced element decoding."""
    z = z.repeat(memory.size(0), 1, 1).unsqueeze(0)
    memory = memory.unsqueeze(0)
    memory, z, label, box = pack_group_batch(memory, z, label, box)
    memory = self.proj_cat_memory(torch.cat((memory, z), dim=-1))
    batch_size, _, _ = mask.shape
    tgt = self.layout_embd(label.squeeze(-1), box)
    length = tgt.size(0)
    causal_mask = generate_square_subsequent_mask(length, tgt.device)
    out = self.decoder(tgt[:length], memory, tgt_mask=causal_mask)
    rec_box = self.box_fcn(out)
    rec_label = self.label_fcn(out)
    rec_box, rec_label = unpack_group_batch(batch_size, rec_box, rec_label)
    return rec_box, rec_label

inference

inference(
    memory: Float[Tensor, "groups batch channels"],
    z: Float[Tensor, "1 batch channels"],
    *,
    max_num_elements: int,
    device: device,
) -> tuple[
    Float[torch.Tensor, "seq groups batch box_logits"],
    Float[torch.Tensor, "seq groups batch label_logits"],
]

Greedy autoregressive element decoding.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
def inference(
    self,
    memory: Float[torch.Tensor, "groups batch channels"],
    z: Float[torch.Tensor, "1 batch channels"],
    *,
    max_num_elements: int,
    device: torch.device,
) -> tuple[
    Float[torch.Tensor, "seq groups batch box_logits"],
    Float[torch.Tensor, "seq groups batch label_logits"],
]:
    """Greedy autoregressive element decoding."""
    groups, batch_size, _ = memory.shape
    z = z.repeat(memory.size(0), 1, 1).unsqueeze(0)
    memory = memory.unsqueeze(0)
    memory, z = pack_group_batch(memory, z)
    memory = self.proj_cat_memory(torch.cat((memory, z), dim=-1))
    tgt = torch.zeros(
        max_num_elements, z.shape[1], self.config.d_model, device=device
    )
    rec_label = torch.zeros(
        max_num_elements, z.shape[1], self.config.num_labels + 3, device=device
    )
    rec_box = torch.zeros(
        max_num_elements, z.shape[1], 4 * self.config.bbox_vocab_size, device=device
    )
    sos_box = torch.zeros(z.shape[1], 4, dtype=torch.long, device=device)
    sos_label = torch.full(
        (z.shape[1],), self.config.element_sos_id, dtype=torch.long, device=device
    )
    tgt[0] = self.layout_embd(sos_label.unsqueeze(0), sos_box.unsqueeze(0)).squeeze(
        0
    )
    out = torch.zeros_like(tgt)
    for idx in range(max_num_elements):
        decoded = self.decoder(tgt[: idx + 1], memory)
        out[idx] = decoded[idx]
        rec_box_i = self.box_fcn(out[idx])
        rec_label_i = self.label_fcn(out[idx])
        rec_box[idx] = rec_box_i
        rec_label[idx] = rec_label_i
        if idx < max_num_elements - 1:
            next_box = rec_box_i.reshape(-1, 4, self.config.bbox_vocab_size).argmax(
                -1
            )
            next_label = rec_label_i.argmax(1)
            tgt[idx + 1] = self.layout_embd(
                next_label.unsqueeze(0), next_box.unsqueeze(0)
            ).squeeze(0)
    rec_box, rec_label = unpack_group_batch(batch_size, rec_box, rec_label)
    _ = groups
    return rec_box, rec_label

CoarseToFineForLayoutGeneration

Bases: PreTrainedModel

Transformers PreTrainedModel with checkpoint-compatible module names.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
class CoarseToFineForLayoutGeneration(PreTrainedModel):
    """Transformers ``PreTrainedModel`` with checkpoint-compatible module names."""

    config_class = CoarseToFineConfig
    base_model_prefix = "coarse_to_fine"
    main_input_name = "labels"
    _tied_weights_keys = {
        "encoder.embedding.label_embed.weight": "layout_embd.label_embed.weight",
        "group_decoder.layout_embd.label_embed.weight": "layout_embd.label_embed.weight",
        "ele_decoder.layout_embd.label_embed.weight": "layout_embd.label_embed.weight",
        "encoder.embedding.bbox_embed.weight": "layout_embd.bbox_embed.weight",
        "group_decoder.layout_embd.bbox_embed.weight": "layout_embd.bbox_embed.weight",
        "ele_decoder.layout_embd.bbox_embed.weight": "layout_embd.bbox_embed.weight",
        "encoder.embedding.proj_cat.weight": "layout_embd.proj_cat.weight",
        "group_decoder.layout_embd.proj_cat.weight": "layout_embd.proj_cat.weight",
        "ele_decoder.layout_embd.proj_cat.weight": "layout_embd.proj_cat.weight",
        "encoder.embedding.proj_cat.bias": "layout_embd.proj_cat.bias",
        "group_decoder.layout_embd.proj_cat.bias": "layout_embd.proj_cat.bias",
        "ele_decoder.layout_embd.proj_cat.bias": "layout_embd.proj_cat.bias",
        "encoder.embedding.group_label_embed.weight": (
            "layout_embd.group_label_embed.weight"
        ),
        "group_decoder.layout_embd.group_label_embed.weight": (
            "layout_embd.group_label_embed.weight"
        ),
        "ele_decoder.layout_embd.group_label_embed.weight": (
            "layout_embd.group_label_embed.weight"
        ),
        "encoder.embedding.group_label_embed.bias": (
            "layout_embd.group_label_embed.bias"
        ),
        "group_decoder.layout_embd.group_label_embed.bias": (
            "layout_embd.group_label_embed.bias"
        ),
        "ele_decoder.layout_embd.group_label_embed.bias": (
            "layout_embd.group_label_embed.bias"
        ),
    }

    def __init__(self, config: CoarseToFineConfig) -> None:
        """Initialize checkpoint-compatible modules."""
        super().__init__(config)
        self.layout_embd = LayoutEmbedding(config)
        self.encoder = Encoder(config, self.layout_embd)
        self.vae = VAE(config)
        self.group_decoder = GroupDecoder(config, self.layout_embd)
        self.ele_decoder = ElementDecoder(config, self.layout_embd)
        self.all_tied_weights_keys = dict(self._tied_weights_keys)

    @property
    def device(self) -> torch.device:
        """Return the current parameter device."""
        return next(self.parameters()).device

    def _sample_latent(
        self,
        *,
        batch_size: int,
        generator: torch.Generator | None,
        device: torch.device,
    ) -> Float[torch.Tensor, "1 batch latent"]:
        return cast(
            torch.FloatTensor,
            randn(
                (1, batch_size, self.config.d_z),
                generator=generator,
                device=device,
            ),
        )

    def forward(
        self,
        labels: Int[torch.Tensor, "batch elements"],
        bbox: Int[torch.Tensor, "batch elements 4"],
        mask: Bool[torch.Tensor, "batch elements"],
        group_bounding_box: Int[torch.Tensor, "batch seq 4"],
        label_in_one_group: Float[torch.Tensor, "batch seq vocab"],
        group_mask: Bool[torch.Tensor, "batch seq"],
        grouped_bbox: Int[torch.Tensor, "batch seq elements 4"],
        grouped_labels: Int[torch.Tensor, "batch seq elements"],
        grouped_mask: Bool[torch.Tensor, "batch seq elements"],
        latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
        use_teacher_forcing: bool = True,
        return_dict: bool | None = None,
    ) -> (
        dict[str, Shaped[torch.Tensor, "..."]] | tuple[Shaped[torch.Tensor, "..."], ...]
    ):
        """Run teacher-forced or greedy hierarchical decoding.

        Args:
            labels: Batch-first internal label ids.
            bbox: Batch-first discrete ``ltwh`` ids.
            mask: Batch-first valid element mask.
            group_bounding_box: Batch-first group discrete ``ltwh`` ids.
            label_in_one_group: Batch-first group label histograms.
            group_mask: Batch-first valid group mask.
            grouped_bbox: Batch-first group-relative discrete ``ltwh`` ids.
            grouped_labels: Batch-first group-relative internal labels.
            grouped_mask: Batch-first valid grouped-element mask.
            latent_z: Optional latent tensor to bypass stochastic sampling.
            use_teacher_forcing: Whether to use provided hierarchy tensors.
            return_dict: Return a dictionary when true.

        Returns:
            Dictionary of raw logits and latent tensors.
        """
        batch_size, groups, seq, dims = grouped_bbox.shape
        seq_bbox = make_seq_first(bbox)
        seq_group_bbox = make_seq_first(group_bounding_box)
        grouped_box = make_seq_first(
            grouped_bbox.reshape(batch_size, groups * seq, dims)
        )
        grouped_box = make_seq_first(
            grouped_box.reshape(groups, seq, batch_size * dims)
        ).reshape(seq, groups, batch_size, dims)
        seq_labels = make_seq_first(labels.unsqueeze(2)).squeeze(2)
        seq_group_labels = make_seq_first(label_in_one_group)
        grouped_label = make_seq_first(
            grouped_labels.reshape(batch_size, groups * seq, 1)
        )
        grouped_label = make_seq_first(
            grouped_label.reshape(groups, seq, batch_size)
        ).unsqueeze(3)
        memory = self.encoder(seq_labels, seq_bbox, mask)
        if latent_z is None:
            z, mu, logvar = self.vae(memory)
        else:
            z = (
                latent_z
                if latent_z.dim() == 3 and latent_z.size(0) == 1
                else make_seq_first(latent_z)
            )
            mu = torch.zeros_like(z)
            logvar = torch.zeros_like(z)
        if use_teacher_forcing:
            group_embd, rec_group_bbox, rec_group_label = self.group_decoder(
                seq_group_labels, seq_group_bbox, z, group_mask
            )
            rec_box, rec_label = self.ele_decoder(
                grouped_label,
                grouped_box,
                group_embd,
                z,
                grouped_mask,
            )
        else:
            group_embd, rec_group_bbox, rec_group_label = self.group_decoder.inference(
                z, max_group_num=seq_group_labels.shape[0], device=self.device
            )
            rec_box, rec_label = self.ele_decoder.inference(
                group_embd,
                z,
                max_num_elements=grouped_label.shape[0],
                device=self.device,
            )
        rec_box = make_group_first(rec_box)
        rec_label = make_group_first(rec_label)
        flat_box = make_batch_first(rec_box.reshape(groups * seq, batch_size, -1))
        flat_label = make_batch_first(rec_label.reshape(groups * seq, batch_size, -1))
        rec_group_bbox = make_batch_first(rec_group_bbox)
        rec_group_label = make_batch_first(rec_group_label)
        output = {
            "group_bounding_box_logits": rec_group_bbox.reshape(
                batch_size, rec_group_bbox.size(1), 4, self.config.bbox_vocab_size
            ),
            "label_in_one_group_logits": rec_group_label,
            "grouped_bbox_logits": flat_box.reshape(
                batch_size, groups, seq, 4, self.config.bbox_vocab_size
            ),
            "grouped_label_logits": flat_label.reshape(batch_size, groups, seq, -1),
            "mu": make_batch_first(mu),
            "logvar": make_batch_first(logvar),
            "latent_z": make_batch_first(z),
        }
        if return_dict is False:
            return tuple(output.values())
        return output

    @torch.no_grad()
    def _decode_hierarchy(
        self, latent_z: Float[torch.Tensor, "1 batch latent"]
    ) -> dict[str, Shaped[torch.Tensor, "..."]]:
        """Decode raw hierarchy logits from a seq-first latent tensor."""
        z = latent_z.to(self.device)
        group_embd, group_bbox, group_label = self.group_decoder.inference(
            z,
            max_group_num=self.config.max_num_elements + 2,
            device=self.device,
        )
        elem_bbox, elem_label = self.ele_decoder.inference(
            group_embd,
            z,
            max_num_elements=self.config.max_num_elements + 2,
            device=self.device,
        )
        elem_bbox = make_group_first(elem_bbox)
        elem_label = make_group_first(elem_label)
        groups, seq, batch_size, _ = elem_bbox.shape
        return {
            "group_bounding_box_logits": make_batch_first(group_bbox).reshape(
                batch_size,
                group_bbox.size(0),
                4,
                self.config.bbox_vocab_size,
            ),
            "label_in_one_group_logits": make_batch_first(group_label),
            "grouped_bbox_logits": make_batch_first(
                elem_bbox.reshape(groups * seq, batch_size, -1)
            ).reshape(batch_size, groups, seq, 4, self.config.bbox_vocab_size),
            "grouped_label_logits": make_batch_first(
                elem_label.reshape(groups * seq, batch_size, -1)
            ).reshape(batch_size, groups, seq, -1),
        }

device property

device: device

Return the current parameter device.

__init__

__init__(config: CoarseToFineConfig) -> None

Initialize checkpoint-compatible modules.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
496
497
498
499
500
501
502
503
504
def __init__(self, config: CoarseToFineConfig) -> None:
    """Initialize checkpoint-compatible modules."""
    super().__init__(config)
    self.layout_embd = LayoutEmbedding(config)
    self.encoder = Encoder(config, self.layout_embd)
    self.vae = VAE(config)
    self.group_decoder = GroupDecoder(config, self.layout_embd)
    self.ele_decoder = ElementDecoder(config, self.layout_embd)
    self.all_tied_weights_keys = dict(self._tied_weights_keys)

forward

forward(
    labels: Int[Tensor, "batch elements"],
    bbox: Int[Tensor, "batch elements 4"],
    mask: Bool[Tensor, "batch elements"],
    group_bounding_box: Int[Tensor, "batch seq 4"],
    label_in_one_group: Float[Tensor, "batch seq vocab"],
    group_mask: Bool[Tensor, "batch seq"],
    grouped_bbox: Int[Tensor, "batch seq elements 4"],
    grouped_labels: Int[Tensor, "batch seq elements"],
    grouped_mask: Bool[Tensor, "batch seq elements"],
    latent_z: Float[Tensor, "1 batch latent"] | None = None,
    use_teacher_forcing: bool = True,
    return_dict: bool | None = None,
) -> (
    dict[str, Shaped[torch.Tensor, "..."]]
    | tuple[Shaped[torch.Tensor, "..."], ...]
)

Run teacher-forced or greedy hierarchical decoding.

Parameters:

Name Type Description Default
labels Int[Tensor, 'batch elements']

Batch-first internal label ids.

required
bbox Int[Tensor, 'batch elements 4']

Batch-first discrete ltwh ids.

required
mask Bool[Tensor, 'batch elements']

Batch-first valid element mask.

required
group_bounding_box Int[Tensor, 'batch seq 4']

Batch-first group discrete ltwh ids.

required
label_in_one_group Float[Tensor, 'batch seq vocab']

Batch-first group label histograms.

required
group_mask Bool[Tensor, 'batch seq']

Batch-first valid group mask.

required
grouped_bbox Int[Tensor, 'batch seq elements 4']

Batch-first group-relative discrete ltwh ids.

required
grouped_labels Int[Tensor, 'batch seq elements']

Batch-first group-relative internal labels.

required
grouped_mask Bool[Tensor, 'batch seq elements']

Batch-first valid grouped-element mask.

required
latent_z Float[Tensor, '1 batch latent'] | None

Optional latent tensor to bypass stochastic sampling.

None
use_teacher_forcing bool

Whether to use provided hierarchy tensors.

True
return_dict bool | None

Return a dictionary when true.

None

Returns:

Type Description
dict[str, Shaped[Tensor, '...']] | tuple[Shaped[Tensor, '...'], ...]

Dictionary of raw logits and latent tensors.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
def forward(
    self,
    labels: Int[torch.Tensor, "batch elements"],
    bbox: Int[torch.Tensor, "batch elements 4"],
    mask: Bool[torch.Tensor, "batch elements"],
    group_bounding_box: Int[torch.Tensor, "batch seq 4"],
    label_in_one_group: Float[torch.Tensor, "batch seq vocab"],
    group_mask: Bool[torch.Tensor, "batch seq"],
    grouped_bbox: Int[torch.Tensor, "batch seq elements 4"],
    grouped_labels: Int[torch.Tensor, "batch seq elements"],
    grouped_mask: Bool[torch.Tensor, "batch seq elements"],
    latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
    use_teacher_forcing: bool = True,
    return_dict: bool | None = None,
) -> (
    dict[str, Shaped[torch.Tensor, "..."]] | tuple[Shaped[torch.Tensor, "..."], ...]
):
    """Run teacher-forced or greedy hierarchical decoding.

    Args:
        labels: Batch-first internal label ids.
        bbox: Batch-first discrete ``ltwh`` ids.
        mask: Batch-first valid element mask.
        group_bounding_box: Batch-first group discrete ``ltwh`` ids.
        label_in_one_group: Batch-first group label histograms.
        group_mask: Batch-first valid group mask.
        grouped_bbox: Batch-first group-relative discrete ``ltwh`` ids.
        grouped_labels: Batch-first group-relative internal labels.
        grouped_mask: Batch-first valid grouped-element mask.
        latent_z: Optional latent tensor to bypass stochastic sampling.
        use_teacher_forcing: Whether to use provided hierarchy tensors.
        return_dict: Return a dictionary when true.

    Returns:
        Dictionary of raw logits and latent tensors.
    """
    batch_size, groups, seq, dims = grouped_bbox.shape
    seq_bbox = make_seq_first(bbox)
    seq_group_bbox = make_seq_first(group_bounding_box)
    grouped_box = make_seq_first(
        grouped_bbox.reshape(batch_size, groups * seq, dims)
    )
    grouped_box = make_seq_first(
        grouped_box.reshape(groups, seq, batch_size * dims)
    ).reshape(seq, groups, batch_size, dims)
    seq_labels = make_seq_first(labels.unsqueeze(2)).squeeze(2)
    seq_group_labels = make_seq_first(label_in_one_group)
    grouped_label = make_seq_first(
        grouped_labels.reshape(batch_size, groups * seq, 1)
    )
    grouped_label = make_seq_first(
        grouped_label.reshape(groups, seq, batch_size)
    ).unsqueeze(3)
    memory = self.encoder(seq_labels, seq_bbox, mask)
    if latent_z is None:
        z, mu, logvar = self.vae(memory)
    else:
        z = (
            latent_z
            if latent_z.dim() == 3 and latent_z.size(0) == 1
            else make_seq_first(latent_z)
        )
        mu = torch.zeros_like(z)
        logvar = torch.zeros_like(z)
    if use_teacher_forcing:
        group_embd, rec_group_bbox, rec_group_label = self.group_decoder(
            seq_group_labels, seq_group_bbox, z, group_mask
        )
        rec_box, rec_label = self.ele_decoder(
            grouped_label,
            grouped_box,
            group_embd,
            z,
            grouped_mask,
        )
    else:
        group_embd, rec_group_bbox, rec_group_label = self.group_decoder.inference(
            z, max_group_num=seq_group_labels.shape[0], device=self.device
        )
        rec_box, rec_label = self.ele_decoder.inference(
            group_embd,
            z,
            max_num_elements=grouped_label.shape[0],
            device=self.device,
        )
    rec_box = make_group_first(rec_box)
    rec_label = make_group_first(rec_label)
    flat_box = make_batch_first(rec_box.reshape(groups * seq, batch_size, -1))
    flat_label = make_batch_first(rec_label.reshape(groups * seq, batch_size, -1))
    rec_group_bbox = make_batch_first(rec_group_bbox)
    rec_group_label = make_batch_first(rec_group_label)
    output = {
        "group_bounding_box_logits": rec_group_bbox.reshape(
            batch_size, rec_group_bbox.size(1), 4, self.config.bbox_vocab_size
        ),
        "label_in_one_group_logits": rec_group_label,
        "grouped_bbox_logits": flat_box.reshape(
            batch_size, groups, seq, 4, self.config.bbox_vocab_size
        ),
        "grouped_label_logits": flat_label.reshape(batch_size, groups, seq, -1),
        "mu": make_batch_first(mu),
        "logvar": make_batch_first(logvar),
        "latent_z": make_batch_first(z),
    }
    if return_dict is False:
        return tuple(output.values())
    return output

make_seq_first

make_seq_first(
    arg: Shaped[Tensor, "batch seq ..."],
) -> Shaped[torch.Tensor, "seq batch ..."]

Convert (batch, seq, ...) tensors to (seq, batch, ...).

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
17
18
19
20
21
22
def make_seq_first(
    arg: Shaped[torch.Tensor, "batch seq ..."],
) -> Shaped[torch.Tensor, "seq batch ..."]:
    """Convert ``(batch, seq, ...)`` tensors to ``(seq, batch, ...)``."""
    dims = [1, 0, *range(2, arg.dim())]
    return arg.permute(*dims)

make_batch_first

make_batch_first(
    arg: Shaped[Tensor, "seq batch ..."],
) -> Shaped[torch.Tensor, "batch seq ..."]

Convert (seq, batch, ...) tensors to (batch, seq, ...).

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
25
26
27
28
29
30
def make_batch_first(
    arg: Shaped[torch.Tensor, "seq batch ..."],
) -> Shaped[torch.Tensor, "batch seq ..."]:
    """Convert ``(seq, batch, ...)`` tensors to ``(batch, seq, ...)``."""
    dims = [1, 0, *range(2, arg.dim())]
    return arg.permute(*dims)

get_key_padding_mask

get_key_padding_mask(
    mask: Bool[Tensor, "batch seq"],
) -> Bool[torch.Tensor, "batch seq"]

Match the checkpoint cumulative padding-mask convention.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
33
34
35
36
37
def get_key_padding_mask(
    mask: Bool[torch.Tensor, "batch seq"],
) -> Bool[torch.Tensor, "batch seq"]:
    """Match the checkpoint cumulative padding-mask convention."""
    return (mask == 0).cumsum(dim=0) > 0

get_padding_mask

get_padding_mask(
    mask: Bool[Tensor, "batch seq"],
) -> Bool[torch.Tensor, "seq batch 1"]

Convert batch-first mask to seq-first broadcast mask.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
40
41
42
43
44
def get_padding_mask(
    mask: Bool[torch.Tensor, "batch seq"],
) -> Bool[torch.Tensor, "seq batch 1"]:
    """Convert batch-first mask to seq-first broadcast mask."""
    return make_seq_first(mask.unsqueeze(2))

make_group_first

make_group_first(
    arg: Shaped[Tensor, "seq groups batch ..."],
) -> Shaped[torch.Tensor, "groups seq batch ..."]

Convert (seq, group, batch, ...) to (group, seq, batch, ...).

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
47
48
49
50
51
def make_group_first(
    arg: Shaped[torch.Tensor, "seq groups batch ..."],
) -> Shaped[torch.Tensor, "groups seq batch ..."]:
    """Convert ``(seq, group, batch, ...)`` to ``(group, seq, batch, ...)``."""
    return arg.permute(1, 0, 2, *range(3, arg.dim()))

pack_group_batch

pack_group_batch(
    *args: Shaped[Tensor, "seq groups batch ..."],
) -> tuple[
    Shaped[torch.Tensor, "seq group_batch ..."], ...
]

Flatten group and batch dimensions in seq-first grouped tensors.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
54
55
56
57
58
59
60
61
def pack_group_batch(
    *args: Shaped[torch.Tensor, "seq groups batch ..."],
) -> tuple[Shaped[torch.Tensor, "seq group_batch ..."], ...]:
    """Flatten group and batch dimensions in seq-first grouped tensors."""
    return tuple(
        arg.reshape(arg.size(0), arg.size(1) * arg.size(2), *arg.shape[3:])
        for arg in args
    )

unpack_group_batch

unpack_group_batch(
    batch_size: int,
    *args: Shaped[Tensor, "seq group_batch ..."],
) -> tuple[
    Shaped[torch.Tensor, "seq groups batch ..."], ...
]

Restore (seq, group, batch, ...) tensors from packed group batches.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
64
65
66
67
68
69
70
def unpack_group_batch(
    batch_size: int, *args: Shaped[torch.Tensor, "seq group_batch ..."]
) -> tuple[Shaped[torch.Tensor, "seq groups batch ..."], ...]:
    """Restore ``(seq, group, batch, ...)`` tensors from packed group batches."""
    return tuple(
        arg.reshape(arg.size(0), -1, batch_size, *arg.shape[2:]) for arg in args
    )

generate_square_subsequent_mask

generate_square_subsequent_mask(
    size: int, device: device
) -> Float[torch.Tensor, "size size"]

Create the causal decoder mask used by the checkpoint model.

Source code in models/coarse-to-fine/src/coarse_to_fine/modeling_coarse_to_fine.py
73
74
75
76
77
78
79
80
def generate_square_subsequent_mask(
    size: int, device: torch.device
) -> Float[torch.Tensor, "size size"]:
    """Create the causal decoder mask used by the checkpoint model."""
    mask = (torch.triu(torch.ones(size, size, device=device)) == 1).transpose(0, 1)
    return (
        mask.float().masked_fill(mask == 0, float("-inf")).masked_fill(mask == 1, 0.0)
    )

pipeline_coarse_to_fine

Pipeline wrapper for Coarse-to-Fine.

CoarseToFinePipeline

Bases: Pipeline

Orchestrate Coarse-to-Fine layout generation.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
class CoarseToFinePipeline(Pipeline):
    """Orchestrate Coarse-to-Fine layout generation."""

    model: CoarseToFineForLayoutGeneration
    processor: CoarseToFineProcessor

    def __init__(
        self,
        model: CoarseToFineForLayoutGeneration,
        processor: CoarseToFineProcessor,
    ) -> None:
        """Initialize the pipeline with a model and processor."""
        super().__init__(model=model, tokenizer=None)
        self.processor = processor

    def _sanitize_parameters(
        self, **kwargs: CoarseToFinePipelineParameter
    ) -> tuple[
        dict[str, CoarseToFinePipelineParameter],
        dict[str, CoarseToFinePipelineParameter],
        dict[str, CoarseToFinePipelineParameter],
    ]:
        return {}, kwargs, {}

    def preprocess(
        self,
        input_: CoarseToFinePipelineParameter,
        **preprocess_parameters: dict[str, CoarseToFinePipelineParameter],
    ) -> dict[str, GenericTensor]:
        """Satisfy the abstract pipeline API; direct calls bypass this path."""
        _ = (input_, preprocess_parameters)
        return {}

    def _forward(
        self,
        input_tensors: dict[str, GenericTensor],
        **forward_parameters: dict[str, CoarseToFinePipelineParameter],
    ) -> ModelOutput:
        _ = (input_tensors, forward_parameters)
        raise NotImplementedError("Use CoarseToFinePipeline.__call__ directly")

    def postprocess(
        self,
        model_outputs: LayoutGenerationOutput | CoarseToFineOutputDict,
        **kwargs: dict[str, CoarseToFinePipelineParameter],
    ) -> LayoutGenerationOutput | CoarseToFineOutputDict:
        """Return model outputs without additional formatting."""
        _ = kwargs
        return model_outputs

    def __call__(
        self,
        *,
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: ConditionType | str = ConditionType.unconditional,
        labels: Int[torch.Tensor, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        num_inference_steps: int | None = None,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
        latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
    ) -> LayoutGenerationOutput | CoarseToFineOutputDict:
        """Generate layouts through model decode and processor post-processing."""
        del labels, bbox, mask, num_elements
        del box_format, normalized, canvas_size, num_inference_steps
        condition = normalize_condition_type(condition_type)
        if condition is not ConditionType.unconditional:
            raise NotImplementedError(
                "Coarse-to-Fine released checkpoints support only unconditional generation"
            )

        generator = resolve_torch_generator(generator=generator, seed=seed)
        if latent_z is None:
            sampled_z = self.model._sample_latent(
                batch_size=batch_size,
                generator=generator,
                device=self.model.device,
            )
        else:
            sampled_z = cast(torch.FloatTensor, latent_z.to(self.model.device))
        raw = self.model._decode_hierarchy(sampled_z)
        hierarchy = decode_hierarchy_from_logits(
            group_bbox_logits=raw["group_bounding_box_logits"],
            group_label_logits=raw["label_in_one_group_logits"],
            grouped_bbox_logits=raw["grouped_bbox_logits"],
            grouped_label_logits=raw["grouped_label_logits"],
            num_labels=self.model.config.num_labels,
            group_eos_index=self.model.config.group_eos_index,
            element_eos_id=self.model.config.element_eos_id,
            discrete_x_grid=self.model.config.discrete_x_grid,
            discrete_y_grid=self.model.config.discrete_y_grid,
        )
        output = cast(
            LayoutGenerationOutput,
            self.processor.post_process_hierarchy(
                hierarchy,
                output_type=OutputType.dataclass,
                return_intermediates=return_intermediates,
            ),
        )
        output.sequences = cast(torch.Tensor, hierarchy.discrete_relative_bbox)
        output.scores = None
        output.trajectory = raw if return_intermediates else None
        if normalize_output_type(output_type) is OutputType.dict:
            return dict(output)
        return output

__init__

__init__(
    model: CoarseToFineForLayoutGeneration,
    processor: CoarseToFineProcessor,
) -> None

Initialize the pipeline with a model and processor.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
49
50
51
52
53
54
55
56
def __init__(
    self,
    model: CoarseToFineForLayoutGeneration,
    processor: CoarseToFineProcessor,
) -> None:
    """Initialize the pipeline with a model and processor."""
    super().__init__(model=model, tokenizer=None)
    self.processor = processor

preprocess

preprocess(
    input_: CoarseToFinePipelineParameter,
    **preprocess_parameters: dict[
        str, CoarseToFinePipelineParameter
    ],
) -> dict[str, GenericTensor]

Satisfy the abstract pipeline API; direct calls bypass this path.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
67
68
69
70
71
72
73
74
def preprocess(
    self,
    input_: CoarseToFinePipelineParameter,
    **preprocess_parameters: dict[str, CoarseToFinePipelineParameter],
) -> dict[str, GenericTensor]:
    """Satisfy the abstract pipeline API; direct calls bypass this path."""
    _ = (input_, preprocess_parameters)
    return {}

postprocess

postprocess(
    model_outputs: LayoutGenerationOutput
    | CoarseToFineOutputDict,
    **kwargs: dict[str, CoarseToFinePipelineParameter],
) -> LayoutGenerationOutput | CoarseToFineOutputDict

Return model outputs without additional formatting.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
84
85
86
87
88
89
90
91
def postprocess(
    self,
    model_outputs: LayoutGenerationOutput | CoarseToFineOutputDict,
    **kwargs: dict[str, CoarseToFinePipelineParameter],
) -> LayoutGenerationOutput | CoarseToFineOutputDict:
    """Return model outputs without additional formatting."""
    _ = kwargs
    return model_outputs

__call__

__call__(
    *,
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: ConditionType
    | str = ConditionType.unconditional,
    labels: Int[Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int
    | list[int]
    | Int[Tensor, "batch"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latent_z: Float[Tensor, "1 batch latent"] | None = None,
) -> LayoutGenerationOutput | CoarseToFineOutputDict

Generate layouts through model decode and processor post-processing.

Source code in models/coarse-to-fine/src/coarse_to_fine/pipeline_coarse_to_fine.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def __call__(
    self,
    *,
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: ConditionType | str = ConditionType.unconditional,
    labels: Int[torch.Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latent_z: Float[torch.Tensor, "1 batch latent"] | None = None,
) -> LayoutGenerationOutput | CoarseToFineOutputDict:
    """Generate layouts through model decode and processor post-processing."""
    del labels, bbox, mask, num_elements
    del box_format, normalized, canvas_size, num_inference_steps
    condition = normalize_condition_type(condition_type)
    if condition is not ConditionType.unconditional:
        raise NotImplementedError(
            "Coarse-to-Fine released checkpoints support only unconditional generation"
        )

    generator = resolve_torch_generator(generator=generator, seed=seed)
    if latent_z is None:
        sampled_z = self.model._sample_latent(
            batch_size=batch_size,
            generator=generator,
            device=self.model.device,
        )
    else:
        sampled_z = cast(torch.FloatTensor, latent_z.to(self.model.device))
    raw = self.model._decode_hierarchy(sampled_z)
    hierarchy = decode_hierarchy_from_logits(
        group_bbox_logits=raw["group_bounding_box_logits"],
        group_label_logits=raw["label_in_one_group_logits"],
        grouped_bbox_logits=raw["grouped_bbox_logits"],
        grouped_label_logits=raw["grouped_label_logits"],
        num_labels=self.model.config.num_labels,
        group_eos_index=self.model.config.group_eos_index,
        element_eos_id=self.model.config.element_eos_id,
        discrete_x_grid=self.model.config.discrete_x_grid,
        discrete_y_grid=self.model.config.discrete_y_grid,
    )
    output = cast(
        LayoutGenerationOutput,
        self.processor.post_process_hierarchy(
            hierarchy,
            output_type=OutputType.dataclass,
            return_intermediates=return_intermediates,
        ),
    )
    output.sequences = cast(torch.Tensor, hierarchy.discrete_relative_bbox)
    output.scores = None
    output.trajectory = raw if return_intermediates else None
    if normalize_output_type(output_type) is OutputType.dict:
        return dict(output)
    return output

processing_coarse_to_fine

Processor for Coarse-to-Fine layouts and hierarchy tensors.

CoarseToFineProcessor

Bases: ProcessorMixin

Convert public layouts to Coarse-to-Fine hierarchy tensors.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
class CoarseToFineProcessor(ProcessorMixin):
    """Convert public layouts to Coarse-to-Fine hierarchy tensors."""

    attributes: list[str] = []
    processor_class = "CoarseToFineProcessor"

    def __init__(
        self,
        dataset: DatasetName | str = DatasetName.rico25,
        *,
        x_grid: int = 128,
        y_grid: int = 128,
        max_num_elements: int = 20,
        id2label: dict[int, str] | None = None,
    ) -> None:
        """Initialize label maps and discretization settings.

        Args:
            dataset: Canonical layout dataset.
            x_grid: Number of x/width bins.
            y_grid: Number of y/height bins.
            max_num_elements: Padded flat sequence length.
            id2label: Optional public label map.

        Examples:
            >>> CoarseToFineProcessor(dataset="publaynet").id2label[0]
            'text'
        """
        normalized_dataset = normalize_dataset_name(dataset)
        self.dataset = str(normalized_dataset)
        self.x_grid = x_grid
        self.y_grid = y_grid
        self.max_num_elements = max_num_elements
        self.id2label = id2label or id2label_for_dataset(normalized_dataset)
        self.label2id = label2id_for_dataset(normalized_dataset)

    @classmethod
    def from_config(
        cls,
        dataset: DatasetName | str = DatasetName.rico25,
        *,
        x_grid: int = 128,
        y_grid: int = 128,
        max_num_elements: int = 20,
        id2label: dict[int, str] | None = None,
    ) -> "CoarseToFineProcessor":
        """Construct a processor without external files."""
        return cls(
            dataset=dataset,
            x_grid=x_grid,
            y_grid=y_grid,
            max_num_elements=max_num_elements,
            id2label=id2label,
        )

    def to_dict(self) -> dict[str, str | int | dict[str, str]]:
        """Serialize processor state to a JSON-compatible dictionary."""
        return {
            "processor_class": self.processor_class,
            "dataset": self.dataset,
            "x_grid": self.x_grid,
            "y_grid": self.y_grid,
            "max_num_elements": self.max_num_elements,
            "id2label": {str(key): value for key, value in self.id2label.items()},
        }

    def save_pretrained(
        self,
        save_directory: str | PathLike[str],
        push_to_hub: bool = False,
        **kwargs: str | int | float | bool | None,
    ) -> list[str]:
        """Save processor metadata next to a converted checkpoint."""
        _ = (push_to_hub, kwargs)
        path = Path(save_directory)
        path.mkdir(parents=True, exist_ok=True)
        out_file = path / "preprocessor_config.json"
        out_file.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n")
        return [str(out_file)]

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | PathLike[str],
        cache_dir: str | PathLike[str] | None = None,
        force_download: bool = False,
        local_files_only: bool = False,
        token: str | bool | None = None,
        revision: str = "main",
        **kwargs: str | int | float | bool | None,
    ) -> "CoarseToFineProcessor":
        """Load processor metadata from a local ``save_pretrained`` directory."""
        _ = (cache_dir, force_download, local_files_only, token, revision, kwargs)
        path = Path(pretrained_model_name_or_path) / "preprocessor_config.json"
        data = json.loads(path.read_text())
        return cls(
            dataset=str(data["dataset"]),
            x_grid=int(data["x_grid"]),
            y_grid=int(data["y_grid"]),
            max_num_elements=int(data["max_num_elements"]),
            id2label={int(key): str(value) for key, value in data["id2label"].items()},
        )

    def _labels_to_vendor(
        self, labels: Int[torch.Tensor, "..."]
    ) -> Int[torch.Tensor, "..."]:
        return cast(torch.LongTensor, labels.long() + 1)

    def __call__(
        self,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | None = None,
        bbox: list[list[list[float]]]
        | Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        return_tensors: Literal["pt"] = "pt",
    ) -> BatchEncoding:
        """Build discrete flat tensors from public layout inputs.

        Args:
            labels: Public zero-based labels or label strings.
            bbox: Public normalized boxes.
            mask: Optional valid-element mask.
            box_format: Input box format.
            normalized: Must be true; pixel boxes are dataset-loader work.
            return_tensors: Only ``"pt"`` is supported.

        Returns:
            BatchEncoding with internal labels, discrete boxes, and masks.

        Raises:
            ValueError: If labels or boxes are missing.
        """
        if return_tensors != "pt":
            raise ValueError("Only return_tensors='pt' is supported")

        if not normalized:
            raise ValueError("CoarseToFineProcessor expects normalized boxes")

        if labels is None or bbox is None:
            raise ValueError("labels and bbox are required for processor encoding")

        label_tensor = self._coerce_labels(labels)
        bbox_tensor = torch.as_tensor(bbox, dtype=torch.float32)
        encoded_mask = (
            cast(torch.BoolTensor, torch.ones(label_tensor.shape, dtype=torch.bool))
            if mask is None
            else cast(torch.BoolTensor, torch.as_tensor(mask, dtype=torch.bool))
        )
        ltwh = public_to_ltwh(bbox_tensor, box_format=box_format)
        discrete = discretize_ltwh(ltwh, num_x_grid=self.x_grid, num_y_grid=self.y_grid)
        vendor_labels = self._labels_to_vendor(label_tensor)
        return BatchEncoding(
            {"labels": vendor_labels, "bbox": discrete, "mask": encoded_mask}
        )

    def _coerce_labels(
        self,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"],
    ) -> Int[torch.Tensor, "batch elements"]:
        if isinstance(labels, torch.Tensor | np.ndarray):
            return cast(torch.LongTensor, torch.as_tensor(labels, dtype=torch.long))
        rows: list[list[int]] = []
        for row in labels:
            values: list[int] = []
            for label in row:
                if isinstance(label, int):
                    values.append(label)
                else:
                    values.append(self.label2id[label])
            rows.append(values)
        max_len = max((len(row) for row in rows), default=1)
        padded = [row + [0] * (max_len - len(row)) for row in rows]
        return cast(torch.LongTensor, torch.tensor(padded, dtype=torch.long))

    def build_hierarchy_batch(
        self,
        labels: Int[torch.Tensor, "batch elements"],
        bbox: Float[torch.Tensor, "batch elements 4"],
        mask: Bool[torch.Tensor, "batch elements"],
    ) -> BatchEncoding:
        """Build padded hierarchy tensors for training/reference batches."""
        batch = labels.size(0)
        flat_enc = self(labels=labels, bbox=bbox, mask=mask, return_tensors="pt")
        encodings = []
        for idx in range(batch):
            valid = mask[idx].bool()
            encodings.append(
                build_cut_hierarchy(
                    public_to_ltwh(bbox[idx, valid]),
                    self._labels_to_vendor(labels[idx, valid]),
                    num_labels=len(self.id2label),
                    discrete_x_grid=self.x_grid,
                    discrete_y_grid=self.y_grid,
                )
            )
        max_groups = max(enc.group_bounding_box.size(0) for enc in encodings) + 2
        max_decode_groups = max_groups - 2
        max_group_elems = (
            max(max(group.size(0) for group in enc.grouped_labels) for enc in encodings)
            + 2
        )
        group_boxes = []
        group_labels = []
        group_masks = []
        grouped_boxes = []
        grouped_labels = []
        grouped_masks = []
        for enc in encodings:
            group_count = enc.group_bounding_box.size(0)
            sos_group_box = torch.zeros((1, 4), dtype=torch.long)
            eos_group_box = torch.zeros((1, 4), dtype=torch.long)
            padded_group_box = torch.cat(
                (sos_group_box, enc.group_bounding_box.long(), eos_group_box), dim=0
            )
            padded_group_box = F.pad(
                padded_group_box, (0, 0, 0, max_groups - padded_group_box.size(0))
            )
            hist = torch.zeros(
                (group_count + 2, len(self.id2label) + 2), dtype=torch.float32
            )
            hist[0, 0] = 1.0
            hist[1 : group_count + 1, 1:-1] = enc.label_in_one_group.float()
            hist[group_count + 1, -1] = 1.0
            hist = F.pad(hist, (0, 0, 0, max_groups - hist.size(0)))
            group_mask = torch.arange(max_groups) < group_count + 2
            per_group_boxes = []
            per_group_labels = []
            per_group_masks = []
            for group_idx in range(group_count):
                box = enc.grouped_bbox[group_idx].long()
                label = enc.grouped_labels[group_idx].long()
                label = torch.cat(
                    (
                        torch.tensor([len(self.id2label) + 1]),
                        label,
                        torch.tensor([len(self.id2label) + 2]),
                    )
                )
                box = torch.cat(
                    (
                        torch.zeros((1, 4), dtype=torch.long),
                        box,
                        torch.zeros((1, 4), dtype=torch.long),
                    )
                )
                valid_count = min(label.size(0), max_group_elems)
                per_group_boxes.append(
                    F.pad(
                        box[:max_group_elems], (0, 0, 0, max_group_elems - valid_count)
                    )
                )
                per_group_labels.append(
                    F.pad(label[:max_group_elems], (0, max_group_elems - valid_count))
                )
                per_group_masks.append(torch.arange(max_group_elems) < valid_count)
            while len(per_group_boxes) < max_decode_groups:
                per_group_boxes.append(
                    torch.zeros((max_group_elems, 4), dtype=torch.long)
                )
                per_group_labels.append(
                    torch.zeros((max_group_elems,), dtype=torch.long)
                )
                per_group_masks.append(
                    torch.zeros((max_group_elems,), dtype=torch.bool)
                )
            group_boxes.append(padded_group_box)
            group_labels.append(hist)
            group_masks.append(group_mask)
            grouped_boxes.append(torch.stack(per_group_boxes[:max_decode_groups]))
            grouped_labels.append(torch.stack(per_group_labels[:max_decode_groups]))
            grouped_masks.append(torch.stack(per_group_masks[:max_decode_groups]))
        flat_enc.update(
            {
                "group_bounding_box": torch.stack(group_boxes),
                "label_in_one_group": torch.stack(group_labels),
                "group_mask": torch.stack(group_masks),
                "grouped_bbox": torch.stack(grouped_boxes),
                "grouped_labels": torch.stack(grouped_labels),
                "grouped_mask": torch.stack(grouped_masks),
            }
        )
        return flat_enc

    def post_process_hierarchy(
        self,
        hierarchy: CoarseToFineHierarchy,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
    ) -> (
        LayoutGenerationOutput
        | dict[
            str,
            Float[torch.Tensor, "..."]
            | Int[torch.Tensor, "..."]
            | Bool[torch.Tensor, "..."]
            | dict[int, str]
            | None,
        ]
    ):
        """Convert decoded hierarchy tensors to the shared output schema."""
        output = flatten_hierarchy(
            hierarchy,
            id2label=dict(self.id2label),
            max_num_elements=self.max_num_elements,
        )
        if not return_intermediates:
            output.intermediates = None
        if normalize_output_type(output_type) is OutputType.dict:
            return dict(output)
        return output

__init__

__init__(
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> None

Initialize label maps and discretization settings.

Parameters:

Name Type Description Default
dataset DatasetName | str

Canonical layout dataset.

rico25
x_grid int

Number of x/width bins.

128
y_grid int

Number of y/height bins.

128
max_num_elements int

Padded flat sequence length.

20
id2label dict[int, str] | None

Optional public label map.

None

Examples:

>>> CoarseToFineProcessor(dataset="publaynet").id2label[0]
'text'
Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def __init__(
    self,
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> None:
    """Initialize label maps and discretization settings.

    Args:
        dataset: Canonical layout dataset.
        x_grid: Number of x/width bins.
        y_grid: Number of y/height bins.
        max_num_elements: Padded flat sequence length.
        id2label: Optional public label map.

    Examples:
        >>> CoarseToFineProcessor(dataset="publaynet").id2label[0]
        'text'
    """
    normalized_dataset = normalize_dataset_name(dataset)
    self.dataset = str(normalized_dataset)
    self.x_grid = x_grid
    self.y_grid = y_grid
    self.max_num_elements = max_num_elements
    self.id2label = id2label or id2label_for_dataset(normalized_dataset)
    self.label2id = label2id_for_dataset(normalized_dataset)

from_config classmethod

from_config(
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> "CoarseToFineProcessor"

Construct a processor without external files.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@classmethod
def from_config(
    cls,
    dataset: DatasetName | str = DatasetName.rico25,
    *,
    x_grid: int = 128,
    y_grid: int = 128,
    max_num_elements: int = 20,
    id2label: dict[int, str] | None = None,
) -> "CoarseToFineProcessor":
    """Construct a processor without external files."""
    return cls(
        dataset=dataset,
        x_grid=x_grid,
        y_grid=y_grid,
        max_num_elements=max_num_elements,
        id2label=id2label,
    )

to_dict

to_dict() -> dict[str, str | int | dict[str, str]]

Serialize processor state to a JSON-compatible dictionary.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
81
82
83
84
85
86
87
88
89
90
def to_dict(self) -> dict[str, str | int | dict[str, str]]:
    """Serialize processor state to a JSON-compatible dictionary."""
    return {
        "processor_class": self.processor_class,
        "dataset": self.dataset,
        "x_grid": self.x_grid,
        "y_grid": self.y_grid,
        "max_num_elements": self.max_num_elements,
        "id2label": {str(key): value for key, value in self.id2label.items()},
    }

save_pretrained

save_pretrained(
    save_directory: str | PathLike[str],
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> list[str]

Save processor metadata next to a converted checkpoint.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def save_pretrained(
    self,
    save_directory: str | PathLike[str],
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> list[str]:
    """Save processor metadata next to a converted checkpoint."""
    _ = (push_to_hub, kwargs)
    path = Path(save_directory)
    path.mkdir(parents=True, exist_ok=True)
    out_file = path / "preprocessor_config.json"
    out_file.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n")
    return [str(out_file)]

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    cache_dir: str | PathLike[str] | None = None,
    force_download: bool = False,
    local_files_only: bool = False,
    token: str | bool | None = None,
    revision: str = "main",
    **kwargs: str | int | float | bool | None,
) -> "CoarseToFineProcessor"

Load processor metadata from a local save_pretrained directory.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | PathLike[str],
    cache_dir: str | PathLike[str] | None = None,
    force_download: bool = False,
    local_files_only: bool = False,
    token: str | bool | None = None,
    revision: str = "main",
    **kwargs: str | int | float | bool | None,
) -> "CoarseToFineProcessor":
    """Load processor metadata from a local ``save_pretrained`` directory."""
    _ = (cache_dir, force_download, local_files_only, token, revision, kwargs)
    path = Path(pretrained_model_name_or_path) / "preprocessor_config.json"
    data = json.loads(path.read_text())
    return cls(
        dataset=str(data["dataset"]),
        x_grid=int(data["x_grid"]),
        y_grid=int(data["y_grid"]),
        max_num_elements=int(data["max_num_elements"]),
        id2label={int(key): str(value) for key, value in data["id2label"].items()},
    )

__call__

__call__(
    labels: list[list[int | str]]
    | Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | None = None,
    bbox: list[list[list[float]]]
    | Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding

Build discrete flat tensors from public layout inputs.

Parameters:

Name Type Description Default
labels list[list[int | str]] | Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | None

Public zero-based labels or label strings.

None
bbox list[list[list[float]]] | Float[Tensor, 'batch elements 4'] | Float[ndarray, 'batch elements 4'] | None

Public normalized boxes.

None
mask Bool[Tensor, 'batch elements'] | Bool[ndarray, 'batch elements'] | None

Optional valid-element mask.

None
box_format BoxFormat | str

Input box format.

xywh
normalized bool

Must be true; pixel boxes are dataset-loader work.

True
return_tensors Literal['pt']

Only "pt" is supported.

'pt'

Returns:

Type Description
BatchEncoding

BatchEncoding with internal labels, discrete boxes, and masks.

Raises:

Type Description
ValueError

If labels or boxes are missing.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def __call__(
    self,
    labels: list[list[int | str]]
    | Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | None = None,
    bbox: list[list[list[float]]]
    | Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding:
    """Build discrete flat tensors from public layout inputs.

    Args:
        labels: Public zero-based labels or label strings.
        bbox: Public normalized boxes.
        mask: Optional valid-element mask.
        box_format: Input box format.
        normalized: Must be true; pixel boxes are dataset-loader work.
        return_tensors: Only ``"pt"`` is supported.

    Returns:
        BatchEncoding with internal labels, discrete boxes, and masks.

    Raises:
        ValueError: If labels or boxes are missing.
    """
    if return_tensors != "pt":
        raise ValueError("Only return_tensors='pt' is supported")

    if not normalized:
        raise ValueError("CoarseToFineProcessor expects normalized boxes")

    if labels is None or bbox is None:
        raise ValueError("labels and bbox are required for processor encoding")

    label_tensor = self._coerce_labels(labels)
    bbox_tensor = torch.as_tensor(bbox, dtype=torch.float32)
    encoded_mask = (
        cast(torch.BoolTensor, torch.ones(label_tensor.shape, dtype=torch.bool))
        if mask is None
        else cast(torch.BoolTensor, torch.as_tensor(mask, dtype=torch.bool))
    )
    ltwh = public_to_ltwh(bbox_tensor, box_format=box_format)
    discrete = discretize_ltwh(ltwh, num_x_grid=self.x_grid, num_y_grid=self.y_grid)
    vendor_labels = self._labels_to_vendor(label_tensor)
    return BatchEncoding(
        {"labels": vendor_labels, "bbox": discrete, "mask": encoded_mask}
    )

build_hierarchy_batch

build_hierarchy_batch(
    labels: Int[Tensor, "batch elements"],
    bbox: Float[Tensor, "batch elements 4"],
    mask: Bool[Tensor, "batch elements"],
) -> BatchEncoding

Build padded hierarchy tensors for training/reference batches.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
def build_hierarchy_batch(
    self,
    labels: Int[torch.Tensor, "batch elements"],
    bbox: Float[torch.Tensor, "batch elements 4"],
    mask: Bool[torch.Tensor, "batch elements"],
) -> BatchEncoding:
    """Build padded hierarchy tensors for training/reference batches."""
    batch = labels.size(0)
    flat_enc = self(labels=labels, bbox=bbox, mask=mask, return_tensors="pt")
    encodings = []
    for idx in range(batch):
        valid = mask[idx].bool()
        encodings.append(
            build_cut_hierarchy(
                public_to_ltwh(bbox[idx, valid]),
                self._labels_to_vendor(labels[idx, valid]),
                num_labels=len(self.id2label),
                discrete_x_grid=self.x_grid,
                discrete_y_grid=self.y_grid,
            )
        )
    max_groups = max(enc.group_bounding_box.size(0) for enc in encodings) + 2
    max_decode_groups = max_groups - 2
    max_group_elems = (
        max(max(group.size(0) for group in enc.grouped_labels) for enc in encodings)
        + 2
    )
    group_boxes = []
    group_labels = []
    group_masks = []
    grouped_boxes = []
    grouped_labels = []
    grouped_masks = []
    for enc in encodings:
        group_count = enc.group_bounding_box.size(0)
        sos_group_box = torch.zeros((1, 4), dtype=torch.long)
        eos_group_box = torch.zeros((1, 4), dtype=torch.long)
        padded_group_box = torch.cat(
            (sos_group_box, enc.group_bounding_box.long(), eos_group_box), dim=0
        )
        padded_group_box = F.pad(
            padded_group_box, (0, 0, 0, max_groups - padded_group_box.size(0))
        )
        hist = torch.zeros(
            (group_count + 2, len(self.id2label) + 2), dtype=torch.float32
        )
        hist[0, 0] = 1.0
        hist[1 : group_count + 1, 1:-1] = enc.label_in_one_group.float()
        hist[group_count + 1, -1] = 1.0
        hist = F.pad(hist, (0, 0, 0, max_groups - hist.size(0)))
        group_mask = torch.arange(max_groups) < group_count + 2
        per_group_boxes = []
        per_group_labels = []
        per_group_masks = []
        for group_idx in range(group_count):
            box = enc.grouped_bbox[group_idx].long()
            label = enc.grouped_labels[group_idx].long()
            label = torch.cat(
                (
                    torch.tensor([len(self.id2label) + 1]),
                    label,
                    torch.tensor([len(self.id2label) + 2]),
                )
            )
            box = torch.cat(
                (
                    torch.zeros((1, 4), dtype=torch.long),
                    box,
                    torch.zeros((1, 4), dtype=torch.long),
                )
            )
            valid_count = min(label.size(0), max_group_elems)
            per_group_boxes.append(
                F.pad(
                    box[:max_group_elems], (0, 0, 0, max_group_elems - valid_count)
                )
            )
            per_group_labels.append(
                F.pad(label[:max_group_elems], (0, max_group_elems - valid_count))
            )
            per_group_masks.append(torch.arange(max_group_elems) < valid_count)
        while len(per_group_boxes) < max_decode_groups:
            per_group_boxes.append(
                torch.zeros((max_group_elems, 4), dtype=torch.long)
            )
            per_group_labels.append(
                torch.zeros((max_group_elems,), dtype=torch.long)
            )
            per_group_masks.append(
                torch.zeros((max_group_elems,), dtype=torch.bool)
            )
        group_boxes.append(padded_group_box)
        group_labels.append(hist)
        group_masks.append(group_mask)
        grouped_boxes.append(torch.stack(per_group_boxes[:max_decode_groups]))
        grouped_labels.append(torch.stack(per_group_labels[:max_decode_groups]))
        grouped_masks.append(torch.stack(per_group_masks[:max_decode_groups]))
    flat_enc.update(
        {
            "group_bounding_box": torch.stack(group_boxes),
            "label_in_one_group": torch.stack(group_labels),
            "group_mask": torch.stack(group_masks),
            "grouped_bbox": torch.stack(grouped_boxes),
            "grouped_labels": torch.stack(grouped_labels),
            "grouped_mask": torch.stack(grouped_masks),
        }
    )
    return flat_enc

post_process_hierarchy

post_process_hierarchy(
    hierarchy: CoarseToFineHierarchy,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
) -> (
    LayoutGenerationOutput
    | dict[
        str,
        Float[torch.Tensor, "..."]
        | Int[torch.Tensor, "..."]
        | Bool[torch.Tensor, "..."]
        | dict[int, str]
        | None,
    ]
)

Convert decoded hierarchy tensors to the shared output schema.

Source code in models/coarse-to-fine/src/coarse_to_fine/processing_coarse_to_fine.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
def post_process_hierarchy(
    self,
    hierarchy: CoarseToFineHierarchy,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
) -> (
    LayoutGenerationOutput
    | dict[
        str,
        Float[torch.Tensor, "..."]
        | Int[torch.Tensor, "..."]
        | Bool[torch.Tensor, "..."]
        | dict[int, str]
        | None,
    ]
):
    """Convert decoded hierarchy tensors to the shared output schema."""
    output = flatten_hierarchy(
        hierarchy,
        id2label=dict(self.id2label),
        max_num_elements=self.max_num_elements,
    )
    if not return_intermediates:
        output.intermediates = None
    if normalize_output_type(output_type) is OutputType.dict:
        return dict(output)
    return output

types

Closed public option sets for Coarse-to-Fine.

OutputType

Bases: StrEnum

Supported output containers.

Source code in models/coarse-to-fine/src/coarse_to_fine/types.py
10
11
12
13
14
class OutputType(StrEnum):
    """Supported output containers."""

    dataclass = auto()
    dict = auto()

normalize_output_type

normalize_output_type(
    output_type: OutputType | str,
) -> OutputType

Normalize a public output type value.

Parameters:

Name Type Description Default
output_type OutputType | str

Enum value or string.

required

Returns:

Type Description
OutputType

Normalized output type.

Raises:

Type Description
ValueError

If the output type is unknown.

Examples:

>>> str(normalize_output_type("dict"))
'dict'
Source code in models/coarse-to-fine/src/coarse_to_fine/types.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def normalize_output_type(output_type: OutputType | str) -> OutputType:
    """Normalize a public output type value.

    Args:
        output_type: Enum value or string.

    Returns:
        Normalized output type.

    Raises:
        ValueError: If the output type is unknown.

    Examples:
        >>> str(normalize_output_type("dict"))
        'dict'
    """
    return normalize_enum_value(
        output_type,
        OutputType,
        option_name="output_type",
    )