Skip to content

Commit

Permalink
naive permutative blame
Browse files Browse the repository at this point in the history
  • Loading branch information
mcnuttandrew committed Feb 10, 2024
1 parent 8a58de8 commit 60f1977
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 151 deletions.
10 changes: 10 additions & 0 deletions public/lint-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"==": { "$ref": "#/definitions/LintComparisonBase" }
},
"required": ["=="]
Expand All @@ -237,6 +238,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"!=": { "$ref": "#/definitions/LintComparisonBase" }
},
"required": ["!="]
Expand All @@ -245,6 +247,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"<": { "$ref": "#/definitions/LintComparisonBase" }
},
"required": ["<"]
Expand All @@ -253,6 +256,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
">": { "$ref": "#/definitions/LintComparisonBase" }
},
"required": [">"]
Expand All @@ -261,6 +265,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"similar": {
"type": "object",
"additionalProperties": false,
Expand Down Expand Up @@ -358,6 +363,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"and": {
"type": "array",
"items": { "$ref": "#/definitions/LintExpression" }
Expand All @@ -369,6 +375,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"or": {
"type": "array",
"items": { "$ref": "#/definitions/LintExpression" }
Expand All @@ -380,6 +387,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"not": { "$ref": "#/definitions/LintExpression" }
},
"required": ["not"]
Expand All @@ -393,6 +401,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"all": { "$ref": "#/definitions/LintQuantifierBase" }
},
"required": ["all"]
Expand All @@ -401,6 +410,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"exist": { "$ref": "#/definitions/LintQuantifierBase" }
},
"required": ["exist"]
Expand Down
60 changes: 59 additions & 1 deletion src/lib/lint-language/LintLanguage.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from "vitest";
import { LLEval, prettyPrintLL } from "./lint-language";
import { LLEval, prettyPrintLL, permutativeBlame } from "./lint-language";
import { Color } from "../Color";
import type { Palette } from "../../stores/color-store";
import type { LintProgram } from "./lint-type";
Expand Down Expand Up @@ -102,6 +102,64 @@ test("LintLanguage Quantifiers All - Simple", () => {
expect(LLEval(simpProg(["#7bb9ff"]), exampleColors).result).toBe(true);
});

test("LintLanguage permutativeBlame - counts", () => {
const prog1 = { "<": { left: { count: "colors" }, right: 2 } };
expect(permutativeBlame(prog1, exampleColors, "single")).toStrictEqual([
0, 1, 2,
]);
});

test("LintLanguage permutativeBlame - Quantifiers Simple", () => {
const simpProg = (colors: string[]) => ({
exist: {
in: "colors",
varb: "a",
predicate: {
exist: {
in: toColors(colors),
varb: "b",
predicate: { "==": { left: "a", right: "b" } },
},
},
},
});
expect(
permutativeBlame(simpProg(["red"]), exampleColors, "single")
).toStrictEqual([0, 1, 2]);
expect(
permutativeBlame(simpProg(["#7bb9ff"]), exampleColors, "single")
).toStrictEqual([]);
});

test("LintLanguage permutativeBlame - Quantifiers deuteranopia", () => {
const prog = allBlindProg("deuteranopia");
const pal = toPal(["#d4a8ff", "#7bb9ff", "#008694", "black"]);
expect(permutativeBlame(prog, pal, "single")).toStrictEqual([0, 1]);
expect(permutativeBlame(prog, pal, "pair")).toStrictEqual([[0, 1]]);
});

test("LintLanguage permutativeBlame - Tableau 10", () => {
const prog = allBlindProg("deuteranopia");
const pal = toPal([
"#1f77b4",
"#ff7f0e",
"#2ca02c",
"#d62728",
"#9467bd",
"#8c564b",
"#e377c2",
"#7f7f7f",
"#bcbd22",
"#17becf",
]);
expect(permutativeBlame(prog, pal, "pair")).toStrictEqual([
[0, 4],
[1, 8],
[2, 3],
[6, 9],
]);
});

const expectedOutBlind = (type: string) =>
`ALL a in colors, ALL b in colors WHERE index(a) != index(b), NOT similar(cvdSim(a, ${type}), cvdSim(b, ${type})) > 9`;
const allBlindProg = (type: string) => ({
Expand Down
89 changes: 89 additions & 0 deletions src/lib/lint-language/lint-language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,8 +695,12 @@ export class LLQuantifier extends LLNode {
);
let blameIndices = new Set<number>([]);
let topEnv = env.copy();
const checkedKeys = new Set<string>();
const mappedEvaluations = carts
.map((combo: any) => {
const key = combo.sort().join(",");
if (checkedKeys.has(key)) return "skip";
checkedKeys.add(key);
const varbIndex = this.varbs.map((varb, idx) => {
return [varb, inputData[combo[idx]]];
});
Expand Down Expand Up @@ -964,3 +968,88 @@ export function prettyPrintLL(
const ast = parseToAST({ id: [root] }, opts);
return ast.toString();
}

export function permutativeBlame(
root: LintProgram,
palette: Palette,
mode: "single" | "pair"
): number[] | number[][] {
const initialRun = LLEval(root, palette);
if (initialRun.result) return [];

// single blame
const allIndices = palette.colors.map((_, i) => i);
let blamedIndices = [] as number[];
if (mode === "single") {
// constructive blame
allIndices.forEach((x) => {
const tempPalette = { ...palette, colors: [palette.colors[x]] };
const result = LLEval(root, tempPalette);
if (!result.result) {
blamedIndices.push(x);
console.log("constructive", x);
}
});
if (blamedIndices.length > 0) return blamedIndices;
blamedIndices = palette.colors
.map((_, i) => {
const tempPalette = {
...palette,
colors: palette.colors.filter((_, j) => i !== j),
};
const result = LLEval(root, tempPalette);
// if it passes then this index is the problem

return result.result ? i : -1;
})
.filter((x) => x !== -1);
} else if (mode === "pair") {
// pair blame
// todo add trimming as appropriate
let checkIndices = new Set<string>();
const pairBlame = [] as number[][];
// constructive blame
allIndices.forEach((x) => {
allIndices.forEach((y) => {
if (x === y) return;
const key = [x, y].sort().join(",");
if (checkIndices.has(key)) return;
checkIndices.add(key);
const tempPalette = {
...palette,
colors: [palette.colors[x], palette.colors[y]],
};
const result = LLEval(root, tempPalette);
if (!result.result) {
pairBlame.push([x, y]);
}
});
if (pairBlame.length > 0) return pairBlame;
// reductive blame
blamedIndices.forEach((x) => {
blamedIndices.forEach((y) => {
const key = [x, y].sort().join(",");
if (checkIndices.has(key)) return;
if (x === y) return;
checkIndices.add(key);
// try out filter the pairs of blamed indices as pairs
const tempPalette = {
...palette,
colors: palette.colors.filter((_, j) => x !== j && y !== j),
};
const result = LLEval(root, tempPalette);
if (result.result) {
pairBlame.push([x, y]);
}
});
});
});

return pairBlame;
}
if (blamedIndices.length === 0) {
blamedIndices = [...allIndices];
}

return blamedIndices;
}
2 changes: 1 addition & 1 deletion src/lib/lint-language/lint-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ type LintReduce = Record<
>;
type LintColorFunction =
| {
cvdSim: LintVariable | LintColor;
cvd_sim: LintVariable | LintColor;
type: "protanomaly" | "deuteranomaly" | "tritanopia" | "grayscale";
}
| { name: LintVariable | LintColor }
Expand Down
5 changes: 0 additions & 5 deletions src/lib/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { Palette } from "../stores/color-store";

import NameDiscrim from "./lints/name-discrim";
import Discrims from "./lints/size-discrim";
import Blinds from "./lints/blind-check";
import ColorSimilarity from "./lints/color-similarity";
import BackgroundDifferentiability from "./lints/background-differentiability";
import SequentialOrder from "./lints/sequential-order";
Expand All @@ -24,7 +23,6 @@ export function runLintChecks(
[
NameDiscrim,
...Discrims,
...Blinds,
ColorSimilarity,
BackgroundDifferentiability,
SequentialOrder,
Expand All @@ -49,6 +47,3 @@ export function runLintChecks(
}
});
}

// typesript type for an list of classes
// https://stackoverflow.com/questions/57465358/typescript-type-for-an-list-of-classes
1 change: 1 addition & 0 deletions src/lib/lints/ColorLint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class ColorLint<CheckData, ParamType> {
isCustom: false | string = false;
group: string = "";
description: string = "";
blameMode: "pair" | "single" | "none" = "none";
paramOptions:
| { type: "number"; min: number; max: number; step: number }
| { type: "enum"; options: string[] }
Expand Down
36 changes: 30 additions & 6 deletions src/lib/lints/CustomLint.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { LintProgram } from "../lint-language/lint-type";
import { ColorLint } from "./ColorLint";
import type { TaskType } from "./ColorLint";
import { LLEval, prettyPrintLL } from "../lint-language/lint-language";
import {
LLEval,
prettyPrintLL,
permutativeBlame,
} from "../lint-language/lint-language";

import * as Json from "jsonc-parser";
export interface CustomLint {
Expand All @@ -13,30 +17,50 @@ export interface CustomLint {
description: string;
failMessage: string;
id: string;
blameMode: "pair" | "single" | "none";
}

export function CreateCustomLint(props: CustomLint) {
return class CustomLint extends ColorLint<number[], any> {
return class CustomLint extends ColorLint<number[] | number[][], any> {
name = props.name;
taskTypes = props.taskTypes;
level = props.level;
group = props.group;
description = props.description;
hasHeuristicFix = false;
isCustom = props.id;
blameMode = props.blameMode;

_runCheck() {
const prog = Json.parse(props.program);
const { blame, result } = LLEval(prog, this.palette, {
debugCompare: false,
});
return { passCheck: result, data: blame };
if (result) return { passCheck: true, data: blame };

return {
passCheck: result,
data:
props.blameMode !== "none"
? permutativeBlame(prog, this.palette, props.blameMode)
: [],
};
}

buildMessage() {
const blame = this.checkData
.map((x) => this.palette.colors[x].toHex())
.join(", ");
let blame = "";
if (this.blameMode === "pair") {
blame = (this.checkData as number[][])
.map((x) =>
x.map((x) => this.palette.colors[x].toHex()).join(" and ")
)
.join(", ");
} else {
blame = (this.checkData as number[])
.map((x) => this.palette.colors[x].toHex())
.join(", ");
}

return props.failMessage.replace("{{blame}}", blame);
}
};
Expand Down
Loading

0 comments on commit 60f1977

Please sign in to comment.