skills/model-equivariance-auditor/SKILL.md
Verifies that implemented neural network models correctly respect their intended symmetries through systematic equivariance testing, layer-wise isolation, and gradient analysis. Use when testing model equivariance, debugging symmetry bugs, verifying implementation correctness, checking if a model is actually equivariant, or diagnosing why an equivariant model isn't working.
npx skillsauth add lyndonkl/claude model-equivariance-auditorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Even with equivariant libraries, implementation bugs can break equivariance. A model that claims equivariance but lacks it will train poorly and give inconsistent predictions. Catching these bugs early saves significant debugging time.
Copy this checklist and track your progress:
Equivariance Audit Progress:
- [ ] Step 1: Gather model and symmetry specification
- [ ] Step 2: Run numerical equivariance tests
- [ ] Step 3: Test individual layers
- [ ] Step 4: Check gradient equivariance
- [ ] Step 5: Identify and diagnose failures
- [ ] Step 6: Document audit results
Step 1: Gather model and symmetry specification
Collect: the implemented model, the intended symmetry group, whether each output should be invariant or equivariant, the transformation functions for input and output spaces. Review the architecture specification from design phase. Clarify ambiguities with user before testing.
Step 2: Run numerical equivariance tests
Execute end-to-end equivariance tests using Test Implementation. For invariance: verify ||f(T(x)) - f(x)|| < ε. For equivariance: verify ||f(T(x)) - T'(f(x))|| < ε. Use multiple random inputs and transformations. Record error statistics. See Error Interpretation for thresholds. For ready-to-use test code, see Test Code Templates.
Step 3: Test individual layers
If end-to-end test fails, isolate the problem by testing layers individually. For each layer: freeze other layers, test equivariance of that layer alone. This identifies which layer breaks equivariance. Use Layer-wise Testing protocol. Check nonlinearities, normalizations, and custom operations especially carefully.
Step 4: Check gradient equivariance
Verify that gradients also respect equivariance (important for training). Compute gradients at x and T(x). Check that gradients transform appropriately. Gradient bugs can cause training to "unlearn" equivariance. See Gradient Testing.
Step 5: Identify and diagnose failures
If tests fail, use Common Failure Modes to diagnose. Check: non-equivariant nonlinearities, batch normalization issues, incorrect output transformation, numerical precision problems, implementation bugs in custom layers. Provide specific fix recommendations. For step-by-step troubleshooting, consult Debugging Guide.
Step 6: Document audit results
Create audit report using Output Template. Include: pass/fail for each test, error magnitudes, identified issues, and recommendations. Distinguish between: exact equivariance (numerical precision), approximate equivariance (acceptable error), and broken equivariance (needs fixing). For detailed audit methodology, see Methodology Details. Quality criteria for this output are defined in Quality Rubric.
import torch
def test_model_equivariance(model, x, input_transform, output_transform,
n_tests=100, tol=1e-5):
"""
Test if model is equivariant: f(T(x)) ≈ T'(f(x))
Args:
model: The neural network to test
x: Sample input tensor
input_transform: Function that transforms input
output_transform: Function that transforms output
n_tests: Number of random transformations to test
tol: Error tolerance
Returns:
dict with test results
"""
model.eval()
errors = []
with torch.no_grad():
for _ in range(n_tests):
# Generate random transformation
T = sample_random_transform()
# Method 1: Transform input, then apply model
x_transformed = input_transform(x, T)
y1 = model(x_transformed)
# Method 2: Apply model, then transform output
y = model(x)
y2 = output_transform(y, T)
# Compute error
error = torch.norm(y1 - y2).item()
relative_error = error / (torch.norm(y2).item() + 1e-8)
errors.append({
'absolute': error,
'relative': relative_error
})
return {
'mean_absolute': np.mean([e['absolute'] for e in errors]),
'max_absolute': np.max([e['absolute'] for e in errors]),
'mean_relative': np.mean([e['relative'] for e in errors]),
'max_relative': np.max([e['relative'] for e in errors]),
'pass': all(e['relative'] < tol for e in errors)
}
def test_model_invariance(model, x, transform, n_tests=100, tol=1e-5):
"""Test if model output is invariant to transformations."""
model.eval()
errors = []
with torch.no_grad():
y_original = model(x)
for _ in range(n_tests):
T = sample_random_transform()
x_transformed = transform(x, T)
y_transformed = model(x_transformed)
error = torch.norm(y_transformed - y_original).item()
errors.append(error)
return {
'mean_error': np.mean(errors),
'max_error': np.max(errors),
'pass': max(errors) < tol
}
def test_layer_equivariance(layer, x, input_transform, output_transform):
"""Test a single layer for equivariance."""
layer.eval()
with torch.no_grad():
T = sample_random_transform()
# Transform then layer
y1 = layer(input_transform(x, T))
# Layer then transform
y2 = output_transform(layer(x), T)
error = torch.norm(y1 - y2).item()
return {
'layer': layer.__class__.__name__,
'error': error,
'pass': error < tolerance
}
def audit_all_layers(model, x, transforms):
"""Test each layer individually."""
results = []
for name, layer in model.named_modules():
if is_testable_layer(layer):
result = test_layer_equivariance(layer, x, *transforms)
result['name'] = name
results.append(result)
return results
| Layer Type | What to Check | |------------|---------------| | Convolution | Kernel equivariance | | Nonlinearity | Should preserve equivariance | | Normalization | Often breaks equivariance | | Pooling | Correct aggregation | | Linear | Weight sharing patterns | | Attention | Permutation equivariance |
Forward pass can be equivariant while backward pass is not. This causes:
def test_gradient_equivariance(model, x, loss_fn, transform, tol=1e-4):
"""Test if gradients respect equivariance."""
model.train()
# Gradients at original input
x1 = x.clone().requires_grad_(True)
y1 = model(x1)
loss1 = loss_fn(y1)
loss1.backward()
grad1 = x1.grad.clone()
# Gradients at transformed input
model.zero_grad()
T = sample_random_transform()
x2 = transform(x.clone(), T).requires_grad_(True)
y2 = model(x2)
loss2 = loss_fn(y2)
loss2.backward()
grad2 = x2.grad.clone()
# Transform grad1 and compare to grad2
grad1_transformed = transform_gradient(grad1, T)
error = torch.norm(grad2 - grad1_transformed).item()
return {'error': error, 'pass': error < tol}
| Error Level | Interpretation | Action | |-------------|----------------|--------| | < 1e-6 | Perfect (float32 precision) | Pass | | 1e-6 to 1e-4 | Excellent (acceptable) | Pass | | 1e-4 to 1e-2 | Approximate equivariance | Investigate | | > 1e-2 | Broken equivariance | Fix required |
Use relative error when output magnitudes vary. Use absolute when comparing to numerical precision.
Symptom: Error increases after nonlinearity layers Cause: Using ReLU, sigmoid on equivariant features Fix: Use gated nonlinearities, norm-based, or restrict to invariant features
Symptom: Error varies with batch composition Cause: BN computes different stats for different orientations Fix: Use LayerNorm, GroupNorm, or equivariant batch norm
Symptom: Test fails even for identity transform Cause: output_transform doesn't match model output type Fix: Verify output transformation matches layer output representation
Symptom: Small but non-zero error everywhere Cause: Floating point accumulation, interpolation Fix: Use float64 for testing, accept small tolerance
Symptom: Error isolated to specific layer Cause: Implementation error in custom equivariant layer Fix: Review layer implementation against equivariance constraints
Symptom: Error higher near edges Cause: Padding doesn't respect symmetry Fix: Use circular padding or handle boundaries explicitly
MODEL EQUIVARIANCE AUDIT REPORT
===============================
Model: [Model name/description]
Intended Symmetry: [Group]
Symmetry Type: [Invariant/Equivariant]
END-TO-END TESTS:
-----------------
Test samples: [N]
Transformations tested: [M]
Invariance/Equivariance Error:
- Mean absolute: [value]
- Max absolute: [value]
- Mean relative: [value]
- Max relative: [value]
- RESULT: [PASS/FAIL]
LAYER-WISE ANALYSIS:
--------------------
[For each layer]
- Layer: [name]
- Error: [value]
- Result: [PASS/FAIL]
GRADIENT TEST:
--------------
- Gradient equivariance error: [value]
- RESULT: [PASS/FAIL]
IDENTIFIED ISSUES:
------------------
1. [Issue description]
- Location: [layer/component]
- Severity: [High/Medium/Low]
- Recommended fix: [description]
OVERALL VERDICT: [PASS/FAIL/NEEDS_ATTENTION]
Recommendations:
- [List of actions needed]
testing
Cluster a conference's event records into a small set of coarse themes with finer sub-clusters, an explicit outlier bucket, and soft (multi-membership) affinities — using the hybrid embed-then-label pipeline (embed abstracts, reduce, density-cluster, then LLM-label the clusters) when embedding libraries are available, and an LLM-reasoned hierarchical fallback when they are not. Embeddings do the grouping; the LLM only names the groups. Conference-agnostic. Use when turning structured event records into a navigable theme map for preference elicitation and scheduling, when you need 6-8 reasonable themes rather than 20 muddy ones, or when overlapping talks must belong to more than one theme. Trigger keywords - theme clustering, cluster talks, embed then label, soft membership, outlier talks, conference themes, topic map.
development
Build a personal conference schedule as a constraint-optimization problem — hard constraints (no time overlap, room-to-room travel time, capacity/registration, the attendee's own must-attends and blackouts) plus a user-owned weighted objective trading interest against breadth, pacing (maximize contiguous free time), and serendipity. Surfaces unbreakable conflicts (two high-value overlapping talks the model cannot rank) as decisions for the human rather than silently picking, and reports what each choice traded away. Conference-agnostic. Use to turn a preference profile plus a theme map into a day-by-day plan, to resolve overlapping sessions, or to balance a packed vs paced schedule. Trigger keywords - schedule optimization, conference schedule, constraint optimization, overlapping talks, contiguous free time, conflict surfacing, packed vs paced.
development
Parse a heterogeneous conference program (markdown, HTML, PDF-derived text, or JSON) into normalized event records with per-field confidence scores and independent classification axes (topic, depth, format, prerequisites, recorded, capacity). Detects the program's format before extracting, treats every inferred field as uncertain (present vs inferred vs missing), and flags thin or missing abstracts so downstream enrichment can target them. Conference-agnostic. Use when ingesting a conference or event schedule into a structured store, normalizing a talk/session list, or extracting per-session metadata with calibrated confidence. Trigger keywords - program ingestion, parse schedule, session extraction, event records, conference program, talk metadata, per-field confidence.
development
Build a personalized preference profile from a small number of well-chosen, cluster-grounded questions instead of a long survey. Represents the person's interests as an uncertainty region over the theme map, picks the single highest-information-gain choice-based question (contrasting real talks from different clusters), balances exploiting known interests against exploring uncertain ones, deliberately injects outlier probes to fight selection bias, and stops as soon as the schedule would be stable. Also elicits the user-owned objective weights and hard constraints. Interactive — runs where it can actually ask the person. Conference-agnostic. Use to turn a theme map into a preference profile, to decide what to ask a conference attendee, or to elicit scheduling priorities. Trigger keywords - preference elicitation, ask few questions, information gain, choice-based questions, selection bias probe, objective weights, attendee preferences.