Skip to content

Layoutformerpp

Transformers-style LayoutFormer++ 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

LayoutFormerPPConfig

Bases: PretrainedConfig

Stores model, tokenizer, and task defaults for converted checkpoints.

Source code in models/layoutformerpp/src/layoutformerpp/configuration_layoutformerpp.py
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
class LayoutFormerPPConfig(PretrainedConfig):
    """Stores model, tokenizer, and task defaults for converted checkpoints."""

    model_type = "layoutformerpp"

    def __init__(
        self,
        vocab_size: int = 0,
        max_position_embeddings: int | None = None,
        d_model: int = 512,
        encoder_layers: int = 8,
        decoder_layers: int | None = None,
        encoder_attention_heads: int = 8,
        decoder_attention_heads: int | None = None,
        dropout: float = 0.1,
        dim_feedforward: int | None = None,
        share_embedding: bool = True,
        dataset: DatasetName | str = DatasetName.rico25,
        task: LayoutFormerPPTask | ConditionType | str = LayoutFormerPPTask.gen_t,
        max_num_elements: int = 20,
        bbox_format: BoxFormat | str = BoxFormat.ltwh,
        default_box_format: BoxFormat | str = BoxFormat.xywh,
        discrete_x_grid: int = 128,
        discrete_y_grid: int = 128,
        add_sep_token: bool = True,
        sort_by_dict: bool = True,
        add_task_embedding: bool = False,
        add_task_prompt_token_in_model: bool = False,
        num_task_prompt_token: int = 1,
        task_id: int | None = None,
        decode_max_length: int | None = None,
        eval_seed: int | None = None,
        gen_t_add_unk_token: bool = False,
        gen_ts_add_unk_token: bool = False,
        gen_r_add_unk_token: bool = False,
        gen_r_compact: bool = False,
        bos_token_id: int = 0,
        eos_token_id: int = 1,
        pad_token_id: int = 2,
        is_encoder_decoder: bool = True,
        condition_type: ConditionType | str | None = None,
        model_type: str | None = None,
        transformers_version: str | None = None,
        architectures: list[str] | None = None,
        output_hidden_states: bool | None = False,
        output_attentions: bool | None = False,
        return_dict: bool | None = True,
        chunk_size_feed_forward: int = 0,
        problem_type: Literal[
            "regression", "single_label_classification", "multi_label_classification"
        ]
        | None = None,
        id2label: dict[int | str, str] | None = None,
        label2id: dict[str, int] | None = None,
        torch_dtype: str | None = None,
        dtype: str | None = None,
        tie_word_embeddings: bool = True,
        task_specific_params: dict[
            str, str | int | float | bool | list[str | int | float | bool]
        ]
        | None = None,
        name_or_path: str = "",
        _name_or_path: str | None = None,
        _commit_hash: str | None = None,
        attn_implementation: str | None = None,
        **kwargs: str | int | float | bool | None,
    ) -> None:
        """Initialize architecture and task-specific generation defaults."""
        _ = (condition_type, model_type, transformers_version)
        normalized_dataset = normalize_layoutformerpp_dataset(dataset)
        normalized_task = normalize_layoutformerpp_task(task)
        condition_type = TASK_TO_CONDITION[normalized_task]
        self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
        self.task = str(normalized_task)
        self.condition_type = str(condition_type)
        defaults = TASK_DEFAULTS.get((normalized_dataset, condition_type), {})
        if max_position_embeddings is None:
            max_position_embeddings = int(defaults.get("max_position_embeddings", 150))
        if decode_max_length is None:
            decode_max_length = int(defaults.get("decode_max_length", 120))
        if eval_seed is None:
            eval_seed = int(defaults.get("eval_seed", 100))

        normalized_id2label = (
            {int(key): str(value) for key, value in id2label.items()}
            if id2label is not None
            else None
        )

        self.vocab_size = vocab_size
        self.max_position_embeddings = max_position_embeddings
        self.d_model = d_model
        self.encoder_layers = encoder_layers
        self.decoder_layers = (
            decoder_layers if decoder_layers is not None else encoder_layers
        )
        self.encoder_attention_heads = encoder_attention_heads
        self.decoder_attention_heads = (
            decoder_attention_heads
            if decoder_attention_heads is not None
            else encoder_attention_heads
        )
        self.dropout = dropout
        self.dim_feedforward = (
            dim_feedforward if dim_feedforward is not None else d_model * 4
        )
        self.share_embedding = share_embedding
        self.max_num_elements = max_num_elements
        self.bbox_format = str(normalize_box_format(bbox_format))
        self.default_box_format = str(normalize_box_format(default_box_format))

        self.discrete_x_grid = discrete_x_grid
        self.discrete_y_grid = discrete_y_grid
        self.add_sep_token = add_sep_token
        self.sort_by_dict = sort_by_dict
        self.add_task_embedding = add_task_embedding
        self.add_task_prompt_token_in_model = add_task_prompt_token_in_model
        self.num_task_prompt_token = num_task_prompt_token
        self.task_id = task_id

        self.decode_max_length = decode_max_length
        self.eval_seed = eval_seed
        self.gen_t_add_unk_token = gen_t_add_unk_token
        self.gen_ts_add_unk_token = gen_ts_add_unk_token
        self.gen_r_add_unk_token = gen_r_add_unk_token
        self.gen_r_compact = gen_r_compact

        super().__init__(
            transformers_version=transformers_version,
            architectures=architectures,
            output_hidden_states=output_hidden_states,
            return_dict=return_dict,
            chunk_size_feed_forward=chunk_size_feed_forward,
            problem_type=problem_type,
            is_encoder_decoder=is_encoder_decoder,
            id2label=normalized_id2label,
            label2id=label2id,
            dtype=torch_dtype or dtype,
        )
        # Transformers v5 keeps model-specific token/vocabulary fields on the
        # subclass; only the common fields above belong in the base call.
        self.vocab_size = vocab_size
        self.bos_token_id = bos_token_id
        self.eos_token_id = eos_token_id
        self.pad_token_id = pad_token_id
        self.tie_word_embeddings = tie_word_embeddings
        if output_attentions is not None:
            self.output_attentions = output_attentions
        if task_specific_params is not None:
            self.task_specific_params = task_specific_params
        self.name_or_path = name_or_path if _name_or_path is None else _name_or_path
        self._commit_hash = _commit_hash
        self._attn_implementation = attn_implementation
        # Transformers v5 passes legacy and model-specific fields through the
        # tolerant config-loading path; preserve them as ordinary attributes.
        for key, value in kwargs.items():
            setattr(self, key, value)

__init__

__init__(
    vocab_size: int = 0,
    max_position_embeddings: int | None = None,
    d_model: int = 512,
    encoder_layers: int = 8,
    decoder_layers: int | None = None,
    encoder_attention_heads: int = 8,
    decoder_attention_heads: int | None = None,
    dropout: float = 0.1,
    dim_feedforward: int | None = None,
    share_embedding: bool = True,
    dataset: DatasetName | str = DatasetName.rico25,
    task: LayoutFormerPPTask
    | ConditionType
    | str = LayoutFormerPPTask.gen_t,
    max_num_elements: int = 20,
    bbox_format: BoxFormat | str = BoxFormat.ltwh,
    default_box_format: BoxFormat | str = BoxFormat.xywh,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    add_sep_token: bool = True,
    sort_by_dict: bool = True,
    add_task_embedding: bool = False,
    add_task_prompt_token_in_model: bool = False,
    num_task_prompt_token: int = 1,
    task_id: int | None = None,
    decode_max_length: int | None = None,
    eval_seed: int | None = None,
    gen_t_add_unk_token: bool = False,
    gen_ts_add_unk_token: bool = False,
    gen_r_add_unk_token: bool = False,
    gen_r_compact: bool = False,
    bos_token_id: int = 0,
    eos_token_id: int = 1,
    pad_token_id: int = 2,
    is_encoder_decoder: bool = True,
    condition_type: ConditionType | str | None = None,
    model_type: str | None = None,
    transformers_version: str | None = None,
    architectures: list[str] | None = None,
    output_hidden_states: bool | None = False,
    output_attentions: bool | None = False,
    return_dict: bool | None = True,
    chunk_size_feed_forward: int = 0,
    problem_type: Literal[
        "regression",
        "single_label_classification",
        "multi_label_classification",
    ]
    | None = None,
    id2label: dict[int | str, str] | None = None,
    label2id: dict[str, int] | None = None,
    torch_dtype: str | None = None,
    dtype: str | None = None,
    tie_word_embeddings: bool = True,
    task_specific_params: dict[
        str,
        str
        | int
        | float
        | bool
        | list[str | int | float | bool],
    ]
    | None = None,
    name_or_path: str = "",
    _name_or_path: str | None = None,
    _commit_hash: str | None = None,
    attn_implementation: str | None = None,
    **kwargs: str | int | float | bool | None,
) -> None

Initialize architecture and task-specific generation defaults.

Source code in models/layoutformerpp/src/layoutformerpp/configuration_layoutformerpp.py
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
def __init__(
    self,
    vocab_size: int = 0,
    max_position_embeddings: int | None = None,
    d_model: int = 512,
    encoder_layers: int = 8,
    decoder_layers: int | None = None,
    encoder_attention_heads: int = 8,
    decoder_attention_heads: int | None = None,
    dropout: float = 0.1,
    dim_feedforward: int | None = None,
    share_embedding: bool = True,
    dataset: DatasetName | str = DatasetName.rico25,
    task: LayoutFormerPPTask | ConditionType | str = LayoutFormerPPTask.gen_t,
    max_num_elements: int = 20,
    bbox_format: BoxFormat | str = BoxFormat.ltwh,
    default_box_format: BoxFormat | str = BoxFormat.xywh,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    add_sep_token: bool = True,
    sort_by_dict: bool = True,
    add_task_embedding: bool = False,
    add_task_prompt_token_in_model: bool = False,
    num_task_prompt_token: int = 1,
    task_id: int | None = None,
    decode_max_length: int | None = None,
    eval_seed: int | None = None,
    gen_t_add_unk_token: bool = False,
    gen_ts_add_unk_token: bool = False,
    gen_r_add_unk_token: bool = False,
    gen_r_compact: bool = False,
    bos_token_id: int = 0,
    eos_token_id: int = 1,
    pad_token_id: int = 2,
    is_encoder_decoder: bool = True,
    condition_type: ConditionType | str | None = None,
    model_type: str | None = None,
    transformers_version: str | None = None,
    architectures: list[str] | None = None,
    output_hidden_states: bool | None = False,
    output_attentions: bool | None = False,
    return_dict: bool | None = True,
    chunk_size_feed_forward: int = 0,
    problem_type: Literal[
        "regression", "single_label_classification", "multi_label_classification"
    ]
    | None = None,
    id2label: dict[int | str, str] | None = None,
    label2id: dict[str, int] | None = None,
    torch_dtype: str | None = None,
    dtype: str | None = None,
    tie_word_embeddings: bool = True,
    task_specific_params: dict[
        str, str | int | float | bool | list[str | int | float | bool]
    ]
    | None = None,
    name_or_path: str = "",
    _name_or_path: str | None = None,
    _commit_hash: str | None = None,
    attn_implementation: str | None = None,
    **kwargs: str | int | float | bool | None,
) -> None:
    """Initialize architecture and task-specific generation defaults."""
    _ = (condition_type, model_type, transformers_version)
    normalized_dataset = normalize_layoutformerpp_dataset(dataset)
    normalized_task = normalize_layoutformerpp_task(task)
    condition_type = TASK_TO_CONDITION[normalized_task]
    self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
    self.task = str(normalized_task)
    self.condition_type = str(condition_type)
    defaults = TASK_DEFAULTS.get((normalized_dataset, condition_type), {})
    if max_position_embeddings is None:
        max_position_embeddings = int(defaults.get("max_position_embeddings", 150))
    if decode_max_length is None:
        decode_max_length = int(defaults.get("decode_max_length", 120))
    if eval_seed is None:
        eval_seed = int(defaults.get("eval_seed", 100))

    normalized_id2label = (
        {int(key): str(value) for key, value in id2label.items()}
        if id2label is not None
        else None
    )

    self.vocab_size = vocab_size
    self.max_position_embeddings = max_position_embeddings
    self.d_model = d_model
    self.encoder_layers = encoder_layers
    self.decoder_layers = (
        decoder_layers if decoder_layers is not None else encoder_layers
    )
    self.encoder_attention_heads = encoder_attention_heads
    self.decoder_attention_heads = (
        decoder_attention_heads
        if decoder_attention_heads is not None
        else encoder_attention_heads
    )
    self.dropout = dropout
    self.dim_feedforward = (
        dim_feedforward if dim_feedforward is not None else d_model * 4
    )
    self.share_embedding = share_embedding
    self.max_num_elements = max_num_elements
    self.bbox_format = str(normalize_box_format(bbox_format))
    self.default_box_format = str(normalize_box_format(default_box_format))

    self.discrete_x_grid = discrete_x_grid
    self.discrete_y_grid = discrete_y_grid
    self.add_sep_token = add_sep_token
    self.sort_by_dict = sort_by_dict
    self.add_task_embedding = add_task_embedding
    self.add_task_prompt_token_in_model = add_task_prompt_token_in_model
    self.num_task_prompt_token = num_task_prompt_token
    self.task_id = task_id

    self.decode_max_length = decode_max_length
    self.eval_seed = eval_seed
    self.gen_t_add_unk_token = gen_t_add_unk_token
    self.gen_ts_add_unk_token = gen_ts_add_unk_token
    self.gen_r_add_unk_token = gen_r_add_unk_token
    self.gen_r_compact = gen_r_compact

    super().__init__(
        transformers_version=transformers_version,
        architectures=architectures,
        output_hidden_states=output_hidden_states,
        return_dict=return_dict,
        chunk_size_feed_forward=chunk_size_feed_forward,
        problem_type=problem_type,
        is_encoder_decoder=is_encoder_decoder,
        id2label=normalized_id2label,
        label2id=label2id,
        dtype=torch_dtype or dtype,
    )
    # Transformers v5 keeps model-specific token/vocabulary fields on the
    # subclass; only the common fields above belong in the base call.
    self.vocab_size = vocab_size
    self.bos_token_id = bos_token_id
    self.eos_token_id = eos_token_id
    self.pad_token_id = pad_token_id
    self.tie_word_embeddings = tie_word_embeddings
    if output_attentions is not None:
        self.output_attentions = output_attentions
    if task_specific_params is not None:
        self.task_specific_params = task_specific_params
    self.name_or_path = name_or_path if _name_or_path is None else _name_or_path
    self._commit_hash = _commit_hash
    self._attn_implementation = attn_implementation
    # Transformers v5 passes legacy and model-specific fields through the
    # tolerant config-loading path; preserve them as ordinary attributes.
    for key, value in kwargs.items():
        setattr(self, key, value)

LayoutFormerPPForConditionalGeneration

Bases: PreTrainedModel

Transformers PreTrainedModel with checkpoint-compatible module names.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.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
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
class LayoutFormerPPForConditionalGeneration(PreTrainedModel):
    """Transformers `PreTrainedModel` with checkpoint-compatible module names."""

    config_class = LayoutFormerPPConfig
    base_model_prefix = "layoutformerpp"
    main_input_name = "input_ids"
    _tied_weights_keys = {
        "dec_embedding.weight": "enc_embedding.weight",
        "out.weight": "dec_embedding.weight",
    }

    def __init__(self, config: LayoutFormerPPConfig) -> None:
        """Initialize checkpoint-compatible encoder/decoder modules."""
        super().__init__(config)
        self.d_model = config.d_model
        self.vocab_size = config.vocab_size
        self.bos_token_id = int(config.bos_token_id)
        self.pad_token_id = int(config.pad_token_id)
        self.eos_token_id = int(config.eos_token_id)

        self.enc_embedding = nn.Embedding(config.vocab_size, config.d_model)
        self.enc_pos_embedding = PositionalEncoding(
            config.d_model, config.dropout, max_len=config.max_position_embeddings
        )
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=config.d_model,
            nhead=config.encoder_attention_heads,
            dropout=config.dropout,
            dim_feedforward=config.dim_feedforward,
        )
        self.encoder = nn.TransformerEncoder(
            encoder_layer, num_layers=config.encoder_layers
        )

        self.dec_embedding = (
            self.enc_embedding
            if config.share_embedding
            else nn.Embedding(config.vocab_size, config.d_model)
        )
        self.dec_pos_embedding = PositionalEncoding(
            config.d_model, config.dropout, max_len=config.max_position_embeddings
        )
        decoder_layer = nn.TransformerDecoderLayer(
            d_model=config.d_model,
            nhead=config.decoder_attention_heads,
            dropout=config.dropout,
            dim_feedforward=config.dim_feedforward,
        )
        self.decoder = nn.TransformerDecoder(
            decoder_layer, num_layers=config.decoder_layers
        )
        self.out = nn.Linear(config.d_model, config.vocab_size, bias=False)
        self.out.weight = self.dec_embedding.weight

        self.task_embedding = None
        if config.add_task_embedding:
            self.task_embedding = nn.Embedding(6, config.d_model)

        self.task_prompt_embed = None
        if config.add_task_prompt_token_in_model:
            self.num_task_prompt_token = config.num_task_prompt_token
            self.task_prompt_embed = nn.Parameter(
                torch.empty(6, config.num_task_prompt_token, config.d_model)
            )
            nn.init.normal_(self.task_prompt_embed)
        self.tie_weights()
        self.all_tied_weights_keys = dict(self._tied_weights_keys)

    def encode(
        self,
        input_ids: Int[torch.Tensor, "batch tokens"],
        padding_mask: Bool[torch.Tensor, "batch tokens"],
        task_ids: Int[torch.Tensor, "batch"] | None = None,
    ) -> tuple[
        Float[torch.Tensor, "seq batch channels"],
        Bool[torch.Tensor, "batch seq"],
    ]:
        """Encode input token ids with optional task prompt embeddings."""
        if self.task_prompt_embed is not None:
            if task_ids is None:
                raise ValueError(
                    "task_ids are required when task prompt embeddings are enabled"
                )

            x = self.enc_embedding(input_ids)
            prompts = self.task_prompt_embed[task_ids]
            x = torch.cat([prompts, x], dim=1).permute(1, 0, 2)
            bsz = input_ids.size(0)
            prompt_mask = padding_mask.new_zeros(
                (bsz, self.num_task_prompt_token)
            ).bool()
            enc_padding_mask = torch.cat([prompt_mask, padding_mask], dim=1)
        else:
            x = self.enc_embedding(input_ids).permute(1, 0, 2)
            enc_padding_mask = padding_mask
        enc_hs = self.encoder(
            self.enc_pos_embedding(x), src_key_padding_mask=enc_padding_mask
        )
        if self.task_embedding is not None:
            if task_ids is None:
                raise ValueError(
                    "task_ids are required when task embeddings are enabled"
                )

            enc_hs = enc_hs + self.task_embedding(task_ids).unsqueeze(0)
        return enc_hs, enc_padding_mask

    def prepare_decoder_input_ids_from_labels(
        self, labels: Int[torch.Tensor, "batch tokens"]
    ) -> Int[torch.Tensor, "batch tokens"]:
        """Shift labels right and prepend BOS."""
        bos = labels.new_full((labels.size(0), 1), self.bos_token_id)
        return torch.cat([bos, labels[:, :-1]], dim=1)

    def forward(
        self,
        input_ids: Int[torch.Tensor, "batch tokens"],
        attention_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        labels: Int[torch.Tensor, "batch tokens"] | None = None,
        decoder_input_ids: Int[torch.Tensor, "batch tokens"] | None = None,
        task_ids: Int[torch.Tensor, "batch"] | None = None,
        return_dict: bool | None = None,
    ) -> Seq2SeqLMOutput | tuple[Float[torch.Tensor, "..."], ...]:
        """Run teacher-forced LayoutFormer++ decoding."""
        if attention_mask is None:
            attention_mask = input_ids.ne(self.pad_token_id)
        padding_mask = ~attention_mask.bool()
        if decoder_input_ids is None:
            if labels is None:
                raise ValueError("decoder_input_ids or labels must be provided")

            decoder_input_ids = self.prepare_decoder_input_ids_from_labels(labels)
        enc_hs, enc_padding_mask = self.encode(input_ids, padding_mask, task_ids)
        dec_input = self.dec_pos_embedding(
            self.dec_embedding(decoder_input_ids).permute(1, 0, 2)
        )
        tgt_mask = generate_square_subsequent_mask(dec_input.size(0), dec_input.device)
        y = self.decoder(
            tgt=dec_input,
            memory=enc_hs,
            tgt_mask=tgt_mask,
            memory_key_padding_mask=enc_padding_mask,
        )
        logits = self.out(y.permute(1, 0, 2))
        loss = None
        if labels is not None:
            targets = labels.clone()
            targets[targets == self.pad_token_id] = -100
            loss = F.cross_entropy(
                logits.reshape(-1, logits.size(-1)),
                targets.reshape(-1),
                ignore_index=-100,
            )
        if return_dict is False:
            return (logits,) if loss is None else (loss, logits)
        return Seq2SeqLMOutput(loss=cast(torch.FloatTensor | None, loss), logits=logits)

    @torch.no_grad()
    def _generate_sequences(
        self,
        input_ids: Int[torch.Tensor, "batch tokens"],
        attention_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        *,
        max_length: int | None = None,
        do_sample: bool = False,
        top_k: int = 10,
        temperature: float = 0.7,
        generation_constraint_fn: Callable[
            [int, int, Int[torch.Tensor, "tokens"]], tuple[list[int], int | None]
        ]
        | None = None,
        task_ids: Int[torch.Tensor, "batch"] | None = None,
        generator: torch.Generator | None = None,
    ) -> Int[torch.Tensor, "batch tokens"]:
        """Run the reference greedy/top-k autoregressive loop."""
        if attention_mask is None:
            attention_mask = input_ids.ne(self.pad_token_id)
        padding_mask = ~attention_mask.bool()
        max_length = max_length or self.config.decode_max_length
        enc_hs, enc_padding_mask = self.encode(input_ids, padding_mask, task_ids)
        bsz = input_ids.size(0)
        stop = input_ids.new_zeros(bsz, dtype=torch.bool)
        pred_ids = input_ids.new_full((bsz, 1), self.bos_token_id)
        outs: list[Int[torch.Tensor, "batch"]] = []
        for idx in range(max_length):
            dec_input = self.dec_pos_embedding(
                self.dec_embedding(pred_ids).permute(1, 0, 2)
            )
            tgt_mask = generate_square_subsequent_mask(idx + 1, input_ids.device)
            y = self.decoder(
                tgt=dec_input,
                memory=enc_hs,
                tgt_mask=tgt_mask,
                memory_key_padding_mask=enc_padding_mask,
            )
            logits = self.out(y.permute(1, 0, 2)[:, -1, :])
            if generation_constraint_fn is not None:
                current = (
                    torch.stack(outs, dim=1) if outs else input_ids.new_empty((bsz, 0))
                )
                for batch_idx in range(bsz):
                    allowed, _ = generation_constraint_fn(
                        batch_idx, idx, current[batch_idx]
                    )
                    mask = torch.ones(
                        logits.size(-1), dtype=torch.bool, device=logits.device
                    )
                    mask[allowed] = False
                    logits[batch_idx].masked_fill_(mask, -math.inf)
            if do_sample:
                probs = F.softmax(top_k_logits(logits / temperature, top_k), dim=-1)
                curr = torch.multinomial(
                    probs,
                    num_samples=1,
                    generator=generator,
                ).squeeze(-1)
            else:
                curr = torch.argmax(logits, dim=-1)
            eos = curr.eq(self.eos_token_id)
            curr[stop] = self.pad_token_id
            outs.append(curr)
            pred_ids = torch.cat([pred_ids, curr.unsqueeze(1)], dim=1)
            stop = torch.logical_or(stop, eos)
            if bool(torch.all(stop)):
                break
        return torch.stack(outs, dim=1)

__init__

__init__(config: LayoutFormerPPConfig) -> None

Initialize checkpoint-compatible encoder/decoder modules.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
 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
def __init__(self, config: LayoutFormerPPConfig) -> None:
    """Initialize checkpoint-compatible encoder/decoder modules."""
    super().__init__(config)
    self.d_model = config.d_model
    self.vocab_size = config.vocab_size
    self.bos_token_id = int(config.bos_token_id)
    self.pad_token_id = int(config.pad_token_id)
    self.eos_token_id = int(config.eos_token_id)

    self.enc_embedding = nn.Embedding(config.vocab_size, config.d_model)
    self.enc_pos_embedding = PositionalEncoding(
        config.d_model, config.dropout, max_len=config.max_position_embeddings
    )
    encoder_layer = nn.TransformerEncoderLayer(
        d_model=config.d_model,
        nhead=config.encoder_attention_heads,
        dropout=config.dropout,
        dim_feedforward=config.dim_feedforward,
    )
    self.encoder = nn.TransformerEncoder(
        encoder_layer, num_layers=config.encoder_layers
    )

    self.dec_embedding = (
        self.enc_embedding
        if config.share_embedding
        else nn.Embedding(config.vocab_size, config.d_model)
    )
    self.dec_pos_embedding = PositionalEncoding(
        config.d_model, config.dropout, max_len=config.max_position_embeddings
    )
    decoder_layer = nn.TransformerDecoderLayer(
        d_model=config.d_model,
        nhead=config.decoder_attention_heads,
        dropout=config.dropout,
        dim_feedforward=config.dim_feedforward,
    )
    self.decoder = nn.TransformerDecoder(
        decoder_layer, num_layers=config.decoder_layers
    )
    self.out = nn.Linear(config.d_model, config.vocab_size, bias=False)
    self.out.weight = self.dec_embedding.weight

    self.task_embedding = None
    if config.add_task_embedding:
        self.task_embedding = nn.Embedding(6, config.d_model)

    self.task_prompt_embed = None
    if config.add_task_prompt_token_in_model:
        self.num_task_prompt_token = config.num_task_prompt_token
        self.task_prompt_embed = nn.Parameter(
            torch.empty(6, config.num_task_prompt_token, config.d_model)
        )
        nn.init.normal_(self.task_prompt_embed)
    self.tie_weights()
    self.all_tied_weights_keys = dict(self._tied_weights_keys)

encode

encode(
    input_ids: Int[Tensor, "batch tokens"],
    padding_mask: Bool[Tensor, "batch tokens"],
    task_ids: Int[Tensor, "batch"] | None = None,
) -> tuple[
    Float[torch.Tensor, "seq batch channels"],
    Bool[torch.Tensor, "batch seq"],
]

Encode input token ids with optional task prompt embeddings.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
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
def encode(
    self,
    input_ids: Int[torch.Tensor, "batch tokens"],
    padding_mask: Bool[torch.Tensor, "batch tokens"],
    task_ids: Int[torch.Tensor, "batch"] | None = None,
) -> tuple[
    Float[torch.Tensor, "seq batch channels"],
    Bool[torch.Tensor, "batch seq"],
]:
    """Encode input token ids with optional task prompt embeddings."""
    if self.task_prompt_embed is not None:
        if task_ids is None:
            raise ValueError(
                "task_ids are required when task prompt embeddings are enabled"
            )

        x = self.enc_embedding(input_ids)
        prompts = self.task_prompt_embed[task_ids]
        x = torch.cat([prompts, x], dim=1).permute(1, 0, 2)
        bsz = input_ids.size(0)
        prompt_mask = padding_mask.new_zeros(
            (bsz, self.num_task_prompt_token)
        ).bool()
        enc_padding_mask = torch.cat([prompt_mask, padding_mask], dim=1)
    else:
        x = self.enc_embedding(input_ids).permute(1, 0, 2)
        enc_padding_mask = padding_mask
    enc_hs = self.encoder(
        self.enc_pos_embedding(x), src_key_padding_mask=enc_padding_mask
    )
    if self.task_embedding is not None:
        if task_ids is None:
            raise ValueError(
                "task_ids are required when task embeddings are enabled"
            )

        enc_hs = enc_hs + self.task_embedding(task_ids).unsqueeze(0)
    return enc_hs, enc_padding_mask

prepare_decoder_input_ids_from_labels

prepare_decoder_input_ids_from_labels(
    labels: Int[Tensor, "batch tokens"],
) -> Int[torch.Tensor, "batch tokens"]

Shift labels right and prepend BOS.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
162
163
164
165
166
167
def prepare_decoder_input_ids_from_labels(
    self, labels: Int[torch.Tensor, "batch tokens"]
) -> Int[torch.Tensor, "batch tokens"]:
    """Shift labels right and prepend BOS."""
    bos = labels.new_full((labels.size(0), 1), self.bos_token_id)
    return torch.cat([bos, labels[:, :-1]], dim=1)

forward

forward(
    input_ids: Int[Tensor, "batch tokens"],
    attention_mask: Bool[Tensor, "batch tokens"]
    | None = None,
    labels: Int[Tensor, "batch tokens"] | None = None,
    decoder_input_ids: Int[Tensor, "batch tokens"]
    | None = None,
    task_ids: Int[Tensor, "batch"] | None = None,
    return_dict: bool | None = None,
) -> (
    Seq2SeqLMOutput | tuple[Float[torch.Tensor, "..."], ...]
)

Run teacher-forced LayoutFormer++ decoding.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def forward(
    self,
    input_ids: Int[torch.Tensor, "batch tokens"],
    attention_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    labels: Int[torch.Tensor, "batch tokens"] | None = None,
    decoder_input_ids: Int[torch.Tensor, "batch tokens"] | None = None,
    task_ids: Int[torch.Tensor, "batch"] | None = None,
    return_dict: bool | None = None,
) -> Seq2SeqLMOutput | tuple[Float[torch.Tensor, "..."], ...]:
    """Run teacher-forced LayoutFormer++ decoding."""
    if attention_mask is None:
        attention_mask = input_ids.ne(self.pad_token_id)
    padding_mask = ~attention_mask.bool()
    if decoder_input_ids is None:
        if labels is None:
            raise ValueError("decoder_input_ids or labels must be provided")

        decoder_input_ids = self.prepare_decoder_input_ids_from_labels(labels)
    enc_hs, enc_padding_mask = self.encode(input_ids, padding_mask, task_ids)
    dec_input = self.dec_pos_embedding(
        self.dec_embedding(decoder_input_ids).permute(1, 0, 2)
    )
    tgt_mask = generate_square_subsequent_mask(dec_input.size(0), dec_input.device)
    y = self.decoder(
        tgt=dec_input,
        memory=enc_hs,
        tgt_mask=tgt_mask,
        memory_key_padding_mask=enc_padding_mask,
    )
    logits = self.out(y.permute(1, 0, 2))
    loss = None
    if labels is not None:
        targets = labels.clone()
        targets[targets == self.pad_token_id] = -100
        loss = F.cross_entropy(
            logits.reshape(-1, logits.size(-1)),
            targets.reshape(-1),
            ignore_index=-100,
        )
    if return_dict is False:
        return (logits,) if loss is None else (loss, logits)
    return Seq2SeqLMOutput(loss=cast(torch.FloatTensor | None, loss), logits=logits)

LayoutFormerPPPipeline

Bases: LayoutGenerationPipeline

Compose a LayoutFormer++ model and processor for layout generation.

Parameters:

Name Type Description Default
model LayoutFormerPPForConditionalGeneration

Converted LayoutFormer++ model.

required
processor LayoutFormerPPProcessor

Matching processor/tokenizer.

required
config LayoutFormerPPConfig | None

Optional root pipeline config. Defaults to model.config.

None

Examples:

>>> processor = LayoutFormerPPProcessor.from_config(dataset="rico", task="gen_t")
>>> config = LayoutFormerPPConfig(vocab_size=processor.tokenizer.vocab_size)
>>> pipe = LayoutFormerPPPipeline(
...     model=LayoutFormerPPForConditionalGeneration(config),
...     processor=processor,
... )
>>> pipe.config.model_type
'layoutformerpp'
Source code in models/layoutformerpp/src/layoutformerpp/pipeline_layoutformerpp.py
 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
class LayoutFormerPPPipeline(LayoutGenerationPipeline):
    """Compose a LayoutFormer++ model and processor for layout generation.

    Args:
        model: Converted LayoutFormer++ model.
        processor: Matching processor/tokenizer.
        config: Optional root pipeline config. Defaults to `model.config`.

    Examples:
        >>> processor = LayoutFormerPPProcessor.from_config(dataset="rico", task="gen_t")
        >>> config = LayoutFormerPPConfig(vocab_size=processor.tokenizer.vocab_size)
        >>> pipe = LayoutFormerPPPipeline(
        ...     model=LayoutFormerPPForConditionalGeneration(config),
        ...     processor=processor,
        ... )
        >>> pipe.config.model_type
        'layoutformerpp'
    """

    config_class: ClassVar[type[PretrainedConfig]] = LayoutFormerPPConfig
    component_specs: ClassVar[dict[str, PipelineComponentSpec]] = (
        model_processor_component_specs(
            model_loader=_load_model_component,
            processor_loader=_load_processor_component,
        )
    )

    config: LayoutFormerPPConfig
    model: LayoutFormerPPForConditionalGeneration
    processor: LayoutFormerPPProcessor

    def __init__(
        self,
        model: LayoutFormerPPForConditionalGeneration,
        processor: LayoutFormerPPProcessor,
        config: LayoutFormerPPConfig | None = None,
    ) -> None:
        """Initialize the pipeline with model and processor components."""
        super().__init__(config or model.config)
        self.config = config or model.config
        self.model = model
        self.processor = processor

    @classmethod
    def _from_pretrained_components(
        cls,
        *,
        config: PretrainedConfig,
        components: Mapping[str, PipelineComponent | None],
    ) -> LayoutFormerPPPipeline:
        """Build a pipeline from loaded root components."""
        return cls(
            config=cast(LayoutFormerPPConfig, config),
            model=cast(LayoutFormerPPForConditionalGeneration, components["model"]),
            processor=cast(LayoutFormerPPProcessor, components["processor"]),
        )

    @torch.no_grad()
    def __call__(
        self,
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: ConditionType | str = ConditionType.unconditional,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        bbox: LayoutFormerPPBBoxInput = None,
        mask: Bool[torch.Tensor, "batch elements"] | None = None,
        relations: list[list[tuple[int, int, int, int, int]]]
        | Int[torch.Tensor, "batch relations relation_attrs"]
        | 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,
        max_length: int | None = None,
        do_sample: bool | None = None,
        top_k: int = 10,
        temperature: float = 0.7,
    ) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:  # ty: ignore[invalid-method-override]
        """Generate layouts by encoding conditions, generating ids, and decoding.

        Args:
            batch_size: Number of layouts to generate when labels are omitted.
            seed: Convenience seed used only when `generator` is absent.
            generator: Optional PyTorch generator; takes precedence over `seed`.
            condition_type: Canonical condition type or supported alias.
            labels: Optional label conditions.
            bbox: Optional layout boxes for size/completion/refinement conditions.
            mask: Reserved public validity mask input.
            relations: Optional relation tuples for relation-conditioned checkpoints.
            num_elements: Reserved v1 interface argument.
            box_format: Input and output bounding-box format.
            normalized: Whether public boxes are normalized.
            canvas_size: Reserved v1 interface argument.
            num_inference_steps: Reserved v1 interface argument.
            output_type: Return `dataclass` or `dict`.
            return_intermediates: Reserved output detail flag.
            max_length: Optional token decode length override.
            do_sample: Optional sampling override.
            top_k: Top-k value used by the reference sampling loop.
            temperature: Sampling temperature.

        Returns:
            Layout generation output dataclass or dictionary.

        Raises:
            ValueError: If processor inputs are invalid.
        """
        _ = (num_elements, num_inference_steps, return_intermediates)
        encoded = self.processor(
            condition_type=condition_type,
            batch_size=batch_size,
            return_tensors="pt",
            labels=labels,
            bbox=bbox,
            mask=mask,
            relations=relations,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        model_device = next(self.model.parameters()).device
        input_ids = encoded["input_ids"].to(model_device)
        attention_mask = encoded["attention_mask"].to(model_device)
        condition = self.processor.normalize_condition_type(condition_type)
        generation_generator = self.prepare_generator(
            generator=generator,
            seed=seed,
            device=model_device,
        )
        default_do_sample = condition in {
            ConditionType.unconditional,
            ConditionType.completion,
        }
        sequences = self.model._generate_sequences(
            input_ids,
            attention_mask,
            max_length=max_length,
            do_sample=default_do_sample if do_sample is None else do_sample,
            top_k=top_k,
            temperature=temperature,
            generator=generation_generator,
        )
        return self.processor.post_process_layouts(
            sequences.cpu(),
            box_format=box_format,
            output_type=output_type,
        )

__init__

__init__(
    model: LayoutFormerPPForConditionalGeneration,
    processor: LayoutFormerPPProcessor,
    config: LayoutFormerPPConfig | None = None,
) -> None

Initialize the pipeline with model and processor components.

Source code in models/layoutformerpp/src/layoutformerpp/pipeline_layoutformerpp.py
104
105
106
107
108
109
110
111
112
113
114
def __init__(
    self,
    model: LayoutFormerPPForConditionalGeneration,
    processor: LayoutFormerPPProcessor,
    config: LayoutFormerPPConfig | None = None,
) -> None:
    """Initialize the pipeline with model and processor components."""
    super().__init__(config or model.config)
    self.config = config or model.config
    self.model = model
    self.processor = processor

__call__

__call__(
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: ConditionType
    | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[Tensor, "batch elements"] | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[Tensor, "batch relations relation_attrs"]
    | 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,
    max_length: int | None = None,
    do_sample: bool | None = None,
    top_k: int = 10,
    temperature: float = 0.7,
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict

Generate layouts by encoding conditions, generating ids, and decoding.

Parameters:

Name Type Description Default
batch_size int

Number of layouts to generate when labels are omitted.

1
seed int | None

Convenience seed used only when generator is absent.

None
generator Generator | None

Optional PyTorch generator; takes precedence over seed.

None
condition_type ConditionType | str

Canonical condition type or supported alias.

unconditional
labels list[list[int | str]] | Int[Tensor, 'batch elements'] | None

Optional label conditions.

None
bbox LayoutFormerPPBBoxInput

Optional layout boxes for size/completion/refinement conditions.

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

Reserved public validity mask input.

None
relations list[list[tuple[int, int, int, int, int]]] | Int[Tensor, 'batch relations relation_attrs'] | None

Optional relation tuples for relation-conditioned checkpoints.

None
num_elements int | list[int] | Int[Tensor, 'batch'] | None

Reserved v1 interface argument.

None
box_format BoxFormat | str

Input and output bounding-box format.

xywh
normalized bool

Whether public boxes are normalized.

True
canvas_size tuple[int, int] | None

Reserved v1 interface argument.

None
num_inference_steps int | None

Reserved v1 interface argument.

None
output_type OutputType | str

Return dataclass or dict.

dataclass
return_intermediates bool

Reserved output detail flag.

False
max_length int | None

Optional token decode length override.

None
do_sample bool | None

Optional sampling override.

None
top_k int

Top-k value used by the reference sampling loop.

10
temperature float

Sampling temperature.

0.7

Returns:

Type Description
LayoutGenerationOutput | LayoutFormerPPOutputDict

Layout generation output dataclass or dictionary.

Raises:

Type Description
ValueError

If processor inputs are invalid.

Source code in models/layoutformerpp/src/layoutformerpp/pipeline_layoutformerpp.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
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
@torch.no_grad()
def __call__(
    self,
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: ConditionType | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[torch.Tensor, "batch elements"] | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[torch.Tensor, "batch relations relation_attrs"]
    | 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,
    max_length: int | None = None,
    do_sample: bool | None = None,
    top_k: int = 10,
    temperature: float = 0.7,
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:  # ty: ignore[invalid-method-override]
    """Generate layouts by encoding conditions, generating ids, and decoding.

    Args:
        batch_size: Number of layouts to generate when labels are omitted.
        seed: Convenience seed used only when `generator` is absent.
        generator: Optional PyTorch generator; takes precedence over `seed`.
        condition_type: Canonical condition type or supported alias.
        labels: Optional label conditions.
        bbox: Optional layout boxes for size/completion/refinement conditions.
        mask: Reserved public validity mask input.
        relations: Optional relation tuples for relation-conditioned checkpoints.
        num_elements: Reserved v1 interface argument.
        box_format: Input and output bounding-box format.
        normalized: Whether public boxes are normalized.
        canvas_size: Reserved v1 interface argument.
        num_inference_steps: Reserved v1 interface argument.
        output_type: Return `dataclass` or `dict`.
        return_intermediates: Reserved output detail flag.
        max_length: Optional token decode length override.
        do_sample: Optional sampling override.
        top_k: Top-k value used by the reference sampling loop.
        temperature: Sampling temperature.

    Returns:
        Layout generation output dataclass or dictionary.

    Raises:
        ValueError: If processor inputs are invalid.
    """
    _ = (num_elements, num_inference_steps, return_intermediates)
    encoded = self.processor(
        condition_type=condition_type,
        batch_size=batch_size,
        return_tensors="pt",
        labels=labels,
        bbox=bbox,
        mask=mask,
        relations=relations,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    model_device = next(self.model.parameters()).device
    input_ids = encoded["input_ids"].to(model_device)
    attention_mask = encoded["attention_mask"].to(model_device)
    condition = self.processor.normalize_condition_type(condition_type)
    generation_generator = self.prepare_generator(
        generator=generator,
        seed=seed,
        device=model_device,
    )
    default_do_sample = condition in {
        ConditionType.unconditional,
        ConditionType.completion,
    }
    sequences = self.model._generate_sequences(
        input_ids,
        attention_mask,
        max_length=max_length,
        do_sample=default_do_sample if do_sample is None else do_sample,
        top_k=top_k,
        temperature=temperature,
        generator=generation_generator,
    )
    return self.processor.post_process_layouts(
        sequences.cpu(),
        box_format=box_format,
        output_type=output_type,
    )

LayoutFormerPPProcessor

Bases: ProcessorMixin

Build LayoutFormer++ text inputs and parse generated layouts.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
 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
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
class LayoutFormerPPProcessor(ProcessorMixin):
    """Build LayoutFormer++ text inputs and parse generated layouts."""

    attributes = ["tokenizer"]
    tokenizer_class = "LayoutFormerPPTokenizer"

    def __init__(
        self,
        tokenizer: LayoutFormerPPTokenizer,
        dataset: DatasetName | str = DEFAULT_DATASET,
        task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
        add_sep_token: bool = True,
        x_grid: int = 128,
        y_grid: int = 128,
        id2label: dict[int, str] | None = None,
    ) -> None:
        """Initialize serializers, label maps, and tokenizer state."""
        self.tokenizer = tokenizer
        normalized_dataset = normalize_layoutformerpp_dataset(dataset)
        normalized_task = normalize_layoutformerpp_task(task)
        self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
        self.task = str(normalized_task)
        self.add_sep_token = add_sep_token
        self.x_grid = x_grid
        self.y_grid = y_grid
        labels = labels_for_dataset(normalized_dataset)
        self.id2label = (
            {int(key): str(value) for key, value in id2label.items()}
            if id2label is not None
            else dict(enumerate(labels))
        )
        self.public_id2label = dict(self.id2label)
        self.public_label2id = {
            value.lower(): key for key, value in self.public_id2label.items()
        }
        self.internal_id2label = {
            idx + 1: f"label_{idx + 1}" for idx in range(len(labels))
        }
        self.serializer = T5LayoutSequence(
            self.internal_id2label, add_sep_token=add_sep_token
        )
        self.gen_t_serializer = T5LayoutSequenceForGenT(
            self.internal_id2label, add_sep_token=add_sep_token
        )
        self.gen_r_serializer = T5LayoutSequenceForGenR(
            self.internal_id2label, add_sep_token=add_sep_token
        )
        super().__init__(tokenizer=tokenizer)

    @classmethod
    def from_config(
        cls,
        dataset: DatasetName | str = DEFAULT_DATASET,
        task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
        *,
        add_sep_token: bool = True,
        x_grid: int = 128,
        y_grid: int = 128,
        id2label: dict[int, str] | None = None,
    ) -> "LayoutFormerPPProcessor":
        """Construct processor and tokenizer without external files."""
        normalized_dataset = normalize_layoutformerpp_dataset(dataset)
        normalized_task = normalize_layoutformerpp_task(task)
        labels = labels_for_dataset(normalized_dataset)
        tokens = build_default_tokens(labels, task=normalized_task, grid=x_grid)
        tokenizer = LayoutFormerPPTokenizer(tokens=tokens)
        return cls(
            tokenizer=tokenizer,
            dataset=normalized_dataset,
            task=normalized_task,
            add_sep_token=add_sep_token,
            x_grid=x_grid,
            y_grid=y_grid,
            id2label=id2label,
        )

    @classmethod
    def _load_tokenizer_from_pretrained(
        cls,
        sub_processor_type: str,
        pretrained_model_name_or_path: str | PathLike[str],
        subfolder: str = "",
        **kwargs: str | int | float | bool | None,
    ) -> LayoutFormerPPTokenizer:
        """Load the local tokenizer for `ProcessorMixin.from_pretrained`."""
        _ = sub_processor_type
        path = Path(pretrained_model_name_or_path)
        tokenizer_path = path / subfolder if subfolder else path
        token = kwargs.get("token")
        return LayoutFormerPPTokenizer.from_pretrained(
            tokenizer_path,
            cache_dir=cast(str | PathLike[str] | None, kwargs.get("cache_dir")),
            force_download=bool(kwargs.get("force_download", False)),
            local_files_only=bool(kwargs.get("local_files_only", False)),
            token=token if isinstance(token, str | bool) else None,
            revision=str(kwargs.get("revision", "main")),
        )

    def normalize_condition_type(
        self, condition_type: ConditionType | str
    ) -> SupportedConditionType:
        """Normalize public condition aliases."""
        try:
            condition = normalize_common_condition_type(condition_type)
        except ValueError as exc:
            raise ValueError(f"Unsupported condition_type: {condition_type}") from exc

        if condition not in SUPPORTED_CONDITIONS:
            raise ValueError(f"Unsupported condition_type: {condition_type}")

        return cast(SupportedConditionType, condition)

    def _label_to_internal_id(self, label: int | str) -> int:
        if isinstance(label, int):
            return label + 1 if label in self.public_id2label else label
        lowered = label.lower()
        if lowered in self.public_label2id:
            return self.public_label2id[lowered] + 1
        if lowered.startswith("label_"):
            return int(lowered.split("_", 1)[1])
        raise ValueError(f"Unknown label: {label}")

    def _prepare_labels(
        self,
        labels: list[list[int | str]] | Int[torch.Tensor, "batch elements"] | None,
        batch_size: int,
        mask: list[list[bool]] | None = None,
    ) -> list[list[int]]:
        if labels is None:
            return [[] for _ in range(batch_size)]
        if isinstance(labels, torch.Tensor):
            return [[int(value) for value in row] for row in labels.tolist()]
        rows: list[list[int]] = []
        for row_idx, item in enumerate(labels):
            row_mask = None if mask is None else mask[row_idx]
            rows.append(
                [
                    self._label_to_internal_id(label)
                    for idx, label in enumerate(item)
                    if row_mask is None or row_mask[idx]
                ]
            )
        return rows

    def _prepare_mask(
        self,
        mask: Bool[torch.Tensor, "batch elements"]
        | list[list[bool]]
        | list[bool]
        | None,
        *,
        batch_size: int,
        row_lengths: list[int],
    ) -> list[list[bool]] | None:
        if mask is None:
            return None
        mask_tensor = torch.as_tensor(mask, dtype=torch.bool)
        if mask_tensor.ndim == 1:
            mask_tensor = mask_tensor.unsqueeze(0)
        if mask_tensor.ndim != 2:
            raise ValueError("mask must have shape (batch, sequence)")

        if mask_tensor.size(0) != batch_size:
            raise ValueError("mask batch dimension must match labels or batch_size")

        rows = mask_tensor.tolist()
        for row, expected_length in zip(rows, row_lengths, strict=True):
            if len(row) < expected_length:
                raise ValueError("mask sequence length must cover all labels")

        return rows

    def _prepare_bbox(
        self,
        bbox: LayoutFormerPPBBoxInput,
        *,
        labels: list[list[int]],
        box_format: BoxFormat | str,
        mask: list[list[bool]] | None = None,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
    ) -> list[list[list[int]]]:
        if bbox is None:
            return [[[0, 0, 1, 1] for _ in item] for item in labels]
        tensor = torch.as_tensor(bbox, dtype=torch.float32)
        if tensor.ndim == 2:
            tensor = tensor.unsqueeze(0)
        discrete_box_format = box_format
        if not normalized:
            if canvas_size is None:
                raise ValueError("canvas_size is required when normalized=False")

            tensor = normalize_boxes(
                tensor,
                canvas_size=canvas_size,
                box_format=box_format,
            )
            discrete_box_format = BoxFormat.xywh
        discrete = public_to_discrete_ltwh(
            tensor,
            box_format=discrete_box_format,
            x_grid=self.x_grid,
            y_grid=self.y_grid,
        )
        rows = discrete.tolist()
        if mask is None:
            return rows
        return [
            [box for idx, box in enumerate(row) if row_mask[idx]]
            for row, row_mask in zip(rows, mask, strict=True)
        ]

    def __call__(
        self,
        condition_type: ConditionType | str = ConditionType.unconditional,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        bbox: LayoutFormerPPBBoxInput = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | list[list[bool]]
        | list[bool]
        | None = None,
        relations: list[list[tuple[int, int, int, int, int]]]
        | Int[torch.Tensor, "batch relations relation_attrs"]
        | None = None,
        batch_size: int | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        return_tensors: Literal["pt"] = "pt",
    ) -> BatchEncoding:
        """Build tokenized model inputs for a public condition."""
        condition = self.normalize_condition_type(condition_type)
        batch_size = batch_size or (len(labels) if labels is not None else 1)
        row_lengths = (
            [len(row) for row in labels] if labels is not None else [0] * batch_size
        )
        prepared_mask = self._prepare_mask(
            mask,
            batch_size=batch_size,
            row_lengths=row_lengths,
        )
        internal_labels = self._prepare_labels(labels, batch_size, prepared_mask)
        internal_bbox = self._prepare_bbox(
            bbox,
            labels=internal_labels,
            box_format=box_format,
            mask=prepared_mask,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        texts: list[str] = []
        for idx in range(batch_size):
            if condition is ConditionType.unconditional:
                texts.append("")
            elif condition is ConditionType.label:
                texts.append(
                    self.gen_t_serializer.build_input_seq(
                        "gen_t", internal_labels[idx], internal_bbox[idx]
                    )
                )
            elif condition is ConditionType.label_size:
                texts.append(
                    self.gen_t_serializer.build_input_seq(
                        "gen_ts", internal_labels[idx], internal_bbox[idx]
                    )
                )
            elif condition is ConditionType.relation:
                item_relations = [] if relations is None else relations[idx]
                if isinstance(item_relations, torch.Tensor):
                    item_relations = cast(
                        list[tuple[int, int, int, int, int]],
                        [
                            tuple(int(value) for value in row)
                            for row in item_relations.tolist()
                        ],
                    )
                texts.append(
                    self.gen_r_serializer.build_input_seq(
                        internal_labels[idx], item_relations
                    )
                )
            elif condition is ConditionType.completion:
                texts.append(
                    self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
                )
            elif condition is ConditionType.refinement:
                texts.append(
                    self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
                )
            else:
                raise ValueError(f"Unsupported condition_type: {condition}")

        encoded = self.tokenizer.encode_text(texts, add_eos=True, add_bos=False)
        if return_tensors != "pt":
            raise ValueError("Only return_tensors='pt' is supported")

        return BatchEncoding(encoded)

    def post_process_layouts(
        self,
        sequences: Int[torch.Tensor, "batch tokens"],
        *,
        box_format: BoxFormat | str = BoxFormat.xywh,
        output_type: OutputType | str = OutputType.dataclass,
        return_tensors: Literal["pt"] = "pt",
    ) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:
        """Parse generated token ids to the common layout output schema."""
        texts = self.tokenizer.batch_decode(sequences, skip_special_tokens=True)
        parsed = [self.serializer.parse_seq(text.strip()) for text in texts]
        max_len = max(
            (len(item.labels) for item in parsed if item is not None), default=0
        )
        if max_len == 0:
            max_len = 1
        label_rows: list[Int[torch.Tensor, "elements"]] = []
        bbox_rows: list[Int[torch.Tensor, "elements 4"]] = []
        mask_rows: list[Bool[torch.Tensor, "elements"]] = []
        for item in parsed:
            if item is None:
                labels = torch.zeros(max_len, dtype=torch.long)
                boxes = torch.zeros(max_len, 4, dtype=torch.long)
                mask = torch.zeros(max_len, dtype=torch.bool)
            else:
                labels = torch.tensor(
                    [max(0, label - 1) for label in item.labels], dtype=torch.long
                )
                boxes = torch.tensor(item.bbox, dtype=torch.long)
                mask = torch.ones(len(labels), dtype=torch.bool)
                if len(labels) < max_len:
                    pad = max_len - len(labels)
                    labels = torch.nn.functional.pad(labels, (0, pad))
                    boxes = torch.nn.functional.pad(boxes, (0, 0, 0, pad))
                    mask = torch.nn.functional.pad(mask, (0, pad))
            label_rows.append(labels)
            bbox_rows.append(boxes)
            mask_rows.append(mask)
        bbox_ids = torch.stack(bbox_rows)
        normalized_box_format = normalize_box_format(box_format)
        bbox = discrete_ltwh_to_public(
            bbox_ids,
            box_format=normalized_box_format,
            x_grid=self.x_grid,
            y_grid=self.y_grid,
        )
        output = LayoutGenerationOutput(
            bbox=bbox.float(),
            labels=torch.stack(label_rows).long(),
            mask=torch.stack(mask_rows).bool(),
            id2label=dict(self.public_id2label),
            sequences=sequences.long(),
            intermediates={
                "generated_text": texts,
                "box_format": normalized_box_format,
            },
        )
        try:
            normalized_output_type = (
                output_type
                if isinstance(output_type, OutputType)
                else OutputType(output_type)
            )
        except ValueError as exc:
            raise ValueError(f"Unsupported output_type: {output_type}") from exc

        if normalized_output_type is OutputType.dict:
            return dict(output)
        if normalized_output_type is not OutputType.dataclass:
            assert_never(normalized_output_type)
        if return_tensors != "pt":
            raise ValueError("Only return_tensors='pt' is supported")

        return output

__init__

__init__(
    tokenizer: LayoutFormerPPTokenizer,
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask
    | ConditionType
    | str = DEFAULT_TASK,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> None

Initialize serializers, label maps, and tokenizer state.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
 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
def __init__(
    self,
    tokenizer: LayoutFormerPPTokenizer,
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> None:
    """Initialize serializers, label maps, and tokenizer state."""
    self.tokenizer = tokenizer
    normalized_dataset = normalize_layoutformerpp_dataset(dataset)
    normalized_task = normalize_layoutformerpp_task(task)
    self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
    self.task = str(normalized_task)
    self.add_sep_token = add_sep_token
    self.x_grid = x_grid
    self.y_grid = y_grid
    labels = labels_for_dataset(normalized_dataset)
    self.id2label = (
        {int(key): str(value) for key, value in id2label.items()}
        if id2label is not None
        else dict(enumerate(labels))
    )
    self.public_id2label = dict(self.id2label)
    self.public_label2id = {
        value.lower(): key for key, value in self.public_id2label.items()
    }
    self.internal_id2label = {
        idx + 1: f"label_{idx + 1}" for idx in range(len(labels))
    }
    self.serializer = T5LayoutSequence(
        self.internal_id2label, add_sep_token=add_sep_token
    )
    self.gen_t_serializer = T5LayoutSequenceForGenT(
        self.internal_id2label, add_sep_token=add_sep_token
    )
    self.gen_r_serializer = T5LayoutSequenceForGenR(
        self.internal_id2label, add_sep_token=add_sep_token
    )
    super().__init__(tokenizer=tokenizer)

from_config classmethod

from_config(
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask
    | ConditionType
    | str = DEFAULT_TASK,
    *,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> "LayoutFormerPPProcessor"

Construct processor and tokenizer without external files.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
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
@classmethod
def from_config(
    cls,
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
    *,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> "LayoutFormerPPProcessor":
    """Construct processor and tokenizer without external files."""
    normalized_dataset = normalize_layoutformerpp_dataset(dataset)
    normalized_task = normalize_layoutformerpp_task(task)
    labels = labels_for_dataset(normalized_dataset)
    tokens = build_default_tokens(labels, task=normalized_task, grid=x_grid)
    tokenizer = LayoutFormerPPTokenizer(tokens=tokens)
    return cls(
        tokenizer=tokenizer,
        dataset=normalized_dataset,
        task=normalized_task,
        add_sep_token=add_sep_token,
        x_grid=x_grid,
        y_grid=y_grid,
        id2label=id2label,
    )

normalize_condition_type

normalize_condition_type(
    condition_type: ConditionType | str,
) -> SupportedConditionType

Normalize public condition aliases.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
160
161
162
163
164
165
166
167
168
169
170
171
172
def normalize_condition_type(
    self, condition_type: ConditionType | str
) -> SupportedConditionType:
    """Normalize public condition aliases."""
    try:
        condition = normalize_common_condition_type(condition_type)
    except ValueError as exc:
        raise ValueError(f"Unsupported condition_type: {condition_type}") from exc

    if condition not in SUPPORTED_CONDITIONS:
        raise ValueError(f"Unsupported condition_type: {condition_type}")

    return cast(SupportedConditionType, condition)

__call__

__call__(
    condition_type: ConditionType
    | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[Tensor, "batch elements"]
    | list[list[bool]]
    | list[bool]
    | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[Tensor, "batch relations relation_attrs"]
    | None = None,
    batch_size: int | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding

Build tokenized model inputs for a public condition.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
def __call__(
    self,
    condition_type: ConditionType | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | list[list[bool]]
    | list[bool]
    | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[torch.Tensor, "batch relations relation_attrs"]
    | None = None,
    batch_size: int | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding:
    """Build tokenized model inputs for a public condition."""
    condition = self.normalize_condition_type(condition_type)
    batch_size = batch_size or (len(labels) if labels is not None else 1)
    row_lengths = (
        [len(row) for row in labels] if labels is not None else [0] * batch_size
    )
    prepared_mask = self._prepare_mask(
        mask,
        batch_size=batch_size,
        row_lengths=row_lengths,
    )
    internal_labels = self._prepare_labels(labels, batch_size, prepared_mask)
    internal_bbox = self._prepare_bbox(
        bbox,
        labels=internal_labels,
        box_format=box_format,
        mask=prepared_mask,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    texts: list[str] = []
    for idx in range(batch_size):
        if condition is ConditionType.unconditional:
            texts.append("")
        elif condition is ConditionType.label:
            texts.append(
                self.gen_t_serializer.build_input_seq(
                    "gen_t", internal_labels[idx], internal_bbox[idx]
                )
            )
        elif condition is ConditionType.label_size:
            texts.append(
                self.gen_t_serializer.build_input_seq(
                    "gen_ts", internal_labels[idx], internal_bbox[idx]
                )
            )
        elif condition is ConditionType.relation:
            item_relations = [] if relations is None else relations[idx]
            if isinstance(item_relations, torch.Tensor):
                item_relations = cast(
                    list[tuple[int, int, int, int, int]],
                    [
                        tuple(int(value) for value in row)
                        for row in item_relations.tolist()
                    ],
                )
            texts.append(
                self.gen_r_serializer.build_input_seq(
                    internal_labels[idx], item_relations
                )
            )
        elif condition is ConditionType.completion:
            texts.append(
                self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
            )
        elif condition is ConditionType.refinement:
            texts.append(
                self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
            )
        else:
            raise ValueError(f"Unsupported condition_type: {condition}")

    encoded = self.tokenizer.encode_text(texts, add_eos=True, add_bos=False)
    if return_tensors != "pt":
        raise ValueError("Only return_tensors='pt' is supported")

    return BatchEncoding(encoded)

post_process_layouts

post_process_layouts(
    sequences: Int[Tensor, "batch tokens"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    output_type: OutputType | str = OutputType.dataclass,
    return_tensors: Literal["pt"] = "pt",
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict

Parse generated token ids to the common layout output schema.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
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
def post_process_layouts(
    self,
    sequences: Int[torch.Tensor, "batch tokens"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    output_type: OutputType | str = OutputType.dataclass,
    return_tensors: Literal["pt"] = "pt",
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:
    """Parse generated token ids to the common layout output schema."""
    texts = self.tokenizer.batch_decode(sequences, skip_special_tokens=True)
    parsed = [self.serializer.parse_seq(text.strip()) for text in texts]
    max_len = max(
        (len(item.labels) for item in parsed if item is not None), default=0
    )
    if max_len == 0:
        max_len = 1
    label_rows: list[Int[torch.Tensor, "elements"]] = []
    bbox_rows: list[Int[torch.Tensor, "elements 4"]] = []
    mask_rows: list[Bool[torch.Tensor, "elements"]] = []
    for item in parsed:
        if item is None:
            labels = torch.zeros(max_len, dtype=torch.long)
            boxes = torch.zeros(max_len, 4, dtype=torch.long)
            mask = torch.zeros(max_len, dtype=torch.bool)
        else:
            labels = torch.tensor(
                [max(0, label - 1) for label in item.labels], dtype=torch.long
            )
            boxes = torch.tensor(item.bbox, dtype=torch.long)
            mask = torch.ones(len(labels), dtype=torch.bool)
            if len(labels) < max_len:
                pad = max_len - len(labels)
                labels = torch.nn.functional.pad(labels, (0, pad))
                boxes = torch.nn.functional.pad(boxes, (0, 0, 0, pad))
                mask = torch.nn.functional.pad(mask, (0, pad))
        label_rows.append(labels)
        bbox_rows.append(boxes)
        mask_rows.append(mask)
    bbox_ids = torch.stack(bbox_rows)
    normalized_box_format = normalize_box_format(box_format)
    bbox = discrete_ltwh_to_public(
        bbox_ids,
        box_format=normalized_box_format,
        x_grid=self.x_grid,
        y_grid=self.y_grid,
    )
    output = LayoutGenerationOutput(
        bbox=bbox.float(),
        labels=torch.stack(label_rows).long(),
        mask=torch.stack(mask_rows).bool(),
        id2label=dict(self.public_id2label),
        sequences=sequences.long(),
        intermediates={
            "generated_text": texts,
            "box_format": normalized_box_format,
        },
    )
    try:
        normalized_output_type = (
            output_type
            if isinstance(output_type, OutputType)
            else OutputType(output_type)
        )
    except ValueError as exc:
        raise ValueError(f"Unsupported output_type: {output_type}") from exc

    if normalized_output_type is OutputType.dict:
        return dict(output)
    if normalized_output_type is not OutputType.dataclass:
        assert_never(normalized_output_type)
    if return_tensors != "pt":
        raise ValueError("Only return_tensors='pt' is supported")

    return output

LayoutFormerPPTask

Bases: StrEnum

Supported converted LayoutFormer++ checkpoint variants.

Source code in models/layoutformerpp/src/layoutformerpp/tasks.py
12
13
14
15
16
17
18
19
20
class LayoutFormerPPTask(StrEnum):
    """Supported converted LayoutFormer++ checkpoint variants."""

    ugen = auto()
    gen_t = auto()
    gen_ts = auto()
    gen_r = auto()
    completion = auto()
    refinement = auto()

OutputType

Bases: StrEnum

Supported post-processing return shapes.

Source code in models/layoutformerpp/src/layoutformerpp/tasks.py
23
24
25
26
27
class OutputType(StrEnum):
    """Supported post-processing return shapes."""

    dataclass = auto()
    dict = auto()

LayoutFormerPPTokenizer

Bases: WhitespaceTokenizerMixin, PreTrainedTokenizer

Whitespace tokenizer backed by the released vocab.json format.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.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
 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
class LayoutFormerPPTokenizer(WhitespaceTokenizerMixin, PreTrainedTokenizer):
    """Whitespace tokenizer backed by the released `vocab.json` format."""

    vocab_files_names = {"vocab_file": "vocab.json"}
    model_input_names = ["input_ids", "attention_mask"]

    def __init__(
        self,
        vocab_file: str | None = None,
        tokens: list[str] | None = None,
        x_grid: int = 128,
        y_grid: int = 128,
        bbox_order: BoxFormat | str = BoxFormat.ltwh,
        bos_token: str = "<bos>",
        eos_token: str = "<eos>",
        pad_token: str = "<pad>",
        sep_token: str = "<sep>",
        unk_token: str = "<unk>",
        model_max_length: int = DEFAULT_MODEL_MAX_LENGTH,
        padding_side: str = "right",
        truncation_side: str = "right",
        clean_up_tokenization_spaces: bool = False,
        added_tokens_decoder: dict[int | str, str] | None = None,
        backend: str = "custom",
        tokenizer_file: str | None = None,
        name_or_path: str = "",
        is_local: bool = False,
        local_files_only: bool = False,
        processor_class: str | None = None,
    ) -> None:
        """Initialize a tokenizer from a vocab file or synthetic token list."""
        _ = (backend, tokenizer_file, is_local, local_files_only, processor_class)
        self.x_grid = x_grid
        self.y_grid = y_grid
        self.bbox_order = str(normalize_box_format(bbox_order))
        self._token2id, self._id2token = build_token_maps(
            vocab_file=vocab_file,
            tokens=tokens,
            base_tokens=("<bos>", "<eos>", "<pad>", "<sep>", "<unk>"),
        )
        tokenizer_kwargs: dict[str, object] = {
            "bos_token": bos_token,
            "eos_token": eos_token,
            "pad_token": pad_token,
            "sep_token": sep_token,
            "unk_token": unk_token,
            "model_max_length": model_max_length,
            "padding_side": padding_side,
            "truncation_side": truncation_side,
            "clean_up_tokenization_spaces": clean_up_tokenization_spaces,
            "backend": backend,
            "name_or_path": name_or_path,
        }
        if added_tokens_decoder is not None:
            tokenizer_kwargs["added_tokens_decoder"] = added_tokens_decoder
        super().__init__(**tokenizer_kwargs)

    def save_vocabulary(
        self, save_directory: str, filename_prefix: str | None = None
    ) -> tuple[str]:
        """Save `vocab.json` in checkpoint-compatible token-to-id format."""
        return save_json_vocabulary(
            save_directory=save_directory,
            filename="vocab.json",
            data=self._token2id,
            filename_prefix=filename_prefix,
        )

    def save_pretrained(
        self,
        save_directory: str | PathLike[str],
        legacy_format: bool | None = None,
        filename_prefix: str | None = None,
        push_to_hub: bool = False,
        **kwargs: str | int | float | bool | None,
    ) -> tuple[str, ...]:
        """Save tokenizer files plus LayoutFormer++ tokenizer metadata."""
        _ = kwargs
        paths = super().save_pretrained(
            str(save_directory),
            legacy_format=legacy_format,
            filename_prefix=filename_prefix,
            push_to_hub=push_to_hub,
        )
        metadata = {
            "x_grid": self.x_grid,
            "y_grid": self.y_grid,
            "bbox_order": self.bbox_order,
        }
        with (Path(save_directory) / "layoutformerpp_tokenizer_config.json").open(
            "w"
        ) as f:
            json.dump(metadata, f, indent=2, sort_keys=True)
        return paths

    @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",
        x_grid: int | None = None,
        y_grid: int | None = None,
        bbox_order: BoxFormat | str | None = None,
    ) -> "LayoutFormerPPTokenizer":
        """Load tokenizer and LayoutFormer++ metadata."""
        path = Path(pretrained_model_name_or_path)
        metadata_path = path / "layoutformerpp_tokenizer_config.json"
        metadata: dict[str, object] = {}
        if metadata_path.exists():
            with metadata_path.open() as f:
                metadata = json.load(f)
        if x_grid is not None:
            metadata["x_grid"] = x_grid
        if y_grid is not None:
            metadata["y_grid"] = y_grid
        if bbox_order is not None:
            metadata["bbox_order"] = bbox_order
        return cast(
            "LayoutFormerPPTokenizer",
            super().from_pretrained(
                str(pretrained_model_name_or_path),
                cache_dir=cache_dir,
                force_download=force_download,
                local_files_only=local_files_only,
                token=token,
                revision=revision,
                **metadata,
            ),
        )

    def encode_text(
        self, text: str | list[str], *, add_eos: bool = True, add_bos: bool = False
    ) -> BatchEncoding:
        """Tokenize reference-style text while matching the original EOS/BOS behavior."""
        if isinstance(text, str):
            texts = [text]
        else:
            texts = text
        normalized = []
        for item in texts:
            tokens = item.strip().split()
            if add_eos:
                tokens.append(self.eos_token)
            if add_bos:
                tokens.insert(0, self.bos_token)
            normalized.append(" ".join(tokens))
        return self(
            normalized, padding=True, add_special_tokens=False, return_tensors="pt"
        )

__init__

__init__(
    vocab_file: str | None = None,
    tokens: list[str] | None = None,
    x_grid: int = 128,
    y_grid: int = 128,
    bbox_order: BoxFormat | str = BoxFormat.ltwh,
    bos_token: str = "<bos>",
    eos_token: str = "<eos>",
    pad_token: str = "<pad>",
    sep_token: str = "<sep>",
    unk_token: str = "<unk>",
    model_max_length: int = DEFAULT_MODEL_MAX_LENGTH,
    padding_side: str = "right",
    truncation_side: str = "right",
    clean_up_tokenization_spaces: bool = False,
    added_tokens_decoder: dict[int | str, str]
    | None = None,
    backend: str = "custom",
    tokenizer_file: str | None = None,
    name_or_path: str = "",
    is_local: bool = False,
    local_files_only: bool = False,
    processor_class: str | None = None,
) -> None

Initialize a tokenizer from a vocab file or synthetic token list.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
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
def __init__(
    self,
    vocab_file: str | None = None,
    tokens: list[str] | None = None,
    x_grid: int = 128,
    y_grid: int = 128,
    bbox_order: BoxFormat | str = BoxFormat.ltwh,
    bos_token: str = "<bos>",
    eos_token: str = "<eos>",
    pad_token: str = "<pad>",
    sep_token: str = "<sep>",
    unk_token: str = "<unk>",
    model_max_length: int = DEFAULT_MODEL_MAX_LENGTH,
    padding_side: str = "right",
    truncation_side: str = "right",
    clean_up_tokenization_spaces: bool = False,
    added_tokens_decoder: dict[int | str, str] | None = None,
    backend: str = "custom",
    tokenizer_file: str | None = None,
    name_or_path: str = "",
    is_local: bool = False,
    local_files_only: bool = False,
    processor_class: str | None = None,
) -> None:
    """Initialize a tokenizer from a vocab file or synthetic token list."""
    _ = (backend, tokenizer_file, is_local, local_files_only, processor_class)
    self.x_grid = x_grid
    self.y_grid = y_grid
    self.bbox_order = str(normalize_box_format(bbox_order))
    self._token2id, self._id2token = build_token_maps(
        vocab_file=vocab_file,
        tokens=tokens,
        base_tokens=("<bos>", "<eos>", "<pad>", "<sep>", "<unk>"),
    )
    tokenizer_kwargs: dict[str, object] = {
        "bos_token": bos_token,
        "eos_token": eos_token,
        "pad_token": pad_token,
        "sep_token": sep_token,
        "unk_token": unk_token,
        "model_max_length": model_max_length,
        "padding_side": padding_side,
        "truncation_side": truncation_side,
        "clean_up_tokenization_spaces": clean_up_tokenization_spaces,
        "backend": backend,
        "name_or_path": name_or_path,
    }
    if added_tokens_decoder is not None:
        tokenizer_kwargs["added_tokens_decoder"] = added_tokens_decoder
    super().__init__(**tokenizer_kwargs)

save_vocabulary

save_vocabulary(
    save_directory: str, filename_prefix: str | None = None
) -> tuple[str]

Save vocab.json in checkpoint-compatible token-to-id format.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
78
79
80
81
82
83
84
85
86
87
def save_vocabulary(
    self, save_directory: str, filename_prefix: str | None = None
) -> tuple[str]:
    """Save `vocab.json` in checkpoint-compatible token-to-id format."""
    return save_json_vocabulary(
        save_directory=save_directory,
        filename="vocab.json",
        data=self._token2id,
        filename_prefix=filename_prefix,
    )

save_pretrained

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

Save tokenizer files plus LayoutFormer++ tokenizer metadata.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
 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
def save_pretrained(
    self,
    save_directory: str | PathLike[str],
    legacy_format: bool | None = None,
    filename_prefix: str | None = None,
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> tuple[str, ...]:
    """Save tokenizer files plus LayoutFormer++ tokenizer metadata."""
    _ = kwargs
    paths = super().save_pretrained(
        str(save_directory),
        legacy_format=legacy_format,
        filename_prefix=filename_prefix,
        push_to_hub=push_to_hub,
    )
    metadata = {
        "x_grid": self.x_grid,
        "y_grid": self.y_grid,
        "bbox_order": self.bbox_order,
    }
    with (Path(save_directory) / "layoutformerpp_tokenizer_config.json").open(
        "w"
    ) as f:
        json.dump(metadata, f, indent=2, sort_keys=True)
    return paths

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",
    x_grid: int | None = None,
    y_grid: int | None = None,
    bbox_order: BoxFormat | str | None = None,
) -> "LayoutFormerPPTokenizer"

Load tokenizer and LayoutFormer++ metadata.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
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
@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",
    x_grid: int | None = None,
    y_grid: int | None = None,
    bbox_order: BoxFormat | str | None = None,
) -> "LayoutFormerPPTokenizer":
    """Load tokenizer and LayoutFormer++ metadata."""
    path = Path(pretrained_model_name_or_path)
    metadata_path = path / "layoutformerpp_tokenizer_config.json"
    metadata: dict[str, object] = {}
    if metadata_path.exists():
        with metadata_path.open() as f:
            metadata = json.load(f)
    if x_grid is not None:
        metadata["x_grid"] = x_grid
    if y_grid is not None:
        metadata["y_grid"] = y_grid
    if bbox_order is not None:
        metadata["bbox_order"] = bbox_order
    return cast(
        "LayoutFormerPPTokenizer",
        super().from_pretrained(
            str(pretrained_model_name_or_path),
            cache_dir=cache_dir,
            force_download=force_download,
            local_files_only=local_files_only,
            token=token,
            revision=revision,
            **metadata,
        ),
    )

encode_text

encode_text(
    text: str | list[str],
    *,
    add_eos: bool = True,
    add_bos: bool = False,
) -> BatchEncoding

Tokenize reference-style text while matching the original EOS/BOS behavior.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def encode_text(
    self, text: str | list[str], *, add_eos: bool = True, add_bos: bool = False
) -> BatchEncoding:
    """Tokenize reference-style text while matching the original EOS/BOS behavior."""
    if isinstance(text, str):
        texts = [text]
    else:
        texts = text
    normalized = []
    for item in texts:
        tokens = item.strip().split()
        if add_eos:
            tokens.append(self.eos_token)
        if add_bos:
            tokens.insert(0, self.bos_token)
        normalized.append(" ".join(tokens))
    return self(
        normalized, padding=True, add_special_tokens=False, return_tensors="pt"
    )

configuration_layoutformerpp

Configuration for LayoutFormer++.

TaskDefaults

Bases: TypedDict

Evaluation defaults for one dataset/condition pair.

Attributes:

Name Type Description
max_position_embeddings int

Upper bound for the model's input token sequence.

decode_max_length int

Evaluation-time budget for generated tokens.

eval_seed int

Evaluation-time seed used by stochastic decoding.

Values are selected per dataset and condition to reproduce the evaluation recipes documented in models/layoutformerpp/REPRODUCING.md.

Source code in models/layoutformerpp/src/layoutformerpp/configuration_layoutformerpp.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class TaskDefaults(TypedDict, total=False):
    """Evaluation defaults for one dataset/condition pair.

    Attributes:
        max_position_embeddings: Upper bound for the model's input token
            sequence.
        decode_max_length: Evaluation-time budget for generated tokens.
        eval_seed: Evaluation-time seed used by stochastic decoding.

    Values are selected per dataset and condition to reproduce the evaluation
    recipes documented in ``models/layoutformerpp/REPRODUCING.md``.
    """

    max_position_embeddings: int
    decode_max_length: int
    eval_seed: int

LayoutFormerPPConfig

Bases: PretrainedConfig

Stores model, tokenizer, and task defaults for converted checkpoints.

Source code in models/layoutformerpp/src/layoutformerpp/configuration_layoutformerpp.py
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
class LayoutFormerPPConfig(PretrainedConfig):
    """Stores model, tokenizer, and task defaults for converted checkpoints."""

    model_type = "layoutformerpp"

    def __init__(
        self,
        vocab_size: int = 0,
        max_position_embeddings: int | None = None,
        d_model: int = 512,
        encoder_layers: int = 8,
        decoder_layers: int | None = None,
        encoder_attention_heads: int = 8,
        decoder_attention_heads: int | None = None,
        dropout: float = 0.1,
        dim_feedforward: int | None = None,
        share_embedding: bool = True,
        dataset: DatasetName | str = DatasetName.rico25,
        task: LayoutFormerPPTask | ConditionType | str = LayoutFormerPPTask.gen_t,
        max_num_elements: int = 20,
        bbox_format: BoxFormat | str = BoxFormat.ltwh,
        default_box_format: BoxFormat | str = BoxFormat.xywh,
        discrete_x_grid: int = 128,
        discrete_y_grid: int = 128,
        add_sep_token: bool = True,
        sort_by_dict: bool = True,
        add_task_embedding: bool = False,
        add_task_prompt_token_in_model: bool = False,
        num_task_prompt_token: int = 1,
        task_id: int | None = None,
        decode_max_length: int | None = None,
        eval_seed: int | None = None,
        gen_t_add_unk_token: bool = False,
        gen_ts_add_unk_token: bool = False,
        gen_r_add_unk_token: bool = False,
        gen_r_compact: bool = False,
        bos_token_id: int = 0,
        eos_token_id: int = 1,
        pad_token_id: int = 2,
        is_encoder_decoder: bool = True,
        condition_type: ConditionType | str | None = None,
        model_type: str | None = None,
        transformers_version: str | None = None,
        architectures: list[str] | None = None,
        output_hidden_states: bool | None = False,
        output_attentions: bool | None = False,
        return_dict: bool | None = True,
        chunk_size_feed_forward: int = 0,
        problem_type: Literal[
            "regression", "single_label_classification", "multi_label_classification"
        ]
        | None = None,
        id2label: dict[int | str, str] | None = None,
        label2id: dict[str, int] | None = None,
        torch_dtype: str | None = None,
        dtype: str | None = None,
        tie_word_embeddings: bool = True,
        task_specific_params: dict[
            str, str | int | float | bool | list[str | int | float | bool]
        ]
        | None = None,
        name_or_path: str = "",
        _name_or_path: str | None = None,
        _commit_hash: str | None = None,
        attn_implementation: str | None = None,
        **kwargs: str | int | float | bool | None,
    ) -> None:
        """Initialize architecture and task-specific generation defaults."""
        _ = (condition_type, model_type, transformers_version)
        normalized_dataset = normalize_layoutformerpp_dataset(dataset)
        normalized_task = normalize_layoutformerpp_task(task)
        condition_type = TASK_TO_CONDITION[normalized_task]
        self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
        self.task = str(normalized_task)
        self.condition_type = str(condition_type)
        defaults = TASK_DEFAULTS.get((normalized_dataset, condition_type), {})
        if max_position_embeddings is None:
            max_position_embeddings = int(defaults.get("max_position_embeddings", 150))
        if decode_max_length is None:
            decode_max_length = int(defaults.get("decode_max_length", 120))
        if eval_seed is None:
            eval_seed = int(defaults.get("eval_seed", 100))

        normalized_id2label = (
            {int(key): str(value) for key, value in id2label.items()}
            if id2label is not None
            else None
        )

        self.vocab_size = vocab_size
        self.max_position_embeddings = max_position_embeddings
        self.d_model = d_model
        self.encoder_layers = encoder_layers
        self.decoder_layers = (
            decoder_layers if decoder_layers is not None else encoder_layers
        )
        self.encoder_attention_heads = encoder_attention_heads
        self.decoder_attention_heads = (
            decoder_attention_heads
            if decoder_attention_heads is not None
            else encoder_attention_heads
        )
        self.dropout = dropout
        self.dim_feedforward = (
            dim_feedforward if dim_feedforward is not None else d_model * 4
        )
        self.share_embedding = share_embedding
        self.max_num_elements = max_num_elements
        self.bbox_format = str(normalize_box_format(bbox_format))
        self.default_box_format = str(normalize_box_format(default_box_format))

        self.discrete_x_grid = discrete_x_grid
        self.discrete_y_grid = discrete_y_grid
        self.add_sep_token = add_sep_token
        self.sort_by_dict = sort_by_dict
        self.add_task_embedding = add_task_embedding
        self.add_task_prompt_token_in_model = add_task_prompt_token_in_model
        self.num_task_prompt_token = num_task_prompt_token
        self.task_id = task_id

        self.decode_max_length = decode_max_length
        self.eval_seed = eval_seed
        self.gen_t_add_unk_token = gen_t_add_unk_token
        self.gen_ts_add_unk_token = gen_ts_add_unk_token
        self.gen_r_add_unk_token = gen_r_add_unk_token
        self.gen_r_compact = gen_r_compact

        super().__init__(
            transformers_version=transformers_version,
            architectures=architectures,
            output_hidden_states=output_hidden_states,
            return_dict=return_dict,
            chunk_size_feed_forward=chunk_size_feed_forward,
            problem_type=problem_type,
            is_encoder_decoder=is_encoder_decoder,
            id2label=normalized_id2label,
            label2id=label2id,
            dtype=torch_dtype or dtype,
        )
        # Transformers v5 keeps model-specific token/vocabulary fields on the
        # subclass; only the common fields above belong in the base call.
        self.vocab_size = vocab_size
        self.bos_token_id = bos_token_id
        self.eos_token_id = eos_token_id
        self.pad_token_id = pad_token_id
        self.tie_word_embeddings = tie_word_embeddings
        if output_attentions is not None:
            self.output_attentions = output_attentions
        if task_specific_params is not None:
            self.task_specific_params = task_specific_params
        self.name_or_path = name_or_path if _name_or_path is None else _name_or_path
        self._commit_hash = _commit_hash
        self._attn_implementation = attn_implementation
        # Transformers v5 passes legacy and model-specific fields through the
        # tolerant config-loading path; preserve them as ordinary attributes.
        for key, value in kwargs.items():
            setattr(self, key, value)

__init__

__init__(
    vocab_size: int = 0,
    max_position_embeddings: int | None = None,
    d_model: int = 512,
    encoder_layers: int = 8,
    decoder_layers: int | None = None,
    encoder_attention_heads: int = 8,
    decoder_attention_heads: int | None = None,
    dropout: float = 0.1,
    dim_feedforward: int | None = None,
    share_embedding: bool = True,
    dataset: DatasetName | str = DatasetName.rico25,
    task: LayoutFormerPPTask
    | ConditionType
    | str = LayoutFormerPPTask.gen_t,
    max_num_elements: int = 20,
    bbox_format: BoxFormat | str = BoxFormat.ltwh,
    default_box_format: BoxFormat | str = BoxFormat.xywh,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    add_sep_token: bool = True,
    sort_by_dict: bool = True,
    add_task_embedding: bool = False,
    add_task_prompt_token_in_model: bool = False,
    num_task_prompt_token: int = 1,
    task_id: int | None = None,
    decode_max_length: int | None = None,
    eval_seed: int | None = None,
    gen_t_add_unk_token: bool = False,
    gen_ts_add_unk_token: bool = False,
    gen_r_add_unk_token: bool = False,
    gen_r_compact: bool = False,
    bos_token_id: int = 0,
    eos_token_id: int = 1,
    pad_token_id: int = 2,
    is_encoder_decoder: bool = True,
    condition_type: ConditionType | str | None = None,
    model_type: str | None = None,
    transformers_version: str | None = None,
    architectures: list[str] | None = None,
    output_hidden_states: bool | None = False,
    output_attentions: bool | None = False,
    return_dict: bool | None = True,
    chunk_size_feed_forward: int = 0,
    problem_type: Literal[
        "regression",
        "single_label_classification",
        "multi_label_classification",
    ]
    | None = None,
    id2label: dict[int | str, str] | None = None,
    label2id: dict[str, int] | None = None,
    torch_dtype: str | None = None,
    dtype: str | None = None,
    tie_word_embeddings: bool = True,
    task_specific_params: dict[
        str,
        str
        | int
        | float
        | bool
        | list[str | int | float | bool],
    ]
    | None = None,
    name_or_path: str = "",
    _name_or_path: str | None = None,
    _commit_hash: str | None = None,
    attn_implementation: str | None = None,
    **kwargs: str | int | float | bool | None,
) -> None

Initialize architecture and task-specific generation defaults.

Source code in models/layoutformerpp/src/layoutformerpp/configuration_layoutformerpp.py
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
def __init__(
    self,
    vocab_size: int = 0,
    max_position_embeddings: int | None = None,
    d_model: int = 512,
    encoder_layers: int = 8,
    decoder_layers: int | None = None,
    encoder_attention_heads: int = 8,
    decoder_attention_heads: int | None = None,
    dropout: float = 0.1,
    dim_feedforward: int | None = None,
    share_embedding: bool = True,
    dataset: DatasetName | str = DatasetName.rico25,
    task: LayoutFormerPPTask | ConditionType | str = LayoutFormerPPTask.gen_t,
    max_num_elements: int = 20,
    bbox_format: BoxFormat | str = BoxFormat.ltwh,
    default_box_format: BoxFormat | str = BoxFormat.xywh,
    discrete_x_grid: int = 128,
    discrete_y_grid: int = 128,
    add_sep_token: bool = True,
    sort_by_dict: bool = True,
    add_task_embedding: bool = False,
    add_task_prompt_token_in_model: bool = False,
    num_task_prompt_token: int = 1,
    task_id: int | None = None,
    decode_max_length: int | None = None,
    eval_seed: int | None = None,
    gen_t_add_unk_token: bool = False,
    gen_ts_add_unk_token: bool = False,
    gen_r_add_unk_token: bool = False,
    gen_r_compact: bool = False,
    bos_token_id: int = 0,
    eos_token_id: int = 1,
    pad_token_id: int = 2,
    is_encoder_decoder: bool = True,
    condition_type: ConditionType | str | None = None,
    model_type: str | None = None,
    transformers_version: str | None = None,
    architectures: list[str] | None = None,
    output_hidden_states: bool | None = False,
    output_attentions: bool | None = False,
    return_dict: bool | None = True,
    chunk_size_feed_forward: int = 0,
    problem_type: Literal[
        "regression", "single_label_classification", "multi_label_classification"
    ]
    | None = None,
    id2label: dict[int | str, str] | None = None,
    label2id: dict[str, int] | None = None,
    torch_dtype: str | None = None,
    dtype: str | None = None,
    tie_word_embeddings: bool = True,
    task_specific_params: dict[
        str, str | int | float | bool | list[str | int | float | bool]
    ]
    | None = None,
    name_or_path: str = "",
    _name_or_path: str | None = None,
    _commit_hash: str | None = None,
    attn_implementation: str | None = None,
    **kwargs: str | int | float | bool | None,
) -> None:
    """Initialize architecture and task-specific generation defaults."""
    _ = (condition_type, model_type, transformers_version)
    normalized_dataset = normalize_layoutformerpp_dataset(dataset)
    normalized_task = normalize_layoutformerpp_task(task)
    condition_type = TASK_TO_CONDITION[normalized_task]
    self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
    self.task = str(normalized_task)
    self.condition_type = str(condition_type)
    defaults = TASK_DEFAULTS.get((normalized_dataset, condition_type), {})
    if max_position_embeddings is None:
        max_position_embeddings = int(defaults.get("max_position_embeddings", 150))
    if decode_max_length is None:
        decode_max_length = int(defaults.get("decode_max_length", 120))
    if eval_seed is None:
        eval_seed = int(defaults.get("eval_seed", 100))

    normalized_id2label = (
        {int(key): str(value) for key, value in id2label.items()}
        if id2label is not None
        else None
    )

    self.vocab_size = vocab_size
    self.max_position_embeddings = max_position_embeddings
    self.d_model = d_model
    self.encoder_layers = encoder_layers
    self.decoder_layers = (
        decoder_layers if decoder_layers is not None else encoder_layers
    )
    self.encoder_attention_heads = encoder_attention_heads
    self.decoder_attention_heads = (
        decoder_attention_heads
        if decoder_attention_heads is not None
        else encoder_attention_heads
    )
    self.dropout = dropout
    self.dim_feedforward = (
        dim_feedforward if dim_feedforward is not None else d_model * 4
    )
    self.share_embedding = share_embedding
    self.max_num_elements = max_num_elements
    self.bbox_format = str(normalize_box_format(bbox_format))
    self.default_box_format = str(normalize_box_format(default_box_format))

    self.discrete_x_grid = discrete_x_grid
    self.discrete_y_grid = discrete_y_grid
    self.add_sep_token = add_sep_token
    self.sort_by_dict = sort_by_dict
    self.add_task_embedding = add_task_embedding
    self.add_task_prompt_token_in_model = add_task_prompt_token_in_model
    self.num_task_prompt_token = num_task_prompt_token
    self.task_id = task_id

    self.decode_max_length = decode_max_length
    self.eval_seed = eval_seed
    self.gen_t_add_unk_token = gen_t_add_unk_token
    self.gen_ts_add_unk_token = gen_ts_add_unk_token
    self.gen_r_add_unk_token = gen_r_add_unk_token
    self.gen_r_compact = gen_r_compact

    super().__init__(
        transformers_version=transformers_version,
        architectures=architectures,
        output_hidden_states=output_hidden_states,
        return_dict=return_dict,
        chunk_size_feed_forward=chunk_size_feed_forward,
        problem_type=problem_type,
        is_encoder_decoder=is_encoder_decoder,
        id2label=normalized_id2label,
        label2id=label2id,
        dtype=torch_dtype or dtype,
    )
    # Transformers v5 keeps model-specific token/vocabulary fields on the
    # subclass; only the common fields above belong in the base call.
    self.vocab_size = vocab_size
    self.bos_token_id = bos_token_id
    self.eos_token_id = eos_token_id
    self.pad_token_id = pad_token_id
    self.tie_word_embeddings = tie_word_embeddings
    if output_attentions is not None:
        self.output_attentions = output_attentions
    if task_specific_params is not None:
        self.task_specific_params = task_specific_params
    self.name_or_path = name_or_path if _name_or_path is None else _name_or_path
    self._commit_hash = _commit_hash
    self._attn_implementation = attn_implementation
    # Transformers v5 passes legacy and model-specific fields through the
    # tolerant config-loading path; preserve them as ordinary attributes.
    for key, value in kwargs.items():
        setattr(self, key, value)

conversion

Checkpoint conversion helpers for LayoutFormer++.

DatasetCardMetadata

Bases: TypedDict

Hub-facing metadata for one LayoutFormer++ dataset.

Source code in models/layoutformerpp/src/layoutformerpp/conversion.py
37
38
39
40
41
class DatasetCardMetadata(TypedDict):
    """Hub-facing metadata for one LayoutFormer++ dataset."""

    hub_slug: str
    dataset_id: str

layoutformerpp_hub_id

layoutformerpp_hub_id(
    dataset: DatasetName | str,
    task: LayoutFormerPPTask | ConditionType | str,
) -> str

Return the task-specific Hub id for a converted checkpoint.

Source code in models/layoutformerpp/src/layoutformerpp/conversion.py
56
57
58
59
60
61
62
63
64
65
66
def layoutformerpp_hub_id(
    dataset: DatasetName | str,
    task: LayoutFormerPPTask | ConditionType | str,
) -> str:
    """Return the task-specific Hub id for a converted checkpoint."""
    normalized_task = normalize_layoutformerpp_task(task)
    suffix = TASK_TO_CONDITION[normalized_task].replace("_", "-")
    return (
        "creative-graphic-design/layoutformerpp-"
        f"{layoutformerpp_dataset_slug(dataset)}-{suffix}"
    )

load_original_state_dict

load_original_state_dict(
    path: Path,
) -> dict[str, Shaped[torch.Tensor, "..."]]

Load a published LayoutFormer++ checkpoint and strip DDP prefixes.

Source code in models/layoutformerpp/src/layoutformerpp/conversion.py
69
70
71
72
73
74
75
76
def load_original_state_dict(path: Path) -> dict[str, Shaped[torch.Tensor, "..."]]:
    """Load a published LayoutFormer++ checkpoint and strip DDP prefixes."""
    raw = torch.load(path, map_location="cpu")
    state = raw.get("state_dict", raw) if isinstance(raw, dict) else raw
    if not isinstance(state, dict):
        raise TypeError("checkpoint must contain a state-dict mapping")

    return {str(key).removeprefix("module."): value for key, value in state.items()}

layoutformerpp_model_card

layoutformerpp_model_card(
    *,
    dataset: DatasetName | str,
    task: LayoutFormerPPTask | ConditionType | str,
    parity_metrics: list[ParityMetricInput] | None = None,
) -> ModelCard

Build a Hub model card for one LayoutFormer++ checkpoint.

Source code in models/layoutformerpp/src/layoutformerpp/conversion.py
 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
def layoutformerpp_model_card(
    *,
    dataset: DatasetName | str,
    task: LayoutFormerPPTask | ConditionType | str,
    parity_metrics: list[ParityMetricInput] | None = None,
) -> ModelCard:
    """Build a Hub model card for one LayoutFormer++ checkpoint."""
    normalized_dataset = normalize_layoutformerpp_dataset(dataset)
    normalized_task = normalize_layoutformerpp_task(task)
    model_id = layoutformerpp_hub_id(normalized_dataset, normalized_task)
    condition = TASK_TO_CONDITION[normalized_task]
    dataset_metadata = DATASET_CARD_METADATA[normalized_dataset]
    dataset_slug = dataset_metadata["hub_slug"]
    dataset_id = dataset_metadata["dataset_id"]
    metrics = parity_metrics or [
        ParityMetric(
            dataset=f"{dataset_slug}_{normalized_task}",
            tokenizer_exact="vocab.json exact",
            deterministic_exact="not run",
            logits_max_abs=0.0,
            logits_max_rel=0.0,
        )
    ]
    how_to_use = f"""
from layoutformerpp import LayoutFormerPPPipeline

pipe = LayoutFormerPPPipeline.from_pretrained("{model_id}")

out = pipe(condition_type="{condition}", labels=[["Text"]], max_length=8)
print(out.bbox, out.labels, out.mask)
"""
    return build_layout_model_card(
        model_id=model_id,
        model_name=f"LayoutFormer++ {dataset_slug} {normalized_task}",
        dataset_ids=[dataset_id],
        license="mit",
        library_name="transformers",
        pipeline_tag="other",
        tags=[
            "layout-generation",
            "layoutformer++",
            "transformers",
            dataset_slug,
            str(normalized_task),
        ],
        model_details=(
            "Transformers-format conversion of the LayoutFormer++ autoregressive "
            f"layout transformer checkpoint for `{dataset_slug}` / "
            f"`{normalized_task}`. The "
            "processor returns normalized center `xywh` boxes, dataset-local "
            "labels, masks, and `id2label` using the shared `laygen.common` schema."
        ),
        intended_uses=(
            "Use this checkpoint to reproduce and evaluate LayoutFormer++ "
            f"`{dataset_slug}` / `{normalized_task}` conditional graphic layout "
            "generation in a Transformers-style API."
        ),
        limitations=(
            "This conversion preserves the released LayoutFormer++ checkpoint "
            "contract and inherits the dataset and task coverage of the original "
            "research release. Local reference parity covers tokenizer behavior, "
            "teacher-forced logits, reference greedy/top-k generation, and constrained "
            "label or label-size generation for every public `rico` and `publaynet` "
            "LayoutFormer++ task checkpoint. This checkpoint is not intended for "
            "OCR, document understanding, or unreviewed production design decisions."
        ),
        how_to_use=how_to_use,
        training_data=(
            f"The original checkpoint was trained on `{dataset_id}` using the "
            "preprocessed LayoutFormer++ release artifacts from `jzy124/LayoutFormer`."
        ),
        parity_metrics=metrics,
        citation_bibtex=LAYOUTFORMERPP_BIBTEX,
        original_implementation_url=(
            "https://github.com/microsoft/LayoutGeneration/tree/main/LayoutFormer%2B%2B"
        ),
    )

write_layoutformerpp_model_card

write_layoutformerpp_model_card(
    output_dir: Path,
    *,
    dataset: DatasetName | str,
    task: LayoutFormerPPTask | ConditionType | str,
    parity_metrics: list[ParityMetricInput] | None = None,
) -> Path

Write the checkpoint README model card next to converted weights.

Source code in models/layoutformerpp/src/layoutformerpp/conversion.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def write_layoutformerpp_model_card(
    output_dir: Path,
    *,
    dataset: DatasetName | str,
    task: LayoutFormerPPTask | ConditionType | str,
    parity_metrics: list[ParityMetricInput] | None = None,
) -> Path:
    """Write the checkpoint README model card next to converted weights."""
    output_dir.mkdir(parents=True, exist_ok=True)
    readme_path = output_dir / "README.md"
    readme_path.write_text(
        str(
            layoutformerpp_model_card(
                dataset=dataset,
                task=task,
                parity_metrics=parity_metrics,
            )
        ),
        encoding="utf-8",
    )
    return readme_path

geometry

Geometry helpers for LayoutFormer++ discrete ltwh boxes.

discretize_ltwh

discretize_ltwh(
    bbox: Float[Tensor, "... 4"],
    *,
    x_grid: int = 128,
    y_grid: int = 128,
) -> Int[torch.Tensor, "... 4"]

Convert normalized ltwh values to LayoutFormer++ integer bins.

Source code in models/layoutformerpp/src/layoutformerpp/geometry.py
16
17
18
19
20
21
22
23
24
25
26
def discretize_ltwh(
    bbox: Float[torch.Tensor, "... 4"], *, x_grid: int = 128, y_grid: int = 128
) -> Int[torch.Tensor, "... 4"]:
    """Convert normalized ltwh values to LayoutFormer++ integer bins."""
    grids = (
        torch.tensor(
            (x_grid, y_grid, x_grid, y_grid), dtype=bbox.dtype, device=bbox.device
        )
        - 1
    )
    return torch.floor(bbox.clamp(0.0, 1.0) * grids).long().clamp_min(0)

continuize_ltwh

continuize_ltwh(
    ids: Int[Tensor, "... 4"],
    *,
    x_grid: int = 128,
    y_grid: int = 128,
) -> Float[torch.Tensor, "... 4"]

Convert LayoutFormer++ integer bins back to normalized ltwh.

Source code in models/layoutformerpp/src/layoutformerpp/geometry.py
29
30
31
32
33
34
35
36
37
38
39
def continuize_ltwh(
    ids: Int[torch.Tensor, "... 4"], *, x_grid: int = 128, y_grid: int = 128
) -> Float[torch.Tensor, "... 4"]:
    """Convert LayoutFormer++ integer bins back to normalized ltwh."""
    grids = (
        torch.tensor(
            (x_grid, y_grid, x_grid, y_grid), dtype=torch.float32, device=ids.device
        )
        - 1
    )
    return ids.float().clamp_min(0) / grids

public_to_discrete_ltwh

public_to_discrete_ltwh(
    bbox: Float[Tensor, "... 4"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    x_grid: int = 128,
    y_grid: int = 128,
) -> Int[torch.Tensor, "... 4"]

Convert public normalized boxes to internal discrete ltwh tokens.

Source code in models/layoutformerpp/src/layoutformerpp/geometry.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def public_to_discrete_ltwh(
    bbox: Float[torch.Tensor, "... 4"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    x_grid: int = 128,
    y_grid: int = 128,
) -> Int[torch.Tensor, "... 4"]:
    """Convert public normalized boxes to internal discrete ltwh tokens."""
    fmt = normalize_box_format(box_format)
    ltwh = xywh_to_ltwh(bbox.float()) if fmt is BoxFormat.xywh else bbox.float()
    if fmt is not BoxFormat.xywh and fmt is not BoxFormat.ltwh:
        raise ValueError(f"Unsupported box_format: {box_format}")

    return discretize_ltwh(ltwh, x_grid=x_grid, y_grid=y_grid)

discrete_ltwh_to_public

discrete_ltwh_to_public(
    ids: Int[Tensor, "... 4"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    x_grid: int = 128,
    y_grid: int = 128,
) -> Float[torch.Tensor, "... 4"]

Convert internal discrete ltwh tokens to public normalized boxes.

Source code in models/layoutformerpp/src/layoutformerpp/geometry.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def discrete_ltwh_to_public(
    ids: Int[torch.Tensor, "... 4"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    x_grid: int = 128,
    y_grid: int = 128,
) -> Float[torch.Tensor, "... 4"]:
    """Convert internal discrete ltwh tokens to public normalized boxes."""
    ltwh = continuize_ltwh(ids, x_grid=x_grid, y_grid=y_grid)
    fmt = normalize_box_format(box_format)
    if fmt is BoxFormat.xywh:
        return ltwh_to_xywh(ltwh).clamp(0.0, 1.0)
    if fmt is BoxFormat.ltwh:
        return ltwh.clamp(0.0, 1.0)
    raise ValueError(f"Unsupported box_format: {box_format}")

modeling_layoutformerpp

PyTorch model wrapper for LayoutFormer++.

PositionalEncoding

Bases: Module

Learned positional embeddings matching the original implementation.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class PositionalEncoding(nn.Module):
    """Learned positional embeddings matching the original implementation."""

    def __init__(self, d_model: int, dropout: float = 0.1, max_len: int = 512) -> None:
        """Initialize learned position tokens."""
        super().__init__()
        self.dropout = nn.Dropout(p=dropout)
        self.pos_token = nn.Parameter(torch.rand(max_len, 1, d_model))

    def forward(
        self, x: Float[torch.Tensor, "seq batch channels"]
    ) -> Float[torch.Tensor, "seq batch channels"]:
        """Add learned position tokens to `(seq, batch, hidden)` input."""
        return self.dropout(x + self.pos_token[: x.size(0)])

__init__

__init__(
    d_model: int, dropout: float = 0.1, max_len: int = 512
) -> None

Initialize learned position tokens.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
42
43
44
45
46
def __init__(self, d_model: int, dropout: float = 0.1, max_len: int = 512) -> None:
    """Initialize learned position tokens."""
    super().__init__()
    self.dropout = nn.Dropout(p=dropout)
    self.pos_token = nn.Parameter(torch.rand(max_len, 1, d_model))

forward

forward(
    x: Float[Tensor, "seq batch channels"],
) -> Float[torch.Tensor, "seq batch channels"]

Add learned position tokens to (seq, batch, hidden) input.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
48
49
50
51
52
def forward(
    self, x: Float[torch.Tensor, "seq batch channels"]
) -> Float[torch.Tensor, "seq batch channels"]:
    """Add learned position tokens to `(seq, batch, hidden)` input."""
    return self.dropout(x + self.pos_token[: x.size(0)])

LayoutFormerPPForConditionalGeneration

Bases: PreTrainedModel

Transformers PreTrainedModel with checkpoint-compatible module names.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.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
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
class LayoutFormerPPForConditionalGeneration(PreTrainedModel):
    """Transformers `PreTrainedModel` with checkpoint-compatible module names."""

    config_class = LayoutFormerPPConfig
    base_model_prefix = "layoutformerpp"
    main_input_name = "input_ids"
    _tied_weights_keys = {
        "dec_embedding.weight": "enc_embedding.weight",
        "out.weight": "dec_embedding.weight",
    }

    def __init__(self, config: LayoutFormerPPConfig) -> None:
        """Initialize checkpoint-compatible encoder/decoder modules."""
        super().__init__(config)
        self.d_model = config.d_model
        self.vocab_size = config.vocab_size
        self.bos_token_id = int(config.bos_token_id)
        self.pad_token_id = int(config.pad_token_id)
        self.eos_token_id = int(config.eos_token_id)

        self.enc_embedding = nn.Embedding(config.vocab_size, config.d_model)
        self.enc_pos_embedding = PositionalEncoding(
            config.d_model, config.dropout, max_len=config.max_position_embeddings
        )
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=config.d_model,
            nhead=config.encoder_attention_heads,
            dropout=config.dropout,
            dim_feedforward=config.dim_feedforward,
        )
        self.encoder = nn.TransformerEncoder(
            encoder_layer, num_layers=config.encoder_layers
        )

        self.dec_embedding = (
            self.enc_embedding
            if config.share_embedding
            else nn.Embedding(config.vocab_size, config.d_model)
        )
        self.dec_pos_embedding = PositionalEncoding(
            config.d_model, config.dropout, max_len=config.max_position_embeddings
        )
        decoder_layer = nn.TransformerDecoderLayer(
            d_model=config.d_model,
            nhead=config.decoder_attention_heads,
            dropout=config.dropout,
            dim_feedforward=config.dim_feedforward,
        )
        self.decoder = nn.TransformerDecoder(
            decoder_layer, num_layers=config.decoder_layers
        )
        self.out = nn.Linear(config.d_model, config.vocab_size, bias=False)
        self.out.weight = self.dec_embedding.weight

        self.task_embedding = None
        if config.add_task_embedding:
            self.task_embedding = nn.Embedding(6, config.d_model)

        self.task_prompt_embed = None
        if config.add_task_prompt_token_in_model:
            self.num_task_prompt_token = config.num_task_prompt_token
            self.task_prompt_embed = nn.Parameter(
                torch.empty(6, config.num_task_prompt_token, config.d_model)
            )
            nn.init.normal_(self.task_prompt_embed)
        self.tie_weights()
        self.all_tied_weights_keys = dict(self._tied_weights_keys)

    def encode(
        self,
        input_ids: Int[torch.Tensor, "batch tokens"],
        padding_mask: Bool[torch.Tensor, "batch tokens"],
        task_ids: Int[torch.Tensor, "batch"] | None = None,
    ) -> tuple[
        Float[torch.Tensor, "seq batch channels"],
        Bool[torch.Tensor, "batch seq"],
    ]:
        """Encode input token ids with optional task prompt embeddings."""
        if self.task_prompt_embed is not None:
            if task_ids is None:
                raise ValueError(
                    "task_ids are required when task prompt embeddings are enabled"
                )

            x = self.enc_embedding(input_ids)
            prompts = self.task_prompt_embed[task_ids]
            x = torch.cat([prompts, x], dim=1).permute(1, 0, 2)
            bsz = input_ids.size(0)
            prompt_mask = padding_mask.new_zeros(
                (bsz, self.num_task_prompt_token)
            ).bool()
            enc_padding_mask = torch.cat([prompt_mask, padding_mask], dim=1)
        else:
            x = self.enc_embedding(input_ids).permute(1, 0, 2)
            enc_padding_mask = padding_mask
        enc_hs = self.encoder(
            self.enc_pos_embedding(x), src_key_padding_mask=enc_padding_mask
        )
        if self.task_embedding is not None:
            if task_ids is None:
                raise ValueError(
                    "task_ids are required when task embeddings are enabled"
                )

            enc_hs = enc_hs + self.task_embedding(task_ids).unsqueeze(0)
        return enc_hs, enc_padding_mask

    def prepare_decoder_input_ids_from_labels(
        self, labels: Int[torch.Tensor, "batch tokens"]
    ) -> Int[torch.Tensor, "batch tokens"]:
        """Shift labels right and prepend BOS."""
        bos = labels.new_full((labels.size(0), 1), self.bos_token_id)
        return torch.cat([bos, labels[:, :-1]], dim=1)

    def forward(
        self,
        input_ids: Int[torch.Tensor, "batch tokens"],
        attention_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        labels: Int[torch.Tensor, "batch tokens"] | None = None,
        decoder_input_ids: Int[torch.Tensor, "batch tokens"] | None = None,
        task_ids: Int[torch.Tensor, "batch"] | None = None,
        return_dict: bool | None = None,
    ) -> Seq2SeqLMOutput | tuple[Float[torch.Tensor, "..."], ...]:
        """Run teacher-forced LayoutFormer++ decoding."""
        if attention_mask is None:
            attention_mask = input_ids.ne(self.pad_token_id)
        padding_mask = ~attention_mask.bool()
        if decoder_input_ids is None:
            if labels is None:
                raise ValueError("decoder_input_ids or labels must be provided")

            decoder_input_ids = self.prepare_decoder_input_ids_from_labels(labels)
        enc_hs, enc_padding_mask = self.encode(input_ids, padding_mask, task_ids)
        dec_input = self.dec_pos_embedding(
            self.dec_embedding(decoder_input_ids).permute(1, 0, 2)
        )
        tgt_mask = generate_square_subsequent_mask(dec_input.size(0), dec_input.device)
        y = self.decoder(
            tgt=dec_input,
            memory=enc_hs,
            tgt_mask=tgt_mask,
            memory_key_padding_mask=enc_padding_mask,
        )
        logits = self.out(y.permute(1, 0, 2))
        loss = None
        if labels is not None:
            targets = labels.clone()
            targets[targets == self.pad_token_id] = -100
            loss = F.cross_entropy(
                logits.reshape(-1, logits.size(-1)),
                targets.reshape(-1),
                ignore_index=-100,
            )
        if return_dict is False:
            return (logits,) if loss is None else (loss, logits)
        return Seq2SeqLMOutput(loss=cast(torch.FloatTensor | None, loss), logits=logits)

    @torch.no_grad()
    def _generate_sequences(
        self,
        input_ids: Int[torch.Tensor, "batch tokens"],
        attention_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        *,
        max_length: int | None = None,
        do_sample: bool = False,
        top_k: int = 10,
        temperature: float = 0.7,
        generation_constraint_fn: Callable[
            [int, int, Int[torch.Tensor, "tokens"]], tuple[list[int], int | None]
        ]
        | None = None,
        task_ids: Int[torch.Tensor, "batch"] | None = None,
        generator: torch.Generator | None = None,
    ) -> Int[torch.Tensor, "batch tokens"]:
        """Run the reference greedy/top-k autoregressive loop."""
        if attention_mask is None:
            attention_mask = input_ids.ne(self.pad_token_id)
        padding_mask = ~attention_mask.bool()
        max_length = max_length or self.config.decode_max_length
        enc_hs, enc_padding_mask = self.encode(input_ids, padding_mask, task_ids)
        bsz = input_ids.size(0)
        stop = input_ids.new_zeros(bsz, dtype=torch.bool)
        pred_ids = input_ids.new_full((bsz, 1), self.bos_token_id)
        outs: list[Int[torch.Tensor, "batch"]] = []
        for idx in range(max_length):
            dec_input = self.dec_pos_embedding(
                self.dec_embedding(pred_ids).permute(1, 0, 2)
            )
            tgt_mask = generate_square_subsequent_mask(idx + 1, input_ids.device)
            y = self.decoder(
                tgt=dec_input,
                memory=enc_hs,
                tgt_mask=tgt_mask,
                memory_key_padding_mask=enc_padding_mask,
            )
            logits = self.out(y.permute(1, 0, 2)[:, -1, :])
            if generation_constraint_fn is not None:
                current = (
                    torch.stack(outs, dim=1) if outs else input_ids.new_empty((bsz, 0))
                )
                for batch_idx in range(bsz):
                    allowed, _ = generation_constraint_fn(
                        batch_idx, idx, current[batch_idx]
                    )
                    mask = torch.ones(
                        logits.size(-1), dtype=torch.bool, device=logits.device
                    )
                    mask[allowed] = False
                    logits[batch_idx].masked_fill_(mask, -math.inf)
            if do_sample:
                probs = F.softmax(top_k_logits(logits / temperature, top_k), dim=-1)
                curr = torch.multinomial(
                    probs,
                    num_samples=1,
                    generator=generator,
                ).squeeze(-1)
            else:
                curr = torch.argmax(logits, dim=-1)
            eos = curr.eq(self.eos_token_id)
            curr[stop] = self.pad_token_id
            outs.append(curr)
            pred_ids = torch.cat([pred_ids, curr.unsqueeze(1)], dim=1)
            stop = torch.logical_or(stop, eos)
            if bool(torch.all(stop)):
                break
        return torch.stack(outs, dim=1)

__init__

__init__(config: LayoutFormerPPConfig) -> None

Initialize checkpoint-compatible encoder/decoder modules.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
 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
def __init__(self, config: LayoutFormerPPConfig) -> None:
    """Initialize checkpoint-compatible encoder/decoder modules."""
    super().__init__(config)
    self.d_model = config.d_model
    self.vocab_size = config.vocab_size
    self.bos_token_id = int(config.bos_token_id)
    self.pad_token_id = int(config.pad_token_id)
    self.eos_token_id = int(config.eos_token_id)

    self.enc_embedding = nn.Embedding(config.vocab_size, config.d_model)
    self.enc_pos_embedding = PositionalEncoding(
        config.d_model, config.dropout, max_len=config.max_position_embeddings
    )
    encoder_layer = nn.TransformerEncoderLayer(
        d_model=config.d_model,
        nhead=config.encoder_attention_heads,
        dropout=config.dropout,
        dim_feedforward=config.dim_feedforward,
    )
    self.encoder = nn.TransformerEncoder(
        encoder_layer, num_layers=config.encoder_layers
    )

    self.dec_embedding = (
        self.enc_embedding
        if config.share_embedding
        else nn.Embedding(config.vocab_size, config.d_model)
    )
    self.dec_pos_embedding = PositionalEncoding(
        config.d_model, config.dropout, max_len=config.max_position_embeddings
    )
    decoder_layer = nn.TransformerDecoderLayer(
        d_model=config.d_model,
        nhead=config.decoder_attention_heads,
        dropout=config.dropout,
        dim_feedforward=config.dim_feedforward,
    )
    self.decoder = nn.TransformerDecoder(
        decoder_layer, num_layers=config.decoder_layers
    )
    self.out = nn.Linear(config.d_model, config.vocab_size, bias=False)
    self.out.weight = self.dec_embedding.weight

    self.task_embedding = None
    if config.add_task_embedding:
        self.task_embedding = nn.Embedding(6, config.d_model)

    self.task_prompt_embed = None
    if config.add_task_prompt_token_in_model:
        self.num_task_prompt_token = config.num_task_prompt_token
        self.task_prompt_embed = nn.Parameter(
            torch.empty(6, config.num_task_prompt_token, config.d_model)
        )
        nn.init.normal_(self.task_prompt_embed)
    self.tie_weights()
    self.all_tied_weights_keys = dict(self._tied_weights_keys)

encode

encode(
    input_ids: Int[Tensor, "batch tokens"],
    padding_mask: Bool[Tensor, "batch tokens"],
    task_ids: Int[Tensor, "batch"] | None = None,
) -> tuple[
    Float[torch.Tensor, "seq batch channels"],
    Bool[torch.Tensor, "batch seq"],
]

Encode input token ids with optional task prompt embeddings.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
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
def encode(
    self,
    input_ids: Int[torch.Tensor, "batch tokens"],
    padding_mask: Bool[torch.Tensor, "batch tokens"],
    task_ids: Int[torch.Tensor, "batch"] | None = None,
) -> tuple[
    Float[torch.Tensor, "seq batch channels"],
    Bool[torch.Tensor, "batch seq"],
]:
    """Encode input token ids with optional task prompt embeddings."""
    if self.task_prompt_embed is not None:
        if task_ids is None:
            raise ValueError(
                "task_ids are required when task prompt embeddings are enabled"
            )

        x = self.enc_embedding(input_ids)
        prompts = self.task_prompt_embed[task_ids]
        x = torch.cat([prompts, x], dim=1).permute(1, 0, 2)
        bsz = input_ids.size(0)
        prompt_mask = padding_mask.new_zeros(
            (bsz, self.num_task_prompt_token)
        ).bool()
        enc_padding_mask = torch.cat([prompt_mask, padding_mask], dim=1)
    else:
        x = self.enc_embedding(input_ids).permute(1, 0, 2)
        enc_padding_mask = padding_mask
    enc_hs = self.encoder(
        self.enc_pos_embedding(x), src_key_padding_mask=enc_padding_mask
    )
    if self.task_embedding is not None:
        if task_ids is None:
            raise ValueError(
                "task_ids are required when task embeddings are enabled"
            )

        enc_hs = enc_hs + self.task_embedding(task_ids).unsqueeze(0)
    return enc_hs, enc_padding_mask

prepare_decoder_input_ids_from_labels

prepare_decoder_input_ids_from_labels(
    labels: Int[Tensor, "batch tokens"],
) -> Int[torch.Tensor, "batch tokens"]

Shift labels right and prepend BOS.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
162
163
164
165
166
167
def prepare_decoder_input_ids_from_labels(
    self, labels: Int[torch.Tensor, "batch tokens"]
) -> Int[torch.Tensor, "batch tokens"]:
    """Shift labels right and prepend BOS."""
    bos = labels.new_full((labels.size(0), 1), self.bos_token_id)
    return torch.cat([bos, labels[:, :-1]], dim=1)

forward

forward(
    input_ids: Int[Tensor, "batch tokens"],
    attention_mask: Bool[Tensor, "batch tokens"]
    | None = None,
    labels: Int[Tensor, "batch tokens"] | None = None,
    decoder_input_ids: Int[Tensor, "batch tokens"]
    | None = None,
    task_ids: Int[Tensor, "batch"] | None = None,
    return_dict: bool | None = None,
) -> (
    Seq2SeqLMOutput | tuple[Float[torch.Tensor, "..."], ...]
)

Run teacher-forced LayoutFormer++ decoding.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def forward(
    self,
    input_ids: Int[torch.Tensor, "batch tokens"],
    attention_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    labels: Int[torch.Tensor, "batch tokens"] | None = None,
    decoder_input_ids: Int[torch.Tensor, "batch tokens"] | None = None,
    task_ids: Int[torch.Tensor, "batch"] | None = None,
    return_dict: bool | None = None,
) -> Seq2SeqLMOutput | tuple[Float[torch.Tensor, "..."], ...]:
    """Run teacher-forced LayoutFormer++ decoding."""
    if attention_mask is None:
        attention_mask = input_ids.ne(self.pad_token_id)
    padding_mask = ~attention_mask.bool()
    if decoder_input_ids is None:
        if labels is None:
            raise ValueError("decoder_input_ids or labels must be provided")

        decoder_input_ids = self.prepare_decoder_input_ids_from_labels(labels)
    enc_hs, enc_padding_mask = self.encode(input_ids, padding_mask, task_ids)
    dec_input = self.dec_pos_embedding(
        self.dec_embedding(decoder_input_ids).permute(1, 0, 2)
    )
    tgt_mask = generate_square_subsequent_mask(dec_input.size(0), dec_input.device)
    y = self.decoder(
        tgt=dec_input,
        memory=enc_hs,
        tgt_mask=tgt_mask,
        memory_key_padding_mask=enc_padding_mask,
    )
    logits = self.out(y.permute(1, 0, 2))
    loss = None
    if labels is not None:
        targets = labels.clone()
        targets[targets == self.pad_token_id] = -100
        loss = F.cross_entropy(
            logits.reshape(-1, logits.size(-1)),
            targets.reshape(-1),
            ignore_index=-100,
        )
    if return_dict is False:
        return (logits,) if loss is None else (loss, logits)
    return Seq2SeqLMOutput(loss=cast(torch.FloatTensor | None, loss), logits=logits)

generate_square_subsequent_mask

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

Create the causal decoder mask used by the checkpoint model.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
19
20
21
22
23
24
def generate_square_subsequent_mask(
    size: int, device: torch.device
) -> Float[torch.Tensor, "target target"]:
    """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, -math.inf).masked_fill(mask == 1, 0.0)

top_k_logits

top_k_logits(
    logits: Float[Tensor, "batch vocab"], k: int
) -> Float[torch.Tensor, "batch vocab"]

Mask logits outside the top-k set.

Source code in models/layoutformerpp/src/layoutformerpp/modeling_layoutformerpp.py
27
28
29
30
31
32
33
34
35
36
def top_k_logits(
    logits: Float[torch.Tensor, "batch vocab"], k: int
) -> Float[torch.Tensor, "batch vocab"]:
    """Mask logits outside the top-k set."""
    if k <= 0 or k >= logits.size(-1):
        return logits
    values = torch.topk(logits, k).values
    out = logits.clone()
    out[out < values[:, [-1]]] = -math.inf
    return out

pipeline_layoutformerpp

Pipeline wrapper for LayoutFormer++.

LayoutFormerPPPipeline

Bases: LayoutGenerationPipeline

Compose a LayoutFormer++ model and processor for layout generation.

Parameters:

Name Type Description Default
model LayoutFormerPPForConditionalGeneration

Converted LayoutFormer++ model.

required
processor LayoutFormerPPProcessor

Matching processor/tokenizer.

required
config LayoutFormerPPConfig | None

Optional root pipeline config. Defaults to model.config.

None

Examples:

>>> processor = LayoutFormerPPProcessor.from_config(dataset="rico", task="gen_t")
>>> config = LayoutFormerPPConfig(vocab_size=processor.tokenizer.vocab_size)
>>> pipe = LayoutFormerPPPipeline(
...     model=LayoutFormerPPForConditionalGeneration(config),
...     processor=processor,
... )
>>> pipe.config.model_type
'layoutformerpp'
Source code in models/layoutformerpp/src/layoutformerpp/pipeline_layoutformerpp.py
 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
class LayoutFormerPPPipeline(LayoutGenerationPipeline):
    """Compose a LayoutFormer++ model and processor for layout generation.

    Args:
        model: Converted LayoutFormer++ model.
        processor: Matching processor/tokenizer.
        config: Optional root pipeline config. Defaults to `model.config`.

    Examples:
        >>> processor = LayoutFormerPPProcessor.from_config(dataset="rico", task="gen_t")
        >>> config = LayoutFormerPPConfig(vocab_size=processor.tokenizer.vocab_size)
        >>> pipe = LayoutFormerPPPipeline(
        ...     model=LayoutFormerPPForConditionalGeneration(config),
        ...     processor=processor,
        ... )
        >>> pipe.config.model_type
        'layoutformerpp'
    """

    config_class: ClassVar[type[PretrainedConfig]] = LayoutFormerPPConfig
    component_specs: ClassVar[dict[str, PipelineComponentSpec]] = (
        model_processor_component_specs(
            model_loader=_load_model_component,
            processor_loader=_load_processor_component,
        )
    )

    config: LayoutFormerPPConfig
    model: LayoutFormerPPForConditionalGeneration
    processor: LayoutFormerPPProcessor

    def __init__(
        self,
        model: LayoutFormerPPForConditionalGeneration,
        processor: LayoutFormerPPProcessor,
        config: LayoutFormerPPConfig | None = None,
    ) -> None:
        """Initialize the pipeline with model and processor components."""
        super().__init__(config or model.config)
        self.config = config or model.config
        self.model = model
        self.processor = processor

    @classmethod
    def _from_pretrained_components(
        cls,
        *,
        config: PretrainedConfig,
        components: Mapping[str, PipelineComponent | None],
    ) -> LayoutFormerPPPipeline:
        """Build a pipeline from loaded root components."""
        return cls(
            config=cast(LayoutFormerPPConfig, config),
            model=cast(LayoutFormerPPForConditionalGeneration, components["model"]),
            processor=cast(LayoutFormerPPProcessor, components["processor"]),
        )

    @torch.no_grad()
    def __call__(
        self,
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: ConditionType | str = ConditionType.unconditional,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        bbox: LayoutFormerPPBBoxInput = None,
        mask: Bool[torch.Tensor, "batch elements"] | None = None,
        relations: list[list[tuple[int, int, int, int, int]]]
        | Int[torch.Tensor, "batch relations relation_attrs"]
        | 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,
        max_length: int | None = None,
        do_sample: bool | None = None,
        top_k: int = 10,
        temperature: float = 0.7,
    ) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:  # ty: ignore[invalid-method-override]
        """Generate layouts by encoding conditions, generating ids, and decoding.

        Args:
            batch_size: Number of layouts to generate when labels are omitted.
            seed: Convenience seed used only when `generator` is absent.
            generator: Optional PyTorch generator; takes precedence over `seed`.
            condition_type: Canonical condition type or supported alias.
            labels: Optional label conditions.
            bbox: Optional layout boxes for size/completion/refinement conditions.
            mask: Reserved public validity mask input.
            relations: Optional relation tuples for relation-conditioned checkpoints.
            num_elements: Reserved v1 interface argument.
            box_format: Input and output bounding-box format.
            normalized: Whether public boxes are normalized.
            canvas_size: Reserved v1 interface argument.
            num_inference_steps: Reserved v1 interface argument.
            output_type: Return `dataclass` or `dict`.
            return_intermediates: Reserved output detail flag.
            max_length: Optional token decode length override.
            do_sample: Optional sampling override.
            top_k: Top-k value used by the reference sampling loop.
            temperature: Sampling temperature.

        Returns:
            Layout generation output dataclass or dictionary.

        Raises:
            ValueError: If processor inputs are invalid.
        """
        _ = (num_elements, num_inference_steps, return_intermediates)
        encoded = self.processor(
            condition_type=condition_type,
            batch_size=batch_size,
            return_tensors="pt",
            labels=labels,
            bbox=bbox,
            mask=mask,
            relations=relations,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        model_device = next(self.model.parameters()).device
        input_ids = encoded["input_ids"].to(model_device)
        attention_mask = encoded["attention_mask"].to(model_device)
        condition = self.processor.normalize_condition_type(condition_type)
        generation_generator = self.prepare_generator(
            generator=generator,
            seed=seed,
            device=model_device,
        )
        default_do_sample = condition in {
            ConditionType.unconditional,
            ConditionType.completion,
        }
        sequences = self.model._generate_sequences(
            input_ids,
            attention_mask,
            max_length=max_length,
            do_sample=default_do_sample if do_sample is None else do_sample,
            top_k=top_k,
            temperature=temperature,
            generator=generation_generator,
        )
        return self.processor.post_process_layouts(
            sequences.cpu(),
            box_format=box_format,
            output_type=output_type,
        )

__init__

__init__(
    model: LayoutFormerPPForConditionalGeneration,
    processor: LayoutFormerPPProcessor,
    config: LayoutFormerPPConfig | None = None,
) -> None

Initialize the pipeline with model and processor components.

Source code in models/layoutformerpp/src/layoutformerpp/pipeline_layoutformerpp.py
104
105
106
107
108
109
110
111
112
113
114
def __init__(
    self,
    model: LayoutFormerPPForConditionalGeneration,
    processor: LayoutFormerPPProcessor,
    config: LayoutFormerPPConfig | None = None,
) -> None:
    """Initialize the pipeline with model and processor components."""
    super().__init__(config or model.config)
    self.config = config or model.config
    self.model = model
    self.processor = processor

__call__

__call__(
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: ConditionType
    | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[Tensor, "batch elements"] | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[Tensor, "batch relations relation_attrs"]
    | 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,
    max_length: int | None = None,
    do_sample: bool | None = None,
    top_k: int = 10,
    temperature: float = 0.7,
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict

Generate layouts by encoding conditions, generating ids, and decoding.

Parameters:

Name Type Description Default
batch_size int

Number of layouts to generate when labels are omitted.

1
seed int | None

Convenience seed used only when generator is absent.

None
generator Generator | None

Optional PyTorch generator; takes precedence over seed.

None
condition_type ConditionType | str

Canonical condition type or supported alias.

unconditional
labels list[list[int | str]] | Int[Tensor, 'batch elements'] | None

Optional label conditions.

None
bbox LayoutFormerPPBBoxInput

Optional layout boxes for size/completion/refinement conditions.

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

Reserved public validity mask input.

None
relations list[list[tuple[int, int, int, int, int]]] | Int[Tensor, 'batch relations relation_attrs'] | None

Optional relation tuples for relation-conditioned checkpoints.

None
num_elements int | list[int] | Int[Tensor, 'batch'] | None

Reserved v1 interface argument.

None
box_format BoxFormat | str

Input and output bounding-box format.

xywh
normalized bool

Whether public boxes are normalized.

True
canvas_size tuple[int, int] | None

Reserved v1 interface argument.

None
num_inference_steps int | None

Reserved v1 interface argument.

None
output_type OutputType | str

Return dataclass or dict.

dataclass
return_intermediates bool

Reserved output detail flag.

False
max_length int | None

Optional token decode length override.

None
do_sample bool | None

Optional sampling override.

None
top_k int

Top-k value used by the reference sampling loop.

10
temperature float

Sampling temperature.

0.7

Returns:

Type Description
LayoutGenerationOutput | LayoutFormerPPOutputDict

Layout generation output dataclass or dictionary.

Raises:

Type Description
ValueError

If processor inputs are invalid.

Source code in models/layoutformerpp/src/layoutformerpp/pipeline_layoutformerpp.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
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
@torch.no_grad()
def __call__(
    self,
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: ConditionType | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[torch.Tensor, "batch elements"] | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[torch.Tensor, "batch relations relation_attrs"]
    | 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,
    max_length: int | None = None,
    do_sample: bool | None = None,
    top_k: int = 10,
    temperature: float = 0.7,
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:  # ty: ignore[invalid-method-override]
    """Generate layouts by encoding conditions, generating ids, and decoding.

    Args:
        batch_size: Number of layouts to generate when labels are omitted.
        seed: Convenience seed used only when `generator` is absent.
        generator: Optional PyTorch generator; takes precedence over `seed`.
        condition_type: Canonical condition type or supported alias.
        labels: Optional label conditions.
        bbox: Optional layout boxes for size/completion/refinement conditions.
        mask: Reserved public validity mask input.
        relations: Optional relation tuples for relation-conditioned checkpoints.
        num_elements: Reserved v1 interface argument.
        box_format: Input and output bounding-box format.
        normalized: Whether public boxes are normalized.
        canvas_size: Reserved v1 interface argument.
        num_inference_steps: Reserved v1 interface argument.
        output_type: Return `dataclass` or `dict`.
        return_intermediates: Reserved output detail flag.
        max_length: Optional token decode length override.
        do_sample: Optional sampling override.
        top_k: Top-k value used by the reference sampling loop.
        temperature: Sampling temperature.

    Returns:
        Layout generation output dataclass or dictionary.

    Raises:
        ValueError: If processor inputs are invalid.
    """
    _ = (num_elements, num_inference_steps, return_intermediates)
    encoded = self.processor(
        condition_type=condition_type,
        batch_size=batch_size,
        return_tensors="pt",
        labels=labels,
        bbox=bbox,
        mask=mask,
        relations=relations,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    model_device = next(self.model.parameters()).device
    input_ids = encoded["input_ids"].to(model_device)
    attention_mask = encoded["attention_mask"].to(model_device)
    condition = self.processor.normalize_condition_type(condition_type)
    generation_generator = self.prepare_generator(
        generator=generator,
        seed=seed,
        device=model_device,
    )
    default_do_sample = condition in {
        ConditionType.unconditional,
        ConditionType.completion,
    }
    sequences = self.model._generate_sequences(
        input_ids,
        attention_mask,
        max_length=max_length,
        do_sample=default_do_sample if do_sample is None else do_sample,
        top_k=top_k,
        temperature=temperature,
        generator=generation_generator,
    )
    return self.processor.post_process_layouts(
        sequences.cpu(),
        box_format=box_format,
        output_type=output_type,
    )

processing_layoutformerpp

Processor for LayoutFormer++ conditions and generated sequences.

LayoutFormerPPProcessor

Bases: ProcessorMixin

Build LayoutFormer++ text inputs and parse generated layouts.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
 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
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
class LayoutFormerPPProcessor(ProcessorMixin):
    """Build LayoutFormer++ text inputs and parse generated layouts."""

    attributes = ["tokenizer"]
    tokenizer_class = "LayoutFormerPPTokenizer"

    def __init__(
        self,
        tokenizer: LayoutFormerPPTokenizer,
        dataset: DatasetName | str = DEFAULT_DATASET,
        task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
        add_sep_token: bool = True,
        x_grid: int = 128,
        y_grid: int = 128,
        id2label: dict[int, str] | None = None,
    ) -> None:
        """Initialize serializers, label maps, and tokenizer state."""
        self.tokenizer = tokenizer
        normalized_dataset = normalize_layoutformerpp_dataset(dataset)
        normalized_task = normalize_layoutformerpp_task(task)
        self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
        self.task = str(normalized_task)
        self.add_sep_token = add_sep_token
        self.x_grid = x_grid
        self.y_grid = y_grid
        labels = labels_for_dataset(normalized_dataset)
        self.id2label = (
            {int(key): str(value) for key, value in id2label.items()}
            if id2label is not None
            else dict(enumerate(labels))
        )
        self.public_id2label = dict(self.id2label)
        self.public_label2id = {
            value.lower(): key for key, value in self.public_id2label.items()
        }
        self.internal_id2label = {
            idx + 1: f"label_{idx + 1}" for idx in range(len(labels))
        }
        self.serializer = T5LayoutSequence(
            self.internal_id2label, add_sep_token=add_sep_token
        )
        self.gen_t_serializer = T5LayoutSequenceForGenT(
            self.internal_id2label, add_sep_token=add_sep_token
        )
        self.gen_r_serializer = T5LayoutSequenceForGenR(
            self.internal_id2label, add_sep_token=add_sep_token
        )
        super().__init__(tokenizer=tokenizer)

    @classmethod
    def from_config(
        cls,
        dataset: DatasetName | str = DEFAULT_DATASET,
        task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
        *,
        add_sep_token: bool = True,
        x_grid: int = 128,
        y_grid: int = 128,
        id2label: dict[int, str] | None = None,
    ) -> "LayoutFormerPPProcessor":
        """Construct processor and tokenizer without external files."""
        normalized_dataset = normalize_layoutformerpp_dataset(dataset)
        normalized_task = normalize_layoutformerpp_task(task)
        labels = labels_for_dataset(normalized_dataset)
        tokens = build_default_tokens(labels, task=normalized_task, grid=x_grid)
        tokenizer = LayoutFormerPPTokenizer(tokens=tokens)
        return cls(
            tokenizer=tokenizer,
            dataset=normalized_dataset,
            task=normalized_task,
            add_sep_token=add_sep_token,
            x_grid=x_grid,
            y_grid=y_grid,
            id2label=id2label,
        )

    @classmethod
    def _load_tokenizer_from_pretrained(
        cls,
        sub_processor_type: str,
        pretrained_model_name_or_path: str | PathLike[str],
        subfolder: str = "",
        **kwargs: str | int | float | bool | None,
    ) -> LayoutFormerPPTokenizer:
        """Load the local tokenizer for `ProcessorMixin.from_pretrained`."""
        _ = sub_processor_type
        path = Path(pretrained_model_name_or_path)
        tokenizer_path = path / subfolder if subfolder else path
        token = kwargs.get("token")
        return LayoutFormerPPTokenizer.from_pretrained(
            tokenizer_path,
            cache_dir=cast(str | PathLike[str] | None, kwargs.get("cache_dir")),
            force_download=bool(kwargs.get("force_download", False)),
            local_files_only=bool(kwargs.get("local_files_only", False)),
            token=token if isinstance(token, str | bool) else None,
            revision=str(kwargs.get("revision", "main")),
        )

    def normalize_condition_type(
        self, condition_type: ConditionType | str
    ) -> SupportedConditionType:
        """Normalize public condition aliases."""
        try:
            condition = normalize_common_condition_type(condition_type)
        except ValueError as exc:
            raise ValueError(f"Unsupported condition_type: {condition_type}") from exc

        if condition not in SUPPORTED_CONDITIONS:
            raise ValueError(f"Unsupported condition_type: {condition_type}")

        return cast(SupportedConditionType, condition)

    def _label_to_internal_id(self, label: int | str) -> int:
        if isinstance(label, int):
            return label + 1 if label in self.public_id2label else label
        lowered = label.lower()
        if lowered in self.public_label2id:
            return self.public_label2id[lowered] + 1
        if lowered.startswith("label_"):
            return int(lowered.split("_", 1)[1])
        raise ValueError(f"Unknown label: {label}")

    def _prepare_labels(
        self,
        labels: list[list[int | str]] | Int[torch.Tensor, "batch elements"] | None,
        batch_size: int,
        mask: list[list[bool]] | None = None,
    ) -> list[list[int]]:
        if labels is None:
            return [[] for _ in range(batch_size)]
        if isinstance(labels, torch.Tensor):
            return [[int(value) for value in row] for row in labels.tolist()]
        rows: list[list[int]] = []
        for row_idx, item in enumerate(labels):
            row_mask = None if mask is None else mask[row_idx]
            rows.append(
                [
                    self._label_to_internal_id(label)
                    for idx, label in enumerate(item)
                    if row_mask is None or row_mask[idx]
                ]
            )
        return rows

    def _prepare_mask(
        self,
        mask: Bool[torch.Tensor, "batch elements"]
        | list[list[bool]]
        | list[bool]
        | None,
        *,
        batch_size: int,
        row_lengths: list[int],
    ) -> list[list[bool]] | None:
        if mask is None:
            return None
        mask_tensor = torch.as_tensor(mask, dtype=torch.bool)
        if mask_tensor.ndim == 1:
            mask_tensor = mask_tensor.unsqueeze(0)
        if mask_tensor.ndim != 2:
            raise ValueError("mask must have shape (batch, sequence)")

        if mask_tensor.size(0) != batch_size:
            raise ValueError("mask batch dimension must match labels or batch_size")

        rows = mask_tensor.tolist()
        for row, expected_length in zip(rows, row_lengths, strict=True):
            if len(row) < expected_length:
                raise ValueError("mask sequence length must cover all labels")

        return rows

    def _prepare_bbox(
        self,
        bbox: LayoutFormerPPBBoxInput,
        *,
        labels: list[list[int]],
        box_format: BoxFormat | str,
        mask: list[list[bool]] | None = None,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
    ) -> list[list[list[int]]]:
        if bbox is None:
            return [[[0, 0, 1, 1] for _ in item] for item in labels]
        tensor = torch.as_tensor(bbox, dtype=torch.float32)
        if tensor.ndim == 2:
            tensor = tensor.unsqueeze(0)
        discrete_box_format = box_format
        if not normalized:
            if canvas_size is None:
                raise ValueError("canvas_size is required when normalized=False")

            tensor = normalize_boxes(
                tensor,
                canvas_size=canvas_size,
                box_format=box_format,
            )
            discrete_box_format = BoxFormat.xywh
        discrete = public_to_discrete_ltwh(
            tensor,
            box_format=discrete_box_format,
            x_grid=self.x_grid,
            y_grid=self.y_grid,
        )
        rows = discrete.tolist()
        if mask is None:
            return rows
        return [
            [box for idx, box in enumerate(row) if row_mask[idx]]
            for row, row_mask in zip(rows, mask, strict=True)
        ]

    def __call__(
        self,
        condition_type: ConditionType | str = ConditionType.unconditional,
        labels: list[list[int | str]]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        bbox: LayoutFormerPPBBoxInput = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | list[list[bool]]
        | list[bool]
        | None = None,
        relations: list[list[tuple[int, int, int, int, int]]]
        | Int[torch.Tensor, "batch relations relation_attrs"]
        | None = None,
        batch_size: int | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        return_tensors: Literal["pt"] = "pt",
    ) -> BatchEncoding:
        """Build tokenized model inputs for a public condition."""
        condition = self.normalize_condition_type(condition_type)
        batch_size = batch_size or (len(labels) if labels is not None else 1)
        row_lengths = (
            [len(row) for row in labels] if labels is not None else [0] * batch_size
        )
        prepared_mask = self._prepare_mask(
            mask,
            batch_size=batch_size,
            row_lengths=row_lengths,
        )
        internal_labels = self._prepare_labels(labels, batch_size, prepared_mask)
        internal_bbox = self._prepare_bbox(
            bbox,
            labels=internal_labels,
            box_format=box_format,
            mask=prepared_mask,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        texts: list[str] = []
        for idx in range(batch_size):
            if condition is ConditionType.unconditional:
                texts.append("")
            elif condition is ConditionType.label:
                texts.append(
                    self.gen_t_serializer.build_input_seq(
                        "gen_t", internal_labels[idx], internal_bbox[idx]
                    )
                )
            elif condition is ConditionType.label_size:
                texts.append(
                    self.gen_t_serializer.build_input_seq(
                        "gen_ts", internal_labels[idx], internal_bbox[idx]
                    )
                )
            elif condition is ConditionType.relation:
                item_relations = [] if relations is None else relations[idx]
                if isinstance(item_relations, torch.Tensor):
                    item_relations = cast(
                        list[tuple[int, int, int, int, int]],
                        [
                            tuple(int(value) for value in row)
                            for row in item_relations.tolist()
                        ],
                    )
                texts.append(
                    self.gen_r_serializer.build_input_seq(
                        internal_labels[idx], item_relations
                    )
                )
            elif condition is ConditionType.completion:
                texts.append(
                    self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
                )
            elif condition is ConditionType.refinement:
                texts.append(
                    self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
                )
            else:
                raise ValueError(f"Unsupported condition_type: {condition}")

        encoded = self.tokenizer.encode_text(texts, add_eos=True, add_bos=False)
        if return_tensors != "pt":
            raise ValueError("Only return_tensors='pt' is supported")

        return BatchEncoding(encoded)

    def post_process_layouts(
        self,
        sequences: Int[torch.Tensor, "batch tokens"],
        *,
        box_format: BoxFormat | str = BoxFormat.xywh,
        output_type: OutputType | str = OutputType.dataclass,
        return_tensors: Literal["pt"] = "pt",
    ) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:
        """Parse generated token ids to the common layout output schema."""
        texts = self.tokenizer.batch_decode(sequences, skip_special_tokens=True)
        parsed = [self.serializer.parse_seq(text.strip()) for text in texts]
        max_len = max(
            (len(item.labels) for item in parsed if item is not None), default=0
        )
        if max_len == 0:
            max_len = 1
        label_rows: list[Int[torch.Tensor, "elements"]] = []
        bbox_rows: list[Int[torch.Tensor, "elements 4"]] = []
        mask_rows: list[Bool[torch.Tensor, "elements"]] = []
        for item in parsed:
            if item is None:
                labels = torch.zeros(max_len, dtype=torch.long)
                boxes = torch.zeros(max_len, 4, dtype=torch.long)
                mask = torch.zeros(max_len, dtype=torch.bool)
            else:
                labels = torch.tensor(
                    [max(0, label - 1) for label in item.labels], dtype=torch.long
                )
                boxes = torch.tensor(item.bbox, dtype=torch.long)
                mask = torch.ones(len(labels), dtype=torch.bool)
                if len(labels) < max_len:
                    pad = max_len - len(labels)
                    labels = torch.nn.functional.pad(labels, (0, pad))
                    boxes = torch.nn.functional.pad(boxes, (0, 0, 0, pad))
                    mask = torch.nn.functional.pad(mask, (0, pad))
            label_rows.append(labels)
            bbox_rows.append(boxes)
            mask_rows.append(mask)
        bbox_ids = torch.stack(bbox_rows)
        normalized_box_format = normalize_box_format(box_format)
        bbox = discrete_ltwh_to_public(
            bbox_ids,
            box_format=normalized_box_format,
            x_grid=self.x_grid,
            y_grid=self.y_grid,
        )
        output = LayoutGenerationOutput(
            bbox=bbox.float(),
            labels=torch.stack(label_rows).long(),
            mask=torch.stack(mask_rows).bool(),
            id2label=dict(self.public_id2label),
            sequences=sequences.long(),
            intermediates={
                "generated_text": texts,
                "box_format": normalized_box_format,
            },
        )
        try:
            normalized_output_type = (
                output_type
                if isinstance(output_type, OutputType)
                else OutputType(output_type)
            )
        except ValueError as exc:
            raise ValueError(f"Unsupported output_type: {output_type}") from exc

        if normalized_output_type is OutputType.dict:
            return dict(output)
        if normalized_output_type is not OutputType.dataclass:
            assert_never(normalized_output_type)
        if return_tensors != "pt":
            raise ValueError("Only return_tensors='pt' is supported")

        return output

__init__

__init__(
    tokenizer: LayoutFormerPPTokenizer,
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask
    | ConditionType
    | str = DEFAULT_TASK,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> None

Initialize serializers, label maps, and tokenizer state.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
 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
def __init__(
    self,
    tokenizer: LayoutFormerPPTokenizer,
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> None:
    """Initialize serializers, label maps, and tokenizer state."""
    self.tokenizer = tokenizer
    normalized_dataset = normalize_layoutformerpp_dataset(dataset)
    normalized_task = normalize_layoutformerpp_task(task)
    self.dataset = layoutformerpp_dataset_slug(normalized_dataset)
    self.task = str(normalized_task)
    self.add_sep_token = add_sep_token
    self.x_grid = x_grid
    self.y_grid = y_grid
    labels = labels_for_dataset(normalized_dataset)
    self.id2label = (
        {int(key): str(value) for key, value in id2label.items()}
        if id2label is not None
        else dict(enumerate(labels))
    )
    self.public_id2label = dict(self.id2label)
    self.public_label2id = {
        value.lower(): key for key, value in self.public_id2label.items()
    }
    self.internal_id2label = {
        idx + 1: f"label_{idx + 1}" for idx in range(len(labels))
    }
    self.serializer = T5LayoutSequence(
        self.internal_id2label, add_sep_token=add_sep_token
    )
    self.gen_t_serializer = T5LayoutSequenceForGenT(
        self.internal_id2label, add_sep_token=add_sep_token
    )
    self.gen_r_serializer = T5LayoutSequenceForGenR(
        self.internal_id2label, add_sep_token=add_sep_token
    )
    super().__init__(tokenizer=tokenizer)

from_config classmethod

from_config(
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask
    | ConditionType
    | str = DEFAULT_TASK,
    *,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> "LayoutFormerPPProcessor"

Construct processor and tokenizer without external files.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
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
@classmethod
def from_config(
    cls,
    dataset: DatasetName | str = DEFAULT_DATASET,
    task: LayoutFormerPPTask | ConditionType | str = DEFAULT_TASK,
    *,
    add_sep_token: bool = True,
    x_grid: int = 128,
    y_grid: int = 128,
    id2label: dict[int, str] | None = None,
) -> "LayoutFormerPPProcessor":
    """Construct processor and tokenizer without external files."""
    normalized_dataset = normalize_layoutformerpp_dataset(dataset)
    normalized_task = normalize_layoutformerpp_task(task)
    labels = labels_for_dataset(normalized_dataset)
    tokens = build_default_tokens(labels, task=normalized_task, grid=x_grid)
    tokenizer = LayoutFormerPPTokenizer(tokens=tokens)
    return cls(
        tokenizer=tokenizer,
        dataset=normalized_dataset,
        task=normalized_task,
        add_sep_token=add_sep_token,
        x_grid=x_grid,
        y_grid=y_grid,
        id2label=id2label,
    )

normalize_condition_type

normalize_condition_type(
    condition_type: ConditionType | str,
) -> SupportedConditionType

Normalize public condition aliases.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
160
161
162
163
164
165
166
167
168
169
170
171
172
def normalize_condition_type(
    self, condition_type: ConditionType | str
) -> SupportedConditionType:
    """Normalize public condition aliases."""
    try:
        condition = normalize_common_condition_type(condition_type)
    except ValueError as exc:
        raise ValueError(f"Unsupported condition_type: {condition_type}") from exc

    if condition not in SUPPORTED_CONDITIONS:
        raise ValueError(f"Unsupported condition_type: {condition_type}")

    return cast(SupportedConditionType, condition)

__call__

__call__(
    condition_type: ConditionType
    | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[Tensor, "batch elements"]
    | list[list[bool]]
    | list[bool]
    | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[Tensor, "batch relations relation_attrs"]
    | None = None,
    batch_size: int | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding

Build tokenized model inputs for a public condition.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
def __call__(
    self,
    condition_type: ConditionType | str = ConditionType.unconditional,
    labels: list[list[int | str]]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    bbox: LayoutFormerPPBBoxInput = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | list[list[bool]]
    | list[bool]
    | None = None,
    relations: list[list[tuple[int, int, int, int, int]]]
    | Int[torch.Tensor, "batch relations relation_attrs"]
    | None = None,
    batch_size: int | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding:
    """Build tokenized model inputs for a public condition."""
    condition = self.normalize_condition_type(condition_type)
    batch_size = batch_size or (len(labels) if labels is not None else 1)
    row_lengths = (
        [len(row) for row in labels] if labels is not None else [0] * batch_size
    )
    prepared_mask = self._prepare_mask(
        mask,
        batch_size=batch_size,
        row_lengths=row_lengths,
    )
    internal_labels = self._prepare_labels(labels, batch_size, prepared_mask)
    internal_bbox = self._prepare_bbox(
        bbox,
        labels=internal_labels,
        box_format=box_format,
        mask=prepared_mask,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    texts: list[str] = []
    for idx in range(batch_size):
        if condition is ConditionType.unconditional:
            texts.append("")
        elif condition is ConditionType.label:
            texts.append(
                self.gen_t_serializer.build_input_seq(
                    "gen_t", internal_labels[idx], internal_bbox[idx]
                )
            )
        elif condition is ConditionType.label_size:
            texts.append(
                self.gen_t_serializer.build_input_seq(
                    "gen_ts", internal_labels[idx], internal_bbox[idx]
                )
            )
        elif condition is ConditionType.relation:
            item_relations = [] if relations is None else relations[idx]
            if isinstance(item_relations, torch.Tensor):
                item_relations = cast(
                    list[tuple[int, int, int, int, int]],
                    [
                        tuple(int(value) for value in row)
                        for row in item_relations.tolist()
                    ],
                )
            texts.append(
                self.gen_r_serializer.build_input_seq(
                    internal_labels[idx], item_relations
                )
            )
        elif condition is ConditionType.completion:
            texts.append(
                self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
            )
        elif condition is ConditionType.refinement:
            texts.append(
                self.serializer.build_seq(internal_labels[idx], internal_bbox[idx])
            )
        else:
            raise ValueError(f"Unsupported condition_type: {condition}")

    encoded = self.tokenizer.encode_text(texts, add_eos=True, add_bos=False)
    if return_tensors != "pt":
        raise ValueError("Only return_tensors='pt' is supported")

    return BatchEncoding(encoded)

post_process_layouts

post_process_layouts(
    sequences: Int[Tensor, "batch tokens"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    output_type: OutputType | str = OutputType.dataclass,
    return_tensors: Literal["pt"] = "pt",
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict

Parse generated token ids to the common layout output schema.

Source code in models/layoutformerpp/src/layoutformerpp/processing_layoutformerpp.py
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
def post_process_layouts(
    self,
    sequences: Int[torch.Tensor, "batch tokens"],
    *,
    box_format: BoxFormat | str = BoxFormat.xywh,
    output_type: OutputType | str = OutputType.dataclass,
    return_tensors: Literal["pt"] = "pt",
) -> LayoutGenerationOutput | LayoutFormerPPOutputDict:
    """Parse generated token ids to the common layout output schema."""
    texts = self.tokenizer.batch_decode(sequences, skip_special_tokens=True)
    parsed = [self.serializer.parse_seq(text.strip()) for text in texts]
    max_len = max(
        (len(item.labels) for item in parsed if item is not None), default=0
    )
    if max_len == 0:
        max_len = 1
    label_rows: list[Int[torch.Tensor, "elements"]] = []
    bbox_rows: list[Int[torch.Tensor, "elements 4"]] = []
    mask_rows: list[Bool[torch.Tensor, "elements"]] = []
    for item in parsed:
        if item is None:
            labels = torch.zeros(max_len, dtype=torch.long)
            boxes = torch.zeros(max_len, 4, dtype=torch.long)
            mask = torch.zeros(max_len, dtype=torch.bool)
        else:
            labels = torch.tensor(
                [max(0, label - 1) for label in item.labels], dtype=torch.long
            )
            boxes = torch.tensor(item.bbox, dtype=torch.long)
            mask = torch.ones(len(labels), dtype=torch.bool)
            if len(labels) < max_len:
                pad = max_len - len(labels)
                labels = torch.nn.functional.pad(labels, (0, pad))
                boxes = torch.nn.functional.pad(boxes, (0, 0, 0, pad))
                mask = torch.nn.functional.pad(mask, (0, pad))
        label_rows.append(labels)
        bbox_rows.append(boxes)
        mask_rows.append(mask)
    bbox_ids = torch.stack(bbox_rows)
    normalized_box_format = normalize_box_format(box_format)
    bbox = discrete_ltwh_to_public(
        bbox_ids,
        box_format=normalized_box_format,
        x_grid=self.x_grid,
        y_grid=self.y_grid,
    )
    output = LayoutGenerationOutput(
        bbox=bbox.float(),
        labels=torch.stack(label_rows).long(),
        mask=torch.stack(mask_rows).bool(),
        id2label=dict(self.public_id2label),
        sequences=sequences.long(),
        intermediates={
            "generated_text": texts,
            "box_format": normalized_box_format,
        },
    )
    try:
        normalized_output_type = (
            output_type
            if isinstance(output_type, OutputType)
            else OutputType(output_type)
        )
    except ValueError as exc:
        raise ValueError(f"Unsupported output_type: {output_type}") from exc

    if normalized_output_type is OutputType.dict:
        return dict(output)
    if normalized_output_type is not OutputType.dataclass:
        assert_never(normalized_output_type)
    if return_tensors != "pt":
        raise ValueError("Only return_tensors='pt' is supported")

    return output

serialization

Task serializers for LayoutFormer++.

RelationType

Bases: StrEnum

Supported LayoutFormer++ relation token names.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
19
20
21
22
23
24
25
26
27
28
29
class RelationType(StrEnum):
    """Supported LayoutFormer++ relation token names."""

    smaller = auto()
    equal = auto()
    larger = auto()
    top = auto()
    center = auto()
    bottom = auto()
    left = auto()
    right = auto()

ParsedLayout dataclass

Parsed discrete layout sequence.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
35
36
37
38
39
40
@dataclass
class ParsedLayout:
    """Parsed discrete layout sequence."""

    labels: list[int]
    bbox: list[list[int]]

T5LayoutSequence

Serialize labels and discrete ltwh bboxes as LayoutFormer++ text.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class T5LayoutSequence:
    """Serialize labels and discrete ltwh bboxes as LayoutFormer++ text."""

    def __init__(self, id2label: dict[int, str], *, add_sep_token: bool = True) -> None:
        """Initialize label lookup tables used by the serializer."""
        self.id2label = id2label
        self.label2id = {label.lower().strip(): idx for idx, label in id2label.items()}
        self.add_sep_token = add_sep_token
        self.error_label_id = 0

    def build_seq(self, labels: list[int], bbox: list[list[int]]) -> str:
        """Build full `label x y w h` sequence."""
        tokens: list[str] = []
        for idx, label_id in enumerate(labels):
            tokens.append(self.id2label[int(label_id)].lower())
            tokens.extend(str(int(value)) for value in bbox[idx])
            if self.add_sep_token and idx < len(labels) - 1:
                tokens.append(SEP_TOKEN)
        return " ".join(tokens)

    def parse_seq(self, output: str) -> ParsedLayout | None:
        """Parse generated text into labels and integer boxes."""
        labels: list[int] = []
        bbox: list[list[int]] = []
        if self.add_sep_token:
            text = output.strip()
            if text:
                text = f"{text} {SEP_TOKEN}"
            pattern = rf"(([\w\-/\s]+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s\{SEP_TOKEN})"
            for match in re.findall(pattern, text):
                label = match[1].strip()
                labels.append(self.label2id.get(label, self.error_label_id))
                bbox.append([int(match[2 + i]) for i in range(4)])
        else:
            tokens = output.split()
            idx = 0
            while idx < len(tokens):
                label_tokens: list[str] = []
                box_tokens: list[int] = []
                while idx < len(tokens) and not tokens[idx].isdigit():
                    label_tokens.append(tokens[idx])
                    idx += 1
                while idx < len(tokens) and tokens[idx].isdigit():
                    box_tokens.append(int(tokens[idx]))
                    idx += 1
                if label_tokens and len(box_tokens) == 4:
                    labels.append(
                        self.label2id.get(
                            " ".join(label_tokens).strip(), self.error_label_id
                        )
                    )
                    bbox.append(box_tokens)
                else:
                    return None
        if not labels:
            return None
        return ParsedLayout(labels=labels, bbox=bbox)

__init__

__init__(
    id2label: dict[int, str], *, add_sep_token: bool = True
) -> None

Initialize label lookup tables used by the serializer.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
46
47
48
49
50
51
def __init__(self, id2label: dict[int, str], *, add_sep_token: bool = True) -> None:
    """Initialize label lookup tables used by the serializer."""
    self.id2label = id2label
    self.label2id = {label.lower().strip(): idx for idx, label in id2label.items()}
    self.add_sep_token = add_sep_token
    self.error_label_id = 0

build_seq

build_seq(labels: list[int], bbox: list[list[int]]) -> str

Build full label x y w h sequence.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
53
54
55
56
57
58
59
60
61
def build_seq(self, labels: list[int], bbox: list[list[int]]) -> str:
    """Build full `label x y w h` sequence."""
    tokens: list[str] = []
    for idx, label_id in enumerate(labels):
        tokens.append(self.id2label[int(label_id)].lower())
        tokens.extend(str(int(value)) for value in bbox[idx])
        if self.add_sep_token and idx < len(labels) - 1:
            tokens.append(SEP_TOKEN)
    return " ".join(tokens)

parse_seq

parse_seq(output: str) -> ParsedLayout | None

Parse generated text into labels and integer boxes.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
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
def parse_seq(self, output: str) -> ParsedLayout | None:
    """Parse generated text into labels and integer boxes."""
    labels: list[int] = []
    bbox: list[list[int]] = []
    if self.add_sep_token:
        text = output.strip()
        if text:
            text = f"{text} {SEP_TOKEN}"
        pattern = rf"(([\w\-/\s]+)\s(\d+)\s(\d+)\s(\d+)\s(\d+)\s\{SEP_TOKEN})"
        for match in re.findall(pattern, text):
            label = match[1].strip()
            labels.append(self.label2id.get(label, self.error_label_id))
            bbox.append([int(match[2 + i]) for i in range(4)])
    else:
        tokens = output.split()
        idx = 0
        while idx < len(tokens):
            label_tokens: list[str] = []
            box_tokens: list[int] = []
            while idx < len(tokens) and not tokens[idx].isdigit():
                label_tokens.append(tokens[idx])
                idx += 1
            while idx < len(tokens) and tokens[idx].isdigit():
                box_tokens.append(int(tokens[idx]))
                idx += 1
            if label_tokens and len(box_tokens) == 4:
                labels.append(
                    self.label2id.get(
                        " ".join(label_tokens).strip(), self.error_label_id
                    )
                )
                bbox.append(box_tokens)
            else:
                return None
    if not labels:
        return None
    return ParsedLayout(labels=labels, bbox=bbox)

T5LayoutSequenceForGenT

Bases: T5LayoutSequence

Serializer for gen_t and gen_ts conditions.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
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
class T5LayoutSequenceForGenT(T5LayoutSequence):
    """Serializer for `gen_t` and `gen_ts` conditions."""

    def build_input_seq(
        self,
        task: LayoutFormerPPTask | str,
        labels: list[int],
        bbox: list[list[int]],
        *,
        add_unk_for_label: bool = False,
        add_unk_for_label_size: bool = False,
    ) -> str:
        """Build task input from labels and optional width/height constraints."""
        normalized_task = normalize_layoutformerpp_task(task)
        tokens: list[str] = []
        for idx, label_id in enumerate(labels):
            tokens.append(self.id2label[int(label_id)].lower())
            if normalized_task is LayoutFormerPPTask.gen_t:
                if add_unk_for_label:
                    tokens.extend(["<unk>", "<unk>", "<unk>", "<unk>"])
            elif normalized_task is LayoutFormerPPTask.gen_ts:
                if add_unk_for_label_size:
                    tokens.extend(["<unk>", "<unk>"])
                tokens.extend(str(int(value)) for value in bbox[idx][2:])
            else:
                raise ValueError(f"Unsupported gen_t serializer task: {task}")

            if self.add_sep_token and idx < len(labels) - 1:
                tokens.append(SEP_TOKEN)
        return " ".join(tokens)

build_input_seq

build_input_seq(
    task: LayoutFormerPPTask | str,
    labels: list[int],
    bbox: list[list[int]],
    *,
    add_unk_for_label: bool = False,
    add_unk_for_label_size: bool = False,
) -> str

Build task input from labels and optional width/height constraints.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
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
def build_input_seq(
    self,
    task: LayoutFormerPPTask | str,
    labels: list[int],
    bbox: list[list[int]],
    *,
    add_unk_for_label: bool = False,
    add_unk_for_label_size: bool = False,
) -> str:
    """Build task input from labels and optional width/height constraints."""
    normalized_task = normalize_layoutformerpp_task(task)
    tokens: list[str] = []
    for idx, label_id in enumerate(labels):
        tokens.append(self.id2label[int(label_id)].lower())
        if normalized_task is LayoutFormerPPTask.gen_t:
            if add_unk_for_label:
                tokens.extend(["<unk>", "<unk>", "<unk>", "<unk>"])
        elif normalized_task is LayoutFormerPPTask.gen_ts:
            if add_unk_for_label_size:
                tokens.extend(["<unk>", "<unk>"])
            tokens.extend(str(int(value)) for value in bbox[idx][2:])
        else:
            raise ValueError(f"Unsupported gen_t serializer task: {task}")

        if self.add_sep_token and idx < len(labels) - 1:
            tokens.append(SEP_TOKEN)
    return " ".join(tokens)

T5LayoutSequenceForGenR

Bases: T5LayoutSequence

Serializer for relation-conditioned generation.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.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
class T5LayoutSequenceForGenR(T5LayoutSequence):
    """Serializer for relation-conditioned generation."""

    def build_input_seq(
        self,
        labels: list[int],
        relations: list[tuple[int, int, int, int, int]],
        *,
        add_unk_token: bool = False,
        compact: bool = False,
    ) -> str:
        """Build relation-conditioned input sequence."""
        tokens: list[str] = []

        for idx, label_id in enumerate(labels):
            tokens.append(self.id2label[int(label_id)].lower())
            if add_unk_token:
                tokens.extend(["<unk>", "<unk>", "<unk>", "<unk>"])
            if self.add_sep_token and idx < len(labels) - 1:
                tokens.append(SEP_TOKEN)

        tokens.append(REL_BEG_TOKEN)
        for label_j, index_j, label_i, index_i, relation_type in relations:
            tokens.append(f"label_{label_i} index_{index_i}" if label_i else "label_0")

            if not compact:
                tokens.append(REL_ELE_SEP_TOKEN)
            tokens.append(f"relation_{relation_type}")

            if not compact:
                tokens.append(REL_ELE_SEP_TOKEN)
            tokens.append(f"label_{label_j} index_{index_j}" if label_j else "label_0")

            tokens.append(REL_SEP_TOKEN)
        return " ".join(tokens)

build_input_seq

build_input_seq(
    labels: list[int],
    relations: list[tuple[int, int, int, int, int]],
    *,
    add_unk_token: bool = False,
    compact: bool = False,
) -> str

Build relation-conditioned input sequence.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
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
def build_input_seq(
    self,
    labels: list[int],
    relations: list[tuple[int, int, int, int, int]],
    *,
    add_unk_token: bool = False,
    compact: bool = False,
) -> str:
    """Build relation-conditioned input sequence."""
    tokens: list[str] = []

    for idx, label_id in enumerate(labels):
        tokens.append(self.id2label[int(label_id)].lower())
        if add_unk_token:
            tokens.extend(["<unk>", "<unk>", "<unk>", "<unk>"])
        if self.add_sep_token and idx < len(labels) - 1:
            tokens.append(SEP_TOKEN)

    tokens.append(REL_BEG_TOKEN)
    for label_j, index_j, label_i, index_i, relation_type in relations:
        tokens.append(f"label_{label_i} index_{index_i}" if label_i else "label_0")

        if not compact:
            tokens.append(REL_ELE_SEP_TOKEN)
        tokens.append(f"relation_{relation_type}")

        if not compact:
            tokens.append(REL_ELE_SEP_TOKEN)
        tokens.append(f"label_{label_j} index_{index_j}" if label_j else "label_0")

        tokens.append(REL_SEP_TOKEN)
    return " ".join(tokens)

build_default_tokens

build_default_tokens(
    dataset_labels: tuple[str, ...],
    *,
    task: LayoutFormerPPTask | str,
    grid: int,
    add_sep_token: bool = True,
) -> list[str]

Construct a checkpoint-compatible vocabulary when no vocab.json is available.

Source code in models/layoutformerpp/src/layoutformerpp/serialization.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def build_default_tokens(
    dataset_labels: tuple[str, ...],
    *,
    task: LayoutFormerPPTask | str,
    grid: int,
    add_sep_token: bool = True,
) -> list[str]:
    """Construct a checkpoint-compatible vocabulary when no `vocab.json` is available."""
    normalized_task = normalize_layoutformerpp_task(task)
    tokens = [f"label_{idx}" for idx in range(1, len(dataset_labels) + 1)]
    tokens.extend(str(idx) for idx in range(grid))
    if add_sep_token:
        tokens.append(SEP_TOKEN)
    if normalized_task is LayoutFormerPPTask.gen_r:
        tokens.append("label_0")
        tokens.extend(f"relation_{idx}" for idx, _ in enumerate(RELATION_TYPES))
        tokens.extend(f"index_{idx}" for idx in range(1, 21))
        tokens.extend([REL_BEG_TOKEN, REL_SEP_TOKEN, REL_ELE_SEP_TOKEN])
    return tokens

tasks

Shared LayoutFormer++ dataset and checkpoint-task helpers.

LayoutFormerPPTask

Bases: StrEnum

Supported converted LayoutFormer++ checkpoint variants.

Source code in models/layoutformerpp/src/layoutformerpp/tasks.py
12
13
14
15
16
17
18
19
20
class LayoutFormerPPTask(StrEnum):
    """Supported converted LayoutFormer++ checkpoint variants."""

    ugen = auto()
    gen_t = auto()
    gen_ts = auto()
    gen_r = auto()
    completion = auto()
    refinement = auto()

OutputType

Bases: StrEnum

Supported post-processing return shapes.

Source code in models/layoutformerpp/src/layoutformerpp/tasks.py
23
24
25
26
27
class OutputType(StrEnum):
    """Supported post-processing return shapes."""

    dataclass = auto()
    dict = auto()

normalize_layoutformerpp_dataset

normalize_layoutformerpp_dataset(
    dataset: DatasetName | str,
) -> DatasetName

Normalize public dataset aliases to a LayoutFormer++ supported dataset.

Source code in models/layoutformerpp/src/layoutformerpp/tasks.py
53
54
55
56
57
58
59
def normalize_layoutformerpp_dataset(dataset: DatasetName | str) -> DatasetName:
    """Normalize public dataset aliases to a LayoutFormer++ supported dataset."""
    normalized = normalize_dataset_name(dataset)
    if normalized not in SUPPORTED_DATASETS:
        raise ValueError(f"Unsupported LayoutFormer++ dataset: {dataset}")

    return normalized

layoutformerpp_dataset_slug

layoutformerpp_dataset_slug(
    dataset: DatasetName | str,
) -> str

Return the dataset slug for a LayoutFormer++ dataset.

Source code in models/layoutformerpp/src/layoutformerpp/tasks.py
62
63
64
def layoutformerpp_dataset_slug(dataset: DatasetName | str) -> str:
    """Return the dataset slug for a LayoutFormer++ dataset."""
    return DATASET_TO_VENDOR_SLUG[normalize_layoutformerpp_dataset(dataset)]

normalize_layoutformerpp_task

normalize_layoutformerpp_task(
    task: LayoutFormerPPTask | ConditionType | str,
) -> LayoutFormerPPTask

Normalize checkpoint variant aliases to the internal task enum.

Source code in models/layoutformerpp/src/layoutformerpp/tasks.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def normalize_layoutformerpp_task(
    task: LayoutFormerPPTask | ConditionType | str,
) -> LayoutFormerPPTask:
    """Normalize checkpoint variant aliases to the internal task enum."""
    if isinstance(task, LayoutFormerPPTask):
        return task
    try:
        return LayoutFormerPPTask(task)
    except ValueError:
        condition = normalize_condition_type(task)
    try:
        return DEFAULT_TASK_FOR_CONDITION[condition]
    except KeyError as exc:
        raise ValueError(f"Unsupported LayoutFormer++ task: {task}") from exc

tokenization_layoutformerpp

Tokenizer for LayoutFormer++ layout strings.

LayoutFormerPPTokenizer

Bases: WhitespaceTokenizerMixin, PreTrainedTokenizer

Whitespace tokenizer backed by the released vocab.json format.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.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
 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
class LayoutFormerPPTokenizer(WhitespaceTokenizerMixin, PreTrainedTokenizer):
    """Whitespace tokenizer backed by the released `vocab.json` format."""

    vocab_files_names = {"vocab_file": "vocab.json"}
    model_input_names = ["input_ids", "attention_mask"]

    def __init__(
        self,
        vocab_file: str | None = None,
        tokens: list[str] | None = None,
        x_grid: int = 128,
        y_grid: int = 128,
        bbox_order: BoxFormat | str = BoxFormat.ltwh,
        bos_token: str = "<bos>",
        eos_token: str = "<eos>",
        pad_token: str = "<pad>",
        sep_token: str = "<sep>",
        unk_token: str = "<unk>",
        model_max_length: int = DEFAULT_MODEL_MAX_LENGTH,
        padding_side: str = "right",
        truncation_side: str = "right",
        clean_up_tokenization_spaces: bool = False,
        added_tokens_decoder: dict[int | str, str] | None = None,
        backend: str = "custom",
        tokenizer_file: str | None = None,
        name_or_path: str = "",
        is_local: bool = False,
        local_files_only: bool = False,
        processor_class: str | None = None,
    ) -> None:
        """Initialize a tokenizer from a vocab file or synthetic token list."""
        _ = (backend, tokenizer_file, is_local, local_files_only, processor_class)
        self.x_grid = x_grid
        self.y_grid = y_grid
        self.bbox_order = str(normalize_box_format(bbox_order))
        self._token2id, self._id2token = build_token_maps(
            vocab_file=vocab_file,
            tokens=tokens,
            base_tokens=("<bos>", "<eos>", "<pad>", "<sep>", "<unk>"),
        )
        tokenizer_kwargs: dict[str, object] = {
            "bos_token": bos_token,
            "eos_token": eos_token,
            "pad_token": pad_token,
            "sep_token": sep_token,
            "unk_token": unk_token,
            "model_max_length": model_max_length,
            "padding_side": padding_side,
            "truncation_side": truncation_side,
            "clean_up_tokenization_spaces": clean_up_tokenization_spaces,
            "backend": backend,
            "name_or_path": name_or_path,
        }
        if added_tokens_decoder is not None:
            tokenizer_kwargs["added_tokens_decoder"] = added_tokens_decoder
        super().__init__(**tokenizer_kwargs)

    def save_vocabulary(
        self, save_directory: str, filename_prefix: str | None = None
    ) -> tuple[str]:
        """Save `vocab.json` in checkpoint-compatible token-to-id format."""
        return save_json_vocabulary(
            save_directory=save_directory,
            filename="vocab.json",
            data=self._token2id,
            filename_prefix=filename_prefix,
        )

    def save_pretrained(
        self,
        save_directory: str | PathLike[str],
        legacy_format: bool | None = None,
        filename_prefix: str | None = None,
        push_to_hub: bool = False,
        **kwargs: str | int | float | bool | None,
    ) -> tuple[str, ...]:
        """Save tokenizer files plus LayoutFormer++ tokenizer metadata."""
        _ = kwargs
        paths = super().save_pretrained(
            str(save_directory),
            legacy_format=legacy_format,
            filename_prefix=filename_prefix,
            push_to_hub=push_to_hub,
        )
        metadata = {
            "x_grid": self.x_grid,
            "y_grid": self.y_grid,
            "bbox_order": self.bbox_order,
        }
        with (Path(save_directory) / "layoutformerpp_tokenizer_config.json").open(
            "w"
        ) as f:
            json.dump(metadata, f, indent=2, sort_keys=True)
        return paths

    @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",
        x_grid: int | None = None,
        y_grid: int | None = None,
        bbox_order: BoxFormat | str | None = None,
    ) -> "LayoutFormerPPTokenizer":
        """Load tokenizer and LayoutFormer++ metadata."""
        path = Path(pretrained_model_name_or_path)
        metadata_path = path / "layoutformerpp_tokenizer_config.json"
        metadata: dict[str, object] = {}
        if metadata_path.exists():
            with metadata_path.open() as f:
                metadata = json.load(f)
        if x_grid is not None:
            metadata["x_grid"] = x_grid
        if y_grid is not None:
            metadata["y_grid"] = y_grid
        if bbox_order is not None:
            metadata["bbox_order"] = bbox_order
        return cast(
            "LayoutFormerPPTokenizer",
            super().from_pretrained(
                str(pretrained_model_name_or_path),
                cache_dir=cache_dir,
                force_download=force_download,
                local_files_only=local_files_only,
                token=token,
                revision=revision,
                **metadata,
            ),
        )

    def encode_text(
        self, text: str | list[str], *, add_eos: bool = True, add_bos: bool = False
    ) -> BatchEncoding:
        """Tokenize reference-style text while matching the original EOS/BOS behavior."""
        if isinstance(text, str):
            texts = [text]
        else:
            texts = text
        normalized = []
        for item in texts:
            tokens = item.strip().split()
            if add_eos:
                tokens.append(self.eos_token)
            if add_bos:
                tokens.insert(0, self.bos_token)
            normalized.append(" ".join(tokens))
        return self(
            normalized, padding=True, add_special_tokens=False, return_tensors="pt"
        )

__init__

__init__(
    vocab_file: str | None = None,
    tokens: list[str] | None = None,
    x_grid: int = 128,
    y_grid: int = 128,
    bbox_order: BoxFormat | str = BoxFormat.ltwh,
    bos_token: str = "<bos>",
    eos_token: str = "<eos>",
    pad_token: str = "<pad>",
    sep_token: str = "<sep>",
    unk_token: str = "<unk>",
    model_max_length: int = DEFAULT_MODEL_MAX_LENGTH,
    padding_side: str = "right",
    truncation_side: str = "right",
    clean_up_tokenization_spaces: bool = False,
    added_tokens_decoder: dict[int | str, str]
    | None = None,
    backend: str = "custom",
    tokenizer_file: str | None = None,
    name_or_path: str = "",
    is_local: bool = False,
    local_files_only: bool = False,
    processor_class: str | None = None,
) -> None

Initialize a tokenizer from a vocab file or synthetic token list.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
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
def __init__(
    self,
    vocab_file: str | None = None,
    tokens: list[str] | None = None,
    x_grid: int = 128,
    y_grid: int = 128,
    bbox_order: BoxFormat | str = BoxFormat.ltwh,
    bos_token: str = "<bos>",
    eos_token: str = "<eos>",
    pad_token: str = "<pad>",
    sep_token: str = "<sep>",
    unk_token: str = "<unk>",
    model_max_length: int = DEFAULT_MODEL_MAX_LENGTH,
    padding_side: str = "right",
    truncation_side: str = "right",
    clean_up_tokenization_spaces: bool = False,
    added_tokens_decoder: dict[int | str, str] | None = None,
    backend: str = "custom",
    tokenizer_file: str | None = None,
    name_or_path: str = "",
    is_local: bool = False,
    local_files_only: bool = False,
    processor_class: str | None = None,
) -> None:
    """Initialize a tokenizer from a vocab file or synthetic token list."""
    _ = (backend, tokenizer_file, is_local, local_files_only, processor_class)
    self.x_grid = x_grid
    self.y_grid = y_grid
    self.bbox_order = str(normalize_box_format(bbox_order))
    self._token2id, self._id2token = build_token_maps(
        vocab_file=vocab_file,
        tokens=tokens,
        base_tokens=("<bos>", "<eos>", "<pad>", "<sep>", "<unk>"),
    )
    tokenizer_kwargs: dict[str, object] = {
        "bos_token": bos_token,
        "eos_token": eos_token,
        "pad_token": pad_token,
        "sep_token": sep_token,
        "unk_token": unk_token,
        "model_max_length": model_max_length,
        "padding_side": padding_side,
        "truncation_side": truncation_side,
        "clean_up_tokenization_spaces": clean_up_tokenization_spaces,
        "backend": backend,
        "name_or_path": name_or_path,
    }
    if added_tokens_decoder is not None:
        tokenizer_kwargs["added_tokens_decoder"] = added_tokens_decoder
    super().__init__(**tokenizer_kwargs)

save_vocabulary

save_vocabulary(
    save_directory: str, filename_prefix: str | None = None
) -> tuple[str]

Save vocab.json in checkpoint-compatible token-to-id format.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
78
79
80
81
82
83
84
85
86
87
def save_vocabulary(
    self, save_directory: str, filename_prefix: str | None = None
) -> tuple[str]:
    """Save `vocab.json` in checkpoint-compatible token-to-id format."""
    return save_json_vocabulary(
        save_directory=save_directory,
        filename="vocab.json",
        data=self._token2id,
        filename_prefix=filename_prefix,
    )

save_pretrained

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

Save tokenizer files plus LayoutFormer++ tokenizer metadata.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
 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
def save_pretrained(
    self,
    save_directory: str | PathLike[str],
    legacy_format: bool | None = None,
    filename_prefix: str | None = None,
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> tuple[str, ...]:
    """Save tokenizer files plus LayoutFormer++ tokenizer metadata."""
    _ = kwargs
    paths = super().save_pretrained(
        str(save_directory),
        legacy_format=legacy_format,
        filename_prefix=filename_prefix,
        push_to_hub=push_to_hub,
    )
    metadata = {
        "x_grid": self.x_grid,
        "y_grid": self.y_grid,
        "bbox_order": self.bbox_order,
    }
    with (Path(save_directory) / "layoutformerpp_tokenizer_config.json").open(
        "w"
    ) as f:
        json.dump(metadata, f, indent=2, sort_keys=True)
    return paths

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",
    x_grid: int | None = None,
    y_grid: int | None = None,
    bbox_order: BoxFormat | str | None = None,
) -> "LayoutFormerPPTokenizer"

Load tokenizer and LayoutFormer++ metadata.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
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
@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",
    x_grid: int | None = None,
    y_grid: int | None = None,
    bbox_order: BoxFormat | str | None = None,
) -> "LayoutFormerPPTokenizer":
    """Load tokenizer and LayoutFormer++ metadata."""
    path = Path(pretrained_model_name_or_path)
    metadata_path = path / "layoutformerpp_tokenizer_config.json"
    metadata: dict[str, object] = {}
    if metadata_path.exists():
        with metadata_path.open() as f:
            metadata = json.load(f)
    if x_grid is not None:
        metadata["x_grid"] = x_grid
    if y_grid is not None:
        metadata["y_grid"] = y_grid
    if bbox_order is not None:
        metadata["bbox_order"] = bbox_order
    return cast(
        "LayoutFormerPPTokenizer",
        super().from_pretrained(
            str(pretrained_model_name_or_path),
            cache_dir=cache_dir,
            force_download=force_download,
            local_files_only=local_files_only,
            token=token,
            revision=revision,
            **metadata,
        ),
    )

encode_text

encode_text(
    text: str | list[str],
    *,
    add_eos: bool = True,
    add_bos: bool = False,
) -> BatchEncoding

Tokenize reference-style text while matching the original EOS/BOS behavior.

Source code in models/layoutformerpp/src/layoutformerpp/tokenization_layoutformerpp.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def encode_text(
    self, text: str | list[str], *, add_eos: bool = True, add_bos: bool = False
) -> BatchEncoding:
    """Tokenize reference-style text while matching the original EOS/BOS behavior."""
    if isinstance(text, str):
        texts = [text]
    else:
        texts = text
    normalized = []
    for item in texts:
        tokens = item.strip().split()
        if add_eos:
            tokens.append(self.eos_token)
        if add_bos:
            tokens.insert(0, self.bos_token)
        normalized.append(" ".join(tokens))
    return self(
        normalized, padding=True, add_special_tokens=False, return_tensors="pt"
    )