Skip to content

Layoutganpp

Transformers-style LayoutGAN++ package exports.

LayoutGANPPConfig

Bases: PretrainedConfig

Configuration for the LayoutGAN++ generator.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias used to resolve labels and sequence length.

rico13
latent_size int

Size of each per-element latent vector.

4
num_labels int | None

Optional label vocabulary size override.

None
id2label Id2LabelMapping | None

Optional mapping from label IDs to display labels.

None
label2id dict[str, int] | None

Optional mapping from display labels to label IDs.

None
d_model int

Transformer hidden size used by the generator.

512
nhead int

Number of transformer attention heads.

8
num_layers int

Number of transformer encoder layers.

4
bbox_format BoxFormat | str

Bounding-box format produced by the model.

xywh
bbox_normalized bool

Whether generated boxes are normalized to the canvas.

True
max_position_embeddings int | None

Maximum element count for generated layouts.

None
**kwargs LayoutGANPPConfigValue

Extra PretrainedConfig keyword arguments.

{}

Examples:

>>> config = LayoutGANPPConfig(dataset_name="rico")
>>> config.model_type
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/configuration_layoutganpp.py
 20
 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
class LayoutGANPPConfig(PretrainedConfig):
    """Configuration for the LayoutGAN++ generator.

    Args:
        dataset_name: Dataset key or alias used to resolve labels and sequence length.
        latent_size: Size of each per-element latent vector.
        num_labels: Optional label vocabulary size override.
        id2label: Optional mapping from label IDs to display labels.
        label2id: Optional mapping from display labels to label IDs.
        d_model: Transformer hidden size used by the generator.
        nhead: Number of transformer attention heads.
        num_layers: Number of transformer encoder layers.
        bbox_format: Bounding-box format produced by the model.
        bbox_normalized: Whether generated boxes are normalized to the canvas.
        max_position_embeddings: Maximum element count for generated layouts.
        **kwargs: Extra `PretrainedConfig` keyword arguments.

    Examples:
        >>> config = LayoutGANPPConfig(dataset_name="rico")
        >>> config.model_type
        'layoutganpp'
    """

    model_type = "layoutganpp"

    def __init__(
        self,
        dataset_name: DatasetName | str = DatasetName.rico13,
        latent_size: int = 4,
        num_labels: int | None = None,
        id2label: Id2LabelMapping | None = None,
        label2id: dict[str, int] | None = None,
        d_model: int = 512,
        nhead: int = 8,
        num_layers: int = 4,
        bbox_format: BoxFormat | str = BoxFormat.xywh,
        bbox_normalized: bool = True,
        max_position_embeddings: int | None = None,
        **kwargs: LayoutGANPPConfigValue,
    ) -> None:
        """Initialize a LayoutGAN++ config.

        Args:
            dataset_name: Dataset key or alias used to resolve labels and metadata.
            latent_size: Size of each latent vector passed to the generator.
            num_labels: Optional explicit label vocabulary size.
            id2label: Optional label ID to text mapping.
            label2id: Optional label text to ID mapping.
            d_model: Transformer hidden size.
            nhead: Number of attention heads.
            num_layers: Number of transformer encoder layers.
            bbox_format: Format of generated bounding boxes.
            bbox_normalized: Whether generated boxes are normalized.
            max_position_embeddings: Optional maximum layout length override.
            **kwargs: Extra `PretrainedConfig` keyword arguments.

        Raises:
            ValueError: If `dataset_name` is not a supported LayoutGAN++ dataset.

        Examples:
            >>> LayoutGANPPConfig(dataset_name="publaynet").num_labels
            5
        """
        metadata = dataset_metadata(dataset_name)
        raw_id2label = id2label or id2label_for_dataset(dataset_name)
        normalized_id2label = {int(k): v for k, v in raw_id2label.items()}
        normalized_label2id = label2id or {
            label: i for i, label in normalized_id2label.items()
        }
        resolved_num_labels = num_labels or len(normalized_id2label)
        super().__init__(
            id2label=normalized_id2label,
            label2id=normalized_label2id,
        )
        for key, value in kwargs.items():
            setattr(self, key, value)
        self.dataset_name = str(metadata["name"])
        self.latent_size = latent_size
        self.num_labels = resolved_num_labels
        self.d_model = d_model
        self.nhead = nhead
        self.num_layers = num_layers
        self.bbox_format = str(normalize_box_format(bbox_format))
        self.bbox_normalized = bbox_normalized
        self.max_position_embeddings = (
            max_position_embeddings or max_elements_for_dataset(metadata["name"])
        )
        self.architectures = ["LayoutGANPPModel"]

__init__

__init__(
    dataset_name: DatasetName | str = DatasetName.rico13,
    latent_size: int = 4,
    num_labels: int | None = None,
    id2label: Id2LabelMapping | None = None,
    label2id: dict[str, int] | None = None,
    d_model: int = 512,
    nhead: int = 8,
    num_layers: int = 4,
    bbox_format: BoxFormat | str = BoxFormat.xywh,
    bbox_normalized: bool = True,
    max_position_embeddings: int | None = None,
    **kwargs: LayoutGANPPConfigValue,
) -> None

Initialize a LayoutGAN++ config.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias used to resolve labels and metadata.

rico13
latent_size int

Size of each latent vector passed to the generator.

4
num_labels int | None

Optional explicit label vocabulary size.

None
id2label Id2LabelMapping | None

Optional label ID to text mapping.

None
label2id dict[str, int] | None

Optional label text to ID mapping.

None
d_model int

Transformer hidden size.

512
nhead int

Number of attention heads.

8
num_layers int

Number of transformer encoder layers.

4
bbox_format BoxFormat | str

Format of generated bounding boxes.

xywh
bbox_normalized bool

Whether generated boxes are normalized.

True
max_position_embeddings int | None

Optional maximum layout length override.

None
**kwargs LayoutGANPPConfigValue

Extra PretrainedConfig keyword arguments.

{}

Raises:

Type Description
ValueError

If dataset_name is not a supported LayoutGAN++ dataset.

Examples:

>>> LayoutGANPPConfig(dataset_name="publaynet").num_labels
5
Source code in models/layoutganpp/src/layoutganpp/configuration_layoutganpp.py
 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
def __init__(
    self,
    dataset_name: DatasetName | str = DatasetName.rico13,
    latent_size: int = 4,
    num_labels: int | None = None,
    id2label: Id2LabelMapping | None = None,
    label2id: dict[str, int] | None = None,
    d_model: int = 512,
    nhead: int = 8,
    num_layers: int = 4,
    bbox_format: BoxFormat | str = BoxFormat.xywh,
    bbox_normalized: bool = True,
    max_position_embeddings: int | None = None,
    **kwargs: LayoutGANPPConfigValue,
) -> None:
    """Initialize a LayoutGAN++ config.

    Args:
        dataset_name: Dataset key or alias used to resolve labels and metadata.
        latent_size: Size of each latent vector passed to the generator.
        num_labels: Optional explicit label vocabulary size.
        id2label: Optional label ID to text mapping.
        label2id: Optional label text to ID mapping.
        d_model: Transformer hidden size.
        nhead: Number of attention heads.
        num_layers: Number of transformer encoder layers.
        bbox_format: Format of generated bounding boxes.
        bbox_normalized: Whether generated boxes are normalized.
        max_position_embeddings: Optional maximum layout length override.
        **kwargs: Extra `PretrainedConfig` keyword arguments.

    Raises:
        ValueError: If `dataset_name` is not a supported LayoutGAN++ dataset.

    Examples:
        >>> LayoutGANPPConfig(dataset_name="publaynet").num_labels
        5
    """
    metadata = dataset_metadata(dataset_name)
    raw_id2label = id2label or id2label_for_dataset(dataset_name)
    normalized_id2label = {int(k): v for k, v in raw_id2label.items()}
    normalized_label2id = label2id or {
        label: i for i, label in normalized_id2label.items()
    }
    resolved_num_labels = num_labels or len(normalized_id2label)
    super().__init__(
        id2label=normalized_id2label,
        label2id=normalized_label2id,
    )
    for key, value in kwargs.items():
        setattr(self, key, value)
    self.dataset_name = str(metadata["name"])
    self.latent_size = latent_size
    self.num_labels = resolved_num_labels
    self.d_model = d_model
    self.nhead = nhead
    self.num_layers = num_layers
    self.bbox_format = str(normalize_box_format(bbox_format))
    self.bbox_normalized = bbox_normalized
    self.max_position_embeddings = (
        max_position_embeddings or max_elements_for_dataset(metadata["name"])
    )
    self.architectures = ["LayoutGANPPModel"]

DatasetName

Bases: StrEnum

Canonical dataset names supported by the shared label registry.

Source code in lib/laygen/src/laygen/common/labels.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class DatasetName(StrEnum):
    """Canonical dataset names supported by the shared label registry."""

    rico25 = auto()
    rico13 = auto()
    publaynet = auto()
    magazine = auto()
    nsr_1k = "nsr-1k"
    grit = auto()
    coco = auto()
    vg_msdn = "vg-msdn"
    coco_grounded = "coco-grounded"
    web = auto()
    webui = auto()
    housegan_floorplan_vectorized = "housegan-floorplan-vectorized"

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()

LayoutGANPPModel

Bases: PreTrainedModel

Transformers-compatible LayoutGAN++ generator.

Parameters:

Name Type Description Default
config LayoutGANPPConfig

LayoutGAN++ model configuration.

required

Examples:

>>> config = LayoutGANPPConfig(num_labels=2, id2label={0: "a", 1: "b"})
>>> model = LayoutGANPPModel(config)
>>> model.config.model_type
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
 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
class LayoutGANPPModel(PreTrainedModel):
    """Transformers-compatible LayoutGAN++ generator.

    Args:
        config: LayoutGAN++ model configuration.

    Examples:
        >>> config = LayoutGANPPConfig(num_labels=2, id2label={0: "a", 1: "b"})
        >>> model = LayoutGANPPModel(config)
        >>> model.config.model_type
        'layoutganpp'
    """

    config_class = LayoutGANPPConfig
    base_model_prefix = "layoutganpp"
    supports_gradient_checkpointing = False

    def __init__(self, config: LayoutGANPPConfig) -> None:
        """Initialize the LayoutGAN++ generator layers.

        Args:
            config: LayoutGAN++ model configuration.

        Examples:
            >>> model = LayoutGANPPModel(LayoutGANPPConfig())
            >>> model.base_model_prefix
            'layoutganpp'
        """
        super().__init__(config)
        self.fc_z = nn.Linear(config.latent_size, config.d_model // 2)
        self.emb_label = nn.Embedding(config.num_labels, config.d_model // 2)
        self.fc_in = nn.Linear(config.d_model, config.d_model)
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=config.d_model,
            nhead=config.nhead,
            dim_feedforward=config.d_model // 2,
            batch_first=False,
        )
        self.transformer = nn.TransformerEncoder(
            encoder_layer, num_layers=config.num_layers
        )
        self.fc_out = nn.Linear(config.d_model, 4)
        self.post_init()

    def forward(
        self,
        latents: Float[torch.Tensor, "batch elements latent"],
        labels: Int[torch.Tensor, "batch elements"],
        attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
        padding_mask: Bool[torch.Tensor, "batch elements"] | None = None,
        return_dict: bool = True,
    ) -> (
        LayoutGANPPModelOutput
        | tuple[
            Float[torch.Tensor, "batch elements 4"],
            Int[torch.Tensor, "batch elements"],
            Bool[torch.Tensor, "batch elements"],
        ]
    ):
        """Run a forward pass from latents and label IDs.

        Args:
            latents: Per-element latent vectors shaped `(batch, sequence, latent_size)`.
            labels: Label IDs shaped `(batch, sequence)`.
            attention_mask: Optional mask where true values mark valid labels.
            padding_mask: Optional mask where true values mark padded labels.
            return_dict: Whether to return a `LayoutGANPPModelOutput`.

        Returns:
            Model output dataclass or tuple containing boxes, labels, and mask.

        Raises:
            ValueError: If labels or latents have invalid shape or label IDs.

        Examples:
            >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
            >>> labels = torch.tensor([[0, 1]])
            >>> latents = torch.zeros(1, 2, model.config.latent_size)
            >>> tuple(model(latents=latents, labels=labels).bbox.shape)
            (1, 2, 4)
        """
        labels = labels.to(dtype=torch.long)
        if labels.ndim != 2:
            raise ValueError("labels must have shape (batch, sequence)")

        if latents.shape[:2] != labels.shape:
            raise ValueError("latents must have shape (batch, sequence, latent_size)")

        if latents.shape[-1] != self.config.latent_size:
            raise ValueError(
                f"latents last dimension must be {self.config.latent_size}"
            )

        if labels.numel() and (
            int(labels.min().item()) < 0
            or int(labels.max().item()) >= self.config.num_labels
        ):
            raise ValueError("labels contain ids outside config.num_labels")

        if padding_mask is None:
            if attention_mask is None:
                padding_mask = torch.zeros(
                    labels.shape, dtype=torch.bool, device=labels.device
                )
            else:
                padding_mask = ~attention_mask.to(
                    device=labels.device, dtype=torch.bool
                )
        else:
            padding_mask = padding_mask.to(device=labels.device, dtype=torch.bool)
        latents = latents.to(device=labels.device, dtype=self.dtype)
        z = self.fc_z(latents)
        label_emb = self.emb_label(labels)
        hidden = torch.cat([z, label_emb], dim=-1)
        hidden = torch.relu(self.fc_in(hidden)).permute(1, 0, 2)
        hidden = self.transformer(hidden, src_key_padding_mask=padding_mask)
        bbox = torch.sigmoid(self.fc_out(hidden.permute(1, 0, 2)))
        mask = ~padding_mask
        if not return_dict:
            return bbox, labels, mask
        return LayoutGANPPModelOutput(
            bbox=bbox, labels=labels, mask=mask, latents=latents
        )

    @torch.no_grad()
    def generate(
        self,
        *,
        batch_size: int = 1,
        condition_type: ConditionType | str = ConditionType.label,
        bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
        labels: Int[torch.Tensor, "batch elements"] | None = None,
        mask: Bool[torch.Tensor, "batch elements"] | None = None,
        attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        num_inference_steps: int | None = None,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
        latents: Float[torch.Tensor, "batch elements latent"] | None = None,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
        """Generate layouts from label conditions.

        Args:
            batch_size: Requested batch size; label shape determines the final value.
            condition_type: Condition type or alias. LayoutGAN++ supports label conditions.
            bbox: Reserved compatibility argument.
            labels: Required label IDs for generation.
            mask: Optional valid-element mask.
            attention_mask: Optional valid-element mask.
            num_elements: Reserved compatibility argument.
            box_format: Reserved compatibility argument.
            normalized: Reserved compatibility argument.
            canvas_size: Reserved compatibility argument.
            seed: Optional random seed for latent sampling.
            generator: Optional PyTorch random generator.
            num_inference_steps: Reserved compatibility argument.
            output_type: Return format, either `dataclass` or `dict`.
            return_intermediates: Whether to include generation intermediates.
            latents: Optional fixed latent vectors.

        Returns:
            A layout generation dataclass or dictionary.

        Raises:
            ValueError: If labels are missing, generation options are unsupported,
                or output type is invalid.

        Examples:
            >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
            >>> out = model.generate(labels=torch.tensor([[0, 1]]), seed=0)
            >>> tuple(out.bbox.shape)
            (1, 2, 4)
        """
        del bbox, num_elements, normalized, canvas_size, num_inference_steps
        normalize_box_format(box_format)
        canonical = normalize_condition_type(condition_type)
        if canonical is ConditionType.unconditional:
            raise ValueError(
                "layoutganpp v1 requires labels; unconditional is unsupported"
            )

        if canonical is not ConditionType.label:
            raise ValueError(f"Unsupported condition_type for layoutganpp: {canonical}")

        if labels is None:
            raise ValueError("labels are required for layoutganpp generation")

        device = next(self.parameters()).device
        labels = torch.as_tensor(labels, dtype=torch.long, device=device)
        if labels.ndim == 1:
            labels = labels.unsqueeze(0)
        batch_size = labels.shape[0]
        if mask is not None:
            attention_mask = mask
        if attention_mask is None:
            attention_mask = torch.ones(labels.shape, dtype=torch.bool, device=device)
        else:
            attention_mask = torch.as_tensor(
                attention_mask, dtype=torch.bool, device=device
            )
            if attention_mask.ndim == 1:
                attention_mask = attention_mask.unsqueeze(0)
        if latents is None:
            latents = self._sample_latents(
                (batch_size, labels.shape[1], self.config.latent_size),
                seed=seed,
                generator=generator,
                device=device,
                dtype=self.dtype,
            )
        else:
            latents = torch.as_tensor(latents, dtype=self.dtype, device=device)
        out = self.forward(
            latents=latents,
            labels=labels,
            attention_mask=attention_mask,
            return_dict=True,
        )
        assert isinstance(out, LayoutGANPPModelOutput)
        assert out.labels is not None
        assert out.mask is not None
        assert out.latents is not None
        layout = LayoutGenerationOutput(
            bbox=out.bbox.detach().cpu(),
            labels=out.labels.detach().cpu(),
            mask=out.mask.detach().cpu(),
            id2label={int(k): v for k, v in self.config.id2label.items()},
            intermediates={
                "condition_type": canonical,
                "latents": out.latents.detach().cpu() if return_intermediates else None,
            }
            if return_intermediates
            else None,
        )
        resolved_output_type = normalize_output_type(output_type)
        if resolved_output_type is OutputType.dict:
            return cast(LayoutGANPPOutputDict, dict(layout))
        if resolved_output_type is OutputType.dataclass:
            return layout
        assert_never(resolved_output_type)

    def _sample_latents(
        self,
        shape: tuple[int, int, int],
        *,
        seed: int | None,
        generator: torch.Generator | None,
        device: torch.device,
        dtype: torch.dtype,
    ) -> Float[torch.Tensor, "batch elements latent"]:
        if generator is None and seed is not None:
            generator = torch.Generator(device=device).manual_seed(seed)
        return torch.randn(shape, generator=generator, device=device, dtype=dtype)

__init__

__init__(config: LayoutGANPPConfig) -> None

Initialize the LayoutGAN++ generator layers.

Parameters:

Name Type Description Default
config LayoutGANPPConfig

LayoutGAN++ model configuration.

required

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig())
>>> model.base_model_prefix
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.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
def __init__(self, config: LayoutGANPPConfig) -> None:
    """Initialize the LayoutGAN++ generator layers.

    Args:
        config: LayoutGAN++ model configuration.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig())
        >>> model.base_model_prefix
        'layoutganpp'
    """
    super().__init__(config)
    self.fc_z = nn.Linear(config.latent_size, config.d_model // 2)
    self.emb_label = nn.Embedding(config.num_labels, config.d_model // 2)
    self.fc_in = nn.Linear(config.d_model, config.d_model)
    encoder_layer = nn.TransformerEncoderLayer(
        d_model=config.d_model,
        nhead=config.nhead,
        dim_feedforward=config.d_model // 2,
        batch_first=False,
    )
    self.transformer = nn.TransformerEncoder(
        encoder_layer, num_layers=config.num_layers
    )
    self.fc_out = nn.Linear(config.d_model, 4)
    self.post_init()

forward

forward(
    latents: Float[Tensor, "batch elements latent"],
    labels: Int[Tensor, "batch elements"],
    attention_mask: Bool[Tensor, "batch elements"]
    | None = None,
    padding_mask: Bool[Tensor, "batch elements"]
    | None = None,
    return_dict: bool = True,
) -> (
    LayoutGANPPModelOutput
    | tuple[
        Float[torch.Tensor, "batch elements 4"],
        Int[torch.Tensor, "batch elements"],
        Bool[torch.Tensor, "batch elements"],
    ]
)

Run a forward pass from latents and label IDs.

Parameters:

Name Type Description Default
latents Float[Tensor, 'batch elements latent']

Per-element latent vectors shaped (batch, sequence, latent_size).

required
labels Int[Tensor, 'batch elements']

Label IDs shaped (batch, sequence).

required
attention_mask Bool[Tensor, 'batch elements'] | None

Optional mask where true values mark valid labels.

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

Optional mask where true values mark padded labels.

None
return_dict bool

Whether to return a LayoutGANPPModelOutput.

True

Returns:

Type Description
LayoutGANPPModelOutput | tuple[Float[Tensor, 'batch elements 4'], Int[Tensor, 'batch elements'], Bool[Tensor, 'batch elements']]

Model output dataclass or tuple containing boxes, labels, and mask.

Raises:

Type Description
ValueError

If labels or latents have invalid shape or label IDs.

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
>>> labels = torch.tensor([[0, 1]])
>>> latents = torch.zeros(1, 2, model.config.latent_size)
>>> tuple(model(latents=latents, labels=labels).bbox.shape)
(1, 2, 4)
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
def forward(
    self,
    latents: Float[torch.Tensor, "batch elements latent"],
    labels: Int[torch.Tensor, "batch elements"],
    attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
    padding_mask: Bool[torch.Tensor, "batch elements"] | None = None,
    return_dict: bool = True,
) -> (
    LayoutGANPPModelOutput
    | tuple[
        Float[torch.Tensor, "batch elements 4"],
        Int[torch.Tensor, "batch elements"],
        Bool[torch.Tensor, "batch elements"],
    ]
):
    """Run a forward pass from latents and label IDs.

    Args:
        latents: Per-element latent vectors shaped `(batch, sequence, latent_size)`.
        labels: Label IDs shaped `(batch, sequence)`.
        attention_mask: Optional mask where true values mark valid labels.
        padding_mask: Optional mask where true values mark padded labels.
        return_dict: Whether to return a `LayoutGANPPModelOutput`.

    Returns:
        Model output dataclass or tuple containing boxes, labels, and mask.

    Raises:
        ValueError: If labels or latents have invalid shape or label IDs.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
        >>> labels = torch.tensor([[0, 1]])
        >>> latents = torch.zeros(1, 2, model.config.latent_size)
        >>> tuple(model(latents=latents, labels=labels).bbox.shape)
        (1, 2, 4)
    """
    labels = labels.to(dtype=torch.long)
    if labels.ndim != 2:
        raise ValueError("labels must have shape (batch, sequence)")

    if latents.shape[:2] != labels.shape:
        raise ValueError("latents must have shape (batch, sequence, latent_size)")

    if latents.shape[-1] != self.config.latent_size:
        raise ValueError(
            f"latents last dimension must be {self.config.latent_size}"
        )

    if labels.numel() and (
        int(labels.min().item()) < 0
        or int(labels.max().item()) >= self.config.num_labels
    ):
        raise ValueError("labels contain ids outside config.num_labels")

    if padding_mask is None:
        if attention_mask is None:
            padding_mask = torch.zeros(
                labels.shape, dtype=torch.bool, device=labels.device
            )
        else:
            padding_mask = ~attention_mask.to(
                device=labels.device, dtype=torch.bool
            )
    else:
        padding_mask = padding_mask.to(device=labels.device, dtype=torch.bool)
    latents = latents.to(device=labels.device, dtype=self.dtype)
    z = self.fc_z(latents)
    label_emb = self.emb_label(labels)
    hidden = torch.cat([z, label_emb], dim=-1)
    hidden = torch.relu(self.fc_in(hidden)).permute(1, 0, 2)
    hidden = self.transformer(hidden, src_key_padding_mask=padding_mask)
    bbox = torch.sigmoid(self.fc_out(hidden.permute(1, 0, 2)))
    mask = ~padding_mask
    if not return_dict:
        return bbox, labels, mask
    return LayoutGANPPModelOutput(
        bbox=bbox, labels=labels, mask=mask, latents=latents
    )

generate

generate(
    *,
    batch_size: int = 1,
    condition_type: ConditionType
    | str = ConditionType.label,
    bbox: Float[Tensor, "batch elements 4"] | None = None,
    labels: Int[Tensor, "batch elements"] | None = None,
    mask: Bool[Tensor, "batch elements"] | None = None,
    attention_mask: Bool[Tensor, "batch elements"]
    | 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,
    seed: int | None = None,
    generator: Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[Tensor, "batch elements latent"]
    | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict

Generate layouts from label conditions.

Parameters:

Name Type Description Default
batch_size int

Requested batch size; label shape determines the final value.

1
condition_type ConditionType | str

Condition type or alias. LayoutGAN++ supports label conditions.

label
bbox Float[Tensor, 'batch elements 4'] | None

Reserved compatibility argument.

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

Required label IDs for generation.

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

Optional valid-element mask.

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

Optional valid-element mask.

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

Reserved compatibility argument.

None
box_format BoxFormat | str

Reserved compatibility argument.

xywh
normalized bool

Reserved compatibility argument.

True
canvas_size tuple[int, int] | None

Reserved compatibility argument.

None
seed int | None

Optional random seed for latent sampling.

None
generator Generator | None

Optional PyTorch random generator.

None
num_inference_steps int | None

Reserved compatibility argument.

None
output_type OutputType | str

Return format, either dataclass or dict.

dataclass
return_intermediates bool

Whether to include generation intermediates.

False
latents Float[Tensor, 'batch elements latent'] | None

Optional fixed latent vectors.

None

Returns:

Type Description
LayoutGenerationOutput | LayoutGANPPOutputDict

A layout generation dataclass or dictionary.

Raises:

Type Description
ValueError

If labels are missing, generation options are unsupported, or output type is invalid.

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
>>> out = model.generate(labels=torch.tensor([[0, 1]]), seed=0)
>>> tuple(out.bbox.shape)
(1, 2, 4)
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
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
@torch.no_grad()
def generate(
    self,
    *,
    batch_size: int = 1,
    condition_type: ConditionType | str = ConditionType.label,
    bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
    labels: Int[torch.Tensor, "batch elements"] | None = None,
    mask: Bool[torch.Tensor, "batch elements"] | None = None,
    attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[torch.Tensor, "batch elements latent"] | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
    """Generate layouts from label conditions.

    Args:
        batch_size: Requested batch size; label shape determines the final value.
        condition_type: Condition type or alias. LayoutGAN++ supports label conditions.
        bbox: Reserved compatibility argument.
        labels: Required label IDs for generation.
        mask: Optional valid-element mask.
        attention_mask: Optional valid-element mask.
        num_elements: Reserved compatibility argument.
        box_format: Reserved compatibility argument.
        normalized: Reserved compatibility argument.
        canvas_size: Reserved compatibility argument.
        seed: Optional random seed for latent sampling.
        generator: Optional PyTorch random generator.
        num_inference_steps: Reserved compatibility argument.
        output_type: Return format, either `dataclass` or `dict`.
        return_intermediates: Whether to include generation intermediates.
        latents: Optional fixed latent vectors.

    Returns:
        A layout generation dataclass or dictionary.

    Raises:
        ValueError: If labels are missing, generation options are unsupported,
            or output type is invalid.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
        >>> out = model.generate(labels=torch.tensor([[0, 1]]), seed=0)
        >>> tuple(out.bbox.shape)
        (1, 2, 4)
    """
    del bbox, num_elements, normalized, canvas_size, num_inference_steps
    normalize_box_format(box_format)
    canonical = normalize_condition_type(condition_type)
    if canonical is ConditionType.unconditional:
        raise ValueError(
            "layoutganpp v1 requires labels; unconditional is unsupported"
        )

    if canonical is not ConditionType.label:
        raise ValueError(f"Unsupported condition_type for layoutganpp: {canonical}")

    if labels is None:
        raise ValueError("labels are required for layoutganpp generation")

    device = next(self.parameters()).device
    labels = torch.as_tensor(labels, dtype=torch.long, device=device)
    if labels.ndim == 1:
        labels = labels.unsqueeze(0)
    batch_size = labels.shape[0]
    if mask is not None:
        attention_mask = mask
    if attention_mask is None:
        attention_mask = torch.ones(labels.shape, dtype=torch.bool, device=device)
    else:
        attention_mask = torch.as_tensor(
            attention_mask, dtype=torch.bool, device=device
        )
        if attention_mask.ndim == 1:
            attention_mask = attention_mask.unsqueeze(0)
    if latents is None:
        latents = self._sample_latents(
            (batch_size, labels.shape[1], self.config.latent_size),
            seed=seed,
            generator=generator,
            device=device,
            dtype=self.dtype,
        )
    else:
        latents = torch.as_tensor(latents, dtype=self.dtype, device=device)
    out = self.forward(
        latents=latents,
        labels=labels,
        attention_mask=attention_mask,
        return_dict=True,
    )
    assert isinstance(out, LayoutGANPPModelOutput)
    assert out.labels is not None
    assert out.mask is not None
    assert out.latents is not None
    layout = LayoutGenerationOutput(
        bbox=out.bbox.detach().cpu(),
        labels=out.labels.detach().cpu(),
        mask=out.mask.detach().cpu(),
        id2label={int(k): v for k, v in self.config.id2label.items()},
        intermediates={
            "condition_type": canonical,
            "latents": out.latents.detach().cpu() if return_intermediates else None,
        }
        if return_intermediates
        else None,
    )
    resolved_output_type = normalize_output_type(output_type)
    if resolved_output_type is OutputType.dict:
        return cast(LayoutGANPPOutputDict, dict(layout))
    if resolved_output_type is OutputType.dataclass:
        return layout
    assert_never(resolved_output_type)

LayoutGANPPModelOutput dataclass

Bases: ModelOutput

Raw LayoutGAN++ model output.

Parameters:

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

Generated normalized xywh boxes with shape (batch, sequence, 4).

required
labels Int[Tensor, 'batch elements'] | None

Optional label IDs used for generation.

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

Optional valid-element mask.

None
latents Float[Tensor, 'batch elements latent'] | None

Optional latent vectors used by the generator.

None

Examples:

>>> out = LayoutGANPPModelOutput(bbox=torch.zeros(1, 1, 4))
>>> tuple(out.bbox.shape)
(1, 1, 4)
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@dataclass
class LayoutGANPPModelOutput(ModelOutput):
    """Raw LayoutGAN++ model output.

    Args:
        bbox: Generated normalized `xywh` boxes with shape `(batch, sequence, 4)`.
        labels: Optional label IDs used for generation.
        mask: Optional valid-element mask.
        latents: Optional latent vectors used by the generator.

    Examples:
        >>> out = LayoutGANPPModelOutput(bbox=torch.zeros(1, 1, 4))
        >>> tuple(out.bbox.shape)
        (1, 1, 4)
    """

    bbox: Float[torch.Tensor, "batch elements 4"]
    labels: Int[torch.Tensor, "batch elements"] | None = None
    mask: Bool[torch.Tensor, "batch elements"] | None = None
    latents: Float[torch.Tensor, "batch elements latent"] | None = None

OutputType

Bases: StrEnum

Supported LayoutGAN++ generation output formats.

Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
58
59
60
61
62
class OutputType(StrEnum):
    """Supported LayoutGAN++ generation output formats."""

    dataclass = auto()
    dict = auto()

LayoutGANPPPipeline

Bases: LayoutGenerationPipeline

Transformers pipeline for LayoutGAN++ label-conditioned generation.

Parameters:

Name Type Description Default
model LayoutGANPPModel

LayoutGAN++ model instance.

required
processor LayoutGANPPProcessor | None

Optional processor for label encoding and decoding.

None
config LayoutGANPPConfig | None

Optional root pipeline config. Defaults to model.config.

None
device int | device | None

Optional torch device passed to the base pipeline.

None
binary_output bool

Whether the base pipeline should produce binary output.

False

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
>>> pipe = LayoutGANPPPipeline(model=model)
>>> pipe.model.config.model_type
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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
class LayoutGANPPPipeline(LayoutGenerationPipeline):
    """Transformers pipeline for LayoutGAN++ label-conditioned generation.

    Args:
        model: LayoutGAN++ model instance.
        processor: Optional processor for label encoding and decoding.
        config: Optional root pipeline config. Defaults to `model.config`.
        device: Optional torch device passed to the base pipeline.
        binary_output: Whether the base pipeline should produce binary output.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
        >>> pipe = LayoutGANPPPipeline(model=model)
        >>> pipe.model.config.model_type
        'layoutganpp'
    """

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

    config: LayoutGANPPConfig
    model: LayoutGANPPModel
    processor: LayoutGANPPProcessor

    def __init__(
        self,
        model: LayoutGANPPModel,
        processor: LayoutGANPPProcessor | None = None,
        config: LayoutGANPPConfig | None = None,
        device: int | torch.device | None = None,
        binary_output: bool = False,
    ) -> None:
        """Initialize a LayoutGAN++ pipeline.

        Args:
            model: LayoutGAN++ model instance.
            processor: Optional processor for label encoding and decoding.
            config: Optional root pipeline config.
            device: Optional torch device passed to the base pipeline.
            binary_output: Whether the base pipeline should produce binary output.

        Examples:
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> isinstance(pipe.processor, LayoutGANPPProcessor)
            True
        """
        _ = binary_output
        super().__init__(config or model.config)
        self.config = config or model.config
        self.model = model
        self.processor = processor or LayoutGANPPProcessor(
            dataset_name=model.config.dataset_name,
            id2label=model.config.id2label,
        )
        if device is not None:
            resolved_device = (
                torch.device("cpu")
                if isinstance(device, int) and device < 0
                else torch.device(f"cuda:{device}")
                if isinstance(device, int)
                else device
            )
            self.to(resolved_device)

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

    def _sanitize_parameters(
        self, **kwargs: LayoutGANPPPipelineKwarg
    ) -> tuple[
        dict[str, LayoutGANPPPipelineKwarg],
        dict[str, LayoutGANPPPipelineKwarg],
        dict[str, LayoutGANPPPipelineKwarg],
    ]:
        sanitized = cast(
            dict[str, LayoutGANPPPipelineKwarg],
            kwargs,
        )
        return {}, sanitized, {}

    def preprocess(
        self,
        input_: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        **preprocess_parameters: LayoutGANPPPipelineKwarg,
    ) -> BatchEncoding:
        """Encode pipeline inputs into model inputs.

        Args:
            input_: Labels supplied as the positional pipeline input.
            **preprocess_parameters: Keyword labels and generation arguments.

        Returns:
            Batch encoding containing label IDs, attention mask, and generation kwargs.

        Raises:
            ValueError: If labels are not supplied or cannot be encoded.

        Examples:
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> "labels" in pipe.preprocess(["Toolbar"])
            True
        """
        labels = preprocess_parameters.pop("labels", input_)
        if labels is None:
            raise ValueError("labels are required for LayoutGANPPPipeline")

        encoded = self._layoutganpp_processor()(
            cast(list[list[str | int]] | list[str | int] | torch.Tensor, labels)
        )
        encoded.update(preprocess_parameters)
        return encoded

    def _forward(
        self,
        model_inputs: dict[str, LayoutGANPPPipelineKwarg],
        **forward_params: LayoutGANPPPipelineKwarg,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
        del forward_params
        labels = torch.as_tensor(model_inputs.pop("labels"), dtype=torch.long)
        attention_mask = torch.as_tensor(
            model_inputs.pop("attention_mask"), dtype=torch.bool
        )
        condition_type = cast(
            ConditionType | str, model_inputs.pop("condition_type", ConditionType.label)
        )
        bbox = cast(torch.Tensor | None, model_inputs.pop("bbox", None))
        mask = cast(torch.Tensor | None, model_inputs.pop("mask", None))
        num_elements = cast(
            int | list[int] | torch.Tensor | None,
            model_inputs.pop("num_elements", None),
        )
        box_format = cast(
            BoxFormat | str, model_inputs.pop("box_format", BoxFormat.xywh)
        )
        normalized = cast(bool, model_inputs.pop("normalized", True))
        canvas_size = cast(
            tuple[int, int] | None, model_inputs.pop("canvas_size", None)
        )
        seed = cast(int | None, model_inputs.pop("seed", None))
        generator = cast(torch.Generator | None, model_inputs.pop("generator", None))
        num_inference_steps = cast(
            int | None, model_inputs.pop("num_inference_steps", None)
        )
        output_type = cast(
            OutputType | str, model_inputs.pop("output_type", OutputType.dataclass)
        )
        return_intermediates = cast(
            bool, model_inputs.pop("return_intermediates", False)
        )
        latents = cast(torch.Tensor | None, model_inputs.pop("latents", None))
        if model_inputs:
            unknown = ", ".join(sorted(model_inputs))
            raise ValueError(f"Unsupported generation kwargs: {unknown}")

        return self._layoutganpp_model().generate(
            condition_type=condition_type,
            bbox=bbox,
            labels=labels,
            mask=mask,
            attention_mask=attention_mask,
            num_elements=num_elements,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
            seed=seed,
            generator=generator,
            num_inference_steps=num_inference_steps,
            output_type=output_type,
            return_intermediates=return_intermediates,
            latents=latents,
        )

    def postprocess(
        self,
        model_outputs: LayoutGenerationOutput | LayoutGANPPOutputDict,
        **kwargs: LayoutGANPPPipelineKwarg,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
        """Return generated layouts from the pipeline output.

        Args:
            model_outputs: Output produced by `LayoutGANPPModel.generate`.
            **kwargs: Reserved post-processing keyword arguments.

        Returns:
            The generated layout output unchanged.

        Examples:
            >>> output = LayoutGenerationOutput(bbox=torch.zeros(1, 1, 4))
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> pipe.postprocess(output) is output
            True
        """
        del kwargs
        return model_outputs

    @torch.no_grad()
    def __call__(
        self,
        labels: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        *,
        batch_size: int = 1,
        condition_type: ConditionType | str = ConditionType.label,
        bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
        mask: Bool[torch.Tensor, "batch elements"] | None = None,
        attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        num_inference_steps: int | None = None,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
        latents: Float[torch.Tensor, "batch elements latent"] | None = None,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:  # ty: ignore[invalid-method-override]
        """Generate LayoutGAN++ boxes from labels.

        Args:
            labels: Label strings or label IDs to condition on.
            batch_size: Reserved compatibility argument.
            condition_type: Condition type or alias.
            bbox: Reserved compatibility argument.
            mask: Optional valid-element mask.
            attention_mask: Optional valid-element mask.
            num_elements: Reserved compatibility argument.
            box_format: Reserved compatibility argument.
            normalized: Reserved compatibility argument.
            canvas_size: Reserved compatibility argument.
            seed: Optional random seed for latent sampling.
            generator: Optional PyTorch random generator.
            num_inference_steps: Reserved compatibility argument.
            output_type: Return format, either `dataclass` or `dict`.
            return_intermediates: Whether to include generation intermediates.
            latents: Optional fixed latent vectors.

        Returns:
            A layout generation dataclass or dictionary.

        Raises:
            ValueError: If labels are missing or generation options are invalid.

        Examples:
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> out = pipe(labels=["Toolbar"], seed=0)
            >>> tuple(out.bbox.shape)
            (1, 1, 4)
        """
        del batch_size
        if labels is None:
            raise ValueError("labels are required for layoutganpp v1")

        if isinstance(labels, torch.Tensor):
            encoded_labels = labels
            resolved_mask = attention_mask if attention_mask is not None else mask
        else:
            encoded = self._layoutganpp_processor()(labels)
            encoded_labels = encoded["labels"]
            if attention_mask is not None:
                resolved_mask = attention_mask
            else:
                resolved_mask = encoded["attention_mask"] if mask is None else mask
        return self._layoutganpp_model().generate(
            condition_type=condition_type,
            bbox=bbox,
            labels=cast(torch.Tensor, encoded_labels),
            mask=cast(torch.Tensor | None, resolved_mask),
            num_elements=num_elements,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
            seed=seed,
            generator=generator,
            num_inference_steps=num_inference_steps,
            output_type=output_type,
            return_intermediates=return_intermediates,
            latents=latents,
        )

    def _layoutganpp_model(self) -> LayoutGANPPModel:
        return self.model

    def _layoutganpp_processor(self) -> LayoutGANPPProcessor:
        return self.processor

__init__

__init__(
    model: LayoutGANPPModel,
    processor: LayoutGANPPProcessor | None = None,
    config: LayoutGANPPConfig | None = None,
    device: int | device | None = None,
    binary_output: bool = False,
) -> None

Initialize a LayoutGAN++ pipeline.

Parameters:

Name Type Description Default
model LayoutGANPPModel

LayoutGAN++ model instance.

required
processor LayoutGANPPProcessor | None

Optional processor for label encoding and decoding.

None
config LayoutGANPPConfig | None

Optional root pipeline config.

None
device int | device | None

Optional torch device passed to the base pipeline.

None
binary_output bool

Whether the base pipeline should produce binary output.

False

Examples:

>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> isinstance(pipe.processor, LayoutGANPPProcessor)
True
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
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
def __init__(
    self,
    model: LayoutGANPPModel,
    processor: LayoutGANPPProcessor | None = None,
    config: LayoutGANPPConfig | None = None,
    device: int | torch.device | None = None,
    binary_output: bool = False,
) -> None:
    """Initialize a LayoutGAN++ pipeline.

    Args:
        model: LayoutGAN++ model instance.
        processor: Optional processor for label encoding and decoding.
        config: Optional root pipeline config.
        device: Optional torch device passed to the base pipeline.
        binary_output: Whether the base pipeline should produce binary output.

    Examples:
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> isinstance(pipe.processor, LayoutGANPPProcessor)
        True
    """
    _ = binary_output
    super().__init__(config or model.config)
    self.config = config or model.config
    self.model = model
    self.processor = processor or LayoutGANPPProcessor(
        dataset_name=model.config.dataset_name,
        id2label=model.config.id2label,
    )
    if device is not None:
        resolved_device = (
            torch.device("cpu")
            if isinstance(device, int) and device < 0
            else torch.device(f"cuda:{device}")
            if isinstance(device, int)
            else device
        )
        self.to(resolved_device)

preprocess

preprocess(
    input_: list[list[str | int]]
    | list[str | int]
    | Int[Tensor, "batch elements"]
    | None = None,
    **preprocess_parameters: LayoutGANPPPipelineKwarg,
) -> BatchEncoding

Encode pipeline inputs into model inputs.

Parameters:

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

Labels supplied as the positional pipeline input.

None
**preprocess_parameters LayoutGANPPPipelineKwarg

Keyword labels and generation arguments.

{}

Returns:

Type Description
BatchEncoding

Batch encoding containing label IDs, attention mask, and generation kwargs.

Raises:

Type Description
ValueError

If labels are not supplied or cannot be encoded.

Examples:

>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> "labels" in pipe.preprocess(["Toolbar"])
True
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
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
def preprocess(
    self,
    input_: list[list[str | int]]
    | list[str | int]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    **preprocess_parameters: LayoutGANPPPipelineKwarg,
) -> BatchEncoding:
    """Encode pipeline inputs into model inputs.

    Args:
        input_: Labels supplied as the positional pipeline input.
        **preprocess_parameters: Keyword labels and generation arguments.

    Returns:
        Batch encoding containing label IDs, attention mask, and generation kwargs.

    Raises:
        ValueError: If labels are not supplied or cannot be encoded.

    Examples:
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> "labels" in pipe.preprocess(["Toolbar"])
        True
    """
    labels = preprocess_parameters.pop("labels", input_)
    if labels is None:
        raise ValueError("labels are required for LayoutGANPPPipeline")

    encoded = self._layoutganpp_processor()(
        cast(list[list[str | int]] | list[str | int] | torch.Tensor, labels)
    )
    encoded.update(preprocess_parameters)
    return encoded

postprocess

postprocess(
    model_outputs: LayoutGenerationOutput
    | LayoutGANPPOutputDict,
    **kwargs: LayoutGANPPPipelineKwarg,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict

Return generated layouts from the pipeline output.

Parameters:

Name Type Description Default
model_outputs LayoutGenerationOutput | LayoutGANPPOutputDict

Output produced by LayoutGANPPModel.generate.

required
**kwargs LayoutGANPPPipelineKwarg

Reserved post-processing keyword arguments.

{}

Returns:

Type Description
LayoutGenerationOutput | LayoutGANPPOutputDict

The generated layout output unchanged.

Examples:

>>> output = LayoutGenerationOutput(bbox=torch.zeros(1, 1, 4))
>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> pipe.postprocess(output) is output
True
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def postprocess(
    self,
    model_outputs: LayoutGenerationOutput | LayoutGANPPOutputDict,
    **kwargs: LayoutGANPPPipelineKwarg,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
    """Return generated layouts from the pipeline output.

    Args:
        model_outputs: Output produced by `LayoutGANPPModel.generate`.
        **kwargs: Reserved post-processing keyword arguments.

    Returns:
        The generated layout output unchanged.

    Examples:
        >>> output = LayoutGenerationOutput(bbox=torch.zeros(1, 1, 4))
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> pipe.postprocess(output) is output
        True
    """
    del kwargs
    return model_outputs

__call__

__call__(
    labels: list[list[str | int]]
    | list[str | int]
    | Int[Tensor, "batch elements"]
    | None = None,
    *,
    batch_size: int = 1,
    condition_type: ConditionType
    | str = ConditionType.label,
    bbox: Float[Tensor, "batch elements 4"] | None = None,
    mask: Bool[Tensor, "batch elements"] | None = None,
    attention_mask: Bool[Tensor, "batch elements"]
    | 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,
    seed: int | None = None,
    generator: Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[Tensor, "batch elements latent"]
    | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict

Generate LayoutGAN++ boxes from labels.

Parameters:

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

Label strings or label IDs to condition on.

None
batch_size int

Reserved compatibility argument.

1
condition_type ConditionType | str

Condition type or alias.

label
bbox Float[Tensor, 'batch elements 4'] | None

Reserved compatibility argument.

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

Optional valid-element mask.

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

Optional valid-element mask.

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

Reserved compatibility argument.

None
box_format BoxFormat | str

Reserved compatibility argument.

xywh
normalized bool

Reserved compatibility argument.

True
canvas_size tuple[int, int] | None

Reserved compatibility argument.

None
seed int | None

Optional random seed for latent sampling.

None
generator Generator | None

Optional PyTorch random generator.

None
num_inference_steps int | None

Reserved compatibility argument.

None
output_type OutputType | str

Return format, either dataclass or dict.

dataclass
return_intermediates bool

Whether to include generation intermediates.

False
latents Float[Tensor, 'batch elements latent'] | None

Optional fixed latent vectors.

None

Returns:

Type Description
LayoutGenerationOutput | LayoutGANPPOutputDict

A layout generation dataclass or dictionary.

Raises:

Type Description
ValueError

If labels are missing or generation options are invalid.

Examples:

>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> out = pipe(labels=["Toolbar"], seed=0)
>>> tuple(out.bbox.shape)
(1, 1, 4)
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
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
@torch.no_grad()
def __call__(
    self,
    labels: list[list[str | int]]
    | list[str | int]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    *,
    batch_size: int = 1,
    condition_type: ConditionType | str = ConditionType.label,
    bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
    mask: Bool[torch.Tensor, "batch elements"] | None = None,
    attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[torch.Tensor, "batch elements latent"] | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict:  # ty: ignore[invalid-method-override]
    """Generate LayoutGAN++ boxes from labels.

    Args:
        labels: Label strings or label IDs to condition on.
        batch_size: Reserved compatibility argument.
        condition_type: Condition type or alias.
        bbox: Reserved compatibility argument.
        mask: Optional valid-element mask.
        attention_mask: Optional valid-element mask.
        num_elements: Reserved compatibility argument.
        box_format: Reserved compatibility argument.
        normalized: Reserved compatibility argument.
        canvas_size: Reserved compatibility argument.
        seed: Optional random seed for latent sampling.
        generator: Optional PyTorch random generator.
        num_inference_steps: Reserved compatibility argument.
        output_type: Return format, either `dataclass` or `dict`.
        return_intermediates: Whether to include generation intermediates.
        latents: Optional fixed latent vectors.

    Returns:
        A layout generation dataclass or dictionary.

    Raises:
        ValueError: If labels are missing or generation options are invalid.

    Examples:
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> out = pipe(labels=["Toolbar"], seed=0)
        >>> tuple(out.bbox.shape)
        (1, 1, 4)
    """
    del batch_size
    if labels is None:
        raise ValueError("labels are required for layoutganpp v1")

    if isinstance(labels, torch.Tensor):
        encoded_labels = labels
        resolved_mask = attention_mask if attention_mask is not None else mask
    else:
        encoded = self._layoutganpp_processor()(labels)
        encoded_labels = encoded["labels"]
        if attention_mask is not None:
            resolved_mask = attention_mask
        else:
            resolved_mask = encoded["attention_mask"] if mask is None else mask
    return self._layoutganpp_model().generate(
        condition_type=condition_type,
        bbox=bbox,
        labels=cast(torch.Tensor, encoded_labels),
        mask=cast(torch.Tensor | None, resolved_mask),
        num_elements=num_elements,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
        seed=seed,
        generator=generator,
        num_inference_steps=num_inference_steps,
        output_type=output_type,
        return_intermediates=return_intermediates,
        latents=latents,
    )

LayoutGANPPProcessor

Bases: ProcessorMixin

Encode LayoutGAN++ labels and decode generated layouts.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

rico13
id2label Id2LabelMapping | None

Optional label ID to text mapping.

None

Examples:

>>> processor = LayoutGANPPProcessor(dataset_name="rico")
>>> processor.label2id["Toolbar"]
0
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
class LayoutGANPPProcessor(ProcessorMixin):
    """Encode LayoutGAN++ labels and decode generated layouts.

    Args:
        dataset_name: Dataset key or alias.
        id2label: Optional label ID to text mapping.

    Examples:
        >>> processor = LayoutGANPPProcessor(dataset_name="rico")
        >>> processor.label2id["Toolbar"]
        0
    """

    config_name = "preprocessor_config.json"

    def __init__(
        self,
        dataset_name: DatasetName | str = DatasetName.rico13,
        id2label: Id2LabelMapping | None = None,
    ) -> None:
        """Initialize a LayoutGAN++ processor.

        Args:
            dataset_name: Dataset key or alias.
            id2label: Optional label ID to text mapping.

        Raises:
            ValueError: If the dataset name is unsupported.

        Examples:
            >>> LayoutGANPPProcessor("publaynet").id2label[0]
            'text'
        """
        self.chat_template = None
        self.dataset_name = str(normalize_dataset_name(dataset_name))
        raw_id2label = id2label or id2label_for_dataset(self.dataset_name)
        self.id2label = {int(k): v for k, v in raw_id2label.items()}
        self.label2id = {v: k for k, v in self.id2label.items()}

    def __call__(
        self,
        labels: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"],
        *,
        padding: bool = True,
        return_tensors: Literal["pt"] = "pt",
    ) -> BatchEncoding:
        """Encode label strings or IDs into tensors.

        Args:
            labels: Label strings, label IDs, or a tensor of label IDs.
            padding: Whether to pad ragged batches.
            return_tensors: Tensor framework. Only `pt` is supported.

        Returns:
            Batch encoding with `labels` and `attention_mask` tensors.

        Raises:
            ValueError: If labels are empty, ragged without padding, unknown,
                or `return_tensors` is not `pt`.

        Examples:
            >>> processor = LayoutGANPPProcessor()
            >>> encoded = processor(["Toolbar", "Image"])
            >>> tuple(encoded["labels"].shape)
            (1, 2)
        """
        if return_tensors != "pt":
            raise ValueError("LayoutGANPPProcessor only supports return_tensors='pt'")

        rows = self._normalize_rows(labels)
        max_len = max(len(row) for row in rows)
        if not padding and len({len(row) for row in rows}) != 1:
            raise ValueError("Ragged labels require padding=True")

        encoded = []
        attention = []
        for row in rows:
            ids = [self._label_to_id(label) for label in row]
            pad = max_len - len(ids)
            encoded.append(ids + [0] * pad)
            attention.append([True] * len(ids) + [False] * pad)
        return BatchEncoding(
            {
                "labels": torch.tensor(encoded, dtype=torch.long),
                "attention_mask": torch.tensor(attention, dtype=torch.bool),
            }
        )

    def batch_decode(
        self,
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
    ) -> list[list[DecodedLayoutGANPPRecord]]:
        """Decode generated boxes and label IDs into records.

        Args:
            bbox: Generated boxes shaped `(batch, sequence, 4)` or `(sequence, 4)`.
            labels: Label IDs shaped `(batch, sequence)` or `(sequence,)`.
            attention_mask: Optional valid-element mask.

        Returns:
            Nested records containing label text, label ID, and bounding box.

        Raises:
            KeyError: If a label ID is not known to this processor.

        Examples:
            >>> processor = LayoutGANPPProcessor()
            >>> records = processor.batch_decode(
            ...     torch.zeros(1, 1, 4), torch.tensor([[0]])
            ... )
            >>> records[0][0]["label"]
            'Toolbar'
        """
        bbox_t = torch.as_tensor(bbox, dtype=torch.float32)
        labels_t = torch.as_tensor(labels, dtype=torch.long)
        if labels_t.ndim == 1:
            labels_t = labels_t.unsqueeze(0)
            bbox_t = bbox_t.unsqueeze(0)
        if attention_mask is None:
            mask_t = torch.ones(labels_t.shape, dtype=torch.bool)
        else:
            mask_t = torch.as_tensor(attention_mask, dtype=torch.bool)
            if mask_t.ndim == 1:
                mask_t = mask_t.unsqueeze(0)
        records: list[list[DecodedLayoutGANPPRecord]] = []
        for boxes, ids, mask in zip(bbox_t, labels_t, mask_t, strict=True):
            row: list[DecodedLayoutGANPPRecord] = []
            for box, label_id in zip(boxes[mask], ids[mask], strict=True):
                idx = int(label_id.item())
                row.append(
                    {
                        "label": self.id2label[idx],
                        "label_id": idx,
                        "bbox": box.tolist(),
                    }
                )
            records.append(row)
        return records

    def _normalize_rows(
        self,
        labels: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"],
    ) -> list[list[str | int]]:
        if isinstance(labels, torch.Tensor):
            if labels.ndim == 1:
                return [[int(v) for v in labels.tolist()]]
            return [[int(v) for v in row] for row in labels.tolist()]
        if not labels:
            raise ValueError("labels must not be empty")

        first = labels[0]
        if isinstance(first, list):
            rows: list[list[str | int]] = []
            for row in labels:
                if not isinstance(row, list):
                    raise ValueError("labels must be a flat list or list of rows")

                rows.append(row)
            return rows
        row = []
        for label in labels:
            if isinstance(label, list):
                raise ValueError("labels must be a flat list or list of rows")

            row.append(label)
        return [row]

    def _label_to_id(self, label: str | int) -> int:
        if isinstance(label, int):
            if label < 0 or label >= len(self.id2label):
                raise ValueError(f"Unknown label id: {label}")

            return label
        try:
            return self.label2id[label]
        except KeyError as exc:
            raise ValueError(f"Unknown label: {label}") from exc

__init__

__init__(
    dataset_name: DatasetName | str = DatasetName.rico13,
    id2label: Id2LabelMapping | None = None,
) -> None

Initialize a LayoutGAN++ processor.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

rico13
id2label Id2LabelMapping | None

Optional label ID to text mapping.

None

Raises:

Type Description
ValueError

If the dataset name is unsupported.

Examples:

>>> LayoutGANPPProcessor("publaynet").id2label[0]
'text'
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def __init__(
    self,
    dataset_name: DatasetName | str = DatasetName.rico13,
    id2label: Id2LabelMapping | None = None,
) -> None:
    """Initialize a LayoutGAN++ processor.

    Args:
        dataset_name: Dataset key or alias.
        id2label: Optional label ID to text mapping.

    Raises:
        ValueError: If the dataset name is unsupported.

    Examples:
        >>> LayoutGANPPProcessor("publaynet").id2label[0]
        'text'
    """
    self.chat_template = None
    self.dataset_name = str(normalize_dataset_name(dataset_name))
    raw_id2label = id2label or id2label_for_dataset(self.dataset_name)
    self.id2label = {int(k): v for k, v in raw_id2label.items()}
    self.label2id = {v: k for k, v in self.id2label.items()}

__call__

__call__(
    labels: list[list[str | int]]
    | list[str | int]
    | Int[Tensor, "batch elements"],
    *,
    padding: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding

Encode label strings or IDs into tensors.

Parameters:

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

Label strings, label IDs, or a tensor of label IDs.

required
padding bool

Whether to pad ragged batches.

True
return_tensors Literal['pt']

Tensor framework. Only pt is supported.

'pt'

Returns:

Type Description
BatchEncoding

Batch encoding with labels and attention_mask tensors.

Raises:

Type Description
ValueError

If labels are empty, ragged without padding, unknown, or return_tensors is not pt.

Examples:

>>> processor = LayoutGANPPProcessor()
>>> encoded = processor(["Toolbar", "Image"])
>>> tuple(encoded["labels"].shape)
(1, 2)
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.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
100
101
102
103
104
105
106
107
108
109
110
111
112
def __call__(
    self,
    labels: list[list[str | int]]
    | list[str | int]
    | Int[torch.Tensor, "batch elements"],
    *,
    padding: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding:
    """Encode label strings or IDs into tensors.

    Args:
        labels: Label strings, label IDs, or a tensor of label IDs.
        padding: Whether to pad ragged batches.
        return_tensors: Tensor framework. Only `pt` is supported.

    Returns:
        Batch encoding with `labels` and `attention_mask` tensors.

    Raises:
        ValueError: If labels are empty, ragged without padding, unknown,
            or `return_tensors` is not `pt`.

    Examples:
        >>> processor = LayoutGANPPProcessor()
        >>> encoded = processor(["Toolbar", "Image"])
        >>> tuple(encoded["labels"].shape)
        (1, 2)
    """
    if return_tensors != "pt":
        raise ValueError("LayoutGANPPProcessor only supports return_tensors='pt'")

    rows = self._normalize_rows(labels)
    max_len = max(len(row) for row in rows)
    if not padding and len({len(row) for row in rows}) != 1:
        raise ValueError("Ragged labels require padding=True")

    encoded = []
    attention = []
    for row in rows:
        ids = [self._label_to_id(label) for label in row]
        pad = max_len - len(ids)
        encoded.append(ids + [0] * pad)
        attention.append([True] * len(ids) + [False] * pad)
    return BatchEncoding(
        {
            "labels": torch.tensor(encoded, dtype=torch.long),
            "attention_mask": torch.tensor(attention, dtype=torch.bool),
        }
    )

batch_decode

batch_decode(
    bbox: Float[Tensor, "batch elements 4"],
    labels: Int[Tensor, "batch elements"],
    attention_mask: Bool[Tensor, "batch elements"]
    | None = None,
) -> list[list[DecodedLayoutGANPPRecord]]

Decode generated boxes and label IDs into records.

Parameters:

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

Generated boxes shaped (batch, sequence, 4) or (sequence, 4).

required
labels Int[Tensor, 'batch elements']

Label IDs shaped (batch, sequence) or (sequence,).

required
attention_mask Bool[Tensor, 'batch elements'] | None

Optional valid-element mask.

None

Returns:

Type Description
list[list[DecodedLayoutGANPPRecord]]

Nested records containing label text, label ID, and bounding box.

Raises:

Type Description
KeyError

If a label ID is not known to this processor.

Examples:

>>> processor = LayoutGANPPProcessor()
>>> records = processor.batch_decode(
...     torch.zeros(1, 1, 4), torch.tensor([[0]])
... )
>>> records[0][0]["label"]
'Toolbar'
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def batch_decode(
    self,
    bbox: Float[torch.Tensor, "batch elements 4"],
    labels: Int[torch.Tensor, "batch elements"],
    attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
) -> list[list[DecodedLayoutGANPPRecord]]:
    """Decode generated boxes and label IDs into records.

    Args:
        bbox: Generated boxes shaped `(batch, sequence, 4)` or `(sequence, 4)`.
        labels: Label IDs shaped `(batch, sequence)` or `(sequence,)`.
        attention_mask: Optional valid-element mask.

    Returns:
        Nested records containing label text, label ID, and bounding box.

    Raises:
        KeyError: If a label ID is not known to this processor.

    Examples:
        >>> processor = LayoutGANPPProcessor()
        >>> records = processor.batch_decode(
        ...     torch.zeros(1, 1, 4), torch.tensor([[0]])
        ... )
        >>> records[0][0]["label"]
        'Toolbar'
    """
    bbox_t = torch.as_tensor(bbox, dtype=torch.float32)
    labels_t = torch.as_tensor(labels, dtype=torch.long)
    if labels_t.ndim == 1:
        labels_t = labels_t.unsqueeze(0)
        bbox_t = bbox_t.unsqueeze(0)
    if attention_mask is None:
        mask_t = torch.ones(labels_t.shape, dtype=torch.bool)
    else:
        mask_t = torch.as_tensor(attention_mask, dtype=torch.bool)
        if mask_t.ndim == 1:
            mask_t = mask_t.unsqueeze(0)
    records: list[list[DecodedLayoutGANPPRecord]] = []
    for boxes, ids, mask in zip(bbox_t, labels_t, mask_t, strict=True):
        row: list[DecodedLayoutGANPPRecord] = []
        for box, label_id in zip(boxes[mask], ids[mask], strict=True):
            idx = int(label_id.item())
            row.append(
                {
                    "label": self.id2label[idx],
                    "label_id": idx,
                    "bbox": box.tolist(),
                }
            )
        records.append(row)
    return records

label2id_for_dataset

label2id_for_dataset(
    dataset_name: DatasetName | str,
) -> dict[str, int]

Return a label-to-ID mapping for a dataset.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

required

Returns:

Type Description
dict[str, int]

Dictionary mapping label names to integer IDs.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> label2id_for_dataset("rico")["Toolbar"]
0
Source code in models/layoutganpp/src/layoutganpp/datasets.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
def label2id_for_dataset(dataset_name: DatasetName | str) -> dict[str, int]:
    """Return a label-to-ID mapping for a dataset.

    Args:
        dataset_name: Dataset key or alias.

    Returns:
        Dictionary mapping label names to integer IDs.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> label2id_for_dataset("rico")["Toolbar"]
        0
    """
    return {label: i for i, label in id2label_for_dataset(dataset_name).items()}

labels_for_dataset

labels_for_dataset(
    dataset_name: DatasetName | str,
) -> tuple[StrEnum, ...]

Return labels for a LayoutGAN++ dataset.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

required

Returns:

Type Description
tuple[StrEnum, ...]

Label names in checkpoint order.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> labels_for_dataset("publaynet")[0]
'text'
Source code in models/layoutganpp/src/layoutganpp/datasets.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def labels_for_dataset(dataset_name: DatasetName | str) -> tuple[StrEnum, ...]:
    """Return labels for a LayoutGAN++ dataset.

    Args:
        dataset_name: Dataset key or alias.

    Returns:
        Label names in checkpoint order.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> labels_for_dataset("publaynet")[0]
        'text'
    """
    return dataset_metadata(dataset_name)["labels"]

normalize_condition_type

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

Normalize condition aliases to a canonical ConditionType.

Parameters:

Name Type Description Default
condition_type ConditionType | str

Canonical condition enum or a public/release alias.

required

Returns:

Type Description
ConditionType

Canonical condition enum.

Raises:

Type Description
ValueError

If the condition type is unknown.

Examples:

>>> str(normalize_condition_type("gen_t"))
'label'
>>> str(normalize_condition_type("gen_r"))
'relation'
Source code in lib/laygen/src/laygen/common/conditions.py
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
def normalize_condition_type(condition_type: ConditionType | str) -> ConditionType:
    """Normalize condition aliases to a canonical ``ConditionType``.

    Args:
        condition_type: Canonical condition enum or a public/release alias.

    Returns:
        Canonical condition enum.

    Raises:
        ValueError: If the condition type is unknown.

    Examples:
        >>> str(normalize_condition_type("gen_t"))
        'label'
        >>> str(normalize_condition_type("gen_r"))
        'relation'
    """
    if isinstance(condition_type, ConditionType):
        return condition_type
    try:
        return _CONDITION_ALIASES[
            ConditionAlias(condition_type.lower().replace("-", "_"))
        ]
    except ValueError as exc:
        raise ValueError(f"Unknown condition_type: {condition_type}") from exc

normalize_output_type

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

Normalize a public output type value.

Parameters:

Name Type Description Default
output_type OutputType | str

Output type enum or string.

required

Returns:

Type Description
OutputType

Normalized output type enum.

Raises:

Type Description
ValueError

If output_type is unsupported.

Examples:

>>> str(normalize_output_type("dict"))
'dict'
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def normalize_output_type(output_type: OutputType | str) -> OutputType:
    """Normalize a public output type value.

    Args:
        output_type: Output type enum or string.

    Returns:
        Normalized output type enum.

    Raises:
        ValueError: If `output_type` is unsupported.

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

layoutganpp_model_card

layoutganpp_model_card(
    dataset: DatasetName | str,
) -> ModelCard

Build a Hugging Face model card for a LayoutGAN++ dataset.

Parameters:

Name Type Description Default
dataset DatasetName | str

Dataset key or alias for the converted checkpoint.

required

Returns:

Type Description
ModelCard

A populated ModelCard for the selected checkpoint.

Raises:

Type Description
ValueError

If dataset is not a supported LayoutGAN++ dataset.

Examples:

>>> card = layoutganpp_model_card("rico")
>>> "layoutganpp-rico" in str(card)
True
Source code in models/layoutganpp/src/layoutganpp/model_card.py
 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
def layoutganpp_model_card(dataset: DatasetName | str) -> ModelCard:
    """Build a Hugging Face model card for a LayoutGAN++ dataset.

    Args:
        dataset: Dataset key or alias for the converted checkpoint.

    Returns:
        A populated `ModelCard` for the selected checkpoint.

    Raises:
        ValueError: If `dataset` is not a supported LayoutGAN++ dataset.

    Examples:
        >>> card = layoutganpp_model_card("rico")
        >>> "layoutganpp-rico" in str(card)
        True
    """
    dataset_name = normalize_dataset_name(dataset)
    dataset_key = _CHECKPOINT_KEYS[dataset_name]
    model_id = _CHECKPOINT_IDS[dataset_name]
    metrics = _PARITY_METRICS[dataset_name]
    how_to_use = f"""
from layoutganpp import LayoutGANPPPipeline

pipe = LayoutGANPPPipeline.from_pretrained("{model_id}")
out = pipe(labels={_EXAMPLE_LABELS[dataset_name]}, seed=0)
print(out.bbox, out.labels, out.mask)
"""
    return build_layout_model_card(
        model_id=model_id,
        model_name=f"LayoutGAN++ {dataset_key}",
        dataset_ids=[_DATASET_IDS[dataset_name]],
        license="agpl-3.0",
        library_name="transformers",
        pipeline_tag="other",
        tags=[
            "layout-generation",
            "layoutganpp",
            "layoutgan++",
            "transformers",
            str(dataset_key),
        ],
        model_details=(
            "Transformers-style conversion of the LayoutGAN++ generator from "
            "`Constrained Graphic Layout Generation via Latent Optimization` "
            f"for `{dataset_key}`. The model generates normalized center `xywh` "
            "layout boxes from category-label conditions. Reference parity compares "
            f"bbox tensors with shape {_parity_metric(metrics, ParityMetricKey.shape)} "
            "against local const-layout fixtures with "
            "`torch.testing.assert_close(atol=1e-6, rtol=1e-5)`."
        ),
        intended_uses=(
            "Use this checkpoint for research on constrained graphic layout "
            "generation and for regression tests that need deterministic "
            "LayoutGAN++ bbox generation from fixed labels and latents."
        ),
        limitations=(
            "This package ports the released generator only. It does not include "
            "LayoutGAN++ latent optimization loops, training code, rendered image "
            "generation, or dataset preprocessing pipelines."
        ),
        how_to_use=how_to_use,
        training_data=(
            f"The original checkpoint was trained on `{_DATASET_IDS[dataset_name]}` "
            "following the upstream LayoutGAN++ release."
        ),
        parity_metrics=[
            {
                "dataset": str(dataset_key),
                "tokenizer_exact": "n/a",
                "deterministic_exact": "bbox exact",
                "logits_max_abs": 0.0,
                "logits_max_rel": 0.0,
            }
        ],
        citation_bibtex=_BIBTEX,
        original_implementation_url="https://github.com/ktrk115/const_layout",
    )

write_layoutganpp_model_card

write_layoutganpp_model_card(
    output_dir: Path, dataset: DatasetName | str
) -> Path

Write a LayoutGAN++ model card to an output directory.

Parameters:

Name Type Description Default
output_dir Path

Directory that will receive README.md.

required
dataset DatasetName | str

Dataset key or alias for the converted checkpoint.

required

Returns:

Type Description
Path

Path to the written README.md file.

Raises:

Type Description
ValueError

If dataset is not a supported LayoutGAN++ dataset.

Examples:

>>> from tempfile import TemporaryDirectory
>>> with TemporaryDirectory() as tmp:
...     path = write_layoutganpp_model_card(Path(tmp), "rico")
...     path.name
'README.md'
Source code in models/layoutganpp/src/layoutganpp/model_card.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def write_layoutganpp_model_card(output_dir: Path, dataset: DatasetName | str) -> Path:
    """Write a LayoutGAN++ model card to an output directory.

    Args:
        output_dir: Directory that will receive `README.md`.
        dataset: Dataset key or alias for the converted checkpoint.

    Returns:
        Path to the written `README.md` file.

    Raises:
        ValueError: If `dataset` is not a supported LayoutGAN++ dataset.

    Examples:
        >>> from tempfile import TemporaryDirectory
        >>> with TemporaryDirectory() as tmp:
        ...     path = write_layoutganpp_model_card(Path(tmp), "rico")
        ...     path.name
        'README.md'
    """
    output_dir.mkdir(parents=True, exist_ok=True)
    readme_path = output_dir / "README.md"
    readme_path.write_text(str(layoutganpp_model_card(dataset)), encoding="utf-8")
    return readme_path

bbox

Bounding-box helpers re-exported for LayoutGAN++ users.

clip_normalized_xywh

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

Clamp normalized box coordinates into the inclusive [0, 1] range.

Source code in lib/laygen/src/laygen/common/bbox.py
102
103
104
def clamp_boxes(bbox: Float[torch.Tensor, "... 4"]) -> Float[torch.Tensor, "... 4"]:
    """Clamp normalized box coordinates into the inclusive ``[0, 1]`` range."""
    return bbox.clamp(0.0, 1.0)

ltrb_to_xywh

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

Convert ltrb boxes to normalized center xywh boxes.

Source code in lib/laygen/src/laygen/common/bbox.py
75
76
77
78
79
80
81
82
83
def ltrb_to_xywh(bbox: Float[torch.Tensor, "... 4"]) -> Float[torch.Tensor, "... 4"]:
    """Convert ``ltrb`` boxes to normalized center ``xywh`` boxes."""
    import torch

    left, top, right, bottom = bbox.unbind(dim=-1)
    return torch.stack(
        ((left + right) / 2, (top + bottom) / 2, right - left, bottom - top),
        dim=-1,
    )

xywh_to_ltrb

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

Convert normalized center xywh boxes to ltrb boxes.

Parameters:

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

torch.Tensor with the last dimension ordered as center x, center y, width, and height.

required

Returns:

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

torch.Tensor with the same leading shape and last dimension ordered as left,

Float[Tensor, '... 4']

top, right, and bottom.

Examples:

>>> import torch
>>> xywh_to_ltrb(torch.tensor([[0.5, 0.5, 0.2, 0.4]])).shape
torch.Size([1, 4])
Source code in lib/laygen/src/laygen/common/bbox.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def xywh_to_ltrb(bbox: Float[torch.Tensor, "... 4"]) -> Float[torch.Tensor, "... 4"]:
    """Convert normalized center ``xywh`` boxes to ``ltrb`` boxes.

    Args:
        bbox: torch.Tensor with the last dimension ordered as center x, center y,
            width, and height.

    Returns:
        torch.Tensor with the same leading shape and last dimension ordered as left,
        top, right, and bottom.

    Examples:
        >>> import torch
        >>> xywh_to_ltrb(torch.tensor([[0.5, 0.5, 0.2, 0.4]])).shape
        torch.Size([1, 4])
    """
    import torch

    x, y, w, h = bbox.unbind(dim=-1)
    return torch.stack((x - w / 2, y - h / 2, x + w / 2, y + h / 2), dim=-1)

layout_to_image

layout_to_image(
    bbox: Float[Tensor, "elements 4"],
    labels: Int[Tensor, "elements"],
    mask: Bool[Tensor, "elements"],
    id2label: dict[int, str],
    *,
    ax: Axes | None = None,
    canvas_size: tuple[int, int] = (1, 1),
    colors: Iterable[str] | None = None,
) -> Axes

Render one layout on a Matplotlib axis.

Parameters:

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

Normalized center xywh boxes for one sample.

required
labels Int[Tensor, 'elements']

Integer labels for one sample.

required
mask Bool[Tensor, 'elements']

Boolean valid-element mask for one sample.

required
id2label dict[int, str]

Mapping from integer ids to label names.

required
ax Axes | None

Optional Matplotlib axis. A new axis is created when omitted.

None
canvas_size tuple[int, int]

Canvas size as (width, height).

(1, 1)
colors Iterable[str] | None

Optional color cycle.

None

Returns:

Type Description
Axes

Axis containing rectangle patches and label text.

Examples:

>>> import torch
>>> ax = render_layout(
...     torch.zeros(1, 4),
...     torch.zeros(1, dtype=torch.long),
...     torch.ones(1, dtype=torch.bool),
...     {0: "text"},
... )
>>> ax is not None
True
Source code in lib/laygen/src/laygen/common/visualization.py
16
17
18
19
20
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
def render_layout(
    bbox: Float[torch.Tensor, "elements 4"],
    labels: Int[torch.Tensor, "elements"],
    mask: Bool[torch.Tensor, "elements"],
    id2label: dict[int, str],
    *,
    ax: Axes | None = None,
    canvas_size: tuple[int, int] = (1, 1),
    colors: Iterable[str] | None = None,
) -> Axes:
    """Render one layout on a Matplotlib axis.

    Args:
        bbox: Normalized center ``xywh`` boxes for one sample.
        labels: Integer labels for one sample.
        mask: Boolean valid-element mask for one sample.
        id2label: Mapping from integer ids to label names.
        ax: Optional Matplotlib axis. A new axis is created when omitted.
        canvas_size: Canvas size as ``(width, height)``.
        colors: Optional color cycle.

    Returns:
        Axis containing rectangle patches and label text.

    Examples:
        >>> import torch
        >>> ax = render_layout(
        ...     torch.zeros(1, 4),
        ...     torch.zeros(1, dtype=torch.long),
        ...     torch.ones(1, dtype=torch.bool),
        ...     {0: "text"},
        ... )
        >>> ax is not None
        True
    """
    if ax is None:
        _, ax = plt.subplots()
    palette = list(colors or plt.rcParams["axes.prop_cycle"].by_key()["color"])
    width, height = canvas_size
    ax.set_xlim(0, width)
    ax.set_ylim(height, 0)
    ax.set_aspect("equal")
    ltrb = xywh_to_ltrb(bbox.detach().cpu())
    for i, valid in enumerate(mask.detach().cpu().tolist()):
        if not valid:
            continue
        left, top, right, bottom = ltrb[i].tolist()
        color = palette[int(labels[i]) % len(palette)]
        rect = Rectangle(
            (left * width, top * height),
            (right - left) * width,
            (bottom - top) * height,
            fill=False,
            edgecolor=color,
        )
        ax.add_patch(rect)
        ax.text(
            left * width,
            top * height,
            id2label.get(int(labels[i]), str(int(labels[i]))),
            color=color,
            fontsize=8,
        )
    return ax

configuration_layoutganpp

Configuration objects for LayoutGAN++ checkpoints.

LayoutGANPPConfig

Bases: PretrainedConfig

Configuration for the LayoutGAN++ generator.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias used to resolve labels and sequence length.

rico13
latent_size int

Size of each per-element latent vector.

4
num_labels int | None

Optional label vocabulary size override.

None
id2label Id2LabelMapping | None

Optional mapping from label IDs to display labels.

None
label2id dict[str, int] | None

Optional mapping from display labels to label IDs.

None
d_model int

Transformer hidden size used by the generator.

512
nhead int

Number of transformer attention heads.

8
num_layers int

Number of transformer encoder layers.

4
bbox_format BoxFormat | str

Bounding-box format produced by the model.

xywh
bbox_normalized bool

Whether generated boxes are normalized to the canvas.

True
max_position_embeddings int | None

Maximum element count for generated layouts.

None
**kwargs LayoutGANPPConfigValue

Extra PretrainedConfig keyword arguments.

{}

Examples:

>>> config = LayoutGANPPConfig(dataset_name="rico")
>>> config.model_type
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/configuration_layoutganpp.py
 20
 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
class LayoutGANPPConfig(PretrainedConfig):
    """Configuration for the LayoutGAN++ generator.

    Args:
        dataset_name: Dataset key or alias used to resolve labels and sequence length.
        latent_size: Size of each per-element latent vector.
        num_labels: Optional label vocabulary size override.
        id2label: Optional mapping from label IDs to display labels.
        label2id: Optional mapping from display labels to label IDs.
        d_model: Transformer hidden size used by the generator.
        nhead: Number of transformer attention heads.
        num_layers: Number of transformer encoder layers.
        bbox_format: Bounding-box format produced by the model.
        bbox_normalized: Whether generated boxes are normalized to the canvas.
        max_position_embeddings: Maximum element count for generated layouts.
        **kwargs: Extra `PretrainedConfig` keyword arguments.

    Examples:
        >>> config = LayoutGANPPConfig(dataset_name="rico")
        >>> config.model_type
        'layoutganpp'
    """

    model_type = "layoutganpp"

    def __init__(
        self,
        dataset_name: DatasetName | str = DatasetName.rico13,
        latent_size: int = 4,
        num_labels: int | None = None,
        id2label: Id2LabelMapping | None = None,
        label2id: dict[str, int] | None = None,
        d_model: int = 512,
        nhead: int = 8,
        num_layers: int = 4,
        bbox_format: BoxFormat | str = BoxFormat.xywh,
        bbox_normalized: bool = True,
        max_position_embeddings: int | None = None,
        **kwargs: LayoutGANPPConfigValue,
    ) -> None:
        """Initialize a LayoutGAN++ config.

        Args:
            dataset_name: Dataset key or alias used to resolve labels and metadata.
            latent_size: Size of each latent vector passed to the generator.
            num_labels: Optional explicit label vocabulary size.
            id2label: Optional label ID to text mapping.
            label2id: Optional label text to ID mapping.
            d_model: Transformer hidden size.
            nhead: Number of attention heads.
            num_layers: Number of transformer encoder layers.
            bbox_format: Format of generated bounding boxes.
            bbox_normalized: Whether generated boxes are normalized.
            max_position_embeddings: Optional maximum layout length override.
            **kwargs: Extra `PretrainedConfig` keyword arguments.

        Raises:
            ValueError: If `dataset_name` is not a supported LayoutGAN++ dataset.

        Examples:
            >>> LayoutGANPPConfig(dataset_name="publaynet").num_labels
            5
        """
        metadata = dataset_metadata(dataset_name)
        raw_id2label = id2label or id2label_for_dataset(dataset_name)
        normalized_id2label = {int(k): v for k, v in raw_id2label.items()}
        normalized_label2id = label2id or {
            label: i for i, label in normalized_id2label.items()
        }
        resolved_num_labels = num_labels or len(normalized_id2label)
        super().__init__(
            id2label=normalized_id2label,
            label2id=normalized_label2id,
        )
        for key, value in kwargs.items():
            setattr(self, key, value)
        self.dataset_name = str(metadata["name"])
        self.latent_size = latent_size
        self.num_labels = resolved_num_labels
        self.d_model = d_model
        self.nhead = nhead
        self.num_layers = num_layers
        self.bbox_format = str(normalize_box_format(bbox_format))
        self.bbox_normalized = bbox_normalized
        self.max_position_embeddings = (
            max_position_embeddings or max_elements_for_dataset(metadata["name"])
        )
        self.architectures = ["LayoutGANPPModel"]

__init__

__init__(
    dataset_name: DatasetName | str = DatasetName.rico13,
    latent_size: int = 4,
    num_labels: int | None = None,
    id2label: Id2LabelMapping | None = None,
    label2id: dict[str, int] | None = None,
    d_model: int = 512,
    nhead: int = 8,
    num_layers: int = 4,
    bbox_format: BoxFormat | str = BoxFormat.xywh,
    bbox_normalized: bool = True,
    max_position_embeddings: int | None = None,
    **kwargs: LayoutGANPPConfigValue,
) -> None

Initialize a LayoutGAN++ config.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias used to resolve labels and metadata.

rico13
latent_size int

Size of each latent vector passed to the generator.

4
num_labels int | None

Optional explicit label vocabulary size.

None
id2label Id2LabelMapping | None

Optional label ID to text mapping.

None
label2id dict[str, int] | None

Optional label text to ID mapping.

None
d_model int

Transformer hidden size.

512
nhead int

Number of attention heads.

8
num_layers int

Number of transformer encoder layers.

4
bbox_format BoxFormat | str

Format of generated bounding boxes.

xywh
bbox_normalized bool

Whether generated boxes are normalized.

True
max_position_embeddings int | None

Optional maximum layout length override.

None
**kwargs LayoutGANPPConfigValue

Extra PretrainedConfig keyword arguments.

{}

Raises:

Type Description
ValueError

If dataset_name is not a supported LayoutGAN++ dataset.

Examples:

>>> LayoutGANPPConfig(dataset_name="publaynet").num_labels
5
Source code in models/layoutganpp/src/layoutganpp/configuration_layoutganpp.py
 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
def __init__(
    self,
    dataset_name: DatasetName | str = DatasetName.rico13,
    latent_size: int = 4,
    num_labels: int | None = None,
    id2label: Id2LabelMapping | None = None,
    label2id: dict[str, int] | None = None,
    d_model: int = 512,
    nhead: int = 8,
    num_layers: int = 4,
    bbox_format: BoxFormat | str = BoxFormat.xywh,
    bbox_normalized: bool = True,
    max_position_embeddings: int | None = None,
    **kwargs: LayoutGANPPConfigValue,
) -> None:
    """Initialize a LayoutGAN++ config.

    Args:
        dataset_name: Dataset key or alias used to resolve labels and metadata.
        latent_size: Size of each latent vector passed to the generator.
        num_labels: Optional explicit label vocabulary size.
        id2label: Optional label ID to text mapping.
        label2id: Optional label text to ID mapping.
        d_model: Transformer hidden size.
        nhead: Number of attention heads.
        num_layers: Number of transformer encoder layers.
        bbox_format: Format of generated bounding boxes.
        bbox_normalized: Whether generated boxes are normalized.
        max_position_embeddings: Optional maximum layout length override.
        **kwargs: Extra `PretrainedConfig` keyword arguments.

    Raises:
        ValueError: If `dataset_name` is not a supported LayoutGAN++ dataset.

    Examples:
        >>> LayoutGANPPConfig(dataset_name="publaynet").num_labels
        5
    """
    metadata = dataset_metadata(dataset_name)
    raw_id2label = id2label or id2label_for_dataset(dataset_name)
    normalized_id2label = {int(k): v for k, v in raw_id2label.items()}
    normalized_label2id = label2id or {
        label: i for i, label in normalized_id2label.items()
    }
    resolved_num_labels = num_labels or len(normalized_id2label)
    super().__init__(
        id2label=normalized_id2label,
        label2id=normalized_label2id,
    )
    for key, value in kwargs.items():
        setattr(self, key, value)
    self.dataset_name = str(metadata["name"])
    self.latent_size = latent_size
    self.num_labels = resolved_num_labels
    self.d_model = d_model
    self.nhead = nhead
    self.num_layers = num_layers
    self.bbox_format = str(normalize_box_format(bbox_format))
    self.bbox_normalized = bbox_normalized
    self.max_position_embeddings = (
        max_position_embeddings or max_elements_for_dataset(metadata["name"])
    )
    self.architectures = ["LayoutGANPPModel"]

conversion

Conversion helpers for original LayoutGAN++ checkpoint metadata.

config_from_checkpoint_args

config_from_checkpoint_args(
    args: CheckpointArgs,
) -> LayoutGANPPConfig

Build a config from original LayoutGAN++ checkpoint arguments.

Parameters:

Name Type Description Default
args CheckpointArgs

Mapping or argparse-style namespace with upstream checkpoint fields.

required

Returns:

Type Description
LayoutGANPPConfig

A LayoutGANPPConfig populated from the checkpoint metadata.

Raises:

Type Description
KeyError

If a required upstream field is missing.

ValueError

If the dataset name is unsupported.

Examples:

>>> config_from_checkpoint_args(
...     {
...         "dataset": "rico",
...         "latent_size": 4,
...         "G_d_model": 512,
...         "G_nhead": 8,
...         "G_num_layers": 4,
...     }
... ).dataset_name
'rico'
Source code in models/layoutganpp/src/layoutganpp/conversion.py
20
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
def config_from_checkpoint_args(args: CheckpointArgs) -> LayoutGANPPConfig:
    """Build a config from original LayoutGAN++ checkpoint arguments.

    Args:
        args: Mapping or argparse-style namespace with upstream checkpoint fields.

    Returns:
        A `LayoutGANPPConfig` populated from the checkpoint metadata.

    Raises:
        KeyError: If a required upstream field is missing.
        ValueError: If the dataset name is unsupported.

    Examples:
        >>> config_from_checkpoint_args(
        ...     {
        ...         "dataset": "rico",
        ...         "latent_size": 4,
        ...         "G_d_model": 512,
        ...         "G_nhead": 8,
        ...         "G_num_layers": 4,
        ...     }
        ... ).dataset_name
        'rico'
    """
    values = _checkpoint_values(args)
    dataset_name = str(values["dataset"])
    canonical_dataset = normalize_dataset_name(dataset_name)
    id2label = id2label_for_dataset(dataset_name)
    return LayoutGANPPConfig(
        dataset_name=dataset_name,
        latent_size=_required_int(values, "latent_size"),
        num_labels=len(id2label),
        id2label=id2label,
        label2id={v: k for k, v in id2label.items()},
        d_model=_required_int(values, "G_d_model"),
        nhead=_required_int(values, "G_nhead"),
        num_layers=_required_int(values, "G_num_layers"),
        max_position_embeddings=max_elements_for_dataset(canonical_dataset),
    )

datasets

Dataset metadata and label helpers for LayoutGAN++ checkpoints.

RicoLabel

Bases: StrEnum

RICO label names in LayoutGAN++ checkpoint order.

Source code in models/layoutganpp/src/layoutganpp/datasets.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class RicoLabel(StrEnum):
    """RICO label names in LayoutGAN++ checkpoint order."""

    toolbar = "Toolbar"
    image = "Image"
    text = "Text"
    icon = "Icon"
    text_button = "Text Button"
    input = "Input"
    list_item = "List Item"
    advertisement = "Advertisement"
    pager_indicator = "Pager Indicator"
    web_view = "Web View"
    background_image = "Background Image"
    drawer = "Drawer"
    modal = "Modal"

DatasetAlias

Bases: StrEnum

LayoutGAN++ dataset aliases accepted at public boundaries.

Source code in models/layoutganpp/src/layoutganpp/datasets.py
29
30
31
32
33
34
35
36
class DatasetAlias(StrEnum):
    """LayoutGAN++ dataset aliases accepted at public boundaries."""

    rico = auto()
    rico13 = auto()
    publaynet = auto()
    pub_laynet = auto()
    magazine = auto()

DatasetMetadata

Bases: TypedDict

Metadata for a LayoutGAN++ checkpoint dataset.

Source code in models/layoutganpp/src/layoutganpp/datasets.py
44
45
46
47
48
class DatasetMetadata(TypedDict):
    """Metadata for a LayoutGAN++ checkpoint dataset."""

    name: DatasetName
    labels: tuple[StrEnum, ...]

normalize_dataset_name

normalize_dataset_name(
    dataset_name: DatasetName | str,
) -> DatasetName

Normalize a LayoutGAN++ dataset name or alias.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key such as rico, publaynet, or magazine.

required

Returns:

Type Description
DatasetName

The canonical dataset key.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> str(normalize_dataset_name("pub-laynet"))
'publaynet'
Source code in models/layoutganpp/src/layoutganpp/datasets.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def normalize_dataset_name(dataset_name: DatasetName | str) -> DatasetName:
    """Normalize a LayoutGAN++ dataset name or alias.

    Args:
        dataset_name: Dataset key such as `rico`, `publaynet`, or `magazine`.

    Returns:
        The canonical dataset key.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> str(normalize_dataset_name("pub-laynet"))
        'publaynet'
    """
    if isinstance(dataset_name, DatasetName):
        return dataset_name
    try:
        alias = DatasetAlias(dataset_name.lower().replace("-", "_"))
    except ValueError as exc:
        raise ValueError(f"Unknown layoutganpp dataset_name: {dataset_name}") from exc

    return _ALIASES[alias]

dataset_metadata

dataset_metadata(
    dataset_name: DatasetName | str,
) -> DatasetMetadata

Return metadata for a LayoutGAN++ dataset.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

required

Returns:

Type Description
DatasetMetadata

Metadata containing the canonical name, labels, and maximum element count.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> max_elements_for_dataset("rico13")
9
Source code in models/layoutganpp/src/layoutganpp/datasets.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def dataset_metadata(dataset_name: DatasetName | str) -> DatasetMetadata:
    """Return metadata for a LayoutGAN++ dataset.

    Args:
        dataset_name: Dataset key or alias.

    Returns:
        Metadata containing the canonical name, labels, and maximum element count.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> max_elements_for_dataset("rico13")
        9
    """
    return DATASET_METADATA[normalize_dataset_name(dataset_name)]

labels_for_dataset

labels_for_dataset(
    dataset_name: DatasetName | str,
) -> tuple[StrEnum, ...]

Return labels for a LayoutGAN++ dataset.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

required

Returns:

Type Description
tuple[StrEnum, ...]

Label names in checkpoint order.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> labels_for_dataset("publaynet")[0]
'text'
Source code in models/layoutganpp/src/layoutganpp/datasets.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def labels_for_dataset(dataset_name: DatasetName | str) -> tuple[StrEnum, ...]:
    """Return labels for a LayoutGAN++ dataset.

    Args:
        dataset_name: Dataset key or alias.

    Returns:
        Label names in checkpoint order.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> labels_for_dataset("publaynet")[0]
        'text'
    """
    return dataset_metadata(dataset_name)["labels"]

id2label_for_dataset

id2label_for_dataset(
    dataset_name: DatasetName | str,
) -> dict[int, str]

Return an ID-to-label mapping for a dataset.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

required

Returns:

Type Description
dict[int, str]

Dictionary mapping integer IDs to label names.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> id2label_for_dataset("magazine")[1]
'image'
Source code in models/layoutganpp/src/layoutganpp/datasets.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def id2label_for_dataset(dataset_name: DatasetName | str) -> dict[int, str]:
    """Return an ID-to-label mapping for a dataset.

    Args:
        dataset_name: Dataset key or alias.

    Returns:
        Dictionary mapping integer IDs to label names.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> id2label_for_dataset("magazine")[1]
        'image'
    """
    return {i: str(label) for i, label in enumerate(labels_for_dataset(dataset_name))}

label2id_for_dataset

label2id_for_dataset(
    dataset_name: DatasetName | str,
) -> dict[str, int]

Return a label-to-ID mapping for a dataset.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

required

Returns:

Type Description
dict[str, int]

Dictionary mapping label names to integer IDs.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> label2id_for_dataset("rico")["Toolbar"]
0
Source code in models/layoutganpp/src/layoutganpp/datasets.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
def label2id_for_dataset(dataset_name: DatasetName | str) -> dict[str, int]:
    """Return a label-to-ID mapping for a dataset.

    Args:
        dataset_name: Dataset key or alias.

    Returns:
        Dictionary mapping label names to integer IDs.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> label2id_for_dataset("rico")["Toolbar"]
        0
    """
    return {label: i for i, label in id2label_for_dataset(dataset_name).items()}

model_card

Model card builders for LayoutGAN++ checkpoint packages.

CheckpointKey

Bases: StrEnum

Dataset suffixes used in LayoutGAN++ checkpoint and Hub IDs.

Source code in models/layoutganpp/src/layoutganpp/model_card.py
15
16
17
18
19
20
class CheckpointKey(StrEnum):
    """Dataset suffixes used in LayoutGAN++ checkpoint and Hub IDs."""

    rico = auto()
    publaynet = auto()
    magazine = auto()

ParityMetricKey

Bases: StrEnum

Internal parity metric keys used in LayoutGAN++ model-card text.

Source code in models/layoutganpp/src/layoutganpp/model_card.py
23
24
25
26
27
class ParityMetricKey(StrEnum):
    """Internal parity metric keys used in LayoutGAN++ model-card text."""

    shape = auto()
    smoke_shape = auto()

ParityMetricText

Bases: TypedDict

Model-card parity text snippets for a converted checkpoint.

Source code in models/layoutganpp/src/layoutganpp/model_card.py
30
31
32
33
34
class ParityMetricText(TypedDict):
    """Model-card parity text snippets for a converted checkpoint."""

    shape: str
    smoke_shape: str

layoutganpp_model_card

layoutganpp_model_card(
    dataset: DatasetName | str,
) -> ModelCard

Build a Hugging Face model card for a LayoutGAN++ dataset.

Parameters:

Name Type Description Default
dataset DatasetName | str

Dataset key or alias for the converted checkpoint.

required

Returns:

Type Description
ModelCard

A populated ModelCard for the selected checkpoint.

Raises:

Type Description
ValueError

If dataset is not a supported LayoutGAN++ dataset.

Examples:

>>> card = layoutganpp_model_card("rico")
>>> "layoutganpp-rico" in str(card)
True
Source code in models/layoutganpp/src/layoutganpp/model_card.py
 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
def layoutganpp_model_card(dataset: DatasetName | str) -> ModelCard:
    """Build a Hugging Face model card for a LayoutGAN++ dataset.

    Args:
        dataset: Dataset key or alias for the converted checkpoint.

    Returns:
        A populated `ModelCard` for the selected checkpoint.

    Raises:
        ValueError: If `dataset` is not a supported LayoutGAN++ dataset.

    Examples:
        >>> card = layoutganpp_model_card("rico")
        >>> "layoutganpp-rico" in str(card)
        True
    """
    dataset_name = normalize_dataset_name(dataset)
    dataset_key = _CHECKPOINT_KEYS[dataset_name]
    model_id = _CHECKPOINT_IDS[dataset_name]
    metrics = _PARITY_METRICS[dataset_name]
    how_to_use = f"""
from layoutganpp import LayoutGANPPPipeline

pipe = LayoutGANPPPipeline.from_pretrained("{model_id}")
out = pipe(labels={_EXAMPLE_LABELS[dataset_name]}, seed=0)
print(out.bbox, out.labels, out.mask)
"""
    return build_layout_model_card(
        model_id=model_id,
        model_name=f"LayoutGAN++ {dataset_key}",
        dataset_ids=[_DATASET_IDS[dataset_name]],
        license="agpl-3.0",
        library_name="transformers",
        pipeline_tag="other",
        tags=[
            "layout-generation",
            "layoutganpp",
            "layoutgan++",
            "transformers",
            str(dataset_key),
        ],
        model_details=(
            "Transformers-style conversion of the LayoutGAN++ generator from "
            "`Constrained Graphic Layout Generation via Latent Optimization` "
            f"for `{dataset_key}`. The model generates normalized center `xywh` "
            "layout boxes from category-label conditions. Reference parity compares "
            f"bbox tensors with shape {_parity_metric(metrics, ParityMetricKey.shape)} "
            "against local const-layout fixtures with "
            "`torch.testing.assert_close(atol=1e-6, rtol=1e-5)`."
        ),
        intended_uses=(
            "Use this checkpoint for research on constrained graphic layout "
            "generation and for regression tests that need deterministic "
            "LayoutGAN++ bbox generation from fixed labels and latents."
        ),
        limitations=(
            "This package ports the released generator only. It does not include "
            "LayoutGAN++ latent optimization loops, training code, rendered image "
            "generation, or dataset preprocessing pipelines."
        ),
        how_to_use=how_to_use,
        training_data=(
            f"The original checkpoint was trained on `{_DATASET_IDS[dataset_name]}` "
            "following the upstream LayoutGAN++ release."
        ),
        parity_metrics=[
            {
                "dataset": str(dataset_key),
                "tokenizer_exact": "n/a",
                "deterministic_exact": "bbox exact",
                "logits_max_abs": 0.0,
                "logits_max_rel": 0.0,
            }
        ],
        citation_bibtex=_BIBTEX,
        original_implementation_url="https://github.com/ktrk115/const_layout",
    )

write_layoutganpp_model_card

write_layoutganpp_model_card(
    output_dir: Path, dataset: DatasetName | str
) -> Path

Write a LayoutGAN++ model card to an output directory.

Parameters:

Name Type Description Default
output_dir Path

Directory that will receive README.md.

required
dataset DatasetName | str

Dataset key or alias for the converted checkpoint.

required

Returns:

Type Description
Path

Path to the written README.md file.

Raises:

Type Description
ValueError

If dataset is not a supported LayoutGAN++ dataset.

Examples:

>>> from tempfile import TemporaryDirectory
>>> with TemporaryDirectory() as tmp:
...     path = write_layoutganpp_model_card(Path(tmp), "rico")
...     path.name
'README.md'
Source code in models/layoutganpp/src/layoutganpp/model_card.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def write_layoutganpp_model_card(output_dir: Path, dataset: DatasetName | str) -> Path:
    """Write a LayoutGAN++ model card to an output directory.

    Args:
        output_dir: Directory that will receive `README.md`.
        dataset: Dataset key or alias for the converted checkpoint.

    Returns:
        Path to the written `README.md` file.

    Raises:
        ValueError: If `dataset` is not a supported LayoutGAN++ dataset.

    Examples:
        >>> from tempfile import TemporaryDirectory
        >>> with TemporaryDirectory() as tmp:
        ...     path = write_layoutganpp_model_card(Path(tmp), "rico")
        ...     path.name
        'README.md'
    """
    output_dir.mkdir(parents=True, exist_ok=True)
    readme_path = output_dir / "README.md"
    readme_path.write_text(str(layoutganpp_model_card(dataset)), encoding="utf-8")
    return readme_path

modeling_layoutganpp

PyTorch model wrapper for the LayoutGAN++ generator.

LayoutGANPPModelOutput dataclass

Bases: ModelOutput

Raw LayoutGAN++ model output.

Parameters:

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

Generated normalized xywh boxes with shape (batch, sequence, 4).

required
labels Int[Tensor, 'batch elements'] | None

Optional label IDs used for generation.

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

Optional valid-element mask.

None
latents Float[Tensor, 'batch elements latent'] | None

Optional latent vectors used by the generator.

None

Examples:

>>> out = LayoutGANPPModelOutput(bbox=torch.zeros(1, 1, 4))
>>> tuple(out.bbox.shape)
(1, 1, 4)
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@dataclass
class LayoutGANPPModelOutput(ModelOutput):
    """Raw LayoutGAN++ model output.

    Args:
        bbox: Generated normalized `xywh` boxes with shape `(batch, sequence, 4)`.
        labels: Optional label IDs used for generation.
        mask: Optional valid-element mask.
        latents: Optional latent vectors used by the generator.

    Examples:
        >>> out = LayoutGANPPModelOutput(bbox=torch.zeros(1, 1, 4))
        >>> tuple(out.bbox.shape)
        (1, 1, 4)
    """

    bbox: Float[torch.Tensor, "batch elements 4"]
    labels: Int[torch.Tensor, "batch elements"] | None = None
    mask: Bool[torch.Tensor, "batch elements"] | None = None
    latents: Float[torch.Tensor, "batch elements latent"] | None = None

LayoutGANPPOutputDict

Bases: TypedDict

Dictionary form of LayoutGAN++ public output.

Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
45
46
47
48
49
50
51
52
53
54
55
class LayoutGANPPOutputDict(TypedDict, total=False):
    """Dictionary form of LayoutGAN++ public output."""

    bbox: Float[torch.Tensor, "batch elements 4"]
    labels: Int[torch.Tensor, "batch elements"]
    mask: Bool[torch.Tensor, "batch elements"]
    id2label: dict[int, str]
    intermediates: (
        dict[str, ConditionType | Float[torch.Tensor, "batch elements latent"] | None]
        | None
    )

OutputType

Bases: StrEnum

Supported LayoutGAN++ generation output formats.

Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
58
59
60
61
62
class OutputType(StrEnum):
    """Supported LayoutGAN++ generation output formats."""

    dataclass = auto()
    dict = auto()

LayoutGANPPModel

Bases: PreTrainedModel

Transformers-compatible LayoutGAN++ generator.

Parameters:

Name Type Description Default
config LayoutGANPPConfig

LayoutGAN++ model configuration.

required

Examples:

>>> config = LayoutGANPPConfig(num_labels=2, id2label={0: "a", 1: "b"})
>>> model = LayoutGANPPModel(config)
>>> model.config.model_type
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
 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
class LayoutGANPPModel(PreTrainedModel):
    """Transformers-compatible LayoutGAN++ generator.

    Args:
        config: LayoutGAN++ model configuration.

    Examples:
        >>> config = LayoutGANPPConfig(num_labels=2, id2label={0: "a", 1: "b"})
        >>> model = LayoutGANPPModel(config)
        >>> model.config.model_type
        'layoutganpp'
    """

    config_class = LayoutGANPPConfig
    base_model_prefix = "layoutganpp"
    supports_gradient_checkpointing = False

    def __init__(self, config: LayoutGANPPConfig) -> None:
        """Initialize the LayoutGAN++ generator layers.

        Args:
            config: LayoutGAN++ model configuration.

        Examples:
            >>> model = LayoutGANPPModel(LayoutGANPPConfig())
            >>> model.base_model_prefix
            'layoutganpp'
        """
        super().__init__(config)
        self.fc_z = nn.Linear(config.latent_size, config.d_model // 2)
        self.emb_label = nn.Embedding(config.num_labels, config.d_model // 2)
        self.fc_in = nn.Linear(config.d_model, config.d_model)
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=config.d_model,
            nhead=config.nhead,
            dim_feedforward=config.d_model // 2,
            batch_first=False,
        )
        self.transformer = nn.TransformerEncoder(
            encoder_layer, num_layers=config.num_layers
        )
        self.fc_out = nn.Linear(config.d_model, 4)
        self.post_init()

    def forward(
        self,
        latents: Float[torch.Tensor, "batch elements latent"],
        labels: Int[torch.Tensor, "batch elements"],
        attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
        padding_mask: Bool[torch.Tensor, "batch elements"] | None = None,
        return_dict: bool = True,
    ) -> (
        LayoutGANPPModelOutput
        | tuple[
            Float[torch.Tensor, "batch elements 4"],
            Int[torch.Tensor, "batch elements"],
            Bool[torch.Tensor, "batch elements"],
        ]
    ):
        """Run a forward pass from latents and label IDs.

        Args:
            latents: Per-element latent vectors shaped `(batch, sequence, latent_size)`.
            labels: Label IDs shaped `(batch, sequence)`.
            attention_mask: Optional mask where true values mark valid labels.
            padding_mask: Optional mask where true values mark padded labels.
            return_dict: Whether to return a `LayoutGANPPModelOutput`.

        Returns:
            Model output dataclass or tuple containing boxes, labels, and mask.

        Raises:
            ValueError: If labels or latents have invalid shape or label IDs.

        Examples:
            >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
            >>> labels = torch.tensor([[0, 1]])
            >>> latents = torch.zeros(1, 2, model.config.latent_size)
            >>> tuple(model(latents=latents, labels=labels).bbox.shape)
            (1, 2, 4)
        """
        labels = labels.to(dtype=torch.long)
        if labels.ndim != 2:
            raise ValueError("labels must have shape (batch, sequence)")

        if latents.shape[:2] != labels.shape:
            raise ValueError("latents must have shape (batch, sequence, latent_size)")

        if latents.shape[-1] != self.config.latent_size:
            raise ValueError(
                f"latents last dimension must be {self.config.latent_size}"
            )

        if labels.numel() and (
            int(labels.min().item()) < 0
            or int(labels.max().item()) >= self.config.num_labels
        ):
            raise ValueError("labels contain ids outside config.num_labels")

        if padding_mask is None:
            if attention_mask is None:
                padding_mask = torch.zeros(
                    labels.shape, dtype=torch.bool, device=labels.device
                )
            else:
                padding_mask = ~attention_mask.to(
                    device=labels.device, dtype=torch.bool
                )
        else:
            padding_mask = padding_mask.to(device=labels.device, dtype=torch.bool)
        latents = latents.to(device=labels.device, dtype=self.dtype)
        z = self.fc_z(latents)
        label_emb = self.emb_label(labels)
        hidden = torch.cat([z, label_emb], dim=-1)
        hidden = torch.relu(self.fc_in(hidden)).permute(1, 0, 2)
        hidden = self.transformer(hidden, src_key_padding_mask=padding_mask)
        bbox = torch.sigmoid(self.fc_out(hidden.permute(1, 0, 2)))
        mask = ~padding_mask
        if not return_dict:
            return bbox, labels, mask
        return LayoutGANPPModelOutput(
            bbox=bbox, labels=labels, mask=mask, latents=latents
        )

    @torch.no_grad()
    def generate(
        self,
        *,
        batch_size: int = 1,
        condition_type: ConditionType | str = ConditionType.label,
        bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
        labels: Int[torch.Tensor, "batch elements"] | None = None,
        mask: Bool[torch.Tensor, "batch elements"] | None = None,
        attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        num_inference_steps: int | None = None,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
        latents: Float[torch.Tensor, "batch elements latent"] | None = None,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
        """Generate layouts from label conditions.

        Args:
            batch_size: Requested batch size; label shape determines the final value.
            condition_type: Condition type or alias. LayoutGAN++ supports label conditions.
            bbox: Reserved compatibility argument.
            labels: Required label IDs for generation.
            mask: Optional valid-element mask.
            attention_mask: Optional valid-element mask.
            num_elements: Reserved compatibility argument.
            box_format: Reserved compatibility argument.
            normalized: Reserved compatibility argument.
            canvas_size: Reserved compatibility argument.
            seed: Optional random seed for latent sampling.
            generator: Optional PyTorch random generator.
            num_inference_steps: Reserved compatibility argument.
            output_type: Return format, either `dataclass` or `dict`.
            return_intermediates: Whether to include generation intermediates.
            latents: Optional fixed latent vectors.

        Returns:
            A layout generation dataclass or dictionary.

        Raises:
            ValueError: If labels are missing, generation options are unsupported,
                or output type is invalid.

        Examples:
            >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
            >>> out = model.generate(labels=torch.tensor([[0, 1]]), seed=0)
            >>> tuple(out.bbox.shape)
            (1, 2, 4)
        """
        del bbox, num_elements, normalized, canvas_size, num_inference_steps
        normalize_box_format(box_format)
        canonical = normalize_condition_type(condition_type)
        if canonical is ConditionType.unconditional:
            raise ValueError(
                "layoutganpp v1 requires labels; unconditional is unsupported"
            )

        if canonical is not ConditionType.label:
            raise ValueError(f"Unsupported condition_type for layoutganpp: {canonical}")

        if labels is None:
            raise ValueError("labels are required for layoutganpp generation")

        device = next(self.parameters()).device
        labels = torch.as_tensor(labels, dtype=torch.long, device=device)
        if labels.ndim == 1:
            labels = labels.unsqueeze(0)
        batch_size = labels.shape[0]
        if mask is not None:
            attention_mask = mask
        if attention_mask is None:
            attention_mask = torch.ones(labels.shape, dtype=torch.bool, device=device)
        else:
            attention_mask = torch.as_tensor(
                attention_mask, dtype=torch.bool, device=device
            )
            if attention_mask.ndim == 1:
                attention_mask = attention_mask.unsqueeze(0)
        if latents is None:
            latents = self._sample_latents(
                (batch_size, labels.shape[1], self.config.latent_size),
                seed=seed,
                generator=generator,
                device=device,
                dtype=self.dtype,
            )
        else:
            latents = torch.as_tensor(latents, dtype=self.dtype, device=device)
        out = self.forward(
            latents=latents,
            labels=labels,
            attention_mask=attention_mask,
            return_dict=True,
        )
        assert isinstance(out, LayoutGANPPModelOutput)
        assert out.labels is not None
        assert out.mask is not None
        assert out.latents is not None
        layout = LayoutGenerationOutput(
            bbox=out.bbox.detach().cpu(),
            labels=out.labels.detach().cpu(),
            mask=out.mask.detach().cpu(),
            id2label={int(k): v for k, v in self.config.id2label.items()},
            intermediates={
                "condition_type": canonical,
                "latents": out.latents.detach().cpu() if return_intermediates else None,
            }
            if return_intermediates
            else None,
        )
        resolved_output_type = normalize_output_type(output_type)
        if resolved_output_type is OutputType.dict:
            return cast(LayoutGANPPOutputDict, dict(layout))
        if resolved_output_type is OutputType.dataclass:
            return layout
        assert_never(resolved_output_type)

    def _sample_latents(
        self,
        shape: tuple[int, int, int],
        *,
        seed: int | None,
        generator: torch.Generator | None,
        device: torch.device,
        dtype: torch.dtype,
    ) -> Float[torch.Tensor, "batch elements latent"]:
        if generator is None and seed is not None:
            generator = torch.Generator(device=device).manual_seed(seed)
        return torch.randn(shape, generator=generator, device=device, dtype=dtype)

__init__

__init__(config: LayoutGANPPConfig) -> None

Initialize the LayoutGAN++ generator layers.

Parameters:

Name Type Description Default
config LayoutGANPPConfig

LayoutGAN++ model configuration.

required

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig())
>>> model.base_model_prefix
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.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
def __init__(self, config: LayoutGANPPConfig) -> None:
    """Initialize the LayoutGAN++ generator layers.

    Args:
        config: LayoutGAN++ model configuration.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig())
        >>> model.base_model_prefix
        'layoutganpp'
    """
    super().__init__(config)
    self.fc_z = nn.Linear(config.latent_size, config.d_model // 2)
    self.emb_label = nn.Embedding(config.num_labels, config.d_model // 2)
    self.fc_in = nn.Linear(config.d_model, config.d_model)
    encoder_layer = nn.TransformerEncoderLayer(
        d_model=config.d_model,
        nhead=config.nhead,
        dim_feedforward=config.d_model // 2,
        batch_first=False,
    )
    self.transformer = nn.TransformerEncoder(
        encoder_layer, num_layers=config.num_layers
    )
    self.fc_out = nn.Linear(config.d_model, 4)
    self.post_init()

forward

forward(
    latents: Float[Tensor, "batch elements latent"],
    labels: Int[Tensor, "batch elements"],
    attention_mask: Bool[Tensor, "batch elements"]
    | None = None,
    padding_mask: Bool[Tensor, "batch elements"]
    | None = None,
    return_dict: bool = True,
) -> (
    LayoutGANPPModelOutput
    | tuple[
        Float[torch.Tensor, "batch elements 4"],
        Int[torch.Tensor, "batch elements"],
        Bool[torch.Tensor, "batch elements"],
    ]
)

Run a forward pass from latents and label IDs.

Parameters:

Name Type Description Default
latents Float[Tensor, 'batch elements latent']

Per-element latent vectors shaped (batch, sequence, latent_size).

required
labels Int[Tensor, 'batch elements']

Label IDs shaped (batch, sequence).

required
attention_mask Bool[Tensor, 'batch elements'] | None

Optional mask where true values mark valid labels.

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

Optional mask where true values mark padded labels.

None
return_dict bool

Whether to return a LayoutGANPPModelOutput.

True

Returns:

Type Description
LayoutGANPPModelOutput | tuple[Float[Tensor, 'batch elements 4'], Int[Tensor, 'batch elements'], Bool[Tensor, 'batch elements']]

Model output dataclass or tuple containing boxes, labels, and mask.

Raises:

Type Description
ValueError

If labels or latents have invalid shape or label IDs.

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
>>> labels = torch.tensor([[0, 1]])
>>> latents = torch.zeros(1, 2, model.config.latent_size)
>>> tuple(model(latents=latents, labels=labels).bbox.shape)
(1, 2, 4)
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
def forward(
    self,
    latents: Float[torch.Tensor, "batch elements latent"],
    labels: Int[torch.Tensor, "batch elements"],
    attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
    padding_mask: Bool[torch.Tensor, "batch elements"] | None = None,
    return_dict: bool = True,
) -> (
    LayoutGANPPModelOutput
    | tuple[
        Float[torch.Tensor, "batch elements 4"],
        Int[torch.Tensor, "batch elements"],
        Bool[torch.Tensor, "batch elements"],
    ]
):
    """Run a forward pass from latents and label IDs.

    Args:
        latents: Per-element latent vectors shaped `(batch, sequence, latent_size)`.
        labels: Label IDs shaped `(batch, sequence)`.
        attention_mask: Optional mask where true values mark valid labels.
        padding_mask: Optional mask where true values mark padded labels.
        return_dict: Whether to return a `LayoutGANPPModelOutput`.

    Returns:
        Model output dataclass or tuple containing boxes, labels, and mask.

    Raises:
        ValueError: If labels or latents have invalid shape or label IDs.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
        >>> labels = torch.tensor([[0, 1]])
        >>> latents = torch.zeros(1, 2, model.config.latent_size)
        >>> tuple(model(latents=latents, labels=labels).bbox.shape)
        (1, 2, 4)
    """
    labels = labels.to(dtype=torch.long)
    if labels.ndim != 2:
        raise ValueError("labels must have shape (batch, sequence)")

    if latents.shape[:2] != labels.shape:
        raise ValueError("latents must have shape (batch, sequence, latent_size)")

    if latents.shape[-1] != self.config.latent_size:
        raise ValueError(
            f"latents last dimension must be {self.config.latent_size}"
        )

    if labels.numel() and (
        int(labels.min().item()) < 0
        or int(labels.max().item()) >= self.config.num_labels
    ):
        raise ValueError("labels contain ids outside config.num_labels")

    if padding_mask is None:
        if attention_mask is None:
            padding_mask = torch.zeros(
                labels.shape, dtype=torch.bool, device=labels.device
            )
        else:
            padding_mask = ~attention_mask.to(
                device=labels.device, dtype=torch.bool
            )
    else:
        padding_mask = padding_mask.to(device=labels.device, dtype=torch.bool)
    latents = latents.to(device=labels.device, dtype=self.dtype)
    z = self.fc_z(latents)
    label_emb = self.emb_label(labels)
    hidden = torch.cat([z, label_emb], dim=-1)
    hidden = torch.relu(self.fc_in(hidden)).permute(1, 0, 2)
    hidden = self.transformer(hidden, src_key_padding_mask=padding_mask)
    bbox = torch.sigmoid(self.fc_out(hidden.permute(1, 0, 2)))
    mask = ~padding_mask
    if not return_dict:
        return bbox, labels, mask
    return LayoutGANPPModelOutput(
        bbox=bbox, labels=labels, mask=mask, latents=latents
    )

generate

generate(
    *,
    batch_size: int = 1,
    condition_type: ConditionType
    | str = ConditionType.label,
    bbox: Float[Tensor, "batch elements 4"] | None = None,
    labels: Int[Tensor, "batch elements"] | None = None,
    mask: Bool[Tensor, "batch elements"] | None = None,
    attention_mask: Bool[Tensor, "batch elements"]
    | 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,
    seed: int | None = None,
    generator: Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[Tensor, "batch elements latent"]
    | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict

Generate layouts from label conditions.

Parameters:

Name Type Description Default
batch_size int

Requested batch size; label shape determines the final value.

1
condition_type ConditionType | str

Condition type or alias. LayoutGAN++ supports label conditions.

label
bbox Float[Tensor, 'batch elements 4'] | None

Reserved compatibility argument.

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

Required label IDs for generation.

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

Optional valid-element mask.

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

Optional valid-element mask.

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

Reserved compatibility argument.

None
box_format BoxFormat | str

Reserved compatibility argument.

xywh
normalized bool

Reserved compatibility argument.

True
canvas_size tuple[int, int] | None

Reserved compatibility argument.

None
seed int | None

Optional random seed for latent sampling.

None
generator Generator | None

Optional PyTorch random generator.

None
num_inference_steps int | None

Reserved compatibility argument.

None
output_type OutputType | str

Return format, either dataclass or dict.

dataclass
return_intermediates bool

Whether to include generation intermediates.

False
latents Float[Tensor, 'batch elements latent'] | None

Optional fixed latent vectors.

None

Returns:

Type Description
LayoutGenerationOutput | LayoutGANPPOutputDict

A layout generation dataclass or dictionary.

Raises:

Type Description
ValueError

If labels are missing, generation options are unsupported, or output type is invalid.

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
>>> out = model.generate(labels=torch.tensor([[0, 1]]), seed=0)
>>> tuple(out.bbox.shape)
(1, 2, 4)
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
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
@torch.no_grad()
def generate(
    self,
    *,
    batch_size: int = 1,
    condition_type: ConditionType | str = ConditionType.label,
    bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
    labels: Int[torch.Tensor, "batch elements"] | None = None,
    mask: Bool[torch.Tensor, "batch elements"] | None = None,
    attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[torch.Tensor, "batch elements latent"] | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
    """Generate layouts from label conditions.

    Args:
        batch_size: Requested batch size; label shape determines the final value.
        condition_type: Condition type or alias. LayoutGAN++ supports label conditions.
        bbox: Reserved compatibility argument.
        labels: Required label IDs for generation.
        mask: Optional valid-element mask.
        attention_mask: Optional valid-element mask.
        num_elements: Reserved compatibility argument.
        box_format: Reserved compatibility argument.
        normalized: Reserved compatibility argument.
        canvas_size: Reserved compatibility argument.
        seed: Optional random seed for latent sampling.
        generator: Optional PyTorch random generator.
        num_inference_steps: Reserved compatibility argument.
        output_type: Return format, either `dataclass` or `dict`.
        return_intermediates: Whether to include generation intermediates.
        latents: Optional fixed latent vectors.

    Returns:
        A layout generation dataclass or dictionary.

    Raises:
        ValueError: If labels are missing, generation options are unsupported,
            or output type is invalid.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
        >>> out = model.generate(labels=torch.tensor([[0, 1]]), seed=0)
        >>> tuple(out.bbox.shape)
        (1, 2, 4)
    """
    del bbox, num_elements, normalized, canvas_size, num_inference_steps
    normalize_box_format(box_format)
    canonical = normalize_condition_type(condition_type)
    if canonical is ConditionType.unconditional:
        raise ValueError(
            "layoutganpp v1 requires labels; unconditional is unsupported"
        )

    if canonical is not ConditionType.label:
        raise ValueError(f"Unsupported condition_type for layoutganpp: {canonical}")

    if labels is None:
        raise ValueError("labels are required for layoutganpp generation")

    device = next(self.parameters()).device
    labels = torch.as_tensor(labels, dtype=torch.long, device=device)
    if labels.ndim == 1:
        labels = labels.unsqueeze(0)
    batch_size = labels.shape[0]
    if mask is not None:
        attention_mask = mask
    if attention_mask is None:
        attention_mask = torch.ones(labels.shape, dtype=torch.bool, device=device)
    else:
        attention_mask = torch.as_tensor(
            attention_mask, dtype=torch.bool, device=device
        )
        if attention_mask.ndim == 1:
            attention_mask = attention_mask.unsqueeze(0)
    if latents is None:
        latents = self._sample_latents(
            (batch_size, labels.shape[1], self.config.latent_size),
            seed=seed,
            generator=generator,
            device=device,
            dtype=self.dtype,
        )
    else:
        latents = torch.as_tensor(latents, dtype=self.dtype, device=device)
    out = self.forward(
        latents=latents,
        labels=labels,
        attention_mask=attention_mask,
        return_dict=True,
    )
    assert isinstance(out, LayoutGANPPModelOutput)
    assert out.labels is not None
    assert out.mask is not None
    assert out.latents is not None
    layout = LayoutGenerationOutput(
        bbox=out.bbox.detach().cpu(),
        labels=out.labels.detach().cpu(),
        mask=out.mask.detach().cpu(),
        id2label={int(k): v for k, v in self.config.id2label.items()},
        intermediates={
            "condition_type": canonical,
            "latents": out.latents.detach().cpu() if return_intermediates else None,
        }
        if return_intermediates
        else None,
    )
    resolved_output_type = normalize_output_type(output_type)
    if resolved_output_type is OutputType.dict:
        return cast(LayoutGANPPOutputDict, dict(layout))
    if resolved_output_type is OutputType.dataclass:
        return layout
    assert_never(resolved_output_type)

normalize_output_type

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

Normalize a public output type value.

Parameters:

Name Type Description Default
output_type OutputType | str

Output type enum or string.

required

Returns:

Type Description
OutputType

Normalized output type enum.

Raises:

Type Description
ValueError

If output_type is unsupported.

Examples:

>>> str(normalize_output_type("dict"))
'dict'
Source code in models/layoutganpp/src/layoutganpp/modeling_layoutganpp.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def normalize_output_type(output_type: OutputType | str) -> OutputType:
    """Normalize a public output type value.

    Args:
        output_type: Output type enum or string.

    Returns:
        Normalized output type enum.

    Raises:
        ValueError: If `output_type` is unsupported.

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

pipeline_layoutganpp

Pipeline interface for LayoutGAN++ layout generation.

LayoutGANPPPipeline

Bases: LayoutGenerationPipeline

Transformers pipeline for LayoutGAN++ label-conditioned generation.

Parameters:

Name Type Description Default
model LayoutGANPPModel

LayoutGAN++ model instance.

required
processor LayoutGANPPProcessor | None

Optional processor for label encoding and decoding.

None
config LayoutGANPPConfig | None

Optional root pipeline config. Defaults to model.config.

None
device int | device | None

Optional torch device passed to the base pipeline.

None
binary_output bool

Whether the base pipeline should produce binary output.

False

Examples:

>>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
>>> pipe = LayoutGANPPPipeline(model=model)
>>> pipe.model.config.model_type
'layoutganpp'
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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
class LayoutGANPPPipeline(LayoutGenerationPipeline):
    """Transformers pipeline for LayoutGAN++ label-conditioned generation.

    Args:
        model: LayoutGAN++ model instance.
        processor: Optional processor for label encoding and decoding.
        config: Optional root pipeline config. Defaults to `model.config`.
        device: Optional torch device passed to the base pipeline.
        binary_output: Whether the base pipeline should produce binary output.

    Examples:
        >>> model = LayoutGANPPModel(LayoutGANPPConfig(num_labels=2))
        >>> pipe = LayoutGANPPPipeline(model=model)
        >>> pipe.model.config.model_type
        'layoutganpp'
    """

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

    config: LayoutGANPPConfig
    model: LayoutGANPPModel
    processor: LayoutGANPPProcessor

    def __init__(
        self,
        model: LayoutGANPPModel,
        processor: LayoutGANPPProcessor | None = None,
        config: LayoutGANPPConfig | None = None,
        device: int | torch.device | None = None,
        binary_output: bool = False,
    ) -> None:
        """Initialize a LayoutGAN++ pipeline.

        Args:
            model: LayoutGAN++ model instance.
            processor: Optional processor for label encoding and decoding.
            config: Optional root pipeline config.
            device: Optional torch device passed to the base pipeline.
            binary_output: Whether the base pipeline should produce binary output.

        Examples:
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> isinstance(pipe.processor, LayoutGANPPProcessor)
            True
        """
        _ = binary_output
        super().__init__(config or model.config)
        self.config = config or model.config
        self.model = model
        self.processor = processor or LayoutGANPPProcessor(
            dataset_name=model.config.dataset_name,
            id2label=model.config.id2label,
        )
        if device is not None:
            resolved_device = (
                torch.device("cpu")
                if isinstance(device, int) and device < 0
                else torch.device(f"cuda:{device}")
                if isinstance(device, int)
                else device
            )
            self.to(resolved_device)

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

    def _sanitize_parameters(
        self, **kwargs: LayoutGANPPPipelineKwarg
    ) -> tuple[
        dict[str, LayoutGANPPPipelineKwarg],
        dict[str, LayoutGANPPPipelineKwarg],
        dict[str, LayoutGANPPPipelineKwarg],
    ]:
        sanitized = cast(
            dict[str, LayoutGANPPPipelineKwarg],
            kwargs,
        )
        return {}, sanitized, {}

    def preprocess(
        self,
        input_: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        **preprocess_parameters: LayoutGANPPPipelineKwarg,
    ) -> BatchEncoding:
        """Encode pipeline inputs into model inputs.

        Args:
            input_: Labels supplied as the positional pipeline input.
            **preprocess_parameters: Keyword labels and generation arguments.

        Returns:
            Batch encoding containing label IDs, attention mask, and generation kwargs.

        Raises:
            ValueError: If labels are not supplied or cannot be encoded.

        Examples:
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> "labels" in pipe.preprocess(["Toolbar"])
            True
        """
        labels = preprocess_parameters.pop("labels", input_)
        if labels is None:
            raise ValueError("labels are required for LayoutGANPPPipeline")

        encoded = self._layoutganpp_processor()(
            cast(list[list[str | int]] | list[str | int] | torch.Tensor, labels)
        )
        encoded.update(preprocess_parameters)
        return encoded

    def _forward(
        self,
        model_inputs: dict[str, LayoutGANPPPipelineKwarg],
        **forward_params: LayoutGANPPPipelineKwarg,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
        del forward_params
        labels = torch.as_tensor(model_inputs.pop("labels"), dtype=torch.long)
        attention_mask = torch.as_tensor(
            model_inputs.pop("attention_mask"), dtype=torch.bool
        )
        condition_type = cast(
            ConditionType | str, model_inputs.pop("condition_type", ConditionType.label)
        )
        bbox = cast(torch.Tensor | None, model_inputs.pop("bbox", None))
        mask = cast(torch.Tensor | None, model_inputs.pop("mask", None))
        num_elements = cast(
            int | list[int] | torch.Tensor | None,
            model_inputs.pop("num_elements", None),
        )
        box_format = cast(
            BoxFormat | str, model_inputs.pop("box_format", BoxFormat.xywh)
        )
        normalized = cast(bool, model_inputs.pop("normalized", True))
        canvas_size = cast(
            tuple[int, int] | None, model_inputs.pop("canvas_size", None)
        )
        seed = cast(int | None, model_inputs.pop("seed", None))
        generator = cast(torch.Generator | None, model_inputs.pop("generator", None))
        num_inference_steps = cast(
            int | None, model_inputs.pop("num_inference_steps", None)
        )
        output_type = cast(
            OutputType | str, model_inputs.pop("output_type", OutputType.dataclass)
        )
        return_intermediates = cast(
            bool, model_inputs.pop("return_intermediates", False)
        )
        latents = cast(torch.Tensor | None, model_inputs.pop("latents", None))
        if model_inputs:
            unknown = ", ".join(sorted(model_inputs))
            raise ValueError(f"Unsupported generation kwargs: {unknown}")

        return self._layoutganpp_model().generate(
            condition_type=condition_type,
            bbox=bbox,
            labels=labels,
            mask=mask,
            attention_mask=attention_mask,
            num_elements=num_elements,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
            seed=seed,
            generator=generator,
            num_inference_steps=num_inference_steps,
            output_type=output_type,
            return_intermediates=return_intermediates,
            latents=latents,
        )

    def postprocess(
        self,
        model_outputs: LayoutGenerationOutput | LayoutGANPPOutputDict,
        **kwargs: LayoutGANPPPipelineKwarg,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
        """Return generated layouts from the pipeline output.

        Args:
            model_outputs: Output produced by `LayoutGANPPModel.generate`.
            **kwargs: Reserved post-processing keyword arguments.

        Returns:
            The generated layout output unchanged.

        Examples:
            >>> output = LayoutGenerationOutput(bbox=torch.zeros(1, 1, 4))
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> pipe.postprocess(output) is output
            True
        """
        del kwargs
        return model_outputs

    @torch.no_grad()
    def __call__(
        self,
        labels: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"]
        | None = None,
        *,
        batch_size: int = 1,
        condition_type: ConditionType | str = ConditionType.label,
        bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
        mask: Bool[torch.Tensor, "batch elements"] | None = None,
        attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        num_inference_steps: int | None = None,
        output_type: OutputType | str = OutputType.dataclass,
        return_intermediates: bool = False,
        latents: Float[torch.Tensor, "batch elements latent"] | None = None,
    ) -> LayoutGenerationOutput | LayoutGANPPOutputDict:  # ty: ignore[invalid-method-override]
        """Generate LayoutGAN++ boxes from labels.

        Args:
            labels: Label strings or label IDs to condition on.
            batch_size: Reserved compatibility argument.
            condition_type: Condition type or alias.
            bbox: Reserved compatibility argument.
            mask: Optional valid-element mask.
            attention_mask: Optional valid-element mask.
            num_elements: Reserved compatibility argument.
            box_format: Reserved compatibility argument.
            normalized: Reserved compatibility argument.
            canvas_size: Reserved compatibility argument.
            seed: Optional random seed for latent sampling.
            generator: Optional PyTorch random generator.
            num_inference_steps: Reserved compatibility argument.
            output_type: Return format, either `dataclass` or `dict`.
            return_intermediates: Whether to include generation intermediates.
            latents: Optional fixed latent vectors.

        Returns:
            A layout generation dataclass or dictionary.

        Raises:
            ValueError: If labels are missing or generation options are invalid.

        Examples:
            >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
            >>> out = pipe(labels=["Toolbar"], seed=0)
            >>> tuple(out.bbox.shape)
            (1, 1, 4)
        """
        del batch_size
        if labels is None:
            raise ValueError("labels are required for layoutganpp v1")

        if isinstance(labels, torch.Tensor):
            encoded_labels = labels
            resolved_mask = attention_mask if attention_mask is not None else mask
        else:
            encoded = self._layoutganpp_processor()(labels)
            encoded_labels = encoded["labels"]
            if attention_mask is not None:
                resolved_mask = attention_mask
            else:
                resolved_mask = encoded["attention_mask"] if mask is None else mask
        return self._layoutganpp_model().generate(
            condition_type=condition_type,
            bbox=bbox,
            labels=cast(torch.Tensor, encoded_labels),
            mask=cast(torch.Tensor | None, resolved_mask),
            num_elements=num_elements,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
            seed=seed,
            generator=generator,
            num_inference_steps=num_inference_steps,
            output_type=output_type,
            return_intermediates=return_intermediates,
            latents=latents,
        )

    def _layoutganpp_model(self) -> LayoutGANPPModel:
        return self.model

    def _layoutganpp_processor(self) -> LayoutGANPPProcessor:
        return self.processor

__init__

__init__(
    model: LayoutGANPPModel,
    processor: LayoutGANPPProcessor | None = None,
    config: LayoutGANPPConfig | None = None,
    device: int | device | None = None,
    binary_output: bool = False,
) -> None

Initialize a LayoutGAN++ pipeline.

Parameters:

Name Type Description Default
model LayoutGANPPModel

LayoutGAN++ model instance.

required
processor LayoutGANPPProcessor | None

Optional processor for label encoding and decoding.

None
config LayoutGANPPConfig | None

Optional root pipeline config.

None
device int | device | None

Optional torch device passed to the base pipeline.

None
binary_output bool

Whether the base pipeline should produce binary output.

False

Examples:

>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> isinstance(pipe.processor, LayoutGANPPProcessor)
True
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
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
def __init__(
    self,
    model: LayoutGANPPModel,
    processor: LayoutGANPPProcessor | None = None,
    config: LayoutGANPPConfig | None = None,
    device: int | torch.device | None = None,
    binary_output: bool = False,
) -> None:
    """Initialize a LayoutGAN++ pipeline.

    Args:
        model: LayoutGAN++ model instance.
        processor: Optional processor for label encoding and decoding.
        config: Optional root pipeline config.
        device: Optional torch device passed to the base pipeline.
        binary_output: Whether the base pipeline should produce binary output.

    Examples:
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> isinstance(pipe.processor, LayoutGANPPProcessor)
        True
    """
    _ = binary_output
    super().__init__(config or model.config)
    self.config = config or model.config
    self.model = model
    self.processor = processor or LayoutGANPPProcessor(
        dataset_name=model.config.dataset_name,
        id2label=model.config.id2label,
    )
    if device is not None:
        resolved_device = (
            torch.device("cpu")
            if isinstance(device, int) and device < 0
            else torch.device(f"cuda:{device}")
            if isinstance(device, int)
            else device
        )
        self.to(resolved_device)

preprocess

preprocess(
    input_: list[list[str | int]]
    | list[str | int]
    | Int[Tensor, "batch elements"]
    | None = None,
    **preprocess_parameters: LayoutGANPPPipelineKwarg,
) -> BatchEncoding

Encode pipeline inputs into model inputs.

Parameters:

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

Labels supplied as the positional pipeline input.

None
**preprocess_parameters LayoutGANPPPipelineKwarg

Keyword labels and generation arguments.

{}

Returns:

Type Description
BatchEncoding

Batch encoding containing label IDs, attention mask, and generation kwargs.

Raises:

Type Description
ValueError

If labels are not supplied or cannot be encoded.

Examples:

>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> "labels" in pipe.preprocess(["Toolbar"])
True
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
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
def preprocess(
    self,
    input_: list[list[str | int]]
    | list[str | int]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    **preprocess_parameters: LayoutGANPPPipelineKwarg,
) -> BatchEncoding:
    """Encode pipeline inputs into model inputs.

    Args:
        input_: Labels supplied as the positional pipeline input.
        **preprocess_parameters: Keyword labels and generation arguments.

    Returns:
        Batch encoding containing label IDs, attention mask, and generation kwargs.

    Raises:
        ValueError: If labels are not supplied or cannot be encoded.

    Examples:
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> "labels" in pipe.preprocess(["Toolbar"])
        True
    """
    labels = preprocess_parameters.pop("labels", input_)
    if labels is None:
        raise ValueError("labels are required for LayoutGANPPPipeline")

    encoded = self._layoutganpp_processor()(
        cast(list[list[str | int]] | list[str | int] | torch.Tensor, labels)
    )
    encoded.update(preprocess_parameters)
    return encoded

postprocess

postprocess(
    model_outputs: LayoutGenerationOutput
    | LayoutGANPPOutputDict,
    **kwargs: LayoutGANPPPipelineKwarg,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict

Return generated layouts from the pipeline output.

Parameters:

Name Type Description Default
model_outputs LayoutGenerationOutput | LayoutGANPPOutputDict

Output produced by LayoutGANPPModel.generate.

required
**kwargs LayoutGANPPPipelineKwarg

Reserved post-processing keyword arguments.

{}

Returns:

Type Description
LayoutGenerationOutput | LayoutGANPPOutputDict

The generated layout output unchanged.

Examples:

>>> output = LayoutGenerationOutput(bbox=torch.zeros(1, 1, 4))
>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> pipe.postprocess(output) is output
True
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def postprocess(
    self,
    model_outputs: LayoutGenerationOutput | LayoutGANPPOutputDict,
    **kwargs: LayoutGANPPPipelineKwarg,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict:
    """Return generated layouts from the pipeline output.

    Args:
        model_outputs: Output produced by `LayoutGANPPModel.generate`.
        **kwargs: Reserved post-processing keyword arguments.

    Returns:
        The generated layout output unchanged.

    Examples:
        >>> output = LayoutGenerationOutput(bbox=torch.zeros(1, 1, 4))
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> pipe.postprocess(output) is output
        True
    """
    del kwargs
    return model_outputs

__call__

__call__(
    labels: list[list[str | int]]
    | list[str | int]
    | Int[Tensor, "batch elements"]
    | None = None,
    *,
    batch_size: int = 1,
    condition_type: ConditionType
    | str = ConditionType.label,
    bbox: Float[Tensor, "batch elements 4"] | None = None,
    mask: Bool[Tensor, "batch elements"] | None = None,
    attention_mask: Bool[Tensor, "batch elements"]
    | 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,
    seed: int | None = None,
    generator: Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[Tensor, "batch elements latent"]
    | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict

Generate LayoutGAN++ boxes from labels.

Parameters:

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

Label strings or label IDs to condition on.

None
batch_size int

Reserved compatibility argument.

1
condition_type ConditionType | str

Condition type or alias.

label
bbox Float[Tensor, 'batch elements 4'] | None

Reserved compatibility argument.

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

Optional valid-element mask.

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

Optional valid-element mask.

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

Reserved compatibility argument.

None
box_format BoxFormat | str

Reserved compatibility argument.

xywh
normalized bool

Reserved compatibility argument.

True
canvas_size tuple[int, int] | None

Reserved compatibility argument.

None
seed int | None

Optional random seed for latent sampling.

None
generator Generator | None

Optional PyTorch random generator.

None
num_inference_steps int | None

Reserved compatibility argument.

None
output_type OutputType | str

Return format, either dataclass or dict.

dataclass
return_intermediates bool

Whether to include generation intermediates.

False
latents Float[Tensor, 'batch elements latent'] | None

Optional fixed latent vectors.

None

Returns:

Type Description
LayoutGenerationOutput | LayoutGANPPOutputDict

A layout generation dataclass or dictionary.

Raises:

Type Description
ValueError

If labels are missing or generation options are invalid.

Examples:

>>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
>>> out = pipe(labels=["Toolbar"], seed=0)
>>> tuple(out.bbox.shape)
(1, 1, 4)
Source code in models/layoutganpp/src/layoutganpp/pipeline_layoutganpp.py
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
@torch.no_grad()
def __call__(
    self,
    labels: list[list[str | int]]
    | list[str | int]
    | Int[torch.Tensor, "batch elements"]
    | None = None,
    *,
    batch_size: int = 1,
    condition_type: ConditionType | str = ConditionType.label,
    bbox: Float[torch.Tensor, "batch elements 4"] | None = None,
    mask: Bool[torch.Tensor, "batch elements"] | None = None,
    attention_mask: Bool[torch.Tensor, "batch elements"] | 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,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    num_inference_steps: int | None = None,
    output_type: OutputType | str = OutputType.dataclass,
    return_intermediates: bool = False,
    latents: Float[torch.Tensor, "batch elements latent"] | None = None,
) -> LayoutGenerationOutput | LayoutGANPPOutputDict:  # ty: ignore[invalid-method-override]
    """Generate LayoutGAN++ boxes from labels.

    Args:
        labels: Label strings or label IDs to condition on.
        batch_size: Reserved compatibility argument.
        condition_type: Condition type or alias.
        bbox: Reserved compatibility argument.
        mask: Optional valid-element mask.
        attention_mask: Optional valid-element mask.
        num_elements: Reserved compatibility argument.
        box_format: Reserved compatibility argument.
        normalized: Reserved compatibility argument.
        canvas_size: Reserved compatibility argument.
        seed: Optional random seed for latent sampling.
        generator: Optional PyTorch random generator.
        num_inference_steps: Reserved compatibility argument.
        output_type: Return format, either `dataclass` or `dict`.
        return_intermediates: Whether to include generation intermediates.
        latents: Optional fixed latent vectors.

    Returns:
        A layout generation dataclass or dictionary.

    Raises:
        ValueError: If labels are missing or generation options are invalid.

    Examples:
        >>> pipe = LayoutGANPPPipeline(LayoutGANPPModel(LayoutGANPPConfig()))
        >>> out = pipe(labels=["Toolbar"], seed=0)
        >>> tuple(out.bbox.shape)
        (1, 1, 4)
    """
    del batch_size
    if labels is None:
        raise ValueError("labels are required for layoutganpp v1")

    if isinstance(labels, torch.Tensor):
        encoded_labels = labels
        resolved_mask = attention_mask if attention_mask is not None else mask
    else:
        encoded = self._layoutganpp_processor()(labels)
        encoded_labels = encoded["labels"]
        if attention_mask is not None:
            resolved_mask = attention_mask
        else:
            resolved_mask = encoded["attention_mask"] if mask is None else mask
    return self._layoutganpp_model().generate(
        condition_type=condition_type,
        bbox=bbox,
        labels=cast(torch.Tensor, encoded_labels),
        mask=cast(torch.Tensor | None, resolved_mask),
        num_elements=num_elements,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
        seed=seed,
        generator=generator,
        num_inference_steps=num_inference_steps,
        output_type=output_type,
        return_intermediates=return_intermediates,
        latents=latents,
    )

processing_layoutganpp

Processor for LayoutGAN++ label encoding and output decoding.

DecodedLayoutGANPPRecord

Bases: TypedDict

One decoded LayoutGAN++ layout element.

Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
16
17
18
19
20
21
class DecodedLayoutGANPPRecord(TypedDict):
    """One decoded LayoutGAN++ layout element."""

    label: str
    label_id: int
    bbox: list[float]

LayoutGANPPProcessor

Bases: ProcessorMixin

Encode LayoutGAN++ labels and decode generated layouts.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

rico13
id2label Id2LabelMapping | None

Optional label ID to text mapping.

None

Examples:

>>> processor = LayoutGANPPProcessor(dataset_name="rico")
>>> processor.label2id["Toolbar"]
0
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
class LayoutGANPPProcessor(ProcessorMixin):
    """Encode LayoutGAN++ labels and decode generated layouts.

    Args:
        dataset_name: Dataset key or alias.
        id2label: Optional label ID to text mapping.

    Examples:
        >>> processor = LayoutGANPPProcessor(dataset_name="rico")
        >>> processor.label2id["Toolbar"]
        0
    """

    config_name = "preprocessor_config.json"

    def __init__(
        self,
        dataset_name: DatasetName | str = DatasetName.rico13,
        id2label: Id2LabelMapping | None = None,
    ) -> None:
        """Initialize a LayoutGAN++ processor.

        Args:
            dataset_name: Dataset key or alias.
            id2label: Optional label ID to text mapping.

        Raises:
            ValueError: If the dataset name is unsupported.

        Examples:
            >>> LayoutGANPPProcessor("publaynet").id2label[0]
            'text'
        """
        self.chat_template = None
        self.dataset_name = str(normalize_dataset_name(dataset_name))
        raw_id2label = id2label or id2label_for_dataset(self.dataset_name)
        self.id2label = {int(k): v for k, v in raw_id2label.items()}
        self.label2id = {v: k for k, v in self.id2label.items()}

    def __call__(
        self,
        labels: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"],
        *,
        padding: bool = True,
        return_tensors: Literal["pt"] = "pt",
    ) -> BatchEncoding:
        """Encode label strings or IDs into tensors.

        Args:
            labels: Label strings, label IDs, or a tensor of label IDs.
            padding: Whether to pad ragged batches.
            return_tensors: Tensor framework. Only `pt` is supported.

        Returns:
            Batch encoding with `labels` and `attention_mask` tensors.

        Raises:
            ValueError: If labels are empty, ragged without padding, unknown,
                or `return_tensors` is not `pt`.

        Examples:
            >>> processor = LayoutGANPPProcessor()
            >>> encoded = processor(["Toolbar", "Image"])
            >>> tuple(encoded["labels"].shape)
            (1, 2)
        """
        if return_tensors != "pt":
            raise ValueError("LayoutGANPPProcessor only supports return_tensors='pt'")

        rows = self._normalize_rows(labels)
        max_len = max(len(row) for row in rows)
        if not padding and len({len(row) for row in rows}) != 1:
            raise ValueError("Ragged labels require padding=True")

        encoded = []
        attention = []
        for row in rows:
            ids = [self._label_to_id(label) for label in row]
            pad = max_len - len(ids)
            encoded.append(ids + [0] * pad)
            attention.append([True] * len(ids) + [False] * pad)
        return BatchEncoding(
            {
                "labels": torch.tensor(encoded, dtype=torch.long),
                "attention_mask": torch.tensor(attention, dtype=torch.bool),
            }
        )

    def batch_decode(
        self,
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
    ) -> list[list[DecodedLayoutGANPPRecord]]:
        """Decode generated boxes and label IDs into records.

        Args:
            bbox: Generated boxes shaped `(batch, sequence, 4)` or `(sequence, 4)`.
            labels: Label IDs shaped `(batch, sequence)` or `(sequence,)`.
            attention_mask: Optional valid-element mask.

        Returns:
            Nested records containing label text, label ID, and bounding box.

        Raises:
            KeyError: If a label ID is not known to this processor.

        Examples:
            >>> processor = LayoutGANPPProcessor()
            >>> records = processor.batch_decode(
            ...     torch.zeros(1, 1, 4), torch.tensor([[0]])
            ... )
            >>> records[0][0]["label"]
            'Toolbar'
        """
        bbox_t = torch.as_tensor(bbox, dtype=torch.float32)
        labels_t = torch.as_tensor(labels, dtype=torch.long)
        if labels_t.ndim == 1:
            labels_t = labels_t.unsqueeze(0)
            bbox_t = bbox_t.unsqueeze(0)
        if attention_mask is None:
            mask_t = torch.ones(labels_t.shape, dtype=torch.bool)
        else:
            mask_t = torch.as_tensor(attention_mask, dtype=torch.bool)
            if mask_t.ndim == 1:
                mask_t = mask_t.unsqueeze(0)
        records: list[list[DecodedLayoutGANPPRecord]] = []
        for boxes, ids, mask in zip(bbox_t, labels_t, mask_t, strict=True):
            row: list[DecodedLayoutGANPPRecord] = []
            for box, label_id in zip(boxes[mask], ids[mask], strict=True):
                idx = int(label_id.item())
                row.append(
                    {
                        "label": self.id2label[idx],
                        "label_id": idx,
                        "bbox": box.tolist(),
                    }
                )
            records.append(row)
        return records

    def _normalize_rows(
        self,
        labels: list[list[str | int]]
        | list[str | int]
        | Int[torch.Tensor, "batch elements"],
    ) -> list[list[str | int]]:
        if isinstance(labels, torch.Tensor):
            if labels.ndim == 1:
                return [[int(v) for v in labels.tolist()]]
            return [[int(v) for v in row] for row in labels.tolist()]
        if not labels:
            raise ValueError("labels must not be empty")

        first = labels[0]
        if isinstance(first, list):
            rows: list[list[str | int]] = []
            for row in labels:
                if not isinstance(row, list):
                    raise ValueError("labels must be a flat list or list of rows")

                rows.append(row)
            return rows
        row = []
        for label in labels:
            if isinstance(label, list):
                raise ValueError("labels must be a flat list or list of rows")

            row.append(label)
        return [row]

    def _label_to_id(self, label: str | int) -> int:
        if isinstance(label, int):
            if label < 0 or label >= len(self.id2label):
                raise ValueError(f"Unknown label id: {label}")

            return label
        try:
            return self.label2id[label]
        except KeyError as exc:
            raise ValueError(f"Unknown label: {label}") from exc

__init__

__init__(
    dataset_name: DatasetName | str = DatasetName.rico13,
    id2label: Id2LabelMapping | None = None,
) -> None

Initialize a LayoutGAN++ processor.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

rico13
id2label Id2LabelMapping | None

Optional label ID to text mapping.

None

Raises:

Type Description
ValueError

If the dataset name is unsupported.

Examples:

>>> LayoutGANPPProcessor("publaynet").id2label[0]
'text'
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def __init__(
    self,
    dataset_name: DatasetName | str = DatasetName.rico13,
    id2label: Id2LabelMapping | None = None,
) -> None:
    """Initialize a LayoutGAN++ processor.

    Args:
        dataset_name: Dataset key or alias.
        id2label: Optional label ID to text mapping.

    Raises:
        ValueError: If the dataset name is unsupported.

    Examples:
        >>> LayoutGANPPProcessor("publaynet").id2label[0]
        'text'
    """
    self.chat_template = None
    self.dataset_name = str(normalize_dataset_name(dataset_name))
    raw_id2label = id2label or id2label_for_dataset(self.dataset_name)
    self.id2label = {int(k): v for k, v in raw_id2label.items()}
    self.label2id = {v: k for k, v in self.id2label.items()}

__call__

__call__(
    labels: list[list[str | int]]
    | list[str | int]
    | Int[Tensor, "batch elements"],
    *,
    padding: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding

Encode label strings or IDs into tensors.

Parameters:

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

Label strings, label IDs, or a tensor of label IDs.

required
padding bool

Whether to pad ragged batches.

True
return_tensors Literal['pt']

Tensor framework. Only pt is supported.

'pt'

Returns:

Type Description
BatchEncoding

Batch encoding with labels and attention_mask tensors.

Raises:

Type Description
ValueError

If labels are empty, ragged without padding, unknown, or return_tensors is not pt.

Examples:

>>> processor = LayoutGANPPProcessor()
>>> encoded = processor(["Toolbar", "Image"])
>>> tuple(encoded["labels"].shape)
(1, 2)
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.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
100
101
102
103
104
105
106
107
108
109
110
111
112
def __call__(
    self,
    labels: list[list[str | int]]
    | list[str | int]
    | Int[torch.Tensor, "batch elements"],
    *,
    padding: bool = True,
    return_tensors: Literal["pt"] = "pt",
) -> BatchEncoding:
    """Encode label strings or IDs into tensors.

    Args:
        labels: Label strings, label IDs, or a tensor of label IDs.
        padding: Whether to pad ragged batches.
        return_tensors: Tensor framework. Only `pt` is supported.

    Returns:
        Batch encoding with `labels` and `attention_mask` tensors.

    Raises:
        ValueError: If labels are empty, ragged without padding, unknown,
            or `return_tensors` is not `pt`.

    Examples:
        >>> processor = LayoutGANPPProcessor()
        >>> encoded = processor(["Toolbar", "Image"])
        >>> tuple(encoded["labels"].shape)
        (1, 2)
    """
    if return_tensors != "pt":
        raise ValueError("LayoutGANPPProcessor only supports return_tensors='pt'")

    rows = self._normalize_rows(labels)
    max_len = max(len(row) for row in rows)
    if not padding and len({len(row) for row in rows}) != 1:
        raise ValueError("Ragged labels require padding=True")

    encoded = []
    attention = []
    for row in rows:
        ids = [self._label_to_id(label) for label in row]
        pad = max_len - len(ids)
        encoded.append(ids + [0] * pad)
        attention.append([True] * len(ids) + [False] * pad)
    return BatchEncoding(
        {
            "labels": torch.tensor(encoded, dtype=torch.long),
            "attention_mask": torch.tensor(attention, dtype=torch.bool),
        }
    )

batch_decode

batch_decode(
    bbox: Float[Tensor, "batch elements 4"],
    labels: Int[Tensor, "batch elements"],
    attention_mask: Bool[Tensor, "batch elements"]
    | None = None,
) -> list[list[DecodedLayoutGANPPRecord]]

Decode generated boxes and label IDs into records.

Parameters:

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

Generated boxes shaped (batch, sequence, 4) or (sequence, 4).

required
labels Int[Tensor, 'batch elements']

Label IDs shaped (batch, sequence) or (sequence,).

required
attention_mask Bool[Tensor, 'batch elements'] | None

Optional valid-element mask.

None

Returns:

Type Description
list[list[DecodedLayoutGANPPRecord]]

Nested records containing label text, label ID, and bounding box.

Raises:

Type Description
KeyError

If a label ID is not known to this processor.

Examples:

>>> processor = LayoutGANPPProcessor()
>>> records = processor.batch_decode(
...     torch.zeros(1, 1, 4), torch.tensor([[0]])
... )
>>> records[0][0]["label"]
'Toolbar'
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def batch_decode(
    self,
    bbox: Float[torch.Tensor, "batch elements 4"],
    labels: Int[torch.Tensor, "batch elements"],
    attention_mask: Bool[torch.Tensor, "batch elements"] | None = None,
) -> list[list[DecodedLayoutGANPPRecord]]:
    """Decode generated boxes and label IDs into records.

    Args:
        bbox: Generated boxes shaped `(batch, sequence, 4)` or `(sequence, 4)`.
        labels: Label IDs shaped `(batch, sequence)` or `(sequence,)`.
        attention_mask: Optional valid-element mask.

    Returns:
        Nested records containing label text, label ID, and bounding box.

    Raises:
        KeyError: If a label ID is not known to this processor.

    Examples:
        >>> processor = LayoutGANPPProcessor()
        >>> records = processor.batch_decode(
        ...     torch.zeros(1, 1, 4), torch.tensor([[0]])
        ... )
        >>> records[0][0]["label"]
        'Toolbar'
    """
    bbox_t = torch.as_tensor(bbox, dtype=torch.float32)
    labels_t = torch.as_tensor(labels, dtype=torch.long)
    if labels_t.ndim == 1:
        labels_t = labels_t.unsqueeze(0)
        bbox_t = bbox_t.unsqueeze(0)
    if attention_mask is None:
        mask_t = torch.ones(labels_t.shape, dtype=torch.bool)
    else:
        mask_t = torch.as_tensor(attention_mask, dtype=torch.bool)
        if mask_t.ndim == 1:
            mask_t = mask_t.unsqueeze(0)
    records: list[list[DecodedLayoutGANPPRecord]] = []
    for boxes, ids, mask in zip(bbox_t, labels_t, mask_t, strict=True):
        row: list[DecodedLayoutGANPPRecord] = []
        for box, label_id in zip(boxes[mask], ids[mask], strict=True):
            idx = int(label_id.item())
            row.append(
                {
                    "label": self.id2label[idx],
                    "label_id": idx,
                    "bbox": box.tolist(),
                }
            )
        records.append(row)
    return records

processor_for_dataset

processor_for_dataset(
    dataset_name: DatasetName | str,
) -> LayoutGANPPProcessor

Create a processor with the default labels for a dataset.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Dataset key or alias.

required

Returns:

Type Description
LayoutGANPPProcessor

Processor initialized with the dataset's default label mapping.

Raises:

Type Description
ValueError

If the dataset name is unknown.

Examples:

>>> processor_for_dataset("magazine").dataset_name
'magazine'
Source code in models/layoutganpp/src/layoutganpp/processing_layoutganpp.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def processor_for_dataset(dataset_name: DatasetName | str) -> LayoutGANPPProcessor:
    """Create a processor with the default labels for a dataset.

    Args:
        dataset_name: Dataset key or alias.

    Returns:
        Processor initialized with the dataset's default label mapping.

    Raises:
        ValueError: If the dataset name is unknown.

    Examples:
        >>> processor_for_dataset("magazine").dataset_name
        'magazine'
    """
    return LayoutGANPPProcessor(
        dataset_name=dataset_name,
        id2label=id2label_for_dataset(dataset_name),
    )