VII · Apps / CHAPTER 70
CamSync — inter-cameral / cross-mesh weight blend
github.com/openfluke/welvet/layers/parallel✅ CamSync
Why it exists
Mix BranchModes and different inits let cams diverge on purpose. Sometimes you want them to share — gently (1% pull) or hard (full average) — within a Parallel, across Stack children, or even between same-shaped stores sitting on wildly different mesh layouts (1×2×1 ↔ 2×4×5). That is CamSync: couple weights without collapsing the graph into one Dense.
What it is
CamSyncConfig{Alpha, When, Groups, Cross}. BlendStores pulls every store in a clique toward the mean. Empty Groups = all cams; Cross wires SyncEndpoint pairs across layers/cams. Shape rule: Rows×Cols must match. Bidirectional today; one-way teacher→student not yet.
Parallel mid (n cams) Stack / mesh (any DxHxW)
┌─ cam0 W ─┐ cell A 1×2×1 cell B 2×4×5
│ cam1 W │ Groups / all-cams layer-1 Dense cam3 layer-1 Dense
│ cam2 W │ α·mean blend OR [128×64] ←──Cross──→ [128×64]
└─ cam3 W ─┘ (mesh size irrelevant — shape is)
│
SyncAfterSample | Step | Pulse | Manual SyncNow1×2×1 cell and another store sits on
cam 3 of a 2×4×5. Name both endpoints; if Rows×Cols match, they blend.
Volumetric layout is configuration — not a built-in grid type inside CamSync.Knobs
| Field | Meaning |
|---|---|
Enabled / Alpha |
Soft pull w ← (1−α)·w + α·mean. 0.01 = 1%, 1.0 = hard sync.
Default α is 1 when Enabled and Alpha ≤ 0. |
When |
SyncManual · SyncAfterSample · SyncAfterStep · SyncAfterPulse.
Hooks fire from TrainStackMSE/CE and Pulse(). |
Groups |
Within one Parallel: cam-index cliques. Empty ⇒ one group of every branch.
Example: {{0,1},{2,3}} syncs pairs only. |
Cross |
[]SyncPair of SyncEndpoint{StackIdx, Branch, Store} —
same layer across cams, Dense stem ↔ matching cam Dense, or any same-shaped pair the Stack can resolve. |
What works / what does not (yet)
- ✅ All cams together, selective groups, soft or hard α
- ✅ Cross-layer / cross-cam pairs inside one Stack
- ✅ “Same layer on a different mesh” when weight shapes match
- ✅ v1.1.1 — cam adjusting on all layer types (Dense, CNN, Parallel, Stack-resolved stores)
- ❌ One-directional (teacher → student) — pairs are mutual mean-blend
- ❌ Mismatched
Rows×Cols— rejected or skipped - ❌ Auto sync across two separate Stack instances — host calls
weights.BlendStoreson the two stores
API
parallel.CamSyncConfig·DefaultCamSync()·SetCamSyncon Layer or StackSyncNow/Pulse/MaybeSyncweights.BlendStores·weights.StoreCosine(diagnostics)
Cameral sandwiches: §68. Parallel package: §27.
Host probe: ok/cam_sync (MNIST sample α×cams matrix).
Go example
cd welvet/examples/70-cam-sync && source ../env.sh && go run .package main
import (
"fmt"
"github.com/openfluke/welvet/core"
"github.com/openfluke/welvet/layers/dense"
"github.com/openfluke/welvet/layers/parallel"
"github.com/openfluke/welvet/quant"
"github.com/openfluke/welvet/stub/seed"
"github.com/openfluke/welvet/weights"
)
func main() {
s, err := parallel.Bicameral(8, 16, 1, core.ActivationLeakyReLU,
core.DTypeFloat32, quant.FormatNone)
if err != nil {
panic(err)
}
hemi := s.Children[1].(*parallel.Layer)
left := hemi.Branches[0].(*dense.Layer).Weights
right := hemi.Branches[1].(*dense.Layer).Weights
_ = seed.InitStoreHe(left, 16, seed.From("cam0"))
_ = seed.InitStoreHe(right, 16, seed.From("cam1"))
hemi.SetCamSync(parallel.CamSyncConfig{
Enabled: true,
Alpha: 1.0,
When: parallel.SyncManual,
})
before, _ := weights.StoreCosine(left, right)
if err := hemi.SyncNow(); err != nil {
panic(err)
}
after, _ := weights.StoreCosine(left, right)
fmt.Printf("cosine before=%.4f after=%.4f\n", before, after)
}
Output
cosine before=-0.0697 after=1.0000