File size: 6,875 Bytes
fc3e70a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

"""Phase 6: Matrix semantic layer β€” semantic operations represented as matrix structures.

Semantic matrix β‰  numerical matrix.
A SemanticMatrix captures the structure of an operation (operand slots, geometry,
relationships) in a matrix-like form. Matrix data and semantic matrix are distinct types.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional

from .ir import Geometry, Operand, SemanticNode, SemanticOperation
from .types import FSMState, GeometryError, SemanticNodeType


# ── Semantic matrix cell ──────────────────────────────────────────────────────

@dataclass
class SemanticCell:
    row: int
    col: int
    operand: Optional[Operand] = None
    label: str = ""
    role: str = ""          # "input", "output", "contraction", "constraint"


# ── Semantic matrix ───────────────────────────────────────────────────────────

@dataclass
class SemanticMatrix:
    operation: SemanticOperation
    rows: int
    cols: int
    cells: list[list[SemanticCell]] = field(default_factory=list)
    geometry: Optional[Geometry] = None
    node_id: str = ""

    def __post_init__(self):
        if not self.cells:
            self.cells = [
                [SemanticCell(row=r, col=c) for c in range(self.cols)]
                for r in range(self.rows)
            ]

    def cell(self, row: int, col: int) -> SemanticCell:
        if row >= self.rows or col >= self.cols:
            raise IndexError(f"SemanticMatrix[{row},{col}] out of bounds ({self.rows}x{self.cols})")
        return self.cells[row][col]

    def set_cell(self, row: int, col: int, operand: Optional[Operand], role: str, label: str = "") -> None:
        c = self.cell(row, col)
        c.operand = operand
        c.role = role
        c.label = label

    def column(self, col: int) -> list[SemanticCell]:
        return [self.cells[r][col] for r in range(self.rows)]

    def row_vec(self, row: int) -> list[SemanticCell]:
        return list(self.cells[row])

    def to_dict(self) -> dict:
        return {
            "operation": self.operation.name,
            "shape": (self.rows, self.cols),
            "geometry": {
                "row": self.geometry.row if self.geometry else None,
                "col": self.geometry.col if self.geometry else None,
            },
            "cells": [
                [{"row": c.row, "col": c.col, "role": c.role, "label": c.label} for c in row]
                for row in self.cells
            ],
        }


# ── Builder ───────────────────────────────────────────────────────────────────

class SemanticMatrixBuilder:
    """Construct a SemanticMatrix from a SemanticNode."""

    def build(self, node: SemanticNode) -> SemanticMatrix:
        op = node.operation
        if op is None:
            op = SemanticOperation(name="identity", arity=1)

        operands = node.operands
        n_ops = len(operands)

        if op.name in ("multiply", "matmul"):
            return self._build_matmul(op, operands, node.geometry)

        if op.name in ("add", "subtract"):
            return self._build_binary(op, operands, node.geometry)

        # Generic: one row per operand, two cols (input / output)
        mat = SemanticMatrix(operation=op, rows=max(n_ops, 1), cols=2, geometry=node.geometry)
        for i, operand in enumerate(operands):
            mat.set_cell(i, 0, operand, role="input", label=operand.name)
        return mat

    def _build_matmul(
        self, op: SemanticOperation, operands: list[Operand], geom: Optional[Geometry]
    ) -> SemanticMatrix:
        """
        Matrix multiply: A(mΓ—n) @ B(nΓ—p) β†’ C(mΓ—p)
        Represent as 3x3:
          [A_input] [contract] [B_input]
          [m_dim]   [n_dim]   [p_dim]
          [output]  [output]  [output]
        """
        mat = SemanticMatrix(operation=op, rows=3, cols=3, geometry=geom)
        a = operands[0] if len(operands) > 0 else None
        b = operands[1] if len(operands) > 1 else None

        mat.set_cell(0, 0, a, role="input", label=a.name if a else "A")
        mat.set_cell(0, 2, b, role="input", label=b.name if b else "B")
        mat.set_cell(0, 1, None, role="contraction", label="n")

        if geom:
            mat.set_cell(1, 0, None, role="dim", label=str(geom.row or "m"))
            mat.set_cell(1, 1, None, role="dim", label=str(geom.contraction or "n"))
            mat.set_cell(1, 2, None, role="dim", label=str(geom.col or "p"))

        mat.set_cell(2, 0, None, role="output", label="C")
        mat.set_cell(2, 1, None, role="output", label="(mΓ—p)")

        return mat

    def _build_binary(
        self, op: SemanticOperation, operands: list[Operand], geom: Optional[Geometry]
    ) -> SemanticMatrix:
        mat = SemanticMatrix(operation=op, rows=2, cols=2, geometry=geom)
        a = operands[0] if len(operands) > 0 else None
        b = operands[1] if len(operands) > 1 else None
        mat.set_cell(0, 0, a, role="input", label=a.name if a else "A")
        mat.set_cell(0, 1, b, role="input", label=b.name if b else "B")
        mat.set_cell(1, 0, None, role="output", label="result")
        return mat


# ── Transform interface ───────────────────────────────────────────────────────

def transform(a: SemanticMatrix, b: SemanticMatrix) -> SemanticMatrix:
    """
    Compose two semantic matrices β€” analogous to function composition.
    a: R^(mΓ—n), b: R^(nΓ—p) β†’ R^(mΓ—p)
    """
    if a.cols != b.rows:
        raise GeometryError(
            FSMState.MATRIX_FORM,
            f"SemanticMatrix composition: a.cols={a.cols} β‰  b.rows={b.rows}",
        )
    composed_op = SemanticOperation(
        name=f"{a.operation.name}∘{b.operation.name}",
        arity=a.operation.arity,
    )
    out_geom: Optional[Geometry] = None
    if a.geometry and b.geometry:
        out_geom = a.geometry.output_geometry(b.geometry)

    mat = SemanticMatrix(operation=composed_op, rows=a.rows, cols=b.cols, geometry=out_geom)
    # Populate diagonal β€” first operands of a flow into outputs of b
    for r in range(min(a.rows, b.rows)):
        src = a.cells[r][0]
        mat.set_cell(r, 0, src.operand, role="composed", label=src.label)
    return mat