diff --git a/doc/cost-models/keeppolicies/index.html b/doc/cost-models/keeppolicies/index.html new file mode 100644 index 00000000000..a228d967b20 --- /dev/null +++ b/doc/cost-models/keeppolicies/index.html @@ -0,0 +1,120 @@ + + + + + + KeepPolicies - Plutus Cost Model Visualization + + + + + + +
+

KeepPolicies Cost Model Visualization

+

+ Interactive 3D visualization of benchmark data and the fitted cost model for the + KeepPolicies builtin. The x-axis is the length of the policy list, the y-axis the total size + of the Value, and the z-axis execution time. Every benchmark point appears twice at the + same place on the floor: as a blue dot for what it measured, and as a red cross for what the model + charges for it. Where a cross sits above its dot the model overcharges that input; for a few of + the largest inputs the fitted plane sits below the measurement. The sizes are on log + axes because they are sampled log-uniformly, which leaves out the few points at p = 0 or n = 0. +

+ +
+

Data Source Configuration

+
+
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+

Plot Controls

+
+
+ + +
+
+ + +
+
+
+ +
+
+
Loading data and generating plot...
+
+ +
+

Plot Information

+ +
+
+
Data points:
+
-
+ +
Policy list length (arg1):
+
-
+ +
Value size range (arg2):
+
-
+ +
Time range:
+
-
+ +
Overhead:
+
-
+
+
+ +
+

The two fits

+
-
+
+ +
+
+
Model type:
+
-
+ +
Model formula:
+
-
+
+
+ +
+
+
+
+
+
+ + + + + + + diff --git a/doc/cost-models/keeppolicies/plot.js b/doc/cost-models/keeppolicies/plot.js new file mode 100644 index 00000000000..2a4445373f2 --- /dev/null +++ b/doc/cost-models/keeppolicies/plot.js @@ -0,0 +1,135 @@ +// KeepPolicies plot configuration and rendering (3D) + +const FUNCTION_NAME = 'KeepPolicies'; // CSV uses PascalCase +const COST_MODEL_NAME = 'keepPolicies'; // JSON uses camelCase +const ARITY = 2; + +let benchmarkData = []; +let costModel = null; +let stockModel = null; +let overhead = 0; +let showModel = true; +let axisScale = 'log'; + +setupCostModelPage({ + slug: 'keeppolicies', + functionName: FUNCTION_NAME, + costModelName: COST_MODEL_NAME, + arity: ARITY, + render(data) { + ({ benchmarkData, costModel, overhead } = data); + // The plain `lm(t ~ x_mem + y_mem)` of models.R, refit in the browser on the same + // points the shipped fit was built from. The shipped model comes from that same fit + // (negative coefficients clamped), so the two blocks agreeing checks that the JSON + // matches the fit; the undercharge lines show where the plane sits below the data. + stockModel = fitLinearInXAndY(benchmarkData.filter(d => d.args[1] > 0), overhead); + updateInfoPanel(); + renderPlot(); + }, + setupControls +}); + +function updateInfoPanel() { + const stats = calculateStats(benchmarkData); + document.getElementById('info-data-points').textContent = stats.dataPoints; + + const xs = benchmarkData.map(d => d.args[0]); + const ys = benchmarkData.map(d => d.args[1]); + document.getElementById('info-x-range').textContent = + `${Math.min(...xs)} - ${Math.max(...xs)}`; + document.getElementById('info-y-range').textContent = + `${Math.min(...ys)} - ${Math.max(...ys)}`; + document.getElementById('info-time-range').textContent = stats.timeRange; + document.getElementById('info-overhead').textContent = overhead > 0 + ? `${overhead.toFixed(2)} ns (arity ${ARITY}) added to predictions` + : 'Not calculated'; + + // Summarize both fits over the population they were built on (the y = 0 controls are + // out of the fit, so they would only distort the undercharge and overcharge lines). + const fitted = benchmarkData.filter(d => d.args[1] > 0); + document.getElementById('fit-comparison').innerHTML = + fitSummary('The shipped model (from the cost-model JSON)', + costModel, fitted, overhead, ['p', 'n']) + + fitSummary('The same least-squares fit recomputed from the CSV', + stockModel, fitted, overhead, ['p', 'n']); + + if (costModel) { + document.getElementById('info-model-type').textContent = costModel.modelType; + document.getElementById('info-model-formula').textContent = + formatModelFormula(costModel.modelType, costModel.coefficients); + } else { + document.getElementById('info-model-type').textContent = 'Not available'; + document.getElementById('info-model-formula').textContent = 'Cost model not found'; + } +} + +/* The two argument sizes on the floor, time up. Every benchmark point appears twice at the +same place on the floor, once as what it measured and once as what the model charges for it, +so the model is safe exactly where the red crosses sit above the blue dots. The sizes are +sampled log-uniformly, so a log floor is what spreads them out; the cost of that is the few +points at p = 0 or n = 0, which a log axis cannot place and which are out of the fit anyway. */ +function plotTraces() { + const traces = [{ + x: benchmarkData.map(d => d.args[0]), + y: benchmarkData.map(d => d.args[1]), + z: benchmarkData.map(d => d.time), + mode: 'markers', + type: 'scatter3d', + name: 'Benchmark Data', + marker: { size: 4, color: '#0033AD', opacity: 0.8 }, + hovertemplate: 'p %{x}, n %{y}
measured %{z:.3s} ns' + }]; + + if (showModel && costModel) { + traces.push({ + x: benchmarkData.map(d => d.args[0]), + y: benchmarkData.map(d => d.args[1]), + z: benchmarkData.map(d => modelCharge(costModel, d.args, overhead)), + mode: 'markers', + type: 'scatter3d', + name: 'Model Predictions', + marker: { size: 4, color: '#E53E3E', opacity: 0.6, symbol: 'x' }, + hovertemplate: 'p %{x}, n %{y}
charged %{z:.3s} ns' + }); + } + return traces; +} + +const PLOT_CONFIG = { responsive: true, displayModeBar: true, displaylogo: false }; + +function renderPlot() { + ensurePlotPanel('plot-3d'); + const scaled = axisScale; + const suffix = axisScale === 'log' ? ', log' : ''; + Plotly.react('plot-3d', plotTraces(), { + title: { text: `${FUNCTION_NAME} - Benchmark vs Model (3D)`, font: { size: 20 } }, + scene: { + xaxis: { title: `Policy list length (p${suffix})`, gridcolor: '#E0E0E0', type: scaled }, + yaxis: { title: `Value total size (n${suffix})`, gridcolor: '#E0E0E0', type: scaled }, + zaxis: { title: `Time (ns${suffix})`, gridcolor: '#E0E0E0', type: scaled }, + camera: { eye: { x: 1.7, y: -1.7, z: 0.7 } } + }, + showlegend: true, + legend: { + x: 0.02, + y: 0.98, + bgcolor: 'rgba(255, 255, 255, 0.8)', + bordercolor: '#BDC3C7', + borderwidth: 1 + }, + margin: { t: 60, b: 10, l: 10, r: 10 }, + height: 700, + paper_bgcolor: 'rgba(0,0,0,0)' + }, PLOT_CONFIG); +} + +function setupControls() { + document.getElementById('show-model').addEventListener('change', e => { + showModel = e.target.checked; + renderPlot(); + }); + document.getElementById('axis-scale').addEventListener('change', e => { + axisScale = e.target.value; + renderPlot(); + }); +} diff --git a/doc/cost-models/shared/utils.js b/doc/cost-models/shared/utils.js index c22a07bbedf..90c5776054d 100644 --- a/doc/cost-models/shared/utils.js +++ b/doc/cost-models/shared/utils.js @@ -308,6 +308,115 @@ function generateModelPredictions(benchmarkData, costModel, overhead) { return predictions; } +/** + * Fit t = intercept + slope1*x + slope2*y by least squares, the way models.R's plain + * `lm(t ~ x_mem + y_mem)` does: on times with the call overhead taken off, and with a + * negative coefficient clamped to 1000 ps the way `adjustModels` clamps it. + * + * `points` are {args: [x, y], time} in nanoseconds; the result is in picoseconds, so it + * can be handed to `evaluateCostModel` alongside a model read from the JSON. + */ +function fitLinearInXAndY(points, overhead) { + const rows = points.map(p => [1, p.args[0], p.args[1]]); + const rhs = points.map(p => p.time - (overhead || 0)); + + // Normal equations, solved by Gauss-Jordan on the 3x4 augmented matrix. + const m = [0, 1, 2].map(i => + [0, 1, 2].map(j => rows.reduce((acc, r, k) => acc + r[i] * r[j], 0)) + .concat(rows.reduce((acc, r, k) => acc + r[i] * rhs[k], 0))); + + for (let i = 0; i < 3; i++) { + let pivot = i; + for (let r = i + 1; r < 3; r++) { + if (Math.abs(m[r][i]) > Math.abs(m[pivot][i])) pivot = r; + } + [m[i], m[pivot]] = [m[pivot], m[i]]; + if (m[i][i] === 0) return null; + const d = m[i][i]; + for (let c = i; c < 4; c++) m[i][c] /= d; + for (let r = 0; r < 3; r++) { + if (r === i || m[r][i] === 0) continue; + const f = m[r][i]; + for (let c = i; c < 4; c++) m[r][c] -= f * m[i][c]; + } + } + + // `adjustModels` in models.R replaces a negative coefficient with 1 ns, ie 1000 ps. + const clamp = v => (v < 0 ? 1000 : v); + return { + modelType: 'linear_in_x_and_y', + coefficients: { + intercept: clamp(m[0][3] * 1000), + slope1: clamp(m[1][3] * 1000), + slope2: clamp(m[2][3] * 1000) + } + }; +} + +/** + * A model's predicted total time in nanoseconds for one benchmark point, overhead + * included, or null when the model is missing or cannot be evaluated. + */ +function modelCharge(model, args, overhead) { + if (!model) return null; + const ps = evaluateCostModel(model.modelType, model.coefficients, args); + return ps === null ? null : ps / 1000 + overhead; +} + +function median(values) { + if (values.length === 0) return 0; + const sorted = [...values].sort((a, b) => a - b); + return sorted[Math.floor(sorted.length / 2)]; +} + +/** + * One info-panel block per fit: coefficients, how many of the given points the model + * undercharges, and by how much. `points` should be the population the fit was built on, + * and `terms` names the two size variables in the charge line (e.g. ['p', 'n']). + */ +function fitSummary(name, model, points, overhead, terms) { + if (!model) return `

${name}: not available

`; + const c = model.coefficients; + const ratios = []; + let undercharged = 0; + let worstShortfall = 0; + let worstRatio = 1; + for (const d of points) { + const predicted = modelCharge(model, d.args, overhead); + if (predicted === null) return `

${name}: not evaluable (${model.modelType})

`; + ratios.push(predicted / d.time); + if (predicted < d.time) { + undercharged += 1; + worstShortfall = Math.max(worstShortfall, d.time - predicted); + worstRatio = Math.min(worstRatio, predicted / d.time); + } + } + const fmt = v => v.toLocaleString('en-US', { maximumFractionDigits: 0 }); + return ` +

${name}

+
+
Charge (ps):
+
${fmt(c.intercept)} + ${fmt(c.slope1)}·${terms[0]} + ${fmt(c.slope2)}·${terms[1]}
+
Undercharged points:
+
${undercharged} of ${points.length}${undercharged + ? `, worst ${(worstShortfall / 1000).toFixed(1)} us (${worstRatio.toFixed(2)}x)` + : ''}
+
Median overcharge:
+
${median(ratios).toFixed(2)}x
+
`; +} + +/* The shared loader replaces the contents of `#plot-container` with its own status message +before it calls a page's render, so the plot div has to be the page's to create. */ +function ensurePlotPanel(id) { + if (document.getElementById(id)) return; + const container = document.getElementById('plot-container'); + container.innerHTML = ''; + const panel = document.createElement('div'); + panel.id = id; + container.appendChild(panel); +} + /** * Format model formula as human-readable string */ @@ -528,6 +637,9 @@ const PAGES = [ ['policies', 'Policies', 'Returns the currency symbols of a Plutus Value; linear in the number ' + 'of policies. (2D visualization: Policy Count vs Time)'], + ['keeppolicies', 'KeepPolicies', + 'Retains only the listed currencies of a Plutus Value. ' + + '(3D visualization: List Length \u00d7 Value Size \u00d7 Time)'], ['listtoarray', 'ListToArray', 'Converts a Plutus list to an array representation. ' + '(2D visualization: List Size vs Time)'], diff --git a/plutus-core/changelog.d/20260828_144508_yuriy.lazaryev_issue_2310_keep_policies_costing.md b/plutus-core/changelog.d/20260828_144508_yuriy.lazaryev_issue_2310_keep_policies_costing.md new file mode 100644 index 00000000000..04249bee422 --- /dev/null +++ b/plutus-core/changelog.d/20260828_144508_yuriy.lazaryev_issue_2310_keep_policies_costing.md @@ -0,0 +1,3 @@ +### Added + +- A cost model for the `keepPolicies` builtin ([CIP-0168](https://cips.cardano.org/cip/CIP-0168)), with five new cost model parameters. Linear in the length of the policy list and in the total size of the `Value`. diff --git a/plutus-core/cost-model/budgeting-bench/Benchmarks/Values.hs b/plutus-core/cost-model/budgeting-bench/Benchmarks/Values.hs index 9c574ba6cc3..756a7f0fec4 100644 --- a/plutus-core/cost-model/budgeting-bench/Benchmarks/Values.hs +++ b/plutus-core/cost-model/budgeting-bench/Benchmarks/Values.hs @@ -24,6 +24,7 @@ import PlutusCore ( DefaultFun ( AssetCount , InsertCoin + , KeepPolicies , LookupCoin , Policies , ScaleValue @@ -70,6 +71,7 @@ makeBenchmarks gen = , scaleValueBenchmark gen , assetCountBenchmark gen , policiesBenchmark gen + , keepPoliciesBenchmark gen ] ---------------------------------------------------------------------------------------------------- @@ -414,17 +416,11 @@ policiesBenchmark :: StdGen -> Benchmark policiesBenchmark gen = createOneTermBuiltinBenchWithWrapper_NF ValueOuterSize Policies [] (runBenchGen gen policiesArgs) where - -- 40k pairs: safely above the largest `Value` a script can build within the - -- CPU budget (roughly 14k `insertCoin` applications). - maxTotalSize :: Int - maxTotalSize = Value.valueDataMaxSize - policiesArgs :: StatefulGen g m => g -> m [Value] policiesArgs g = do randoms <- replicateM 100 do - u <- uniformRM (0 :: Double, log (fromIntegral maxTotalSize)) g - let numPolicies = min maxTotalSize (max 1 (round (exp u))) - numTokens <- uniformRM (1, maxTotalSize `div` numPolicies) g + numPolicies <- logUniform g maxValueTotalSize + numTokens <- uniformRM (1, maxValueTotalSize `div` numPolicies) g generate g numPolicies numTokens stacks <- sequence @@ -435,14 +431,122 @@ policiesBenchmark gen = pure $ Value.empty : randoms <> stacks generate :: StatefulGen g m => g -> Int -> Int -> m Value - generate g numPolicies numTokens = do - policyIds <- replicateM numPolicies (generateKey g) - tokenNames <- replicateM numTokens (generateKey g) - pure $ buildValue policyIds tokenNames (mkQuantity 1) + generate g numPolicies numTokens = snd <$> generateValueWithPolicyIds g numPolicies numTokens + +-- KeepPolicies ------------------------------------------------------------------------------------ + +{- Note [Benchmarking keepPolicies] +`keepPolicies` pays for three things: building a `Set` from the list, walking the outer +map to drop what the list did not name, and recomputing the caches the `Value` carries +over what is left. Only the first is proportional to the list length, and the last is +proportional to the size of the result, which the cost model never sees. It sees the list +length and the total size of the argument. + +Every point of `randomSizes`, the hundred with both sizes drawn independently on a log +scale, is built to make those two agree: the list names every policy the `Value` has, so +nothing is dropped and the result is the whole argument. The pairs are packed into as few +policies as the list can cover, and the rest of the list is ids the `Value` does not have. +On an input where half the policies get dropped the same two sizes would buy half the +work, and a fit trained on those would undercharge a script that names them all. The log +scale is what puts the sizes that occur on chain in the sample and not only the large ones. + +`shapeSweep` and `hitSweep` hold both sizes fixed and vary what the model cannot see: how +many policies hold the pairs, and how much of the list hits. They therefore look like +repeated measurements of one point, and a model that cannot tell those inputs apart shows +it as vertical spread there. For `keepPolicies` that spread is widest at short lists and +narrows as the list grows, because building the `Set` comes to dominate and the shape of +the `Value` barely enters. + +`listOnly` hands the builtin an empty `Value`, which it returns from before it looks at +the list. Those points are the control on the rig: had criterion floated the list work out +of the measured loop, a list against an empty `Value` would cost what it costs against a +full one. It costs a small fraction of that, so the list work is inside the loop where the +measurement can see it. `models.R` drops these points from the fit, since nothing there is +proportional to either size. +-} + +keepPoliciesBenchmark :: StdGen -> Benchmark +keepPoliciesBenchmark gen = + createTwoTermBuiltinBenchElementwise KeepPolicies [] (runBenchGen gen keepPoliciesArgs) + +-- | See Note [Benchmarking keepPolicies] +keepPoliciesArgs :: forall g m. StatefulGen g m => g -> m [([ByteString], Value)] +keepPoliciesArgs g = do + -- Both sizes drawn independently on a log scale, each point the costliest of its sizes. + randomSizes <- replicateM 100 do + totalSize <- logUniform g maxValueTotalSize + listLen <- logUniform g maxValueTotalSize + worstCase totalSize listLen + -- One `Value`, growing list. + listSweep <- traverse (worstCase 1000) [1000, 3000, 10_000, maxValueTotalSize] + -- One total size, policy counts from one to one per pair. Run against a short list as + -- well as a long one, because a long list swamps the `Value` term. + shapeSweep <- + sequence + [ reaching numPolicies (10_000 `div` numPolicies) listLen + | listLen <- [100, 10_000] + , numPolicies <- [1, 10, 100, 1000, 10_000] + ] + -- One pair of sizes, list hits from none to all. + hitSweep <- traverse (hitting 10_000) [0, 2500, 5000, 7500, 10_000] + -- The list on its own, including the empty list. + listOnly <- + traverse + (\listLen -> (,Value.empty) <$> paddedList [] listLen) + [0, 10, 100, 1000, 10_000, maxValueTotalSize] + -- Lovelace: the empty bytestring is a valid policy id, and `policySet` keeps it. + lovelace <- do + tokenName <- generateKey g + pure ([BS.empty], buildValue [emptyKey] [tokenName] (mkQuantity 1)) + pure $ randomSizes <> listSweep <> shapeSweep <> hitSweep <> listOnly <> [lovelace] + where + -- The costliest input of its sizes: the pairs packed into as few policies as the list + -- covers, so that the list names every one of them. + worstCase :: Int -> Int -> m ([ByteString], Value) + worstCase totalSize listLen = + let numPolicies = max 1 (min listLen totalSize) + in reaching numPolicies (max 1 (totalSize `div` numPolicies)) listLen + + -- A `Value` of the given shape, and a list of the given length that names as much of + -- it as it can. + reaching :: Int -> Int -> Int -> m ([ByteString], Value) + reaching numPolicies numTokens listLen = do + (policyIds, value) <- generateValueWithPolicyIds g numPolicies numTokens + ps <- paddedList policyIds listLen + pure (ps, value) + + -- Every policy id the list has room for, padded to the given length with ids that are + -- absent from the `Value`. + paddedList :: [K] -> Int -> m [ByteString] + paddedList policyIds listLen = do + let hits = take listLen policyIds + misses <- replicateM (listLen - length hits) (generateKey g) + pure $ Value.unK <$> (hits <> misses) + + -- A flat `Value` of @totalSize@ pairs, and a list of the same length hitting @numHits@ + -- of its policies. + hitting :: Int -> Int -> m ([ByteString], Value) + hitting totalSize numHits = do + (policyIds, value) <- generateValueWithPolicyIds g totalSize 1 + ps <- paddedList (take numHits policyIds) totalSize + pure (ps, value) ---------------------------------------------------------------------------------------------------- -- Value Generators -------------------------------------------------------------------------------- +{-| 40k pairs: safely above the largest `Value` a script can build within the CPU budget +(roughly 14k `insertCoin` applications). -} +maxValueTotalSize :: Int +maxValueTotalSize = Value.valueDataMaxSize + +{-| A `Value` of @numPolicies@ policies holding @numTokens@ tokens each, together with its +policy ids. -} +generateValueWithPolicyIds :: StatefulGen g m => g -> Int -> Int -> m ([K], Value) +generateValueWithPolicyIds g numPolicies numTokens = do + policyIds <- replicateM numPolicies (generateKey g) + tokenNames <- replicateM numTokens (generateKey g) + pure (policyIds, buildValue policyIds tokenNames (mkQuantity 1)) + {-| Build Value from given policy IDs, token names and and a single quantity for each (policy ID, token name) pair. Uses 'unsafeFromList' because 'generateKey' may generate duplicate keys, although @@ -545,6 +649,20 @@ generateConstrainedValue numPolicies tokensPerPolicy g = do ---------------------------------------------------------------------------------------------------- -- Other Generators -------------------------------------------------------------------------------- +{-| Sample @[1, hi]@ uniformly on a log scale, so that every decade gets a similar number of +points. Sampling the count itself would put almost every point above 1000, leaving the sizes +that occur on chain unmeasured. -} +logUniform :: StatefulGen g m => g -> Int -> m Int +logUniform g hi = do + u <- uniformRM (0 :: Double, log (fromIntegral hi)) g + pure $ min hi (max 1 (round (exp u))) + +-- | The policy id of lovelace. +emptyKey :: K +emptyKey = case Value.k BS.empty of + Just key -> key + Nothing -> error "Internal error: the empty bytestring should always be a valid key" + {-| Generate a worst-case key for benchmarking ByteString comparisons. ByteString comparison is lexicographic and short-circuits on the first differing byte. diff --git a/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs b/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs index eac826dd397..47bf0a80e7e 100644 --- a/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs +++ b/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs @@ -387,6 +387,11 @@ builtinMemoryModels = -- `Value`, so only the list spine is new, at three words per cons cell (as for -- `multiIndexArray`). The size measure is the number of policies (`ValueOuterSize`). paramPolicies = Id $ ModelOneArgumentLinearInX $ OneVariableLinearFunction 4 3 + , -- The result shares the inner maps of the `Value` wholesale and its policy ids by + -- pointer, so the only new structure is the outer map spine, at six words per `Bin` + -- node, of which the result holds at most one per pair. The intercept covers the + -- `Value` closure, the `IntMap` of inner sizes and the two `Tip`s. + paramKeepPolicies = Id $ ModelTwoArgumentsLinearInY $ OneVariableLinearFunction 32 6 } where identityFunction = OneVariableLinearFunction 0 1 diff --git a/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs b/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs index fe1a5ef834f..40cc7ea4315 100644 --- a/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs +++ b/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs @@ -156,6 +156,7 @@ builtinCostModelNames = , paramMultiIndexArray = "multiIndexArrayModel" , paramAssetCount = "assetCountModel" , paramPolicies = "policiesModel" + , paramKeepPolicies = "keepPoliciesModel" } {-| Loads the models from R. @@ -314,6 +315,7 @@ createBuiltinCostModel bmfile rfile = do paramMultiIndexArray <- getParams readCF2 paramMultiIndexArray paramAssetCount <- getParams readCF1 paramAssetCount paramPolicies <- getParams readCF1 paramPolicies + paramKeepPolicies <- getParams readCF2 paramKeepPolicies pure $ BuiltinCostModelBase {..} diff --git a/plutus-core/cost-model/data/benching-conway.csv b/plutus-core/cost-model/data/benching-conway.csv index 1a614e5ffbe..a241e03ea96 100644 --- a/plutus-core/cost-model/data/benching-conway.csv +++ b/plutus-core/cost-model/data/benching-conway.csv @@ -14993,3 +14993,129 @@ Policies/5000,7.189463631358013e-5,7.156936535249899e-5,7.233595900980706e-5,1.2 Policies/5000,6.977548091312812e-5,6.943112384678736e-5,7.019367856507662e-5,1.2159543852228183e-6,9.90682373757167e-7,1.5841499768829372e-6 Policies/5000,7.12030144189684e-5,7.071833317524066e-5,7.162200396101847e-5,1.484040635252136e-6,1.1899313792295193e-6,1.8877811906884912e-6 Policies/5000,6.948013194114908e-5,6.91854455461997e-5,6.976571016174288e-5,9.917311481076059e-7,8.296841366362688e-7,1.2065254642363083e-6 +KeepPolicies/12/2016,8.34452423230735e-6,8.311440108636333e-6,8.356198257094977e-6,6.549637851043791e-8,2.6154538666287462e-8,1.2803004743756804e-7 +KeepPolicies/1682/2,4.3460923809998664e-4,4.344533293464378e-4,4.348154106649132e-4,6.062042986473072e-7,4.882792391789208e-7,7.578226879388894e-7 +KeepPolicies/2248/7,6.03307042691011e-4,6.031638429801315e-4,6.035007885207912e-4,5.450170373800897e-7,4.035813703515201e-7,7.80100900717365e-7 +KeepPolicies/13319/1341,5.349670033707519e-3,5.346318671606141e-3,5.355008837304453e-3,1.2931311073034712e-5,8.401181398989266e-6,1.9960012941799645e-5 +KeepPolicies/91/16,1.3549082747626022e-5,1.3544294501465235e-5,1.3555384031830399e-5,1.8622673558593624e-8,1.3784628796940805e-8,2.9287389896924162e-8 +KeepPolicies/22/8778,3.0599488095687494e-5,3.045422916551157e-5,3.073888584488617e-5,5.107799301593917e-7,4.183006848062761e-7,6.398071711792458e-7 +KeepPolicies/198/1,3.135437274559374e-5,3.134344302090515e-5,3.136542946183127e-5,3.712915283679371e-8,3.089700833448893e-8,4.710389050393016e-8 +KeepPolicies/226/1582,4.719424582600942e-5,4.717677448045436e-5,4.721545321868843e-5,6.744330939190366e-8,4.978294796660503e-8,9.949096584140667e-8 +KeepPolicies/5/35,1.6083128141315506e-6,1.6068920900866857e-6,1.6100745484145253e-6,5.1983876510946296e-9,4.01962713057327e-9,6.9003540623143065e-9 +KeepPolicies/1089/2,2.5895907565909104e-4,2.5885339299062786e-4,2.591849293620828e-4,5.084194452143421e-7,1.9266889356460098e-7,8.487663998886138e-7 +KeepPolicies/17207/15,7.6261801596576645e-3,7.6227043401021185e-3,7.629446202225316e-3,9.608265798458434e-6,7.981396596525845e-6,1.2709167487182126e-5 +KeepPolicies/12382/174,4.828524239513516e-3,4.824503611444812e-3,4.831963235796406e-3,1.1705936721710128e-5,8.786799732217193e-6,1.5720433441823495e-5 +KeepPolicies/5/5,1.4814424992788024e-6,1.4801861248136993e-6,1.4827478643296807e-6,4.212291499480468e-9,3.40842639086626e-9,5.354398305902664e-9 +KeepPolicies/342/1710,7.754375206124261e-5,7.712681195643798e-5,7.792581155626945e-5,1.3084890009696134e-6,1.2391276350370044e-6,1.3638010380689058e-6 +KeepPolicies/25194/4243,1.1893887263605159e-2,1.1816906087855301e-2,1.2193877306084126e-2,3.7933756508274025e-4,1.5224176236337361e-5,7.776886740126747e-4 +KeepPolicies/40/80,6.0682133891193e-6,6.037981272718933e-6,6.104360579988268e-6,1.0888208707387058e-7,9.095349336514853e-8,1.1914467320952174e-7 +KeepPolicies/228/36024,1.493275500890498e-4,1.4920048474337768e-4,1.4946932397854606e-4,4.4866928481012307e-7,2.743154883620133e-7,7.499080708399551e-7 +KeepPolicies/3244/10,9.749434345112689e-4,9.743183790244611e-4,9.758747052880327e-4,2.5169451312134653e-6,1.811057031525325e-6,3.629924565802736e-6 +KeepPolicies/954/44,2.225418198841045e-4,2.2248641160858788e-4,2.226176873189782e-4,2.1541038112498643e-7,1.6704560337523392e-7,3.22334874785056e-7 +KeepPolicies/16851/118,7.552255522092183e-3,7.54726140861873e-3,7.558495619832893e-3,1.6554027466884027e-5,1.3168567096557539e-5,2.0485290350898528e-5 +KeepPolicies/8137/1,2.840765676083742e-3,2.8392230557021136e-3,2.8428362679642637e-3,5.972319408854604e-6,5.146035636159394e-6,7.342870562231395e-6 +KeepPolicies/24/4,3.2287089420462223e-6,3.2092020993419694e-6,3.2550879156058364e-6,7.399863577922531e-8,6.06212130400683e-8,8.271421438349691e-8 +KeepPolicies/39/2418,1.3456235722918716e-5,1.3443213165890612e-5,1.3470453759723634e-5,4.4499403860609125e-8,3.45433701815779e-8,5.887217787107002e-8 +KeepPolicies/33926/2512,1.8749763261834248e-2,1.8249655181692453e-2,1.913648148808545e-2,1.058843735995058e-3,7.65458667428421e-4,1.4175748395950987e-3 +KeepPolicies/201/1005,4.08762344784375e-5,4.0848755521905284e-5,4.0908472301222595e-5,9.888074242171163e-8,7.682849980365556e-8,1.3769696710747835e-7 +KeepPolicies/254/1270,5.298297426539896e-5,5.291498488204394e-5,5.3038642914978236e-5,2.0252596858665239e-7,1.7894618434569426e-7,2.3443087163969852e-7 +KeepPolicies/150/3,2.3093215968825676e-5,2.3056483266773117e-5,2.313391441396461e-5,1.3183024016149674e-7,1.1392369925837756e-7,1.4237343010393732e-7 +KeepPolicies/1007/283,2.480355901411246e-4,2.474984651497883e-4,2.485236581249494e-4,1.7487833078669998e-6,1.495126026223553e-6,2.009371891343252e-6 +KeepPolicies/3787/30296,1.3327879256018714e-3,1.3317224760689653e-3,1.335532460506836e-3,5.333146236836567e-6,2.3269201489781214e-6,9.054168445142165e-6 +KeepPolicies/685/24,1.4568773193484062e-4,1.4563792606226905e-4,1.458379195318242e-4,2.6154266573904533e-7,1.347541696445606e-7,5.161416677052253e-7 +KeepPolicies/62/2046,1.65177886803625e-5,1.6510056313300373e-5,1.6527541995192453e-5,2.837542611517423e-8,2.3774367656319283e-8,3.370534818779589e-8 +KeepPolicies/60/13260,4.742279163706607e-5,4.7275425386212756e-5,4.757895432500401e-5,5.472618647048666e-7,4.640317013857647e-7,6.543415903536758e-7 +KeepPolicies/18208/2407,8.240005647875021e-3,8.220167148545891e-3,8.259836800033428e-3,5.5436999064845e-5,4.814107941776064e-5,6.482519529254937e-5 +KeepPolicies/594/4,1.2076255012278089e-4,1.2070813097959217e-4,1.2092488891192438e-4,3.318365038287857e-7,6.326773064622122e-8,6.323482648712004e-7 +KeepPolicies/1308/133,3.209523707470092e-4,3.208064547057704e-4,3.2145682923059946e-4,7.709104173280084e-7,2.982470237736029e-7,1.6477993999810816e-6 +KeepPolicies/918/33966,3.674375003910061e-4,3.6718019238516237e-4,3.678659169820835e-4,1.0936497308072537e-6,7.608940294095604e-7,1.8237333328947571e-6 +KeepPolicies/37598/8987,2.14741729368081e-2,2.10485496893098e-2,2.2184953598807695e-2,1.250158516500455e-3,9.896701666401008e-4,1.7500943543296487e-3 +KeepPolicies/31224/2101,1.603668796278421e-2,1.5924305496024756e-2,1.6220284627692802e-2,3.426979003116951e-4,2.1806543330899768e-4,6.206122563591985e-4 +KeepPolicies/5449/13,1.879184674901998e-3,1.8783087808791684e-3,1.880180585085764e-3,3.0821860736487477e-6,2.692673228025177e-6,3.6974287820076093e-6 +KeepPolicies/8/1,1.6684795750137093e-6,1.6668205171984368e-6,1.6700007296863363e-6,5.384711454409226e-9,4.770628281340246e-9,6.1350624520992e-9 +KeepPolicies/14/14,2.4077531312995663e-6,2.4065393425197495e-6,2.409538370001584e-6,4.898762810928834e-9,3.8062470257763976e-9,6.5259853102246764e-9 +KeepPolicies/10204/43,3.796431786675184e-3,3.794113411475345e-3,3.80230169649601e-3,1.1782664048061357e-5,6.307620186084471e-6,2.1787841074821108e-5 +KeepPolicies/10/8350,2.720832211115342e-5,2.7080120929591304e-5,2.7320294781648828e-5,4.061418190670462e-7,3.3784222193882784e-7,5.997681984069162e-7 +KeepPolicies/9547/25,3.6433034862251353e-3,3.6393423378762073e-3,3.6483522240930793e-3,1.5122074545705697e-5,1.1257678382938116e-5,2.3067473797104728e-5 +KeepPolicies/1261/970,3.328532996386912e-4,3.327787015758768e-4,3.329390542909821e-4,2.633574993789877e-7,2.112223441762704e-7,3.3073232220425766e-7 +KeepPolicies/16694/3,7.070018751369125e-3,7.064337007047928e-3,7.082346661701785e-3,2.3147900541281882e-5,1.2255080231103777e-5,4.2369789138755996e-5 +KeepPolicies/1/8096,3.816788998343174e-5,3.809106663981347e-5,3.819641097310257e-5,1.4894058320375643e-7,7.376851538650934e-8,2.809590425892531e-7 +KeepPolicies/20209/7929,9.323766820062638e-3,9.315545633940301e-3,9.339464139439922e-3,3.0889406699882534e-5,1.593501747822226e-5,5.8323906566951693e-5 +KeepPolicies/85/2,1.1393419519714661e-5,1.138179584727921e-5,1.1403681888623612e-5,3.567225875919743e-8,3.168936310776552e-8,4.0198475534360737e-8 +KeepPolicies/21/63,3.436028919500751e-6,3.4322947276478504e-6,3.45040124173042e-6,2.0937619092906914e-8,6.677860636330805e-9,4.25282011375531e-8 +KeepPolicies/300/28800,1.5393304417138373e-4,1.5366931727800722e-4,1.5408319996627263e-4,6.455124707910082e-7,4.467214821878464e-7,9.426471452736311e-7 +KeepPolicies/30314/3631,1.6137060650064754e-2,1.5739029607137186e-2,1.6433450089596246e-2,8.601764460514637e-4,6.392026750795067e-4,1.0234706031415648e-3 +KeepPolicies/7343/5,2.6963895509172844e-3,2.6940533502990533e-3,2.6986101793825554e-3,7.836068522893435e-6,6.635310793673531e-6,9.553417264391918e-6 +KeepPolicies/447/3576,1.1182142254986294e-4,1.1168868484110851e-4,1.1207228088358934e-4,5.930200299433338e-7,2.876951473435506e-7,1.0911966422111278e-6 +KeepPolicies/43/42,6.2789573031022125e-6,6.274135535297077e-6,6.284923910582627e-6,1.8025558672194792e-8,1.4389162310047203e-8,2.3268117899523858e-8 +KeepPolicies/3/1,1.295222120085136e-6,1.2937941092438029e-6,1.2990342833254465e-6,7.462677871152894e-9,3.2788836137941475e-9,1.4551415597298707e-8 +KeepPolicies/2915/5,8.57045319070747e-4,8.5682134510172e-4,8.572738101652811e-4,7.595043332144929e-7,5.661698802330357e-7,1.1888057242804937e-6 +KeepPolicies/1754/751,4.8387279707086357e-4,4.8351113855941616e-4,4.8418887123222144e-4,1.151677839070935e-6,1.0533977187271131e-6,1.2725161107534052e-6 +KeepPolicies/2/26,1.3800409012093964e-6,1.3776640941696782e-6,1.3822158270848647e-6,7.680344736308382e-9,6.451868004244626e-9,9.054696406083068e-9 +KeepPolicies/407/13,7.625968832315485e-5,7.624256401947775e-5,7.628125707030219e-5,6.131428515529124e-8,4.6899128185226845e-8,8.17607529742969e-8 +KeepPolicies/764/10,1.6362538174420976e-4,1.6359338679520298e-4,1.636802770977125e-4,1.4747556269903103e-7,7.245030249466167e-8,2.3418973513324133e-7 +KeepPolicies/107/17869,7.395673250001695e-5,7.362432195690697e-5,7.419048969159555e-5,9.074654559546643e-7,7.323055542420074e-7,1.1167576360199357e-6 +KeepPolicies/12/24,2.2660843672914076e-6,2.264423294488468e-6,2.2679267741367724e-6,5.713068761943689e-9,5.083785465046485e-9,6.70420702600026e-9 +KeepPolicies/34291/3345,1.892731130587675e-2,1.8460394159001015e-2,1.9216963852787275e-2,8.832799386535062e-4,5.757165474732595e-4,1.1108195509366094e-3 +KeepPolicies/34/14824,4.8220014134668305e-5,4.789960624648113e-5,4.847622648200584e-5,9.084725350113807e-7,7.207341655231266e-7,1.0820812410926212e-6 +KeepPolicies/100/1,1.3727362382073472e-5,1.372051637534225e-5,1.3732169260510325e-5,1.8879902951859488e-8,1.4483273776884123e-8,2.776399289921702e-8 +KeepPolicies/1/253,1.933586503648855e-6,1.92790529950271e-6,1.937104986268359e-6,1.562729047245983e-8,1.2230442594134778e-8,2.0413997704732056e-8 +KeepPolicies/12/48,2.4171906834509693e-6,2.4163424467173552e-6,2.417966593151045e-6,2.8395926294118742e-9,2.32607923322153e-9,3.490253796410157e-9 +KeepPolicies/148/33,2.3218790254523786e-5,2.320951307344857e-5,2.322904694211889e-5,3.36224531639932e-8,2.5522214449161475e-8,4.967625480313176e-8 +KeepPolicies/764/143,1.7287190807959156e-4,1.7282671096446344e-4,1.7295439802467565e-4,2.0397524412620376e-7,1.296202653723472e-7,3.0927686473574836e-7 +KeepPolicies/1116/401,2.803458882209666e-4,2.8025141825983163e-4,2.805256660682557e-4,4.4210508439252226e-7,2.7177474735421845e-7,8.110433858992414e-7 +KeepPolicies/20/39140,1.4148865002280042e-4,1.408923524582039e-4,1.4168548625306926e-4,1.0250755520104568e-6,3.9557183308713393e-7,2.029879075051348e-6 +KeepPolicies/24222/224,1.0991287627212722e-2,1.0881171006694097e-2,1.1173235809470635e-2,3.732323478626389e-4,2.3549076156835768e-4,5.046867165004387e-4 +KeepPolicies/1250/64,3.028713475918936e-4,3.02820332331106e-4,3.0304836736706135e-4,2.788273276777325e-7,1.1071393629043304e-7,5.449072846527146e-7 +KeepPolicies/9/18,1.9109063719357526e-6,1.9100537863486604e-6,1.9121522407752012e-6,3.4830759265214323e-9,2.572776609233702e-9,5.708804746347951e-9 +KeepPolicies/5897/5416,2.097356809162859e-3,2.0960584324797985e-3,2.098753527555635e-3,4.374563588081874e-6,3.7216507768224796e-6,5.546523126314264e-6 +KeepPolicies/16/16,2.6837475415626287e-6,2.6817725028042306e-6,2.6861097229433507e-6,7.553362928978441e-9,5.998320795402098e-9,1.0317196051539616e-8 +KeepPolicies/46/10,6.148323019525302e-6,6.145124929528357e-6,6.15189569657857e-6,1.1001418611254766e-8,8.871691831431291e-9,1.6092877293770975e-8 +KeepPolicies/3/702,3.4156407101739755e-6,3.4108774685282634e-6,3.418451686243187e-6,1.165392996136369e-8,7.966234296553319e-9,1.9206382256794915e-8 +KeepPolicies/15032/4,6.44029652884813e-3,6.4382694024888755e-3,6.442557481418943e-3,6.498476059127708e-6,5.0305999753738085e-6,9.043704955889573e-6 +KeepPolicies/46/15364,5.2753369338903524e-5,5.239140713277697e-5,5.3063190941810516e-5,1.160579235449775e-6,9.612799220066187e-7,1.3642891624814108e-6 +KeepPolicies/125/6625,4.025454156212467e-5,4.024428009438962e-5,4.026532253075534e-5,3.6481957558418235e-8,3.0875512107137294e-8,4.6776376771933447e-8 +KeepPolicies/713/18,1.5322734834960386e-4,1.5318363316626794e-4,1.5336228664035198e-4,2.3219828459948976e-7,8.496833251361321e-8,4.982174468186235e-7 +KeepPolicies/5/40,1.6538601010695255e-6,1.6534428248489994e-6,1.6544097440079172e-6,1.622045126929525e-9,1.3128410182944956e-9,1.9972520148162566e-9 +KeepPolicies/1458/2,3.62666561984307e-4,3.6259618227821967e-4,3.6283418940507716e-4,3.289577210945002e-7,1.494932360941445e-7,6.332680130905202e-7 +KeepPolicies/17/85,3.00195105179893e-6,2.9999628707607485e-6,3.004416776892495e-6,7.395694231882257e-9,6.230582025279794e-9,9.222045246872217e-9 +KeepPolicies/3296/228,1.0026246463957823e-3,1.0023594696187234e-3,1.0030981415372617e-3,1.1127079953837005e-6,7.543012949220954e-7,1.8030113009855697e-6 +KeepPolicies/1/7,1.1889323299595621e-6,1.1861631396665084e-6,1.191485759305703e-6,9.167940707271487e-9,8.243536968263997e-9,1.0142075911186093e-8 +KeepPolicies/7427/117,2.6729138479061445e-3,2.6720676134498475e-3,2.674603738859107e-3,4.025890265049097e-6,2.5002318383440227e-6,7.011526982741162e-6 +KeepPolicies/14470/14470,6.2551145288556416e-3,6.248502762100676e-3,6.260815018586763e-3,1.8451254362006166e-5,1.5584894139071383e-5,2.1474370059552556e-5 +KeepPolicies/47/4,5.948260751733513e-6,5.923252787873301e-6,5.983304914231822e-6,1.0253612368172906e-7,6.696098835649568e-8,1.3375092290968562e-7 +KeepPolicies/27868/10478,1.375325311342286e-2,1.3678246115339755e-2,1.3893790075057713e-2,2.5506958819605335e-4,9.276987617544492e-5,4.0172814797437517e-4 +KeepPolicies/253/16951,1.0324732411979808e-4,1.0319666693936085e-4,1.033034560258703e-4,1.8374753179158072e-7,1.519774778649462e-7,2.400354594957357e-7 +KeepPolicies/110/5280,3.5823133855277284e-5,3.580507164895569e-5,3.584228319800851e-5,6.380343475962144e-8,4.966104617350527e-8,8.334220482381078e-8 +KeepPolicies/33/7,4.317323334841175e-6,4.313828164091268e-6,4.322670714444863e-6,1.4220148157927285e-8,1.0642772369434257e-8,1.730812102035045e-8 +KeepPolicies/20/7300,2.450410381029135e-5,2.449281200330174e-5,2.451362686943817e-5,3.474613717649599e-8,2.433888624266182e-8,4.955052489821649e-8 +KeepPolicies/364/1456,8.227019097436708e-5,8.192189469903249e-5,8.265381988482585e-5,1.2239411306208391e-6,1.1461759918348695e-6,1.289090829485932e-6 +KeepPolicies/1/96,1.4384826355940793e-6,1.4372804848127812e-6,1.4397743377030947e-6,4.305693025226102e-9,3.4528337164647744e-9,5.462904237693843e-9 +KeepPolicies/13442/2,5.779208654796054e-3,5.774170174922734e-3,5.783930996559928e-3,1.4663562184802957e-5,1.1005247599562239e-5,1.963245614825446e-5 +KeepPolicies/38564/87,2.236053737614652e-2,2.2069067206934273e-2,2.2708130105225426e-2,7.440391338481491e-4,6.137250472261507e-4,8.965628088422877e-4 +KeepPolicies/1000/1000,2.553029959878976e-4,2.552251320626936e-4,2.5540443386668014e-4,3.0452585249925687e-7,2.304100985269306e-7,4.116608176773928e-7 +KeepPolicies/3000/1000,9.0367049538478e-4,9.034813211342243e-4,9.039311449551064e-4,7.542536977771421e-7,5.196580463024318e-7,1.2480832933705278e-6 +KeepPolicies/10000/1000,3.9024764149771206e-3,3.901216522272979e-3,3.904983119459712e-3,5.407179447347465e-6,3.3836763859305933e-6,9.280822404745052e-6 +KeepPolicies/40000/1000,2.094799994752802e-2,2.082851205415138e-2,2.1184481589028758e-2,3.8345065329451595e-4,2.519247085318177e-4,5.808567861256287e-4 +KeepPolicies/100/10000,6.275697660168382e-5,6.267237900968753e-5,6.28231968605292e-5,2.466684226775891e-7,1.7470727129322545e-7,3.461140517641356e-7 +KeepPolicies/100/10000,4.7136562836383576e-5,4.687981496332207e-5,4.7335529160231854e-5,7.219714054148111e-7,5.386897527020627e-7,9.264082187484521e-7 +KeepPolicies/100/10000,4.706836316465256e-5,4.6948753777016064e-5,4.714989861597643e-5,3.186101339901925e-7,2.4106774139374934e-7,4.186859386880225e-7 +KeepPolicies/100/10000,2.4366520524943095e-5,2.4356038572157324e-5,2.4376513427602884e-5,3.425734107796843e-8,2.9681810141377015e-8,4.0798986200565076e-8 +KeepPolicies/100/10000,2.4960406357643733e-5,2.4940154067506225e-5,2.4983763974045722e-5,7.26331602568211e-8,6.123237332184721e-8,9.306549219821148e-8 +KeepPolicies/10000/10000,3.7589672878095095e-3,3.752659628271018e-3,3.765889455991348e-3,2.0294196509839806e-5,1.763047493692375e-5,2.3526755664552882e-5 +KeepPolicies/10000/10000,3.952454708517962e-3,3.948343483136616e-3,3.956585998673329e-3,1.2450591234147912e-5,1.0026677563836277e-5,1.572602699024895e-5 +KeepPolicies/10000/10000,3.8055816263683016e-3,3.7996755818863856e-3,3.811260999032312e-3,1.8815155961851637e-5,1.5473217849711637e-5,2.2725388358058714e-5 +KeepPolicies/10000/10000,3.8943541158001694e-3,3.8925824677040997e-3,3.897409458176648e-3,7.192795077119276e-6,4.821580875529609e-6,1.248418321601707e-5 +KeepPolicies/10000/10000,3.9870165001568945e-3,3.9856397876158025e-3,3.98920637607986e-3,5.193699686296274e-6,3.679798808145113e-6,8.592078052785928e-6 +KeepPolicies/10000/10000,4.635939157388207e-3,4.634366902334829e-3,4.639050791850601e-3,6.706824100457602e-6,4.17116466552532e-6,1.140013540085088e-5 +KeepPolicies/10000/10000,4.6324196557905595e-3,4.605888111956143e-3,4.6639684195667564e-3,9.187297051305469e-5,7.0390432912444e-5,1.3773510859273734e-4 +KeepPolicies/10000/10000,4.501773276808983e-3,4.47725714795292e-3,4.532870509625553e-3,8.997032818871563e-5,7.628713832014608e-5,1.0791847223438296e-4 +KeepPolicies/10000/10000,4.39389814672427e-3,4.369305690650765e-3,4.426179014073755e-3,9.129698929545358e-5,7.669248818713926e-5,1.1717219090289631e-4 +KeepPolicies/10000/10000,3.943501645373088e-3,3.941648576673032e-3,3.948502890385087e-3,8.956823589353712e-6,4.183917874463928e-6,1.7169716521178647e-5 +KeepPolicies/0/0,1.073777144078427e-6,1.0727133674196693e-6,1.0748087914400964e-6,3.589401178238296e-9,2.9674908464591197e-9,4.843991955564007e-9 +KeepPolicies/10/0,1.092581161224314e-6,1.0913558098556756e-6,1.0940731762838627e-6,4.6370575865682716e-9,3.891345834913469e-9,5.817993329050029e-9 +KeepPolicies/100/0,1.3235472015597536e-6,1.32290109860828e-6,1.3241294492774318e-6,2.1176482484183325e-9,1.7337840523418974e-9,2.684241178187931e-9 +KeepPolicies/1000/0,4.18300348834241e-6,4.181991868700537e-6,4.1840835040184515e-6,3.6533520549819114e-9,2.9489096732123943e-9,4.548093354483196e-9 +KeepPolicies/10000/0,4.440282330963081e-5,4.4398360261952016e-5,4.4409153285901145e-5,1.712155553493034e-8,1.179838863495409e-8,2.5074861334381542e-8 +KeepPolicies/40000/0,9.144561287579648e-5,9.13924072766682e-5,9.162202535203485e-5,2.899958382807669e-7,4.372217409053752e-8,5.978640247657308e-7 +KeepPolicies/1/1,1.1596820731990927e-6,1.158974430744399e-6,1.1604112830032068e-6,2.340799687346653e-9,1.9930271054282425e-9,2.767822960598697e-9 diff --git a/plutus-core/cost-model/data/builtinCostModelA.json b/plutus-core/cost-model/data/builtinCostModelA.json index c5a59edee46..1ad82986593 100644 --- a/plutus-core/cost-model/data/builtinCostModelA.json +++ b/plutus-core/cost-model/data/builtinCostModelA.json @@ -1364,5 +1364,22 @@ }, "type": "linear_in_x" } + }, + "keepPolicies": { + "cpu": { + "arguments": { + "intercept": 1000, + "slope1": 517590, + "slope2": 3833 + }, + "type": "linear_in_x_and_y" + }, + "memory": { + "arguments": { + "intercept": 32, + "slope": 6 + }, + "type": "linear_in_y" + } } } diff --git a/plutus-core/cost-model/data/builtinCostModelB.json b/plutus-core/cost-model/data/builtinCostModelB.json index 9eeb61bee16..e6447a53df1 100644 --- a/plutus-core/cost-model/data/builtinCostModelB.json +++ b/plutus-core/cost-model/data/builtinCostModelB.json @@ -1364,5 +1364,22 @@ }, "type": "linear_in_x" } + }, + "keepPolicies": { + "cpu": { + "arguments": { + "intercept": 1000, + "slope1": 517590, + "slope2": 3833 + }, + "type": "linear_in_x_and_y" + }, + "memory": { + "arguments": { + "intercept": 32, + "slope": 6 + }, + "type": "linear_in_y" + } } } diff --git a/plutus-core/cost-model/data/builtinCostModelC.json b/plutus-core/cost-model/data/builtinCostModelC.json index 66e34d7f2c8..874b3d987c1 100644 --- a/plutus-core/cost-model/data/builtinCostModelC.json +++ b/plutus-core/cost-model/data/builtinCostModelC.json @@ -1382,5 +1382,22 @@ }, "type": "linear_in_x" } + }, + "keepPolicies": { + "cpu": { + "arguments": { + "intercept": 1000, + "slope1": 517590, + "slope2": 3833 + }, + "type": "linear_in_x_and_y" + }, + "memory": { + "arguments": { + "intercept": 32, + "slope": 6 + }, + "type": "linear_in_y" + } } } diff --git a/plutus-core/cost-model/data/builtinCostModelD.json b/plutus-core/cost-model/data/builtinCostModelD.json index 9978dd0ca0b..ed975d2ed95 100644 --- a/plutus-core/cost-model/data/builtinCostModelD.json +++ b/plutus-core/cost-model/data/builtinCostModelD.json @@ -1364,5 +1364,22 @@ }, "type": "linear_in_x" } + }, + "keepPolicies": { + "cpu": { + "arguments": { + "intercept": 1000, + "slope1": 517590, + "slope2": 3833 + }, + "type": "linear_in_x_and_y" + }, + "memory": { + "arguments": { + "intercept": 32, + "slope": 6 + }, + "type": "linear_in_y" + } } } diff --git a/plutus-core/cost-model/data/builtinCostModelE.json b/plutus-core/cost-model/data/builtinCostModelE.json index 2a2cbddc5a7..ca529ca741e 100644 --- a/plutus-core/cost-model/data/builtinCostModelE.json +++ b/plutus-core/cost-model/data/builtinCostModelE.json @@ -1382,5 +1382,22 @@ }, "type": "linear_in_x" } + }, + "keepPolicies": { + "cpu": { + "arguments": { + "intercept": 1000, + "slope1": 517590, + "slope2": 3833 + }, + "type": "linear_in_x_and_y" + }, + "memory": { + "arguments": { + "intercept": 32, + "slope": 6 + }, + "type": "linear_in_y" + } } } diff --git a/plutus-core/cost-model/data/models.R b/plutus-core/cost-model/data/models.R index d3d18bfae64..d507ef34e49 100644 --- a/plutus-core/cost-model/data/models.R +++ b/plutus-core/cost-model/data/models.R @@ -162,6 +162,7 @@ arity <- function(name) { "MultiIndexArray" = 2, "AssetCount" = 1, "Policies" = 1, + "KeepPolicies" = 2, -1 ## Default for missing values ) } @@ -447,6 +448,19 @@ modelFun <- function(path) { return (mk.result(m, "linear_in_u")) } + ## Two-variable linear model on the points with y_mem > 0. Used by the `Value` + ## policy-filter builtins, which return before they look at the list when the `Value` is + ## empty, so the y = 0 points cost the machine sizing the list rather than the builtin + ## doing its work, and one slope cannot price both. + linearInXAndYNonzeroY <- function (fname) { + filtered <- data %>% + filter.and.check.nonempty(fname) %>% + filter(y_mem > 0) %>% + discard.overhead () + m <- lm(t ~ x_mem + y_mem, filtered) + return (mk.result(m, "linear_in_x_and_y")) + } + linearOnDiagonal <- function (fname) { filtered <- filter(data, name == fname) data.on.diagonal <- filter(filtered, x_mem == y_mem) @@ -856,6 +870,11 @@ modelFun <- function(path) { ## X wrapped with `ValueOuterSize` policiesModel <- linearInX ("Policies") + ## X is the length of the policy list, Y is `Value.totalSize`. The builtin skips the part + ## of the outer map the list cannot reach, so what costs anything is the pairs the list + ## names. See Note [Benchmarking keepPolicies] in Benchmarks.Values. + keepPoliciesModel <- linearInXAndYNonzeroY ("KeepPolicies") + ## Values # Z wrapped with `Logarithmic . ValueOuterOrMaxInner` @@ -1014,7 +1033,8 @@ modelFun <- function(path) { scaleValueModel = scaleValueModel, multiIndexArrayModel = multiIndexArrayModel, assetCountModel = assetCountModel, - policiesModel = policiesModel + policiesModel = policiesModel, + keepPoliciesModel = keepPoliciesModel ) ## The integer division functions have a complex costing behaviour that requires some negative diff --git a/plutus-core/cost-model/test/TestCostModels.hs b/plutus-core/cost-model/test/TestCostModels.hs index 5aa26720334..e38d0170801 100644 --- a/plutus-core/cost-model/test/TestCostModels.hs +++ b/plutus-core/cost-model/test/TestCostModels.hs @@ -559,4 +559,5 @@ main = -- , $(genTest 1 "unValueData") $(genTest 2 "scaleValue") Everywhere , $(genTest 1 "assetCount") + , $(genTest 2 "keepPolicies") Everywhere ] diff --git a/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs b/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs index 9978daa6a98..b8d27fa85b3 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs @@ -2516,7 +2516,7 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where {-# INLINE keepPoliciesDenotation #-} in makeBuiltinMeaning keepPoliciesDenotation - (runCostingFunTwoArguments . unimplementedCostingFun) + (runCostingFunTwoArguments . paramKeepPolicies) toBuiltinMeaning _semvar DropPolicies = let dropPoliciesDenotation :: [ByteString] -> Value -> Value dropPoliciesDenotation = Value.dropPolicies diff --git a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs index 4705cb2c3a5..9fa5f372364 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs @@ -201,6 +201,7 @@ data BuiltinCostModelBase f paramMultiIndexArray :: f ModelTwoArguments , paramAssetCount :: f ModelOneArgument , paramPolicies :: f ModelOneArgument + , paramKeepPolicies :: f ModelTwoArguments } deriving stock (Generic) deriving anyclass (FunctorB, TraversableB, ConstraintsB) diff --git a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs index b38a304056b..667af631d96 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs @@ -431,6 +431,7 @@ unitCostBuiltinCostModel = paramMultiIndexArray = unitCostTwoArguments , paramAssetCount = unitCostOneArgument , paramPolicies = unitCostOneArgument + , paramKeepPolicies = unitCostTwoArguments } unitCekParameters diff --git a/plutus-ledger-api/src/PlutusLedgerApi/V1/ParamName.hs b/plutus-ledger-api/src/PlutusLedgerApi/V1/ParamName.hs index 09239bbe622..72e31495237 100644 --- a/plutus-ledger-api/src/PlutusLedgerApi/V1/ParamName.hs +++ b/plutus-ledger-api/src/PlutusLedgerApi/V1/ParamName.hs @@ -361,5 +361,10 @@ data ParamName | Policies'cpu'arguments'slope | Policies'memory'arguments'intercept | Policies'memory'arguments'slope + | KeepPolicies'cpu'arguments'intercept + | KeepPolicies'cpu'arguments'slope1 + | KeepPolicies'cpu'arguments'slope2 + | KeepPolicies'memory'arguments'intercept + | KeepPolicies'memory'arguments'slope deriving stock (Eq, Ord, Enum, Ix, Bounded, Generic) deriving (IsParamName) via (GenericParamName ParamName) diff --git a/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs b/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs index 41092f16d7d..edc6711ea76 100644 --- a/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs +++ b/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs @@ -363,5 +363,10 @@ data ParamName | Policies'cpu'arguments'slope | Policies'memory'arguments'intercept | Policies'memory'arguments'slope + | KeepPolicies'cpu'arguments'intercept + | KeepPolicies'cpu'arguments'slope1 + | KeepPolicies'cpu'arguments'slope2 + | KeepPolicies'memory'arguments'intercept + | KeepPolicies'memory'arguments'slope deriving stock (Eq, Ord, Enum, Ix, Bounded, Generic) deriving (IsParamName) via (GenericParamName ParamName) diff --git a/plutus-ledger-api/src/PlutusLedgerApi/V3/ParamName.hs b/plutus-ledger-api/src/PlutusLedgerApi/V3/ParamName.hs index 399d9b570a4..bf1e30a91d3 100644 --- a/plutus-ledger-api/src/PlutusLedgerApi/V3/ParamName.hs +++ b/plutus-ledger-api/src/PlutusLedgerApi/V3/ParamName.hs @@ -379,5 +379,10 @@ data ParamName | Policies'cpu'arguments'slope | Policies'memory'arguments'intercept | Policies'memory'arguments'slope + | KeepPolicies'cpu'arguments'intercept + | KeepPolicies'cpu'arguments'slope1 + | KeepPolicies'cpu'arguments'slope2 + | KeepPolicies'memory'arguments'intercept + | KeepPolicies'memory'arguments'slope deriving stock (Eq, Ord, Enum, Ix, Bounded, Generic) deriving (IsParamName) via (GenericParamName ParamName) diff --git a/plutus-ledger-api/src/PlutusLedgerApi/V4/ParamName.hs b/plutus-ledger-api/src/PlutusLedgerApi/V4/ParamName.hs index 5d7a5498118..7e3b2b7de89 100644 --- a/plutus-ledger-api/src/PlutusLedgerApi/V4/ParamName.hs +++ b/plutus-ledger-api/src/PlutusLedgerApi/V4/ParamName.hs @@ -374,5 +374,10 @@ data ParamName | Policies'cpu'arguments'slope | Policies'memory'arguments'intercept | Policies'memory'arguments'slope + | KeepPolicies'cpu'arguments'intercept + | KeepPolicies'cpu'arguments'slope1 + | KeepPolicies'cpu'arguments'slope2 + | KeepPolicies'memory'arguments'intercept + | KeepPolicies'memory'arguments'slope deriving stock (Eq, Ord, Enum, Ix, Bounded, Generic) deriving (IsParamName) via (GenericParamName ParamName) diff --git a/plutus-ledger-api/test/Spec/CostModelParams.hs b/plutus-ledger-api/test/Spec/CostModelParams.hs index 3993cf191b0..d56bb728108 100644 --- a/plutus-ledger-api/test/Spec/CostModelParams.hs +++ b/plutus-ledger-api/test/Spec/CostModelParams.hs @@ -41,10 +41,10 @@ tests = "CostModelParams" "costModelParams" [ embed $ testCase "length" do - 343 @=? length v1_ParamNames - 343 @=? length v2_ParamNames - 361 @=? length v3_ParamNames - 361 @=? length v4_ParamNames + 348 @=? length v1_ParamNames + 348 @=? length v2_ParamNames + 366 @=? length v3_ParamNames + 366 @=? length v4_ParamNames , embed $ testCase "tripping paramname" do for_ v1_ParamNames \p -> assertBool "tripping v1 cm params failed" $