Skip to content

Layousyn

Diffusers-style LayouSyn / Lay-Your-Scene conversion package.

ConditionType

Bases: StrEnum

Canonical condition names used by layout generation interfaces.

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

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

LayoutGenerationOutput dataclass

Bases: BaseOutput

Layout-generation output for Diffusers pipelines.

Attributes:

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

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

labels Int[Tensor, 'batch elements']

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

mask Bool[Tensor, 'batch elements']

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

id2label dict[int, str]

Mapping from integer label ids to display names.

sequences object | None

Optional raw token sequences.

scores object | None

Optional per-token or per-element scores.

trajectory object | None

Optional sampling trajectory.

intermediates object | None

Optional model-specific debug or auxiliary data.

Examples:

>>> import torch
>>> output = LayoutGenerationOutput(
...     bbox=torch.zeros(1, 1, 4),
...     labels=torch.zeros(1, 1, dtype=torch.long),
...     mask=torch.ones(1, 1, dtype=torch.bool),
...     id2label={0: "text"},
... )
>>> output.to_tuple()[0].shape
torch.Size([1, 1, 4])
Source code in lib/laygen/src/laygen/pipelines/pipeline_output.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@dataclass
class LayoutGenerationOutput(BaseOutput):
    """Layout-generation output for Diffusers pipelines.

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

    Examples:
        >>> import torch
        >>> output = LayoutGenerationOutput(
        ...     bbox=torch.zeros(1, 1, 4),
        ...     labels=torch.zeros(1, 1, dtype=torch.long),
        ...     mask=torch.ones(1, 1, dtype=torch.bool),
        ...     id2label={0: "text"},
        ... )
        >>> output.to_tuple()[0].shape
        torch.Size([1, 1, 4])
    """

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

LayouSynConfig

Bases: ConfigMixin

Serializable LayouSyn configuration.

Parameters:

Name Type Description Default
model_name str

Reference DiT architecture key.

'DiT-S'
in_channels int

Layout coordinate channels.

4
concept_in_channels int

Concept embedding width.

768
y_in_channels int | None

Caption embedding width.

768
max_in_len int

Maximum number of object slots.

60
max_y_len int | None

Maximum number of caption tokens.

120
layout_type LayoutType

Reference layout coordinate type.

'xyxy'
t5_size str | None

Reference T5 size suffix.

'base'
scale float

Default classifier-free guidance scale.

2.0
noise_schedule str

Reference diffusion beta schedule.

'linear'
diffusion_steps int

Number of diffusion training timesteps.

100
hidden_size int | None

Optional resolved hidden width override.

None
depth int | None

Optional resolved transformer depth override.

None
num_heads int | None

Optional resolved attention head override.

None
license str

Upstream checkpoint license identifier.

'cc-by-nc-4.0'
Source code in models/layousyn/src/layousyn/configuration_layousyn.py
 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
class LayouSynConfig(ConfigMixin):
    """Serializable LayouSyn configuration.

    Args:
        model_name: Reference DiT architecture key.
        in_channels: Layout coordinate channels.
        concept_in_channels: Concept embedding width.
        y_in_channels: Caption embedding width.
        max_in_len: Maximum number of object slots.
        max_y_len: Maximum number of caption tokens.
        layout_type: Reference layout coordinate type.
        t5_size: Reference T5 size suffix.
        scale: Default classifier-free guidance scale.
        noise_schedule: Reference diffusion beta schedule.
        diffusion_steps: Number of diffusion training timesteps.
        hidden_size: Optional resolved hidden width override.
        depth: Optional resolved transformer depth override.
        num_heads: Optional resolved attention head override.
        license: Upstream checkpoint license identifier.
    """

    config_name = "config.json"

    @register_to_config
    def __init__(
        self,
        *,
        model_name: str = "DiT-S",
        in_channels: int = 4,
        concept_in_channels: int = 768,
        y_in_channels: int | None = 768,
        max_in_len: int = 60,
        max_y_len: int | None = 120,
        layout_type: LayoutType = "xyxy",
        t5_size: str | None = "base",
        scale: float = 2.0,
        noise_schedule: str = "linear",
        diffusion_steps: int = 100,
        hidden_size: int | None = None,
        depth: int | None = None,
        num_heads: int | None = None,
        license: str = "cc-by-nc-4.0",
    ) -> None:
        """Initialize configuration fields."""
        shape = resolve_model_shape(
            model_name,
            hidden_size=hidden_size,
            depth=depth,
            num_heads=num_heads,
        )
        self.model_name = model_name
        self.in_channels = in_channels
        self.concept_in_channels = concept_in_channels
        self.y_in_channels = y_in_channels
        self.max_in_len = max_in_len
        self.max_y_len = max_y_len
        self.layout_type = layout_type
        self.t5_size = t5_size
        self.scale = scale
        self.noise_schedule = noise_schedule
        self.diffusion_steps = diffusion_steps
        self.hidden_size = shape["hidden_size"]
        self.depth = shape["depth"]
        self.num_heads = shape["num_heads"]
        self.license = license

    @classmethod
    def from_reference_json(cls, path: str | Path) -> "LayouSynConfig":
        """Load a reference JSON config.

        Args:
            path: Path to a Lay-Your-Scene JSON config.

        Returns:
            Converted configuration object.
        """
        data = json.loads(Path(path).read_text())
        layout_type = data.get("layout_type", "xyxy")
        if not isinstance(layout_type, str):
            layout_type = str(layout_type)
        return cls(
            model_name=data.get("model", "DiT-S"),
            in_channels=data.get("in_channel", 4),
            concept_in_channels=data.get("concept_in_channel", 768),
            y_in_channels=data.get("y_in_channel"),
            max_in_len=data.get("max_in_len", 60),
            max_y_len=data.get("max_y_len"),
            layout_type="cxcywh" if "cxcywh" in layout_type.lower() else "xyxy",
            t5_size=data.get("t5_size"),
            scale=data.get("scale", 1.0),
            noise_schedule=data.get("noise_schedule", "linear"),
            diffusion_steps=data.get("diffusion_steps", 1000),
        )

    def to_reference_dict(self) -> LayouSynReferenceConfig:
        """Return the config keys expected by the original repository."""
        return {
            "model": self.model_name,
            "in_channel": self.in_channels,
            "concept_in_channel": self.concept_in_channels,
            "y_in_channel": self.y_in_channels,
            "max_in_len": self.max_in_len,
            "max_y_len": self.max_y_len,
            "scale": self.scale,
            "noise_schedule": self.noise_schedule,
            "layout_type": self.layout_type,
            "diffusion_steps": self.diffusion_steps,
            "t5_size": self.t5_size,
        }

__init__

__init__(
    *,
    model_name: str = "DiT-S",
    in_channels: int = 4,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_in_len: int = 60,
    max_y_len: int | None = 120,
    layout_type: LayoutType = "xyxy",
    t5_size: str | None = "base",
    scale: float = 2.0,
    noise_schedule: str = "linear",
    diffusion_steps: int = 100,
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    license: str = "cc-by-nc-4.0",
) -> None

Initialize configuration fields.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
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
@register_to_config
def __init__(
    self,
    *,
    model_name: str = "DiT-S",
    in_channels: int = 4,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_in_len: int = 60,
    max_y_len: int | None = 120,
    layout_type: LayoutType = "xyxy",
    t5_size: str | None = "base",
    scale: float = 2.0,
    noise_schedule: str = "linear",
    diffusion_steps: int = 100,
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    license: str = "cc-by-nc-4.0",
) -> None:
    """Initialize configuration fields."""
    shape = resolve_model_shape(
        model_name,
        hidden_size=hidden_size,
        depth=depth,
        num_heads=num_heads,
    )
    self.model_name = model_name
    self.in_channels = in_channels
    self.concept_in_channels = concept_in_channels
    self.y_in_channels = y_in_channels
    self.max_in_len = max_in_len
    self.max_y_len = max_y_len
    self.layout_type = layout_type
    self.t5_size = t5_size
    self.scale = scale
    self.noise_schedule = noise_schedule
    self.diffusion_steps = diffusion_steps
    self.hidden_size = shape["hidden_size"]
    self.depth = shape["depth"]
    self.num_heads = shape["num_heads"]
    self.license = license

from_reference_json classmethod

from_reference_json(path: str | Path) -> 'LayouSynConfig'

Load a reference JSON config.

Parameters:

Name Type Description Default
path str | Path

Path to a Lay-Your-Scene JSON config.

required

Returns:

Type Description
'LayouSynConfig'

Converted configuration object.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
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
@classmethod
def from_reference_json(cls, path: str | Path) -> "LayouSynConfig":
    """Load a reference JSON config.

    Args:
        path: Path to a Lay-Your-Scene JSON config.

    Returns:
        Converted configuration object.
    """
    data = json.loads(Path(path).read_text())
    layout_type = data.get("layout_type", "xyxy")
    if not isinstance(layout_type, str):
        layout_type = str(layout_type)
    return cls(
        model_name=data.get("model", "DiT-S"),
        in_channels=data.get("in_channel", 4),
        concept_in_channels=data.get("concept_in_channel", 768),
        y_in_channels=data.get("y_in_channel"),
        max_in_len=data.get("max_in_len", 60),
        max_y_len=data.get("max_y_len"),
        layout_type="cxcywh" if "cxcywh" in layout_type.lower() else "xyxy",
        t5_size=data.get("t5_size"),
        scale=data.get("scale", 1.0),
        noise_schedule=data.get("noise_schedule", "linear"),
        diffusion_steps=data.get("diffusion_steps", 1000),
    )

to_reference_dict

to_reference_dict() -> LayouSynReferenceConfig

Return the config keys expected by the original repository.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def to_reference_dict(self) -> LayouSynReferenceConfig:
    """Return the config keys expected by the original repository."""
    return {
        "model": self.model_name,
        "in_channel": self.in_channels,
        "concept_in_channel": self.concept_in_channels,
        "y_in_channel": self.y_in_channels,
        "max_in_len": self.max_in_len,
        "max_y_len": self.max_y_len,
        "scale": self.scale,
        "noise_schedule": self.noise_schedule,
        "layout_type": self.layout_type,
        "diffusion_steps": self.diffusion_steps,
        "t5_size": self.t5_size,
    }

LayouSynDiTModel

Bases: ModelMixin, ConfigMixin

Converted LayouSyn DiT denoiser.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
class LayouSynDiTModel(ModelMixin, ConfigMixin):
    """Converted LayouSyn DiT denoiser."""

    config_name = "model_config.json"

    @register_to_config
    def __init__(
        self,
        *,
        in_channels: int = 4,
        max_in_len: int = 60,
        concept_in_channels: int = 768,
        y_in_channels: int | None = 768,
        max_y_len: int | None = 120,
        model_name: str = "DiT-S",
        hidden_size: int | None = None,
        depth: int | None = None,
        num_heads: int | None = None,
        mlp_ratio: float = 4.0,
        class_dropout_prob: float = 0.1,
        learn_sigma: bool = True,
        is_unconditional: bool = False,
    ) -> None:
        """Initialize the converted DiT model."""
        super().__init__()
        shape = resolve_model_shape(
            model_name,
            hidden_size=hidden_size,
            depth=depth,
            num_heads=num_heads,
        )
        self.in_channels = in_channels
        self.learn_sigma = learn_sigma
        self.num_heads = shape["num_heads"]
        self.max_len = max_in_len
        self.is_unconditional = is_unconditional
        self.x_embedder = InputEmbedder(in_channels, shape["hidden_size"])
        self.concept_embedder = ConceptEmbedder(
            concept_in_channels, shape["hidden_size"]
        )
        self.t_embedder = ScalarEmbedder(shape["hidden_size"])
        self.ar_embedder = ScalarEmbedder(shape["hidden_size"])
        if is_unconditional:
            self.y_embedder = CaptionEmbedderIdentity()
        else:
            if y_in_channels is None or max_y_len is None:
                raise ValueError("y_in_channels and max_y_len are required")

            y_null = torch.zeros(max_y_len, y_in_channels)
            y_mask = torch.ones(max_y_len, dtype=torch.bool)
            self.y_embedder = CaptionEmbedder(
                y_in_channels,
                shape["hidden_size"],
                class_dropout_prob,
                y_null,
                y_mask,
            )
        self.pos_embed = nn.Parameter(
            torch.zeros(1, max_in_len, shape["hidden_size"]), requires_grad=False
        )
        block_cls = DiTUCBlock if is_unconditional else DiTBlock
        self.blocks = nn.ModuleList(
            [
                block_cls(shape["hidden_size"], shape["num_heads"], mlp_ratio=mlp_ratio)
                for _ in range(shape["depth"])
            ]
        )
        self.final_layer = FinalLayer(shape["hidden_size"], 2 * in_channels)
        self.initialize_weights()

    def initialize_weights(self) -> None:
        """Initialize weights with the reference policy."""

        def _basic_init(module: nn.Module) -> None:
            if isinstance(module, nn.Linear):
                torch.nn.init.xavier_uniform_(module.weight)
                if module.bias is not None:
                    nn.init.constant_(module.bias, 0)

        self.apply(_basic_init)
        pos_embed = get_1d_sincos_pos_embed(self.pos_embed.shape[-1], self.max_len)
        self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0))
        nn.init.xavier_uniform_(self.x_embedder.proj.weight)
        nn.init.constant_(self.x_embedder.proj.bias, 0)

        if not self.is_unconditional and isinstance(self.y_embedder, CaptionEmbedder):
            nn.init.normal_(self.y_embedder.proj.fc1.weight, std=0.02)
            nn.init.normal_(self.y_embedder.proj.fc2.weight, std=0.02)

        nn.init.normal_(self.concept_embedder.proj.fc1.weight, std=0.02)
        nn.init.normal_(self.concept_embedder.proj.fc2.weight, std=0.02)
        nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
        nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
        nn.init.normal_(self.ar_embedder.mlp[0].weight, std=0.02)
        nn.init.normal_(self.ar_embedder.mlp[2].weight, std=0.02)

        for block in self.blocks:
            block.initialize_weights()

        nn.init.constant_(self.final_layer.adaLN_modulation[-1].weight, 0)
        nn.init.constant_(self.final_layer.adaLN_modulation[-1].bias, 0)
        nn.init.constant_(self.final_layer.linear.weight, 0)
        nn.init.constant_(self.final_layer.linear.bias, 0)

    def forward(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        *,
        x_padding_mask: Bool[torch.Tensor, "batch elements"],
        aspect_ratio: Float[torch.Tensor, "batch"],
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    ) -> Float[torch.Tensor, "batch seq channels"]:
        """Predict epsilon and variance channels for one timestep."""
        x = self.x_embedder(sample)
        x_enc = self.concept_embedder(concept_embeds)
        c = self.t_embedder(timestep) + self.ar_embedder(aspect_ratio)
        if self.is_unconditional:
            for block in self.blocks:
                x = block(x, x_padding_mask=x_padding_mask, c=c)
        else:
            if caption_embeds is None or caption_padding_mask is None:
                raise ValueError("caption_embeds and caption_padding_mask are required")

            y, y_padding_mask = self.y_embedder(
                caption_embeds, caption_padding_mask, self.training
            )
            for block in self.blocks:
                x, x_enc = block(
                    x,
                    x_enc,
                    x_padding_mask,
                    c,
                    y=y,
                    y_padding_mask=y_padding_mask,
                    pos_embed=self.pos_embed[:, : x.shape[1]],
                )
        out = self.final_layer(x, c).chunk(2, dim=-1)
        return torch.cat([out[0], out[1]], dim=1)

    def forward_with_cfg(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        *,
        x_padding_mask: Bool[torch.Tensor, "batch elements"],
        aspect_ratio: Float[torch.Tensor, "batch"],
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"],
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
        guidance_scale: float,
    ) -> Float[torch.Tensor, "batch seq channels"]:
        """Run reference classifier-free guidance batching."""
        half = sample[: len(sample) // 2]
        combined = torch.cat([half, half], dim=0)
        model_out = self.forward(
            combined,
            timestep,
            x_padding_mask=x_padding_mask,
            aspect_ratio=aspect_ratio,
            concept_embeds=concept_embeds,
            caption_embeds=caption_embeds,
            caption_padding_mask=caption_padding_mask,
        )
        eps, rest = model_out[:, : sample.shape[1]], model_out[:, sample.shape[1] :]
        cond_eps, uncond_eps = torch.split(eps, len(eps) // 2, dim=0)
        half_eps = uncond_eps + guidance_scale * (cond_eps - uncond_eps)
        eps = torch.cat([half_eps, half_eps], dim=0)
        return torch.cat([eps, rest], dim=1)

__init__

__init__(
    *,
    in_channels: int = 4,
    max_in_len: int = 60,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_y_len: int | None = 120,
    model_name: str = "DiT-S",
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    mlp_ratio: float = 4.0,
    class_dropout_prob: float = 0.1,
    learn_sigma: bool = True,
    is_unconditional: bool = False,
) -> None

Initialize the converted DiT model.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
@register_to_config
def __init__(
    self,
    *,
    in_channels: int = 4,
    max_in_len: int = 60,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_y_len: int | None = 120,
    model_name: str = "DiT-S",
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    mlp_ratio: float = 4.0,
    class_dropout_prob: float = 0.1,
    learn_sigma: bool = True,
    is_unconditional: bool = False,
) -> None:
    """Initialize the converted DiT model."""
    super().__init__()
    shape = resolve_model_shape(
        model_name,
        hidden_size=hidden_size,
        depth=depth,
        num_heads=num_heads,
    )
    self.in_channels = in_channels
    self.learn_sigma = learn_sigma
    self.num_heads = shape["num_heads"]
    self.max_len = max_in_len
    self.is_unconditional = is_unconditional
    self.x_embedder = InputEmbedder(in_channels, shape["hidden_size"])
    self.concept_embedder = ConceptEmbedder(
        concept_in_channels, shape["hidden_size"]
    )
    self.t_embedder = ScalarEmbedder(shape["hidden_size"])
    self.ar_embedder = ScalarEmbedder(shape["hidden_size"])
    if is_unconditional:
        self.y_embedder = CaptionEmbedderIdentity()
    else:
        if y_in_channels is None or max_y_len is None:
            raise ValueError("y_in_channels and max_y_len are required")

        y_null = torch.zeros(max_y_len, y_in_channels)
        y_mask = torch.ones(max_y_len, dtype=torch.bool)
        self.y_embedder = CaptionEmbedder(
            y_in_channels,
            shape["hidden_size"],
            class_dropout_prob,
            y_null,
            y_mask,
        )
    self.pos_embed = nn.Parameter(
        torch.zeros(1, max_in_len, shape["hidden_size"]), requires_grad=False
    )
    block_cls = DiTUCBlock if is_unconditional else DiTBlock
    self.blocks = nn.ModuleList(
        [
            block_cls(shape["hidden_size"], shape["num_heads"], mlp_ratio=mlp_ratio)
            for _ in range(shape["depth"])
        ]
    )
    self.final_layer = FinalLayer(shape["hidden_size"], 2 * in_channels)
    self.initialize_weights()

initialize_weights

initialize_weights() -> None

Initialize weights with the reference policy.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
def initialize_weights(self) -> None:
    """Initialize weights with the reference policy."""

    def _basic_init(module: nn.Module) -> None:
        if isinstance(module, nn.Linear):
            torch.nn.init.xavier_uniform_(module.weight)
            if module.bias is not None:
                nn.init.constant_(module.bias, 0)

    self.apply(_basic_init)
    pos_embed = get_1d_sincos_pos_embed(self.pos_embed.shape[-1], self.max_len)
    self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0))
    nn.init.xavier_uniform_(self.x_embedder.proj.weight)
    nn.init.constant_(self.x_embedder.proj.bias, 0)

    if not self.is_unconditional and isinstance(self.y_embedder, CaptionEmbedder):
        nn.init.normal_(self.y_embedder.proj.fc1.weight, std=0.02)
        nn.init.normal_(self.y_embedder.proj.fc2.weight, std=0.02)

    nn.init.normal_(self.concept_embedder.proj.fc1.weight, std=0.02)
    nn.init.normal_(self.concept_embedder.proj.fc2.weight, std=0.02)
    nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
    nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
    nn.init.normal_(self.ar_embedder.mlp[0].weight, std=0.02)
    nn.init.normal_(self.ar_embedder.mlp[2].weight, std=0.02)

    for block in self.blocks:
        block.initialize_weights()

    nn.init.constant_(self.final_layer.adaLN_modulation[-1].weight, 0)
    nn.init.constant_(self.final_layer.adaLN_modulation[-1].bias, 0)
    nn.init.constant_(self.final_layer.linear.weight, 0)
    nn.init.constant_(self.final_layer.linear.bias, 0)

forward

forward(
    sample: Float[Tensor, "batch elements channels"],
    timestep: Int[Tensor, "batch"],
    *,
    x_padding_mask: Bool[Tensor, "batch elements"],
    aspect_ratio: Float[Tensor, "batch"],
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ],
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ]
    | None = None,
    caption_padding_mask: Bool[Tensor, "batch tokens"]
    | None = None,
) -> Float[torch.Tensor, "batch seq channels"]

Predict epsilon and variance channels for one timestep.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def forward(
    self,
    sample: Float[torch.Tensor, "batch elements channels"],
    timestep: Int[torch.Tensor, "batch"],
    *,
    x_padding_mask: Bool[torch.Tensor, "batch elements"],
    aspect_ratio: Float[torch.Tensor, "batch"],
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
) -> Float[torch.Tensor, "batch seq channels"]:
    """Predict epsilon and variance channels for one timestep."""
    x = self.x_embedder(sample)
    x_enc = self.concept_embedder(concept_embeds)
    c = self.t_embedder(timestep) + self.ar_embedder(aspect_ratio)
    if self.is_unconditional:
        for block in self.blocks:
            x = block(x, x_padding_mask=x_padding_mask, c=c)
    else:
        if caption_embeds is None or caption_padding_mask is None:
            raise ValueError("caption_embeds and caption_padding_mask are required")

        y, y_padding_mask = self.y_embedder(
            caption_embeds, caption_padding_mask, self.training
        )
        for block in self.blocks:
            x, x_enc = block(
                x,
                x_enc,
                x_padding_mask,
                c,
                y=y,
                y_padding_mask=y_padding_mask,
                pos_embed=self.pos_embed[:, : x.shape[1]],
            )
    out = self.final_layer(x, c).chunk(2, dim=-1)
    return torch.cat([out[0], out[1]], dim=1)

forward_with_cfg

forward_with_cfg(
    sample: Float[Tensor, "batch elements channels"],
    timestep: Int[Tensor, "batch"],
    *,
    x_padding_mask: Bool[Tensor, "batch elements"],
    aspect_ratio: Float[Tensor, "batch"],
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ],
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ],
    caption_padding_mask: Bool[Tensor, "batch tokens"],
    guidance_scale: float,
) -> Float[torch.Tensor, "batch seq channels"]

Run reference classifier-free guidance batching.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
def forward_with_cfg(
    self,
    sample: Float[torch.Tensor, "batch elements channels"],
    timestep: Int[torch.Tensor, "batch"],
    *,
    x_padding_mask: Bool[torch.Tensor, "batch elements"],
    aspect_ratio: Float[torch.Tensor, "batch"],
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"],
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
    guidance_scale: float,
) -> Float[torch.Tensor, "batch seq channels"]:
    """Run reference classifier-free guidance batching."""
    half = sample[: len(sample) // 2]
    combined = torch.cat([half, half], dim=0)
    model_out = self.forward(
        combined,
        timestep,
        x_padding_mask=x_padding_mask,
        aspect_ratio=aspect_ratio,
        concept_embeds=concept_embeds,
        caption_embeds=caption_embeds,
        caption_padding_mask=caption_padding_mask,
    )
    eps, rest = model_out[:, : sample.shape[1]], model_out[:, sample.shape[1] :]
    cond_eps, uncond_eps = torch.split(eps, len(eps) // 2, dim=0)
    half_eps = uncond_eps + guidance_scale * (cond_eps - uncond_eps)
    eps = torch.cat([half_eps, half_eps], dim=0)
    return torch.cat([eps, rest], dim=1)

LayouSynPipeline

Bases: DiffusionPipeline

Generate open-vocabulary scene layouts with LayouSyn.

Parameters:

Name Type Description Default
model LayouSynDiTModel

Converted DiT denoiser.

required
scheduler LayouSynScheduler

LayouSyn Gaussian/DDIM scheduler.

required
processor LayouSynProcessor

Processor for prompt/concept inputs and postprocessing.

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

    Args:
        model: Converted DiT denoiser.
        scheduler: LayouSyn Gaussian/DDIM scheduler.
        processor: Processor for prompt/concept inputs and postprocessing.
    """

    model_cpu_offload_seq = "model"
    _optional_components = ["processor"]

    def __init__(
        self,
        model: LayouSynDiTModel,
        scheduler: LayouSynScheduler,
        processor: LayouSynProcessor,
    ) -> None:
        """Initialize the pipeline."""
        super().__init__()
        self._register_layousyn_modules(model, scheduler, processor)
        self.model.eval()

    def _register_layousyn_modules(
        self,
        model: LayouSynDiTModel,
        scheduler: LayouSynScheduler,
        processor: LayouSynProcessor,
    ) -> None:
        """Register pipeline modules and keep concrete attributes typed."""
        self.register_modules(model=model, scheduler=scheduler)
        self.model = model
        self.scheduler = scheduler
        self.processor = processor

    @property
    def components(
        self,
    ) -> dict[str, LayouSynDiTModel | LayouSynScheduler | LayouSynProcessor]:
        """Return serializable pipeline components."""
        return {
            "model": self.model,
            "scheduler": self.scheduler,
            "processor": self.processor,
        }

    def save_pretrained(
        self,
        save_directory: str | os.PathLike[str],
        **kwargs: str | int | float | bool | None,
    ) -> None:
        """Save pipeline components plus processor metadata."""
        super().save_pretrained(save_directory, **kwargs)
        self.processor.save_pretrained(save_directory)

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        **kwargs: str | int | float | bool | None,
    ) -> LayouSynPipeline:
        """Load pipeline and restore local processor metadata."""
        pipe = super().from_pretrained(pretrained_model_name_or_path, **kwargs)
        if not isinstance(pipe, cls):
            raise TypeError(f"Expected {cls.__name__}, got {type(pipe).__name__}")

        processor_config = (
            Path(pretrained_model_name_or_path) / LayouSynProcessor.config_name
        )
        if pipe.processor is None and processor_config.exists():
            pipe.processor = LayouSynProcessor.from_pretrained(
                pretrained_model_name_or_path
            )
        return pipe

    @torch.no_grad()
    def __call__(
        self,
        *,
        prompt: str | list[str] | None = None,
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: ConditionType | str = ConditionType.text,
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | list[str]
        | list[list[str]]
        | None = None,
        id2label: dict[int, str] | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        aspect_ratio: float | list[float] | Float[torch.Tensor, "batch"] = 1.0,
        num_inference_steps: int | None = None,
        guidance_scale: float = 2.0,
        sampling_type: Literal["ddim", "ddpm"] = "ddim",
        output_type: Literal["dataclass", "dict"] = "dataclass",
        return_intermediates: bool = False,
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
        | None = None,
    ) -> LayoutGenerationOutput | LayouSynOutputDict:
        """Run LayouSyn denoising.

        Args:
            prompt: Caption text.
            batch_size: Number of generated layouts when labels are unbatched.
            seed: Convenience seed used only if ``generator`` is absent.
            generator: Exact reproducibility API.
            condition_type: Canonical condition name. First-class public mode is
                ``text``; unsupported modes fail explicitly.
            labels: String concepts or integer ids.
            id2label: Mapping for integer labels.
            bbox: Reserved for future initialization/refinement support.
            mask: Optional valid concept mask.
            num_elements: Optional expected element count. It is validated
                against labels when supplied.
            box_format: Public input bbox format.
            normalized: Whether input boxes are normalized.
            canvas_size: Required for pixel boxes.
            aspect_ratio: Scalar or per-example aspect ratio.
            num_inference_steps: Number of reverse diffusion steps.
            guidance_scale: Classifier-free guidance scale.
            sampling_type: ``ddim`` or ``ddpm``.
            output_type: ``dataclass`` or ``dict``.
            return_intermediates: Whether to return denoising trajectory.
            caption_embeds: Precomputed caption embeddings.
            caption_padding_mask: Precomputed caption padding mask.
            concept_embeds: Precomputed concept embeddings.

        Returns:
            Public layout output.
        """
        del batch_size
        canonical = normalize_condition_type(condition_type)
        if canonical is not ConditionType.text:
            raise NotImplementedError(
                f"LayouSyn public pipeline supports condition_type='text', got {condition_type}"
            )

        if generator is None and seed is not None:
            generator = torch.Generator(device=self.device).manual_seed(seed)
        encoded = self.processor(
            prompt=prompt,
            labels=labels,
            id2label=id2label,
            bbox=bbox,
            mask=mask,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
            aspect_ratio=aspect_ratio,
            caption_embeds=caption_embeds,
            caption_padding_mask=caption_padding_mask,
            concept_embeds=concept_embeds,
        )
        self._validate_num_elements(num_elements, encoded[LAYOUSYN_LABEL_TEXTS_KEY])
        concept_mask = encoded[LAYOUSYN_CONCEPT_MASK_KEY].to(self.device)
        batch = concept_mask.shape[0]
        self.scheduler.set_timesteps(num_inference_steps, device=self.device)
        sample = self.scheduler.initial_sample(
            batch,
            self.processor.max_in_len,
            self.model.config.in_channels,
            device=self.device,
            generator=generator,
        )
        model_kwargs = {
            "x_padding_mask": concept_mask,
            "aspect_ratio": encoded[LAYOUSYN_ASPECT_RATIO_KEY].to(self.device),
            "concept_embeds": encoded[LAYOUSYN_CONCEPT_EMBEDS_KEY].to(self.device),
            "caption_embeds": encoded[LAYOUSYN_CAPTION_EMBEDS_KEY].to(self.device),
            "caption_padding_mask": encoded[LAYOUSYN_CAPTION_MASK_KEY].to(self.device),
        }
        if guidance_scale != 1.0:
            sample = torch.cat([sample, sample], dim=0)
            model_kwargs = self._cfg_model_kwargs(model_kwargs, batch)
        trajectory = []
        for index, timestep in enumerate(self.scheduler.timesteps):
            model_timestep = self.scheduler.model_timesteps[index]
            t_model = torch.full(
                (sample.shape[0],),
                int(model_timestep),
                device=self.device,
                dtype=torch.long,
            )
            t_step = torch.full(
                (sample.shape[0],), int(timestep), device=self.device, dtype=torch.long
            )
            if guidance_scale != 1.0:
                model_output = self.model.forward_with_cfg(
                    sample, t_model, guidance_scale=guidance_scale, **model_kwargs
                )
            else:
                model_output = self.model(sample, t_model, **model_kwargs)
            step = self.scheduler.step(
                model_output,
                t_step,
                sample,
                generator=generator,
                sampling_type=sampling_type,
                clip_denoised=False,
            )
            sample = step.prev_sample
            if return_intermediates:
                trajectory.append(step.pred_original_sample[:batch].detach().cpu())
        sample = sample[:batch].clamp(-1.0, 1.0).detach().cpu()
        return self.processor.postprocess(
            sample,
            labels=encoded[LAYOUSYN_LABEL_TEXTS_KEY],
            id2label=encoded[LAYOUSYN_ID2LABEL_KEY],
            id2label_per_example=encoded[LAYOUSYN_PER_EXAMPLE_ID2LABEL_KEY],
            output_type=output_type,
            return_intermediates=return_intermediates,
            intermediates={"trajectory": trajectory} if return_intermediates else None,
        )

    generate = __call__

    def _cfg_model_kwargs(
        self, model_kwargs: dict[str, Shaped[torch.Tensor, ...]], batch_size: int
    ) -> dict[str, Shaped[torch.Tensor, ...]]:
        y_null = (
            self.model.y_embedder.y_embedding.to(self.device)
            .unsqueeze(0)
            .repeat(batch_size, 1, 1)
        )
        y_mask_null = (
            self.model.y_embedder.y_padding_mask.to(self.device)
            .unsqueeze(0)
            .repeat(batch_size, 1)
        )
        return {
            "x_padding_mask": torch.cat(
                [model_kwargs["x_padding_mask"], model_kwargs["x_padding_mask"]], dim=0
            ),
            "aspect_ratio": torch.cat(
                [model_kwargs["aspect_ratio"], model_kwargs["aspect_ratio"]], dim=0
            ),
            "concept_embeds": torch.cat(
                [model_kwargs["concept_embeds"], model_kwargs["concept_embeds"]], dim=0
            ),
            "caption_embeds": torch.cat(
                [model_kwargs["caption_embeds"], y_null], dim=0
            ),
            "caption_padding_mask": torch.cat(
                [model_kwargs["caption_padding_mask"], y_mask_null], dim=0
            ),
        }

    @staticmethod
    def _validate_num_elements(
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None,
        labels: list[list[str]],
    ) -> None:
        if num_elements is None:
            return
        if isinstance(num_elements, int):
            expected = [num_elements] * len(labels)
        elif isinstance(num_elements, torch.Tensor):
            expected = [int(item) for item in num_elements.tolist()]
        else:
            expected = [int(item) for item in num_elements]
        actual = [len(row) for row in labels]
        if expected != actual:
            raise ValueError(f"num_elements {expected} does not match labels {actual}")

components property

components: dict[
    str,
    LayouSynDiTModel
    | LayouSynScheduler
    | LayouSynProcessor,
]

Return serializable pipeline components.

__init__

__init__(
    model: LayouSynDiTModel,
    scheduler: LayouSynScheduler,
    processor: LayouSynProcessor,
) -> None

Initialize the pipeline.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.py
46
47
48
49
50
51
52
53
54
55
def __init__(
    self,
    model: LayouSynDiTModel,
    scheduler: LayouSynScheduler,
    processor: LayouSynProcessor,
) -> None:
    """Initialize the pipeline."""
    super().__init__()
    self._register_layousyn_modules(model, scheduler, processor)
    self.model.eval()

save_pretrained

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

Save pipeline components plus processor metadata.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.py
80
81
82
83
84
85
86
87
def save_pretrained(
    self,
    save_directory: str | os.PathLike[str],
    **kwargs: str | int | float | bool | None,
) -> None:
    """Save pipeline components plus processor metadata."""
    super().save_pretrained(save_directory, **kwargs)
    self.processor.save_pretrained(save_directory)

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    **kwargs: str | int | float | bool | None,
) -> LayouSynPipeline

Load pipeline and restore local processor metadata.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    **kwargs: str | int | float | bool | None,
) -> LayouSynPipeline:
    """Load pipeline and restore local processor metadata."""
    pipe = super().from_pretrained(pretrained_model_name_or_path, **kwargs)
    if not isinstance(pipe, cls):
        raise TypeError(f"Expected {cls.__name__}, got {type(pipe).__name__}")

    processor_config = (
        Path(pretrained_model_name_or_path) / LayouSynProcessor.config_name
    )
    if pipe.processor is None and processor_config.exists():
        pipe.processor = LayouSynProcessor.from_pretrained(
            pretrained_model_name_or_path
        )
    return pipe

__call__

__call__(
    *,
    prompt: str | list[str] | None = None,
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: ConditionType
    | str = ConditionType.text,
    labels: Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | list[str]
    | list[list[str]]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int
    | list[int]
    | Int[Tensor, "batch"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float
    | list[float]
    | Float[Tensor, "batch"] = 1.0,
    num_inference_steps: int | None = None,
    guidance_scale: float = 2.0,
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ]
    | None = None,
    caption_padding_mask: Bool[Tensor, "batch tokens"]
    | None = None,
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ]
    | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict

Run LayouSyn denoising.

Parameters:

Name Type Description Default
prompt str | list[str] | None

Caption text.

None
batch_size int

Number of generated layouts when labels are unbatched.

1
seed int | None

Convenience seed used only if generator is absent.

None
generator Generator | None

Exact reproducibility API.

None
condition_type ConditionType | str

Canonical condition name. First-class public mode is text; unsupported modes fail explicitly.

text
labels Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | list[str] | list[list[str]] | None

String concepts or integer ids.

None
id2label dict[int, str] | None

Mapping for integer labels.

None
bbox Float[Tensor, 'batch elements 4'] | Float[ndarray, 'batch elements 4'] | Sequence[ArrayLikeInput] | None

Reserved for future initialization/refinement support.

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

Optional valid concept mask.

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

Optional expected element count. It is validated against labels when supplied.

None
box_format BoxFormat | str

Public input bbox format.

xywh
normalized bool

Whether input boxes are normalized.

True
canvas_size tuple[int, int] | None

Required for pixel boxes.

None
aspect_ratio float | list[float] | Float[Tensor, 'batch']

Scalar or per-example aspect ratio.

1.0
num_inference_steps int | None

Number of reverse diffusion steps.

None
guidance_scale float

Classifier-free guidance scale.

2.0
sampling_type Literal['ddim', 'ddpm']

ddim or ddpm.

'ddim'
output_type Literal['dataclass', 'dict']

dataclass or dict.

'dataclass'
return_intermediates bool

Whether to return denoising trajectory.

False
caption_embeds Float[Tensor, 'batch tokens embedding_dim'] | None

Precomputed caption embeddings.

None
caption_padding_mask Bool[Tensor, 'batch tokens'] | None

Precomputed caption padding mask.

None
concept_embeds Float[Tensor, 'batch elements embedding_dim'] | None

Precomputed concept embeddings.

None

Returns:

Type Description
LayoutGenerationOutput | LayouSynOutputDict

Public layout output.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.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
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
@torch.no_grad()
def __call__(
    self,
    *,
    prompt: str | list[str] | None = None,
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: ConditionType | str = ConditionType.text,
    labels: Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | list[str]
    | list[list[str]]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float | list[float] | Float[torch.Tensor, "batch"] = 1.0,
    num_inference_steps: int | None = None,
    guidance_scale: float = 2.0,
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
    | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict:
    """Run LayouSyn denoising.

    Args:
        prompt: Caption text.
        batch_size: Number of generated layouts when labels are unbatched.
        seed: Convenience seed used only if ``generator`` is absent.
        generator: Exact reproducibility API.
        condition_type: Canonical condition name. First-class public mode is
            ``text``; unsupported modes fail explicitly.
        labels: String concepts or integer ids.
        id2label: Mapping for integer labels.
        bbox: Reserved for future initialization/refinement support.
        mask: Optional valid concept mask.
        num_elements: Optional expected element count. It is validated
            against labels when supplied.
        box_format: Public input bbox format.
        normalized: Whether input boxes are normalized.
        canvas_size: Required for pixel boxes.
        aspect_ratio: Scalar or per-example aspect ratio.
        num_inference_steps: Number of reverse diffusion steps.
        guidance_scale: Classifier-free guidance scale.
        sampling_type: ``ddim`` or ``ddpm``.
        output_type: ``dataclass`` or ``dict``.
        return_intermediates: Whether to return denoising trajectory.
        caption_embeds: Precomputed caption embeddings.
        caption_padding_mask: Precomputed caption padding mask.
        concept_embeds: Precomputed concept embeddings.

    Returns:
        Public layout output.
    """
    del batch_size
    canonical = normalize_condition_type(condition_type)
    if canonical is not ConditionType.text:
        raise NotImplementedError(
            f"LayouSyn public pipeline supports condition_type='text', got {condition_type}"
        )

    if generator is None and seed is not None:
        generator = torch.Generator(device=self.device).manual_seed(seed)
    encoded = self.processor(
        prompt=prompt,
        labels=labels,
        id2label=id2label,
        bbox=bbox,
        mask=mask,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
        aspect_ratio=aspect_ratio,
        caption_embeds=caption_embeds,
        caption_padding_mask=caption_padding_mask,
        concept_embeds=concept_embeds,
    )
    self._validate_num_elements(num_elements, encoded[LAYOUSYN_LABEL_TEXTS_KEY])
    concept_mask = encoded[LAYOUSYN_CONCEPT_MASK_KEY].to(self.device)
    batch = concept_mask.shape[0]
    self.scheduler.set_timesteps(num_inference_steps, device=self.device)
    sample = self.scheduler.initial_sample(
        batch,
        self.processor.max_in_len,
        self.model.config.in_channels,
        device=self.device,
        generator=generator,
    )
    model_kwargs = {
        "x_padding_mask": concept_mask,
        "aspect_ratio": encoded[LAYOUSYN_ASPECT_RATIO_KEY].to(self.device),
        "concept_embeds": encoded[LAYOUSYN_CONCEPT_EMBEDS_KEY].to(self.device),
        "caption_embeds": encoded[LAYOUSYN_CAPTION_EMBEDS_KEY].to(self.device),
        "caption_padding_mask": encoded[LAYOUSYN_CAPTION_MASK_KEY].to(self.device),
    }
    if guidance_scale != 1.0:
        sample = torch.cat([sample, sample], dim=0)
        model_kwargs = self._cfg_model_kwargs(model_kwargs, batch)
    trajectory = []
    for index, timestep in enumerate(self.scheduler.timesteps):
        model_timestep = self.scheduler.model_timesteps[index]
        t_model = torch.full(
            (sample.shape[0],),
            int(model_timestep),
            device=self.device,
            dtype=torch.long,
        )
        t_step = torch.full(
            (sample.shape[0],), int(timestep), device=self.device, dtype=torch.long
        )
        if guidance_scale != 1.0:
            model_output = self.model.forward_with_cfg(
                sample, t_model, guidance_scale=guidance_scale, **model_kwargs
            )
        else:
            model_output = self.model(sample, t_model, **model_kwargs)
        step = self.scheduler.step(
            model_output,
            t_step,
            sample,
            generator=generator,
            sampling_type=sampling_type,
            clip_denoised=False,
        )
        sample = step.prev_sample
        if return_intermediates:
            trajectory.append(step.pred_original_sample[:batch].detach().cpu())
    sample = sample[:batch].clamp(-1.0, 1.0).detach().cpu()
    return self.processor.postprocess(
        sample,
        labels=encoded[LAYOUSYN_LABEL_TEXTS_KEY],
        id2label=encoded[LAYOUSYN_ID2LABEL_KEY],
        id2label_per_example=encoded[LAYOUSYN_PER_EXAMPLE_ID2LABEL_KEY],
        output_type=output_type,
        return_intermediates=return_intermediates,
        intermediates={"trajectory": trajectory} if return_intermediates else None,
    )

LayouSynProcessor

Bases: ProcessorMixin

Encode prompts and open-vocabulary concepts for LayouSyn.

Parameters:

Name Type Description Default
layout_type Literal['xyxy', 'cxcywh']

Reference layout type used by generated coordinates.

'xyxy'
max_in_len int

Maximum number of concept slots.

60
caption_model_name str

Text encoder identifier used for captions.

't5-v1_1-base'
concept_model_name str

Sentence-transformers model id for concept labels.

'sentence-transformers/sentence-t5-base'
id2label dict[int, str] | None

Optional fixed vocabulary for integer labels.

None
open_vocabulary bool

Whether string labels are accepted per request.

True
Source code in models/layousyn/src/layousyn/processing_layousyn.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
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
class LayouSynProcessor(ProcessorMixin):
    """Encode prompts and open-vocabulary concepts for LayouSyn.

    Args:
        layout_type: Reference layout type used by generated coordinates.
        max_in_len: Maximum number of concept slots.
        caption_model_name: Text encoder identifier used for captions.
        concept_model_name: Sentence-transformers model id for concept labels.
        id2label: Optional fixed vocabulary for integer labels.
        open_vocabulary: Whether string labels are accepted per request.
    """

    config_name = "processor_config.json"

    def __init__(
        self,
        *,
        layout_type: Literal["xyxy", "cxcywh"] = "xyxy",
        max_in_len: int = 60,
        max_y_len: int = 120,
        concept_in_channels: int = 768,
        y_in_channels: int = 768,
        caption_model_name: str = "t5-v1_1-base",
        concept_model_name: str = "sentence-transformers/sentence-t5-base",
        id2label: dict[int, str] | None = None,
        open_vocabulary: bool = True,
    ) -> None:
        """Initialize processor metadata."""
        super().__init__()
        self.layout_type = layout_type
        self.max_in_len = max_in_len
        self.max_y_len = max_y_len
        self.concept_in_channels = concept_in_channels
        self.y_in_channels = y_in_channels
        self.caption_model_name = caption_model_name
        self.concept_model_name = concept_model_name
        self.id2label = id2label
        self.open_vocabulary = open_vocabulary

    def to_dict(self) -> dict[str, str | int | bool | dict[int, str] | None]:
        """Serialize processor metadata."""
        return {
            "layout_type": self.layout_type,
            "max_in_len": self.max_in_len,
            "max_y_len": self.max_y_len,
            "concept_in_channels": self.concept_in_channels,
            "y_in_channels": self.y_in_channels,
            "caption_model_name": self.caption_model_name,
            "concept_model_name": self.concept_model_name,
            "id2label": self.id2label,
            "open_vocabulary": self.open_vocabulary,
            "license": "cc-by-nc-4.0",
        }

    def save_pretrained(
        self,
        save_directory: str | Path,
        push_to_hub: bool = False,
        **kwargs: str | int | float | bool | None,
    ) -> tuple[str]:
        """Save processor metadata."""
        del kwargs
        if push_to_hub:
            raise ValueError("LayouSynProcessor does not push to Hub directly")

        path = Path(save_directory)
        path.mkdir(parents=True, exist_ok=True)
        out = path / self.config_name
        out.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True))
        return (str(out),)

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        cache_dir: str | os.PathLike[str] | None = None,
        force_download: bool = False,
        local_files_only: bool = False,
        token: str | bool | None = None,
        revision: str = "main",
        **kwargs: str | int | float | bool | None,
    ) -> LayouSynProcessor:
        """Load processor metadata from a local directory."""
        del cache_dir, force_download, local_files_only, token, revision
        path = Path(pretrained_model_name_or_path) / cls.config_name
        data = json.loads(path.read_text())
        data.update(kwargs)
        data.pop("license", None)
        if data.get("id2label") is not None:
            data["id2label"] = {int(k): str(v) for k, v in data["id2label"].items()}
        return cls(**data)

    def __call__(
        self,
        *,
        prompt: str | Sequence[str] | None = None,
        labels: Sequence[str]
        | Sequence[Sequence[str]]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | None = None,
        id2label: dict[int, str] | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        aspect_ratio: float | Sequence[float] | Float[torch.Tensor, "batch"] = 1.0,
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
        | None = None,
    ) -> LayouSynBatch:
        """Encode public text and concept inputs.

        Args:
            prompt: Caption text or batch of captions.
            labels: String concepts or integer labels.
            id2label: Mapping required for integer labels when no fixed
                processor mapping exists.
            bbox: Optional conditioning boxes for future init/refinement paths.
            mask: Optional valid-element mask.
            box_format: Public bbox format.
            normalized: Whether bbox coordinates are normalized.
            canvas_size: Required when ``normalized=False``.
            aspect_ratio: Scalar or per-example aspect ratio.
            caption_embeds: Precomputed caption embeddings.
            caption_padding_mask: Precomputed caption padding mask.
            concept_embeds: Precomputed concept embeddings.

        Returns:
            Encoded processor batch.

        Raises:
            ValueError: If required labels or embeddings are missing.
        """
        prompts = self._normalize_prompts(prompt)
        label_texts, union_id2label, per_example = self._normalize_labels(
            labels, id2label=id2label, batch_size=len(prompts)
        )
        batch_size = len(label_texts)
        if len(prompts) == 1 and batch_size > 1:
            prompts = prompts * batch_size
        if len(prompts) != batch_size:
            raise ValueError("prompt and labels batch sizes must match")

        concept_padding_mask = self._concept_padding_mask(label_texts, mask=mask)
        if concept_embeds is None:
            concept_embeds = self._encode_concepts(label_texts)
        concept_embeds = self._pad_concept_embeds(concept_embeds, batch_size)
        if caption_embeds is None or caption_padding_mask is None:
            caption_embeds, caption_padding_mask = self._encode_captions(prompts)
        caption_embeds = caption_embeds.float()
        caption_padding_mask = caption_padding_mask.bool()
        if bbox is not None:
            self._normalize_optional_bbox(
                bbox,
                box_format=box_format,
                normalized=normalized,
                canvas_size=canvas_size,
            )
        return LayouSynBatch(
            concept_embeds=concept_embeds.float(),
            concept_padding_mask=concept_padding_mask,
            caption_embeds=caption_embeds,
            caption_padding_mask=caption_padding_mask,
            aspect_ratio=self._aspect_ratio_tensor(aspect_ratio, batch_size),
            label_texts=label_texts,
            id2label=union_id2label,
            id2label_per_example=per_example,
        )

    def postprocess(
        self,
        sample: Float[torch.Tensor, "batch elements 4"],
        *,
        labels: list[list[str]],
        id2label: dict[int, str],
        id2label_per_example: list[dict[int, str]] | None = None,
        output_type: Literal["dataclass", "dict"] = "dataclass",
        return_intermediates: bool = False,
        intermediates: LayouSynIntermediateValue | None = None,
    ) -> LayoutGenerationOutput | LayouSynOutputDict:
        """Convert generated reference coordinates into the public schema."""
        sample = ((sample.clamp(-1, 1) + 1.0) / 2.0).float()
        if self.layout_type == "xyxy":
            left, top, right, bottom = sample.unbind(dim=-1)
            fixed = torch.stack(
                (
                    torch.minimum(left, right),
                    torch.minimum(top, bottom),
                    torch.maximum(left, right),
                    torch.maximum(top, bottom),
                ),
                dim=-1,
            )
            bbox = clamp_boxes(ltrb_to_xywh(fixed))
        else:
            bbox = clamp_boxes(sample)
        batch_size = sample.shape[0]
        label_ids = torch.zeros(batch_size, self.max_in_len, dtype=torch.long)
        mask = torch.zeros(batch_size, self.max_in_len, dtype=torch.bool)
        label2id = {text: idx for idx, text in id2label.items()}
        for batch_idx, batch_labels in enumerate(labels):
            for pos, text in enumerate(batch_labels[: self.max_in_len]):
                label_ids[batch_idx, pos] = label2id[text]
                mask[batch_idx, pos] = True
        payload = intermediates if return_intermediates else None
        if return_intermediates:
            payload = {
                "label_texts": labels,
                "id2label_per_example": id2label_per_example,
                "reference_layout_type": self.layout_type,
                "intermediates": intermediates,
            }
        output = LayoutGenerationOutput(
            bbox=bbox,
            labels=label_ids,
            mask=mask,
            id2label=id2label,
            intermediates=payload,
        )
        if output_type == "dict":
            return cast(LayouSynOutputDict, dict(output))
        if output_type != "dataclass":
            raise ValueError(f"Unsupported output_type: {output_type}")

        return output

    def _normalize_prompts(self, prompt: str | Sequence[str] | None) -> list[str]:
        if prompt is None:
            return [""]
        if isinstance(prompt, str):
            return [prompt]
        return [str(item) for item in prompt]

    def _normalize_labels(
        self,
        labels: Sequence[str]
        | Sequence[Sequence[str]]
        | Int[torch.Tensor, ...]
        | Int[np.ndarray, ...]
        | None,
        *,
        id2label: dict[int, str] | None,
        batch_size: int,
    ) -> tuple[list[list[str]], dict[int, str], list[dict[int, str]]]:
        if labels is None:
            raise ValueError("LayouSyn requires labels/concepts for object slots")

        mapping = id2label or self.id2label
        if isinstance(labels, torch.Tensor | np.ndarray):
            if mapping is None:
                raise ValueError("id2label is required when labels are integer ids")

            labels_t = torch.as_tensor(labels, dtype=torch.long)
            if labels_t.ndim == 1:
                labels_t = labels_t.unsqueeze(0)
            label_texts = [
                [mapping[int(idx)] for idx in row.tolist() if int(idx) in mapping]
                for row in labels_t
            ]
        else:
            label_texts = self._string_label_batches(labels, batch_size=batch_size)
        union: dict[str, int] = {}
        per_example: list[dict[int, str]] = []
        for batch_labels in label_texts:
            local: dict[int, str] = {}
            for text in batch_labels:
                if text not in union:
                    union[text] = len(union)
                if text not in local.values():
                    local[len(local)] = text
            per_example.append(local)
        return label_texts, {idx: text for text, idx in union.items()}, per_example

    def _string_label_batches(
        self,
        labels: Sequence[str] | Sequence[Sequence[str]],
        *,
        batch_size: int,
    ) -> list[list[str]]:
        if len(labels) == 0:
            return [[]]
        first = labels[0]
        if isinstance(first, str):
            return [[str(item) for item in labels]]
        return [[str(item) for item in row] for row in labels]

    def _concept_padding_mask(
        self,
        labels: list[list[str]],
        *,
        mask: Bool[torch.Tensor, ...]
        | Bool[np.ndarray, ...]
        | Sequence[ArrayLikeInput]
        | None,
    ) -> Bool[torch.Tensor, "batch elements"]:
        if mask is not None:
            mask_t = torch.as_tensor(mask, dtype=torch.bool)
            if mask_t.ndim == 1:
                mask_t = mask_t.unsqueeze(0)
            valid = torch.zeros(mask_t.shape[0], self.max_in_len, dtype=torch.bool)
            valid[:, : min(mask_t.shape[1], self.max_in_len)] = mask_t[
                :, : self.max_in_len
            ]
            return ~valid
        padding = torch.ones(len(labels), self.max_in_len, dtype=torch.bool)
        for batch_idx, batch_labels in enumerate(labels):
            padding[batch_idx, : min(len(batch_labels), self.max_in_len)] = False
        return padding

    def _pad_concept_embeds(
        self,
        embeds: Float[torch.Tensor, "batch elements embedding_dim"],
        batch_size: int,
    ) -> Float[torch.Tensor, "batch elements embedding_dim"]:
        if embeds.ndim != 3:
            raise ValueError("concept_embeds must have shape (batch, seq, dim)")

        if embeds.shape[0] != batch_size:
            raise ValueError("concept_embeds batch size must match labels")

        if embeds.shape[1] > self.max_in_len:
            return embeds[:, : self.max_in_len]
        if embeds.shape[1] == self.max_in_len:
            return embeds
        pad = torch.zeros(
            batch_size,
            self.max_in_len - embeds.shape[1],
            embeds.shape[2],
            dtype=embeds.dtype,
            device=embeds.device,
        )
        return torch.cat((embeds, pad), dim=1)

    def _encode_concepts(
        self, labels: list[list[str]]
    ) -> Float[torch.Tensor, "batch elements embedding_dim"]:
        try:
            from sentence_transformers import SentenceTransformer
        except ImportError as exc:
            raise ValueError(
                "concept_embeds are required without sentence-transformers"
            ) from exc

        flat = [text for row in labels for text in row]
        if not flat:
            return torch.zeros(len(labels), 0, self.concept_in_channels)
        encoder = SentenceTransformer(self.concept_model_name)
        encoded = torch.as_tensor(encoder.encode(flat), dtype=torch.float32)
        rows = []
        offset = 0
        for row in labels:
            rows.append(encoded[offset : offset + len(row)])
            offset += len(row)
        return torch.nested.as_nested_tensor(rows).to_padded_tensor(0.0)

    def _encode_captions(
        self, prompts: list[str]
    ) -> tuple[
        Float[torch.Tensor, "batch tokens embedding_dim"],
        Bool[torch.Tensor, "batch tokens"],
    ]:
        if any(prompts):
            raise ValueError(
                "caption_embeds are required for prompt-conditioned tests/offline use"
            )

        return (
            torch.zeros(len(prompts), self.max_y_len, self.y_in_channels),
            torch.ones(len(prompts), self.max_y_len, dtype=torch.bool),
        )

    def _aspect_ratio_tensor(
        self,
        aspect_ratio: float | Sequence[float] | Float[torch.Tensor, "batch"],
        batch_size: int,
    ) -> Float[torch.Tensor, "batch"]:
        if isinstance(aspect_ratio, torch.Tensor):
            out = aspect_ratio.float()
        elif isinstance(aspect_ratio, float | int):
            out = torch.full((batch_size,), float(aspect_ratio))
        else:
            out = torch.tensor([float(item) for item in aspect_ratio])
        if out.numel() == 1 and batch_size > 1:
            out = out.repeat(batch_size)
        if out.shape != (batch_size,):
            raise ValueError("aspect_ratio must be scalar or match batch size")

        return out

    def _normalize_optional_bbox(
        self,
        bbox: Float[torch.Tensor, "... 4"]
        | Float[np.ndarray, "... 4"]
        | Sequence[ArrayLikeInput],
        *,
        box_format: BoxFormat | str,
        normalized: bool,
        canvas_size: tuple[int, int] | None,
    ) -> Float[torch.Tensor, "... 4"]:
        bbox_t = torch.as_tensor(bbox, dtype=torch.float32)
        if not normalized:
            if canvas_size is None:
                raise ValueError("canvas_size is required when normalized=False")

            bbox_t = normalize_boxes(
                bbox_t, canvas_size=canvas_size, box_format=box_format
            )
        elif normalize_box_format(box_format) is BoxFormat.ltrb:
            bbox_t = ltrb_to_xywh(bbox_t)
        if self.layout_type == "xyxy":
            return xywh_to_ltrb(bbox_t) * 2 - 1
        return bbox_t * 2 - 1

__init__

__init__(
    *,
    layout_type: Literal["xyxy", "cxcywh"] = "xyxy",
    max_in_len: int = 60,
    max_y_len: int = 120,
    concept_in_channels: int = 768,
    y_in_channels: int = 768,
    caption_model_name: str = "t5-v1_1-base",
    concept_model_name: str = "sentence-transformers/sentence-t5-base",
    id2label: dict[int, str] | None = None,
    open_vocabulary: bool = True,
) -> None

Initialize processor metadata.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def __init__(
    self,
    *,
    layout_type: Literal["xyxy", "cxcywh"] = "xyxy",
    max_in_len: int = 60,
    max_y_len: int = 120,
    concept_in_channels: int = 768,
    y_in_channels: int = 768,
    caption_model_name: str = "t5-v1_1-base",
    concept_model_name: str = "sentence-transformers/sentence-t5-base",
    id2label: dict[int, str] | None = None,
    open_vocabulary: bool = True,
) -> None:
    """Initialize processor metadata."""
    super().__init__()
    self.layout_type = layout_type
    self.max_in_len = max_in_len
    self.max_y_len = max_y_len
    self.concept_in_channels = concept_in_channels
    self.y_in_channels = y_in_channels
    self.caption_model_name = caption_model_name
    self.concept_model_name = concept_model_name
    self.id2label = id2label
    self.open_vocabulary = open_vocabulary

to_dict

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

Serialize processor metadata.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def to_dict(self) -> dict[str, str | int | bool | dict[int, str] | None]:
    """Serialize processor metadata."""
    return {
        "layout_type": self.layout_type,
        "max_in_len": self.max_in_len,
        "max_y_len": self.max_y_len,
        "concept_in_channels": self.concept_in_channels,
        "y_in_channels": self.y_in_channels,
        "caption_model_name": self.caption_model_name,
        "concept_model_name": self.concept_model_name,
        "id2label": self.id2label,
        "open_vocabulary": self.open_vocabulary,
        "license": "cc-by-nc-4.0",
    }

save_pretrained

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

Save processor metadata.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def save_pretrained(
    self,
    save_directory: str | Path,
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> tuple[str]:
    """Save processor metadata."""
    del kwargs
    if push_to_hub:
        raise ValueError("LayouSynProcessor does not push to Hub directly")

    path = Path(save_directory)
    path.mkdir(parents=True, exist_ok=True)
    out = path / self.config_name
    out.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True))
    return (str(out),)

from_pretrained classmethod

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

Load processor metadata from a local directory.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    cache_dir: str | os.PathLike[str] | None = None,
    force_download: bool = False,
    local_files_only: bool = False,
    token: str | bool | None = None,
    revision: str = "main",
    **kwargs: str | int | float | bool | None,
) -> LayouSynProcessor:
    """Load processor metadata from a local directory."""
    del cache_dir, force_download, local_files_only, token, revision
    path = Path(pretrained_model_name_or_path) / cls.config_name
    data = json.loads(path.read_text())
    data.update(kwargs)
    data.pop("license", None)
    if data.get("id2label") is not None:
        data["id2label"] = {int(k): str(v) for k, v in data["id2label"].items()}
    return cls(**data)

__call__

__call__(
    *,
    prompt: str | Sequence[str] | None = None,
    labels: Sequence[str]
    | Sequence[Sequence[str]]
    | Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float
    | Sequence[float]
    | Float[Tensor, "batch"] = 1.0,
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ]
    | None = None,
    caption_padding_mask: Bool[Tensor, "batch tokens"]
    | None = None,
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ]
    | None = None,
) -> LayouSynBatch

Encode public text and concept inputs.

Parameters:

Name Type Description Default
prompt str | Sequence[str] | None

Caption text or batch of captions.

None
labels Sequence[str] | Sequence[Sequence[str]] | Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | None

String concepts or integer labels.

None
id2label dict[int, str] | None

Mapping required for integer labels when no fixed processor mapping exists.

None
bbox Float[Tensor, 'batch elements 4'] | Float[ndarray, 'batch elements 4'] | Sequence[ArrayLikeInput] | None

Optional conditioning boxes for future init/refinement paths.

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

Optional valid-element mask.

None
box_format BoxFormat | str

Public bbox format.

xywh
normalized bool

Whether bbox coordinates are normalized.

True
canvas_size tuple[int, int] | None

Required when normalized=False.

None
aspect_ratio float | Sequence[float] | Float[Tensor, 'batch']

Scalar or per-example aspect ratio.

1.0
caption_embeds Float[Tensor, 'batch tokens embedding_dim'] | None

Precomputed caption embeddings.

None
caption_padding_mask Bool[Tensor, 'batch tokens'] | None

Precomputed caption padding mask.

None
concept_embeds Float[Tensor, 'batch elements embedding_dim'] | None

Precomputed concept embeddings.

None

Returns:

Type Description
LayouSynBatch

Encoded processor batch.

Raises:

Type Description
ValueError

If required labels or embeddings are missing.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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
def __call__(
    self,
    *,
    prompt: str | Sequence[str] | None = None,
    labels: Sequence[str]
    | Sequence[Sequence[str]]
    | Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float | Sequence[float] | Float[torch.Tensor, "batch"] = 1.0,
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
    | None = None,
) -> LayouSynBatch:
    """Encode public text and concept inputs.

    Args:
        prompt: Caption text or batch of captions.
        labels: String concepts or integer labels.
        id2label: Mapping required for integer labels when no fixed
            processor mapping exists.
        bbox: Optional conditioning boxes for future init/refinement paths.
        mask: Optional valid-element mask.
        box_format: Public bbox format.
        normalized: Whether bbox coordinates are normalized.
        canvas_size: Required when ``normalized=False``.
        aspect_ratio: Scalar or per-example aspect ratio.
        caption_embeds: Precomputed caption embeddings.
        caption_padding_mask: Precomputed caption padding mask.
        concept_embeds: Precomputed concept embeddings.

    Returns:
        Encoded processor batch.

    Raises:
        ValueError: If required labels or embeddings are missing.
    """
    prompts = self._normalize_prompts(prompt)
    label_texts, union_id2label, per_example = self._normalize_labels(
        labels, id2label=id2label, batch_size=len(prompts)
    )
    batch_size = len(label_texts)
    if len(prompts) == 1 and batch_size > 1:
        prompts = prompts * batch_size
    if len(prompts) != batch_size:
        raise ValueError("prompt and labels batch sizes must match")

    concept_padding_mask = self._concept_padding_mask(label_texts, mask=mask)
    if concept_embeds is None:
        concept_embeds = self._encode_concepts(label_texts)
    concept_embeds = self._pad_concept_embeds(concept_embeds, batch_size)
    if caption_embeds is None or caption_padding_mask is None:
        caption_embeds, caption_padding_mask = self._encode_captions(prompts)
    caption_embeds = caption_embeds.float()
    caption_padding_mask = caption_padding_mask.bool()
    if bbox is not None:
        self._normalize_optional_bbox(
            bbox,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
    return LayouSynBatch(
        concept_embeds=concept_embeds.float(),
        concept_padding_mask=concept_padding_mask,
        caption_embeds=caption_embeds,
        caption_padding_mask=caption_padding_mask,
        aspect_ratio=self._aspect_ratio_tensor(aspect_ratio, batch_size),
        label_texts=label_texts,
        id2label=union_id2label,
        id2label_per_example=per_example,
    )

postprocess

postprocess(
    sample: Float[Tensor, "batch elements 4"],
    *,
    labels: list[list[str]],
    id2label: dict[int, str],
    id2label_per_example: list[dict[int, str]]
    | None = None,
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    intermediates: LayouSynIntermediateValue | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict

Convert generated reference coordinates into the public schema.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def postprocess(
    self,
    sample: Float[torch.Tensor, "batch elements 4"],
    *,
    labels: list[list[str]],
    id2label: dict[int, str],
    id2label_per_example: list[dict[int, str]] | None = None,
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    intermediates: LayouSynIntermediateValue | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict:
    """Convert generated reference coordinates into the public schema."""
    sample = ((sample.clamp(-1, 1) + 1.0) / 2.0).float()
    if self.layout_type == "xyxy":
        left, top, right, bottom = sample.unbind(dim=-1)
        fixed = torch.stack(
            (
                torch.minimum(left, right),
                torch.minimum(top, bottom),
                torch.maximum(left, right),
                torch.maximum(top, bottom),
            ),
            dim=-1,
        )
        bbox = clamp_boxes(ltrb_to_xywh(fixed))
    else:
        bbox = clamp_boxes(sample)
    batch_size = sample.shape[0]
    label_ids = torch.zeros(batch_size, self.max_in_len, dtype=torch.long)
    mask = torch.zeros(batch_size, self.max_in_len, dtype=torch.bool)
    label2id = {text: idx for idx, text in id2label.items()}
    for batch_idx, batch_labels in enumerate(labels):
        for pos, text in enumerate(batch_labels[: self.max_in_len]):
            label_ids[batch_idx, pos] = label2id[text]
            mask[batch_idx, pos] = True
    payload = intermediates if return_intermediates else None
    if return_intermediates:
        payload = {
            "label_texts": labels,
            "id2label_per_example": id2label_per_example,
            "reference_layout_type": self.layout_type,
            "intermediates": intermediates,
        }
    output = LayoutGenerationOutput(
        bbox=bbox,
        labels=label_ids,
        mask=mask,
        id2label=id2label,
        intermediates=payload,
    )
    if output_type == "dict":
        return cast(LayouSynOutputDict, dict(output))
    if output_type != "dataclass":
        raise ValueError(f"Unsupported output_type: {output_type}")

    return output

LayouSynScheduler

Bases: SchedulerMixin, ConfigMixin

OpenAI-style Gaussian scheduler for LayouSyn layout tensors.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
class LayouSynScheduler(SchedulerMixin, ConfigMixin):
    """OpenAI-style Gaussian scheduler for LayouSyn layout tensors."""

    config_name = "scheduler_config.json"
    order = 1

    @register_to_config
    def __init__(
        self,
        *,
        num_train_timesteps: int = 100,
        beta_schedule: Literal["linear", "squaredcos_cap_v2"] = "linear",
        alpha_scale: float = 1.0,
        prediction_type: Literal["epsilon"] = "epsilon",
        variance_type: Literal["learned_range"] = "learned_range",
        sampling_type: Literal["ddim", "ddpm"] = "ddim",
    ) -> None:
        """Initialize scheduler buffers."""
        if prediction_type != "epsilon":
            raise ValueError("LayouSyn only supports prediction_type='epsilon'")

        if variance_type != "learned_range":
            raise ValueError("LayouSyn only supports variance_type='learned_range'")

        self.num_train_timesteps = num_train_timesteps
        self.sampling_type = sampling_type
        self.original_betas = get_layousyn_beta_schedule(
            beta_schedule, num_train_timesteps, alpha_scale=alpha_scale
        )
        self.timestep_map = torch.arange(num_train_timesteps, dtype=torch.long)
        self.model_timesteps = torch.empty(0, dtype=torch.long)
        self.timesteps = torch.empty(0, dtype=torch.long)
        self._set_betas(self.original_betas)
        self.set_timesteps(num_train_timesteps)

    def _set_betas(self, betas: Float[torch.Tensor, "timesteps"]) -> None:
        """Set derived buffers from the active beta sequence."""
        self.betas = betas.double()
        alphas = 1.0 - self.betas
        alphas_cumprod = torch.cumprod(alphas, dim=0)
        self.alphas_cumprod = alphas_cumprod
        self.alphas_cumprod_prev = torch.cat(
            [torch.ones(1, dtype=torch.float64), alphas_cumprod[:-1]]
        )
        posterior_variance = (
            self.betas * (1.0 - self.alphas_cumprod_prev) / (1.0 - alphas_cumprod)
        )
        self.posterior_variance = posterior_variance
        clipped = torch.log(
            torch.cat([posterior_variance[1:2], posterior_variance[1:]])
        )
        self.posterior_log_variance_clipped = clipped
        self.sqrt_recip_alphas_cumprod = torch.sqrt(1.0 / alphas_cumprod)
        self.sqrt_recipm1_alphas_cumprod = torch.sqrt(1.0 / alphas_cumprod - 1)
        self.posterior_mean_coef1 = (
            self.betas * torch.sqrt(self.alphas_cumprod_prev) / (1.0 - alphas_cumprod)
        )
        self.posterior_mean_coef2 = (
            (1.0 - self.alphas_cumprod_prev)
            * torch.sqrt(alphas)
            / (1.0 - alphas_cumprod)
        )

    def set_timesteps(
        self,
        num_inference_steps: int | None = None,
        device: torch.device | str | None = None,
    ) -> None:
        """Set descending denoising timesteps with reference respacing."""
        steps = num_inference_steps or self.num_train_timesteps
        if steps > self.num_train_timesteps:
            raise ValueError("num_inference_steps cannot exceed num_train_timesteps")

        use_timesteps = _space_timesteps(self.num_train_timesteps, steps)
        base_alphas = torch.cumprod(1.0 - self.original_betas.double(), dim=0)
        last_alpha_cumprod = torch.tensor(1.0, dtype=torch.float64)
        new_betas = []
        timestep_map = []
        for index, alpha_cumprod in enumerate(base_alphas):
            if index in use_timesteps:
                new_betas.append(1 - alpha_cumprod / last_alpha_cumprod)
                last_alpha_cumprod = alpha_cumprod
                timestep_map.append(index)
        self.timestep_map = torch.tensor(timestep_map, dtype=torch.long, device=device)
        self._set_betas(torch.stack(new_betas))
        self.timesteps = torch.arange(
            len(timestep_map) - 1, -1, -1, dtype=torch.long, device=device
        )
        self.model_timesteps = self.timestep_map[self.timesteps]

    def initial_sample(
        self,
        batch_size: int,
        seq_len: int,
        channels: int,
        *,
        device: torch.device,
        generator: torch.Generator | None = None,
    ) -> Float[torch.Tensor, "batch elements channels"]:
        """Create initial Gaussian noise."""
        return torch.randn(
            batch_size,
            seq_len,
            channels,
            dtype=torch.float32,
            device=device,
            generator=generator,
        )

    def add_noise(
        self,
        original_samples: Float[torch.Tensor, "batch elements channels"],
        noise: Float[torch.Tensor, "batch elements channels"],
        timesteps: Int[torch.Tensor, "batch"],
    ) -> Float[torch.Tensor, "batch elements channels"]:
        """Add forward-process noise to clean samples."""
        alphas = self.alphas_cumprod.to(original_samples.device)
        sqrt_alpha = torch.gather(alphas.sqrt(), 0, timesteps).reshape(-1, 1, 1)
        sqrt_one_minus = torch.gather(torch.sqrt(1.0 - alphas), 0, timesteps).reshape(
            -1, 1, 1
        )
        return sqrt_alpha * original_samples + sqrt_one_minus * noise

    def step(
        self,
        model_output: Float[torch.Tensor, "batch model_elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        sample: Float[torch.Tensor, "batch elements channels"],
        *,
        generator: torch.Generator | None = None,
        eta: float = 0.0,
        clip_denoised: bool = False,
        sampling_type: Literal["ddim", "ddpm"] | None = None,
        return_dict: bool = True,
    ) -> (
        LayouSynSchedulerOutput | tuple[Float[torch.Tensor, "batch elements channels"]]
    ):
        """Take one reverse diffusion step."""
        mode = sampling_type or self.sampling_type
        eps, model_var_values = torch.split(model_output, sample.shape[1], dim=1)
        pred_xstart = self._predict_xstart_from_eps(sample, timestep, eps)
        if clip_denoised:
            pred_xstart = pred_xstart.clamp(-1, 1)
        if mode == "ddim":
            prev_sample = self._ddim_step(
                sample, timestep, pred_xstart, generator=generator, eta=eta
            )
        elif mode == "ddpm":
            prev_sample = self._ddpm_step(
                sample, timestep, pred_xstart, model_var_values, generator=generator
            )
        else:
            raise ValueError(f"Unsupported sampling_type: {mode}")

        output = LayouSynSchedulerOutput(
            prev_sample=prev_sample, pred_original_sample=pred_xstart
        )
        if not return_dict:
            return (output.prev_sample,)
        return output

    def _ddim_step(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        pred_xstart: Float[torch.Tensor, "batch elements channels"],
        *,
        generator: torch.Generator | None,
        eta: float,
    ) -> Float[torch.Tensor, "batch elements channels"]:
        eps = self._predict_eps_from_xstart(sample, timestep, pred_xstart)
        alpha_bar = self._extract(self.alphas_cumprod, timestep, sample.shape)
        alpha_bar_prev = self._extract(self.alphas_cumprod_prev, timestep, sample.shape)
        sigma = (
            eta
            * torch.sqrt((1 - alpha_bar_prev) / (1 - alpha_bar))
            * torch.sqrt(1 - alpha_bar / alpha_bar_prev)
        )
        noise = torch.randn(
            sample.shape, dtype=sample.dtype, device=sample.device, generator=generator
        )
        mean_pred = (
            pred_xstart * torch.sqrt(alpha_bar_prev)
            + torch.sqrt(1 - alpha_bar_prev - sigma**2) * eps
        )
        nonzero_mask = (timestep != 0).float().view(-1, 1, 1)
        return mean_pred + nonzero_mask * sigma * noise

    def _ddpm_step(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        pred_xstart: Float[torch.Tensor, "batch elements channels"],
        model_var_values: Float[torch.Tensor, "batch elements channels"],
        *,
        generator: torch.Generator | None,
    ) -> Float[torch.Tensor, "batch elements channels"]:
        mean = (
            self._extract(self.posterior_mean_coef1, timestep, sample.shape)
            * pred_xstart
        )
        mean = (
            mean
            + self._extract(self.posterior_mean_coef2, timestep, sample.shape) * sample
        )
        min_log = self._extract(
            self.posterior_log_variance_clipped, timestep, sample.shape
        )
        max_log = self._extract(torch.log(self.betas), timestep, sample.shape)
        frac = (model_var_values + 1) / 2
        model_log_variance = frac * max_log + (1 - frac) * min_log
        noise = torch.randn(
            sample.shape, dtype=sample.dtype, device=sample.device, generator=generator
        )
        nonzero_mask = (timestep != 0).float().view(-1, 1, 1)
        return mean + nonzero_mask * torch.exp(0.5 * model_log_variance) * noise

    def _predict_xstart_from_eps(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        eps: Float[torch.Tensor, "batch elements channels"],
    ) -> Float[torch.Tensor, "batch elements channels"]:
        return self._extract(
            self.sqrt_recip_alphas_cumprod, timestep, sample.shape
        ) * sample - (
            self._extract(self.sqrt_recipm1_alphas_cumprod, timestep, sample.shape)
            * eps
        )

    def _predict_eps_from_xstart(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        pred_xstart: Float[torch.Tensor, "batch elements channels"],
    ) -> Float[torch.Tensor, "batch elements channels"]:
        return (
            self._extract(self.sqrt_recip_alphas_cumprod, timestep, sample.shape)
            * sample
            - pred_xstart
        ) / self._extract(self.sqrt_recipm1_alphas_cumprod, timestep, sample.shape)

    @staticmethod
    def _extract(
        values: Shaped[torch.Tensor, "timesteps"],
        timesteps: Int[torch.Tensor, "batch"],
        broadcast_shape: torch.Size,
    ) -> Float[torch.Tensor, "..."]:
        out = values.to(device=timesteps.device).gather(0, timesteps).float()
        while out.ndim < len(broadcast_shape):
            out = out.unsqueeze(-1)
        return out

__init__

__init__(
    *,
    num_train_timesteps: int = 100,
    beta_schedule: Literal[
        "linear", "squaredcos_cap_v2"
    ] = "linear",
    alpha_scale: float = 1.0,
    prediction_type: Literal["epsilon"] = "epsilon",
    variance_type: Literal[
        "learned_range"
    ] = "learned_range",
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
) -> None

Initialize scheduler buffers.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@register_to_config
def __init__(
    self,
    *,
    num_train_timesteps: int = 100,
    beta_schedule: Literal["linear", "squaredcos_cap_v2"] = "linear",
    alpha_scale: float = 1.0,
    prediction_type: Literal["epsilon"] = "epsilon",
    variance_type: Literal["learned_range"] = "learned_range",
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
) -> None:
    """Initialize scheduler buffers."""
    if prediction_type != "epsilon":
        raise ValueError("LayouSyn only supports prediction_type='epsilon'")

    if variance_type != "learned_range":
        raise ValueError("LayouSyn only supports variance_type='learned_range'")

    self.num_train_timesteps = num_train_timesteps
    self.sampling_type = sampling_type
    self.original_betas = get_layousyn_beta_schedule(
        beta_schedule, num_train_timesteps, alpha_scale=alpha_scale
    )
    self.timestep_map = torch.arange(num_train_timesteps, dtype=torch.long)
    self.model_timesteps = torch.empty(0, dtype=torch.long)
    self.timesteps = torch.empty(0, dtype=torch.long)
    self._set_betas(self.original_betas)
    self.set_timesteps(num_train_timesteps)

set_timesteps

set_timesteps(
    num_inference_steps: int | None = None,
    device: device | str | None = None,
) -> None

Set descending denoising timesteps with reference respacing.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def set_timesteps(
    self,
    num_inference_steps: int | None = None,
    device: torch.device | str | None = None,
) -> None:
    """Set descending denoising timesteps with reference respacing."""
    steps = num_inference_steps or self.num_train_timesteps
    if steps > self.num_train_timesteps:
        raise ValueError("num_inference_steps cannot exceed num_train_timesteps")

    use_timesteps = _space_timesteps(self.num_train_timesteps, steps)
    base_alphas = torch.cumprod(1.0 - self.original_betas.double(), dim=0)
    last_alpha_cumprod = torch.tensor(1.0, dtype=torch.float64)
    new_betas = []
    timestep_map = []
    for index, alpha_cumprod in enumerate(base_alphas):
        if index in use_timesteps:
            new_betas.append(1 - alpha_cumprod / last_alpha_cumprod)
            last_alpha_cumprod = alpha_cumprod
            timestep_map.append(index)
    self.timestep_map = torch.tensor(timestep_map, dtype=torch.long, device=device)
    self._set_betas(torch.stack(new_betas))
    self.timesteps = torch.arange(
        len(timestep_map) - 1, -1, -1, dtype=torch.long, device=device
    )
    self.model_timesteps = self.timestep_map[self.timesteps]

initial_sample

initial_sample(
    batch_size: int,
    seq_len: int,
    channels: int,
    *,
    device: device,
    generator: Generator | None = None,
) -> Float[torch.Tensor, "batch elements channels"]

Create initial Gaussian noise.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def initial_sample(
    self,
    batch_size: int,
    seq_len: int,
    channels: int,
    *,
    device: torch.device,
    generator: torch.Generator | None = None,
) -> Float[torch.Tensor, "batch elements channels"]:
    """Create initial Gaussian noise."""
    return torch.randn(
        batch_size,
        seq_len,
        channels,
        dtype=torch.float32,
        device=device,
        generator=generator,
    )

add_noise

add_noise(
    original_samples: Float[
        Tensor, "batch elements channels"
    ],
    noise: Float[Tensor, "batch elements channels"],
    timesteps: Int[Tensor, "batch"],
) -> Float[torch.Tensor, "batch elements channels"]

Add forward-process noise to clean samples.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
135
136
137
138
139
140
141
142
143
144
145
146
147
def add_noise(
    self,
    original_samples: Float[torch.Tensor, "batch elements channels"],
    noise: Float[torch.Tensor, "batch elements channels"],
    timesteps: Int[torch.Tensor, "batch"],
) -> Float[torch.Tensor, "batch elements channels"]:
    """Add forward-process noise to clean samples."""
    alphas = self.alphas_cumprod.to(original_samples.device)
    sqrt_alpha = torch.gather(alphas.sqrt(), 0, timesteps).reshape(-1, 1, 1)
    sqrt_one_minus = torch.gather(torch.sqrt(1.0 - alphas), 0, timesteps).reshape(
        -1, 1, 1
    )
    return sqrt_alpha * original_samples + sqrt_one_minus * noise

step

step(
    model_output: Float[
        Tensor, "batch model_elements channels"
    ],
    timestep: Int[Tensor, "batch"],
    sample: Float[Tensor, "batch elements channels"],
    *,
    generator: Generator | None = None,
    eta: float = 0.0,
    clip_denoised: bool = False,
    sampling_type: Literal["ddim", "ddpm"] | None = None,
    return_dict: bool = True,
) -> (
    LayouSynSchedulerOutput
    | tuple[Float[torch.Tensor, "batch elements channels"]]
)

Take one reverse diffusion step.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
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
def step(
    self,
    model_output: Float[torch.Tensor, "batch model_elements channels"],
    timestep: Int[torch.Tensor, "batch"],
    sample: Float[torch.Tensor, "batch elements channels"],
    *,
    generator: torch.Generator | None = None,
    eta: float = 0.0,
    clip_denoised: bool = False,
    sampling_type: Literal["ddim", "ddpm"] | None = None,
    return_dict: bool = True,
) -> (
    LayouSynSchedulerOutput | tuple[Float[torch.Tensor, "batch elements channels"]]
):
    """Take one reverse diffusion step."""
    mode = sampling_type or self.sampling_type
    eps, model_var_values = torch.split(model_output, sample.shape[1], dim=1)
    pred_xstart = self._predict_xstart_from_eps(sample, timestep, eps)
    if clip_denoised:
        pred_xstart = pred_xstart.clamp(-1, 1)
    if mode == "ddim":
        prev_sample = self._ddim_step(
            sample, timestep, pred_xstart, generator=generator, eta=eta
        )
    elif mode == "ddpm":
        prev_sample = self._ddpm_step(
            sample, timestep, pred_xstart, model_var_values, generator=generator
        )
    else:
        raise ValueError(f"Unsupported sampling_type: {mode}")

    output = LayouSynSchedulerOutput(
        prev_sample=prev_sample, pred_original_sample=pred_xstart
    )
    if not return_dict:
        return (output.prev_sample,)
    return output

configuration_layousyn

Configuration helpers for converted LayouSyn checkpoints.

LayouSynModelShape

Bases: TypedDict

Resolved DiT architecture shape.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
16
17
18
19
20
21
class LayouSynModelShape(TypedDict):
    """Resolved DiT architecture shape."""

    hidden_size: int
    depth: int
    num_heads: int

LayouSynReferenceConfig

Bases: TypedDict

Reference repository JSON config payload.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class LayouSynReferenceConfig(TypedDict):
    """Reference repository JSON config payload."""

    model: str
    in_channel: int
    concept_in_channel: int
    y_in_channel: int | None
    max_in_len: int
    max_y_len: int | None
    scale: float
    noise_schedule: str
    layout_type: LayoutType
    diffusion_steps: int
    t5_size: str | None

LayouSynConfig

Bases: ConfigMixin

Serializable LayouSyn configuration.

Parameters:

Name Type Description Default
model_name str

Reference DiT architecture key.

'DiT-S'
in_channels int

Layout coordinate channels.

4
concept_in_channels int

Concept embedding width.

768
y_in_channels int | None

Caption embedding width.

768
max_in_len int

Maximum number of object slots.

60
max_y_len int | None

Maximum number of caption tokens.

120
layout_type LayoutType

Reference layout coordinate type.

'xyxy'
t5_size str | None

Reference T5 size suffix.

'base'
scale float

Default classifier-free guidance scale.

2.0
noise_schedule str

Reference diffusion beta schedule.

'linear'
diffusion_steps int

Number of diffusion training timesteps.

100
hidden_size int | None

Optional resolved hidden width override.

None
depth int | None

Optional resolved transformer depth override.

None
num_heads int | None

Optional resolved attention head override.

None
license str

Upstream checkpoint license identifier.

'cc-by-nc-4.0'
Source code in models/layousyn/src/layousyn/configuration_layousyn.py
 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
class LayouSynConfig(ConfigMixin):
    """Serializable LayouSyn configuration.

    Args:
        model_name: Reference DiT architecture key.
        in_channels: Layout coordinate channels.
        concept_in_channels: Concept embedding width.
        y_in_channels: Caption embedding width.
        max_in_len: Maximum number of object slots.
        max_y_len: Maximum number of caption tokens.
        layout_type: Reference layout coordinate type.
        t5_size: Reference T5 size suffix.
        scale: Default classifier-free guidance scale.
        noise_schedule: Reference diffusion beta schedule.
        diffusion_steps: Number of diffusion training timesteps.
        hidden_size: Optional resolved hidden width override.
        depth: Optional resolved transformer depth override.
        num_heads: Optional resolved attention head override.
        license: Upstream checkpoint license identifier.
    """

    config_name = "config.json"

    @register_to_config
    def __init__(
        self,
        *,
        model_name: str = "DiT-S",
        in_channels: int = 4,
        concept_in_channels: int = 768,
        y_in_channels: int | None = 768,
        max_in_len: int = 60,
        max_y_len: int | None = 120,
        layout_type: LayoutType = "xyxy",
        t5_size: str | None = "base",
        scale: float = 2.0,
        noise_schedule: str = "linear",
        diffusion_steps: int = 100,
        hidden_size: int | None = None,
        depth: int | None = None,
        num_heads: int | None = None,
        license: str = "cc-by-nc-4.0",
    ) -> None:
        """Initialize configuration fields."""
        shape = resolve_model_shape(
            model_name,
            hidden_size=hidden_size,
            depth=depth,
            num_heads=num_heads,
        )
        self.model_name = model_name
        self.in_channels = in_channels
        self.concept_in_channels = concept_in_channels
        self.y_in_channels = y_in_channels
        self.max_in_len = max_in_len
        self.max_y_len = max_y_len
        self.layout_type = layout_type
        self.t5_size = t5_size
        self.scale = scale
        self.noise_schedule = noise_schedule
        self.diffusion_steps = diffusion_steps
        self.hidden_size = shape["hidden_size"]
        self.depth = shape["depth"]
        self.num_heads = shape["num_heads"]
        self.license = license

    @classmethod
    def from_reference_json(cls, path: str | Path) -> "LayouSynConfig":
        """Load a reference JSON config.

        Args:
            path: Path to a Lay-Your-Scene JSON config.

        Returns:
            Converted configuration object.
        """
        data = json.loads(Path(path).read_text())
        layout_type = data.get("layout_type", "xyxy")
        if not isinstance(layout_type, str):
            layout_type = str(layout_type)
        return cls(
            model_name=data.get("model", "DiT-S"),
            in_channels=data.get("in_channel", 4),
            concept_in_channels=data.get("concept_in_channel", 768),
            y_in_channels=data.get("y_in_channel"),
            max_in_len=data.get("max_in_len", 60),
            max_y_len=data.get("max_y_len"),
            layout_type="cxcywh" if "cxcywh" in layout_type.lower() else "xyxy",
            t5_size=data.get("t5_size"),
            scale=data.get("scale", 1.0),
            noise_schedule=data.get("noise_schedule", "linear"),
            diffusion_steps=data.get("diffusion_steps", 1000),
        )

    def to_reference_dict(self) -> LayouSynReferenceConfig:
        """Return the config keys expected by the original repository."""
        return {
            "model": self.model_name,
            "in_channel": self.in_channels,
            "concept_in_channel": self.concept_in_channels,
            "y_in_channel": self.y_in_channels,
            "max_in_len": self.max_in_len,
            "max_y_len": self.max_y_len,
            "scale": self.scale,
            "noise_schedule": self.noise_schedule,
            "layout_type": self.layout_type,
            "diffusion_steps": self.diffusion_steps,
            "t5_size": self.t5_size,
        }

__init__

__init__(
    *,
    model_name: str = "DiT-S",
    in_channels: int = 4,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_in_len: int = 60,
    max_y_len: int | None = 120,
    layout_type: LayoutType = "xyxy",
    t5_size: str | None = "base",
    scale: float = 2.0,
    noise_schedule: str = "linear",
    diffusion_steps: int = 100,
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    license: str = "cc-by-nc-4.0",
) -> None

Initialize configuration fields.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
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
@register_to_config
def __init__(
    self,
    *,
    model_name: str = "DiT-S",
    in_channels: int = 4,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_in_len: int = 60,
    max_y_len: int | None = 120,
    layout_type: LayoutType = "xyxy",
    t5_size: str | None = "base",
    scale: float = 2.0,
    noise_schedule: str = "linear",
    diffusion_steps: int = 100,
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    license: str = "cc-by-nc-4.0",
) -> None:
    """Initialize configuration fields."""
    shape = resolve_model_shape(
        model_name,
        hidden_size=hidden_size,
        depth=depth,
        num_heads=num_heads,
    )
    self.model_name = model_name
    self.in_channels = in_channels
    self.concept_in_channels = concept_in_channels
    self.y_in_channels = y_in_channels
    self.max_in_len = max_in_len
    self.max_y_len = max_y_len
    self.layout_type = layout_type
    self.t5_size = t5_size
    self.scale = scale
    self.noise_schedule = noise_schedule
    self.diffusion_steps = diffusion_steps
    self.hidden_size = shape["hidden_size"]
    self.depth = shape["depth"]
    self.num_heads = shape["num_heads"]
    self.license = license

from_reference_json classmethod

from_reference_json(path: str | Path) -> 'LayouSynConfig'

Load a reference JSON config.

Parameters:

Name Type Description Default
path str | Path

Path to a Lay-Your-Scene JSON config.

required

Returns:

Type Description
'LayouSynConfig'

Converted configuration object.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
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
@classmethod
def from_reference_json(cls, path: str | Path) -> "LayouSynConfig":
    """Load a reference JSON config.

    Args:
        path: Path to a Lay-Your-Scene JSON config.

    Returns:
        Converted configuration object.
    """
    data = json.loads(Path(path).read_text())
    layout_type = data.get("layout_type", "xyxy")
    if not isinstance(layout_type, str):
        layout_type = str(layout_type)
    return cls(
        model_name=data.get("model", "DiT-S"),
        in_channels=data.get("in_channel", 4),
        concept_in_channels=data.get("concept_in_channel", 768),
        y_in_channels=data.get("y_in_channel"),
        max_in_len=data.get("max_in_len", 60),
        max_y_len=data.get("max_y_len"),
        layout_type="cxcywh" if "cxcywh" in layout_type.lower() else "xyxy",
        t5_size=data.get("t5_size"),
        scale=data.get("scale", 1.0),
        noise_schedule=data.get("noise_schedule", "linear"),
        diffusion_steps=data.get("diffusion_steps", 1000),
    )

to_reference_dict

to_reference_dict() -> LayouSynReferenceConfig

Return the config keys expected by the original repository.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def to_reference_dict(self) -> LayouSynReferenceConfig:
    """Return the config keys expected by the original repository."""
    return {
        "model": self.model_name,
        "in_channel": self.in_channels,
        "concept_in_channel": self.concept_in_channels,
        "y_in_channel": self.y_in_channels,
        "max_in_len": self.max_in_len,
        "max_y_len": self.max_y_len,
        "scale": self.scale,
        "noise_schedule": self.noise_schedule,
        "layout_type": self.layout_type,
        "diffusion_steps": self.diffusion_steps,
        "t5_size": self.t5_size,
    }

resolve_model_shape

resolve_model_shape(
    model_name: str,
    *,
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
) -> LayouSynModelShape

Resolve a reference DiT name to concrete architecture dimensions.

Parameters:

Name Type Description Default
model_name str

Reference model key such as DiT-S or DiT-D28-H1152-N16.

required
hidden_size int | None

Optional explicit override.

None
depth int | None

Optional explicit override.

None
num_heads int | None

Optional explicit override.

None

Returns:

Type Description
LayouSynModelShape

Resolved architecture dimensions.

Raises:

Type Description
ValueError

If the model name is unsupported.

Source code in models/layousyn/src/layousyn/configuration_layousyn.py
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
def resolve_model_shape(
    model_name: str,
    *,
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
) -> LayouSynModelShape:
    """Resolve a reference DiT name to concrete architecture dimensions.

    Args:
        model_name: Reference model key such as ``DiT-S`` or
            ``DiT-D28-H1152-N16``.
        hidden_size: Optional explicit override.
        depth: Optional explicit override.
        num_heads: Optional explicit override.

    Returns:
        Resolved architecture dimensions.

    Raises:
        ValueError: If the model name is unsupported.
    """
    if model_name in _MODEL_SHAPES:
        shape = dict(_MODEL_SHAPES[model_name])
    else:
        match = _DIT_REGEX.match(model_name)
        if match is None:
            raise ValueError(f"Unsupported LayouSyn model_name: {model_name}")

        shape = {
            "hidden_size": int(match.group("hidden_size")),
            "depth": int(match.group("depth")),
            "num_heads": int(match.group("num_heads")),
        }
    if hidden_size is not None:
        shape["hidden_size"] = hidden_size
    if depth is not None:
        shape["depth"] = depth
    if num_heads is not None:
        shape["num_heads"] = num_heads
    return cast(LayouSynModelShape, shape)

conversion

Checkpoint conversion helpers for LayouSyn.

convert_checkpoint

convert_checkpoint(
    *,
    checkpoint_path: str | Path,
    config_path: str | Path,
    output_dir: str | Path,
    variant_name: str,
    push_to_hub: bool = False,
    hub_repo_id: str | None = None,
) -> LayouSynPipeline

Convert a vendor checkpoint into a local Diffusers pipeline.

Parameters:

Name Type Description Default
checkpoint_path str | Path

Vendor .pt checkpoint path.

required
config_path str | Path

Vendor JSON config path.

required
output_dir str | Path

Local output directory.

required
variant_name str

Human-readable checkpoint variant metadata.

required
push_to_hub bool

Reserved; ordinary implementation PRs must leave this false.

False
hub_repo_id str | None

Optional Hub repository id for future publishing.

None

Returns:

Type Description
LayouSynPipeline

Saved pipeline instance.

Raises:

Type Description
ValueError

If Hub push is requested from this implementation helper.

Source code in models/layousyn/src/layousyn/conversion.py
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def convert_checkpoint(
    *,
    checkpoint_path: str | Path,
    config_path: str | Path,
    output_dir: str | Path,
    variant_name: str,
    push_to_hub: bool = False,
    hub_repo_id: str | None = None,
) -> LayouSynPipeline:
    """Convert a vendor checkpoint into a local Diffusers pipeline.

    Args:
        checkpoint_path: Vendor ``.pt`` checkpoint path.
        config_path: Vendor JSON config path.
        output_dir: Local output directory.
        variant_name: Human-readable checkpoint variant metadata.
        push_to_hub: Reserved; ordinary implementation PRs must leave this
            false.
        hub_repo_id: Optional Hub repository id for future publishing.

    Returns:
        Saved pipeline instance.

    Raises:
        ValueError: If Hub push is requested from this implementation helper.
    """
    if push_to_hub:
        raise ValueError("Hub push is disabled for implementation PR conversion")

    del hub_repo_id
    config = LayouSynConfig.from_reference_json(config_path)
    model = LayouSynDiTModel(
        in_channels=config.in_channels,
        max_in_len=config.max_in_len,
        concept_in_channels=config.concept_in_channels,
        y_in_channels=config.y_in_channels,
        max_y_len=config.max_y_len,
        model_name=config.model_name,
        hidden_size=config.hidden_size,
        depth=config.depth,
        num_heads=config.num_heads,
    )
    raw = torch.load(checkpoint_path, map_location="cpu")
    state_dict = raw["ema"] if isinstance(raw, dict) and "ema" in raw else raw
    missing, unexpected = model.load_state_dict(
        convert_reference_state_dict(state_dict), strict=False
    )
    if unexpected:
        raise ValueError(
            f"Unexpected checkpoint keys for {variant_name}: {unexpected[:5]}"
        )

    scheduler = LayouSynScheduler(
        num_train_timesteps=config.diffusion_steps,
        beta_schedule=config.noise_schedule,
        alpha_scale=config.scale,
    )
    processor = LayouSynProcessor(
        layout_type=config.layout_type,
        max_in_len=config.max_in_len,
        max_y_len=config.max_y_len or 120,
        concept_in_channels=config.concept_in_channels,
        y_in_channels=config.y_in_channels or 768,
    )
    pipe = LayouSynPipeline(model=model, scheduler=scheduler, processor=processor)
    out = Path(output_dir)
    pipe.save_pretrained(out)
    processor.save_pretrained(out)
    (out / "conversion_metadata.json").write_text(
        json.dumps(
            {
                "variant_name": variant_name,
                "missing_keys": list(missing),
                "license": "cc-by-nc-4.0",
            },
            indent=2,
            sort_keys=True,
        )
    )
    return pipe

modeling_layousyn

PyTorch modules for the converted LayouSyn DiT denoiser.

Mlp

Bases: Module

Small MLP with timm-compatible fc1/fc2 parameter names.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Mlp(nn.Module):
    """Small MLP with timm-compatible ``fc1``/``fc2`` parameter names."""

    def __init__(
        self, in_features: int, hidden_features: int, out_features: int
    ) -> None:
        """Initialize the feed-forward projection."""
        super().__init__()
        self.fc1 = nn.Linear(in_features, hidden_features)
        self.act = nn.GELU(approximate="tanh")
        self.fc2 = nn.Linear(hidden_features, out_features)

    def forward(
        self, x: Float[torch.Tensor, "... in_features"]
    ) -> Float[torch.Tensor, "... out_features"]:
        """Apply the MLP."""
        return self.fc2(self.act(self.fc1(x)))

__init__

__init__(
    in_features: int,
    hidden_features: int,
    out_features: int,
) -> None

Initialize the feed-forward projection.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
30
31
32
33
34
35
36
37
def __init__(
    self, in_features: int, hidden_features: int, out_features: int
) -> None:
    """Initialize the feed-forward projection."""
    super().__init__()
    self.fc1 = nn.Linear(in_features, hidden_features)
    self.act = nn.GELU(approximate="tanh")
    self.fc2 = nn.Linear(hidden_features, out_features)

forward

forward(
    x: Float[Tensor, "... in_features"],
) -> Float[torch.Tensor, "... out_features"]

Apply the MLP.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
39
40
41
42
43
def forward(
    self, x: Float[torch.Tensor, "... in_features"]
) -> Float[torch.Tensor, "... out_features"]:
    """Apply the MLP."""
    return self.fc2(self.act(self.fc1(x)))

ScalarEmbedder

Bases: Module

Reference sinusoidal scalar embedding plus MLP projection.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
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
class ScalarEmbedder(nn.Module):
    """Reference sinusoidal scalar embedding plus MLP projection."""

    def __init__(self, hidden_size: int, frequency_embedding_size: int = 256) -> None:
        """Initialize the embedder."""
        super().__init__()
        self.mlp = nn.Sequential(
            nn.Linear(frequency_embedding_size, hidden_size),
            nn.SiLU(),
            nn.Linear(hidden_size, hidden_size),
        )
        self.frequency_embedding_size = frequency_embedding_size

    @staticmethod
    def scalar_embedding(
        scalar: Float[torch.Tensor, "batch"] | Int[torch.Tensor, "batch"],
        dim: int,
        max_period: int = 10000,
    ) -> Float[torch.Tensor, "batch channels"]:
        """Create sinusoidal embeddings for scalar values."""
        half = dim // 2
        freqs = torch.exp(
            -math.log(max_period)
            * torch.arange(start=0, end=half, dtype=torch.float32)
            / half
        ).to(device=scalar.device)
        args = scalar[:, None].float() * freqs[None]
        embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
        if dim % 2:
            embedding = torch.cat(
                [embedding, torch.zeros_like(embedding[:, :1])], dim=-1
            )
        return embedding

    def forward(
        self, scalar: Float[torch.Tensor, "batch"] | Int[torch.Tensor, "batch"]
    ) -> Float[torch.Tensor, "batch channels"]:
        """Embed scalar values."""
        return self.mlp(self.scalar_embedding(scalar, self.frequency_embedding_size))

__init__

__init__(
    hidden_size: int, frequency_embedding_size: int = 256
) -> None

Initialize the embedder.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
49
50
51
52
53
54
55
56
57
def __init__(self, hidden_size: int, frequency_embedding_size: int = 256) -> None:
    """Initialize the embedder."""
    super().__init__()
    self.mlp = nn.Sequential(
        nn.Linear(frequency_embedding_size, hidden_size),
        nn.SiLU(),
        nn.Linear(hidden_size, hidden_size),
    )
    self.frequency_embedding_size = frequency_embedding_size

scalar_embedding staticmethod

scalar_embedding(
    scalar: Float[Tensor, "batch"] | Int[Tensor, "batch"],
    dim: int,
    max_period: int = 10000,
) -> Float[torch.Tensor, "batch channels"]

Create sinusoidal embeddings for scalar values.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@staticmethod
def scalar_embedding(
    scalar: Float[torch.Tensor, "batch"] | Int[torch.Tensor, "batch"],
    dim: int,
    max_period: int = 10000,
) -> Float[torch.Tensor, "batch channels"]:
    """Create sinusoidal embeddings for scalar values."""
    half = dim // 2
    freqs = torch.exp(
        -math.log(max_period)
        * torch.arange(start=0, end=half, dtype=torch.float32)
        / half
    ).to(device=scalar.device)
    args = scalar[:, None].float() * freqs[None]
    embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
    if dim % 2:
        embedding = torch.cat(
            [embedding, torch.zeros_like(embedding[:, :1])], dim=-1
        )
    return embedding

forward

forward(
    scalar: Float[Tensor, "batch"] | Int[Tensor, "batch"],
) -> Float[torch.Tensor, "batch channels"]

Embed scalar values.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
80
81
82
83
84
def forward(
    self, scalar: Float[torch.Tensor, "batch"] | Int[torch.Tensor, "batch"]
) -> Float[torch.Tensor, "batch channels"]:
    """Embed scalar values."""
    return self.mlp(self.scalar_embedding(scalar, self.frequency_embedding_size))

InputEmbedder

Bases: Module

Linear layout-coordinate embedder.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
87
88
89
90
91
92
93
94
95
96
97
98
99
class InputEmbedder(nn.Module):
    """Linear layout-coordinate embedder."""

    def __init__(self, input_dim: int, hidden_dim: int) -> None:
        """Initialize projection."""
        super().__init__()
        self.proj = nn.Linear(input_dim, hidden_dim)

    def forward(
        self, x: Float[torch.Tensor, "batch elements channels"]
    ) -> Float[torch.Tensor, "batch elements hidden"]:
        """Project layout coordinates."""
        return self.proj(x)

__init__

__init__(input_dim: int, hidden_dim: int) -> None

Initialize projection.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
90
91
92
93
def __init__(self, input_dim: int, hidden_dim: int) -> None:
    """Initialize projection."""
    super().__init__()
    self.proj = nn.Linear(input_dim, hidden_dim)

forward

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

Project layout coordinates.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
95
96
97
98
99
def forward(
    self, x: Float[torch.Tensor, "batch elements channels"]
) -> Float[torch.Tensor, "batch elements hidden"]:
    """Project layout coordinates."""
    return self.proj(x)

ConceptEmbedder

Bases: Module

Project concept embeddings into the DiT hidden width.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
102
103
104
105
106
107
108
109
110
111
112
113
114
class ConceptEmbedder(nn.Module):
    """Project concept embeddings into the DiT hidden width."""

    def __init__(self, in_channels: int, hidden_size: int) -> None:
        """Initialize projection."""
        super().__init__()
        self.proj = Mlp(in_channels, hidden_size, hidden_size)

    def forward(
        self, x: Float[torch.Tensor, "batch elements channels"]
    ) -> Float[torch.Tensor, "batch elements hidden"]:
        """Project concept embeddings."""
        return self.proj(x)

__init__

__init__(in_channels: int, hidden_size: int) -> None

Initialize projection.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
105
106
107
108
def __init__(self, in_channels: int, hidden_size: int) -> None:
    """Initialize projection."""
    super().__init__()
    self.proj = Mlp(in_channels, hidden_size, hidden_size)

forward

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

Project concept embeddings.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
110
111
112
113
114
def forward(
    self, x: Float[torch.Tensor, "batch elements channels"]
) -> Float[torch.Tensor, "batch elements hidden"]:
    """Project concept embeddings."""
    return self.proj(x)

CaptionEmbedderIdentity

Bases: Module

No-op caption embedder for unconditional checkpoints.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
class CaptionEmbedderIdentity(nn.Module):
    """No-op caption embedder for unconditional checkpoints."""

    def forward(
        self,
        caption: Float[torch.Tensor, "batch tokens embedding_dim"] | None,
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None,
        train: bool,
        force_drop_ids: Int[torch.Tensor, "batch"]
        | Bool[torch.Tensor, "batch"]
        | None = None,
    ) -> tuple[
        Float[torch.Tensor, "batch tokens embedding_dim"] | None,
        Bool[torch.Tensor, "batch tokens"] | None,
    ]:
        """Return caption inputs unchanged."""
        del train, force_drop_ids
        return caption, caption_padding_mask

forward

forward(
    caption: Float[Tensor, "batch tokens embedding_dim"]
    | None,
    caption_padding_mask: Bool[Tensor, "batch tokens"]
    | None,
    train: bool,
    force_drop_ids: Int[Tensor, "batch"]
    | Bool[Tensor, "batch"]
    | None = None,
) -> tuple[
    Float[torch.Tensor, "batch tokens embedding_dim"]
    | None,
    Bool[torch.Tensor, "batch tokens"] | None,
]

Return caption inputs unchanged.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def forward(
    self,
    caption: Float[torch.Tensor, "batch tokens embedding_dim"] | None,
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None,
    train: bool,
    force_drop_ids: Int[torch.Tensor, "batch"]
    | Bool[torch.Tensor, "batch"]
    | None = None,
) -> tuple[
    Float[torch.Tensor, "batch tokens embedding_dim"] | None,
    Bool[torch.Tensor, "batch tokens"] | None,
]:
    """Return caption inputs unchanged."""
    del train, force_drop_ids
    return caption, caption_padding_mask

CaptionEmbedder

Bases: Module

Project caption embeddings and apply classifier-free label dropout.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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
class CaptionEmbedder(nn.Module):
    """Project caption embeddings and apply classifier-free label dropout."""

    y_embedding: Float[torch.Tensor, "tokens embedding_dim"]
    y_padding_mask: Bool[torch.Tensor, "tokens"]

    def __init__(
        self,
        in_channels: int,
        hidden_size: int,
        uncond_prob: float,
        y_null_embedding: Float[torch.Tensor, "tokens embedding_dim"],
        y_null_embedding_mask: Bool[torch.Tensor, "tokens"],
    ) -> None:
        """Initialize caption projection and null caption buffers."""
        super().__init__()
        self.proj = Mlp(in_channels, hidden_size, hidden_size)
        self.register_buffer("y_embedding", y_null_embedding.float())
        self.register_buffer("y_padding_mask", y_null_embedding_mask.bool())
        self.uncond_prob = uncond_prob

    def token_drop(
        self,
        caption: Float[torch.Tensor, "batch tokens embedding_dim"],
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
        force_drop_ids: Int[torch.Tensor, "batch"]
        | Bool[torch.Tensor, "batch"]
        | None = None,
    ) -> tuple[
        Float[torch.Tensor, "batch tokens embedding_dim"],
        Bool[torch.Tensor, "batch tokens"],
    ]:
        """Replace selected captions with the learned null caption."""
        if force_drop_ids is None:
            drop_ids = (
                torch.rand(caption.shape[0], device=caption.device) < self.uncond_prob
            )
        else:
            drop_ids = force_drop_ids == 1
        y_embedding = (
            self.y_embedding.to(caption.device).unsqueeze(0).expand_as(caption)
        )
        y_padding_mask = (
            self.y_padding_mask.to(caption_padding_mask.device)
            .unsqueeze(0)
            .expand_as(caption_padding_mask)
        )
        caption = torch.where(drop_ids[:, None, None], y_embedding, caption)
        caption_padding_mask = torch.where(
            drop_ids[:, None], y_padding_mask, caption_padding_mask
        )
        return caption, caption_padding_mask

    def forward(
        self,
        caption: Float[torch.Tensor, "batch tokens embedding_dim"],
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
        train: bool,
        force_drop_ids: Int[torch.Tensor, "batch"]
        | Bool[torch.Tensor, "batch"]
        | None = None,
    ) -> tuple[
        Float[torch.Tensor, "batch tokens hidden"],
        Bool[torch.Tensor, "batch tokens"],
    ]:
        """Project caption embeddings."""
        if (train and self.uncond_prob > 0) or force_drop_ids is not None:
            caption, caption_padding_mask = self.token_drop(
                caption, caption_padding_mask, force_drop_ids
            )
        return self.proj(caption), caption_padding_mask

__init__

__init__(
    in_channels: int,
    hidden_size: int,
    uncond_prob: float,
    y_null_embedding: Float[Tensor, "tokens embedding_dim"],
    y_null_embedding_mask: Bool[Tensor, "tokens"],
) -> None

Initialize caption projection and null caption buffers.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
def __init__(
    self,
    in_channels: int,
    hidden_size: int,
    uncond_prob: float,
    y_null_embedding: Float[torch.Tensor, "tokens embedding_dim"],
    y_null_embedding_mask: Bool[torch.Tensor, "tokens"],
) -> None:
    """Initialize caption projection and null caption buffers."""
    super().__init__()
    self.proj = Mlp(in_channels, hidden_size, hidden_size)
    self.register_buffer("y_embedding", y_null_embedding.float())
    self.register_buffer("y_padding_mask", y_null_embedding_mask.bool())
    self.uncond_prob = uncond_prob

token_drop

token_drop(
    caption: Float[Tensor, "batch tokens embedding_dim"],
    caption_padding_mask: Bool[Tensor, "batch tokens"],
    force_drop_ids: Int[Tensor, "batch"]
    | Bool[Tensor, "batch"]
    | None = None,
) -> tuple[
    Float[torch.Tensor, "batch tokens embedding_dim"],
    Bool[torch.Tensor, "batch tokens"],
]

Replace selected captions with the learned null caption.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def token_drop(
    self,
    caption: Float[torch.Tensor, "batch tokens embedding_dim"],
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
    force_drop_ids: Int[torch.Tensor, "batch"]
    | Bool[torch.Tensor, "batch"]
    | None = None,
) -> tuple[
    Float[torch.Tensor, "batch tokens embedding_dim"],
    Bool[torch.Tensor, "batch tokens"],
]:
    """Replace selected captions with the learned null caption."""
    if force_drop_ids is None:
        drop_ids = (
            torch.rand(caption.shape[0], device=caption.device) < self.uncond_prob
        )
    else:
        drop_ids = force_drop_ids == 1
    y_embedding = (
        self.y_embedding.to(caption.device).unsqueeze(0).expand_as(caption)
    )
    y_padding_mask = (
        self.y_padding_mask.to(caption_padding_mask.device)
        .unsqueeze(0)
        .expand_as(caption_padding_mask)
    )
    caption = torch.where(drop_ids[:, None, None], y_embedding, caption)
    caption_padding_mask = torch.where(
        drop_ids[:, None], y_padding_mask, caption_padding_mask
    )
    return caption, caption_padding_mask

forward

forward(
    caption: Float[Tensor, "batch tokens embedding_dim"],
    caption_padding_mask: Bool[Tensor, "batch tokens"],
    train: bool,
    force_drop_ids: Int[Tensor, "batch"]
    | Bool[Tensor, "batch"]
    | None = None,
) -> tuple[
    Float[torch.Tensor, "batch tokens hidden"],
    Bool[torch.Tensor, "batch tokens"],
]

Project caption embeddings.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def forward(
    self,
    caption: Float[torch.Tensor, "batch tokens embedding_dim"],
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
    train: bool,
    force_drop_ids: Int[torch.Tensor, "batch"]
    | Bool[torch.Tensor, "batch"]
    | None = None,
) -> tuple[
    Float[torch.Tensor, "batch tokens hidden"],
    Bool[torch.Tensor, "batch tokens"],
]:
    """Project caption embeddings."""
    if (train and self.uncond_prob > 0) or force_drop_ids is not None:
        caption, caption_padding_mask = self.token_drop(
            caption, caption_padding_mask, force_drop_ids
        )
    return self.proj(caption), caption_padding_mask

DiTBlock

Bases: Module

LayouSyn conditional DiT block with concept and caption attention.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
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
class DiTBlock(nn.Module):
    """LayouSyn conditional DiT block with concept and caption attention."""

    def __init__(
        self, hidden_size: int, num_heads: int, mlp_ratio: float = 4.0
    ) -> None:
        """Initialize one conditional block."""
        super().__init__()
        self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
        self.attn = nn.MultiheadAttention(
            hidden_size, num_heads=num_heads, batch_first=True
        )
        self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
        self.cross_attn = nn.MultiheadAttention(
            hidden_size, num_heads, dropout=0.1, batch_first=True
        )
        self.norm3 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
        self.norm4 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
        mlp_hidden_dim = int(hidden_size * mlp_ratio)
        self.mlp_x = Mlp(hidden_size, mlp_hidden_dim, hidden_size)
        self.mlp_xenc = Mlp(hidden_size, mlp_hidden_dim, hidden_size)
        self.adaLN_modulation = nn.Sequential(
            nn.SiLU(), nn.Linear(hidden_size, 9 * hidden_size)
        )

    def forward(
        self,
        x: Float[torch.Tensor, "batch elements hidden"],
        x_enc: Float[torch.Tensor, "batch elements hidden"],
        x_padding_mask: Bool[torch.Tensor, "batch elements"],
        c: Float[torch.Tensor, "batch hidden"],
        y: Float[torch.Tensor, "batch tokens hidden"],
        y_padding_mask: Bool[torch.Tensor, "batch tokens"],
        pos_embed: Float[torch.Tensor, "1 elements hidden"],
    ) -> tuple[
        Float[torch.Tensor, "batch elements hidden"],
        Float[torch.Tensor, "batch elements hidden"],
    ]:
        """Apply one conditional block."""
        (
            shift_msa,
            scale_msa,
            gate_msa,
            shift_mlp_x,
            scale_mlp_x,
            gate_mlp_x,
            shift_mlp_xenc,
            scale_mlp_xenc,
            gate_mlp_xenc,
        ) = self.adaLN_modulation(c).chunk(9, dim=1)
        modulate_sa = modulate(self.norm1(x), shift_msa, scale_msa)
        x = (
            x
            + gate_msa.unsqueeze(1)
            * self.attn(
                modulate_sa + pos_embed + x_enc,
                modulate_sa + pos_embed + x_enc,
                modulate_sa,
                key_padding_mask=x_padding_mask,
            )[0]
        )
        x_concat = torch.cat([x + pos_embed + x_enc, x_enc + pos_embed], dim=1)
        x_res, x_enc_res = self.cross_attn(
            x_concat, y, y, key_padding_mask=y_padding_mask
        )[0].chunk(2, dim=1)
        x = x + x_res
        x_enc = x_enc + x_enc_res
        x = x + gate_mlp_x.unsqueeze(1) * self.mlp_x(
            modulate(self.norm3(x), shift_mlp_x, scale_mlp_x)
        )
        x_enc = x_enc + gate_mlp_xenc.unsqueeze(1) * self.mlp_xenc(
            modulate(self.norm4(x_enc), shift_mlp_xenc, scale_mlp_xenc)
        )
        return x, x_enc

    def initialize_weights(self) -> None:
        """Zero reference adaLN and cross-attention output projections."""
        modulation = cast(nn.Linear, self.adaLN_modulation[-1])
        nn.init.constant_(self.cross_attn.out_proj.weight, 0)
        nn.init.constant_(self.cross_attn.out_proj.bias, 0)
        nn.init.constant_(modulation.weight, 0)
        nn.init.constant_(modulation.bias, 0)

__init__

__init__(
    hidden_size: int, num_heads: int, mlp_ratio: float = 4.0
) -> None

Initialize one conditional block.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
def __init__(
    self, hidden_size: int, num_heads: int, mlp_ratio: float = 4.0
) -> None:
    """Initialize one conditional block."""
    super().__init__()
    self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
    self.attn = nn.MultiheadAttention(
        hidden_size, num_heads=num_heads, batch_first=True
    )
    self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
    self.cross_attn = nn.MultiheadAttention(
        hidden_size, num_heads, dropout=0.1, batch_first=True
    )
    self.norm3 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
    self.norm4 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
    mlp_hidden_dim = int(hidden_size * mlp_ratio)
    self.mlp_x = Mlp(hidden_size, mlp_hidden_dim, hidden_size)
    self.mlp_xenc = Mlp(hidden_size, mlp_hidden_dim, hidden_size)
    self.adaLN_modulation = nn.Sequential(
        nn.SiLU(), nn.Linear(hidden_size, 9 * hidden_size)
    )

forward

forward(
    x: Float[Tensor, "batch elements hidden"],
    x_enc: Float[Tensor, "batch elements hidden"],
    x_padding_mask: Bool[Tensor, "batch elements"],
    c: Float[Tensor, "batch hidden"],
    y: Float[Tensor, "batch tokens hidden"],
    y_padding_mask: Bool[Tensor, "batch tokens"],
    pos_embed: Float[Tensor, "1 elements hidden"],
) -> tuple[
    Float[torch.Tensor, "batch elements hidden"],
    Float[torch.Tensor, "batch elements hidden"],
]

Apply one conditional block.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
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
def forward(
    self,
    x: Float[torch.Tensor, "batch elements hidden"],
    x_enc: Float[torch.Tensor, "batch elements hidden"],
    x_padding_mask: Bool[torch.Tensor, "batch elements"],
    c: Float[torch.Tensor, "batch hidden"],
    y: Float[torch.Tensor, "batch tokens hidden"],
    y_padding_mask: Bool[torch.Tensor, "batch tokens"],
    pos_embed: Float[torch.Tensor, "1 elements hidden"],
) -> tuple[
    Float[torch.Tensor, "batch elements hidden"],
    Float[torch.Tensor, "batch elements hidden"],
]:
    """Apply one conditional block."""
    (
        shift_msa,
        scale_msa,
        gate_msa,
        shift_mlp_x,
        scale_mlp_x,
        gate_mlp_x,
        shift_mlp_xenc,
        scale_mlp_xenc,
        gate_mlp_xenc,
    ) = self.adaLN_modulation(c).chunk(9, dim=1)
    modulate_sa = modulate(self.norm1(x), shift_msa, scale_msa)
    x = (
        x
        + gate_msa.unsqueeze(1)
        * self.attn(
            modulate_sa + pos_embed + x_enc,
            modulate_sa + pos_embed + x_enc,
            modulate_sa,
            key_padding_mask=x_padding_mask,
        )[0]
    )
    x_concat = torch.cat([x + pos_embed + x_enc, x_enc + pos_embed], dim=1)
    x_res, x_enc_res = self.cross_attn(
        x_concat, y, y, key_padding_mask=y_padding_mask
    )[0].chunk(2, dim=1)
    x = x + x_res
    x_enc = x_enc + x_enc_res
    x = x + gate_mlp_x.unsqueeze(1) * self.mlp_x(
        modulate(self.norm3(x), shift_mlp_x, scale_mlp_x)
    )
    x_enc = x_enc + gate_mlp_xenc.unsqueeze(1) * self.mlp_xenc(
        modulate(self.norm4(x_enc), shift_mlp_xenc, scale_mlp_xenc)
    )
    return x, x_enc

initialize_weights

initialize_weights() -> None

Zero reference adaLN and cross-attention output projections.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
285
286
287
288
289
290
291
def initialize_weights(self) -> None:
    """Zero reference adaLN and cross-attention output projections."""
    modulation = cast(nn.Linear, self.adaLN_modulation[-1])
    nn.init.constant_(self.cross_attn.out_proj.weight, 0)
    nn.init.constant_(self.cross_attn.out_proj.bias, 0)
    nn.init.constant_(modulation.weight, 0)
    nn.init.constant_(modulation.bias, 0)

DiTUCBlock

Bases: Module

LayouSyn unconditional DiT block.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
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
class DiTUCBlock(nn.Module):
    """LayouSyn unconditional DiT block."""

    def __init__(
        self, hidden_size: int, num_heads: int, mlp_ratio: float = 4.0
    ) -> None:
        """Initialize one unconditional block."""
        super().__init__()
        self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
        self.attn = nn.MultiheadAttention(
            hidden_size, num_heads=num_heads, batch_first=True
        )
        self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
        self.mlp = Mlp(hidden_size, int(hidden_size * mlp_ratio), hidden_size)
        self.adaLN_modulation = nn.Sequential(
            nn.SiLU(), nn.Linear(hidden_size, 6 * hidden_size)
        )

    def forward(
        self,
        x: Float[torch.Tensor, "batch elements hidden"],
        x_padding_mask: Bool[torch.Tensor, "batch elements"],
        c: Float[torch.Tensor, "batch hidden"],
        **kwargs: str | int | float | bool | None,
    ) -> Float[torch.Tensor, "batch elements hidden"]:
        """Apply one unconditional block."""
        del kwargs
        shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
            self.adaLN_modulation(c).chunk(6, dim=1)
        )
        modulate_sa = modulate(self.norm1(x), shift_msa, scale_msa)
        x = (
            x
            + gate_msa.unsqueeze(1)
            * self.attn(
                modulate_sa,
                modulate_sa,
                modulate_sa,
                key_padding_mask=x_padding_mask,
            )[0]
        )
        return x + gate_mlp.unsqueeze(1) * self.mlp(
            modulate(self.norm2(x), shift_mlp, scale_mlp)
        )

    def initialize_weights(self) -> None:
        """Zero reference adaLN projection."""
        modulation = cast(nn.Linear, self.adaLN_modulation[-1])
        nn.init.constant_(modulation.weight, 0)
        nn.init.constant_(modulation.bias, 0)

__init__

__init__(
    hidden_size: int, num_heads: int, mlp_ratio: float = 4.0
) -> None

Initialize one unconditional block.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
def __init__(
    self, hidden_size: int, num_heads: int, mlp_ratio: float = 4.0
) -> None:
    """Initialize one unconditional block."""
    super().__init__()
    self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
    self.attn = nn.MultiheadAttention(
        hidden_size, num_heads=num_heads, batch_first=True
    )
    self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
    self.mlp = Mlp(hidden_size, int(hidden_size * mlp_ratio), hidden_size)
    self.adaLN_modulation = nn.Sequential(
        nn.SiLU(), nn.Linear(hidden_size, 6 * hidden_size)
    )

forward

forward(
    x: Float[Tensor, "batch elements hidden"],
    x_padding_mask: Bool[Tensor, "batch elements"],
    c: Float[Tensor, "batch hidden"],
    **kwargs: str | int | float | bool | None,
) -> Float[torch.Tensor, "batch elements hidden"]

Apply one unconditional block.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
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
def forward(
    self,
    x: Float[torch.Tensor, "batch elements hidden"],
    x_padding_mask: Bool[torch.Tensor, "batch elements"],
    c: Float[torch.Tensor, "batch hidden"],
    **kwargs: str | int | float | bool | None,
) -> Float[torch.Tensor, "batch elements hidden"]:
    """Apply one unconditional block."""
    del kwargs
    shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
        self.adaLN_modulation(c).chunk(6, dim=1)
    )
    modulate_sa = modulate(self.norm1(x), shift_msa, scale_msa)
    x = (
        x
        + gate_msa.unsqueeze(1)
        * self.attn(
            modulate_sa,
            modulate_sa,
            modulate_sa,
            key_padding_mask=x_padding_mask,
        )[0]
    )
    return x + gate_mlp.unsqueeze(1) * self.mlp(
        modulate(self.norm2(x), shift_mlp, scale_mlp)
    )

initialize_weights

initialize_weights() -> None

Zero reference adaLN projection.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
339
340
341
342
343
def initialize_weights(self) -> None:
    """Zero reference adaLN projection."""
    modulation = cast(nn.Linear, self.adaLN_modulation[-1])
    nn.init.constant_(modulation.weight, 0)
    nn.init.constant_(modulation.bias, 0)

FinalLayer

Bases: Module

Reference final adaLN projection.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
class FinalLayer(nn.Module):
    """Reference final adaLN projection."""

    def __init__(self, hidden_size: int, out_channels: int) -> None:
        """Initialize final layer."""
        super().__init__()
        self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
        self.linear = nn.Linear(hidden_size, out_channels)
        self.adaLN_modulation = nn.Sequential(
            nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size)
        )

    def forward(
        self,
        x: Float[torch.Tensor, "batch elements hidden"],
        c: Float[torch.Tensor, "batch hidden"],
    ) -> Float[torch.Tensor, "batch elements channels"]:
        """Project hidden states to epsilon and variance channels."""
        shift, scale = self.adaLN_modulation(c).chunk(2, dim=1)
        return self.linear(modulate(self.norm_final(x), shift, scale))

__init__

__init__(hidden_size: int, out_channels: int) -> None

Initialize final layer.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
349
350
351
352
353
354
355
356
def __init__(self, hidden_size: int, out_channels: int) -> None:
    """Initialize final layer."""
    super().__init__()
    self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
    self.linear = nn.Linear(hidden_size, out_channels)
    self.adaLN_modulation = nn.Sequential(
        nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size)
    )

forward

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

Project hidden states to epsilon and variance channels.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
358
359
360
361
362
363
364
365
def forward(
    self,
    x: Float[torch.Tensor, "batch elements hidden"],
    c: Float[torch.Tensor, "batch hidden"],
) -> Float[torch.Tensor, "batch elements channels"]:
    """Project hidden states to epsilon and variance channels."""
    shift, scale = self.adaLN_modulation(c).chunk(2, dim=1)
    return self.linear(modulate(self.norm_final(x), shift, scale))

LayouSynDiTModel

Bases: ModelMixin, ConfigMixin

Converted LayouSyn DiT denoiser.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
class LayouSynDiTModel(ModelMixin, ConfigMixin):
    """Converted LayouSyn DiT denoiser."""

    config_name = "model_config.json"

    @register_to_config
    def __init__(
        self,
        *,
        in_channels: int = 4,
        max_in_len: int = 60,
        concept_in_channels: int = 768,
        y_in_channels: int | None = 768,
        max_y_len: int | None = 120,
        model_name: str = "DiT-S",
        hidden_size: int | None = None,
        depth: int | None = None,
        num_heads: int | None = None,
        mlp_ratio: float = 4.0,
        class_dropout_prob: float = 0.1,
        learn_sigma: bool = True,
        is_unconditional: bool = False,
    ) -> None:
        """Initialize the converted DiT model."""
        super().__init__()
        shape = resolve_model_shape(
            model_name,
            hidden_size=hidden_size,
            depth=depth,
            num_heads=num_heads,
        )
        self.in_channels = in_channels
        self.learn_sigma = learn_sigma
        self.num_heads = shape["num_heads"]
        self.max_len = max_in_len
        self.is_unconditional = is_unconditional
        self.x_embedder = InputEmbedder(in_channels, shape["hidden_size"])
        self.concept_embedder = ConceptEmbedder(
            concept_in_channels, shape["hidden_size"]
        )
        self.t_embedder = ScalarEmbedder(shape["hidden_size"])
        self.ar_embedder = ScalarEmbedder(shape["hidden_size"])
        if is_unconditional:
            self.y_embedder = CaptionEmbedderIdentity()
        else:
            if y_in_channels is None or max_y_len is None:
                raise ValueError("y_in_channels and max_y_len are required")

            y_null = torch.zeros(max_y_len, y_in_channels)
            y_mask = torch.ones(max_y_len, dtype=torch.bool)
            self.y_embedder = CaptionEmbedder(
                y_in_channels,
                shape["hidden_size"],
                class_dropout_prob,
                y_null,
                y_mask,
            )
        self.pos_embed = nn.Parameter(
            torch.zeros(1, max_in_len, shape["hidden_size"]), requires_grad=False
        )
        block_cls = DiTUCBlock if is_unconditional else DiTBlock
        self.blocks = nn.ModuleList(
            [
                block_cls(shape["hidden_size"], shape["num_heads"], mlp_ratio=mlp_ratio)
                for _ in range(shape["depth"])
            ]
        )
        self.final_layer = FinalLayer(shape["hidden_size"], 2 * in_channels)
        self.initialize_weights()

    def initialize_weights(self) -> None:
        """Initialize weights with the reference policy."""

        def _basic_init(module: nn.Module) -> None:
            if isinstance(module, nn.Linear):
                torch.nn.init.xavier_uniform_(module.weight)
                if module.bias is not None:
                    nn.init.constant_(module.bias, 0)

        self.apply(_basic_init)
        pos_embed = get_1d_sincos_pos_embed(self.pos_embed.shape[-1], self.max_len)
        self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0))
        nn.init.xavier_uniform_(self.x_embedder.proj.weight)
        nn.init.constant_(self.x_embedder.proj.bias, 0)

        if not self.is_unconditional and isinstance(self.y_embedder, CaptionEmbedder):
            nn.init.normal_(self.y_embedder.proj.fc1.weight, std=0.02)
            nn.init.normal_(self.y_embedder.proj.fc2.weight, std=0.02)

        nn.init.normal_(self.concept_embedder.proj.fc1.weight, std=0.02)
        nn.init.normal_(self.concept_embedder.proj.fc2.weight, std=0.02)
        nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
        nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
        nn.init.normal_(self.ar_embedder.mlp[0].weight, std=0.02)
        nn.init.normal_(self.ar_embedder.mlp[2].weight, std=0.02)

        for block in self.blocks:
            block.initialize_weights()

        nn.init.constant_(self.final_layer.adaLN_modulation[-1].weight, 0)
        nn.init.constant_(self.final_layer.adaLN_modulation[-1].bias, 0)
        nn.init.constant_(self.final_layer.linear.weight, 0)
        nn.init.constant_(self.final_layer.linear.bias, 0)

    def forward(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        *,
        x_padding_mask: Bool[torch.Tensor, "batch elements"],
        aspect_ratio: Float[torch.Tensor, "batch"],
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    ) -> Float[torch.Tensor, "batch seq channels"]:
        """Predict epsilon and variance channels for one timestep."""
        x = self.x_embedder(sample)
        x_enc = self.concept_embedder(concept_embeds)
        c = self.t_embedder(timestep) + self.ar_embedder(aspect_ratio)
        if self.is_unconditional:
            for block in self.blocks:
                x = block(x, x_padding_mask=x_padding_mask, c=c)
        else:
            if caption_embeds is None or caption_padding_mask is None:
                raise ValueError("caption_embeds and caption_padding_mask are required")

            y, y_padding_mask = self.y_embedder(
                caption_embeds, caption_padding_mask, self.training
            )
            for block in self.blocks:
                x, x_enc = block(
                    x,
                    x_enc,
                    x_padding_mask,
                    c,
                    y=y,
                    y_padding_mask=y_padding_mask,
                    pos_embed=self.pos_embed[:, : x.shape[1]],
                )
        out = self.final_layer(x, c).chunk(2, dim=-1)
        return torch.cat([out[0], out[1]], dim=1)

    def forward_with_cfg(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        *,
        x_padding_mask: Bool[torch.Tensor, "batch elements"],
        aspect_ratio: Float[torch.Tensor, "batch"],
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"],
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
        guidance_scale: float,
    ) -> Float[torch.Tensor, "batch seq channels"]:
        """Run reference classifier-free guidance batching."""
        half = sample[: len(sample) // 2]
        combined = torch.cat([half, half], dim=0)
        model_out = self.forward(
            combined,
            timestep,
            x_padding_mask=x_padding_mask,
            aspect_ratio=aspect_ratio,
            concept_embeds=concept_embeds,
            caption_embeds=caption_embeds,
            caption_padding_mask=caption_padding_mask,
        )
        eps, rest = model_out[:, : sample.shape[1]], model_out[:, sample.shape[1] :]
        cond_eps, uncond_eps = torch.split(eps, len(eps) // 2, dim=0)
        half_eps = uncond_eps + guidance_scale * (cond_eps - uncond_eps)
        eps = torch.cat([half_eps, half_eps], dim=0)
        return torch.cat([eps, rest], dim=1)

__init__

__init__(
    *,
    in_channels: int = 4,
    max_in_len: int = 60,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_y_len: int | None = 120,
    model_name: str = "DiT-S",
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    mlp_ratio: float = 4.0,
    class_dropout_prob: float = 0.1,
    learn_sigma: bool = True,
    is_unconditional: bool = False,
) -> None

Initialize the converted DiT model.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
@register_to_config
def __init__(
    self,
    *,
    in_channels: int = 4,
    max_in_len: int = 60,
    concept_in_channels: int = 768,
    y_in_channels: int | None = 768,
    max_y_len: int | None = 120,
    model_name: str = "DiT-S",
    hidden_size: int | None = None,
    depth: int | None = None,
    num_heads: int | None = None,
    mlp_ratio: float = 4.0,
    class_dropout_prob: float = 0.1,
    learn_sigma: bool = True,
    is_unconditional: bool = False,
) -> None:
    """Initialize the converted DiT model."""
    super().__init__()
    shape = resolve_model_shape(
        model_name,
        hidden_size=hidden_size,
        depth=depth,
        num_heads=num_heads,
    )
    self.in_channels = in_channels
    self.learn_sigma = learn_sigma
    self.num_heads = shape["num_heads"]
    self.max_len = max_in_len
    self.is_unconditional = is_unconditional
    self.x_embedder = InputEmbedder(in_channels, shape["hidden_size"])
    self.concept_embedder = ConceptEmbedder(
        concept_in_channels, shape["hidden_size"]
    )
    self.t_embedder = ScalarEmbedder(shape["hidden_size"])
    self.ar_embedder = ScalarEmbedder(shape["hidden_size"])
    if is_unconditional:
        self.y_embedder = CaptionEmbedderIdentity()
    else:
        if y_in_channels is None or max_y_len is None:
            raise ValueError("y_in_channels and max_y_len are required")

        y_null = torch.zeros(max_y_len, y_in_channels)
        y_mask = torch.ones(max_y_len, dtype=torch.bool)
        self.y_embedder = CaptionEmbedder(
            y_in_channels,
            shape["hidden_size"],
            class_dropout_prob,
            y_null,
            y_mask,
        )
    self.pos_embed = nn.Parameter(
        torch.zeros(1, max_in_len, shape["hidden_size"]), requires_grad=False
    )
    block_cls = DiTUCBlock if is_unconditional else DiTBlock
    self.blocks = nn.ModuleList(
        [
            block_cls(shape["hidden_size"], shape["num_heads"], mlp_ratio=mlp_ratio)
            for _ in range(shape["depth"])
        ]
    )
    self.final_layer = FinalLayer(shape["hidden_size"], 2 * in_channels)
    self.initialize_weights()

initialize_weights

initialize_weights() -> None

Initialize weights with the reference policy.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
def initialize_weights(self) -> None:
    """Initialize weights with the reference policy."""

    def _basic_init(module: nn.Module) -> None:
        if isinstance(module, nn.Linear):
            torch.nn.init.xavier_uniform_(module.weight)
            if module.bias is not None:
                nn.init.constant_(module.bias, 0)

    self.apply(_basic_init)
    pos_embed = get_1d_sincos_pos_embed(self.pos_embed.shape[-1], self.max_len)
    self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0))
    nn.init.xavier_uniform_(self.x_embedder.proj.weight)
    nn.init.constant_(self.x_embedder.proj.bias, 0)

    if not self.is_unconditional and isinstance(self.y_embedder, CaptionEmbedder):
        nn.init.normal_(self.y_embedder.proj.fc1.weight, std=0.02)
        nn.init.normal_(self.y_embedder.proj.fc2.weight, std=0.02)

    nn.init.normal_(self.concept_embedder.proj.fc1.weight, std=0.02)
    nn.init.normal_(self.concept_embedder.proj.fc2.weight, std=0.02)
    nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
    nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
    nn.init.normal_(self.ar_embedder.mlp[0].weight, std=0.02)
    nn.init.normal_(self.ar_embedder.mlp[2].weight, std=0.02)

    for block in self.blocks:
        block.initialize_weights()

    nn.init.constant_(self.final_layer.adaLN_modulation[-1].weight, 0)
    nn.init.constant_(self.final_layer.adaLN_modulation[-1].bias, 0)
    nn.init.constant_(self.final_layer.linear.weight, 0)
    nn.init.constant_(self.final_layer.linear.bias, 0)

forward

forward(
    sample: Float[Tensor, "batch elements channels"],
    timestep: Int[Tensor, "batch"],
    *,
    x_padding_mask: Bool[Tensor, "batch elements"],
    aspect_ratio: Float[Tensor, "batch"],
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ],
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ]
    | None = None,
    caption_padding_mask: Bool[Tensor, "batch tokens"]
    | None = None,
) -> Float[torch.Tensor, "batch seq channels"]

Predict epsilon and variance channels for one timestep.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def forward(
    self,
    sample: Float[torch.Tensor, "batch elements channels"],
    timestep: Int[torch.Tensor, "batch"],
    *,
    x_padding_mask: Bool[torch.Tensor, "batch elements"],
    aspect_ratio: Float[torch.Tensor, "batch"],
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
) -> Float[torch.Tensor, "batch seq channels"]:
    """Predict epsilon and variance channels for one timestep."""
    x = self.x_embedder(sample)
    x_enc = self.concept_embedder(concept_embeds)
    c = self.t_embedder(timestep) + self.ar_embedder(aspect_ratio)
    if self.is_unconditional:
        for block in self.blocks:
            x = block(x, x_padding_mask=x_padding_mask, c=c)
    else:
        if caption_embeds is None or caption_padding_mask is None:
            raise ValueError("caption_embeds and caption_padding_mask are required")

        y, y_padding_mask = self.y_embedder(
            caption_embeds, caption_padding_mask, self.training
        )
        for block in self.blocks:
            x, x_enc = block(
                x,
                x_enc,
                x_padding_mask,
                c,
                y=y,
                y_padding_mask=y_padding_mask,
                pos_embed=self.pos_embed[:, : x.shape[1]],
            )
    out = self.final_layer(x, c).chunk(2, dim=-1)
    return torch.cat([out[0], out[1]], dim=1)

forward_with_cfg

forward_with_cfg(
    sample: Float[Tensor, "batch elements channels"],
    timestep: Int[Tensor, "batch"],
    *,
    x_padding_mask: Bool[Tensor, "batch elements"],
    aspect_ratio: Float[Tensor, "batch"],
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ],
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ],
    caption_padding_mask: Bool[Tensor, "batch tokens"],
    guidance_scale: float,
) -> Float[torch.Tensor, "batch seq channels"]

Run reference classifier-free guidance batching.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
def forward_with_cfg(
    self,
    sample: Float[torch.Tensor, "batch elements channels"],
    timestep: Int[torch.Tensor, "batch"],
    *,
    x_padding_mask: Bool[torch.Tensor, "batch elements"],
    aspect_ratio: Float[torch.Tensor, "batch"],
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"],
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"],
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"],
    guidance_scale: float,
) -> Float[torch.Tensor, "batch seq channels"]:
    """Run reference classifier-free guidance batching."""
    half = sample[: len(sample) // 2]
    combined = torch.cat([half, half], dim=0)
    model_out = self.forward(
        combined,
        timestep,
        x_padding_mask=x_padding_mask,
        aspect_ratio=aspect_ratio,
        concept_embeds=concept_embeds,
        caption_embeds=caption_embeds,
        caption_padding_mask=caption_padding_mask,
    )
    eps, rest = model_out[:, : sample.shape[1]], model_out[:, sample.shape[1] :]
    cond_eps, uncond_eps = torch.split(eps, len(eps) // 2, dim=0)
    half_eps = uncond_eps + guidance_scale * (cond_eps - uncond_eps)
    eps = torch.cat([half_eps, half_eps], dim=0)
    return torch.cat([eps, rest], dim=1)

modulate

modulate(
    x: Float[Tensor, "batch tokens channels"],
    shift: Float[Tensor, "batch channels"],
    scale: Float[Tensor, "batch channels"],
) -> Float[torch.Tensor, "batch tokens channels"]

Apply adaLN shift and scale.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
18
19
20
21
22
23
24
def modulate(
    x: Float[torch.Tensor, "batch tokens channels"],
    shift: Float[torch.Tensor, "batch channels"],
    scale: Float[torch.Tensor, "batch channels"],
) -> Float[torch.Tensor, "batch tokens channels"]:
    """Apply adaLN shift and scale."""
    return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)

get_1d_sincos_pos_embed

get_1d_sincos_pos_embed(
    embed_dim: int, max_len: int
) -> Float[np.ndarray, "positions embed_dim"]

Create reference sine/cosine positional embeddings.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
368
369
370
371
372
373
def get_1d_sincos_pos_embed(
    embed_dim: int, max_len: int
) -> Float[np.ndarray, "positions embed_dim"]:
    """Create reference sine/cosine positional embeddings."""
    grid = np.arange(max_len, dtype=np.float32)
    return get_1d_sincos_pos_embed_from_grid(embed_dim, grid)

get_1d_sincos_pos_embed_from_grid

get_1d_sincos_pos_embed_from_grid(
    embed_dim: int, pos: Float[ndarray, "positions"]
) -> Float[np.ndarray, "positions embed_dim"]

Create reference sine/cosine positional embeddings from positions.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
376
377
378
379
380
381
382
383
384
def get_1d_sincos_pos_embed_from_grid(
    embed_dim: int, pos: Float[np.ndarray, "positions"]
) -> Float[np.ndarray, "positions embed_dim"]:
    """Create reference sine/cosine positional embeddings from positions."""
    omega = np.arange(embed_dim // 2, dtype=np.float64)
    omega /= embed_dim / 2.0
    omega = 1.0 / 10000**omega
    out = np.einsum("m,d->md", pos.reshape(-1), omega)
    return np.concatenate([np.sin(out), np.cos(out)], axis=1)

convert_reference_state_dict

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

Convert a reference DiT state dict to the wrapped model key space.

Source code in models/layousyn/src/layousyn/modeling_layousyn.py
560
561
562
563
564
def convert_reference_state_dict(
    state_dict: dict[str, Shaped[torch.Tensor, "..."]],
) -> dict[str, Shaped[torch.Tensor, "..."]]:
    """Convert a reference DiT state dict to the wrapped model key space."""
    return dict(state_dict)

pipeline_layousyn

Diffusers pipeline for converted LayouSyn text-to-layout generation.

LayouSynPipeline

Bases: DiffusionPipeline

Generate open-vocabulary scene layouts with LayouSyn.

Parameters:

Name Type Description Default
model LayouSynDiTModel

Converted DiT denoiser.

required
scheduler LayouSynScheduler

LayouSyn Gaussian/DDIM scheduler.

required
processor LayouSynProcessor

Processor for prompt/concept inputs and postprocessing.

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

    Args:
        model: Converted DiT denoiser.
        scheduler: LayouSyn Gaussian/DDIM scheduler.
        processor: Processor for prompt/concept inputs and postprocessing.
    """

    model_cpu_offload_seq = "model"
    _optional_components = ["processor"]

    def __init__(
        self,
        model: LayouSynDiTModel,
        scheduler: LayouSynScheduler,
        processor: LayouSynProcessor,
    ) -> None:
        """Initialize the pipeline."""
        super().__init__()
        self._register_layousyn_modules(model, scheduler, processor)
        self.model.eval()

    def _register_layousyn_modules(
        self,
        model: LayouSynDiTModel,
        scheduler: LayouSynScheduler,
        processor: LayouSynProcessor,
    ) -> None:
        """Register pipeline modules and keep concrete attributes typed."""
        self.register_modules(model=model, scheduler=scheduler)
        self.model = model
        self.scheduler = scheduler
        self.processor = processor

    @property
    def components(
        self,
    ) -> dict[str, LayouSynDiTModel | LayouSynScheduler | LayouSynProcessor]:
        """Return serializable pipeline components."""
        return {
            "model": self.model,
            "scheduler": self.scheduler,
            "processor": self.processor,
        }

    def save_pretrained(
        self,
        save_directory: str | os.PathLike[str],
        **kwargs: str | int | float | bool | None,
    ) -> None:
        """Save pipeline components plus processor metadata."""
        super().save_pretrained(save_directory, **kwargs)
        self.processor.save_pretrained(save_directory)

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        **kwargs: str | int | float | bool | None,
    ) -> LayouSynPipeline:
        """Load pipeline and restore local processor metadata."""
        pipe = super().from_pretrained(pretrained_model_name_or_path, **kwargs)
        if not isinstance(pipe, cls):
            raise TypeError(f"Expected {cls.__name__}, got {type(pipe).__name__}")

        processor_config = (
            Path(pretrained_model_name_or_path) / LayouSynProcessor.config_name
        )
        if pipe.processor is None and processor_config.exists():
            pipe.processor = LayouSynProcessor.from_pretrained(
                pretrained_model_name_or_path
            )
        return pipe

    @torch.no_grad()
    def __call__(
        self,
        *,
        prompt: str | list[str] | None = None,
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: ConditionType | str = ConditionType.text,
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | list[str]
        | list[list[str]]
        | None = None,
        id2label: dict[int, str] | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        aspect_ratio: float | list[float] | Float[torch.Tensor, "batch"] = 1.0,
        num_inference_steps: int | None = None,
        guidance_scale: float = 2.0,
        sampling_type: Literal["ddim", "ddpm"] = "ddim",
        output_type: Literal["dataclass", "dict"] = "dataclass",
        return_intermediates: bool = False,
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
        | None = None,
    ) -> LayoutGenerationOutput | LayouSynOutputDict:
        """Run LayouSyn denoising.

        Args:
            prompt: Caption text.
            batch_size: Number of generated layouts when labels are unbatched.
            seed: Convenience seed used only if ``generator`` is absent.
            generator: Exact reproducibility API.
            condition_type: Canonical condition name. First-class public mode is
                ``text``; unsupported modes fail explicitly.
            labels: String concepts or integer ids.
            id2label: Mapping for integer labels.
            bbox: Reserved for future initialization/refinement support.
            mask: Optional valid concept mask.
            num_elements: Optional expected element count. It is validated
                against labels when supplied.
            box_format: Public input bbox format.
            normalized: Whether input boxes are normalized.
            canvas_size: Required for pixel boxes.
            aspect_ratio: Scalar or per-example aspect ratio.
            num_inference_steps: Number of reverse diffusion steps.
            guidance_scale: Classifier-free guidance scale.
            sampling_type: ``ddim`` or ``ddpm``.
            output_type: ``dataclass`` or ``dict``.
            return_intermediates: Whether to return denoising trajectory.
            caption_embeds: Precomputed caption embeddings.
            caption_padding_mask: Precomputed caption padding mask.
            concept_embeds: Precomputed concept embeddings.

        Returns:
            Public layout output.
        """
        del batch_size
        canonical = normalize_condition_type(condition_type)
        if canonical is not ConditionType.text:
            raise NotImplementedError(
                f"LayouSyn public pipeline supports condition_type='text', got {condition_type}"
            )

        if generator is None and seed is not None:
            generator = torch.Generator(device=self.device).manual_seed(seed)
        encoded = self.processor(
            prompt=prompt,
            labels=labels,
            id2label=id2label,
            bbox=bbox,
            mask=mask,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
            aspect_ratio=aspect_ratio,
            caption_embeds=caption_embeds,
            caption_padding_mask=caption_padding_mask,
            concept_embeds=concept_embeds,
        )
        self._validate_num_elements(num_elements, encoded[LAYOUSYN_LABEL_TEXTS_KEY])
        concept_mask = encoded[LAYOUSYN_CONCEPT_MASK_KEY].to(self.device)
        batch = concept_mask.shape[0]
        self.scheduler.set_timesteps(num_inference_steps, device=self.device)
        sample = self.scheduler.initial_sample(
            batch,
            self.processor.max_in_len,
            self.model.config.in_channels,
            device=self.device,
            generator=generator,
        )
        model_kwargs = {
            "x_padding_mask": concept_mask,
            "aspect_ratio": encoded[LAYOUSYN_ASPECT_RATIO_KEY].to(self.device),
            "concept_embeds": encoded[LAYOUSYN_CONCEPT_EMBEDS_KEY].to(self.device),
            "caption_embeds": encoded[LAYOUSYN_CAPTION_EMBEDS_KEY].to(self.device),
            "caption_padding_mask": encoded[LAYOUSYN_CAPTION_MASK_KEY].to(self.device),
        }
        if guidance_scale != 1.0:
            sample = torch.cat([sample, sample], dim=0)
            model_kwargs = self._cfg_model_kwargs(model_kwargs, batch)
        trajectory = []
        for index, timestep in enumerate(self.scheduler.timesteps):
            model_timestep = self.scheduler.model_timesteps[index]
            t_model = torch.full(
                (sample.shape[0],),
                int(model_timestep),
                device=self.device,
                dtype=torch.long,
            )
            t_step = torch.full(
                (sample.shape[0],), int(timestep), device=self.device, dtype=torch.long
            )
            if guidance_scale != 1.0:
                model_output = self.model.forward_with_cfg(
                    sample, t_model, guidance_scale=guidance_scale, **model_kwargs
                )
            else:
                model_output = self.model(sample, t_model, **model_kwargs)
            step = self.scheduler.step(
                model_output,
                t_step,
                sample,
                generator=generator,
                sampling_type=sampling_type,
                clip_denoised=False,
            )
            sample = step.prev_sample
            if return_intermediates:
                trajectory.append(step.pred_original_sample[:batch].detach().cpu())
        sample = sample[:batch].clamp(-1.0, 1.0).detach().cpu()
        return self.processor.postprocess(
            sample,
            labels=encoded[LAYOUSYN_LABEL_TEXTS_KEY],
            id2label=encoded[LAYOUSYN_ID2LABEL_KEY],
            id2label_per_example=encoded[LAYOUSYN_PER_EXAMPLE_ID2LABEL_KEY],
            output_type=output_type,
            return_intermediates=return_intermediates,
            intermediates={"trajectory": trajectory} if return_intermediates else None,
        )

    generate = __call__

    def _cfg_model_kwargs(
        self, model_kwargs: dict[str, Shaped[torch.Tensor, ...]], batch_size: int
    ) -> dict[str, Shaped[torch.Tensor, ...]]:
        y_null = (
            self.model.y_embedder.y_embedding.to(self.device)
            .unsqueeze(0)
            .repeat(batch_size, 1, 1)
        )
        y_mask_null = (
            self.model.y_embedder.y_padding_mask.to(self.device)
            .unsqueeze(0)
            .repeat(batch_size, 1)
        )
        return {
            "x_padding_mask": torch.cat(
                [model_kwargs["x_padding_mask"], model_kwargs["x_padding_mask"]], dim=0
            ),
            "aspect_ratio": torch.cat(
                [model_kwargs["aspect_ratio"], model_kwargs["aspect_ratio"]], dim=0
            ),
            "concept_embeds": torch.cat(
                [model_kwargs["concept_embeds"], model_kwargs["concept_embeds"]], dim=0
            ),
            "caption_embeds": torch.cat(
                [model_kwargs["caption_embeds"], y_null], dim=0
            ),
            "caption_padding_mask": torch.cat(
                [model_kwargs["caption_padding_mask"], y_mask_null], dim=0
            ),
        }

    @staticmethod
    def _validate_num_elements(
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None,
        labels: list[list[str]],
    ) -> None:
        if num_elements is None:
            return
        if isinstance(num_elements, int):
            expected = [num_elements] * len(labels)
        elif isinstance(num_elements, torch.Tensor):
            expected = [int(item) for item in num_elements.tolist()]
        else:
            expected = [int(item) for item in num_elements]
        actual = [len(row) for row in labels]
        if expected != actual:
            raise ValueError(f"num_elements {expected} does not match labels {actual}")

components property

components: dict[
    str,
    LayouSynDiTModel
    | LayouSynScheduler
    | LayouSynProcessor,
]

Return serializable pipeline components.

__init__

__init__(
    model: LayouSynDiTModel,
    scheduler: LayouSynScheduler,
    processor: LayouSynProcessor,
) -> None

Initialize the pipeline.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.py
46
47
48
49
50
51
52
53
54
55
def __init__(
    self,
    model: LayouSynDiTModel,
    scheduler: LayouSynScheduler,
    processor: LayouSynProcessor,
) -> None:
    """Initialize the pipeline."""
    super().__init__()
    self._register_layousyn_modules(model, scheduler, processor)
    self.model.eval()

save_pretrained

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

Save pipeline components plus processor metadata.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.py
80
81
82
83
84
85
86
87
def save_pretrained(
    self,
    save_directory: str | os.PathLike[str],
    **kwargs: str | int | float | bool | None,
) -> None:
    """Save pipeline components plus processor metadata."""
    super().save_pretrained(save_directory, **kwargs)
    self.processor.save_pretrained(save_directory)

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    **kwargs: str | int | float | bool | None,
) -> LayouSynPipeline

Load pipeline and restore local processor metadata.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    **kwargs: str | int | float | bool | None,
) -> LayouSynPipeline:
    """Load pipeline and restore local processor metadata."""
    pipe = super().from_pretrained(pretrained_model_name_or_path, **kwargs)
    if not isinstance(pipe, cls):
        raise TypeError(f"Expected {cls.__name__}, got {type(pipe).__name__}")

    processor_config = (
        Path(pretrained_model_name_or_path) / LayouSynProcessor.config_name
    )
    if pipe.processor is None and processor_config.exists():
        pipe.processor = LayouSynProcessor.from_pretrained(
            pretrained_model_name_or_path
        )
    return pipe

__call__

__call__(
    *,
    prompt: str | list[str] | None = None,
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: ConditionType
    | str = ConditionType.text,
    labels: Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | list[str]
    | list[list[str]]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int
    | list[int]
    | Int[Tensor, "batch"]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float
    | list[float]
    | Float[Tensor, "batch"] = 1.0,
    num_inference_steps: int | None = None,
    guidance_scale: float = 2.0,
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ]
    | None = None,
    caption_padding_mask: Bool[Tensor, "batch tokens"]
    | None = None,
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ]
    | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict

Run LayouSyn denoising.

Parameters:

Name Type Description Default
prompt str | list[str] | None

Caption text.

None
batch_size int

Number of generated layouts when labels are unbatched.

1
seed int | None

Convenience seed used only if generator is absent.

None
generator Generator | None

Exact reproducibility API.

None
condition_type ConditionType | str

Canonical condition name. First-class public mode is text; unsupported modes fail explicitly.

text
labels Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | list[str] | list[list[str]] | None

String concepts or integer ids.

None
id2label dict[int, str] | None

Mapping for integer labels.

None
bbox Float[Tensor, 'batch elements 4'] | Float[ndarray, 'batch elements 4'] | Sequence[ArrayLikeInput] | None

Reserved for future initialization/refinement support.

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

Optional valid concept mask.

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

Optional expected element count. It is validated against labels when supplied.

None
box_format BoxFormat | str

Public input bbox format.

xywh
normalized bool

Whether input boxes are normalized.

True
canvas_size tuple[int, int] | None

Required for pixel boxes.

None
aspect_ratio float | list[float] | Float[Tensor, 'batch']

Scalar or per-example aspect ratio.

1.0
num_inference_steps int | None

Number of reverse diffusion steps.

None
guidance_scale float

Classifier-free guidance scale.

2.0
sampling_type Literal['ddim', 'ddpm']

ddim or ddpm.

'ddim'
output_type Literal['dataclass', 'dict']

dataclass or dict.

'dataclass'
return_intermediates bool

Whether to return denoising trajectory.

False
caption_embeds Float[Tensor, 'batch tokens embedding_dim'] | None

Precomputed caption embeddings.

None
caption_padding_mask Bool[Tensor, 'batch tokens'] | None

Precomputed caption padding mask.

None
concept_embeds Float[Tensor, 'batch elements embedding_dim'] | None

Precomputed concept embeddings.

None

Returns:

Type Description
LayoutGenerationOutput | LayouSynOutputDict

Public layout output.

Source code in models/layousyn/src/layousyn/pipeline_layousyn.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
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
@torch.no_grad()
def __call__(
    self,
    *,
    prompt: str | list[str] | None = None,
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: ConditionType | str = ConditionType.text,
    labels: Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | list[str]
    | list[list[str]]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float | list[float] | Float[torch.Tensor, "batch"] = 1.0,
    num_inference_steps: int | None = None,
    guidance_scale: float = 2.0,
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
    | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict:
    """Run LayouSyn denoising.

    Args:
        prompt: Caption text.
        batch_size: Number of generated layouts when labels are unbatched.
        seed: Convenience seed used only if ``generator`` is absent.
        generator: Exact reproducibility API.
        condition_type: Canonical condition name. First-class public mode is
            ``text``; unsupported modes fail explicitly.
        labels: String concepts or integer ids.
        id2label: Mapping for integer labels.
        bbox: Reserved for future initialization/refinement support.
        mask: Optional valid concept mask.
        num_elements: Optional expected element count. It is validated
            against labels when supplied.
        box_format: Public input bbox format.
        normalized: Whether input boxes are normalized.
        canvas_size: Required for pixel boxes.
        aspect_ratio: Scalar or per-example aspect ratio.
        num_inference_steps: Number of reverse diffusion steps.
        guidance_scale: Classifier-free guidance scale.
        sampling_type: ``ddim`` or ``ddpm``.
        output_type: ``dataclass`` or ``dict``.
        return_intermediates: Whether to return denoising trajectory.
        caption_embeds: Precomputed caption embeddings.
        caption_padding_mask: Precomputed caption padding mask.
        concept_embeds: Precomputed concept embeddings.

    Returns:
        Public layout output.
    """
    del batch_size
    canonical = normalize_condition_type(condition_type)
    if canonical is not ConditionType.text:
        raise NotImplementedError(
            f"LayouSyn public pipeline supports condition_type='text', got {condition_type}"
        )

    if generator is None and seed is not None:
        generator = torch.Generator(device=self.device).manual_seed(seed)
    encoded = self.processor(
        prompt=prompt,
        labels=labels,
        id2label=id2label,
        bbox=bbox,
        mask=mask,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
        aspect_ratio=aspect_ratio,
        caption_embeds=caption_embeds,
        caption_padding_mask=caption_padding_mask,
        concept_embeds=concept_embeds,
    )
    self._validate_num_elements(num_elements, encoded[LAYOUSYN_LABEL_TEXTS_KEY])
    concept_mask = encoded[LAYOUSYN_CONCEPT_MASK_KEY].to(self.device)
    batch = concept_mask.shape[0]
    self.scheduler.set_timesteps(num_inference_steps, device=self.device)
    sample = self.scheduler.initial_sample(
        batch,
        self.processor.max_in_len,
        self.model.config.in_channels,
        device=self.device,
        generator=generator,
    )
    model_kwargs = {
        "x_padding_mask": concept_mask,
        "aspect_ratio": encoded[LAYOUSYN_ASPECT_RATIO_KEY].to(self.device),
        "concept_embeds": encoded[LAYOUSYN_CONCEPT_EMBEDS_KEY].to(self.device),
        "caption_embeds": encoded[LAYOUSYN_CAPTION_EMBEDS_KEY].to(self.device),
        "caption_padding_mask": encoded[LAYOUSYN_CAPTION_MASK_KEY].to(self.device),
    }
    if guidance_scale != 1.0:
        sample = torch.cat([sample, sample], dim=0)
        model_kwargs = self._cfg_model_kwargs(model_kwargs, batch)
    trajectory = []
    for index, timestep in enumerate(self.scheduler.timesteps):
        model_timestep = self.scheduler.model_timesteps[index]
        t_model = torch.full(
            (sample.shape[0],),
            int(model_timestep),
            device=self.device,
            dtype=torch.long,
        )
        t_step = torch.full(
            (sample.shape[0],), int(timestep), device=self.device, dtype=torch.long
        )
        if guidance_scale != 1.0:
            model_output = self.model.forward_with_cfg(
                sample, t_model, guidance_scale=guidance_scale, **model_kwargs
            )
        else:
            model_output = self.model(sample, t_model, **model_kwargs)
        step = self.scheduler.step(
            model_output,
            t_step,
            sample,
            generator=generator,
            sampling_type=sampling_type,
            clip_denoised=False,
        )
        sample = step.prev_sample
        if return_intermediates:
            trajectory.append(step.pred_original_sample[:batch].detach().cpu())
    sample = sample[:batch].clamp(-1.0, 1.0).detach().cpu()
    return self.processor.postprocess(
        sample,
        labels=encoded[LAYOUSYN_LABEL_TEXTS_KEY],
        id2label=encoded[LAYOUSYN_ID2LABEL_KEY],
        id2label_per_example=encoded[LAYOUSYN_PER_EXAMPLE_ID2LABEL_KEY],
        output_type=output_type,
        return_intermediates=return_intermediates,
        intermediates={"trajectory": trajectory} if return_intermediates else None,
    )

processing_layousyn

Processor for LayouSyn text/concept-conditioned layout tensors.

LayouSynBatch

Bases: TypedDict

Encoded LayouSyn processor batch.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
36
37
38
39
40
41
42
43
44
45
46
class LayouSynBatch(TypedDict):
    """Encoded LayouSyn processor batch."""

    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
    concept_padding_mask: Bool[torch.Tensor, "batch elements"]
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"]
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"]
    aspect_ratio: Float[torch.Tensor, "batch"]
    label_texts: list[list[str]]
    id2label: dict[int, str]
    id2label_per_example: list[dict[int, str]]

LayouSynIntermediateValue

Bases: TypedDict

Optional auxiliary payload passed into post-processing.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
49
50
51
52
53
class LayouSynIntermediateValue(TypedDict, total=False):
    """Optional auxiliary payload passed into post-processing."""

    trajectory: list[Float[torch.Tensor, "batch elements 4"]]
    raw: bool

LayouSynOutputIntermediates

Bases: TypedDict

LayouSyn auxiliary output metadata.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
56
57
58
59
60
61
62
class LayouSynOutputIntermediates(TypedDict):
    """LayouSyn auxiliary output metadata."""

    label_texts: list[list[str]]
    id2label_per_example: list[dict[int, str]] | None
    reference_layout_type: str
    intermediates: LayouSynIntermediateValue | None

LayouSynOutputDict

Bases: TypedDict

Dictionary form of LayouSyn public output.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
65
66
67
68
69
70
71
72
class LayouSynOutputDict(TypedDict, total=False):
    """Dictionary form of LayouSyn 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: LayouSynOutputIntermediates | None

LayouSynProcessor

Bases: ProcessorMixin

Encode prompts and open-vocabulary concepts for LayouSyn.

Parameters:

Name Type Description Default
layout_type Literal['xyxy', 'cxcywh']

Reference layout type used by generated coordinates.

'xyxy'
max_in_len int

Maximum number of concept slots.

60
caption_model_name str

Text encoder identifier used for captions.

't5-v1_1-base'
concept_model_name str

Sentence-transformers model id for concept labels.

'sentence-transformers/sentence-t5-base'
id2label dict[int, str] | None

Optional fixed vocabulary for integer labels.

None
open_vocabulary bool

Whether string labels are accepted per request.

True
Source code in models/layousyn/src/layousyn/processing_layousyn.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
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
class LayouSynProcessor(ProcessorMixin):
    """Encode prompts and open-vocabulary concepts for LayouSyn.

    Args:
        layout_type: Reference layout type used by generated coordinates.
        max_in_len: Maximum number of concept slots.
        caption_model_name: Text encoder identifier used for captions.
        concept_model_name: Sentence-transformers model id for concept labels.
        id2label: Optional fixed vocabulary for integer labels.
        open_vocabulary: Whether string labels are accepted per request.
    """

    config_name = "processor_config.json"

    def __init__(
        self,
        *,
        layout_type: Literal["xyxy", "cxcywh"] = "xyxy",
        max_in_len: int = 60,
        max_y_len: int = 120,
        concept_in_channels: int = 768,
        y_in_channels: int = 768,
        caption_model_name: str = "t5-v1_1-base",
        concept_model_name: str = "sentence-transformers/sentence-t5-base",
        id2label: dict[int, str] | None = None,
        open_vocabulary: bool = True,
    ) -> None:
        """Initialize processor metadata."""
        super().__init__()
        self.layout_type = layout_type
        self.max_in_len = max_in_len
        self.max_y_len = max_y_len
        self.concept_in_channels = concept_in_channels
        self.y_in_channels = y_in_channels
        self.caption_model_name = caption_model_name
        self.concept_model_name = concept_model_name
        self.id2label = id2label
        self.open_vocabulary = open_vocabulary

    def to_dict(self) -> dict[str, str | int | bool | dict[int, str] | None]:
        """Serialize processor metadata."""
        return {
            "layout_type": self.layout_type,
            "max_in_len": self.max_in_len,
            "max_y_len": self.max_y_len,
            "concept_in_channels": self.concept_in_channels,
            "y_in_channels": self.y_in_channels,
            "caption_model_name": self.caption_model_name,
            "concept_model_name": self.concept_model_name,
            "id2label": self.id2label,
            "open_vocabulary": self.open_vocabulary,
            "license": "cc-by-nc-4.0",
        }

    def save_pretrained(
        self,
        save_directory: str | Path,
        push_to_hub: bool = False,
        **kwargs: str | int | float | bool | None,
    ) -> tuple[str]:
        """Save processor metadata."""
        del kwargs
        if push_to_hub:
            raise ValueError("LayouSynProcessor does not push to Hub directly")

        path = Path(save_directory)
        path.mkdir(parents=True, exist_ok=True)
        out = path / self.config_name
        out.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True))
        return (str(out),)

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        cache_dir: str | os.PathLike[str] | None = None,
        force_download: bool = False,
        local_files_only: bool = False,
        token: str | bool | None = None,
        revision: str = "main",
        **kwargs: str | int | float | bool | None,
    ) -> LayouSynProcessor:
        """Load processor metadata from a local directory."""
        del cache_dir, force_download, local_files_only, token, revision
        path = Path(pretrained_model_name_or_path) / cls.config_name
        data = json.loads(path.read_text())
        data.update(kwargs)
        data.pop("license", None)
        if data.get("id2label") is not None:
            data["id2label"] = {int(k): str(v) for k, v in data["id2label"].items()}
        return cls(**data)

    def __call__(
        self,
        *,
        prompt: str | Sequence[str] | None = None,
        labels: Sequence[str]
        | Sequence[Sequence[str]]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | None = None,
        id2label: dict[int, str] | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        box_format: BoxFormat | str = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        aspect_ratio: float | Sequence[float] | Float[torch.Tensor, "batch"] = 1.0,
        caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
        caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
        concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
        | None = None,
    ) -> LayouSynBatch:
        """Encode public text and concept inputs.

        Args:
            prompt: Caption text or batch of captions.
            labels: String concepts or integer labels.
            id2label: Mapping required for integer labels when no fixed
                processor mapping exists.
            bbox: Optional conditioning boxes for future init/refinement paths.
            mask: Optional valid-element mask.
            box_format: Public bbox format.
            normalized: Whether bbox coordinates are normalized.
            canvas_size: Required when ``normalized=False``.
            aspect_ratio: Scalar or per-example aspect ratio.
            caption_embeds: Precomputed caption embeddings.
            caption_padding_mask: Precomputed caption padding mask.
            concept_embeds: Precomputed concept embeddings.

        Returns:
            Encoded processor batch.

        Raises:
            ValueError: If required labels or embeddings are missing.
        """
        prompts = self._normalize_prompts(prompt)
        label_texts, union_id2label, per_example = self._normalize_labels(
            labels, id2label=id2label, batch_size=len(prompts)
        )
        batch_size = len(label_texts)
        if len(prompts) == 1 and batch_size > 1:
            prompts = prompts * batch_size
        if len(prompts) != batch_size:
            raise ValueError("prompt and labels batch sizes must match")

        concept_padding_mask = self._concept_padding_mask(label_texts, mask=mask)
        if concept_embeds is None:
            concept_embeds = self._encode_concepts(label_texts)
        concept_embeds = self._pad_concept_embeds(concept_embeds, batch_size)
        if caption_embeds is None or caption_padding_mask is None:
            caption_embeds, caption_padding_mask = self._encode_captions(prompts)
        caption_embeds = caption_embeds.float()
        caption_padding_mask = caption_padding_mask.bool()
        if bbox is not None:
            self._normalize_optional_bbox(
                bbox,
                box_format=box_format,
                normalized=normalized,
                canvas_size=canvas_size,
            )
        return LayouSynBatch(
            concept_embeds=concept_embeds.float(),
            concept_padding_mask=concept_padding_mask,
            caption_embeds=caption_embeds,
            caption_padding_mask=caption_padding_mask,
            aspect_ratio=self._aspect_ratio_tensor(aspect_ratio, batch_size),
            label_texts=label_texts,
            id2label=union_id2label,
            id2label_per_example=per_example,
        )

    def postprocess(
        self,
        sample: Float[torch.Tensor, "batch elements 4"],
        *,
        labels: list[list[str]],
        id2label: dict[int, str],
        id2label_per_example: list[dict[int, str]] | None = None,
        output_type: Literal["dataclass", "dict"] = "dataclass",
        return_intermediates: bool = False,
        intermediates: LayouSynIntermediateValue | None = None,
    ) -> LayoutGenerationOutput | LayouSynOutputDict:
        """Convert generated reference coordinates into the public schema."""
        sample = ((sample.clamp(-1, 1) + 1.0) / 2.0).float()
        if self.layout_type == "xyxy":
            left, top, right, bottom = sample.unbind(dim=-1)
            fixed = torch.stack(
                (
                    torch.minimum(left, right),
                    torch.minimum(top, bottom),
                    torch.maximum(left, right),
                    torch.maximum(top, bottom),
                ),
                dim=-1,
            )
            bbox = clamp_boxes(ltrb_to_xywh(fixed))
        else:
            bbox = clamp_boxes(sample)
        batch_size = sample.shape[0]
        label_ids = torch.zeros(batch_size, self.max_in_len, dtype=torch.long)
        mask = torch.zeros(batch_size, self.max_in_len, dtype=torch.bool)
        label2id = {text: idx for idx, text in id2label.items()}
        for batch_idx, batch_labels in enumerate(labels):
            for pos, text in enumerate(batch_labels[: self.max_in_len]):
                label_ids[batch_idx, pos] = label2id[text]
                mask[batch_idx, pos] = True
        payload = intermediates if return_intermediates else None
        if return_intermediates:
            payload = {
                "label_texts": labels,
                "id2label_per_example": id2label_per_example,
                "reference_layout_type": self.layout_type,
                "intermediates": intermediates,
            }
        output = LayoutGenerationOutput(
            bbox=bbox,
            labels=label_ids,
            mask=mask,
            id2label=id2label,
            intermediates=payload,
        )
        if output_type == "dict":
            return cast(LayouSynOutputDict, dict(output))
        if output_type != "dataclass":
            raise ValueError(f"Unsupported output_type: {output_type}")

        return output

    def _normalize_prompts(self, prompt: str | Sequence[str] | None) -> list[str]:
        if prompt is None:
            return [""]
        if isinstance(prompt, str):
            return [prompt]
        return [str(item) for item in prompt]

    def _normalize_labels(
        self,
        labels: Sequence[str]
        | Sequence[Sequence[str]]
        | Int[torch.Tensor, ...]
        | Int[np.ndarray, ...]
        | None,
        *,
        id2label: dict[int, str] | None,
        batch_size: int,
    ) -> tuple[list[list[str]], dict[int, str], list[dict[int, str]]]:
        if labels is None:
            raise ValueError("LayouSyn requires labels/concepts for object slots")

        mapping = id2label or self.id2label
        if isinstance(labels, torch.Tensor | np.ndarray):
            if mapping is None:
                raise ValueError("id2label is required when labels are integer ids")

            labels_t = torch.as_tensor(labels, dtype=torch.long)
            if labels_t.ndim == 1:
                labels_t = labels_t.unsqueeze(0)
            label_texts = [
                [mapping[int(idx)] for idx in row.tolist() if int(idx) in mapping]
                for row in labels_t
            ]
        else:
            label_texts = self._string_label_batches(labels, batch_size=batch_size)
        union: dict[str, int] = {}
        per_example: list[dict[int, str]] = []
        for batch_labels in label_texts:
            local: dict[int, str] = {}
            for text in batch_labels:
                if text not in union:
                    union[text] = len(union)
                if text not in local.values():
                    local[len(local)] = text
            per_example.append(local)
        return label_texts, {idx: text for text, idx in union.items()}, per_example

    def _string_label_batches(
        self,
        labels: Sequence[str] | Sequence[Sequence[str]],
        *,
        batch_size: int,
    ) -> list[list[str]]:
        if len(labels) == 0:
            return [[]]
        first = labels[0]
        if isinstance(first, str):
            return [[str(item) for item in labels]]
        return [[str(item) for item in row] for row in labels]

    def _concept_padding_mask(
        self,
        labels: list[list[str]],
        *,
        mask: Bool[torch.Tensor, ...]
        | Bool[np.ndarray, ...]
        | Sequence[ArrayLikeInput]
        | None,
    ) -> Bool[torch.Tensor, "batch elements"]:
        if mask is not None:
            mask_t = torch.as_tensor(mask, dtype=torch.bool)
            if mask_t.ndim == 1:
                mask_t = mask_t.unsqueeze(0)
            valid = torch.zeros(mask_t.shape[0], self.max_in_len, dtype=torch.bool)
            valid[:, : min(mask_t.shape[1], self.max_in_len)] = mask_t[
                :, : self.max_in_len
            ]
            return ~valid
        padding = torch.ones(len(labels), self.max_in_len, dtype=torch.bool)
        for batch_idx, batch_labels in enumerate(labels):
            padding[batch_idx, : min(len(batch_labels), self.max_in_len)] = False
        return padding

    def _pad_concept_embeds(
        self,
        embeds: Float[torch.Tensor, "batch elements embedding_dim"],
        batch_size: int,
    ) -> Float[torch.Tensor, "batch elements embedding_dim"]:
        if embeds.ndim != 3:
            raise ValueError("concept_embeds must have shape (batch, seq, dim)")

        if embeds.shape[0] != batch_size:
            raise ValueError("concept_embeds batch size must match labels")

        if embeds.shape[1] > self.max_in_len:
            return embeds[:, : self.max_in_len]
        if embeds.shape[1] == self.max_in_len:
            return embeds
        pad = torch.zeros(
            batch_size,
            self.max_in_len - embeds.shape[1],
            embeds.shape[2],
            dtype=embeds.dtype,
            device=embeds.device,
        )
        return torch.cat((embeds, pad), dim=1)

    def _encode_concepts(
        self, labels: list[list[str]]
    ) -> Float[torch.Tensor, "batch elements embedding_dim"]:
        try:
            from sentence_transformers import SentenceTransformer
        except ImportError as exc:
            raise ValueError(
                "concept_embeds are required without sentence-transformers"
            ) from exc

        flat = [text for row in labels for text in row]
        if not flat:
            return torch.zeros(len(labels), 0, self.concept_in_channels)
        encoder = SentenceTransformer(self.concept_model_name)
        encoded = torch.as_tensor(encoder.encode(flat), dtype=torch.float32)
        rows = []
        offset = 0
        for row in labels:
            rows.append(encoded[offset : offset + len(row)])
            offset += len(row)
        return torch.nested.as_nested_tensor(rows).to_padded_tensor(0.0)

    def _encode_captions(
        self, prompts: list[str]
    ) -> tuple[
        Float[torch.Tensor, "batch tokens embedding_dim"],
        Bool[torch.Tensor, "batch tokens"],
    ]:
        if any(prompts):
            raise ValueError(
                "caption_embeds are required for prompt-conditioned tests/offline use"
            )

        return (
            torch.zeros(len(prompts), self.max_y_len, self.y_in_channels),
            torch.ones(len(prompts), self.max_y_len, dtype=torch.bool),
        )

    def _aspect_ratio_tensor(
        self,
        aspect_ratio: float | Sequence[float] | Float[torch.Tensor, "batch"],
        batch_size: int,
    ) -> Float[torch.Tensor, "batch"]:
        if isinstance(aspect_ratio, torch.Tensor):
            out = aspect_ratio.float()
        elif isinstance(aspect_ratio, float | int):
            out = torch.full((batch_size,), float(aspect_ratio))
        else:
            out = torch.tensor([float(item) for item in aspect_ratio])
        if out.numel() == 1 and batch_size > 1:
            out = out.repeat(batch_size)
        if out.shape != (batch_size,):
            raise ValueError("aspect_ratio must be scalar or match batch size")

        return out

    def _normalize_optional_bbox(
        self,
        bbox: Float[torch.Tensor, "... 4"]
        | Float[np.ndarray, "... 4"]
        | Sequence[ArrayLikeInput],
        *,
        box_format: BoxFormat | str,
        normalized: bool,
        canvas_size: tuple[int, int] | None,
    ) -> Float[torch.Tensor, "... 4"]:
        bbox_t = torch.as_tensor(bbox, dtype=torch.float32)
        if not normalized:
            if canvas_size is None:
                raise ValueError("canvas_size is required when normalized=False")

            bbox_t = normalize_boxes(
                bbox_t, canvas_size=canvas_size, box_format=box_format
            )
        elif normalize_box_format(box_format) is BoxFormat.ltrb:
            bbox_t = ltrb_to_xywh(bbox_t)
        if self.layout_type == "xyxy":
            return xywh_to_ltrb(bbox_t) * 2 - 1
        return bbox_t * 2 - 1

__init__

__init__(
    *,
    layout_type: Literal["xyxy", "cxcywh"] = "xyxy",
    max_in_len: int = 60,
    max_y_len: int = 120,
    concept_in_channels: int = 768,
    y_in_channels: int = 768,
    caption_model_name: str = "t5-v1_1-base",
    concept_model_name: str = "sentence-transformers/sentence-t5-base",
    id2label: dict[int, str] | None = None,
    open_vocabulary: bool = True,
) -> None

Initialize processor metadata.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def __init__(
    self,
    *,
    layout_type: Literal["xyxy", "cxcywh"] = "xyxy",
    max_in_len: int = 60,
    max_y_len: int = 120,
    concept_in_channels: int = 768,
    y_in_channels: int = 768,
    caption_model_name: str = "t5-v1_1-base",
    concept_model_name: str = "sentence-transformers/sentence-t5-base",
    id2label: dict[int, str] | None = None,
    open_vocabulary: bool = True,
) -> None:
    """Initialize processor metadata."""
    super().__init__()
    self.layout_type = layout_type
    self.max_in_len = max_in_len
    self.max_y_len = max_y_len
    self.concept_in_channels = concept_in_channels
    self.y_in_channels = y_in_channels
    self.caption_model_name = caption_model_name
    self.concept_model_name = concept_model_name
    self.id2label = id2label
    self.open_vocabulary = open_vocabulary

to_dict

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

Serialize processor metadata.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def to_dict(self) -> dict[str, str | int | bool | dict[int, str] | None]:
    """Serialize processor metadata."""
    return {
        "layout_type": self.layout_type,
        "max_in_len": self.max_in_len,
        "max_y_len": self.max_y_len,
        "concept_in_channels": self.concept_in_channels,
        "y_in_channels": self.y_in_channels,
        "caption_model_name": self.caption_model_name,
        "concept_model_name": self.concept_model_name,
        "id2label": self.id2label,
        "open_vocabulary": self.open_vocabulary,
        "license": "cc-by-nc-4.0",
    }

save_pretrained

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

Save processor metadata.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def save_pretrained(
    self,
    save_directory: str | Path,
    push_to_hub: bool = False,
    **kwargs: str | int | float | bool | None,
) -> tuple[str]:
    """Save processor metadata."""
    del kwargs
    if push_to_hub:
        raise ValueError("LayouSynProcessor does not push to Hub directly")

    path = Path(save_directory)
    path.mkdir(parents=True, exist_ok=True)
    out = path / self.config_name
    out.write_text(json.dumps(self.to_dict(), indent=2, sort_keys=True))
    return (str(out),)

from_pretrained classmethod

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

Load processor metadata from a local directory.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    cache_dir: str | os.PathLike[str] | None = None,
    force_download: bool = False,
    local_files_only: bool = False,
    token: str | bool | None = None,
    revision: str = "main",
    **kwargs: str | int | float | bool | None,
) -> LayouSynProcessor:
    """Load processor metadata from a local directory."""
    del cache_dir, force_download, local_files_only, token, revision
    path = Path(pretrained_model_name_or_path) / cls.config_name
    data = json.loads(path.read_text())
    data.update(kwargs)
    data.pop("license", None)
    if data.get("id2label") is not None:
        data["id2label"] = {int(k): str(v) for k, v in data["id2label"].items()}
    return cls(**data)

__call__

__call__(
    *,
    prompt: str | Sequence[str] | None = None,
    labels: Sequence[str]
    | Sequence[Sequence[str]]
    | Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float
    | Sequence[float]
    | Float[Tensor, "batch"] = 1.0,
    caption_embeds: Float[
        Tensor, "batch tokens embedding_dim"
    ]
    | None = None,
    caption_padding_mask: Bool[Tensor, "batch tokens"]
    | None = None,
    concept_embeds: Float[
        Tensor, "batch elements embedding_dim"
    ]
    | None = None,
) -> LayouSynBatch

Encode public text and concept inputs.

Parameters:

Name Type Description Default
prompt str | Sequence[str] | None

Caption text or batch of captions.

None
labels Sequence[str] | Sequence[Sequence[str]] | Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | None

String concepts or integer labels.

None
id2label dict[int, str] | None

Mapping required for integer labels when no fixed processor mapping exists.

None
bbox Float[Tensor, 'batch elements 4'] | Float[ndarray, 'batch elements 4'] | Sequence[ArrayLikeInput] | None

Optional conditioning boxes for future init/refinement paths.

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

Optional valid-element mask.

None
box_format BoxFormat | str

Public bbox format.

xywh
normalized bool

Whether bbox coordinates are normalized.

True
canvas_size tuple[int, int] | None

Required when normalized=False.

None
aspect_ratio float | Sequence[float] | Float[Tensor, 'batch']

Scalar or per-example aspect ratio.

1.0
caption_embeds Float[Tensor, 'batch tokens embedding_dim'] | None

Precomputed caption embeddings.

None
caption_padding_mask Bool[Tensor, 'batch tokens'] | None

Precomputed caption padding mask.

None
concept_embeds Float[Tensor, 'batch elements embedding_dim'] | None

Precomputed concept embeddings.

None

Returns:

Type Description
LayouSynBatch

Encoded processor batch.

Raises:

Type Description
ValueError

If required labels or embeddings are missing.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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
def __call__(
    self,
    *,
    prompt: str | Sequence[str] | None = None,
    labels: Sequence[str]
    | Sequence[Sequence[str]]
    | Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | None = None,
    id2label: dict[int, str] | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    box_format: BoxFormat | str = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    aspect_ratio: float | Sequence[float] | Float[torch.Tensor, "batch"] = 1.0,
    caption_embeds: Float[torch.Tensor, "batch tokens embedding_dim"] | None = None,
    caption_padding_mask: Bool[torch.Tensor, "batch tokens"] | None = None,
    concept_embeds: Float[torch.Tensor, "batch elements embedding_dim"]
    | None = None,
) -> LayouSynBatch:
    """Encode public text and concept inputs.

    Args:
        prompt: Caption text or batch of captions.
        labels: String concepts or integer labels.
        id2label: Mapping required for integer labels when no fixed
            processor mapping exists.
        bbox: Optional conditioning boxes for future init/refinement paths.
        mask: Optional valid-element mask.
        box_format: Public bbox format.
        normalized: Whether bbox coordinates are normalized.
        canvas_size: Required when ``normalized=False``.
        aspect_ratio: Scalar or per-example aspect ratio.
        caption_embeds: Precomputed caption embeddings.
        caption_padding_mask: Precomputed caption padding mask.
        concept_embeds: Precomputed concept embeddings.

    Returns:
        Encoded processor batch.

    Raises:
        ValueError: If required labels or embeddings are missing.
    """
    prompts = self._normalize_prompts(prompt)
    label_texts, union_id2label, per_example = self._normalize_labels(
        labels, id2label=id2label, batch_size=len(prompts)
    )
    batch_size = len(label_texts)
    if len(prompts) == 1 and batch_size > 1:
        prompts = prompts * batch_size
    if len(prompts) != batch_size:
        raise ValueError("prompt and labels batch sizes must match")

    concept_padding_mask = self._concept_padding_mask(label_texts, mask=mask)
    if concept_embeds is None:
        concept_embeds = self._encode_concepts(label_texts)
    concept_embeds = self._pad_concept_embeds(concept_embeds, batch_size)
    if caption_embeds is None or caption_padding_mask is None:
        caption_embeds, caption_padding_mask = self._encode_captions(prompts)
    caption_embeds = caption_embeds.float()
    caption_padding_mask = caption_padding_mask.bool()
    if bbox is not None:
        self._normalize_optional_bbox(
            bbox,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
    return LayouSynBatch(
        concept_embeds=concept_embeds.float(),
        concept_padding_mask=concept_padding_mask,
        caption_embeds=caption_embeds,
        caption_padding_mask=caption_padding_mask,
        aspect_ratio=self._aspect_ratio_tensor(aspect_ratio, batch_size),
        label_texts=label_texts,
        id2label=union_id2label,
        id2label_per_example=per_example,
    )

postprocess

postprocess(
    sample: Float[Tensor, "batch elements 4"],
    *,
    labels: list[list[str]],
    id2label: dict[int, str],
    id2label_per_example: list[dict[int, str]]
    | None = None,
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    intermediates: LayouSynIntermediateValue | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict

Convert generated reference coordinates into the public schema.

Source code in models/layousyn/src/layousyn/processing_layousyn.py
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def postprocess(
    self,
    sample: Float[torch.Tensor, "batch elements 4"],
    *,
    labels: list[list[str]],
    id2label: dict[int, str],
    id2label_per_example: list[dict[int, str]] | None = None,
    output_type: Literal["dataclass", "dict"] = "dataclass",
    return_intermediates: bool = False,
    intermediates: LayouSynIntermediateValue | None = None,
) -> LayoutGenerationOutput | LayouSynOutputDict:
    """Convert generated reference coordinates into the public schema."""
    sample = ((sample.clamp(-1, 1) + 1.0) / 2.0).float()
    if self.layout_type == "xyxy":
        left, top, right, bottom = sample.unbind(dim=-1)
        fixed = torch.stack(
            (
                torch.minimum(left, right),
                torch.minimum(top, bottom),
                torch.maximum(left, right),
                torch.maximum(top, bottom),
            ),
            dim=-1,
        )
        bbox = clamp_boxes(ltrb_to_xywh(fixed))
    else:
        bbox = clamp_boxes(sample)
    batch_size = sample.shape[0]
    label_ids = torch.zeros(batch_size, self.max_in_len, dtype=torch.long)
    mask = torch.zeros(batch_size, self.max_in_len, dtype=torch.bool)
    label2id = {text: idx for idx, text in id2label.items()}
    for batch_idx, batch_labels in enumerate(labels):
        for pos, text in enumerate(batch_labels[: self.max_in_len]):
            label_ids[batch_idx, pos] = label2id[text]
            mask[batch_idx, pos] = True
    payload = intermediates if return_intermediates else None
    if return_intermediates:
        payload = {
            "label_texts": labels,
            "id2label_per_example": id2label_per_example,
            "reference_layout_type": self.layout_type,
            "intermediates": intermediates,
        }
    output = LayoutGenerationOutput(
        bbox=bbox,
        labels=label_ids,
        mask=mask,
        id2label=id2label,
        intermediates=payload,
    )
    if output_type == "dict":
        return cast(LayouSynOutputDict, dict(output))
    if output_type != "dataclass":
        raise ValueError(f"Unsupported output_type: {output_type}")

    return output

scheduling_layousyn

Scheduler preserving LayouSyn's OpenAI Gaussian/DDIM diffusion math.

LayouSynSchedulerOutput dataclass

Bases: BaseOutput

Output returned by a LayouSyn scheduler step.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
18
19
20
21
22
23
@dataclass
class LayouSynSchedulerOutput(BaseOutput):
    """Output returned by a LayouSyn scheduler step."""

    prev_sample: Float[torch.Tensor, "batch elements channels"]
    pred_original_sample: Float[torch.Tensor, "batch elements channels"]

LayouSynScheduler

Bases: SchedulerMixin, ConfigMixin

OpenAI-style Gaussian scheduler for LayouSyn layout tensors.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
class LayouSynScheduler(SchedulerMixin, ConfigMixin):
    """OpenAI-style Gaussian scheduler for LayouSyn layout tensors."""

    config_name = "scheduler_config.json"
    order = 1

    @register_to_config
    def __init__(
        self,
        *,
        num_train_timesteps: int = 100,
        beta_schedule: Literal["linear", "squaredcos_cap_v2"] = "linear",
        alpha_scale: float = 1.0,
        prediction_type: Literal["epsilon"] = "epsilon",
        variance_type: Literal["learned_range"] = "learned_range",
        sampling_type: Literal["ddim", "ddpm"] = "ddim",
    ) -> None:
        """Initialize scheduler buffers."""
        if prediction_type != "epsilon":
            raise ValueError("LayouSyn only supports prediction_type='epsilon'")

        if variance_type != "learned_range":
            raise ValueError("LayouSyn only supports variance_type='learned_range'")

        self.num_train_timesteps = num_train_timesteps
        self.sampling_type = sampling_type
        self.original_betas = get_layousyn_beta_schedule(
            beta_schedule, num_train_timesteps, alpha_scale=alpha_scale
        )
        self.timestep_map = torch.arange(num_train_timesteps, dtype=torch.long)
        self.model_timesteps = torch.empty(0, dtype=torch.long)
        self.timesteps = torch.empty(0, dtype=torch.long)
        self._set_betas(self.original_betas)
        self.set_timesteps(num_train_timesteps)

    def _set_betas(self, betas: Float[torch.Tensor, "timesteps"]) -> None:
        """Set derived buffers from the active beta sequence."""
        self.betas = betas.double()
        alphas = 1.0 - self.betas
        alphas_cumprod = torch.cumprod(alphas, dim=0)
        self.alphas_cumprod = alphas_cumprod
        self.alphas_cumprod_prev = torch.cat(
            [torch.ones(1, dtype=torch.float64), alphas_cumprod[:-1]]
        )
        posterior_variance = (
            self.betas * (1.0 - self.alphas_cumprod_prev) / (1.0 - alphas_cumprod)
        )
        self.posterior_variance = posterior_variance
        clipped = torch.log(
            torch.cat([posterior_variance[1:2], posterior_variance[1:]])
        )
        self.posterior_log_variance_clipped = clipped
        self.sqrt_recip_alphas_cumprod = torch.sqrt(1.0 / alphas_cumprod)
        self.sqrt_recipm1_alphas_cumprod = torch.sqrt(1.0 / alphas_cumprod - 1)
        self.posterior_mean_coef1 = (
            self.betas * torch.sqrt(self.alphas_cumprod_prev) / (1.0 - alphas_cumprod)
        )
        self.posterior_mean_coef2 = (
            (1.0 - self.alphas_cumprod_prev)
            * torch.sqrt(alphas)
            / (1.0 - alphas_cumprod)
        )

    def set_timesteps(
        self,
        num_inference_steps: int | None = None,
        device: torch.device | str | None = None,
    ) -> None:
        """Set descending denoising timesteps with reference respacing."""
        steps = num_inference_steps or self.num_train_timesteps
        if steps > self.num_train_timesteps:
            raise ValueError("num_inference_steps cannot exceed num_train_timesteps")

        use_timesteps = _space_timesteps(self.num_train_timesteps, steps)
        base_alphas = torch.cumprod(1.0 - self.original_betas.double(), dim=0)
        last_alpha_cumprod = torch.tensor(1.0, dtype=torch.float64)
        new_betas = []
        timestep_map = []
        for index, alpha_cumprod in enumerate(base_alphas):
            if index in use_timesteps:
                new_betas.append(1 - alpha_cumprod / last_alpha_cumprod)
                last_alpha_cumprod = alpha_cumprod
                timestep_map.append(index)
        self.timestep_map = torch.tensor(timestep_map, dtype=torch.long, device=device)
        self._set_betas(torch.stack(new_betas))
        self.timesteps = torch.arange(
            len(timestep_map) - 1, -1, -1, dtype=torch.long, device=device
        )
        self.model_timesteps = self.timestep_map[self.timesteps]

    def initial_sample(
        self,
        batch_size: int,
        seq_len: int,
        channels: int,
        *,
        device: torch.device,
        generator: torch.Generator | None = None,
    ) -> Float[torch.Tensor, "batch elements channels"]:
        """Create initial Gaussian noise."""
        return torch.randn(
            batch_size,
            seq_len,
            channels,
            dtype=torch.float32,
            device=device,
            generator=generator,
        )

    def add_noise(
        self,
        original_samples: Float[torch.Tensor, "batch elements channels"],
        noise: Float[torch.Tensor, "batch elements channels"],
        timesteps: Int[torch.Tensor, "batch"],
    ) -> Float[torch.Tensor, "batch elements channels"]:
        """Add forward-process noise to clean samples."""
        alphas = self.alphas_cumprod.to(original_samples.device)
        sqrt_alpha = torch.gather(alphas.sqrt(), 0, timesteps).reshape(-1, 1, 1)
        sqrt_one_minus = torch.gather(torch.sqrt(1.0 - alphas), 0, timesteps).reshape(
            -1, 1, 1
        )
        return sqrt_alpha * original_samples + sqrt_one_minus * noise

    def step(
        self,
        model_output: Float[torch.Tensor, "batch model_elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        sample: Float[torch.Tensor, "batch elements channels"],
        *,
        generator: torch.Generator | None = None,
        eta: float = 0.0,
        clip_denoised: bool = False,
        sampling_type: Literal["ddim", "ddpm"] | None = None,
        return_dict: bool = True,
    ) -> (
        LayouSynSchedulerOutput | tuple[Float[torch.Tensor, "batch elements channels"]]
    ):
        """Take one reverse diffusion step."""
        mode = sampling_type or self.sampling_type
        eps, model_var_values = torch.split(model_output, sample.shape[1], dim=1)
        pred_xstart = self._predict_xstart_from_eps(sample, timestep, eps)
        if clip_denoised:
            pred_xstart = pred_xstart.clamp(-1, 1)
        if mode == "ddim":
            prev_sample = self._ddim_step(
                sample, timestep, pred_xstart, generator=generator, eta=eta
            )
        elif mode == "ddpm":
            prev_sample = self._ddpm_step(
                sample, timestep, pred_xstart, model_var_values, generator=generator
            )
        else:
            raise ValueError(f"Unsupported sampling_type: {mode}")

        output = LayouSynSchedulerOutput(
            prev_sample=prev_sample, pred_original_sample=pred_xstart
        )
        if not return_dict:
            return (output.prev_sample,)
        return output

    def _ddim_step(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        pred_xstart: Float[torch.Tensor, "batch elements channels"],
        *,
        generator: torch.Generator | None,
        eta: float,
    ) -> Float[torch.Tensor, "batch elements channels"]:
        eps = self._predict_eps_from_xstart(sample, timestep, pred_xstart)
        alpha_bar = self._extract(self.alphas_cumprod, timestep, sample.shape)
        alpha_bar_prev = self._extract(self.alphas_cumprod_prev, timestep, sample.shape)
        sigma = (
            eta
            * torch.sqrt((1 - alpha_bar_prev) / (1 - alpha_bar))
            * torch.sqrt(1 - alpha_bar / alpha_bar_prev)
        )
        noise = torch.randn(
            sample.shape, dtype=sample.dtype, device=sample.device, generator=generator
        )
        mean_pred = (
            pred_xstart * torch.sqrt(alpha_bar_prev)
            + torch.sqrt(1 - alpha_bar_prev - sigma**2) * eps
        )
        nonzero_mask = (timestep != 0).float().view(-1, 1, 1)
        return mean_pred + nonzero_mask * sigma * noise

    def _ddpm_step(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        pred_xstart: Float[torch.Tensor, "batch elements channels"],
        model_var_values: Float[torch.Tensor, "batch elements channels"],
        *,
        generator: torch.Generator | None,
    ) -> Float[torch.Tensor, "batch elements channels"]:
        mean = (
            self._extract(self.posterior_mean_coef1, timestep, sample.shape)
            * pred_xstart
        )
        mean = (
            mean
            + self._extract(self.posterior_mean_coef2, timestep, sample.shape) * sample
        )
        min_log = self._extract(
            self.posterior_log_variance_clipped, timestep, sample.shape
        )
        max_log = self._extract(torch.log(self.betas), timestep, sample.shape)
        frac = (model_var_values + 1) / 2
        model_log_variance = frac * max_log + (1 - frac) * min_log
        noise = torch.randn(
            sample.shape, dtype=sample.dtype, device=sample.device, generator=generator
        )
        nonzero_mask = (timestep != 0).float().view(-1, 1, 1)
        return mean + nonzero_mask * torch.exp(0.5 * model_log_variance) * noise

    def _predict_xstart_from_eps(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        eps: Float[torch.Tensor, "batch elements channels"],
    ) -> Float[torch.Tensor, "batch elements channels"]:
        return self._extract(
            self.sqrt_recip_alphas_cumprod, timestep, sample.shape
        ) * sample - (
            self._extract(self.sqrt_recipm1_alphas_cumprod, timestep, sample.shape)
            * eps
        )

    def _predict_eps_from_xstart(
        self,
        sample: Float[torch.Tensor, "batch elements channels"],
        timestep: Int[torch.Tensor, "batch"],
        pred_xstart: Float[torch.Tensor, "batch elements channels"],
    ) -> Float[torch.Tensor, "batch elements channels"]:
        return (
            self._extract(self.sqrt_recip_alphas_cumprod, timestep, sample.shape)
            * sample
            - pred_xstart
        ) / self._extract(self.sqrt_recipm1_alphas_cumprod, timestep, sample.shape)

    @staticmethod
    def _extract(
        values: Shaped[torch.Tensor, "timesteps"],
        timesteps: Int[torch.Tensor, "batch"],
        broadcast_shape: torch.Size,
    ) -> Float[torch.Tensor, "..."]:
        out = values.to(device=timesteps.device).gather(0, timesteps).float()
        while out.ndim < len(broadcast_shape):
            out = out.unsqueeze(-1)
        return out

__init__

__init__(
    *,
    num_train_timesteps: int = 100,
    beta_schedule: Literal[
        "linear", "squaredcos_cap_v2"
    ] = "linear",
    alpha_scale: float = 1.0,
    prediction_type: Literal["epsilon"] = "epsilon",
    variance_type: Literal[
        "learned_range"
    ] = "learned_range",
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
) -> None

Initialize scheduler buffers.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@register_to_config
def __init__(
    self,
    *,
    num_train_timesteps: int = 100,
    beta_schedule: Literal["linear", "squaredcos_cap_v2"] = "linear",
    alpha_scale: float = 1.0,
    prediction_type: Literal["epsilon"] = "epsilon",
    variance_type: Literal["learned_range"] = "learned_range",
    sampling_type: Literal["ddim", "ddpm"] = "ddim",
) -> None:
    """Initialize scheduler buffers."""
    if prediction_type != "epsilon":
        raise ValueError("LayouSyn only supports prediction_type='epsilon'")

    if variance_type != "learned_range":
        raise ValueError("LayouSyn only supports variance_type='learned_range'")

    self.num_train_timesteps = num_train_timesteps
    self.sampling_type = sampling_type
    self.original_betas = get_layousyn_beta_schedule(
        beta_schedule, num_train_timesteps, alpha_scale=alpha_scale
    )
    self.timestep_map = torch.arange(num_train_timesteps, dtype=torch.long)
    self.model_timesteps = torch.empty(0, dtype=torch.long)
    self.timesteps = torch.empty(0, dtype=torch.long)
    self._set_betas(self.original_betas)
    self.set_timesteps(num_train_timesteps)

set_timesteps

set_timesteps(
    num_inference_steps: int | None = None,
    device: device | str | None = None,
) -> None

Set descending denoising timesteps with reference respacing.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def set_timesteps(
    self,
    num_inference_steps: int | None = None,
    device: torch.device | str | None = None,
) -> None:
    """Set descending denoising timesteps with reference respacing."""
    steps = num_inference_steps or self.num_train_timesteps
    if steps > self.num_train_timesteps:
        raise ValueError("num_inference_steps cannot exceed num_train_timesteps")

    use_timesteps = _space_timesteps(self.num_train_timesteps, steps)
    base_alphas = torch.cumprod(1.0 - self.original_betas.double(), dim=0)
    last_alpha_cumprod = torch.tensor(1.0, dtype=torch.float64)
    new_betas = []
    timestep_map = []
    for index, alpha_cumprod in enumerate(base_alphas):
        if index in use_timesteps:
            new_betas.append(1 - alpha_cumprod / last_alpha_cumprod)
            last_alpha_cumprod = alpha_cumprod
            timestep_map.append(index)
    self.timestep_map = torch.tensor(timestep_map, dtype=torch.long, device=device)
    self._set_betas(torch.stack(new_betas))
    self.timesteps = torch.arange(
        len(timestep_map) - 1, -1, -1, dtype=torch.long, device=device
    )
    self.model_timesteps = self.timestep_map[self.timesteps]

initial_sample

initial_sample(
    batch_size: int,
    seq_len: int,
    channels: int,
    *,
    device: device,
    generator: Generator | None = None,
) -> Float[torch.Tensor, "batch elements channels"]

Create initial Gaussian noise.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def initial_sample(
    self,
    batch_size: int,
    seq_len: int,
    channels: int,
    *,
    device: torch.device,
    generator: torch.Generator | None = None,
) -> Float[torch.Tensor, "batch elements channels"]:
    """Create initial Gaussian noise."""
    return torch.randn(
        batch_size,
        seq_len,
        channels,
        dtype=torch.float32,
        device=device,
        generator=generator,
    )

add_noise

add_noise(
    original_samples: Float[
        Tensor, "batch elements channels"
    ],
    noise: Float[Tensor, "batch elements channels"],
    timesteps: Int[Tensor, "batch"],
) -> Float[torch.Tensor, "batch elements channels"]

Add forward-process noise to clean samples.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
135
136
137
138
139
140
141
142
143
144
145
146
147
def add_noise(
    self,
    original_samples: Float[torch.Tensor, "batch elements channels"],
    noise: Float[torch.Tensor, "batch elements channels"],
    timesteps: Int[torch.Tensor, "batch"],
) -> Float[torch.Tensor, "batch elements channels"]:
    """Add forward-process noise to clean samples."""
    alphas = self.alphas_cumprod.to(original_samples.device)
    sqrt_alpha = torch.gather(alphas.sqrt(), 0, timesteps).reshape(-1, 1, 1)
    sqrt_one_minus = torch.gather(torch.sqrt(1.0 - alphas), 0, timesteps).reshape(
        -1, 1, 1
    )
    return sqrt_alpha * original_samples + sqrt_one_minus * noise

step

step(
    model_output: Float[
        Tensor, "batch model_elements channels"
    ],
    timestep: Int[Tensor, "batch"],
    sample: Float[Tensor, "batch elements channels"],
    *,
    generator: Generator | None = None,
    eta: float = 0.0,
    clip_denoised: bool = False,
    sampling_type: Literal["ddim", "ddpm"] | None = None,
    return_dict: bool = True,
) -> (
    LayouSynSchedulerOutput
    | tuple[Float[torch.Tensor, "batch elements channels"]]
)

Take one reverse diffusion step.

Source code in models/layousyn/src/layousyn/scheduling_layousyn.py
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
def step(
    self,
    model_output: Float[torch.Tensor, "batch model_elements channels"],
    timestep: Int[torch.Tensor, "batch"],
    sample: Float[torch.Tensor, "batch elements channels"],
    *,
    generator: torch.Generator | None = None,
    eta: float = 0.0,
    clip_denoised: bool = False,
    sampling_type: Literal["ddim", "ddpm"] | None = None,
    return_dict: bool = True,
) -> (
    LayouSynSchedulerOutput | tuple[Float[torch.Tensor, "batch elements channels"]]
):
    """Take one reverse diffusion step."""
    mode = sampling_type or self.sampling_type
    eps, model_var_values = torch.split(model_output, sample.shape[1], dim=1)
    pred_xstart = self._predict_xstart_from_eps(sample, timestep, eps)
    if clip_denoised:
        pred_xstart = pred_xstart.clamp(-1, 1)
    if mode == "ddim":
        prev_sample = self._ddim_step(
            sample, timestep, pred_xstart, generator=generator, eta=eta
        )
    elif mode == "ddpm":
        prev_sample = self._ddpm_step(
            sample, timestep, pred_xstart, model_var_values, generator=generator
        )
    else:
        raise ValueError(f"Unsupported sampling_type: {mode}")

    output = LayouSynSchedulerOutput(
        prev_sample=prev_sample, pred_original_sample=pred_xstart
    )
    if not return_dict:
        return (output.prev_sample,)
    return output