Skip to content

Commit 5188e28

Browse files
authored
fix(cek): cost model handling (#277)
Signed-off-by: Chris Gianelloni <wolf31o2@blinklabs.io>
1 parent 8301822 commit 5188e28

3 files changed

Lines changed: 139 additions & 16 deletions

File tree

cek/builtins.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3963,12 +3963,15 @@ func dropList[T syn.Eval](m *Machine[T], b *Builtin[T]) (Value[T], error) {
39633963
// Additional CPU spending proportional to |n| to align with conformance budgets
39643964
// Empirically derived cost model parameters for dropList operation.
39653965
const (
3966-
dropListCpuPerElement int64 = 1957 // CPU cost per element to drop
39673966
dropListBaseCpuApprox int64 = 212811 // Base CPU cost approximation
3968-
dropListHugeBitLenThresh = 40 // ~1e12 threshold for "huge" requests
3967+
dropListHugeBitLenThresh int = 40 // ~1e12 threshold for "huge" requests
39693968
)
39703969
if origAbsN.Sign() != 0 {
3971-
per := big.NewInt(dropListCpuPerElement)
3970+
perElementCpu := int64(1957)
3971+
if dropListCost, ok := m.costs.builtinCosts[builtin.DropList].cpu.(*DropListCost); ok {
3972+
perElementCpu = dropListCost.slope
3973+
}
3974+
per := big.NewInt(perElementCpu)
39723975
extra := new(big.Int).Mul(per, origAbsN)
39733976
// For extremely large requests, spend just enough so base+extra ~= MaxInt64
39743977
if origAbsN.BitLen() > dropListHugeBitLenThresh {

cek/cost_model_builtins.go

Lines changed: 94 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,24 @@ func (b *BuiltinCosts) update(param string, val int64) error {
7373
paramIdx = 5
7474
}
7575

76-
// For 3-part parameter names (builtin-cpu/memory-arguments), only ConstantCost is valid
76+
// Some parameter lists use a single value for models that also have
77+
// calibrated default slopes/coefficients. In that case, update the
78+
// leading term and preserve the calibrated defaults for the rest.
7779
if len(paramParts) == 3 {
78-
if constCost, ok := args.(*ConstantCost); ok {
79-
constCost.c = val
80+
switch a := args.(type) {
81+
case *ConstantCost:
82+
a.c = val
8083
return nil
81-
} else {
82-
// For non-ConstantCost types with 3-part names, throw an error
83-
// This can happen when genesis file format differs from code expectations
84+
case *LinearCost:
85+
a.intercept = val
86+
return nil
87+
case *DropListCost:
88+
a.intercept = val
89+
return nil
90+
case *QuadraticInXModel:
91+
a.coeff0 = val
92+
return nil
93+
default:
8494
return errors.New("cannot map parameter name to costing info: " + param)
8595
}
8696
}
@@ -119,7 +129,19 @@ func (b *BuiltinCosts) update(param string, val int64) error {
119129
case *ConstAboveDiagonalModel:
120130
if len(paramParts) > 5 && paramParts[3] == "model" && paramParts[4] == "arguments" {
121131
// Accessing the nested model (e.g., divideInteger-cpu-arguments-model-arguments-intercept)
122-
if model, ok := a.model.(*MultipliedSizesModel); ok {
132+
switch model := a.model.(type) {
133+
case *LinearInXAndY:
134+
switch paramParts[5] {
135+
case "intercept":
136+
model.intercept = val
137+
case "slope1":
138+
model.slope1 = val
139+
case "slope2":
140+
model.slope2 = val
141+
default:
142+
return fmt.Errorf("unknown model param for builtin %s: %s", builtinName, paramParts[5])
143+
}
144+
case *MultipliedSizesModel:
123145
switch paramParts[5] {
124146
case "intercept":
125147
model.intercept = val
@@ -128,7 +150,7 @@ func (b *BuiltinCosts) update(param string, val int64) error {
128150
default:
129151
return fmt.Errorf("unknown model param for builtin %s: %s", builtinName, paramParts[5])
130152
}
131-
} else {
153+
default:
132154
return errors.New("unexpected model type for builtin " + builtinName)
133155
}
134156
} else {
@@ -171,15 +193,33 @@ func (b *BuiltinCosts) update(param string, val int64) error {
171193
}
172194
case *ExpMod:
173195
switch paramParts[paramIdx] {
174-
case "coeff00":
196+
case "coeff00", "coefficient00":
175197
a.coeff00 = val
176-
case "coeff11":
198+
case "coeff11", "coefficient11":
177199
a.coeff11 = val
178-
case "coeff12":
200+
case "coeff12", "coefficient12":
179201
a.coeff12 = val
180202
default:
181203
return fmt.Errorf("unknown cost param for builtin %s: %s", builtinName, paramParts[paramIdx])
182204
}
205+
case *DropListCost:
206+
switch paramParts[paramIdx] {
207+
case "intercept":
208+
a.intercept = val
209+
case "slope":
210+
a.slope = val
211+
default:
212+
return fmt.Errorf("unknown cost param for builtin %s: %s", builtinName, paramParts[paramIdx])
213+
}
214+
case *FourLinearInU:
215+
switch paramParts[paramIdx] {
216+
case "intercept":
217+
a.intercept = val
218+
case "slope":
219+
a.slope = val
220+
default:
221+
return fmt.Errorf("unknown cost param for builtin %s: %s", builtinName, paramParts[paramIdx])
222+
}
183223
case *LinearCost:
184224
switch paramParts[paramIdx] {
185225
case "intercept":
@@ -256,6 +296,17 @@ func (b *BuiltinCosts) update(param string, val int64) error {
256296
default:
257297
return fmt.Errorf("unknown cost param for builtin %s: %s", builtinName, paramParts[paramIdx])
258298
}
299+
case *QuadraticInXModel:
300+
switch paramParts[paramIdx] {
301+
case "coeff0", "c0", "intercept":
302+
a.coeff0 = val
303+
case "coeff1", "c1", "slope":
304+
a.coeff1 = val
305+
case "coeff2", "c2":
306+
a.coeff2 = val
307+
default:
308+
return fmt.Errorf("unknown cost param for builtin %s: %s", builtinName, paramParts[paramIdx])
309+
}
259310
case *QuadraticInYModel:
260311
switch paramParts[paramIdx] {
261312
case "coeff0", "c0":
@@ -354,6 +405,19 @@ func (b *BuiltinCosts) update(param string, val int64) error {
354405
default:
355406
return fmt.Errorf("unknown cost param for builtin %s: %s", builtinName, paramParts[paramIdx])
356407
}
408+
case *WithInteractionInXAndY:
409+
switch paramParts[paramIdx] {
410+
case "c00":
411+
a.c00 = val
412+
case "c01":
413+
a.c01 = val
414+
case "c10":
415+
a.c10 = val
416+
case "c11":
417+
a.c11 = val
418+
default:
419+
return fmt.Errorf("unknown cost param for builtin %s: %s", builtinName, paramParts[paramIdx])
420+
}
357421
default:
358422
return fmt.Errorf("unknown cost type for builtin %s: %T", builtinName, args)
359423
}
@@ -1003,8 +1067,10 @@ var DefaultBuiltinCosts = BuiltinCosts{
10031067
},
10041068
builtin.DropList: &CostingFunc[Arguments]{
10051069
mem: &ConstantCost{4},
1006-
// Raise baseline CPU so n=0 matches expected total budget better
1007-
cpu: &ConstantCost{116711},
1070+
cpu: &DropListCost{LinearCost{
1071+
intercept: 116711,
1072+
slope: 1957,
1073+
}},
10081074
},
10091075
builtin.LengthOfArray: &CostingFunc[Arguments]{
10101076
mem: &ConstantCost{10},
@@ -1298,6 +1364,7 @@ var (
12981364
hasConstantsFalse1 = []bool{false}
12991365
hasConstantsFalseTrue = []bool{false, true}
13001366
hasConstantsTrueFalse = []bool{true, false}
1367+
hasConstantsTrueTrue = []bool{true, true}
13011368
hasConstantsFalseFalse = []bool{false, false}
13021369
hasConstantsFalseFalseFalse = []bool{false, false, false}
13031370
hasConstantsFalseTrueTrue = []bool{false, true, true}
@@ -1341,6 +1408,20 @@ func (l LinearCost) Cost(x ExMem) int64 {
13411408
return l.slope*int64(x) + l.intercept
13421409
}
13431410

1411+
type DropListCost struct {
1412+
LinearCost
1413+
}
1414+
1415+
func (d DropListCost) CostTwo(x, y ExMem) int64 {
1416+
return d.intercept
1417+
}
1418+
1419+
func (DropListCost) HasConstants() []bool {
1420+
return hasConstantsTrueTrue
1421+
}
1422+
1423+
func (DropListCost) isArguments() {}
1424+
13441425
// TwoArgument interface for costing functions with two arguments
13451426
type TwoArgument interface {
13461427
CostTwo(x, y ExMem) int64

cek/cost_model_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,45 @@ func TestUpdateV3CostModel(t *testing.T) {
299299
}
300300
}
301301

302+
func TestUpdateV3CostModelWithExpModCoefficientNames(t *testing.T) {
303+
params := make([]int64, len(lang.CostModelParamNamesV3))
304+
var expModIdx int
305+
foundExpMod := false
306+
for i, name := range lang.CostModelParamNamesV3 {
307+
if name == "expModInteger-cpu-arguments-coefficient00" {
308+
expModIdx = i
309+
foundExpMod = true
310+
break
311+
}
312+
}
313+
if !foundExpMod {
314+
t.Fatal("expModInteger coefficient parameters not found")
315+
}
316+
params[expModIdx] = 607153
317+
params[expModIdx+1] = 231697
318+
params[expModIdx+2] = 53144
319+
320+
updatedCM, err := costModelFromList(
321+
lang.LanguageVersionV3,
322+
SemanticsVariantC,
323+
params,
324+
)
325+
if err != nil {
326+
t.Fatalf("unexpected error building cost model from list: %s", err)
327+
}
328+
329+
expModCPU := updatedCM.builtinCosts[builtin.ExpModInteger].cpu.(*ExpMod)
330+
if expModCPU.coeff00 != 607153 {
331+
t.Fatalf("expected expMod coeff00 to be 607153, got %d", expModCPU.coeff00)
332+
}
333+
if expModCPU.coeff11 != 231697 {
334+
t.Fatalf("expected expMod coeff11 to be 231697, got %d", expModCPU.coeff11)
335+
}
336+
if expModCPU.coeff12 != 53144 {
337+
t.Fatalf("expected expMod coeff12 to be 53144, got %d", expModCPU.coeff12)
338+
}
339+
}
340+
302341
func TestUpdateV1CostModelFromMap(t *testing.T) {
303342
// PlutusV1 cost model from preview network alonzo-genesis.json
304343
// Source: https://github.com/blinklabs-io/docker-cardano-configs/blob/main/config/preview/alonzo-genesis.json

0 commit comments

Comments
 (0)