Skip to content

Commit c7a1972

Browse files
gestaltclaude
andcommitted
Dedupe ternary_add/ternary_mul; remove dead EpochMetrics/ControllerState
ternary.py: ternary_add and ternary_mul were identical except for + vs * in the digit-wise combination. Extracted _ternary_digitwise_modular_op. This is the most foundational, most-depended-on file in the codebase, so verified against an independently-written reference implementation on 5000 random index pairs (not just the existing test suite) before trusting it, in addition to running all core ternary tests and the full suite. contracts.py: removed EpochMetrics and ControllerState, both defined but never referenced anywhere else in the repo. Dropped the now-unused dataclass/Union imports. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent bee459e commit c7a1972

2 files changed

Lines changed: 26 additions & 58 deletions

File tree

src/core/contracts.py

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66
"""Type-safe contracts for p-adic VAE components."""
77

8-
from dataclasses import dataclass
9-
from typing import Any, Dict, List, Optional, TypedDict, Union
8+
from typing import Any, Dict, List, Optional, TypedDict
109

1110
import torch
1211

@@ -84,39 +83,6 @@ class TrainingResults(TypedDict):
8483
grokking_events: List[Dict[str, Any]]
8584

8685

87-
@dataclass
88-
class EpochMetrics:
89-
"""Metrics for a single training/validation epoch."""
90-
epoch: int
91-
train_loss: float
92-
train_acc: float
93-
val_acc: float
94-
val_coverage: float
95-
hierarchy_A: float
96-
hierarchy_B: float
97-
Q_A: float
98-
Q_B: float
99-
dist_corr: float
100-
mean_radius_A: float
101-
tree_coherence_A: float
102-
tree_coherence_B: float
103-
# Optional detailed metrics
104-
level_hierarchy: Optional[Dict[int, float]] = None
105-
ari_per_level: Optional[Dict[int, float]] = None
106-
ari_composite: Optional[float] = None
107-
aq_value: Optional[float] = None
108-
intra_sim: Optional[float] = None
109-
inter_sim: Optional[float] = None
110-
111-
112-
class ControllerState(TypedDict):
113-
"""Output from the MetricBasedLR controller."""
114-
lr_scales: Dict[str, float]
115-
events: List[Dict[str, Any]]
116-
status: Dict[str, Union[bool, float, int, str]]
117-
best_q: float
118-
119-
12086
class GrokkingState(TypedDict):
12187
"""Output from the GrokkingDetector."""
12288
plateau: bool

src/core/ternary.py

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,29 @@ def from_ternary(self, ternary: torch.Tensor) -> torch.Tensor:
442442
# Compute index as base-3 number
443443
return (digits * weights).sum(dim=-1)
444444

445+
def _ternary_digitwise_modular_op(
446+
self, idx_a: torch.Tensor, idx_b: torch.Tensor, op
447+
) -> torch.Tensor:
448+
"""Shared digit-wise Z_3 arithmetic for ternary_add/ternary_mul.
449+
450+
Converts both indices to ternary, maps {-1,0,1} digits to standard
451+
Z_3 representation {2,0,1} via `% 3`, applies `op` digit-wise mod 3,
452+
then maps the {0,1,2} result back to {-1,0,1} (2 → -1) before
453+
converting back to an index.
454+
455+
Args:
456+
idx_a, idx_b: Tensors of indices, shape (N,)
457+
op: Digit-wise combination, e.g. torch.add or torch.mul
458+
459+
Returns:
460+
Tensor of indices of the result, shape (N,)
461+
"""
462+
d_a = self.to_ternary(idx_a) % 3
463+
d_b = self.to_ternary(idx_b) % 3
464+
d_result = op(d_a, d_b) % 3
465+
t_result = d_result - 3.0 * (d_result == 2.0).to(d_result.dtype)
466+
return self.from_ternary(t_result)
467+
445468
def ternary_add(self, idx_a: torch.Tensor, idx_b: torch.Tensor) -> torch.Tensor:
446469
"""Perform 3-adic modular addition of two indices.
447470
@@ -459,18 +482,7 @@ def ternary_add(self, idx_a: torch.Tensor, idx_b: torch.Tensor) -> torch.Tensor:
459482
Returns:
460483
Tensor of indices of the sums, shape (N,)
461484
"""
462-
t_a = self.to_ternary(idx_a) # (N, 9)
463-
t_b = self.to_ternary(idx_b) # (N, 9)
464-
465-
# Map {-1, 0, 1} to {2, 0, 1} which is standard Z_3
466-
d_a = t_a % 3
467-
d_b = t_b % 3
468-
469-
# Modular addition in {0, 1, 2}, then map 2 → -1 via arithmetic.
470-
# d - 3*(d==2): 0→0, 1→1, 2→2-3=-1
471-
d_sum = (d_a + d_b) % 3
472-
t_sum = d_sum - 3.0 * (d_sum == 2.0).to(d_sum.dtype)
473-
return self.from_ternary(t_sum)
485+
return self._ternary_digitwise_modular_op(idx_a, idx_b, torch.add)
474486

475487
def ternary_mul(self, idx_a: torch.Tensor, idx_b: torch.Tensor) -> torch.Tensor:
476488
"""Perform 3-adic modular multiplication of two indices.
@@ -488,17 +500,7 @@ def ternary_mul(self, idx_a: torch.Tensor, idx_b: torch.Tensor) -> torch.Tensor:
488500
Returns:
489501
Tensor of indices of the products, shape (N,)
490502
"""
491-
t_a = self.to_ternary(idx_a)
492-
t_b = self.to_ternary(idx_b)
493-
494-
# Standard multiplication in Z_3 {0, 1, 2}
495-
# (-1) maps to 2 in % 3
496-
d_a = t_a % 3
497-
d_b = t_b % 3
498-
499-
d_prod = (d_a * d_b) % 3
500-
t_prod = d_prod - 3.0 * (d_prod == 2.0).to(d_prod.dtype)
501-
return self.from_ternary(t_prod)
503+
return self._ternary_digitwise_modular_op(idx_a, idx_b, torch.mul)
502504

503505
# =========================================================================
504506
# Convenience Methods

0 commit comments

Comments
 (0)