File size: 5,019 Bytes
7c5e40e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
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
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
"""Configuration for STRATA neural models."""

from __future__ import annotations

import json
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any


@dataclass(frozen=True, slots=True)
class StrataConfig:
    """Plain-PyTorch decoder configuration.

    The config intentionally has no DeepSpeed, CPU offload, or distributed
    runtime knobs. Those concerns must stay outside the core model.
    """

    vocab_size: int = 64_288
    max_position_embeddings: int = 4096
    d_model: int = 768
    num_layers: int = 12
    num_heads: int = 12
    d_ff: int = 3072
    dropout: float = 0.0
    local_attention_window: int = 1024
    predicate_block_every: int = 3
    graph_relation_types: int = 16
    node_type_vocab_size: int = 16
    chart_type_vocab_size: int = 64
    max_graph_edges_per_token: int = 8
    use_attention_bias_from_graph: bool = True
    # Optional first-class graph-object memory path. Defaults off so existing
    # checkpoints instantiate the exact legacy architecture and load strictly.
    use_graph_object_memory: bool = False
    graph_object_relation_types: int = 70
    graph_object_node_types: int = 20
    graph_object_gate_init: float = -4.0
    # v5 relation-conditioned message algebra. When enabled, graph-object edge
    # values are relation-conditioned messages over (src, dst, relation), rather
    # than generic src+dst+relation slots.
    graph_object_relation_conditioned_messages: bool = False
    # v7 predicate-slot memory. Relation labels select role-addressed predicate
    # slots: edge(src, relation, predicate) writes src into slot(predicate,
    # relation). This is the typed variable-binding path used to test whether
    # labels become operative rather than merely decodable edge features.
    graph_object_predicate_slot_memory: bool = False
    # When False (default), the node/chart/edge prediction heads are computed only
    # on the final predicate block (the ones the losses/eval consume). Set True to
    # restore the legacy behaviour where every predicate block emits them.
    emit_all_block_graph_heads: bool = False
    tie_word_embeddings: bool = True
    initializer_range: float = 0.02
    pad_token_id: int = 0
    bos_token_id: int = 1
    eos_token_id: int = 2

    def __post_init__(self) -> None:
        self.validate()

    def validate(self) -> None:
        if self.vocab_size <= 0:
            raise ValueError("vocab_size must be positive")
        if self.max_position_embeddings <= 0:
            raise ValueError("max_position_embeddings must be positive")
        if self.d_model <= 0:
            raise ValueError("d_model must be positive")
        if self.num_layers <= 0:
            raise ValueError("num_layers must be positive")
        if self.num_heads <= 0:
            raise ValueError("num_heads must be positive")
        if self.d_model % self.num_heads != 0:
            raise ValueError("d_model must be divisible by num_heads")
        if self.d_ff <= 0:
            raise ValueError("d_ff must be positive")
        if not 0.0 <= self.dropout < 1.0:
            raise ValueError("dropout must be in [0, 1)")
        if self.local_attention_window <= 0:
            raise ValueError("local_attention_window must be positive")
        if self.predicate_block_every <= 0:
            raise ValueError("predicate_block_every must be positive")
        if self.graph_relation_types <= 0:
            raise ValueError("graph_relation_types must be positive")
        if self.node_type_vocab_size <= 0:
            raise ValueError("node_type_vocab_size must be positive")
        if self.chart_type_vocab_size <= 0:
            raise ValueError("chart_type_vocab_size must be positive")
        if self.max_graph_edges_per_token <= 0:
            raise ValueError("max_graph_edges_per_token must be positive")
        if self.graph_object_relation_types <= 0:
            raise ValueError("graph_object_relation_types must be positive")
        if self.graph_object_node_types <= 0:
            raise ValueError("graph_object_node_types must be positive")

    @property
    def head_dim(self) -> int:
        return self.d_model // self.num_heads

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)

    @classmethod
    def from_dict(cls, values: dict[str, Any]) -> "StrataConfig":
        return cls(**values)

    @classmethod
    def from_json_file(cls, path: str | Path) -> "StrataConfig":
        with Path(path).open("r", encoding="utf-8") as handle:
            payload = json.load(handle)
        if not isinstance(payload, dict):
            raise ValueError("config JSON must contain an object")
        return cls.from_dict(payload)

    def to_json_file(self, path: str | Path) -> None:
        destination = Path(path)
        destination.parent.mkdir(parents=True, exist_ok=True)
        with destination.open("w", encoding="utf-8") as handle:
            json.dump(self.to_dict(), handle, indent=2, sort_keys=True)
            handle.write("\n")