Skip to content

Commit 7a4487f

Browse files
authored
Show information gain in header validator output (#1157)
1 parent 72daf75 commit 7a4487f

File tree

7 files changed

+69
-16
lines changed

7 files changed

+69
-16
lines changed

ts/src/flexible-event/main.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,18 @@ config.peek((config) => {
117117

118118
console.log(`Number of possible different output states: ${out.numStates}`)
119119
console.log(`Information gain: ${out.infoGain.toFixed(2)} bits`)
120-
console.log(`Flip percent: ${(100 * out.flipProb).toFixed(5)}%`)
120+
console.log(`Randomized trigger rate: ${out.flipProb.toFixed(7)}`)
121121

122122
if (out.excessive) {
123123
const e = out.excessive
124124
console.log(
125125
`WARNING: info gain > ${infoGainMax.toFixed(2)} for ${
126126
options.source_type
127-
} sources. Would require a ${(100 * e.newFlipProb).toFixed(
128-
5
129-
)}% flip chance (effective epsilon = ${e.newEps.toFixed(3)}) to resolve.`
127+
} sources. Would require a ${e.newFlipProb.toFixed(
128+
7
129+
)} randomized trigger rate (effective epsilon = ${e.newEps.toFixed(
130+
3
131+
)}) to resolve.`
130132
)
131133
}
132134
})

ts/src/header-validator/context.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ export type Issue = {
88
export type ValidationResult = {
99
errors: Issue[]
1010
warnings: Issue[]
11+
notes: Issue[]
1112
}
1213

1314
export class Context {
1415
private readonly path: PathComponent[] = []
15-
private readonly result: ValidationResult = { errors: [], warnings: [] }
16+
private readonly result: ValidationResult = {
17+
errors: [],
18+
warnings: [],
19+
notes: [],
20+
}
1621

1722
scope<T>(c: PathComponent, f: () => T): T {
1823
this.path.push(c)
@@ -33,6 +38,10 @@ export class Context {
3338
this.result.warnings.push(this.issue(msg))
3439
}
3540

41+
note(msg: string): void {
42+
this.result.notes.push(this.issue(msg))
43+
}
44+
3645
finish(topLevelError?: string): ValidationResult {
3746
if (typeof topLevelError !== 'undefined') {
3847
this.result.errors.push({ msg: topLevelError })

ts/src/header-validator/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ <h1>Attribution Reporting Header Validation</h1>
8585
<ul id=errors></ul>
8686
<p>Warnings:
8787
<ul id=warnings></ul>
88+
<p>Notes:
89+
<ul id=notes></ul>
8890
</output>
8991
</fieldset>
9092
</div>

ts/src/header-validator/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const sourceTypeRadios = form.elements.namedItem(
1313
)! as RadioNodeList
1414
const errorList = document.querySelector('#errors')!
1515
const warningList = document.querySelector('#warnings')!
16+
const noteList = document.querySelector('#notes')!
1617
const successDiv = document.querySelector('#success')!
1718
const sourceTypeFieldset = document.querySelector(
1819
'#source-type'
@@ -69,7 +70,8 @@ function validate(): void {
6970
input.value,
7071
vsv.Chromium,
7172
sourceType(),
72-
flexCheckbox.checked
73+
flexCheckbox.checked,
74+
/*noteInfoGain=*/ true
7375
)[0]
7476
break
7577
case 'trigger':
@@ -103,6 +105,7 @@ function validate(): void {
103105

104106
errorList.replaceChildren(...result.errors.map(makeLi))
105107
warningList.replaceChildren(...result.warnings.map(makeLi))
108+
noteList.replaceChildren(...result.notes.map(makeLi))
106109
}
107110

108111
form.addEventListener('input', validate)

ts/src/header-validator/source.test.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import * as jsontest from './validate-json.test'
1111

1212
type TestCase = jsontest.TestCase<Source> & {
1313
sourceType?: SourceType
14+
noteInfoGain?: boolean
1415
}
1516

1617
const testCases: TestCase[] = [
@@ -1223,6 +1224,7 @@ const testCases: TestCase[] = [
12231224
name: 'channel-capacity-default-event',
12241225
json: `{"destination": "https://a.test"}`,
12251226
sourceType: SourceType.event,
1227+
noteInfoGain: true,
12261228
vsv: {
12271229
maxEventLevelChannelCapacityPerSource: {
12281230
[SourceType.event]: 0,
@@ -1233,14 +1235,25 @@ const testCases: TestCase[] = [
12331235
expectedErrors: [
12341236
{
12351237
path: [],
1236-
msg: 'exceeds max event-level channel capacity per event source (1.58 > 0.00)',
1238+
msg: 'information gain: 1.58 exceeds max event-level channel capacity per event source (0.00)',
1239+
},
1240+
],
1241+
expectedNotes: [
1242+
{
1243+
path: [],
1244+
msg: 'number of possible output states: 3',
1245+
},
1246+
{
1247+
path: [],
1248+
msg: 'randomized trigger rate: 0.0000025',
12371249
},
12381250
],
12391251
},
12401252
{
12411253
name: 'channel-capacity-default-navigation',
12421254
json: `{"destination": "https://a.test"}`,
12431255
sourceType: SourceType.navigation,
1256+
noteInfoGain: true,
12441257
vsv: {
12451258
maxEventLevelChannelCapacityPerSource: {
12461259
[SourceType.event]: Infinity,
@@ -1251,7 +1264,17 @@ const testCases: TestCase[] = [
12511264
expectedErrors: [
12521265
{
12531266
path: [],
1254-
msg: 'exceeds max event-level channel capacity per navigation source (11.46 > 0.00)',
1267+
msg: 'information gain: 11.46 exceeds max event-level channel capacity per navigation source (0.00)',
1268+
},
1269+
],
1270+
expectedNotes: [
1271+
{
1272+
path: [],
1273+
msg: 'number of possible output states: 2925',
1274+
},
1275+
{
1276+
path: [],
1277+
msg: 'randomized trigger rate: 0.0024263',
12551278
},
12561279
],
12571280
},
@@ -1833,7 +1856,8 @@ testCases.forEach((tc) =>
18331856
tc.json,
18341857
{ ...vsv.Chromium, ...tc.vsv },
18351858
tc.sourceType ?? SourceType.navigation,
1836-
tc.parseFullFlex ?? false
1859+
tc.parseFullFlex ?? false,
1860+
tc.noteInfoGain ?? false
18371861
)
18381862
)
18391863
)

ts/src/header-validator/util.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as context from './context'
55
export type TestCase = {
66
expectedWarnings?: context.Issue[]
77
expectedErrors?: context.Issue[]
8+
expectedNotes?: context.Issue[]
89
}
910

1011
export function run(
@@ -17,6 +18,7 @@ export function run(
1718
assert.deepEqual(result, {
1819
errors: tc.expectedErrors ?? [],
1920
warnings: tc.expectedWarnings ?? [],
21+
notes: tc.expectedNotes ?? [],
2022
})
2123
})
2224
}

ts/src/header-validator/validate-json.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class SourceContext extends RegistrationContext {
3030
constructor(
3131
vsv: Readonly<VendorSpecificValues>,
3232
parseFullFlex: boolean,
33-
readonly sourceType: SourceType
33+
readonly sourceType: SourceType,
34+
readonly noteInfoGain: boolean
3435
) {
3536
super(vsv, parseFullFlex)
3637
}
@@ -832,18 +833,27 @@ function channelCapacity(ctx: SourceContext, s: Source): void {
832833
perTriggerDataConfigs
833834
)
834835

835-
const { infoGain } = config.computeConfigData(
836+
const out = config.computeConfigData(
836837
s.eventLevelEpsilon,
837838
ctx.vsv.maxEventLevelChannelCapacityPerSource[ctx.sourceType]
838839
)
839840

840841
const max = ctx.vsv.maxEventLevelChannelCapacityPerSource[ctx.sourceType]
841-
if (infoGain > max) {
842+
const infoGainMsg = `information gain: ${out.infoGain.toFixed(2)}`
843+
844+
if (out.infoGain > max) {
842845
ctx.error(
843-
`exceeds max event-level channel capacity per ${
846+
`${infoGainMsg} exceeds max event-level channel capacity per ${
844847
ctx.sourceType
845-
} source (${infoGain.toFixed(2)} > ${max.toFixed(2)})`
848+
} source (${max.toFixed(2)})`
846849
)
850+
} else if (ctx.noteInfoGain) {
851+
ctx.note(infoGainMsg)
852+
}
853+
854+
if (ctx.noteInfoGain) {
855+
ctx.note(`number of possible output states: ${out.numStates}`)
856+
ctx.note(`randomized trigger rate: ${out.flipProb.toFixed(7)}`)
847857
}
848858
}
849859

@@ -1421,10 +1431,11 @@ export function validateSource(
14211431
json: string,
14221432
vsv: Readonly<VendorSpecificValues>,
14231433
sourceType: SourceType,
1424-
parseFullFlex: boolean = false
1434+
parseFullFlex: boolean = false,
1435+
noteInfoGain: boolean = false
14251436
): [ValidationResult, Maybe<Source>] {
14261437
return validateJSON(
1427-
new SourceContext(vsv, parseFullFlex, sourceType),
1438+
new SourceContext(vsv, parseFullFlex, sourceType, noteInfoGain),
14281439
json,
14291440
source
14301441
)

0 commit comments

Comments
 (0)