diff --git a/src/helper/argParse.ts b/src/helper/argParse.ts index 63bc148..b8e37e7 100644 --- a/src/helper/argParse.ts +++ b/src/helper/argParse.ts @@ -245,6 +245,11 @@ export const parsedArgs = y "How to bias where to place an operation during the scheduling mutation. 'reversebell' , 'uniform' uniformly selects in the possible interval.", choices: SPILLING, }) + .option("threshold", { + default: 0, + number: true, + describe: "How likely is a worse solution to be accepted. -1 for auto, i.e. depening on how bad it is.", + }) .help("help") .alias("h", "help") .wrap(Math.min(160, y.terminalWidth())) diff --git a/src/optimizer/optimizer.class.ts b/src/optimizer/optimizer.class.ts index 4dfbdea..8448c1f 100644 --- a/src/optimizer/optimizer.class.ts +++ b/src/optimizer/optimizer.class.ts @@ -41,7 +41,7 @@ import { RegisterAllocator } from "@/registerAllocator"; import type { AnalyseResult, OptimizerArgs } from "@/types"; import { genStatistics, genStatusLine, logMutation, printStartInfo } from "./optimizer.helper"; -import { init } from "./optimizer.helper.class"; +import { init, acceptWorseWithThres } from "./optimizer.helper.class"; import MeasureUtil from "./measure.class"; let choice: CHOICE; @@ -285,10 +285,11 @@ export class Optimizer { let kept: boolean; if ( - // A is not worse and A is new - (metricA <= metricB && currentFunctionIsA()) || - // or B is not worse and B is new - (metricA >= metricB && !currentFunctionIsA()) + acceptWorseWithThres( + currentFunctionIsA() ? metricA : metricB, + currentFunctionIsA() ? metricB : metricA, + this.args.threshold, + ) ) { Logger.log("kept mutation"); kept = true; diff --git a/src/optimizer/optimizer.helper.class.ts b/src/optimizer/optimizer.helper.class.ts index 77ccc1a..fef7485 100644 --- a/src/optimizer/optimizer.helper.class.ts +++ b/src/optimizer/optimizer.helper.class.ts @@ -186,3 +186,18 @@ export function init(tmpDir: string, args: neededArgs): { symbolname: string; me } return createMS(r, sharedObjectFilename); } +export function acceptWorseWithThres(new_metric: number, old_metric: number, thres: number): boolean { + // if the new one is already better, then accept + if (new_metric <= old_metric) { + return true; + } + + // else acceptance depends on the threshold. + // -1 means auto + if (thres == -1) { + const relation = old_metric / new_metric; // 0 number; + +beforeAll(() => { + originalRandom = Math.random; +}); +afterAll(() => { + Math.random = originalRandom; +}); + +describe("acceptWorseWithThres", () => { + it("should return true if new is smaller than old", () => { + expect(acceptWorseWithThres(10, 20, -1)).toBeTruthy(); + }); + + it("should likely return true thres=auto and new approx old", () => { + Math.random = () => 0.1; + expect(acceptWorseWithThres(21, 20, -1)).toBeTruthy(); + }); + it("should likely return false thres=auto and new far off old", () => { + Math.random = () => 0.1; + expect(acceptWorseWithThres(200, 20, -1)).toBeFalsy(); + }); + it("should likely return true if le than thres, independent of how much worse new is", () => { + Math.random = () => 0.1; + expect(acceptWorseWithThres(21, 20, 0.1)).toBeTruthy(); + expect(acceptWorseWithThres(22, 20, 0.1)).toBeTruthy(); + expect(acceptWorseWithThres(2200, 20, 0.1)).toBeTruthy(); + }); + it("should likely return false if higher than thres, independent of how much worse new is", () => { + Math.random = () => 0.2; + expect(acceptWorseWithThres(21, 20, 0.1)).toBeFalsy(); + expect(acceptWorseWithThres(22, 20, 0.1)).toBeFalsy(); + expect(acceptWorseWithThres(2200, 20, 0.1)).toBeFalsy(); + }); + it("should not return true if thres is 0 and the new one is worse (default)", () => { + Math.random = () => 0.2; + expect(acceptWorseWithThres(21, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(22, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(2200, 20, 0)).toBeFalsy(); + Math.random = () => 0.1; + expect(acceptWorseWithThres(21, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(22, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(2200, 20, 0)).toBeFalsy(); + Math.random = () => 0.00001; + expect(acceptWorseWithThres(21, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(22, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(2200, 20, 0)).toBeFalsy(); + Math.random = () => 0.9; + expect(acceptWorseWithThres(21, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(22, 20, 0)).toBeFalsy(); + expect(acceptWorseWithThres(2200, 20, 0)).toBeFalsy(); + }); +}); diff --git a/test/test-helpers.ts b/test/test-helpers.ts index 735bc15..7ae5c86 100644 --- a/test/test-helpers.ts +++ b/test/test-helpers.ts @@ -55,6 +55,7 @@ export function getTestArgs(filename: string): OptimizerArgs { objectiveFunction: "cycles", uicaarch: "RKL", spilling: "reversebell", + threshold: 0, }; }