Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { visualizeTinyGraph } from "./visualizeTinyGraph"
export type { StaticallyUnroutableRouteSummary } from "./static-reachability"

const GREEDY_FINAL_ROUTE_MAX_ITERATIONS = 50e3
export const GREEDY_PANIC_START_FRAC = 0.7
const DEFAULT_GREEDY_PANIC_START_FRAC = GREEDY_PANIC_START_FRAC

export const createEmptyRegionIntersectionCache =
(): RegionIntersectionCache => ({
Expand Down Expand Up @@ -238,6 +240,7 @@ export interface TinyHyperGraphSolverOptions {
STATIC_REACHABILITY_PRECHECK_MAX_HOPS?: number
ACCEPT_BEST_SOLUTION_ON_TIMEOUT?: boolean
GREEDY_FINAL_ROUTE_ITERS?: number
GREEDY_PANIC_START_FRAC?: number
}

export interface TinyHyperGraphSolverOptionTarget {
Expand All @@ -255,6 +258,7 @@ export interface TinyHyperGraphSolverOptionTarget {
STATIC_REACHABILITY_PRECHECK_MAX_HOPS: number
ACCEPT_BEST_SOLUTION_ON_TIMEOUT: boolean
GREEDY_FINAL_ROUTE_ITERS: number
GREEDY_PANIC_START_FRAC?: number
}

export const applyTinyHyperGraphSolverOptions = (
Expand Down Expand Up @@ -310,6 +314,9 @@ export const applyTinyHyperGraphSolverOptions = (
if (options.GREEDY_FINAL_ROUTE_ITERS !== undefined) {
solver.GREEDY_FINAL_ROUTE_ITERS = options.GREEDY_FINAL_ROUTE_ITERS
}
if (options.GREEDY_PANIC_START_FRAC !== undefined) {
solver.GREEDY_PANIC_START_FRAC = options.GREEDY_PANIC_START_FRAC
}
}

export const getTinyHyperGraphSolverOptions = (
Expand All @@ -330,6 +337,7 @@ export const getTinyHyperGraphSolverOptions = (
solver.STATIC_REACHABILITY_PRECHECK_MAX_HOPS,
ACCEPT_BEST_SOLUTION_ON_TIMEOUT: solver.ACCEPT_BEST_SOLUTION_ON_TIMEOUT,
GREEDY_FINAL_ROUTE_ITERS: solver.GREEDY_FINAL_ROUTE_ITERS,
GREEDY_PANIC_START_FRAC: solver.GREEDY_PANIC_START_FRAC,
})

const compareCandidatesByF = (left: Candidate, right: Candidate) =>
Expand Down Expand Up @@ -375,6 +383,7 @@ export class TinyHyperGraphSolver extends BaseSolver {
STATIC_REACHABILITY_PRECHECK_MAX_HOPS = 16
ACCEPT_BEST_SOLUTION_ON_TIMEOUT = true
GREEDY_FINAL_ROUTE_ITERS = 4
GREEDY_PANIC_START_FRAC = DEFAULT_GREEDY_PANIC_START_FRAC

constructor(
public topology: TinyHyperGraphTopology,
Expand Down Expand Up @@ -728,6 +737,35 @@ export class TinyHyperGraphSolver extends BaseSolver {
)
}

protected getGreedyPanicPenaltyScale(): number {
if (this.bestSolvedStateSnapshot) {
return 1
}

const currentRouteId = this.state.currentRouteId
if (
currentRouteId === undefined ||
this.routeAttemptCountByRouteId[currentRouteId]! <= 1 ||
this.routeSuccessCountByRouteId[currentRouteId]! > 0
) {
return 1
}

const startFrac = Math.min(1, Math.max(0, this.GREEDY_PANIC_START_FRAC))
const maxIterations = Math.max(1, this.MAX_ITERATIONS)
const progress = Math.min(1, Math.max(0, this.iterations / maxIterations))

if (progress <= startFrac) {
return 1
}

if (startFrac >= 1) {
return 1
}

return Math.max(0, (1 - progress) / (1 - startFrac))
}

populateSegmentGeometryScratch(
regionId: RegionId,
port1Id: PortId,
Expand Down Expand Up @@ -1403,11 +1441,14 @@ export class TinyHyperGraphSolver extends BaseSolver {
regionCache.existingSegmentCount + 1,
) - regionCache.existingRegionCost

const penaltyScale = this.getGreedyPanicPenaltyScale()

return (
currentCandidate.g +
newRegionCost +
state.regionCongestionCost[nextRegionId] +
(this.problem.portPenalty?.[neighborPortId] ?? 0)
(state.regionCongestionCost[nextRegionId] +
(this.problem.portPenalty?.[neighborPortId] ?? 0)) *
penaltyScale
)
}

Expand Down
35 changes: 35 additions & 0 deletions tests/solver/on-all-routes-routed.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect, test } from "bun:test"
import {
type Candidate,
GREEDY_PANIC_START_FRAC,
type TinyHyperGraphProblem,
TinyHyperGraphSolver,
type TinyHyperGraphTopology,
Expand Down Expand Up @@ -259,6 +260,7 @@ test("constructor options override snake-case hyperparameters before setup", ()
MAX_ITERATIONS: 1234,
ACCEPT_BEST_SOLUTION_ON_TIMEOUT: false,
GREEDY_FINAL_ROUTE_ITERS: 6,
GREEDY_PANIC_START_FRAC: 0.8,
})

expect(solver.DISTANCE_TO_COST).toBe(0.25)
Expand All @@ -269,5 +271,38 @@ test("constructor options override snake-case hyperparameters before setup", ()
expect(solver.MAX_ITERATIONS).toBe(1234)
expect(solver.ACCEPT_BEST_SOLUTION_ON_TIMEOUT).toBe(false)
expect(solver.GREEDY_FINAL_ROUTE_ITERS).toBe(6)
expect(solver.GREEDY_PANIC_START_FRAC).toBe(0.8)
expect(solver.problemSetup.portHCostToEndOfRoute[0]).toBe(0.25)
})

test("soft route penalties linearly drop after the greedy panic start fraction", () => {
const solver = createTestSolver({ MAX_ITERATIONS: 100 })
const currentCandidate: Candidate = {
nextRegionId: 0,
portId: 0,
f: 2,
g: 2,
h: 0,
}

solver.state.currentRouteId = 0
solver.state.currentRouteNetId = 0
solver.state.regionCongestionCost[0] = 6
solver.problem.portPenalty = new Float64Array([0, 4, 0, 0])
const solverWithProtectedCounters = solver as unknown as {
routeAttemptCountByRouteId: Uint32Array
}
solverWithProtectedCounters.routeAttemptCountByRouteId[0] = 2

solver.iterations = 0
expect(solver.computeG(currentCandidate, 1)).toBe(12)

solver.iterations = GREEDY_PANIC_START_FRAC * solver.MAX_ITERATIONS
expect(solver.computeG(currentCandidate, 1)).toBe(12)

solver.iterations = 85
expect(solver.computeG(currentCandidate, 1)).toBeCloseTo(7)

solver.iterations = solver.MAX_ITERATIONS
expect(solver.computeG(currentCandidate, 1)).toBe(2)
})
Loading