feat(landscape): computation graph, params and per-pixel kernels

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 13:16:17 +05:00
parent 922812b765
commit 831ddd1154
6 changed files with 761 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
package landscape
import (
"errors"
"fmt"
)
// Params are the run-time knobs of the graph (TZ §6): window size, tie
// priority for functional zoning, and the V1/V2 suitability thresholds.
type Params struct {
WindowSize int `json:"window_size"`
TiePriority []int `json:"tie_priority"`
V1MinSE12 int `json:"v1_min_s_e12"`
V1MinSE3 int `json:"v1_min_s_e3"`
V2MinSE3 int `json:"v2_min_s_e3"`
V2MinSE4 int `json:"v2_min_s_e4"`
}
// DefaultParams returns the TZ defaults.
func DefaultParams() Params {
return Params{WindowSize: 2048, TiePriority: []int{1, 2, 3, 4, 5}, V1MinSE12: 3, V1MinSE3: 4, V2MinSE3: 3, V2MinSE4: 5}
}
// WithDefaults fills zero-valued fields from DefaultParams.
func (p Params) WithDefaults() Params {
d := DefaultParams()
if p.WindowSize == 0 {
p.WindowSize = d.WindowSize
}
if len(p.TiePriority) == 0 {
p.TiePriority = d.TiePriority
}
if p.V1MinSE12 == 0 {
p.V1MinSE12 = d.V1MinSE12
}
if p.V1MinSE3 == 0 {
p.V1MinSE3 = d.V1MinSE3
}
if p.V2MinSE3 == 0 {
p.V2MinSE3 = d.V2MinSE3
}
if p.V2MinSE4 == 0 {
p.V2MinSE4 = d.V2MinSE4
}
return p
}
// Validate checks ranges: window 256..8192, thresholds 1..5, tie_priority a
// permutation of 1..5.
func (p Params) Validate() error {
var errs []error
if p.WindowSize < 256 || p.WindowSize > 8192 {
errs = append(errs, fmt.Errorf("window_size must be in 256..8192"))
}
for name, v := range map[string]int{"v1_min_s_e12": p.V1MinSE12, "v1_min_s_e3": p.V1MinSE3, "v2_min_s_e3": p.V2MinSE3, "v2_min_s_e4": p.V2MinSE4} {
if v < 1 || v > 5 {
errs = append(errs, fmt.Errorf("%s must be in 1..5", name))
}
}
seen := map[int]bool{}
for _, k := range p.TiePriority {
if k < 1 || k > 5 || seen[k] {
errs = append(errs, fmt.Errorf("tie_priority must be a permutation of 1..5"))
break
}
seen[k] = true
}
if len(p.TiePriority) != 5 && len(seen) == len(p.TiePriority) {
errs = append(errs, fmt.Errorf("tie_priority must list all of 1..5"))
}
return errors.Join(errs...)
}
// ForNode keeps only the fields that influence the node's result (window size
// never does), so fingerprints do not change when irrelevant knobs change.
func (p Params) ForNode(code string) Params {
if code == "30" {
return Params{TiePriority: p.TiePriority, V1MinSE12: p.V1MinSE12, V1MinSE3: p.V1MinSE3, V2MinSE3: p.V2MinSE3, V2MinSE4: p.V2MinSE4}
}
return Params{}
}