Book contents +

VII · Apps / CHAPTER 68

Cameral sandwiches + AAI Lucy

Samuel Watson · Welvet feature bookOriginal source ↗

github.com/openfluke/welvet/layers/parallel✅ cameral


Why it exists

A single Dense chain cannot host two independent weight copies that share an input, merge, and optionally train under different updates. That is the cameral graph: hemispheres, not screen-space sprites and not a second hidden size.

What it is

Hemispheres / Bicameral / Sandwich / Mix. Stem → Parallel mid → Dense head. SetBranchModes + TrainStackMSE. Lucy races in AAI (test41 / test48 / test50) import this API; measuring is lucy/; harness is not engine.

x  →  Dense stem  →  ┬→ Hemi 1 (own W, own TrainMode)
                     ├→ Hemi 2
                     └→ Hemi n     merge add|avg|concat|filter
                              → Dense head → ŷ
                              MSE → g_y → each branch’s update
Sandwich: stem and head are shared; hemispheres are sibling copies.

Why cameral exists

Welvet already has Sequential (ordered compose) and Residual (skip). Neither is “two brains on one x.” A hemisphere is an independent sub-net with its own weights. Bi / Tri / Quad are n hemispheres plus a merge. Mix stamps a different TrainMode per hemi (SetBranchModes) so left can be StepBP while right is TweenChain — one loss on the merge. Grid training.Step applies one mode to the whole net; Mix needs TrainStackMSE or the BranchModes stamp is a lie.

The sandwich (stem → mid → head) is not extra depth for its own sake. Credit modes need a head Jacobian (or a W_head^T proxy). Without a head, FastProxy / HeadProxy have nothing to inject. Stem gets the down-going vector after hemispheres merge. That is why AAI Lucy jobs are sandwiches even when cameral count is 1 (mid is a single Dense, not Parallel).

API (engine, public)

  • Hemispheres / HemispheresFrom — n twins; merge add/avg/concat/filter
  • Bicameral / BicameralFrom / Sandwich — stem → Parallel → head Stack
  • SetBranchModes / TrainStackMSE — Mix path
  • ResidualGraft — y = F(x)+x when F is Parallel
  • WriteCameralFile / LoadCameral — cameral .entity (in model/entity)
  • CamSync — optional weight averaging across cams / layers → §70

Layer package chapter: §27. Named updates: §67. Weight sync across cams (and across mesh layouts when shapes match): §70.

AAI Lucy benches

AAI is a separate tree. It replaces github.com/openfluke/welvet (public chaosglue module). The engine does not import AAI. Lucy math is §66.

BenchWhat it answers
test41Native cameral Lucy (Bi/Tri/Mix) on toys — does the sandwich train at all.
test48Credit sweep: layers × dtypes × short jobs. Home of the equations in §67.
test50Deep FP32 race: all 29 named modes × Dense/Bi/Tri × 1³/2³/3³ origin-only. Rival = hard Acc vs StepBP.

test50 copy: Split / Alt / MeshSplit family about +5 to +12 Acc over StepBP (~68–74%). Sine: Acc ceiling (many modes 100% including StepBP); FastProxy SoftAcc ~56–68 vs StepBP ~42–53. XOR: almost everyone at 75% (3 of 4 bits) — parking lot. Sparse wins Score, not Acc. Origin-only cubes: extra cells are disabled; 3³ is hop topology, not 27 copies.

w2a Test49 is the permutation smoke of those 29 modes (in [0] Run ALL), not a Lucy race. Do not quote Test49 as “we beat backprop.”

Go example

examples/68-cameral/main.go

Run:cd welvet/examples/68-cameral && source ../env.sh && go run .
package main

import (
	"fmt"

	"github.com/openfluke/welvet/core"
	"github.com/openfluke/welvet/layers/parallel"
	"github.com/openfluke/welvet/quant"
)

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)
	hemi.SetBranchModes(parallel.ModeStepBP, parallel.ModeTweenSplitFastProxy)

	x := core.NewTensor[float32](1, 8)
	t := core.NewTensor[float32](1, 1)
	t.Data[0] = 0.5
	loss, err := parallel.TrainStackMSE(s, x, t, parallel.ModeStepBP, 0.01)
	fmt.Println("mix loss", loss, "err", err)
}

Output

exit 0 · last run via go run .

mix loss 0 err <nil>