Skip to content

Coarse to fine

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

ConditionType

Bases: StrEnum

Canonical condition names used by layout generation interfaces.

Source code in lib/laygen/src/laygen/common/conditions.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
class ConditionType(StrEnum):
    """Canonical condition names used by layout generation interfaces."""

    unconditional = auto()
    label = auto()
    label_size = auto()
    completion = auto()
    refinement = auto()
    text = auto()
    content_image = auto()
    relation = auto()
    hierarchical = auto()
    retrieval = auto()

LayoutGenerationOutput dataclass

Bases: ModelOutput

Canonical layout-generation output for Transformers-style APIs.

Attributes:

Name Type Description
bbox Float[ndarray, 'batch elements 4'] | Float[Tensor, 'batch elements 4']

Normalized center xywh boxes with shape (batch, elements, 4).

labels Int[ndarray, 'batch elements'] | Int[Tensor, 'batch elements']

Dataset-local integer labels with shape (batch, elements).

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

Boolean valid-element mask with shape (batch, elements).

id2label dict[int, str]

Mapping from integer label ids to display names.

sequences object | None

Optional raw token sequences.

scores object | None

Optional per-token or per-element scores.

trajectory object | None

Optional sampling trajectory.

intermediates object | None

Optional model-specific debug or auxiliary data.

Examples:

>>> import numpy as np
>>> output = LayoutGenerationOutput(
...     bbox=np.zeros((1, 1, 4), dtype=np.float32),
...     labels=np.zeros((1, 1), dtype=np.int64),
...     mask=np.ones((1, 1), dtype=bool),
...     id2label={0: "text"},
... )
>>> output["bbox"].shape
(1, 1, 4)
Source code in lib/laygen/src/laygen/modeling_outputs.py
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@dataclass
class LayoutGenerationOutput(ModelOutput):
    """Canonical layout-generation output for Transformers-style APIs.

    Attributes:
        bbox: Normalized center ``xywh`` boxes with shape
            ``(batch, elements, 4)``.
        labels: Dataset-local integer labels with shape ``(batch, elements)``.
        mask: Boolean valid-element mask with shape ``(batch, elements)``.
        id2label: Mapping from integer label ids to display names.
        sequences: Optional raw token sequences.
        scores: Optional per-token or per-element scores.
        trajectory: Optional sampling trajectory.
        intermediates: Optional model-specific debug or auxiliary data.

    Examples:
        >>> import numpy as np
        >>> output = LayoutGenerationOutput(
        ...     bbox=np.zeros((1, 1, 4), dtype=np.float32),
        ...     labels=np.zeros((1, 1), dtype=np.int64),
        ...     mask=np.ones((1, 1), dtype=bool),
        ...     id2label={0: "text"},
        ... )
        >>> output["bbox"].shape
        (1, 1, 4)
    """

    bbox: (
        Float[np.ndarray, "batch elements 4"] | Float[torch.Tensor, "batch elements 4"]
    )
    labels: Int[np.ndarray, "batch elements"] | Int[torch.Tensor, "batch elements"] = (
        cast(
            'Int[np.ndarray, "batch elements"] | Int[torch.Tensor, "batch elements"]',
            None,
        )
    )
    mask: Bool[np.ndarray, "batch elements"] | Bool[torch.Tensor, "batch elements"] = (
        cast(
            'Bool[np.ndarray, "batch elements"] | Bool[torch.Tensor, "batch elements"]',
            None,
        )
    )
    id2label: dict[int, str] = cast(dict[int, str], None)
    sequences: object | None = None
    scores: object | None = None
    trajectory: object | None = None
    intermediates: object | None = None

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
455
456
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
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"]:
        sample_device = generator.device if generator is not None else device
        return cast(
            torch.FloatTensor,
            torch.randn(
                (1, batch_size, self.config.d_z),
                generator=generator,
                device=sample_device,
            ).to(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
494
495
496
497
498
499
500
501
502
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
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
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
 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
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"
            )

        if generator is None and seed is not None:
            generator = torch.Generator(device=self.model.device).manual_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
48
49
50
51
52
53
54
55
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
66
67
68
69
70
71
72
73
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
83
84
85
86
87
88
89
90
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
 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
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"
        )

    if generator is None and seed is not None:
        generator = torch.Generator(device=self.model.device).manual_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
 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
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
84
85
86
87
88
89
90
91
92
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
 99
100
101
102
103
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
105
106
107
108
109
110
111
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
113
114
115
116
117
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
119
120
121
122
123
124
125
126
127
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
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
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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
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
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
170
171
172
173
174
175
176
177
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
185
186
187
188
189
190
191
192
193
194
195
196
197
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
455
456
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
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"]:
        sample_device = generator.device if generator is not None else device
        return cast(
            torch.FloatTensor,
            torch.randn(
                (1, batch_size, self.config.d_z),
                generator=generator,
                device=sample_device,
            ).to(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
494
495
496
497
498
499
500
501
502
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
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
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
15
16
17
18
19
20
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
23
24
25
26
27
28
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
31
32
33
34
35
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
38
39
40
41
42
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
45
46
47
48
49
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
52
53
54
55
56
57
58
59
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
62
63
64
65
66
67
68
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
71
72
73
74
75
76
77
78
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
 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
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"
            )

        if generator is None and seed is not None:
            generator = torch.Generator(device=self.model.device).manual_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
48
49
50
51
52
53
54
55
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
66
67
68
69
70
71
72
73
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
83
84
85
86
87
88
89
90
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
 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
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"
        )

    if generator is None and seed is not None:
        generator = torch.Generator(device=self.model.device).manual_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",
    )