| """ |
| FusionOps V2 - Data Models |
| Compact, typed models designed for LLM-native observation and action spaces. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import json |
| from dataclasses import dataclass, field |
| from enum import Enum |
| from typing import Optional |
|
|
|
|
| class OpType(str, Enum): |
| MATMUL = "matmul" |
| CONV2D = "conv2d" |
| RELU = "relu" |
| GELU = "gelu" |
| LAYERNORM = "layernorm" |
| SOFTMAX = "softmax" |
| ADD = "add" |
| TRANSPOSE = "transpose" |
| REDUCE = "reduce" |
|
|
|
|
| |
| POINTWISE_OPS = {OpType.RELU, OpType.GELU, OpType.ADD, OpType.TRANSPOSE} |
| REDUCTION_OPS = {OpType.LAYERNORM, OpType.SOFTMAX, OpType.REDUCE} |
| COMPUTE_OPS = {OpType.MATMUL, OpType.CONV2D} |
|
|
|
|
| @dataclass |
| class Node: |
| """A single operation in the computation graph.""" |
| id: int |
| op: OpType |
| shape: list[int] |
| inputs: list[int] |
| flops: float |
| output_bytes: int |
| input_bytes: list[int] |
|
|
|
|
| @dataclass |
| class HardwareSpec: |
| fast_mem_capacity: int |
| slow_mem_bandwidth: float |
| kernel_launch_cost: float |
| max_fusion_depth: int |
|
|
|
|
| @dataclass |
| class Graph: |
| """Computation graph with hardware constraints.""" |
| nodes: list[Node] |
| hardware: HardwareSpec |
| |
| _successors: dict[int, list[int]] = field(default_factory=dict, repr=False) |
| _consumers_count: dict[int, int] = field(default_factory=dict, repr=False) |
|
|
| def __post_init__(self): |
| self._build_derived() |
|
|
| def _build_derived(self): |
| self._successors = {n.id: [] for n in self.nodes} |
| self._consumers_count = {n.id: 0 for n in self.nodes} |
| for n in self.nodes: |
| for pred_id in n.inputs: |
| self._successors[pred_id].append(n.id) |
| self._consumers_count[pred_id] = self._consumers_count.get(pred_id, 0) + 1 |
|
|
| def successors(self, node_id: int) -> list[int]: |
| return self._successors.get(node_id, []) |
|
|
| def future_uses(self, node_id: int) -> int: |
| """How many downstream ops still need this node's output.""" |
| return self._consumers_count.get(node_id, 0) |
|
|
| def topo_order(self) -> list[int]: |
| """Return nodes in topological order.""" |
| from collections import deque |
| in_degree = {} |
| for n in self.nodes: |
| in_degree[n.id] = len(n.inputs) |
| queue = deque(nid for nid, d in in_degree.items() if d == 0) |
| order = [] |
| while queue: |
| nid = queue.popleft() |
| order.append(nid) |
| for succ in self._successors[nid]: |
| in_degree[succ] -= 1 |
| if in_degree[succ] == 0: |
| queue.append(succ) |
| return order |
|
|
|
|
| @dataclass |
| class Action: |
| """Agent's per-step decision.""" |
| fuse_with_prev: bool |
| tile: int |
| retain: list[int] |
|
|
| VALID_TILES = [32, 64, 128, 256] |
|
|
|
|
| @dataclass |
| class FusionGroup: |
| """A group of ops that will execute as one kernel.""" |
| node_ids: list[int] |
| tile: int |
| retained: list[int] |
| latency: float = 0.0 |
|
|
|
|
| @dataclass |
| class ScheduleState: |
| """Mutable state for an episode.""" |
| step: int = 0 |
| current_node_idx: int = 0 |
| fusion_groups: list[FusionGroup] = field(default_factory=list) |
| fast_mem_contents: set[int] = field(default_factory=set) |
| fast_mem_used: int = 0 |
| total_latency: float = 0.0 |
| total_reloads: int = 0 |
| total_kernel_launches: int = 0 |
| |
| remaining_uses: dict[int, int] = field(default_factory=dict) |
| |
| current_group: Optional[FusionGroup] = None |
|
|
| def clone(self) -> ScheduleState: |
| import copy |
| return copy.deepcopy(self) |
|
|