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
|