Skip to content

Layout fid

Public API for the layout FID evaluator package.

LayoutFIDArchitecture

Bases: StrEnum

Closed set of supported layout FID encoder architectures.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
19
20
21
22
23
class LayoutFIDArchitecture(StrEnum):
    """Closed set of supported layout FID encoder architectures."""

    fidnet_v3 = auto()
    layoutnet = auto()

LayoutFIDConfig

Bases: PretrainedConfig

Configuration saved with layout FID checkpoints.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Canonical layout dataset name.

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

Dataset-local id-to-label metadata.

None
architecture LayoutFIDArchitecture | str

Encoder architecture selected by the checkpoint.

required
source LayoutFIDSource | str

Released artifact family selected by the checkpoint.

required
num_public_labels int

Number of public dataset labels.

required
num_label_embeddings int

Number of model label embeddings.

required
max_length int

Maximum element count accepted by the checkpoint.

required
d_model int

Transformer hidden dimension.

256
nhead int

Number of attention heads.

4
num_layers int

Number of transformer encoder layers.

4
bbox_format_for_model BoxFormat | str

Internal bbox format consumed by the encoder.

'ltrb'
label_id_offset int

Offset applied before model label embedding lookup.

0
pad_label_id int

Label id used only in padded model tensor positions.

0
reference_stats dict[str, str] | None

Relative reference-statistics paths by split.

None
kwargs LayoutFIDConfigValue

Extra Hugging Face config fields.

{}

Raises:

Type Description
ValueError

If label counts or enum values are invalid.

Examples:

>>> cfg = LayoutFIDConfig(
...     dataset_name="publaynet",
...     architecture="layoutnet",
...     source="layoutflow",
...     num_public_labels=5,
...     num_label_embeddings=6,
...     max_length=20,
... )
>>> cfg.reference_stats["test"]
'reference_stats/test.npz'
Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
 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
class LayoutFIDConfig(PretrainedConfig):
    """Configuration saved with layout FID checkpoints.

    Args:
        dataset_name: Canonical layout dataset name.
        id2label: Dataset-local id-to-label metadata.
        architecture: Encoder architecture selected by the checkpoint.
        source: Released artifact family selected by the checkpoint.
        num_public_labels: Number of public dataset labels.
        num_label_embeddings: Number of model label embeddings.
        max_length: Maximum element count accepted by the checkpoint.
        d_model: Transformer hidden dimension.
        nhead: Number of attention heads.
        num_layers: Number of transformer encoder layers.
        bbox_format_for_model: Internal bbox format consumed by the encoder.
        label_id_offset: Offset applied before model label embedding lookup.
        pad_label_id: Label id used only in padded model tensor positions.
        reference_stats: Relative reference-statistics paths by split.
        kwargs: Extra Hugging Face config fields.

    Raises:
        ValueError: If label counts or enum values are invalid.

    Examples:
        >>> cfg = LayoutFIDConfig(
        ...     dataset_name="publaynet",
        ...     architecture="layoutnet",
        ...     source="layoutflow",
        ...     num_public_labels=5,
        ...     num_label_embeddings=6,
        ...     max_length=20,
        ... )
        >>> cfg.reference_stats["test"]
        'reference_stats/test.npz'
    """

    model_type = "layout-fid"
    has_no_defaults_at_init = True

    def __init__(
        self,
        *,
        dataset_name: DatasetName | str,
        id2label: Mapping[int, str] | Mapping[str, str] | None = None,
        architecture: LayoutFIDArchitecture | str,
        source: LayoutFIDSource | str,
        num_public_labels: int,
        num_label_embeddings: int,
        max_length: int,
        d_model: int = 256,
        nhead: int = 4,
        num_layers: int = 4,
        bbox_format_for_model: BoxFormat | str = "ltrb",
        label_id_offset: int = 0,
        pad_label_id: int = 0,
        reference_stats: dict[str, str] | None = None,
        **kwargs: LayoutFIDConfigValue,
    ) -> None:
        """Initialize a layout FID checkpoint configuration."""
        super().__init__(**kwargs)  # ty: ignore[invalid-argument-type]
        dataset = normalize_dataset_name(dataset_name)
        arch = normalize_architecture(architecture)
        src = normalize_source(source)
        box_format = normalize_box_format(bbox_format_for_model)
        if num_public_labels <= 0:
            raise ValueError("num_public_labels must be positive")

        if num_label_embeddings < num_public_labels:
            raise ValueError("num_label_embeddings must cover public labels")

        if max_length <= 0:
            raise ValueError("max_length must be positive")

        if pad_label_id < 0 or pad_label_id >= num_label_embeddings:
            raise ValueError("pad_label_id must be inside the embedding table")

        raw_id2label = id2label or id2label_for_dataset(dataset)
        self.dataset_name = str(dataset)
        self.id2label = {int(k): v for k, v in raw_id2label.items()}
        self.label2id = {label: idx for idx, label in self.id2label.items()}
        self.architecture = str(arch)
        self.source = str(src)

        self.num_public_labels = num_public_labels
        self.num_label_embeddings = num_label_embeddings
        self.max_length = max_length
        self.d_model = d_model
        self.nhead = nhead
        self.num_layers = num_layers

        self.bbox_format_for_model = str(box_format)
        self.label_id_offset = label_id_offset
        self.pad_label_id = pad_label_id
        self.reference_stats = dict(reference_stats or DEFAULT_REFERENCE_STATS)

    @property
    def feature_dim(self) -> int:
        """Return the layout FID feature dimension."""
        return self.d_model

    def _get_generation_parameters(self) -> dict[str, str]:
        """Return no generation parameters for this evaluator config."""
        return {}

feature_dim property

feature_dim: int

Return the layout FID feature dimension.

__init__

__init__(
    *,
    dataset_name: DatasetName | str,
    id2label: Mapping[int, str]
    | Mapping[str, str]
    | None = None,
    architecture: LayoutFIDArchitecture | str,
    source: LayoutFIDSource | str,
    num_public_labels: int,
    num_label_embeddings: int,
    max_length: int,
    d_model: int = 256,
    nhead: int = 4,
    num_layers: int = 4,
    bbox_format_for_model: BoxFormat | str = "ltrb",
    label_id_offset: int = 0,
    pad_label_id: int = 0,
    reference_stats: dict[str, str] | None = None,
    **kwargs: LayoutFIDConfigValue,
) -> None

Initialize a layout FID checkpoint configuration.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
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
def __init__(
    self,
    *,
    dataset_name: DatasetName | str,
    id2label: Mapping[int, str] | Mapping[str, str] | None = None,
    architecture: LayoutFIDArchitecture | str,
    source: LayoutFIDSource | str,
    num_public_labels: int,
    num_label_embeddings: int,
    max_length: int,
    d_model: int = 256,
    nhead: int = 4,
    num_layers: int = 4,
    bbox_format_for_model: BoxFormat | str = "ltrb",
    label_id_offset: int = 0,
    pad_label_id: int = 0,
    reference_stats: dict[str, str] | None = None,
    **kwargs: LayoutFIDConfigValue,
) -> None:
    """Initialize a layout FID checkpoint configuration."""
    super().__init__(**kwargs)  # ty: ignore[invalid-argument-type]
    dataset = normalize_dataset_name(dataset_name)
    arch = normalize_architecture(architecture)
    src = normalize_source(source)
    box_format = normalize_box_format(bbox_format_for_model)
    if num_public_labels <= 0:
        raise ValueError("num_public_labels must be positive")

    if num_label_embeddings < num_public_labels:
        raise ValueError("num_label_embeddings must cover public labels")

    if max_length <= 0:
        raise ValueError("max_length must be positive")

    if pad_label_id < 0 or pad_label_id >= num_label_embeddings:
        raise ValueError("pad_label_id must be inside the embedding table")

    raw_id2label = id2label or id2label_for_dataset(dataset)
    self.dataset_name = str(dataset)
    self.id2label = {int(k): v for k, v in raw_id2label.items()}
    self.label2id = {label: idx for idx, label in self.id2label.items()}
    self.architecture = str(arch)
    self.source = str(src)

    self.num_public_labels = num_public_labels
    self.num_label_embeddings = num_label_embeddings
    self.max_length = max_length
    self.d_model = d_model
    self.nhead = nhead
    self.num_layers = num_layers

    self.bbox_format_for_model = str(box_format)
    self.label_id_offset = label_id_offset
    self.pad_label_id = pad_label_id
    self.reference_stats = dict(reference_stats or DEFAULT_REFERENCE_STATS)

LayoutFIDSource

Bases: StrEnum

Closed set of supported released artifact families.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
26
27
28
29
30
class LayoutFIDSource(StrEnum):
    """Closed set of supported released artifact families."""

    layoutdm = auto()
    layoutflow = auto()

LayoutFIDStatsSplit

Bases: StrEnum

Closed set of bundled reference-statistics splits.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
33
34
35
36
37
class LayoutFIDStatsSplit(StrEnum):
    """Closed set of bundled reference-statistics splits."""

    val = auto()
    test = auto()

LayoutFIDStatistics dataclass

Feature distribution statistics used by layout FID.

Source code in models/layout-fid/src/layout_fid/evaluation.py
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
@dataclass(frozen=True)
class LayoutFIDStatistics:
    """Feature distribution statistics used by layout FID."""

    mu: Float[np.ndarray, "channels"]
    sigma: Float[np.ndarray, "channels channels"]
    split: str
    dataset_name: str
    source: str
    feature_dim: int
    num_samples: int | None = None

    @classmethod
    def from_mapping(
        cls,
        values: Mapping[
            str,
            Float[np.ndarray, "..."]
            | list[float]
            | list[list[float]]
            | str
            | int
            | None,
        ],
    ) -> "LayoutFIDStatistics":
        """Create statistics from a mapping."""
        mu = np.asarray(values["mu"], dtype=np.float64)
        sigma = np.asarray(values["sigma"], dtype=np.float64)
        return cls(
            mu=mu,
            sigma=sigma,
            split=str(values.get("split", "candidate")),
            dataset_name=str(values.get("dataset_name", "")),
            source=str(values.get("source", "")),
            feature_dim=int(cast(int | str, values.get("feature_dim", mu.shape[0]))),
            num_samples=(
                None
                if values.get("num_samples") is None
                else int(cast(int | str, values["num_samples"]))
            ),
        )

from_mapping classmethod

from_mapping(
    values: Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
) -> "LayoutFIDStatistics"

Create statistics from a mapping.

Source code in models/layout-fid/src/layout_fid/evaluation.py
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
@classmethod
def from_mapping(
    cls,
    values: Mapping[
        str,
        Float[np.ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
) -> "LayoutFIDStatistics":
    """Create statistics from a mapping."""
    mu = np.asarray(values["mu"], dtype=np.float64)
    sigma = np.asarray(values["sigma"], dtype=np.float64)
    return cls(
        mu=mu,
        sigma=sigma,
        split=str(values.get("split", "candidate")),
        dataset_name=str(values.get("dataset_name", "")),
        source=str(values.get("source", "")),
        feature_dim=int(cast(int | str, values.get("feature_dim", mu.shape[0]))),
        num_samples=(
            None
            if values.get("num_samples") is None
            else int(cast(int | str, values["num_samples"]))
        ),
    )

LayoutFIDModel

Bases: PreTrainedModel

Feature encoder used for layout FID evaluation.

Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
class LayoutFIDModel(PreTrainedModel):
    """Feature encoder used for layout FID evaluation."""

    config_class = LayoutFIDConfig
    base_model_prefix = "layout_fid"

    def __init__(self, config: LayoutFIDConfig) -> None:
        """Create a layout FID encoder.

        Args:
            config: Explicit layout FID configuration.
        """
        super().__init__(config)
        self.all_tied_weights_keys: dict[str, str] = {}
        self.emb_label = nn.Embedding(config.num_label_embeddings, config.d_model)
        self.fc_bbox = nn.Linear(4, config.d_model)
        self.enc_fc_in = nn.Linear(config.d_model * 2, config.d_model)
        self.enc_transformer = TransformerWithToken(
            d_model=config.d_model,
            dim_feedforward=config.d_model // 2,
            nhead=config.nhead,
            num_layers=config.num_layers,
        )
        self.fc_out_disc = nn.Linear(config.d_model, 1)
        self.pos_token = nn.Parameter(torch.rand(config.max_length, 1, config.d_model))
        self.dec_fc_in = nn.Linear(config.d_model * 2, config.d_model)
        dec_layer = nn.TransformerEncoderLayer(
            d_model=config.d_model,
            nhead=config.nhead,
            dim_feedforward=config.d_model // 2,
        )
        self.dec_transformer = nn.TransformerEncoder(
            dec_layer, num_layers=config.num_layers
        )
        self.fc_out_cls = nn.Linear(config.d_model, config.num_label_embeddings)
        self.fc_out_bbox = nn.Linear(config.d_model, 4)

    def extract_features(
        self,
        *,
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
    ) -> Float[torch.Tensor, "batch channels"]:
        """Extract batch-level feature vectors.

        Args:
            bbox: Model-ready boxes.
            labels: Model-ready label ids.
            padding_mask: Boolean mask where ``True`` marks padded elements.

        Returns:
            Feature tensor shaped ``(batch, d_model)``.

        Raises:
            ValueError: If input shapes are inconsistent.

        Examples:
            >>> from layout_fid import LayoutFIDConfig, LayoutFIDModel
            >>> cfg = LayoutFIDConfig(
            ...     dataset_name="publaynet", architecture="layoutnet",
            ...     source="layoutflow", num_public_labels=5,
            ...     num_label_embeddings=6, max_length=2,
            ... )
            >>> model = LayoutFIDModel(cfg)
            >>> out = model.extract_features(
            ...     bbox=torch.zeros(1, 2, 4),
            ...     labels=torch.zeros(1, 2, dtype=torch.long),
            ...     padding_mask=torch.zeros(1, 2, dtype=torch.bool),
            ... )
            >>> tuple(out.shape)
            (1, 256)
        """
        self._validate_inputs(bbox, labels, padding_mask)
        box_features = self.fc_bbox(bbox)
        label_features = self.emb_label(labels)
        hidden = self.enc_fc_in(torch.cat([box_features, label_features], dim=-1))
        hidden = torch.relu(hidden).permute(1, 0, 2)
        encoded = self.enc_transformer(hidden, padding_mask)
        return encoded[0]

    def forward(
        self,
        *,
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
        output_reconstruction: bool = False,
        return_dict: bool = True,
    ) -> LayoutFIDOutput | tuple[Shaped[torch.Tensor, "..."], ...]:
        """Run feature extraction and optional reconstruction heads.

        Args:
            bbox: Model-ready boxes.
            labels: Model-ready label ids.
            padding_mask: Boolean mask where ``True`` marks padded elements.
            output_reconstruction: Whether to return class and bbox predictions.
            return_dict: Whether to return ``LayoutFIDOutput``.

        Returns:
            ``LayoutFIDOutput`` or a tuple with the same non-``None`` fields.

        Raises:
            ValueError: If input shapes are inconsistent.
        """
        features = self.extract_features(
            bbox=bbox, labels=labels, padding_mask=padding_mask
        )
        discriminator_logits = self.fc_out_disc(features).squeeze(-1)
        class_logits: Float[torch.Tensor, "... labels"] | None = None
        bbox_pred: Float[torch.Tensor, "... 4"] | None = None
        if output_reconstruction:
            class_logits, bbox_pred = self._decode(features, padding_mask)
        if not return_dict:
            values = (features, discriminator_logits, class_logits, bbox_pred)
            return tuple(value for value in values if value is not None)
        return LayoutFIDOutput(
            features=features,
            discriminator_logits=discriminator_logits,
            class_logits=class_logits,
            bbox_pred=bbox_pred,
        )

    def _decode(
        self,
        features: Float[torch.Tensor, "batch channels"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
    ) -> tuple[Float[torch.Tensor, "... labels"], Float[torch.Tensor, "... 4"]]:
        batch_size, elements = padding_mask.shape
        hidden = features.unsqueeze(0).expand(elements, -1, -1)
        positions = self.pos_token[:elements].expand(-1, batch_size, -1)
        hidden = torch.relu(self.dec_fc_in(torch.cat([hidden, positions], dim=-1)))
        hidden = self.dec_transformer(hidden, src_key_padding_mask=padding_mask)
        hidden = hidden.permute(1, 0, 2)
        class_logits = self.fc_out_cls(hidden)
        bbox_pred = torch.sigmoid(self.fc_out_bbox(hidden))
        if normalize_source(self.config.source) is LayoutFIDSource.layoutflow:
            valid = ~padding_mask
            class_logits = class_logits[valid]
            bbox_pred = bbox_pred[valid]
        return class_logits, bbox_pred

    @staticmethod
    def _validate_inputs(
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
    ) -> None:
        if bbox.ndim != 3 or bbox.shape[-1] != 4:
            raise ValueError("bbox must have shape (batch, elements, 4)")

        if labels.shape != bbox.shape[:2]:
            raise ValueError("labels must have shape (batch, elements)")

        if padding_mask.shape != bbox.shape[:2]:
            raise ValueError("padding_mask must have shape (batch, elements)")

__init__

__init__(config: LayoutFIDConfig) -> None

Create a layout FID encoder.

Parameters:

Name Type Description Default
config LayoutFIDConfig

Explicit layout FID configuration.

required
Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def __init__(self, config: LayoutFIDConfig) -> None:
    """Create a layout FID encoder.

    Args:
        config: Explicit layout FID configuration.
    """
    super().__init__(config)
    self.all_tied_weights_keys: dict[str, str] = {}
    self.emb_label = nn.Embedding(config.num_label_embeddings, config.d_model)
    self.fc_bbox = nn.Linear(4, config.d_model)
    self.enc_fc_in = nn.Linear(config.d_model * 2, config.d_model)
    self.enc_transformer = TransformerWithToken(
        d_model=config.d_model,
        dim_feedforward=config.d_model // 2,
        nhead=config.nhead,
        num_layers=config.num_layers,
    )
    self.fc_out_disc = nn.Linear(config.d_model, 1)
    self.pos_token = nn.Parameter(torch.rand(config.max_length, 1, config.d_model))
    self.dec_fc_in = nn.Linear(config.d_model * 2, config.d_model)
    dec_layer = nn.TransformerEncoderLayer(
        d_model=config.d_model,
        nhead=config.nhead,
        dim_feedforward=config.d_model // 2,
    )
    self.dec_transformer = nn.TransformerEncoder(
        dec_layer, num_layers=config.num_layers
    )
    self.fc_out_cls = nn.Linear(config.d_model, config.num_label_embeddings)
    self.fc_out_bbox = nn.Linear(config.d_model, 4)

extract_features

extract_features(
    *,
    bbox: Float[Tensor, "batch elements 4"],
    labels: Int[Tensor, "batch elements"],
    padding_mask: Bool[Tensor, "batch elements"],
) -> Float[torch.Tensor, "batch channels"]

Extract batch-level feature vectors.

Parameters:

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

Model-ready boxes.

required
labels Int[Tensor, 'batch elements']

Model-ready label ids.

required
padding_mask Bool[Tensor, 'batch elements']

Boolean mask where True marks padded elements.

required

Returns:

Type Description
Float[Tensor, 'batch channels']

Feature tensor shaped (batch, d_model).

Raises:

Type Description
ValueError

If input shapes are inconsistent.

Examples:

>>> from layout_fid import LayoutFIDConfig, LayoutFIDModel
>>> cfg = LayoutFIDConfig(
...     dataset_name="publaynet", architecture="layoutnet",
...     source="layoutflow", num_public_labels=5,
...     num_label_embeddings=6, max_length=2,
... )
>>> model = LayoutFIDModel(cfg)
>>> out = model.extract_features(
...     bbox=torch.zeros(1, 2, 4),
...     labels=torch.zeros(1, 2, dtype=torch.long),
...     padding_mask=torch.zeros(1, 2, dtype=torch.bool),
... )
>>> tuple(out.shape)
(1, 256)
Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
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
def extract_features(
    self,
    *,
    bbox: Float[torch.Tensor, "batch elements 4"],
    labels: Int[torch.Tensor, "batch elements"],
    padding_mask: Bool[torch.Tensor, "batch elements"],
) -> Float[torch.Tensor, "batch channels"]:
    """Extract batch-level feature vectors.

    Args:
        bbox: Model-ready boxes.
        labels: Model-ready label ids.
        padding_mask: Boolean mask where ``True`` marks padded elements.

    Returns:
        Feature tensor shaped ``(batch, d_model)``.

    Raises:
        ValueError: If input shapes are inconsistent.

    Examples:
        >>> from layout_fid import LayoutFIDConfig, LayoutFIDModel
        >>> cfg = LayoutFIDConfig(
        ...     dataset_name="publaynet", architecture="layoutnet",
        ...     source="layoutflow", num_public_labels=5,
        ...     num_label_embeddings=6, max_length=2,
        ... )
        >>> model = LayoutFIDModel(cfg)
        >>> out = model.extract_features(
        ...     bbox=torch.zeros(1, 2, 4),
        ...     labels=torch.zeros(1, 2, dtype=torch.long),
        ...     padding_mask=torch.zeros(1, 2, dtype=torch.bool),
        ... )
        >>> tuple(out.shape)
        (1, 256)
    """
    self._validate_inputs(bbox, labels, padding_mask)
    box_features = self.fc_bbox(bbox)
    label_features = self.emb_label(labels)
    hidden = self.enc_fc_in(torch.cat([box_features, label_features], dim=-1))
    hidden = torch.relu(hidden).permute(1, 0, 2)
    encoded = self.enc_transformer(hidden, padding_mask)
    return encoded[0]

forward

forward(
    *,
    bbox: Float[Tensor, "batch elements 4"],
    labels: Int[Tensor, "batch elements"],
    padding_mask: Bool[Tensor, "batch elements"],
    output_reconstruction: bool = False,
    return_dict: bool = True,
) -> (
    LayoutFIDOutput
    | tuple[Shaped[torch.Tensor, "..."], ...]
)

Run feature extraction and optional reconstruction heads.

Parameters:

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

Model-ready boxes.

required
labels Int[Tensor, 'batch elements']

Model-ready label ids.

required
padding_mask Bool[Tensor, 'batch elements']

Boolean mask where True marks padded elements.

required
output_reconstruction bool

Whether to return class and bbox predictions.

False
return_dict bool

Whether to return LayoutFIDOutput.

True

Returns:

Type Description
LayoutFIDOutput | tuple[Shaped[Tensor, '...'], ...]

LayoutFIDOutput or a tuple with the same non-None fields.

Raises:

Type Description
ValueError

If input shapes are inconsistent.

Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
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
def forward(
    self,
    *,
    bbox: Float[torch.Tensor, "batch elements 4"],
    labels: Int[torch.Tensor, "batch elements"],
    padding_mask: Bool[torch.Tensor, "batch elements"],
    output_reconstruction: bool = False,
    return_dict: bool = True,
) -> LayoutFIDOutput | tuple[Shaped[torch.Tensor, "..."], ...]:
    """Run feature extraction and optional reconstruction heads.

    Args:
        bbox: Model-ready boxes.
        labels: Model-ready label ids.
        padding_mask: Boolean mask where ``True`` marks padded elements.
        output_reconstruction: Whether to return class and bbox predictions.
        return_dict: Whether to return ``LayoutFIDOutput``.

    Returns:
        ``LayoutFIDOutput`` or a tuple with the same non-``None`` fields.

    Raises:
        ValueError: If input shapes are inconsistent.
    """
    features = self.extract_features(
        bbox=bbox, labels=labels, padding_mask=padding_mask
    )
    discriminator_logits = self.fc_out_disc(features).squeeze(-1)
    class_logits: Float[torch.Tensor, "... labels"] | None = None
    bbox_pred: Float[torch.Tensor, "... 4"] | None = None
    if output_reconstruction:
        class_logits, bbox_pred = self._decode(features, padding_mask)
    if not return_dict:
        values = (features, discriminator_logits, class_logits, bbox_pred)
        return tuple(value for value in values if value is not None)
    return LayoutFIDOutput(
        features=features,
        discriminator_logits=discriminator_logits,
        class_logits=class_logits,
        bbox_pred=bbox_pred,
    )

LayoutFIDOutput dataclass

Bases: ModelOutput

Output returned by LayoutFIDModel.forward.

Parameters:

Name Type Description Default
features Float[Tensor, 'batch channels']

Batch-level layout feature vectors.

required
discriminator_logits Float[Tensor, 'batch'] | None

Optional discriminator logits.

None
class_logits Float[Tensor, '... labels'] | None

Optional per-element or valid-element class logits.

None
bbox_pred Float[Tensor, '... 4'] | None

Optional reconstructed boxes.

None
intermediates dict[str, object] | None

Optional diagnostic tensors.

None
Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@dataclass
class LayoutFIDOutput(ModelOutput):
    """Output returned by ``LayoutFIDModel.forward``.

    Args:
        features: Batch-level layout feature vectors.
        discriminator_logits: Optional discriminator logits.
        class_logits: Optional per-element or valid-element class logits.
        bbox_pred: Optional reconstructed boxes.
        intermediates: Optional diagnostic tensors.
    """

    features: Float[torch.Tensor, "batch channels"]
    discriminator_logits: Float[torch.Tensor, "batch"] | None = None
    class_logits: Float[torch.Tensor, "... labels"] | None = None
    bbox_pred: Float[torch.Tensor, "... 4"] | None = None
    intermediates: dict[str, object] | None = None

LayoutFIDEvaluator

Compose a layout FID model, processor, and reference statistics.

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

    def __init__(
        self,
        *,
        model: LayoutFIDModel,
        processor: LayoutFIDProcessor,
        reference_statistics: Mapping[str, LayoutFIDStatistics] | None = None,
        device: torch.device | str | None = None,
    ) -> None:
        """Create an evaluator."""
        self.model = model
        self.processor = processor
        self.reference_statistics = dict(reference_statistics or {})
        self.device = (
            torch.device(device) if device is not None else torch.device("cpu")
        )
        self.model.to(self.device)
        self.model.eval()

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | PathLike[str],
        *,
        device: torch.device | str | None = None,
        **kwargs: LayoutFIDLoadKwarg,
    ) -> LayoutFIDEvaluator:
        """Load evaluator components from a local directory or Hub id."""
        model = LayoutFIDModel.from_pretrained(pretrained_model_name_or_path, **kwargs)
        processor = LayoutFIDProcessor.from_pretrained(
            pretrained_model_name_or_path,
            **cast(dict[str, LayoutFIDConfigValue], kwargs),
        )
        stats = cls._load_reference_statistics(pretrained_model_name_or_path, model)
        return cls(
            model=model, processor=processor, reference_statistics=stats, device=device
        )

    def extract_features(
        self,
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        id2label: Mapping[int, str] | Mapping[str, str] | None = None,
        box_format: str = "xywh",
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        batch_size: int = 512,
    ) -> Float[torch.Tensor, "batch channels"]:
        """Extract features from public layout tensors."""
        layout_kwargs = self._layout_kwargs(
            layouts=layouts,
            bbox=bbox,
            labels=labels,
            mask=mask,
            id2label=id2label,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        batch = self.processor(
            bbox=cast(
                Float[torch.Tensor, "batch elements 4"]
                | Float[np.ndarray, "batch elements 4"]
                | Sequence[ArrayLikeInput],
                layout_kwargs["bbox"],
            ),
            labels=cast(
                Int[torch.Tensor, "batch elements"]
                | Int[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput],
                layout_kwargs["labels"],
            ),
            mask=cast(
                Bool[torch.Tensor, "batch elements"]
                | Bool[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs["mask"],
            ),
            id2label=cast(
                Mapping[int, str] | Mapping[str, str] | None,
                layout_kwargs["id2label"],
            ),
            box_format=cast(str, layout_kwargs["box_format"]),
            normalized=cast(bool, layout_kwargs["normalized"]),
            canvas_size=cast(tuple[int, int] | None, layout_kwargs["canvas_size"]),
            device=self.device,
        )
        outputs: list[Float[torch.Tensor, "batch channels"]] = []
        for start in range(0, batch.bbox.shape[0], batch_size):
            end = start + batch_size
            with torch.no_grad():
                outputs.append(
                    self.model.extract_features(
                        bbox=batch.bbox[start:end],
                        labels=batch.labels[start:end],
                        padding_mask=batch.padding_mask[start:end],
                    ).cpu()
                )
        return torch.cat(outputs, dim=0)

    def compute_statistics(
        self,
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None = None,
        features: Float[torch.Tensor, "batch channels"]
        | Float[np.ndarray, "batch channels"]
        | None = None,
        **layout_kwargs: LayoutFIDLayoutKwarg,
    ) -> LayoutFIDStatistics:
        """Compute candidate feature statistics."""
        if features is not None and (layouts is not None or layout_kwargs):
            raise ValueError("Pass either features or layout inputs, not both")

        if features is None:
            features = self.extract_features(
                layouts=layouts,
                bbox=cast(
                    Float[torch.Tensor, "batch elements 4"]
                    | Float[np.ndarray, "batch elements 4"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layout_kwargs.get("bbox"),
                ),
                labels=cast(
                    Int[torch.Tensor, "batch elements"]
                    | Int[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layout_kwargs.get("labels"),
                ),
                mask=cast(
                    Bool[torch.Tensor, "batch elements"]
                    | Bool[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layout_kwargs.get("mask"),
                ),
                id2label=cast(
                    Mapping[int, str] | Mapping[str, str] | None,
                    layout_kwargs.get("id2label"),
                ),
                box_format=cast(str, layout_kwargs.get("box_format", "xywh")),
                normalized=cast(bool, layout_kwargs.get("normalized", True)),
                canvas_size=cast(
                    tuple[int, int] | None, layout_kwargs.get("canvas_size")
                ),
                batch_size=cast(int, layout_kwargs.get("batch_size", 512)),
            )
        return compute_feature_statistics(
            features,
            dataset_name=self.model.config.dataset_name,
            source=self.model.config.source,
        )

    def compute_fid(
        self,
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None = None,
        features: Float[torch.Tensor, "batch channels"]
        | Float[np.ndarray, "batch channels"]
        | None = None,
        statistics: LayoutFIDStatistics
        | Mapping[
            str,
            Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
        ]
        | None = None,
        reference_statistics: LayoutFIDStatistics
        | Mapping[
            str,
            Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
        ]
        | None = None,
        reference_split: LayoutFIDStatsSplit | str = "test",
        **layout_kwargs: LayoutFIDLayoutKwarg,
    ) -> float:
        """Compute layout FID against bundled or supplied reference statistics."""
        provided = sum(value is not None for value in (layouts, features, statistics))
        if provided + bool(layout_kwargs) == 0:
            raise ValueError("Pass candidate layouts, features, or statistics")

        if statistics is None:
            statistics = self.compute_statistics(
                layouts=layouts, features=features, **layout_kwargs
            )
        reference = (
            LayoutFIDStatistics.from_mapping(
                cast(
                    Mapping[
                        str,
                        Float[np.ndarray, "..."]
                        | list[float]
                        | list[list[float]]
                        | str
                        | int
                        | None,
                    ],
                    reference_statistics,
                )
            )
            if reference_statistics is not None
            and not isinstance(reference_statistics, LayoutFIDStatistics)
            else reference_statistics
        )
        if reference is None:
            split = str(normalize_stats_split(reference_split))
            try:
                reference = self.reference_statistics[split]
            except KeyError as exc:
                raise ValueError(
                    f"Reference statistics split is not loaded: {split}"
                ) from exc

        candidate = (
            LayoutFIDStatistics.from_mapping(
                cast(
                    Mapping[
                        str,
                        Float[np.ndarray, "..."]
                        | list[float]
                        | list[list[float]]
                        | str
                        | int
                        | None,
                    ],
                    statistics,
                )
            )
            if not isinstance(statistics, LayoutFIDStatistics)
            else statistics
        )
        return compute_layout_fid_from_statistics(candidate, reference)

    @staticmethod
    def _layout_kwargs(
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None,
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None,
        id2label: Mapping[int, str] | Mapping[str, str] | None,
        box_format: str,
        normalized: bool,
        canvas_size: tuple[int, int] | None,
    ) -> dict[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | Mapping[int, str]
        | Mapping[str, str]
        | str
        | bool
        | tuple[int, int]
        | None,
    ]:
        if layouts is not None and any(
            value is not None for value in (bbox, labels, mask)
        ):
            raise ValueError("Pass either layouts or explicit bbox/labels/mask")

        if layouts is not None:
            if isinstance(layouts, LayoutGenerationOutput):
                bbox = layouts.bbox
                labels = layouts.labels
                mask = layouts.mask
                id2label = layouts.id2label
            else:
                bbox = cast(
                    Float[torch.Tensor, "batch elements 4"]
                    | Float[np.ndarray, "batch elements 4"]
                    | Sequence[ArrayLikeInput],
                    layouts["bbox"],
                )
                labels = cast(
                    Int[torch.Tensor, "batch elements"]
                    | Int[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput],
                    layouts["labels"],
                )
                mask = cast(
                    Bool[torch.Tensor, "batch elements"]
                    | Bool[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layouts.get("mask"),
                )
                id2label = cast(
                    Mapping[int, str] | Mapping[str, str] | None,
                    layouts.get("id2label"),
                )
        if bbox is None or labels is None:
            raise ValueError("bbox and labels are required")

        return {
            "bbox": bbox,
            "labels": labels,
            "mask": mask,
            "id2label": id2label,
            "box_format": box_format,
            "normalized": normalized,
            "canvas_size": canvas_size,
        }

    @staticmethod
    def _load_reference_statistics(
        model_path: str | PathLike[str], model: LayoutFIDModel
    ) -> dict[str, LayoutFIDStatistics]:
        path = Path(model_path)
        if not path.exists():
            return {}
        stats: dict[str, LayoutFIDStatistics] = {}
        for split, relative in model.config.reference_stats.items():
            stats_path = path / relative
            if stats_path.exists():
                stats[split] = load_reference_statistics(stats_path)
        return stats

__init__

__init__(
    *,
    model: LayoutFIDModel,
    processor: LayoutFIDProcessor,
    reference_statistics: Mapping[str, LayoutFIDStatistics]
    | None = None,
    device: device | str | None = None,
) -> None

Create an evaluator.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def __init__(
    self,
    *,
    model: LayoutFIDModel,
    processor: LayoutFIDProcessor,
    reference_statistics: Mapping[str, LayoutFIDStatistics] | None = None,
    device: torch.device | str | None = None,
) -> None:
    """Create an evaluator."""
    self.model = model
    self.processor = processor
    self.reference_statistics = dict(reference_statistics or {})
    self.device = (
        torch.device(device) if device is not None else torch.device("cpu")
    )
    self.model.to(self.device)
    self.model.eval()

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    *,
    device: device | str | None = None,
    **kwargs: LayoutFIDLoadKwarg,
) -> LayoutFIDEvaluator

Load evaluator components from a local directory or Hub id.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | PathLike[str],
    *,
    device: torch.device | str | None = None,
    **kwargs: LayoutFIDLoadKwarg,
) -> LayoutFIDEvaluator:
    """Load evaluator components from a local directory or Hub id."""
    model = LayoutFIDModel.from_pretrained(pretrained_model_name_or_path, **kwargs)
    processor = LayoutFIDProcessor.from_pretrained(
        pretrained_model_name_or_path,
        **cast(dict[str, LayoutFIDConfigValue], kwargs),
    )
    stats = cls._load_reference_statistics(pretrained_model_name_or_path, model)
    return cls(
        model=model, processor=processor, reference_statistics=stats, device=device
    )

extract_features

extract_features(
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[Tensor, "batch elements 4"]
        | Float[ndarray, "batch elements 4"]
        | Int[Tensor, "batch elements"]
        | Int[ndarray, "batch elements"]
        | Bool[Tensor, "batch elements"]
        | Bool[ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    labels: Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str]
    | Mapping[str, str]
    | None = None,
    box_format: str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    batch_size: int = 512,
) -> Float[torch.Tensor, "batch channels"]

Extract features from public layout tensors.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
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
def extract_features(
    self,
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    labels: Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str] | Mapping[str, str] | None = None,
    box_format: str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    batch_size: int = 512,
) -> Float[torch.Tensor, "batch channels"]:
    """Extract features from public layout tensors."""
    layout_kwargs = self._layout_kwargs(
        layouts=layouts,
        bbox=bbox,
        labels=labels,
        mask=mask,
        id2label=id2label,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    batch = self.processor(
        bbox=cast(
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Sequence[ArrayLikeInput],
            layout_kwargs["bbox"],
        ),
        labels=cast(
            Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Sequence[ArrayLikeInput],
            layout_kwargs["labels"],
        ),
        mask=cast(
            Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Sequence[ArrayLikeInput]
            | None,
            layout_kwargs["mask"],
        ),
        id2label=cast(
            Mapping[int, str] | Mapping[str, str] | None,
            layout_kwargs["id2label"],
        ),
        box_format=cast(str, layout_kwargs["box_format"]),
        normalized=cast(bool, layout_kwargs["normalized"]),
        canvas_size=cast(tuple[int, int] | None, layout_kwargs["canvas_size"]),
        device=self.device,
    )
    outputs: list[Float[torch.Tensor, "batch channels"]] = []
    for start in range(0, batch.bbox.shape[0], batch_size):
        end = start + batch_size
        with torch.no_grad():
            outputs.append(
                self.model.extract_features(
                    bbox=batch.bbox[start:end],
                    labels=batch.labels[start:end],
                    padding_mask=batch.padding_mask[start:end],
                ).cpu()
            )
    return torch.cat(outputs, dim=0)

compute_statistics

compute_statistics(
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[Tensor, "batch elements 4"]
        | Float[ndarray, "batch elements 4"]
        | Int[Tensor, "batch elements"]
        | Int[ndarray, "batch elements"]
        | Bool[Tensor, "batch elements"]
        | Bool[ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[Tensor, "batch channels"]
    | Float[ndarray, "batch channels"]
    | None = None,
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> LayoutFIDStatistics

Compute candidate feature statistics.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
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
def compute_statistics(
    self,
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[torch.Tensor, "batch channels"]
    | Float[np.ndarray, "batch channels"]
    | None = None,
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> LayoutFIDStatistics:
    """Compute candidate feature statistics."""
    if features is not None and (layouts is not None or layout_kwargs):
        raise ValueError("Pass either features or layout inputs, not both")

    if features is None:
        features = self.extract_features(
            layouts=layouts,
            bbox=cast(
                Float[torch.Tensor, "batch elements 4"]
                | Float[np.ndarray, "batch elements 4"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs.get("bbox"),
            ),
            labels=cast(
                Int[torch.Tensor, "batch elements"]
                | Int[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs.get("labels"),
            ),
            mask=cast(
                Bool[torch.Tensor, "batch elements"]
                | Bool[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs.get("mask"),
            ),
            id2label=cast(
                Mapping[int, str] | Mapping[str, str] | None,
                layout_kwargs.get("id2label"),
            ),
            box_format=cast(str, layout_kwargs.get("box_format", "xywh")),
            normalized=cast(bool, layout_kwargs.get("normalized", True)),
            canvas_size=cast(
                tuple[int, int] | None, layout_kwargs.get("canvas_size")
            ),
            batch_size=cast(int, layout_kwargs.get("batch_size", 512)),
        )
    return compute_feature_statistics(
        features,
        dataset_name=self.model.config.dataset_name,
        source=self.model.config.source,
    )

compute_fid

compute_fid(
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[Tensor, "batch elements 4"]
        | Float[ndarray, "batch elements 4"]
        | Int[Tensor, "batch elements"]
        | Int[ndarray, "batch elements"]
        | Bool[Tensor, "batch elements"]
        | Bool[ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[Tensor, "batch channels"]
    | Float[ndarray, "batch channels"]
    | None = None,
    statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, ...]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ]
    | None = None,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, ...]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ]
    | None = None,
    reference_split: LayoutFIDStatsSplit | str = "test",
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> float

Compute layout FID against bundled or supplied reference statistics.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
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
def compute_fid(
    self,
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[torch.Tensor, "batch channels"]
    | Float[np.ndarray, "batch channels"]
    | None = None,
    statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
    ]
    | None = None,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
    ]
    | None = None,
    reference_split: LayoutFIDStatsSplit | str = "test",
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> float:
    """Compute layout FID against bundled or supplied reference statistics."""
    provided = sum(value is not None for value in (layouts, features, statistics))
    if provided + bool(layout_kwargs) == 0:
        raise ValueError("Pass candidate layouts, features, or statistics")

    if statistics is None:
        statistics = self.compute_statistics(
            layouts=layouts, features=features, **layout_kwargs
        )
    reference = (
        LayoutFIDStatistics.from_mapping(
            cast(
                Mapping[
                    str,
                    Float[np.ndarray, "..."]
                    | list[float]
                    | list[list[float]]
                    | str
                    | int
                    | None,
                ],
                reference_statistics,
            )
        )
        if reference_statistics is not None
        and not isinstance(reference_statistics, LayoutFIDStatistics)
        else reference_statistics
    )
    if reference is None:
        split = str(normalize_stats_split(reference_split))
        try:
            reference = self.reference_statistics[split]
        except KeyError as exc:
            raise ValueError(
                f"Reference statistics split is not loaded: {split}"
            ) from exc

    candidate = (
        LayoutFIDStatistics.from_mapping(
            cast(
                Mapping[
                    str,
                    Float[np.ndarray, "..."]
                    | list[float]
                    | list[list[float]]
                    | str
                    | int
                    | None,
                ],
                statistics,
            )
        )
        if not isinstance(statistics, LayoutFIDStatistics)
        else statistics
    )
    return compute_layout_fid_from_statistics(candidate, reference)

LayoutFIDBatch dataclass

Model-ready layout FID batch.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
24
25
26
27
28
29
30
31
32
@dataclass(frozen=True)
class LayoutFIDBatch:
    """Model-ready layout FID batch."""

    bbox: Float[torch.Tensor, "batch elements 4"]
    labels: Int[torch.Tensor, "batch elements"]
    padding_mask: Bool[torch.Tensor, "batch elements"]
    mask: Bool[torch.Tensor, "batch elements"]
    id2label: dict[int, str] | None

LayoutFIDProcessor

Bases: ProcessorMixin

Convert public layout tensors into layout FID model inputs.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
 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
class LayoutFIDProcessor(ProcessorMixin):
    """Convert public layout tensors into layout FID model inputs."""

    config_name = "processor_config.json"

    def __init__(self, config: LayoutFIDConfig) -> None:
        """Create a processor.

        Args:
            config: Explicit layout FID configuration.
        """
        super().__init__()
        self.config = config

    def __call__(
        self,
        *,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput],
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput],
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        id2label: Mapping[int, str] | Mapping[str, str] | None = None,
        box_format: BoxFormat | str = "xywh",
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        label_id_offset: int | None = None,
        max_length: int | None = None,
        pad_label_id: int | None = None,
        device: torch.device | str | None = None,
    ) -> LayoutFIDBatch:
        """Prepare model inputs from the repository public layout schema.

        Args:
            bbox: Public layout boxes.
            labels: Public dataset-local label ids.
            mask: Optional public valid-element mask.
            id2label: Optional public id-to-label metadata.
            box_format: Public input box format.
            normalized: Whether boxes are normalized to ``[0, 1]``.
            canvas_size: Pixel canvas size required when ``normalized=False``.
            label_id_offset: Optional parity/debug label-offset override.
            max_length: Optional maximum sequence length override.
            pad_label_id: Optional padded-position model label id.
            device: Target torch device.

        Returns:
            A ``LayoutFIDBatch`` with ``padding_mask=True`` for padded elements.

        Raises:
            ValueError: If metadata or tensor shapes are inconsistent.

        Examples:
            >>> from layout_fid import LayoutFIDConfig, LayoutFIDProcessor
            >>> cfg = LayoutFIDConfig(
            ...     dataset_name="publaynet", architecture="layoutnet",
            ...     source="layoutflow", num_public_labels=5,
            ...     num_label_embeddings=6, max_length=2,
            ... )
            >>> batch = LayoutFIDProcessor(cfg)(
            ...     bbox=[[[0.5, 0.5, 0.2, 0.2]]], labels=[[0]]
            ... )
            >>> batch.padding_mask.tolist()
            [[False, True]]
        """
        target_device = (
            torch.device(device) if device is not None else torch.device("cpu")
        )
        bbox_t, labels_t, mask_t = prepare_layout_tensors(
            bbox=bbox,
            labels=labels,
            mask=mask,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        bbox_t = bbox_t.to(device=target_device)
        labels_t = labels_t.to(device=target_device)
        mask_t = mask_t.to(device=target_device)
        max_len = max_length if max_length is not None else self.config.max_length
        pad_id = pad_label_id if pad_label_id is not None else self.config.pad_label_id
        bbox_t = self._pad_tensor(bbox_t, max_len, 0.0)
        labels_t = self._pad_tensor(labels_t, max_len, pad_id)
        mask_t = self._pad_tensor(mask_t, max_len, False)
        model_bbox = bbox_t
        if self.config.bbox_format_for_model == "ltrb":
            model_bbox = xywh_to_ltrb(model_bbox)
        offset = (
            label_id_offset
            if label_id_offset is not None
            else self.config.label_id_offset
        )
        model_labels = labels_t + offset
        model_labels = torch.where(
            mask_t, model_labels, torch.full_like(model_labels, pad_id)
        )
        if model_labels[mask_t].numel() and (
            int(model_labels[mask_t].min()) < 0
            or int(model_labels[mask_t].max()) >= self.config.num_label_embeddings
        ):
            raise ValueError("labels after label_id_offset exceed embedding table")

        normalized_id2label = self._normalize_id2label(id2label)
        if (
            normalized_id2label is not None
            and normalized_id2label != self.config.id2label
        ):
            raise ValueError("id2label does not match the evaluator config")

        return LayoutFIDBatch(
            bbox=model_bbox * mask_t.unsqueeze(-1),
            labels=model_labels,
            padding_mask=~mask_t,
            mask=mask_t,
            id2label=normalized_id2label,
        )

    def save_pretrained(  # ty: ignore[invalid-method-override]
        self, save_directory: str | os.PathLike[str]
    ) -> tuple[str]:
        """Save processor metadata.

        Args:
            save_directory: Directory receiving ``processor_config.json``.

        Returns:
            Tuple containing the saved config path.
        """
        import json
        import os

        os.makedirs(save_directory, exist_ok=True)
        path = os.path.join(save_directory, self.config_name)
        with open(path, "w", encoding="utf-8") as file_obj:
            json.dump({"config_class": self.config.__class__.__name__}, file_obj)
            file_obj.write("\n")
        return (path,)

    @classmethod
    def from_pretrained(  # ty: ignore[invalid-method-override]
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        **kwargs: LayoutFIDConfigValue,
    ) -> "LayoutFIDProcessor":
        """Load a processor from a saved model directory.

        Args:
            pretrained_model_name_or_path: Local path or Hub id.
            kwargs: Extra config-loading keyword arguments.

        Returns:
            Loaded processor bound to the model config.
        """
        config = LayoutFIDConfig.from_pretrained(
            pretrained_model_name_or_path,
            **kwargs,  # ty: ignore[invalid-argument-type]
        )
        return cls(config=config)

    @staticmethod
    def _normalize_id2label(
        id2label: Mapping[int, str] | Mapping[str, str] | None,
    ) -> dict[int, str] | None:
        if id2label is None:
            return None
        return {int(key): value for key, value in id2label.items()}

    @staticmethod
    def _pad_tensor(
        tensor: Shaped[torch.Tensor, "batch elements ..."],
        max_length: int,
        value: float | int | bool,
    ) -> Shaped[torch.Tensor, "batch max_elements ..."]:
        if tensor.shape[1] > max_length:
            return tensor[:, :max_length]
        if tensor.shape[1] == max_length:
            return tensor
        pad_shape = (tensor.shape[0], max_length - tensor.shape[1], *tensor.shape[2:])
        pad = torch.full(pad_shape, value, dtype=tensor.dtype, device=tensor.device)
        return torch.cat([tensor, pad], dim=1)

__init__

__init__(config: LayoutFIDConfig) -> None

Create a processor.

Parameters:

Name Type Description Default
config LayoutFIDConfig

Explicit layout FID configuration.

required
Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
40
41
42
43
44
45
46
47
def __init__(self, config: LayoutFIDConfig) -> None:
    """Create a processor.

    Args:
        config: Explicit layout FID configuration.
    """
    super().__init__()
    self.config = config

__call__

__call__(
    *,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput],
    labels: Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput],
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str]
    | Mapping[str, str]
    | None = None,
    box_format: BoxFormat | str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    label_id_offset: int | None = None,
    max_length: int | None = None,
    pad_label_id: int | None = None,
    device: device | str | None = None,
) -> LayoutFIDBatch

Prepare model inputs from the repository public layout schema.

Parameters:

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

Public layout boxes.

required
labels Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | Sequence[ArrayLikeInput]

Public dataset-local label ids.

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

Optional public valid-element mask.

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

Optional public id-to-label metadata.

None
box_format BoxFormat | str

Public input box format.

'xywh'
normalized bool

Whether boxes are normalized to [0, 1].

True
canvas_size tuple[int, int] | None

Pixel canvas size required when normalized=False.

None
label_id_offset int | None

Optional parity/debug label-offset override.

None
max_length int | None

Optional maximum sequence length override.

None
pad_label_id int | None

Optional padded-position model label id.

None
device device | str | None

Target torch device.

None

Returns:

Type Description
LayoutFIDBatch

A LayoutFIDBatch with padding_mask=True for padded elements.

Raises:

Type Description
ValueError

If metadata or tensor shapes are inconsistent.

Examples:

>>> from layout_fid import LayoutFIDConfig, LayoutFIDProcessor
>>> cfg = LayoutFIDConfig(
...     dataset_name="publaynet", architecture="layoutnet",
...     source="layoutflow", num_public_labels=5,
...     num_label_embeddings=6, max_length=2,
... )
>>> batch = LayoutFIDProcessor(cfg)(
...     bbox=[[[0.5, 0.5, 0.2, 0.2]]], labels=[[0]]
... )
>>> batch.padding_mask.tolist()
[[False, True]]
Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
 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
def __call__(
    self,
    *,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput],
    labels: Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput],
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str] | Mapping[str, str] | None = None,
    box_format: BoxFormat | str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    label_id_offset: int | None = None,
    max_length: int | None = None,
    pad_label_id: int | None = None,
    device: torch.device | str | None = None,
) -> LayoutFIDBatch:
    """Prepare model inputs from the repository public layout schema.

    Args:
        bbox: Public layout boxes.
        labels: Public dataset-local label ids.
        mask: Optional public valid-element mask.
        id2label: Optional public id-to-label metadata.
        box_format: Public input box format.
        normalized: Whether boxes are normalized to ``[0, 1]``.
        canvas_size: Pixel canvas size required when ``normalized=False``.
        label_id_offset: Optional parity/debug label-offset override.
        max_length: Optional maximum sequence length override.
        pad_label_id: Optional padded-position model label id.
        device: Target torch device.

    Returns:
        A ``LayoutFIDBatch`` with ``padding_mask=True`` for padded elements.

    Raises:
        ValueError: If metadata or tensor shapes are inconsistent.

    Examples:
        >>> from layout_fid import LayoutFIDConfig, LayoutFIDProcessor
        >>> cfg = LayoutFIDConfig(
        ...     dataset_name="publaynet", architecture="layoutnet",
        ...     source="layoutflow", num_public_labels=5,
        ...     num_label_embeddings=6, max_length=2,
        ... )
        >>> batch = LayoutFIDProcessor(cfg)(
        ...     bbox=[[[0.5, 0.5, 0.2, 0.2]]], labels=[[0]]
        ... )
        >>> batch.padding_mask.tolist()
        [[False, True]]
    """
    target_device = (
        torch.device(device) if device is not None else torch.device("cpu")
    )
    bbox_t, labels_t, mask_t = prepare_layout_tensors(
        bbox=bbox,
        labels=labels,
        mask=mask,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    bbox_t = bbox_t.to(device=target_device)
    labels_t = labels_t.to(device=target_device)
    mask_t = mask_t.to(device=target_device)
    max_len = max_length if max_length is not None else self.config.max_length
    pad_id = pad_label_id if pad_label_id is not None else self.config.pad_label_id
    bbox_t = self._pad_tensor(bbox_t, max_len, 0.0)
    labels_t = self._pad_tensor(labels_t, max_len, pad_id)
    mask_t = self._pad_tensor(mask_t, max_len, False)
    model_bbox = bbox_t
    if self.config.bbox_format_for_model == "ltrb":
        model_bbox = xywh_to_ltrb(model_bbox)
    offset = (
        label_id_offset
        if label_id_offset is not None
        else self.config.label_id_offset
    )
    model_labels = labels_t + offset
    model_labels = torch.where(
        mask_t, model_labels, torch.full_like(model_labels, pad_id)
    )
    if model_labels[mask_t].numel() and (
        int(model_labels[mask_t].min()) < 0
        or int(model_labels[mask_t].max()) >= self.config.num_label_embeddings
    ):
        raise ValueError("labels after label_id_offset exceed embedding table")

    normalized_id2label = self._normalize_id2label(id2label)
    if (
        normalized_id2label is not None
        and normalized_id2label != self.config.id2label
    ):
        raise ValueError("id2label does not match the evaluator config")

    return LayoutFIDBatch(
        bbox=model_bbox * mask_t.unsqueeze(-1),
        labels=model_labels,
        padding_mask=~mask_t,
        mask=mask_t,
        id2label=normalized_id2label,
    )

save_pretrained

save_pretrained(
    save_directory: str | PathLike[str],
) -> tuple[str]

Save processor metadata.

Parameters:

Name Type Description Default
save_directory str | PathLike[str]

Directory receiving processor_config.json.

required

Returns:

Type Description
tuple[str]

Tuple containing the saved config path.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def save_pretrained(  # ty: ignore[invalid-method-override]
    self, save_directory: str | os.PathLike[str]
) -> tuple[str]:
    """Save processor metadata.

    Args:
        save_directory: Directory receiving ``processor_config.json``.

    Returns:
        Tuple containing the saved config path.
    """
    import json
    import os

    os.makedirs(save_directory, exist_ok=True)
    path = os.path.join(save_directory, self.config_name)
    with open(path, "w", encoding="utf-8") as file_obj:
        json.dump({"config_class": self.config.__class__.__name__}, file_obj)
        file_obj.write("\n")
    return (path,)

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    **kwargs: LayoutFIDConfigValue,
) -> "LayoutFIDProcessor"

Load a processor from a saved model directory.

Parameters:

Name Type Description Default
pretrained_model_name_or_path str | PathLike[str]

Local path or Hub id.

required
kwargs LayoutFIDConfigValue

Extra config-loading keyword arguments.

{}

Returns:

Type Description
'LayoutFIDProcessor'

Loaded processor bound to the model config.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
@classmethod
def from_pretrained(  # ty: ignore[invalid-method-override]
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    **kwargs: LayoutFIDConfigValue,
) -> "LayoutFIDProcessor":
    """Load a processor from a saved model directory.

    Args:
        pretrained_model_name_or_path: Local path or Hub id.
        kwargs: Extra config-loading keyword arguments.

    Returns:
        Loaded processor bound to the model config.
    """
    config = LayoutFIDConfig.from_pretrained(
        pretrained_model_name_or_path,
        **kwargs,  # ty: ignore[invalid-argument-type]
    )
    return cls(config=config)

normalize_architecture

normalize_architecture(
    architecture: LayoutFIDArchitecture | str,
) -> LayoutFIDArchitecture

Normalize a public architecture value.

Parameters:

Name Type Description Default
architecture LayoutFIDArchitecture | str

Architecture enum or string value.

required

Returns:

Type Description
LayoutFIDArchitecture

Normalized architecture enum.

Raises:

Type Description
ValueError

If the architecture is unsupported.

Examples:

>>> str(normalize_architecture("layoutnet"))
'layoutnet'
Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def normalize_architecture(
    architecture: LayoutFIDArchitecture | str,
) -> LayoutFIDArchitecture:
    """Normalize a public architecture value.

    Args:
        architecture: Architecture enum or string value.

    Returns:
        Normalized architecture enum.

    Raises:
        ValueError: If the architecture is unsupported.

    Examples:
        >>> str(normalize_architecture("layoutnet"))
        'layoutnet'
    """
    if isinstance(architecture, LayoutFIDArchitecture):
        return architecture
    try:
        return LayoutFIDArchitecture(architecture)
    except ValueError as exc:
        raise ValueError(f"Unsupported architecture: {architecture}") from exc

normalize_source

normalize_source(
    source: LayoutFIDSource | str,
) -> LayoutFIDSource

Normalize a public artifact-source value.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
77
78
79
80
81
82
83
84
def normalize_source(source: LayoutFIDSource | str) -> LayoutFIDSource:
    """Normalize a public artifact-source value."""
    if isinstance(source, LayoutFIDSource):
        return source
    try:
        return LayoutFIDSource(source)
    except ValueError as exc:
        raise ValueError(f"Unsupported source: {source}") from exc

normalize_stats_split

normalize_stats_split(
    split: LayoutFIDStatsSplit | str,
) -> LayoutFIDStatsSplit

Normalize a public reference-statistics split value.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
87
88
89
90
91
92
93
94
def normalize_stats_split(split: LayoutFIDStatsSplit | str) -> LayoutFIDStatsSplit:
    """Normalize a public reference-statistics split value."""
    if isinstance(split, LayoutFIDStatsSplit):
        return split
    try:
        return LayoutFIDStatsSplit(split)
    except ValueError as exc:
        raise ValueError(f"Unsupported reference statistics split: {split}") from exc

calculate_frechet_distance

calculate_frechet_distance(
    mu1: Float[ndarray, "channels"],
    sigma1: Float[ndarray, "channels channels"],
    mu2: Float[ndarray, "channels"],
    sigma2: Float[ndarray, "channels channels"],
    *,
    eps: float = 1e-06,
) -> float

Compute the Frechet distance between two Gaussian distributions.

Parameters:

Name Type Description Default
mu1 Float[ndarray, 'channels']

First mean vector.

required
sigma1 Float[ndarray, 'channels channels']

First covariance matrix.

required
mu2 Float[ndarray, 'channels']

Second mean vector.

required
sigma2 Float[ndarray, 'channels channels']

Second covariance matrix.

required
eps float

Diagonal offset used when covariance products are nearly singular.

1e-06

Returns:

Type Description
float

Frechet distance as a Python float.

Raises:

Type Description
ValueError

If dimensions are inconsistent.

Examples:

>>> mu = np.zeros(2)
>>> sigma = np.eye(2)
>>> calculate_frechet_distance(mu, sigma, mu, sigma)
0.0
Source code in models/layout-fid/src/layout_fid/evaluation.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def calculate_frechet_distance(
    mu1: Float[np.ndarray, "channels"],
    sigma1: Float[np.ndarray, "channels channels"],
    mu2: Float[np.ndarray, "channels"],
    sigma2: Float[np.ndarray, "channels channels"],
    *,
    eps: float = 1e-6,
) -> float:
    """Compute the Frechet distance between two Gaussian distributions.

    Args:
        mu1: First mean vector.
        sigma1: First covariance matrix.
        mu2: Second mean vector.
        sigma2: Second covariance matrix.
        eps: Diagonal offset used when covariance products are nearly singular.

    Returns:
        Frechet distance as a Python float.

    Raises:
        ValueError: If dimensions are inconsistent.

    Examples:
        >>> mu = np.zeros(2)
        >>> sigma = np.eye(2)
        >>> calculate_frechet_distance(mu, sigma, mu, sigma)
        0.0
    """
    from scipy import linalg

    mu1 = np.atleast_1d(mu1).astype(np.float64)
    mu2 = np.atleast_1d(mu2).astype(np.float64)
    sigma1 = np.atleast_2d(sigma1).astype(np.float64)
    sigma2 = np.atleast_2d(sigma2).astype(np.float64)
    if mu1.shape != mu2.shape:
        raise ValueError("mean vectors must have matching dimensions")

    if sigma1.shape != sigma2.shape:
        raise ValueError("covariance matrices must have matching dimensions")

    diff = mu1 - mu2
    covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)
    if not np.isfinite(covmean).all():
        offset = np.eye(sigma1.shape[0], dtype=np.float64) * eps
        covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))
    if np.iscomplexobj(covmean):
        covmean = covmean.real
    value = diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * np.trace(covmean)
    return float(max(value, 0.0))

compute_feature_statistics

compute_feature_statistics(
    features: Float[Tensor, "batch channels"]
    | Float[ndarray, "batch channels"],
    *,
    split: str = "candidate",
    dataset_name: str = "",
    source: str = "",
) -> LayoutFIDStatistics

Compute float64 mean and covariance from feature vectors.

Parameters:

Name Type Description Default
features Float[Tensor, 'batch channels'] | Float[ndarray, 'batch channels']

Feature matrix shaped (samples, feature_dim).

required
split str

Split label stored in the returned metadata.

'candidate'
dataset_name str

Dataset metadata.

''
source str

Source-family metadata.

''

Returns:

Type Description
LayoutFIDStatistics

Feature statistics with NumPy float64 arrays.

Raises:

Type Description
ValueError

If fewer than two feature vectors are provided.

Examples:

>>> stats = compute_feature_statistics(np.eye(3, dtype=np.float32))
>>> stats.sigma.shape
(3, 3)
Source code in models/layout-fid/src/layout_fid/evaluation.py
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def compute_feature_statistics(
    features: Float[torch.Tensor, "batch channels"]
    | Float[np.ndarray, "batch channels"],
    *,
    split: str = "candidate",
    dataset_name: str = "",
    source: str = "",
) -> LayoutFIDStatistics:
    """Compute float64 mean and covariance from feature vectors.

    Args:
        features: Feature matrix shaped ``(samples, feature_dim)``.
        split: Split label stored in the returned metadata.
        dataset_name: Dataset metadata.
        source: Source-family metadata.

    Returns:
        Feature statistics with NumPy ``float64`` arrays.

    Raises:
        ValueError: If fewer than two feature vectors are provided.

    Examples:
        >>> stats = compute_feature_statistics(np.eye(3, dtype=np.float32))
        >>> stats.sigma.shape
        (3, 3)
    """
    array = _as_numpy(features)
    if array.ndim != 2 or array.shape[0] < 2:
        raise ValueError(
            "features must have shape (samples, channels) with samples >= 2"
        )

    array = array.astype(np.float64, copy=False)
    return LayoutFIDStatistics(
        mu=np.mean(array, axis=0),
        sigma=np.cov(array, rowvar=False),
        split=split,
        dataset_name=dataset_name,
        source=source,
        feature_dim=array.shape[1],
        num_samples=array.shape[0],
    )

compute_layout_fid

compute_layout_fid(
    model: "LayoutFIDModel",
    processor: "LayoutFIDProcessor",
    *,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
    batch_size: int = 512,
    **layout_kwargs: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | Mapping[int, str]
    | Mapping[str, str]
    | str
    | bool
    | tuple[int, int]
    | int
    | device
    | None,
) -> float

Compute layout FID directly from model, processor, and layout tensors.

Source code in models/layout-fid/src/layout_fid/evaluation.py
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
def compute_layout_fid(
    model: "LayoutFIDModel",
    processor: "LayoutFIDProcessor",
    *,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, "..."] | list[float] | list[list[float]] | str | int | None,
    ],
    batch_size: int = 512,
    **layout_kwargs: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | Mapping[int, str]
    | Mapping[str, str]
    | str
    | bool
    | tuple[int, int]
    | int
    | torch.device
    | None,
) -> float:
    """Compute layout FID directly from model, processor, and layout tensors."""
    features: list[Float[torch.Tensor, "batch channels"]] = []
    batch = processor(**layout_kwargs)  # ty: ignore[invalid-argument-type]
    for start in range(0, batch.bbox.shape[0], batch_size):
        end = start + batch_size
        with torch.no_grad():
            features.append(
                model.extract_features(
                    bbox=batch.bbox[start:end],
                    labels=batch.labels[start:end],
                    padding_mask=batch.padding_mask[start:end],
                ).cpu()
            )
    candidate = compute_feature_statistics(
        torch.cat(features, dim=0),
        dataset_name=model.config.dataset_name,
        source=model.config.source,
    )
    return compute_layout_fid_from_statistics(candidate, reference_statistics)

compute_layout_fid_from_statistics

compute_layout_fid_from_statistics(
    candidate: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
    reference: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
) -> float

Compute layout FID from two statistics objects.

Source code in models/layout-fid/src/layout_fid/evaluation.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def compute_layout_fid_from_statistics(
    candidate: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, "..."] | list[float] | list[list[float]] | str | int | None,
    ],
    reference: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, "..."] | list[float] | list[list[float]] | str | int | None,
    ],
) -> float:
    """Compute layout FID from two statistics objects."""
    candidate_stats = _coerce_statistics(candidate)
    reference_stats = _coerce_statistics(reference)
    return calculate_frechet_distance(
        candidate_stats.mu,
        candidate_stats.sigma,
        reference_stats.mu,
        reference_stats.sigma,
    )

load_reference_statistics

load_reference_statistics(
    path: str | PathLike[str],
) -> LayoutFIDStatistics

Load reference_stats/{split}.npz statistics.

Parameters:

Name Type Description Default
path str | PathLike[str]

Statistics file path.

required

Returns:

Type Description
LayoutFIDStatistics

Loaded layout FID statistics.

Source code in models/layout-fid/src/layout_fid/evaluation.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def load_reference_statistics(path: str | PathLike[str]) -> LayoutFIDStatistics:
    """Load ``reference_stats/{split}.npz`` statistics.

    Args:
        path: Statistics file path.

    Returns:
        Loaded layout FID statistics.
    """
    data = np.load(path, allow_pickle=False)
    split = str(data["split"].item()) if "split" in data else Path(path).stem
    dataset_name = str(data["dataset_name"].item()) if "dataset_name" in data else ""
    source = str(data["source"].item()) if "source" in data else ""
    mu = data["mu"].astype(np.float64, copy=False)
    sigma = data["sigma"].astype(np.float64, copy=False)
    num_samples = int(data["num_samples"].item()) if "num_samples" in data else None
    return LayoutFIDStatistics(
        mu=mu,
        sigma=sigma,
        split=split,
        dataset_name=dataset_name,
        source=source,
        feature_dim=mu.shape[0],
        num_samples=num_samples,
    )

save_reference_statistics

save_reference_statistics(
    path: str | PathLike[str], stats: LayoutFIDStatistics
) -> None

Save reference statistics in package-local .npz format.

Source code in models/layout-fid/src/layout_fid/evaluation.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def save_reference_statistics(
    path: str | PathLike[str],
    stats: LayoutFIDStatistics,
) -> None:
    """Save reference statistics in package-local ``.npz`` format."""
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    np.savez(
        path,
        mu=stats.mu.astype(np.float64, copy=False),
        sigma=stats.sigma.astype(np.float64, copy=False),
        split=np.array(stats.split),
        dataset_name=np.array(stats.dataset_name),
        source=np.array(stats.source),
        feature_dim=np.array(stats.feature_dim),
        num_samples=np.array(-1 if stats.num_samples is None else stats.num_samples),
        statistics_kind=np.array("reference_real_distribution"),
    )

configuration_layout_fid

Configuration for layout FID feature encoders.

LayoutFIDArchitecture

Bases: StrEnum

Closed set of supported layout FID encoder architectures.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
19
20
21
22
23
class LayoutFIDArchitecture(StrEnum):
    """Closed set of supported layout FID encoder architectures."""

    fidnet_v3 = auto()
    layoutnet = auto()

LayoutFIDSource

Bases: StrEnum

Closed set of supported released artifact families.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
26
27
28
29
30
class LayoutFIDSource(StrEnum):
    """Closed set of supported released artifact families."""

    layoutdm = auto()
    layoutflow = auto()

LayoutFIDStatsSplit

Bases: StrEnum

Closed set of bundled reference-statistics splits.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
33
34
35
36
37
class LayoutFIDStatsSplit(StrEnum):
    """Closed set of bundled reference-statistics splits."""

    val = auto()
    test = auto()

LayoutFIDConfig

Bases: PretrainedConfig

Configuration saved with layout FID checkpoints.

Parameters:

Name Type Description Default
dataset_name DatasetName | str

Canonical layout dataset name.

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

Dataset-local id-to-label metadata.

None
architecture LayoutFIDArchitecture | str

Encoder architecture selected by the checkpoint.

required
source LayoutFIDSource | str

Released artifact family selected by the checkpoint.

required
num_public_labels int

Number of public dataset labels.

required
num_label_embeddings int

Number of model label embeddings.

required
max_length int

Maximum element count accepted by the checkpoint.

required
d_model int

Transformer hidden dimension.

256
nhead int

Number of attention heads.

4
num_layers int

Number of transformer encoder layers.

4
bbox_format_for_model BoxFormat | str

Internal bbox format consumed by the encoder.

'ltrb'
label_id_offset int

Offset applied before model label embedding lookup.

0
pad_label_id int

Label id used only in padded model tensor positions.

0
reference_stats dict[str, str] | None

Relative reference-statistics paths by split.

None
kwargs LayoutFIDConfigValue

Extra Hugging Face config fields.

{}

Raises:

Type Description
ValueError

If label counts or enum values are invalid.

Examples:

>>> cfg = LayoutFIDConfig(
...     dataset_name="publaynet",
...     architecture="layoutnet",
...     source="layoutflow",
...     num_public_labels=5,
...     num_label_embeddings=6,
...     max_length=20,
... )
>>> cfg.reference_stats["test"]
'reference_stats/test.npz'
Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
 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
class LayoutFIDConfig(PretrainedConfig):
    """Configuration saved with layout FID checkpoints.

    Args:
        dataset_name: Canonical layout dataset name.
        id2label: Dataset-local id-to-label metadata.
        architecture: Encoder architecture selected by the checkpoint.
        source: Released artifact family selected by the checkpoint.
        num_public_labels: Number of public dataset labels.
        num_label_embeddings: Number of model label embeddings.
        max_length: Maximum element count accepted by the checkpoint.
        d_model: Transformer hidden dimension.
        nhead: Number of attention heads.
        num_layers: Number of transformer encoder layers.
        bbox_format_for_model: Internal bbox format consumed by the encoder.
        label_id_offset: Offset applied before model label embedding lookup.
        pad_label_id: Label id used only in padded model tensor positions.
        reference_stats: Relative reference-statistics paths by split.
        kwargs: Extra Hugging Face config fields.

    Raises:
        ValueError: If label counts or enum values are invalid.

    Examples:
        >>> cfg = LayoutFIDConfig(
        ...     dataset_name="publaynet",
        ...     architecture="layoutnet",
        ...     source="layoutflow",
        ...     num_public_labels=5,
        ...     num_label_embeddings=6,
        ...     max_length=20,
        ... )
        >>> cfg.reference_stats["test"]
        'reference_stats/test.npz'
    """

    model_type = "layout-fid"
    has_no_defaults_at_init = True

    def __init__(
        self,
        *,
        dataset_name: DatasetName | str,
        id2label: Mapping[int, str] | Mapping[str, str] | None = None,
        architecture: LayoutFIDArchitecture | str,
        source: LayoutFIDSource | str,
        num_public_labels: int,
        num_label_embeddings: int,
        max_length: int,
        d_model: int = 256,
        nhead: int = 4,
        num_layers: int = 4,
        bbox_format_for_model: BoxFormat | str = "ltrb",
        label_id_offset: int = 0,
        pad_label_id: int = 0,
        reference_stats: dict[str, str] | None = None,
        **kwargs: LayoutFIDConfigValue,
    ) -> None:
        """Initialize a layout FID checkpoint configuration."""
        super().__init__(**kwargs)  # ty: ignore[invalid-argument-type]
        dataset = normalize_dataset_name(dataset_name)
        arch = normalize_architecture(architecture)
        src = normalize_source(source)
        box_format = normalize_box_format(bbox_format_for_model)
        if num_public_labels <= 0:
            raise ValueError("num_public_labels must be positive")

        if num_label_embeddings < num_public_labels:
            raise ValueError("num_label_embeddings must cover public labels")

        if max_length <= 0:
            raise ValueError("max_length must be positive")

        if pad_label_id < 0 or pad_label_id >= num_label_embeddings:
            raise ValueError("pad_label_id must be inside the embedding table")

        raw_id2label = id2label or id2label_for_dataset(dataset)
        self.dataset_name = str(dataset)
        self.id2label = {int(k): v for k, v in raw_id2label.items()}
        self.label2id = {label: idx for idx, label in self.id2label.items()}
        self.architecture = str(arch)
        self.source = str(src)

        self.num_public_labels = num_public_labels
        self.num_label_embeddings = num_label_embeddings
        self.max_length = max_length
        self.d_model = d_model
        self.nhead = nhead
        self.num_layers = num_layers

        self.bbox_format_for_model = str(box_format)
        self.label_id_offset = label_id_offset
        self.pad_label_id = pad_label_id
        self.reference_stats = dict(reference_stats or DEFAULT_REFERENCE_STATS)

    @property
    def feature_dim(self) -> int:
        """Return the layout FID feature dimension."""
        return self.d_model

    def _get_generation_parameters(self) -> dict[str, str]:
        """Return no generation parameters for this evaluator config."""
        return {}

feature_dim property

feature_dim: int

Return the layout FID feature dimension.

__init__

__init__(
    *,
    dataset_name: DatasetName | str,
    id2label: Mapping[int, str]
    | Mapping[str, str]
    | None = None,
    architecture: LayoutFIDArchitecture | str,
    source: LayoutFIDSource | str,
    num_public_labels: int,
    num_label_embeddings: int,
    max_length: int,
    d_model: int = 256,
    nhead: int = 4,
    num_layers: int = 4,
    bbox_format_for_model: BoxFormat | str = "ltrb",
    label_id_offset: int = 0,
    pad_label_id: int = 0,
    reference_stats: dict[str, str] | None = None,
    **kwargs: LayoutFIDConfigValue,
) -> None

Initialize a layout FID checkpoint configuration.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
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
def __init__(
    self,
    *,
    dataset_name: DatasetName | str,
    id2label: Mapping[int, str] | Mapping[str, str] | None = None,
    architecture: LayoutFIDArchitecture | str,
    source: LayoutFIDSource | str,
    num_public_labels: int,
    num_label_embeddings: int,
    max_length: int,
    d_model: int = 256,
    nhead: int = 4,
    num_layers: int = 4,
    bbox_format_for_model: BoxFormat | str = "ltrb",
    label_id_offset: int = 0,
    pad_label_id: int = 0,
    reference_stats: dict[str, str] | None = None,
    **kwargs: LayoutFIDConfigValue,
) -> None:
    """Initialize a layout FID checkpoint configuration."""
    super().__init__(**kwargs)  # ty: ignore[invalid-argument-type]
    dataset = normalize_dataset_name(dataset_name)
    arch = normalize_architecture(architecture)
    src = normalize_source(source)
    box_format = normalize_box_format(bbox_format_for_model)
    if num_public_labels <= 0:
        raise ValueError("num_public_labels must be positive")

    if num_label_embeddings < num_public_labels:
        raise ValueError("num_label_embeddings must cover public labels")

    if max_length <= 0:
        raise ValueError("max_length must be positive")

    if pad_label_id < 0 or pad_label_id >= num_label_embeddings:
        raise ValueError("pad_label_id must be inside the embedding table")

    raw_id2label = id2label or id2label_for_dataset(dataset)
    self.dataset_name = str(dataset)
    self.id2label = {int(k): v for k, v in raw_id2label.items()}
    self.label2id = {label: idx for idx, label in self.id2label.items()}
    self.architecture = str(arch)
    self.source = str(src)

    self.num_public_labels = num_public_labels
    self.num_label_embeddings = num_label_embeddings
    self.max_length = max_length
    self.d_model = d_model
    self.nhead = nhead
    self.num_layers = num_layers

    self.bbox_format_for_model = str(box_format)
    self.label_id_offset = label_id_offset
    self.pad_label_id = pad_label_id
    self.reference_stats = dict(reference_stats or DEFAULT_REFERENCE_STATS)

normalize_architecture

normalize_architecture(
    architecture: LayoutFIDArchitecture | str,
) -> LayoutFIDArchitecture

Normalize a public architecture value.

Parameters:

Name Type Description Default
architecture LayoutFIDArchitecture | str

Architecture enum or string value.

required

Returns:

Type Description
LayoutFIDArchitecture

Normalized architecture enum.

Raises:

Type Description
ValueError

If the architecture is unsupported.

Examples:

>>> str(normalize_architecture("layoutnet"))
'layoutnet'
Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def normalize_architecture(
    architecture: LayoutFIDArchitecture | str,
) -> LayoutFIDArchitecture:
    """Normalize a public architecture value.

    Args:
        architecture: Architecture enum or string value.

    Returns:
        Normalized architecture enum.

    Raises:
        ValueError: If the architecture is unsupported.

    Examples:
        >>> str(normalize_architecture("layoutnet"))
        'layoutnet'
    """
    if isinstance(architecture, LayoutFIDArchitecture):
        return architecture
    try:
        return LayoutFIDArchitecture(architecture)
    except ValueError as exc:
        raise ValueError(f"Unsupported architecture: {architecture}") from exc

normalize_source

normalize_source(
    source: LayoutFIDSource | str,
) -> LayoutFIDSource

Normalize a public artifact-source value.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
77
78
79
80
81
82
83
84
def normalize_source(source: LayoutFIDSource | str) -> LayoutFIDSource:
    """Normalize a public artifact-source value."""
    if isinstance(source, LayoutFIDSource):
        return source
    try:
        return LayoutFIDSource(source)
    except ValueError as exc:
        raise ValueError(f"Unsupported source: {source}") from exc

normalize_stats_split

normalize_stats_split(
    split: LayoutFIDStatsSplit | str,
) -> LayoutFIDStatsSplit

Normalize a public reference-statistics split value.

Source code in models/layout-fid/src/layout_fid/configuration_layout_fid.py
87
88
89
90
91
92
93
94
def normalize_stats_split(split: LayoutFIDStatsSplit | str) -> LayoutFIDStatsSplit:
    """Normalize a public reference-statistics split value."""
    if isinstance(split, LayoutFIDStatsSplit):
        return split
    try:
        return LayoutFIDStatsSplit(split)
    except ValueError as exc:
        raise ValueError(f"Unsupported reference statistics split: {split}") from exc

conversion

Conversion helpers for layout FID checkpoints and statistics.

LayoutFlowDatasetSpec

Bases: TypedDict

Conversion metadata for one LayoutFlow dataset.

Source code in models/layout-fid/src/layout_fid/conversion.py
22
23
24
25
26
27
28
class LayoutFlowDatasetSpec(TypedDict):
    """Conversion metadata for one LayoutFlow dataset."""

    num_public_labels: int
    num_label_embeddings: int
    max_length: int
    stats_suffix: str

convert_layoutflow_checkpoint

convert_layoutflow_checkpoint(
    *,
    checkpoint_path: str | PathLike[str],
    output_dir: str | PathLike[str],
    dataset_name: str,
    stats_paths: Mapping[str, str | PathLike[str]]
    | None = None,
) -> LayoutFIDConfig

Convert a LayoutFlow-style LayoutNet checkpoint directory.

Source code in models/layout-fid/src/layout_fid/conversion.py
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
def convert_layoutflow_checkpoint(
    *,
    checkpoint_path: str | PathLike[str],
    output_dir: str | PathLike[str],
    dataset_name: str,
    stats_paths: Mapping[str, str | PathLike[str]] | None = None,
) -> LayoutFIDConfig:
    """Convert a LayoutFlow-style LayoutNet checkpoint directory."""
    spec = _layoutflow_spec(dataset_name)
    state_dict = load_checkpoint_state_dict(checkpoint_path)
    state_dict = strip_module_prefix(state_dict)
    config = LayoutFIDConfig(
        dataset_name=dataset_name,
        id2label=id2label_for_dataset(dataset_name),
        architecture="layoutnet",
        source="layoutflow",
        num_public_labels=int(spec["num_public_labels"]),
        num_label_embeddings=int(state_dict["emb_label.weight"].shape[0]),
        max_length=int(state_dict["pos_token"].shape[0]),
        bbox_format_for_model="ltrb",
        label_id_offset=0,
        pad_label_id=0,
    )
    validate_state_dict_shapes(state_dict, config)
    model = LayoutFIDModel(config)
    model.load_state_dict(state_dict)
    output = Path(output_dir)
    model.save_pretrained(output, safe_serialization=True)
    LayoutFIDProcessor(config).save_pretrained(output)
    if stats_paths:
        for split, path in stats_paths.items():
            stats = load_musig_statistics(
                path,
                split=split,
                dataset_name=config.dataset_name,
                source=config.source,
            )
            save_reference_statistics(output / f"reference_stats/{split}.npz", stats)
    return config

convert_layoutdm_fidnet_v3_checkpoint

convert_layoutdm_fidnet_v3_checkpoint(
    *,
    checkpoint_path: str | PathLike[str],
    output_dir: str | PathLike[str],
    dataset_name: str,
    num_public_labels: int,
    max_length: int,
) -> LayoutFIDConfig

Convert a LayoutDM FIDNetV3 checkpoint when assets are available.

Source code in models/layout-fid/src/layout_fid/conversion.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def convert_layoutdm_fidnet_v3_checkpoint(
    *,
    checkpoint_path: str | PathLike[str],
    output_dir: str | PathLike[str],
    dataset_name: str,
    num_public_labels: int,
    max_length: int,
) -> LayoutFIDConfig:
    """Convert a LayoutDM FIDNetV3 checkpoint when assets are available."""
    state_dict = load_checkpoint_state_dict(
        checkpoint_path, state_dict_key="state_dict"
    )
    config = LayoutFIDConfig(
        dataset_name=dataset_name,
        id2label=id2label_for_dataset(dataset_name),
        architecture="fidnet_v3",
        source="layoutdm",
        num_public_labels=num_public_labels,
        num_label_embeddings=int(state_dict["emb_label.weight"].shape[0]),
        max_length=max_length,
        bbox_format_for_model="xywh",
        label_id_offset=0,
        pad_label_id=0,
    )
    validate_state_dict_shapes(state_dict, config)
    model = LayoutFIDModel(config)
    model.load_state_dict(state_dict)
    output = Path(output_dir)
    model.save_pretrained(output, safe_serialization=True)
    LayoutFIDProcessor(config).save_pretrained(output)
    return config

load_musig_statistics

load_musig_statistics(
    path: str | PathLike[str],
    *,
    split: str,
    dataset_name: str,
    source: str,
) -> LayoutFIDStatistics

Convert a stacked [mu; sigma] tensor into typed statistics.

Source code in models/layout-fid/src/layout_fid/conversion.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def load_musig_statistics(
    path: str | PathLike[str],
    *,
    split: str,
    dataset_name: str,
    source: str,
) -> LayoutFIDStatistics:
    """Convert a stacked ``[mu; sigma]`` tensor into typed statistics."""
    tensor = torch.load(path, map_location="cpu", weights_only=False)
    array = tensor.detach().cpu().numpy().astype(np.float64, copy=False)
    return LayoutFIDStatistics(
        mu=array[0],
        sigma=array[1:],
        split=split,
        dataset_name=dataset_name,
        source=source,
        feature_dim=array.shape[1],
        num_samples=None,
    )

load_checkpoint_state_dict

load_checkpoint_state_dict(
    path: str | PathLike[str],
    *,
    state_dict_key: str | None = None,
) -> dict[str, Shaped[torch.Tensor, "..."]]

Load a torch checkpoint state dict.

Source code in models/layout-fid/src/layout_fid/conversion.py
142
143
144
145
146
147
148
149
150
151
def load_checkpoint_state_dict(
    path: str | PathLike[str],
    *,
    state_dict_key: str | None = None,
) -> dict[str, Shaped[torch.Tensor, "..."]]:
    """Load a torch checkpoint state dict."""
    checkpoint = torch.load(path, map_location="cpu", weights_only=False)
    if state_dict_key is not None:
        checkpoint = checkpoint[state_dict_key]
    return {str(key): value for key, value in checkpoint.items()}

strip_module_prefix

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

Strip optional module. prefixes from checkpoint keys.

Source code in models/layout-fid/src/layout_fid/conversion.py
154
155
156
157
158
def strip_module_prefix(
    state_dict: Mapping[str, Shaped[torch.Tensor, "..."]],
) -> dict[str, Shaped[torch.Tensor, "..."]]:
    """Strip optional ``module.`` prefixes from checkpoint keys."""
    return {key.removeprefix("module."): value for key, value in state_dict.items()}

validate_state_dict_shapes

validate_state_dict_shapes(
    state_dict: Mapping[str, Shaped[Tensor, "..."]],
    config: LayoutFIDConfig,
) -> None

Validate checkpoint tensor shapes before writing artifacts.

Source code in models/layout-fid/src/layout_fid/conversion.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def validate_state_dict_shapes(
    state_dict: Mapping[str, Shaped[torch.Tensor, "..."]], config: LayoutFIDConfig
) -> None:
    """Validate checkpoint tensor shapes before writing artifacts."""
    expected = {
        "emb_label.weight": (config.num_label_embeddings, config.d_model),
        "fc_bbox.weight": (config.d_model, 4),
        "enc_fc_in.weight": (config.d_model, config.d_model * 2),
        "fc_out_cls.weight": (config.num_label_embeddings, config.d_model),
        "fc_out_bbox.weight": (4, config.d_model),
        "pos_token": (config.max_length, 1, config.d_model),
    }
    missing = sorted(set(expected) - set(state_dict))
    if missing:
        raise ValueError(f"checkpoint is missing expected keys: {missing}")

    mismatched = {
        key: (tuple(state_dict[key].shape), shape)
        for key, shape in expected.items()
        if tuple(state_dict[key].shape) != shape
    }
    if mismatched:
        raise ValueError(f"checkpoint tensor shapes do not match config: {mismatched}")

evaluation

Statistics and Frechet-distance helpers for layout FID.

LayoutFIDStatistics dataclass

Feature distribution statistics used by layout FID.

Source code in models/layout-fid/src/layout_fid/evaluation.py
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
@dataclass(frozen=True)
class LayoutFIDStatistics:
    """Feature distribution statistics used by layout FID."""

    mu: Float[np.ndarray, "channels"]
    sigma: Float[np.ndarray, "channels channels"]
    split: str
    dataset_name: str
    source: str
    feature_dim: int
    num_samples: int | None = None

    @classmethod
    def from_mapping(
        cls,
        values: Mapping[
            str,
            Float[np.ndarray, "..."]
            | list[float]
            | list[list[float]]
            | str
            | int
            | None,
        ],
    ) -> "LayoutFIDStatistics":
        """Create statistics from a mapping."""
        mu = np.asarray(values["mu"], dtype=np.float64)
        sigma = np.asarray(values["sigma"], dtype=np.float64)
        return cls(
            mu=mu,
            sigma=sigma,
            split=str(values.get("split", "candidate")),
            dataset_name=str(values.get("dataset_name", "")),
            source=str(values.get("source", "")),
            feature_dim=int(cast(int | str, values.get("feature_dim", mu.shape[0]))),
            num_samples=(
                None
                if values.get("num_samples") is None
                else int(cast(int | str, values["num_samples"]))
            ),
        )

from_mapping classmethod

from_mapping(
    values: Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
) -> "LayoutFIDStatistics"

Create statistics from a mapping.

Source code in models/layout-fid/src/layout_fid/evaluation.py
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
@classmethod
def from_mapping(
    cls,
    values: Mapping[
        str,
        Float[np.ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
) -> "LayoutFIDStatistics":
    """Create statistics from a mapping."""
    mu = np.asarray(values["mu"], dtype=np.float64)
    sigma = np.asarray(values["sigma"], dtype=np.float64)
    return cls(
        mu=mu,
        sigma=sigma,
        split=str(values.get("split", "candidate")),
        dataset_name=str(values.get("dataset_name", "")),
        source=str(values.get("source", "")),
        feature_dim=int(cast(int | str, values.get("feature_dim", mu.shape[0]))),
        num_samples=(
            None
            if values.get("num_samples") is None
            else int(cast(int | str, values["num_samples"]))
        ),
    )

compute_feature_statistics

compute_feature_statistics(
    features: Float[Tensor, "batch channels"]
    | Float[ndarray, "batch channels"],
    *,
    split: str = "candidate",
    dataset_name: str = "",
    source: str = "",
) -> LayoutFIDStatistics

Compute float64 mean and covariance from feature vectors.

Parameters:

Name Type Description Default
features Float[Tensor, 'batch channels'] | Float[ndarray, 'batch channels']

Feature matrix shaped (samples, feature_dim).

required
split str

Split label stored in the returned metadata.

'candidate'
dataset_name str

Dataset metadata.

''
source str

Source-family metadata.

''

Returns:

Type Description
LayoutFIDStatistics

Feature statistics with NumPy float64 arrays.

Raises:

Type Description
ValueError

If fewer than two feature vectors are provided.

Examples:

>>> stats = compute_feature_statistics(np.eye(3, dtype=np.float32))
>>> stats.sigma.shape
(3, 3)
Source code in models/layout-fid/src/layout_fid/evaluation.py
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def compute_feature_statistics(
    features: Float[torch.Tensor, "batch channels"]
    | Float[np.ndarray, "batch channels"],
    *,
    split: str = "candidate",
    dataset_name: str = "",
    source: str = "",
) -> LayoutFIDStatistics:
    """Compute float64 mean and covariance from feature vectors.

    Args:
        features: Feature matrix shaped ``(samples, feature_dim)``.
        split: Split label stored in the returned metadata.
        dataset_name: Dataset metadata.
        source: Source-family metadata.

    Returns:
        Feature statistics with NumPy ``float64`` arrays.

    Raises:
        ValueError: If fewer than two feature vectors are provided.

    Examples:
        >>> stats = compute_feature_statistics(np.eye(3, dtype=np.float32))
        >>> stats.sigma.shape
        (3, 3)
    """
    array = _as_numpy(features)
    if array.ndim != 2 or array.shape[0] < 2:
        raise ValueError(
            "features must have shape (samples, channels) with samples >= 2"
        )

    array = array.astype(np.float64, copy=False)
    return LayoutFIDStatistics(
        mu=np.mean(array, axis=0),
        sigma=np.cov(array, rowvar=False),
        split=split,
        dataset_name=dataset_name,
        source=source,
        feature_dim=array.shape[1],
        num_samples=array.shape[0],
    )

calculate_frechet_distance

calculate_frechet_distance(
    mu1: Float[ndarray, "channels"],
    sigma1: Float[ndarray, "channels channels"],
    mu2: Float[ndarray, "channels"],
    sigma2: Float[ndarray, "channels channels"],
    *,
    eps: float = 1e-06,
) -> float

Compute the Frechet distance between two Gaussian distributions.

Parameters:

Name Type Description Default
mu1 Float[ndarray, 'channels']

First mean vector.

required
sigma1 Float[ndarray, 'channels channels']

First covariance matrix.

required
mu2 Float[ndarray, 'channels']

Second mean vector.

required
sigma2 Float[ndarray, 'channels channels']

Second covariance matrix.

required
eps float

Diagonal offset used when covariance products are nearly singular.

1e-06

Returns:

Type Description
float

Frechet distance as a Python float.

Raises:

Type Description
ValueError

If dimensions are inconsistent.

Examples:

>>> mu = np.zeros(2)
>>> sigma = np.eye(2)
>>> calculate_frechet_distance(mu, sigma, mu, sigma)
0.0
Source code in models/layout-fid/src/layout_fid/evaluation.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def calculate_frechet_distance(
    mu1: Float[np.ndarray, "channels"],
    sigma1: Float[np.ndarray, "channels channels"],
    mu2: Float[np.ndarray, "channels"],
    sigma2: Float[np.ndarray, "channels channels"],
    *,
    eps: float = 1e-6,
) -> float:
    """Compute the Frechet distance between two Gaussian distributions.

    Args:
        mu1: First mean vector.
        sigma1: First covariance matrix.
        mu2: Second mean vector.
        sigma2: Second covariance matrix.
        eps: Diagonal offset used when covariance products are nearly singular.

    Returns:
        Frechet distance as a Python float.

    Raises:
        ValueError: If dimensions are inconsistent.

    Examples:
        >>> mu = np.zeros(2)
        >>> sigma = np.eye(2)
        >>> calculate_frechet_distance(mu, sigma, mu, sigma)
        0.0
    """
    from scipy import linalg

    mu1 = np.atleast_1d(mu1).astype(np.float64)
    mu2 = np.atleast_1d(mu2).astype(np.float64)
    sigma1 = np.atleast_2d(sigma1).astype(np.float64)
    sigma2 = np.atleast_2d(sigma2).astype(np.float64)
    if mu1.shape != mu2.shape:
        raise ValueError("mean vectors must have matching dimensions")

    if sigma1.shape != sigma2.shape:
        raise ValueError("covariance matrices must have matching dimensions")

    diff = mu1 - mu2
    covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)
    if not np.isfinite(covmean).all():
        offset = np.eye(sigma1.shape[0], dtype=np.float64) * eps
        covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))
    if np.iscomplexobj(covmean):
        covmean = covmean.real
    value = diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * np.trace(covmean)
    return float(max(value, 0.0))

compute_layout_fid_from_statistics

compute_layout_fid_from_statistics(
    candidate: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
    reference: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
) -> float

Compute layout FID from two statistics objects.

Source code in models/layout-fid/src/layout_fid/evaluation.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def compute_layout_fid_from_statistics(
    candidate: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, "..."] | list[float] | list[list[float]] | str | int | None,
    ],
    reference: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, "..."] | list[float] | list[list[float]] | str | int | None,
    ],
) -> float:
    """Compute layout FID from two statistics objects."""
    candidate_stats = _coerce_statistics(candidate)
    reference_stats = _coerce_statistics(reference)
    return calculate_frechet_distance(
        candidate_stats.mu,
        candidate_stats.sigma,
        reference_stats.mu,
        reference_stats.sigma,
    )

load_reference_statistics

load_reference_statistics(
    path: str | PathLike[str],
) -> LayoutFIDStatistics

Load reference_stats/{split}.npz statistics.

Parameters:

Name Type Description Default
path str | PathLike[str]

Statistics file path.

required

Returns:

Type Description
LayoutFIDStatistics

Loaded layout FID statistics.

Source code in models/layout-fid/src/layout_fid/evaluation.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def load_reference_statistics(path: str | PathLike[str]) -> LayoutFIDStatistics:
    """Load ``reference_stats/{split}.npz`` statistics.

    Args:
        path: Statistics file path.

    Returns:
        Loaded layout FID statistics.
    """
    data = np.load(path, allow_pickle=False)
    split = str(data["split"].item()) if "split" in data else Path(path).stem
    dataset_name = str(data["dataset_name"].item()) if "dataset_name" in data else ""
    source = str(data["source"].item()) if "source" in data else ""
    mu = data["mu"].astype(np.float64, copy=False)
    sigma = data["sigma"].astype(np.float64, copy=False)
    num_samples = int(data["num_samples"].item()) if "num_samples" in data else None
    return LayoutFIDStatistics(
        mu=mu,
        sigma=sigma,
        split=split,
        dataset_name=dataset_name,
        source=source,
        feature_dim=mu.shape[0],
        num_samples=num_samples,
    )

save_reference_statistics

save_reference_statistics(
    path: str | PathLike[str], stats: LayoutFIDStatistics
) -> None

Save reference statistics in package-local .npz format.

Source code in models/layout-fid/src/layout_fid/evaluation.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def save_reference_statistics(
    path: str | PathLike[str],
    stats: LayoutFIDStatistics,
) -> None:
    """Save reference statistics in package-local ``.npz`` format."""
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    np.savez(
        path,
        mu=stats.mu.astype(np.float64, copy=False),
        sigma=stats.sigma.astype(np.float64, copy=False),
        split=np.array(stats.split),
        dataset_name=np.array(stats.dataset_name),
        source=np.array(stats.source),
        feature_dim=np.array(stats.feature_dim),
        num_samples=np.array(-1 if stats.num_samples is None else stats.num_samples),
        statistics_kind=np.array("reference_real_distribution"),
    )

compute_layout_fid

compute_layout_fid(
    model: "LayoutFIDModel",
    processor: "LayoutFIDProcessor",
    *,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, "..."]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ],
    batch_size: int = 512,
    **layout_kwargs: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | Mapping[int, str]
    | Mapping[str, str]
    | str
    | bool
    | tuple[int, int]
    | int
    | device
    | None,
) -> float

Compute layout FID directly from model, processor, and layout tensors.

Source code in models/layout-fid/src/layout_fid/evaluation.py
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
def compute_layout_fid(
    model: "LayoutFIDModel",
    processor: "LayoutFIDProcessor",
    *,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, "..."] | list[float] | list[list[float]] | str | int | None,
    ],
    batch_size: int = 512,
    **layout_kwargs: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | Mapping[int, str]
    | Mapping[str, str]
    | str
    | bool
    | tuple[int, int]
    | int
    | torch.device
    | None,
) -> float:
    """Compute layout FID directly from model, processor, and layout tensors."""
    features: list[Float[torch.Tensor, "batch channels"]] = []
    batch = processor(**layout_kwargs)  # ty: ignore[invalid-argument-type]
    for start in range(0, batch.bbox.shape[0], batch_size):
        end = start + batch_size
        with torch.no_grad():
            features.append(
                model.extract_features(
                    bbox=batch.bbox[start:end],
                    labels=batch.labels[start:end],
                    padding_mask=batch.padding_mask[start:end],
                ).cpu()
            )
    candidate = compute_feature_statistics(
        torch.cat(features, dim=0),
        dataset_name=model.config.dataset_name,
        source=model.config.source,
    )
    return compute_layout_fid_from_statistics(candidate, reference_statistics)

metrics

Layout metric helpers matching the layout-dm reference definitions.

compute_overlap

compute_overlap(
    bbox: Float[Tensor, "... elements 4"],
    mask: Bool[Tensor, "... elements"] | None = None,
) -> dict[str, Shaped[torch.Tensor, "..."]]

Compute LayoutDM-compatible overlap metrics for normalized xywh boxes.

Source code in models/layout-fid/src/layout_fid/metrics.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def compute_overlap(
    bbox: Float[torch.Tensor, "... elements 4"],
    mask: Bool[torch.Tensor, "... elements"] | None = None,
) -> dict[str, Shaped[torch.Tensor, "..."]]:
    """Compute LayoutDM-compatible overlap metrics for normalized ``xywh`` boxes."""
    bbox, mask = _batched_bbox_and_mask(bbox, mask)
    batch_size, sequence_length = mask.size()
    bbox = bbox.masked_fill(~mask.unsqueeze(-1), 0)
    xl, yt, xr, yb = _torch_xywh_to_ltrb_components(bbox)
    l1, t1, r1, b1 = (
        xl.unsqueeze(-1),
        yt.unsqueeze(-1),
        xr.unsqueeze(-1),
        yb.unsqueeze(-1),
    )
    l2, t2, r2, b2 = (
        xl.unsqueeze(-2),
        yt.unsqueeze(-2),
        xr.unsqueeze(-2),
        yb.unsqueeze(-2),
    )
    area_1 = (r1 - l1) * (b1 - t1)

    left = torch.maximum(l1, l2)
    right = torch.minimum(r1, r2)
    top = torch.maximum(t1, t2)
    bottom = torch.minimum(b1, b2)
    intersects = (left < right) & (top < bottom)
    intersection = torch.where(
        intersects, (right - left) * (bottom - top), torch.zeros_like(area_1[0])
    )

    batch_mask = (~mask).unsqueeze(1) | (~mask).unsqueeze(2)
    idx = torch.arange(sequence_length, device=intersection.device)
    batch_mask[:, idx, idx] = True
    intersection = intersection.masked_fill(batch_mask, 0)

    area_ratio = torch.nan_to_num(intersection / area_1)
    score = area_ratio.sum(dim=(1, 2))
    score_normalized = score / mask.float().sum(-1)
    score_normalized[torch.isnan(score_normalized)] = 0.0

    ids = torch.arange(sequence_length, device=intersection.device)
    row, col = torch.meshgrid(ids, ids, indexing="ij")
    lower_triangle = (row >= col).expand(batch_size, sequence_length, sequence_length)
    layoutgan_overlap = intersection.clone()
    layoutgan_overlap[lower_triangle] = 0.0

    return {
        "overlap-ACLayoutGAN": score,
        "overlap-LayoutGAN++": score_normalized,
        "overlap-LayoutGAN": layoutgan_overlap.sum(dim=(1, 2)),
    }

compute_alignment

compute_alignment(
    bbox: Float[Tensor, "... elements 4"],
    mask: Bool[Tensor, "... elements"] | None = None,
) -> dict[str, Shaped[torch.Tensor, "..."]]

Compute LayoutDM-compatible alignment metrics for normalized xywh boxes.

Source code in models/layout-fid/src/layout_fid/metrics.py
 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
def compute_alignment(
    bbox: Float[torch.Tensor, "... elements 4"],
    mask: Bool[torch.Tensor, "... elements"] | None = None,
) -> dict[str, Shaped[torch.Tensor, "..."]]:
    """Compute LayoutDM-compatible alignment metrics for normalized ``xywh`` boxes."""
    bbox, mask = _batched_bbox_and_mask(bbox, mask)
    _, sequence_length = mask.size()
    xl, yt, xr, yb = _torch_xywh_to_ltrb_components(bbox)
    xc, yc = bbox[..., 0], bbox[..., 1]

    points = torch.stack([xl, xc, xr, yt, yc, yb], dim=1)
    distances = points.unsqueeze(-1) - points.unsqueeze(-2)
    idx = torch.arange(sequence_length, device=distances.device)
    distances[:, :, idx, idx] = 1.0
    distances = distances.abs().permute(0, 2, 1, 3)
    distances[~mask] = 1.0
    distances = distances.min(-1).values.min(-1).values
    distances.masked_fill_(distances.eq(1.0), 0.0)
    distances = -torch.log(1 - distances)

    score = distances.sum(dim=-1)
    score_normalized = score / mask.float().sum(-1)
    score_normalized[torch.isnan(score_normalized)] = 0.0

    x_points = torch.stack([xl, xc, xr], dim=1)
    x_distances = x_points.unsqueeze(2) - x_points.unsqueeze(3)
    batch_mask = (~mask).unsqueeze(1) | (~mask).unsqueeze(2)
    batch_mask[:, idx, idx] = True
    batch_mask = batch_mask.unsqueeze(1).expand(-1, 3, -1, -1)
    x_distances[batch_mask] = 1.0
    x_distances = x_distances.abs().amin(dim=(1, 3))
    x_distances[x_distances == 1.0] = 0.0

    return {
        "alignment-ACLayoutGAN": score,
        "alignment-LayoutGAN++": score_normalized,
        "alignment-NDN": x_distances.sum(dim=-1),
    }

compute_average_iou

compute_average_iou(
    bbox: Float[Tensor, "... elements 4"]
    | Float[ndarray, "... elements 4"]
    | Sequence[
        tuple[
            Float[ndarray, "elements 4"],
            Int[ndarray, "elements"],
        ]
    ],
    mask: Bool[Tensor, "... elements"]
    | Bool[ndarray, "... elements"]
    | None = None,
) -> dict[str, float]

Compute LayoutDM-compatible average IoU metrics.

bbox may be a batched normalized center xywh tensor with a public valid-element mask, or an unpadded sequence of (bbox, labels) layouts. Labels are ignored by this metric and are accepted for LayoutDM call-shape compatibility.

Source code in models/layout-fid/src/layout_fid/metrics.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def compute_average_iou(
    bbox: Float[torch.Tensor, "... elements 4"]
    | Float[np.ndarray, "... elements 4"]
    | Sequence[tuple[Float[np.ndarray, "elements 4"], Int[np.ndarray, "elements"]]],
    mask: Bool[torch.Tensor, "... elements"]
    | Bool[np.ndarray, "... elements"]
    | None = None,
) -> dict[str, float]:
    """Compute LayoutDM-compatible average IoU metrics.

    ``bbox`` may be a batched normalized center ``xywh`` tensor with a public
    valid-element ``mask``, or an unpadded sequence of ``(bbox, labels)`` layouts.
    Labels are ignored by this metric and are accepted for LayoutDM call-shape
    compatibility.
    """
    layouts = _as_layouts(bbox, mask)
    scores_blt = [
        _average_iou_for_layout(layout, perceptual=True) for layout in layouts
    ]
    scores_vtn = [
        _average_iou_for_layout(layout, perceptual=False) for layout in layouts
    ]
    return {
        "average_iou-BLT": float(np.array(scores_blt).mean()),
        "average_iou-VTN": float(np.array(scores_vtn).mean()),
    }

compute_maximum_iou

compute_maximum_iou(
    candidate_bbox: Float[Tensor, "batch elements 4"],
    reference_bbox: Float[Tensor, "batch elements 4"],
    candidate_mask: Bool[Tensor, "batch elements"]
    | None = None,
    reference_mask: Bool[Tensor, "batch elements"]
    | None = None,
) -> Float[torch.Tensor, ""]

Compute maximum pairwise IoU between two layout batches.

Source code in models/layout-fid/src/layout_fid/metrics.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def compute_maximum_iou(
    candidate_bbox: Float[torch.Tensor, "batch elements 4"],
    reference_bbox: Float[torch.Tensor, "batch elements 4"],
    candidate_mask: Bool[torch.Tensor, "batch elements"] | None = None,
    reference_mask: Bool[torch.Tensor, "batch elements"] | None = None,
) -> Float[torch.Tensor, ""]:
    """Compute maximum pairwise IoU between two layout batches."""
    cand, cand_valid = _valid_ltrb(candidate_bbox, candidate_mask)
    ref, ref_valid = _valid_ltrb(reference_bbox, reference_mask)
    values: list[Float[torch.Tensor, ""]] = []
    for cand_item, ref_item, c_valid, r_valid in zip(
        cand, ref, cand_valid, ref_valid, strict=True
    ):
        c = cand_item[c_valid]
        r = ref_item[r_valid]
        if c.numel() == 0 or r.numel() == 0:
            values.append(
                torch.zeros(
                    (), dtype=candidate_bbox.dtype, device=candidate_bbox.device
                )
            )
            continue
        values.append(_pairwise_iou(c, r).max())
    return torch.stack(values).mean()

model_card

Model-card helpers for layout FID evaluator checkpoints.

model_card_metadata

model_card_metadata(
    config: LayoutFIDConfig, *, hub_id: str
) -> dict[str, str | list[str]]

Return model-card metadata for a converted layout FID checkpoint.

Source code in models/layout-fid/src/layout_fid/model_card.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def model_card_metadata(
    config: LayoutFIDConfig, *, hub_id: str
) -> dict[str, str | list[str]]:
    """Return model-card metadata for a converted layout FID checkpoint."""
    return {
        "hub_id": hub_id,
        "library_name": "transformers",
        "pipeline_tag": "other",
        "tags": ["layout-generation", "layout-evaluation", "fid"],
        "datasets": [
            (
                "creative-graphic-design/Rico"
                if config.dataset_name == "rico25"
                else "creative-graphic-design/PubLayNet"
            )
        ],
    }

modeling_layout_fid

PyTorch modules for layout FID feature extraction.

TransformerWithToken

Bases: Module

Transformer encoder with a learned summary token.

Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class TransformerWithToken(nn.Module):
    """Transformer encoder with a learned summary token."""

    token_mask: Bool[torch.Tensor, "1 1"]

    def __init__(
        self,
        *,
        d_model: int,
        nhead: int,
        dim_feedforward: int,
        num_layers: int,
    ) -> None:
        """Initialize the token encoder."""
        super().__init__()
        self.token = nn.Parameter(torch.randn(1, 1, d_model))
        self.register_buffer("token_mask", torch.zeros(1, 1, dtype=torch.bool))
        layer = nn.TransformerEncoderLayer(
            d_model=d_model,
            nhead=nhead,
            dim_feedforward=dim_feedforward,
        )
        self.core = nn.TransformerEncoder(layer, num_layers=num_layers)

    def forward(
        self,
        x: Float[torch.Tensor, "elements batch channels"],
        src_key_padding_mask: Bool[torch.Tensor, "batch elements"],
    ) -> Float[torch.Tensor, "elements_plus_token batch channels"]:
        """Encode element features with a prepended summary token."""
        batch_size = x.size(1)
        token = self.token.expand(-1, batch_size, -1)
        x = torch.cat([token, x], dim=0)
        token_mask = self.token_mask.expand(batch_size, -1)
        padding_mask = torch.cat([token_mask, src_key_padding_mask], dim=1)
        return self.core(x, src_key_padding_mask=padding_mask)

__init__

__init__(
    *,
    d_model: int,
    nhead: int,
    dim_feedforward: int,
    num_layers: int,
) -> None

Initialize the token encoder.

Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def __init__(
    self,
    *,
    d_model: int,
    nhead: int,
    dim_feedforward: int,
    num_layers: int,
) -> None:
    """Initialize the token encoder."""
    super().__init__()
    self.token = nn.Parameter(torch.randn(1, 1, d_model))
    self.register_buffer("token_mask", torch.zeros(1, 1, dtype=torch.bool))
    layer = nn.TransformerEncoderLayer(
        d_model=d_model,
        nhead=nhead,
        dim_feedforward=dim_feedforward,
    )
    self.core = nn.TransformerEncoder(layer, num_layers=num_layers)

forward

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

Encode element features with a prepended summary token.

Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
40
41
42
43
44
45
46
47
48
49
50
51
def forward(
    self,
    x: Float[torch.Tensor, "elements batch channels"],
    src_key_padding_mask: Bool[torch.Tensor, "batch elements"],
) -> Float[torch.Tensor, "elements_plus_token batch channels"]:
    """Encode element features with a prepended summary token."""
    batch_size = x.size(1)
    token = self.token.expand(-1, batch_size, -1)
    x = torch.cat([token, x], dim=0)
    token_mask = self.token_mask.expand(batch_size, -1)
    padding_mask = torch.cat([token_mask, src_key_padding_mask], dim=1)
    return self.core(x, src_key_padding_mask=padding_mask)

LayoutFIDOutput dataclass

Bases: ModelOutput

Output returned by LayoutFIDModel.forward.

Parameters:

Name Type Description Default
features Float[Tensor, 'batch channels']

Batch-level layout feature vectors.

required
discriminator_logits Float[Tensor, 'batch'] | None

Optional discriminator logits.

None
class_logits Float[Tensor, '... labels'] | None

Optional per-element or valid-element class logits.

None
bbox_pred Float[Tensor, '... 4'] | None

Optional reconstructed boxes.

None
intermediates dict[str, object] | None

Optional diagnostic tensors.

None
Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@dataclass
class LayoutFIDOutput(ModelOutput):
    """Output returned by ``LayoutFIDModel.forward``.

    Args:
        features: Batch-level layout feature vectors.
        discriminator_logits: Optional discriminator logits.
        class_logits: Optional per-element or valid-element class logits.
        bbox_pred: Optional reconstructed boxes.
        intermediates: Optional diagnostic tensors.
    """

    features: Float[torch.Tensor, "batch channels"]
    discriminator_logits: Float[torch.Tensor, "batch"] | None = None
    class_logits: Float[torch.Tensor, "... labels"] | None = None
    bbox_pred: Float[torch.Tensor, "... 4"] | None = None
    intermediates: dict[str, object] | None = None

LayoutFIDModel

Bases: PreTrainedModel

Feature encoder used for layout FID evaluation.

Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
class LayoutFIDModel(PreTrainedModel):
    """Feature encoder used for layout FID evaluation."""

    config_class = LayoutFIDConfig
    base_model_prefix = "layout_fid"

    def __init__(self, config: LayoutFIDConfig) -> None:
        """Create a layout FID encoder.

        Args:
            config: Explicit layout FID configuration.
        """
        super().__init__(config)
        self.all_tied_weights_keys: dict[str, str] = {}
        self.emb_label = nn.Embedding(config.num_label_embeddings, config.d_model)
        self.fc_bbox = nn.Linear(4, config.d_model)
        self.enc_fc_in = nn.Linear(config.d_model * 2, config.d_model)
        self.enc_transformer = TransformerWithToken(
            d_model=config.d_model,
            dim_feedforward=config.d_model // 2,
            nhead=config.nhead,
            num_layers=config.num_layers,
        )
        self.fc_out_disc = nn.Linear(config.d_model, 1)
        self.pos_token = nn.Parameter(torch.rand(config.max_length, 1, config.d_model))
        self.dec_fc_in = nn.Linear(config.d_model * 2, config.d_model)
        dec_layer = nn.TransformerEncoderLayer(
            d_model=config.d_model,
            nhead=config.nhead,
            dim_feedforward=config.d_model // 2,
        )
        self.dec_transformer = nn.TransformerEncoder(
            dec_layer, num_layers=config.num_layers
        )
        self.fc_out_cls = nn.Linear(config.d_model, config.num_label_embeddings)
        self.fc_out_bbox = nn.Linear(config.d_model, 4)

    def extract_features(
        self,
        *,
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
    ) -> Float[torch.Tensor, "batch channels"]:
        """Extract batch-level feature vectors.

        Args:
            bbox: Model-ready boxes.
            labels: Model-ready label ids.
            padding_mask: Boolean mask where ``True`` marks padded elements.

        Returns:
            Feature tensor shaped ``(batch, d_model)``.

        Raises:
            ValueError: If input shapes are inconsistent.

        Examples:
            >>> from layout_fid import LayoutFIDConfig, LayoutFIDModel
            >>> cfg = LayoutFIDConfig(
            ...     dataset_name="publaynet", architecture="layoutnet",
            ...     source="layoutflow", num_public_labels=5,
            ...     num_label_embeddings=6, max_length=2,
            ... )
            >>> model = LayoutFIDModel(cfg)
            >>> out = model.extract_features(
            ...     bbox=torch.zeros(1, 2, 4),
            ...     labels=torch.zeros(1, 2, dtype=torch.long),
            ...     padding_mask=torch.zeros(1, 2, dtype=torch.bool),
            ... )
            >>> tuple(out.shape)
            (1, 256)
        """
        self._validate_inputs(bbox, labels, padding_mask)
        box_features = self.fc_bbox(bbox)
        label_features = self.emb_label(labels)
        hidden = self.enc_fc_in(torch.cat([box_features, label_features], dim=-1))
        hidden = torch.relu(hidden).permute(1, 0, 2)
        encoded = self.enc_transformer(hidden, padding_mask)
        return encoded[0]

    def forward(
        self,
        *,
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
        output_reconstruction: bool = False,
        return_dict: bool = True,
    ) -> LayoutFIDOutput | tuple[Shaped[torch.Tensor, "..."], ...]:
        """Run feature extraction and optional reconstruction heads.

        Args:
            bbox: Model-ready boxes.
            labels: Model-ready label ids.
            padding_mask: Boolean mask where ``True`` marks padded elements.
            output_reconstruction: Whether to return class and bbox predictions.
            return_dict: Whether to return ``LayoutFIDOutput``.

        Returns:
            ``LayoutFIDOutput`` or a tuple with the same non-``None`` fields.

        Raises:
            ValueError: If input shapes are inconsistent.
        """
        features = self.extract_features(
            bbox=bbox, labels=labels, padding_mask=padding_mask
        )
        discriminator_logits = self.fc_out_disc(features).squeeze(-1)
        class_logits: Float[torch.Tensor, "... labels"] | None = None
        bbox_pred: Float[torch.Tensor, "... 4"] | None = None
        if output_reconstruction:
            class_logits, bbox_pred = self._decode(features, padding_mask)
        if not return_dict:
            values = (features, discriminator_logits, class_logits, bbox_pred)
            return tuple(value for value in values if value is not None)
        return LayoutFIDOutput(
            features=features,
            discriminator_logits=discriminator_logits,
            class_logits=class_logits,
            bbox_pred=bbox_pred,
        )

    def _decode(
        self,
        features: Float[torch.Tensor, "batch channels"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
    ) -> tuple[Float[torch.Tensor, "... labels"], Float[torch.Tensor, "... 4"]]:
        batch_size, elements = padding_mask.shape
        hidden = features.unsqueeze(0).expand(elements, -1, -1)
        positions = self.pos_token[:elements].expand(-1, batch_size, -1)
        hidden = torch.relu(self.dec_fc_in(torch.cat([hidden, positions], dim=-1)))
        hidden = self.dec_transformer(hidden, src_key_padding_mask=padding_mask)
        hidden = hidden.permute(1, 0, 2)
        class_logits = self.fc_out_cls(hidden)
        bbox_pred = torch.sigmoid(self.fc_out_bbox(hidden))
        if normalize_source(self.config.source) is LayoutFIDSource.layoutflow:
            valid = ~padding_mask
            class_logits = class_logits[valid]
            bbox_pred = bbox_pred[valid]
        return class_logits, bbox_pred

    @staticmethod
    def _validate_inputs(
        bbox: Float[torch.Tensor, "batch elements 4"],
        labels: Int[torch.Tensor, "batch elements"],
        padding_mask: Bool[torch.Tensor, "batch elements"],
    ) -> None:
        if bbox.ndim != 3 or bbox.shape[-1] != 4:
            raise ValueError("bbox must have shape (batch, elements, 4)")

        if labels.shape != bbox.shape[:2]:
            raise ValueError("labels must have shape (batch, elements)")

        if padding_mask.shape != bbox.shape[:2]:
            raise ValueError("padding_mask must have shape (batch, elements)")

__init__

__init__(config: LayoutFIDConfig) -> None

Create a layout FID encoder.

Parameters:

Name Type Description Default
config LayoutFIDConfig

Explicit layout FID configuration.

required
Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def __init__(self, config: LayoutFIDConfig) -> None:
    """Create a layout FID encoder.

    Args:
        config: Explicit layout FID configuration.
    """
    super().__init__(config)
    self.all_tied_weights_keys: dict[str, str] = {}
    self.emb_label = nn.Embedding(config.num_label_embeddings, config.d_model)
    self.fc_bbox = nn.Linear(4, config.d_model)
    self.enc_fc_in = nn.Linear(config.d_model * 2, config.d_model)
    self.enc_transformer = TransformerWithToken(
        d_model=config.d_model,
        dim_feedforward=config.d_model // 2,
        nhead=config.nhead,
        num_layers=config.num_layers,
    )
    self.fc_out_disc = nn.Linear(config.d_model, 1)
    self.pos_token = nn.Parameter(torch.rand(config.max_length, 1, config.d_model))
    self.dec_fc_in = nn.Linear(config.d_model * 2, config.d_model)
    dec_layer = nn.TransformerEncoderLayer(
        d_model=config.d_model,
        nhead=config.nhead,
        dim_feedforward=config.d_model // 2,
    )
    self.dec_transformer = nn.TransformerEncoder(
        dec_layer, num_layers=config.num_layers
    )
    self.fc_out_cls = nn.Linear(config.d_model, config.num_label_embeddings)
    self.fc_out_bbox = nn.Linear(config.d_model, 4)

extract_features

extract_features(
    *,
    bbox: Float[Tensor, "batch elements 4"],
    labels: Int[Tensor, "batch elements"],
    padding_mask: Bool[Tensor, "batch elements"],
) -> Float[torch.Tensor, "batch channels"]

Extract batch-level feature vectors.

Parameters:

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

Model-ready boxes.

required
labels Int[Tensor, 'batch elements']

Model-ready label ids.

required
padding_mask Bool[Tensor, 'batch elements']

Boolean mask where True marks padded elements.

required

Returns:

Type Description
Float[Tensor, 'batch channels']

Feature tensor shaped (batch, d_model).

Raises:

Type Description
ValueError

If input shapes are inconsistent.

Examples:

>>> from layout_fid import LayoutFIDConfig, LayoutFIDModel
>>> cfg = LayoutFIDConfig(
...     dataset_name="publaynet", architecture="layoutnet",
...     source="layoutflow", num_public_labels=5,
...     num_label_embeddings=6, max_length=2,
... )
>>> model = LayoutFIDModel(cfg)
>>> out = model.extract_features(
...     bbox=torch.zeros(1, 2, 4),
...     labels=torch.zeros(1, 2, dtype=torch.long),
...     padding_mask=torch.zeros(1, 2, dtype=torch.bool),
... )
>>> tuple(out.shape)
(1, 256)
Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
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
def extract_features(
    self,
    *,
    bbox: Float[torch.Tensor, "batch elements 4"],
    labels: Int[torch.Tensor, "batch elements"],
    padding_mask: Bool[torch.Tensor, "batch elements"],
) -> Float[torch.Tensor, "batch channels"]:
    """Extract batch-level feature vectors.

    Args:
        bbox: Model-ready boxes.
        labels: Model-ready label ids.
        padding_mask: Boolean mask where ``True`` marks padded elements.

    Returns:
        Feature tensor shaped ``(batch, d_model)``.

    Raises:
        ValueError: If input shapes are inconsistent.

    Examples:
        >>> from layout_fid import LayoutFIDConfig, LayoutFIDModel
        >>> cfg = LayoutFIDConfig(
        ...     dataset_name="publaynet", architecture="layoutnet",
        ...     source="layoutflow", num_public_labels=5,
        ...     num_label_embeddings=6, max_length=2,
        ... )
        >>> model = LayoutFIDModel(cfg)
        >>> out = model.extract_features(
        ...     bbox=torch.zeros(1, 2, 4),
        ...     labels=torch.zeros(1, 2, dtype=torch.long),
        ...     padding_mask=torch.zeros(1, 2, dtype=torch.bool),
        ... )
        >>> tuple(out.shape)
        (1, 256)
    """
    self._validate_inputs(bbox, labels, padding_mask)
    box_features = self.fc_bbox(bbox)
    label_features = self.emb_label(labels)
    hidden = self.enc_fc_in(torch.cat([box_features, label_features], dim=-1))
    hidden = torch.relu(hidden).permute(1, 0, 2)
    encoded = self.enc_transformer(hidden, padding_mask)
    return encoded[0]

forward

forward(
    *,
    bbox: Float[Tensor, "batch elements 4"],
    labels: Int[Tensor, "batch elements"],
    padding_mask: Bool[Tensor, "batch elements"],
    output_reconstruction: bool = False,
    return_dict: bool = True,
) -> (
    LayoutFIDOutput
    | tuple[Shaped[torch.Tensor, "..."], ...]
)

Run feature extraction and optional reconstruction heads.

Parameters:

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

Model-ready boxes.

required
labels Int[Tensor, 'batch elements']

Model-ready label ids.

required
padding_mask Bool[Tensor, 'batch elements']

Boolean mask where True marks padded elements.

required
output_reconstruction bool

Whether to return class and bbox predictions.

False
return_dict bool

Whether to return LayoutFIDOutput.

True

Returns:

Type Description
LayoutFIDOutput | tuple[Shaped[Tensor, '...'], ...]

LayoutFIDOutput or a tuple with the same non-None fields.

Raises:

Type Description
ValueError

If input shapes are inconsistent.

Source code in models/layout-fid/src/layout_fid/modeling_layout_fid.py
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
def forward(
    self,
    *,
    bbox: Float[torch.Tensor, "batch elements 4"],
    labels: Int[torch.Tensor, "batch elements"],
    padding_mask: Bool[torch.Tensor, "batch elements"],
    output_reconstruction: bool = False,
    return_dict: bool = True,
) -> LayoutFIDOutput | tuple[Shaped[torch.Tensor, "..."], ...]:
    """Run feature extraction and optional reconstruction heads.

    Args:
        bbox: Model-ready boxes.
        labels: Model-ready label ids.
        padding_mask: Boolean mask where ``True`` marks padded elements.
        output_reconstruction: Whether to return class and bbox predictions.
        return_dict: Whether to return ``LayoutFIDOutput``.

    Returns:
        ``LayoutFIDOutput`` or a tuple with the same non-``None`` fields.

    Raises:
        ValueError: If input shapes are inconsistent.
    """
    features = self.extract_features(
        bbox=bbox, labels=labels, padding_mask=padding_mask
    )
    discriminator_logits = self.fc_out_disc(features).squeeze(-1)
    class_logits: Float[torch.Tensor, "... labels"] | None = None
    bbox_pred: Float[torch.Tensor, "... 4"] | None = None
    if output_reconstruction:
        class_logits, bbox_pred = self._decode(features, padding_mask)
    if not return_dict:
        values = (features, discriminator_logits, class_logits, bbox_pred)
        return tuple(value for value in values if value is not None)
    return LayoutFIDOutput(
        features=features,
        discriminator_logits=discriminator_logits,
        class_logits=class_logits,
        bbox_pred=bbox_pred,
    )

pipeline_layout_fid

High-level layout FID evaluator.

LayoutFIDEvaluator

Compose a layout FID model, processor, and reference statistics.

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

    def __init__(
        self,
        *,
        model: LayoutFIDModel,
        processor: LayoutFIDProcessor,
        reference_statistics: Mapping[str, LayoutFIDStatistics] | None = None,
        device: torch.device | str | None = None,
    ) -> None:
        """Create an evaluator."""
        self.model = model
        self.processor = processor
        self.reference_statistics = dict(reference_statistics or {})
        self.device = (
            torch.device(device) if device is not None else torch.device("cpu")
        )
        self.model.to(self.device)
        self.model.eval()

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | PathLike[str],
        *,
        device: torch.device | str | None = None,
        **kwargs: LayoutFIDLoadKwarg,
    ) -> LayoutFIDEvaluator:
        """Load evaluator components from a local directory or Hub id."""
        model = LayoutFIDModel.from_pretrained(pretrained_model_name_or_path, **kwargs)
        processor = LayoutFIDProcessor.from_pretrained(
            pretrained_model_name_or_path,
            **cast(dict[str, LayoutFIDConfigValue], kwargs),
        )
        stats = cls._load_reference_statistics(pretrained_model_name_or_path, model)
        return cls(
            model=model, processor=processor, reference_statistics=stats, device=device
        )

    def extract_features(
        self,
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None = None,
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        id2label: Mapping[int, str] | Mapping[str, str] | None = None,
        box_format: str = "xywh",
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        batch_size: int = 512,
    ) -> Float[torch.Tensor, "batch channels"]:
        """Extract features from public layout tensors."""
        layout_kwargs = self._layout_kwargs(
            layouts=layouts,
            bbox=bbox,
            labels=labels,
            mask=mask,
            id2label=id2label,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        batch = self.processor(
            bbox=cast(
                Float[torch.Tensor, "batch elements 4"]
                | Float[np.ndarray, "batch elements 4"]
                | Sequence[ArrayLikeInput],
                layout_kwargs["bbox"],
            ),
            labels=cast(
                Int[torch.Tensor, "batch elements"]
                | Int[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput],
                layout_kwargs["labels"],
            ),
            mask=cast(
                Bool[torch.Tensor, "batch elements"]
                | Bool[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs["mask"],
            ),
            id2label=cast(
                Mapping[int, str] | Mapping[str, str] | None,
                layout_kwargs["id2label"],
            ),
            box_format=cast(str, layout_kwargs["box_format"]),
            normalized=cast(bool, layout_kwargs["normalized"]),
            canvas_size=cast(tuple[int, int] | None, layout_kwargs["canvas_size"]),
            device=self.device,
        )
        outputs: list[Float[torch.Tensor, "batch channels"]] = []
        for start in range(0, batch.bbox.shape[0], batch_size):
            end = start + batch_size
            with torch.no_grad():
                outputs.append(
                    self.model.extract_features(
                        bbox=batch.bbox[start:end],
                        labels=batch.labels[start:end],
                        padding_mask=batch.padding_mask[start:end],
                    ).cpu()
                )
        return torch.cat(outputs, dim=0)

    def compute_statistics(
        self,
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None = None,
        features: Float[torch.Tensor, "batch channels"]
        | Float[np.ndarray, "batch channels"]
        | None = None,
        **layout_kwargs: LayoutFIDLayoutKwarg,
    ) -> LayoutFIDStatistics:
        """Compute candidate feature statistics."""
        if features is not None and (layouts is not None or layout_kwargs):
            raise ValueError("Pass either features or layout inputs, not both")

        if features is None:
            features = self.extract_features(
                layouts=layouts,
                bbox=cast(
                    Float[torch.Tensor, "batch elements 4"]
                    | Float[np.ndarray, "batch elements 4"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layout_kwargs.get("bbox"),
                ),
                labels=cast(
                    Int[torch.Tensor, "batch elements"]
                    | Int[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layout_kwargs.get("labels"),
                ),
                mask=cast(
                    Bool[torch.Tensor, "batch elements"]
                    | Bool[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layout_kwargs.get("mask"),
                ),
                id2label=cast(
                    Mapping[int, str] | Mapping[str, str] | None,
                    layout_kwargs.get("id2label"),
                ),
                box_format=cast(str, layout_kwargs.get("box_format", "xywh")),
                normalized=cast(bool, layout_kwargs.get("normalized", True)),
                canvas_size=cast(
                    tuple[int, int] | None, layout_kwargs.get("canvas_size")
                ),
                batch_size=cast(int, layout_kwargs.get("batch_size", 512)),
            )
        return compute_feature_statistics(
            features,
            dataset_name=self.model.config.dataset_name,
            source=self.model.config.source,
        )

    def compute_fid(
        self,
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None = None,
        features: Float[torch.Tensor, "batch channels"]
        | Float[np.ndarray, "batch channels"]
        | None = None,
        statistics: LayoutFIDStatistics
        | Mapping[
            str,
            Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
        ]
        | None = None,
        reference_statistics: LayoutFIDStatistics
        | Mapping[
            str,
            Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
        ]
        | None = None,
        reference_split: LayoutFIDStatsSplit | str = "test",
        **layout_kwargs: LayoutFIDLayoutKwarg,
    ) -> float:
        """Compute layout FID against bundled or supplied reference statistics."""
        provided = sum(value is not None for value in (layouts, features, statistics))
        if provided + bool(layout_kwargs) == 0:
            raise ValueError("Pass candidate layouts, features, or statistics")

        if statistics is None:
            statistics = self.compute_statistics(
                layouts=layouts, features=features, **layout_kwargs
            )
        reference = (
            LayoutFIDStatistics.from_mapping(
                cast(
                    Mapping[
                        str,
                        Float[np.ndarray, "..."]
                        | list[float]
                        | list[list[float]]
                        | str
                        | int
                        | None,
                    ],
                    reference_statistics,
                )
            )
            if reference_statistics is not None
            and not isinstance(reference_statistics, LayoutFIDStatistics)
            else reference_statistics
        )
        if reference is None:
            split = str(normalize_stats_split(reference_split))
            try:
                reference = self.reference_statistics[split]
            except KeyError as exc:
                raise ValueError(
                    f"Reference statistics split is not loaded: {split}"
                ) from exc

        candidate = (
            LayoutFIDStatistics.from_mapping(
                cast(
                    Mapping[
                        str,
                        Float[np.ndarray, "..."]
                        | list[float]
                        | list[list[float]]
                        | str
                        | int
                        | None,
                    ],
                    statistics,
                )
            )
            if not isinstance(statistics, LayoutFIDStatistics)
            else statistics
        )
        return compute_layout_fid_from_statistics(candidate, reference)

    @staticmethod
    def _layout_kwargs(
        *,
        layouts: LayoutGenerationOutput
        | Mapping[
            str,
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Mapping[int, str]
            | Mapping[str, str]
            | None,
        ]
        | None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput]
        | None,
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None,
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None,
        id2label: Mapping[int, str] | Mapping[str, str] | None,
        box_format: str,
        normalized: bool,
        canvas_size: tuple[int, int] | None,
    ) -> dict[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | Mapping[int, str]
        | Mapping[str, str]
        | str
        | bool
        | tuple[int, int]
        | None,
    ]:
        if layouts is not None and any(
            value is not None for value in (bbox, labels, mask)
        ):
            raise ValueError("Pass either layouts or explicit bbox/labels/mask")

        if layouts is not None:
            if isinstance(layouts, LayoutGenerationOutput):
                bbox = layouts.bbox
                labels = layouts.labels
                mask = layouts.mask
                id2label = layouts.id2label
            else:
                bbox = cast(
                    Float[torch.Tensor, "batch elements 4"]
                    | Float[np.ndarray, "batch elements 4"]
                    | Sequence[ArrayLikeInput],
                    layouts["bbox"],
                )
                labels = cast(
                    Int[torch.Tensor, "batch elements"]
                    | Int[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput],
                    layouts["labels"],
                )
                mask = cast(
                    Bool[torch.Tensor, "batch elements"]
                    | Bool[np.ndarray, "batch elements"]
                    | Sequence[ArrayLikeInput]
                    | None,
                    layouts.get("mask"),
                )
                id2label = cast(
                    Mapping[int, str] | Mapping[str, str] | None,
                    layouts.get("id2label"),
                )
        if bbox is None or labels is None:
            raise ValueError("bbox and labels are required")

        return {
            "bbox": bbox,
            "labels": labels,
            "mask": mask,
            "id2label": id2label,
            "box_format": box_format,
            "normalized": normalized,
            "canvas_size": canvas_size,
        }

    @staticmethod
    def _load_reference_statistics(
        model_path: str | PathLike[str], model: LayoutFIDModel
    ) -> dict[str, LayoutFIDStatistics]:
        path = Path(model_path)
        if not path.exists():
            return {}
        stats: dict[str, LayoutFIDStatistics] = {}
        for split, relative in model.config.reference_stats.items():
            stats_path = path / relative
            if stats_path.exists():
                stats[split] = load_reference_statistics(stats_path)
        return stats

__init__

__init__(
    *,
    model: LayoutFIDModel,
    processor: LayoutFIDProcessor,
    reference_statistics: Mapping[str, LayoutFIDStatistics]
    | None = None,
    device: device | str | None = None,
) -> None

Create an evaluator.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def __init__(
    self,
    *,
    model: LayoutFIDModel,
    processor: LayoutFIDProcessor,
    reference_statistics: Mapping[str, LayoutFIDStatistics] | None = None,
    device: torch.device | str | None = None,
) -> None:
    """Create an evaluator."""
    self.model = model
    self.processor = processor
    self.reference_statistics = dict(reference_statistics or {})
    self.device = (
        torch.device(device) if device is not None else torch.device("cpu")
    )
    self.model.to(self.device)
    self.model.eval()

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    *,
    device: device | str | None = None,
    **kwargs: LayoutFIDLoadKwarg,
) -> LayoutFIDEvaluator

Load evaluator components from a local directory or Hub id.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | PathLike[str],
    *,
    device: torch.device | str | None = None,
    **kwargs: LayoutFIDLoadKwarg,
) -> LayoutFIDEvaluator:
    """Load evaluator components from a local directory or Hub id."""
    model = LayoutFIDModel.from_pretrained(pretrained_model_name_or_path, **kwargs)
    processor = LayoutFIDProcessor.from_pretrained(
        pretrained_model_name_or_path,
        **cast(dict[str, LayoutFIDConfigValue], kwargs),
    )
    stats = cls._load_reference_statistics(pretrained_model_name_or_path, model)
    return cls(
        model=model, processor=processor, reference_statistics=stats, device=device
    )

extract_features

extract_features(
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[Tensor, "batch elements 4"]
        | Float[ndarray, "batch elements 4"]
        | Int[Tensor, "batch elements"]
        | Int[ndarray, "batch elements"]
        | Bool[Tensor, "batch elements"]
        | Bool[ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    labels: Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str]
    | Mapping[str, str]
    | None = None,
    box_format: str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    batch_size: int = 512,
) -> Float[torch.Tensor, "batch channels"]

Extract features from public layout tensors.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
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
def extract_features(
    self,
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput]
    | None = None,
    labels: Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str] | Mapping[str, str] | None = None,
    box_format: str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    batch_size: int = 512,
) -> Float[torch.Tensor, "batch channels"]:
    """Extract features from public layout tensors."""
    layout_kwargs = self._layout_kwargs(
        layouts=layouts,
        bbox=bbox,
        labels=labels,
        mask=mask,
        id2label=id2label,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    batch = self.processor(
        bbox=cast(
            Float[torch.Tensor, "batch elements 4"]
            | Float[np.ndarray, "batch elements 4"]
            | Sequence[ArrayLikeInput],
            layout_kwargs["bbox"],
        ),
        labels=cast(
            Int[torch.Tensor, "batch elements"]
            | Int[np.ndarray, "batch elements"]
            | Sequence[ArrayLikeInput],
            layout_kwargs["labels"],
        ),
        mask=cast(
            Bool[torch.Tensor, "batch elements"]
            | Bool[np.ndarray, "batch elements"]
            | Sequence[ArrayLikeInput]
            | None,
            layout_kwargs["mask"],
        ),
        id2label=cast(
            Mapping[int, str] | Mapping[str, str] | None,
            layout_kwargs["id2label"],
        ),
        box_format=cast(str, layout_kwargs["box_format"]),
        normalized=cast(bool, layout_kwargs["normalized"]),
        canvas_size=cast(tuple[int, int] | None, layout_kwargs["canvas_size"]),
        device=self.device,
    )
    outputs: list[Float[torch.Tensor, "batch channels"]] = []
    for start in range(0, batch.bbox.shape[0], batch_size):
        end = start + batch_size
        with torch.no_grad():
            outputs.append(
                self.model.extract_features(
                    bbox=batch.bbox[start:end],
                    labels=batch.labels[start:end],
                    padding_mask=batch.padding_mask[start:end],
                ).cpu()
            )
    return torch.cat(outputs, dim=0)

compute_statistics

compute_statistics(
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[Tensor, "batch elements 4"]
        | Float[ndarray, "batch elements 4"]
        | Int[Tensor, "batch elements"]
        | Int[ndarray, "batch elements"]
        | Bool[Tensor, "batch elements"]
        | Bool[ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[Tensor, "batch channels"]
    | Float[ndarray, "batch channels"]
    | None = None,
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> LayoutFIDStatistics

Compute candidate feature statistics.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
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
def compute_statistics(
    self,
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[torch.Tensor, "batch channels"]
    | Float[np.ndarray, "batch channels"]
    | None = None,
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> LayoutFIDStatistics:
    """Compute candidate feature statistics."""
    if features is not None and (layouts is not None or layout_kwargs):
        raise ValueError("Pass either features or layout inputs, not both")

    if features is None:
        features = self.extract_features(
            layouts=layouts,
            bbox=cast(
                Float[torch.Tensor, "batch elements 4"]
                | Float[np.ndarray, "batch elements 4"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs.get("bbox"),
            ),
            labels=cast(
                Int[torch.Tensor, "batch elements"]
                | Int[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs.get("labels"),
            ),
            mask=cast(
                Bool[torch.Tensor, "batch elements"]
                | Bool[np.ndarray, "batch elements"]
                | Sequence[ArrayLikeInput]
                | None,
                layout_kwargs.get("mask"),
            ),
            id2label=cast(
                Mapping[int, str] | Mapping[str, str] | None,
                layout_kwargs.get("id2label"),
            ),
            box_format=cast(str, layout_kwargs.get("box_format", "xywh")),
            normalized=cast(bool, layout_kwargs.get("normalized", True)),
            canvas_size=cast(
                tuple[int, int] | None, layout_kwargs.get("canvas_size")
            ),
            batch_size=cast(int, layout_kwargs.get("batch_size", 512)),
        )
    return compute_feature_statistics(
        features,
        dataset_name=self.model.config.dataset_name,
        source=self.model.config.source,
    )

compute_fid

compute_fid(
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[Tensor, "batch elements 4"]
        | Float[ndarray, "batch elements 4"]
        | Int[Tensor, "batch elements"]
        | Int[ndarray, "batch elements"]
        | Bool[Tensor, "batch elements"]
        | Bool[ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[Tensor, "batch channels"]
    | Float[ndarray, "batch channels"]
    | None = None,
    statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, ...]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ]
    | None = None,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[ndarray, ...]
        | list[float]
        | list[list[float]]
        | str
        | int
        | None,
    ]
    | None = None,
    reference_split: LayoutFIDStatsSplit | str = "test",
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> float

Compute layout FID against bundled or supplied reference statistics.

Source code in models/layout-fid/src/layout_fid/pipeline_layout_fid.py
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
def compute_fid(
    self,
    *,
    layouts: LayoutGenerationOutput
    | Mapping[
        str,
        Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Mapping[int, str]
        | Mapping[str, str]
        | None,
    ]
    | None = None,
    features: Float[torch.Tensor, "batch channels"]
    | Float[np.ndarray, "batch channels"]
    | None = None,
    statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
    ]
    | None = None,
    reference_statistics: LayoutFIDStatistics
    | Mapping[
        str,
        Float[np.ndarray, ...] | list[float] | list[list[float]] | str | int | None,
    ]
    | None = None,
    reference_split: LayoutFIDStatsSplit | str = "test",
    **layout_kwargs: LayoutFIDLayoutKwarg,
) -> float:
    """Compute layout FID against bundled or supplied reference statistics."""
    provided = sum(value is not None for value in (layouts, features, statistics))
    if provided + bool(layout_kwargs) == 0:
        raise ValueError("Pass candidate layouts, features, or statistics")

    if statistics is None:
        statistics = self.compute_statistics(
            layouts=layouts, features=features, **layout_kwargs
        )
    reference = (
        LayoutFIDStatistics.from_mapping(
            cast(
                Mapping[
                    str,
                    Float[np.ndarray, "..."]
                    | list[float]
                    | list[list[float]]
                    | str
                    | int
                    | None,
                ],
                reference_statistics,
            )
        )
        if reference_statistics is not None
        and not isinstance(reference_statistics, LayoutFIDStatistics)
        else reference_statistics
    )
    if reference is None:
        split = str(normalize_stats_split(reference_split))
        try:
            reference = self.reference_statistics[split]
        except KeyError as exc:
            raise ValueError(
                f"Reference statistics split is not loaded: {split}"
            ) from exc

    candidate = (
        LayoutFIDStatistics.from_mapping(
            cast(
                Mapping[
                    str,
                    Float[np.ndarray, "..."]
                    | list[float]
                    | list[list[float]]
                    | str
                    | int
                    | None,
                ],
                statistics,
            )
        )
        if not isinstance(statistics, LayoutFIDStatistics)
        else statistics
    )
    return compute_layout_fid_from_statistics(candidate, reference)

processing_layout_fid

Input processing for layout FID evaluators.

LayoutFIDBatch dataclass

Model-ready layout FID batch.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
24
25
26
27
28
29
30
31
32
@dataclass(frozen=True)
class LayoutFIDBatch:
    """Model-ready layout FID batch."""

    bbox: Float[torch.Tensor, "batch elements 4"]
    labels: Int[torch.Tensor, "batch elements"]
    padding_mask: Bool[torch.Tensor, "batch elements"]
    mask: Bool[torch.Tensor, "batch elements"]
    id2label: dict[int, str] | None

LayoutFIDProcessor

Bases: ProcessorMixin

Convert public layout tensors into layout FID model inputs.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
 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
class LayoutFIDProcessor(ProcessorMixin):
    """Convert public layout tensors into layout FID model inputs."""

    config_name = "processor_config.json"

    def __init__(self, config: LayoutFIDConfig) -> None:
        """Create a processor.

        Args:
            config: Explicit layout FID configuration.
        """
        super().__init__()
        self.config = config

    def __call__(
        self,
        *,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | Float[np.ndarray, "batch elements 4"]
        | Sequence[ArrayLikeInput],
        labels: Int[torch.Tensor, "batch elements"]
        | Int[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput],
        mask: Bool[torch.Tensor, "batch elements"]
        | Bool[np.ndarray, "batch elements"]
        | Sequence[ArrayLikeInput]
        | None = None,
        id2label: Mapping[int, str] | Mapping[str, str] | None = None,
        box_format: BoxFormat | str = "xywh",
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        label_id_offset: int | None = None,
        max_length: int | None = None,
        pad_label_id: int | None = None,
        device: torch.device | str | None = None,
    ) -> LayoutFIDBatch:
        """Prepare model inputs from the repository public layout schema.

        Args:
            bbox: Public layout boxes.
            labels: Public dataset-local label ids.
            mask: Optional public valid-element mask.
            id2label: Optional public id-to-label metadata.
            box_format: Public input box format.
            normalized: Whether boxes are normalized to ``[0, 1]``.
            canvas_size: Pixel canvas size required when ``normalized=False``.
            label_id_offset: Optional parity/debug label-offset override.
            max_length: Optional maximum sequence length override.
            pad_label_id: Optional padded-position model label id.
            device: Target torch device.

        Returns:
            A ``LayoutFIDBatch`` with ``padding_mask=True`` for padded elements.

        Raises:
            ValueError: If metadata or tensor shapes are inconsistent.

        Examples:
            >>> from layout_fid import LayoutFIDConfig, LayoutFIDProcessor
            >>> cfg = LayoutFIDConfig(
            ...     dataset_name="publaynet", architecture="layoutnet",
            ...     source="layoutflow", num_public_labels=5,
            ...     num_label_embeddings=6, max_length=2,
            ... )
            >>> batch = LayoutFIDProcessor(cfg)(
            ...     bbox=[[[0.5, 0.5, 0.2, 0.2]]], labels=[[0]]
            ... )
            >>> batch.padding_mask.tolist()
            [[False, True]]
        """
        target_device = (
            torch.device(device) if device is not None else torch.device("cpu")
        )
        bbox_t, labels_t, mask_t = prepare_layout_tensors(
            bbox=bbox,
            labels=labels,
            mask=mask,
            box_format=box_format,
            normalized=normalized,
            canvas_size=canvas_size,
        )
        bbox_t = bbox_t.to(device=target_device)
        labels_t = labels_t.to(device=target_device)
        mask_t = mask_t.to(device=target_device)
        max_len = max_length if max_length is not None else self.config.max_length
        pad_id = pad_label_id if pad_label_id is not None else self.config.pad_label_id
        bbox_t = self._pad_tensor(bbox_t, max_len, 0.0)
        labels_t = self._pad_tensor(labels_t, max_len, pad_id)
        mask_t = self._pad_tensor(mask_t, max_len, False)
        model_bbox = bbox_t
        if self.config.bbox_format_for_model == "ltrb":
            model_bbox = xywh_to_ltrb(model_bbox)
        offset = (
            label_id_offset
            if label_id_offset is not None
            else self.config.label_id_offset
        )
        model_labels = labels_t + offset
        model_labels = torch.where(
            mask_t, model_labels, torch.full_like(model_labels, pad_id)
        )
        if model_labels[mask_t].numel() and (
            int(model_labels[mask_t].min()) < 0
            or int(model_labels[mask_t].max()) >= self.config.num_label_embeddings
        ):
            raise ValueError("labels after label_id_offset exceed embedding table")

        normalized_id2label = self._normalize_id2label(id2label)
        if (
            normalized_id2label is not None
            and normalized_id2label != self.config.id2label
        ):
            raise ValueError("id2label does not match the evaluator config")

        return LayoutFIDBatch(
            bbox=model_bbox * mask_t.unsqueeze(-1),
            labels=model_labels,
            padding_mask=~mask_t,
            mask=mask_t,
            id2label=normalized_id2label,
        )

    def save_pretrained(  # ty: ignore[invalid-method-override]
        self, save_directory: str | os.PathLike[str]
    ) -> tuple[str]:
        """Save processor metadata.

        Args:
            save_directory: Directory receiving ``processor_config.json``.

        Returns:
            Tuple containing the saved config path.
        """
        import json
        import os

        os.makedirs(save_directory, exist_ok=True)
        path = os.path.join(save_directory, self.config_name)
        with open(path, "w", encoding="utf-8") as file_obj:
            json.dump({"config_class": self.config.__class__.__name__}, file_obj)
            file_obj.write("\n")
        return (path,)

    @classmethod
    def from_pretrained(  # ty: ignore[invalid-method-override]
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        **kwargs: LayoutFIDConfigValue,
    ) -> "LayoutFIDProcessor":
        """Load a processor from a saved model directory.

        Args:
            pretrained_model_name_or_path: Local path or Hub id.
            kwargs: Extra config-loading keyword arguments.

        Returns:
            Loaded processor bound to the model config.
        """
        config = LayoutFIDConfig.from_pretrained(
            pretrained_model_name_or_path,
            **kwargs,  # ty: ignore[invalid-argument-type]
        )
        return cls(config=config)

    @staticmethod
    def _normalize_id2label(
        id2label: Mapping[int, str] | Mapping[str, str] | None,
    ) -> dict[int, str] | None:
        if id2label is None:
            return None
        return {int(key): value for key, value in id2label.items()}

    @staticmethod
    def _pad_tensor(
        tensor: Shaped[torch.Tensor, "batch elements ..."],
        max_length: int,
        value: float | int | bool,
    ) -> Shaped[torch.Tensor, "batch max_elements ..."]:
        if tensor.shape[1] > max_length:
            return tensor[:, :max_length]
        if tensor.shape[1] == max_length:
            return tensor
        pad_shape = (tensor.shape[0], max_length - tensor.shape[1], *tensor.shape[2:])
        pad = torch.full(pad_shape, value, dtype=tensor.dtype, device=tensor.device)
        return torch.cat([tensor, pad], dim=1)

__init__

__init__(config: LayoutFIDConfig) -> None

Create a processor.

Parameters:

Name Type Description Default
config LayoutFIDConfig

Explicit layout FID configuration.

required
Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
40
41
42
43
44
45
46
47
def __init__(self, config: LayoutFIDConfig) -> None:
    """Create a processor.

    Args:
        config: Explicit layout FID configuration.
    """
    super().__init__()
    self.config = config

__call__

__call__(
    *,
    bbox: Float[Tensor, "batch elements 4"]
    | Float[ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput],
    labels: Int[Tensor, "batch elements"]
    | Int[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput],
    mask: Bool[Tensor, "batch elements"]
    | Bool[ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str]
    | Mapping[str, str]
    | None = None,
    box_format: BoxFormat | str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    label_id_offset: int | None = None,
    max_length: int | None = None,
    pad_label_id: int | None = None,
    device: device | str | None = None,
) -> LayoutFIDBatch

Prepare model inputs from the repository public layout schema.

Parameters:

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

Public layout boxes.

required
labels Int[Tensor, 'batch elements'] | Int[ndarray, 'batch elements'] | Sequence[ArrayLikeInput]

Public dataset-local label ids.

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

Optional public valid-element mask.

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

Optional public id-to-label metadata.

None
box_format BoxFormat | str

Public input box format.

'xywh'
normalized bool

Whether boxes are normalized to [0, 1].

True
canvas_size tuple[int, int] | None

Pixel canvas size required when normalized=False.

None
label_id_offset int | None

Optional parity/debug label-offset override.

None
max_length int | None

Optional maximum sequence length override.

None
pad_label_id int | None

Optional padded-position model label id.

None
device device | str | None

Target torch device.

None

Returns:

Type Description
LayoutFIDBatch

A LayoutFIDBatch with padding_mask=True for padded elements.

Raises:

Type Description
ValueError

If metadata or tensor shapes are inconsistent.

Examples:

>>> from layout_fid import LayoutFIDConfig, LayoutFIDProcessor
>>> cfg = LayoutFIDConfig(
...     dataset_name="publaynet", architecture="layoutnet",
...     source="layoutflow", num_public_labels=5,
...     num_label_embeddings=6, max_length=2,
... )
>>> batch = LayoutFIDProcessor(cfg)(
...     bbox=[[[0.5, 0.5, 0.2, 0.2]]], labels=[[0]]
... )
>>> batch.padding_mask.tolist()
[[False, True]]
Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
 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
def __call__(
    self,
    *,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | Float[np.ndarray, "batch elements 4"]
    | Sequence[ArrayLikeInput],
    labels: Int[torch.Tensor, "batch elements"]
    | Int[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput],
    mask: Bool[torch.Tensor, "batch elements"]
    | Bool[np.ndarray, "batch elements"]
    | Sequence[ArrayLikeInput]
    | None = None,
    id2label: Mapping[int, str] | Mapping[str, str] | None = None,
    box_format: BoxFormat | str = "xywh",
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    label_id_offset: int | None = None,
    max_length: int | None = None,
    pad_label_id: int | None = None,
    device: torch.device | str | None = None,
) -> LayoutFIDBatch:
    """Prepare model inputs from the repository public layout schema.

    Args:
        bbox: Public layout boxes.
        labels: Public dataset-local label ids.
        mask: Optional public valid-element mask.
        id2label: Optional public id-to-label metadata.
        box_format: Public input box format.
        normalized: Whether boxes are normalized to ``[0, 1]``.
        canvas_size: Pixel canvas size required when ``normalized=False``.
        label_id_offset: Optional parity/debug label-offset override.
        max_length: Optional maximum sequence length override.
        pad_label_id: Optional padded-position model label id.
        device: Target torch device.

    Returns:
        A ``LayoutFIDBatch`` with ``padding_mask=True`` for padded elements.

    Raises:
        ValueError: If metadata or tensor shapes are inconsistent.

    Examples:
        >>> from layout_fid import LayoutFIDConfig, LayoutFIDProcessor
        >>> cfg = LayoutFIDConfig(
        ...     dataset_name="publaynet", architecture="layoutnet",
        ...     source="layoutflow", num_public_labels=5,
        ...     num_label_embeddings=6, max_length=2,
        ... )
        >>> batch = LayoutFIDProcessor(cfg)(
        ...     bbox=[[[0.5, 0.5, 0.2, 0.2]]], labels=[[0]]
        ... )
        >>> batch.padding_mask.tolist()
        [[False, True]]
    """
    target_device = (
        torch.device(device) if device is not None else torch.device("cpu")
    )
    bbox_t, labels_t, mask_t = prepare_layout_tensors(
        bbox=bbox,
        labels=labels,
        mask=mask,
        box_format=box_format,
        normalized=normalized,
        canvas_size=canvas_size,
    )
    bbox_t = bbox_t.to(device=target_device)
    labels_t = labels_t.to(device=target_device)
    mask_t = mask_t.to(device=target_device)
    max_len = max_length if max_length is not None else self.config.max_length
    pad_id = pad_label_id if pad_label_id is not None else self.config.pad_label_id
    bbox_t = self._pad_tensor(bbox_t, max_len, 0.0)
    labels_t = self._pad_tensor(labels_t, max_len, pad_id)
    mask_t = self._pad_tensor(mask_t, max_len, False)
    model_bbox = bbox_t
    if self.config.bbox_format_for_model == "ltrb":
        model_bbox = xywh_to_ltrb(model_bbox)
    offset = (
        label_id_offset
        if label_id_offset is not None
        else self.config.label_id_offset
    )
    model_labels = labels_t + offset
    model_labels = torch.where(
        mask_t, model_labels, torch.full_like(model_labels, pad_id)
    )
    if model_labels[mask_t].numel() and (
        int(model_labels[mask_t].min()) < 0
        or int(model_labels[mask_t].max()) >= self.config.num_label_embeddings
    ):
        raise ValueError("labels after label_id_offset exceed embedding table")

    normalized_id2label = self._normalize_id2label(id2label)
    if (
        normalized_id2label is not None
        and normalized_id2label != self.config.id2label
    ):
        raise ValueError("id2label does not match the evaluator config")

    return LayoutFIDBatch(
        bbox=model_bbox * mask_t.unsqueeze(-1),
        labels=model_labels,
        padding_mask=~mask_t,
        mask=mask_t,
        id2label=normalized_id2label,
    )

save_pretrained

save_pretrained(
    save_directory: str | PathLike[str],
) -> tuple[str]

Save processor metadata.

Parameters:

Name Type Description Default
save_directory str | PathLike[str]

Directory receiving processor_config.json.

required

Returns:

Type Description
tuple[str]

Tuple containing the saved config path.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def save_pretrained(  # ty: ignore[invalid-method-override]
    self, save_directory: str | os.PathLike[str]
) -> tuple[str]:
    """Save processor metadata.

    Args:
        save_directory: Directory receiving ``processor_config.json``.

    Returns:
        Tuple containing the saved config path.
    """
    import json
    import os

    os.makedirs(save_directory, exist_ok=True)
    path = os.path.join(save_directory, self.config_name)
    with open(path, "w", encoding="utf-8") as file_obj:
        json.dump({"config_class": self.config.__class__.__name__}, file_obj)
        file_obj.write("\n")
    return (path,)

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    **kwargs: LayoutFIDConfigValue,
) -> "LayoutFIDProcessor"

Load a processor from a saved model directory.

Parameters:

Name Type Description Default
pretrained_model_name_or_path str | PathLike[str]

Local path or Hub id.

required
kwargs LayoutFIDConfigValue

Extra config-loading keyword arguments.

{}

Returns:

Type Description
'LayoutFIDProcessor'

Loaded processor bound to the model config.

Source code in models/layout-fid/src/layout_fid/processing_layout_fid.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
@classmethod
def from_pretrained(  # ty: ignore[invalid-method-override]
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    **kwargs: LayoutFIDConfigValue,
) -> "LayoutFIDProcessor":
    """Load a processor from a saved model directory.

    Args:
        pretrained_model_name_or_path: Local path or Hub id.
        kwargs: Extra config-loading keyword arguments.

    Returns:
        Loaded processor bound to the model config.
    """
    config = LayoutFIDConfig.from_pretrained(
        pretrained_model_name_or_path,
        **kwargs,  # ty: ignore[invalid-argument-type]
    )
    return cls(config=config)

testing

Test helpers for layout FID packages.

assert_feature_close

assert_feature_close(
    actual: Float[Tensor, "batch channels"],
    expected: Float[Tensor, "batch channels"],
    *,
    atol: float = 1e-06,
    rtol: float = 1e-05,
) -> None

Assert layout FID feature parity.

Source code in models/layout-fid/src/layout_fid/testing.py
10
11
12
13
14
15
16
17
18
def assert_feature_close(
    actual: Float[torch.Tensor, "batch channels"],
    expected: Float[torch.Tensor, "batch channels"],
    *,
    atol: float = 1e-6,
    rtol: float = 1e-5,
) -> None:
    """Assert layout FID feature parity."""
    torch.testing.assert_close(actual, expected, atol=atol, rtol=rtol)

assert_statistics_shape

assert_statistics_shape(
    mu: Float[ndarray, "channels"],
    sigma: Float[ndarray, "channels channels"],
) -> None

Assert reference statistics have compatible shapes.

Source code in models/layout-fid/src/layout_fid/testing.py
21
22
23
24
25
26
27
28
29
30
def assert_statistics_shape(
    mu: Float[np.ndarray, "channels"],
    sigma: Float[np.ndarray, "channels channels"],
) -> None:
    """Assert reference statistics have compatible shapes."""
    if mu.ndim != 1:
        raise AssertionError("mu must be one-dimensional")

    if sigma.shape != (mu.shape[0], mu.shape[0]):
        raise AssertionError("sigma must be square with feature_dim rows")