Skip to content

Commit

Permalink
Show information gain in header validator output (#1157)
Browse files Browse the repository at this point in the history
  • Loading branch information
apasel422 authored Feb 2, 2024
1 parent 72daf75 commit 7a4487f
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 16 deletions.
10 changes: 6 additions & 4 deletions ts/src/flexible-event/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,18 @@ config.peek((config) => {

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

if (out.excessive) {
const e = out.excessive
console.log(
`WARNING: info gain > ${infoGainMax.toFixed(2)} for ${
options.source_type
} sources. Would require a ${(100 * e.newFlipProb).toFixed(
5
)}% flip chance (effective epsilon = ${e.newEps.toFixed(3)}) to resolve.`
} sources. Would require a ${e.newFlipProb.toFixed(
7
)} randomized trigger rate (effective epsilon = ${e.newEps.toFixed(
3
)}) to resolve.`
)
}
})
11 changes: 10 additions & 1 deletion ts/src/header-validator/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ export type Issue = {
export type ValidationResult = {
errors: Issue[]
warnings: Issue[]
notes: Issue[]
}

export class Context {
private readonly path: PathComponent[] = []
private readonly result: ValidationResult = { errors: [], warnings: [] }
private readonly result: ValidationResult = {
errors: [],
warnings: [],
notes: [],
}

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

note(msg: string): void {
this.result.notes.push(this.issue(msg))
}

finish(topLevelError?: string): ValidationResult {
if (typeof topLevelError !== 'undefined') {
this.result.errors.push({ msg: topLevelError })
Expand Down
2 changes: 2 additions & 0 deletions ts/src/header-validator/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ <h1>Attribution Reporting Header Validation</h1>
<ul id=errors></ul>
<p>Warnings:
<ul id=warnings></ul>
<p>Notes:
<ul id=notes></ul>
</output>
</fieldset>
</div>
Expand Down
5 changes: 4 additions & 1 deletion ts/src/header-validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const sourceTypeRadios = form.elements.namedItem(
)! as RadioNodeList
const errorList = document.querySelector('#errors')!
const warningList = document.querySelector('#warnings')!
const noteList = document.querySelector('#notes')!
const successDiv = document.querySelector('#success')!
const sourceTypeFieldset = document.querySelector(
'#source-type'
Expand Down Expand Up @@ -69,7 +70,8 @@ function validate(): void {
input.value,
vsv.Chromium,
sourceType(),
flexCheckbox.checked
flexCheckbox.checked,
/*noteInfoGain=*/ true
)[0]
break
case 'trigger':
Expand Down Expand Up @@ -103,6 +105,7 @@ function validate(): void {

errorList.replaceChildren(...result.errors.map(makeLi))
warningList.replaceChildren(...result.warnings.map(makeLi))
noteList.replaceChildren(...result.notes.map(makeLi))
}

form.addEventListener('input', validate)
Expand Down
30 changes: 27 additions & 3 deletions ts/src/header-validator/source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as jsontest from './validate-json.test'

type TestCase = jsontest.TestCase<Source> & {
sourceType?: SourceType
noteInfoGain?: boolean
}

const testCases: TestCase[] = [
Expand Down Expand Up @@ -1223,6 +1224,7 @@ const testCases: TestCase[] = [
name: 'channel-capacity-default-event',
json: `{"destination": "https://a.test"}`,
sourceType: SourceType.event,
noteInfoGain: true,
vsv: {
maxEventLevelChannelCapacityPerSource: {
[SourceType.event]: 0,
Expand All @@ -1233,14 +1235,25 @@ const testCases: TestCase[] = [
expectedErrors: [
{
path: [],
msg: 'exceeds max event-level channel capacity per event source (1.58 > 0.00)',
msg: 'information gain: 1.58 exceeds max event-level channel capacity per event source (0.00)',
},
],
expectedNotes: [
{
path: [],
msg: 'number of possible output states: 3',
},
{
path: [],
msg: 'randomized trigger rate: 0.0000025',
},
],
},
{
name: 'channel-capacity-default-navigation',
json: `{"destination": "https://a.test"}`,
sourceType: SourceType.navigation,
noteInfoGain: true,
vsv: {
maxEventLevelChannelCapacityPerSource: {
[SourceType.event]: Infinity,
Expand All @@ -1251,7 +1264,17 @@ const testCases: TestCase[] = [
expectedErrors: [
{
path: [],
msg: 'exceeds max event-level channel capacity per navigation source (11.46 > 0.00)',
msg: 'information gain: 11.46 exceeds max event-level channel capacity per navigation source (0.00)',
},
],
expectedNotes: [
{
path: [],
msg: 'number of possible output states: 2925',
},
{
path: [],
msg: 'randomized trigger rate: 0.0024263',
},
],
},
Expand Down Expand Up @@ -1833,7 +1856,8 @@ testCases.forEach((tc) =>
tc.json,
{ ...vsv.Chromium, ...tc.vsv },
tc.sourceType ?? SourceType.navigation,
tc.parseFullFlex ?? false
tc.parseFullFlex ?? false,
tc.noteInfoGain ?? false
)
)
)
2 changes: 2 additions & 0 deletions ts/src/header-validator/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as context from './context'
export type TestCase = {
expectedWarnings?: context.Issue[]
expectedErrors?: context.Issue[]
expectedNotes?: context.Issue[]
}

export function run(
Expand All @@ -17,6 +18,7 @@ export function run(
assert.deepEqual(result, {
errors: tc.expectedErrors ?? [],
warnings: tc.expectedWarnings ?? [],
notes: tc.expectedNotes ?? [],
})
})
}
25 changes: 18 additions & 7 deletions ts/src/header-validator/validate-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class SourceContext extends RegistrationContext {
constructor(
vsv: Readonly<VendorSpecificValues>,
parseFullFlex: boolean,
readonly sourceType: SourceType
readonly sourceType: SourceType,
readonly noteInfoGain: boolean
) {
super(vsv, parseFullFlex)
}
Expand Down Expand Up @@ -832,18 +833,27 @@ function channelCapacity(ctx: SourceContext, s: Source): void {
perTriggerDataConfigs
)

const { infoGain } = config.computeConfigData(
const out = config.computeConfigData(
s.eventLevelEpsilon,
ctx.vsv.maxEventLevelChannelCapacityPerSource[ctx.sourceType]
)

const max = ctx.vsv.maxEventLevelChannelCapacityPerSource[ctx.sourceType]
if (infoGain > max) {
const infoGainMsg = `information gain: ${out.infoGain.toFixed(2)}`

if (out.infoGain > max) {
ctx.error(
`exceeds max event-level channel capacity per ${
`${infoGainMsg} exceeds max event-level channel capacity per ${
ctx.sourceType
} source (${infoGain.toFixed(2)} > ${max.toFixed(2)})`
} source (${max.toFixed(2)})`
)
} else if (ctx.noteInfoGain) {
ctx.note(infoGainMsg)
}

if (ctx.noteInfoGain) {
ctx.note(`number of possible output states: ${out.numStates}`)
ctx.note(`randomized trigger rate: ${out.flipProb.toFixed(7)}`)
}
}

Expand Down Expand Up @@ -1421,10 +1431,11 @@ export function validateSource(
json: string,
vsv: Readonly<VendorSpecificValues>,
sourceType: SourceType,
parseFullFlex: boolean = false
parseFullFlex: boolean = false,
noteInfoGain: boolean = false
): [ValidationResult, Maybe<Source>] {
return validateJSON(
new SourceContext(vsv, parseFullFlex, sourceType),
new SourceContext(vsv, parseFullFlex, sourceType, noteInfoGain),
json,
source
)
Expand Down

0 comments on commit 7a4487f

Please sign in to comment.