@mark1russell7/highways 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -5
- package/build/index.d.ts +954 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10689 -5294
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/bench/screen/exercise.ts +4 -1
- package/src/bench/screen/set.ts +58 -1
- package/src/families/cases.ts +17 -9
- package/src/families/coverage.ts +502 -0
- package/src/families/index.ts +4 -1
- package/src/highways/balance/index.ts +93 -0
- package/src/highways/bronchial/index.ts +144 -0
- package/src/highways/index.ts +5 -0
- package/src/highways/knee/index.ts +154 -0
- package/src/highways/knee/root.ts +19 -0
- package/src/highways/manifolds/index.ts +157 -0
- package/src/highways/slots/index.ts +178 -0
- package/src/index.ts +8 -0
- package/src/levels/detail/index.ts +140 -0
- package/src/levels/venation/index.ts +191 -0
- package/src/plugs/leaders/index.ts +42 -0
- package/src/plugs/spread/index.ts +39 -5
- package/src/routers/adam/index.ts +294 -0
- package/src/routers/annealing/index.ts +260 -0
- package/src/routers/canals/index.ts +191 -0
- package/src/routers/frames.ts +80 -16
- package/src/routers/gutter.ts +545 -0
- package/src/routers/index.ts +8 -2
- package/src/routers/reroute/index.ts +164 -0
- package/src/routers/shared.ts +43 -2
- package/src/routers/tight/index.ts +122 -0
- package/src/strategies/builtins.ts +158 -2
- package/src/strategies/schemas.ts +143 -0
- package/src/whole/curriculum/index.ts +243 -0
- package/src/whole/fireflies/index.ts +177 -0
- package/src/whole/portfolio/index.ts +108 -0
- package/src/whole/preferences/index.ts +247 -0
- package/src/whole/tracing/index.ts +144 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { dist, type Point } from "../../geom.ts";
|
|
2
|
+
import type { Graph, GroupRule, Result, Router } from "../../model.ts";
|
|
3
|
+
import { defineStrategy, type Strategy } from "../../strategies/model.ts";
|
|
4
|
+
import type { OptionSchema } from "../../strategies/options.ts";
|
|
5
|
+
import { sharedRouter, type SharedSearch, type Way } from "../shared.ts";
|
|
6
|
+
import { byCollection } from "../trunks.ts";
|
|
7
|
+
|
|
8
|
+
/** The sine of 1 degree, as a literal. A literal gives the same bits on each platform, so a decision does not depend on `Math.sin`. */
|
|
9
|
+
const SIN_1 = 0.01745240643728351;
|
|
10
|
+
|
|
11
|
+
/** The options of the router `reroute` (RULE-044: a corner graph with the prices of the rules, and rounds of rip-up and reroute). */
|
|
12
|
+
export interface RerouteOptions {
|
|
13
|
+
/** The price of a link in a narrow channel, from 0 to 2. The default is 1. */
|
|
14
|
+
readonly alpha: number;
|
|
15
|
+
/** The price of each edge of another key on a link, from 0 to 2. The default is 0.5. */
|
|
16
|
+
readonly beta: number;
|
|
17
|
+
/** The discount of a link that an edge of the same key uses, from 0 to 2. The default is 0.5. */
|
|
18
|
+
readonly gamma: number;
|
|
19
|
+
/** The price of a turn, in units of `clearance`, from 0 to 2. The default is 0.25. */
|
|
20
|
+
readonly tau: number;
|
|
21
|
+
/** The number of rounds of rip-up and reroute, from 1 to 5. The default is 2. */
|
|
22
|
+
readonly rounds: number;
|
|
23
|
+
/** The share of the edges that each round rips up: the edges with the largest price over their direct length, from 0.05 to 0.3. The default is 0.15. */
|
|
24
|
+
readonly q: number;
|
|
25
|
+
/** A channel narrower than this width gets the price `alpha`, from 4 to 64. The default is 16. */
|
|
26
|
+
readonly clearance: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The default options of the router `reroute`. */
|
|
30
|
+
export const REROUTE_DEFAULTS: Readonly<RerouteOptions> = Object.freeze({ alpha: 1, beta: 0.5, gamma: 0.5, tau: 0.25, rounds: 2, q: 0.15, clearance: 16 });
|
|
31
|
+
|
|
32
|
+
/** The declarations of the options of the router `reroute` (RULE-044). */
|
|
33
|
+
export const REROUTE_SCHEMA: OptionSchema<RerouteOptions> = {
|
|
34
|
+
alpha: { type: "number", min: 0, max: 2, step: 0.05, marks: [0, 1, 2], default: 1, describe: "The price of a link in a narrow channel." },
|
|
35
|
+
beta: { type: "number", min: 0, max: 2, step: 0.05, marks: [0, 0.5, 1], default: 0.5, describe: "The price of each edge of another key on a link." },
|
|
36
|
+
gamma: { type: "number", min: 0, max: 2, step: 0.05, marks: [0, 0.5, 1], default: 0.5, describe: "The discount of a link that an edge of the same key uses." },
|
|
37
|
+
tau: { type: "number", min: 0, max: 2, step: 0.05, marks: [0, 0.25, 1], default: 0.25, describe: "The price of a turn, in units of the clearance." },
|
|
38
|
+
rounds: { type: "integer", min: 1, max: 5, step: 1, marks: [1, 2, 3, 5], default: 2, describe: "The number of rounds of rip-up and reroute." },
|
|
39
|
+
q: { type: "number", min: 0.05, max: 0.3, step: 0.05, marks: [0.05, 0.15, 0.3], default: 0.15, describe: "The share of the edges that each round rips up: the edges with the largest price over their direct length." },
|
|
40
|
+
clearance: { type: "number", min: 4, max: 64, step: 1, marks: [8, 16, 32], default: 16, describe: "A channel narrower than this width gets the price alpha." },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** This function gives the number of turns of a way: the points where the direction changes by more than 1 degree. */
|
|
44
|
+
export function turnsOf(seq: readonly Point[]): number {
|
|
45
|
+
let turns = 0;
|
|
46
|
+
for (let i = 1; i + 1 < seq.length; i++) {
|
|
47
|
+
const a = seq[i - 1]!;
|
|
48
|
+
const b = seq[i]!;
|
|
49
|
+
const c = seq[i + 1]!;
|
|
50
|
+
const ux = b.x - a.x;
|
|
51
|
+
const uy = b.y - a.y;
|
|
52
|
+
const vx = c.x - b.x;
|
|
53
|
+
const vy = c.y - b.y;
|
|
54
|
+
const lu = Math.sqrt(ux * ux + uy * uy);
|
|
55
|
+
const lv = Math.sqrt(vx * vx + vy * vy);
|
|
56
|
+
if (lu <= 1e-9 || lv <= 1e-9) continue;
|
|
57
|
+
if (Math.abs(ux * vy - uy * vx) > SIN_1 * lu * lv || ux * vx + uy * vy < 0) turns++;
|
|
58
|
+
}
|
|
59
|
+
return turns;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The use of one link of the corner graph: the number of edges of each key on it. */
|
|
63
|
+
type LinkUse = Map<string, number>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* This function rips up and reroutes the ways of `shared` (RULE-044, after the rip-up and reroute of PathFinder, McMurchie 1995).
|
|
67
|
+
* The price factor of a link is `max(0.05, 1 + alpha × narrow + beta × others - gamma × same)`.
|
|
68
|
+
* `narrow` is `max(0, 1 - width / clearance)`. `others` counts the edges of the other keys on the link.
|
|
69
|
+
* `same` is 1 when an edge of the same key uses the link.
|
|
70
|
+
* The price of a way is the sum of the price factor times the length of each link, plus `tau × clearance` for each turn.
|
|
71
|
+
* Each round takes the share `q` of the ways with the largest price over their direct length. Each one gets a new search with the prices of the links.
|
|
72
|
+
* The new way stays only when its price is lower. The search has no state for the turns, so only the comparison reads `tau`.
|
|
73
|
+
*/
|
|
74
|
+
export function rerouteWays(ways: readonly Way[], search: SharedSearch, options: Partial<RerouteOptions> = {}): void {
|
|
75
|
+
const o = { ...REROUTE_DEFAULTS, ...options };
|
|
76
|
+
const n = search.corners;
|
|
77
|
+
const use = new Map<number, LinkUse>();
|
|
78
|
+
const linksOf = (w: Way): number[] => {
|
|
79
|
+
const out: number[] = [];
|
|
80
|
+
for (let i = 1; i < w.ids.length; i++) {
|
|
81
|
+
const a = w.ids[i - 1]!;
|
|
82
|
+
const b = w.ids[i]!;
|
|
83
|
+
if (a < n && b < n) out.push(search.link(a, b));
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
};
|
|
87
|
+
const change = (w: Way, by: number): void => {
|
|
88
|
+
for (const k of linksOf(w)) {
|
|
89
|
+
let m = use.get(k);
|
|
90
|
+
if (!m) use.set(k, (m = new Map()));
|
|
91
|
+
const next = (m.get(w.key) ?? 0) + by;
|
|
92
|
+
if (next > 0) m.set(w.key, next);
|
|
93
|
+
else m.delete(w.key);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const found = ways.filter((w) => w.found && w.ids.length >= 2);
|
|
97
|
+
for (const w of found) change(w, 1);
|
|
98
|
+
const factor = (i: number, j: number, key: string): number => {
|
|
99
|
+
const m = use.get(search.link(i, j));
|
|
100
|
+
let others = 0;
|
|
101
|
+
let same = 0;
|
|
102
|
+
for (const [k, c] of m ?? []) {
|
|
103
|
+
if (k === key) same = 1;
|
|
104
|
+
else others += c;
|
|
105
|
+
}
|
|
106
|
+
const narrow = o.alpha > 0 ? Math.max(0, 1 - search.width(i, j) / o.clearance) : 0;
|
|
107
|
+
return Math.max(0.05, 1 + o.alpha * narrow + o.beta * others - o.gamma * same);
|
|
108
|
+
};
|
|
109
|
+
const priceOf = (seq: readonly Point[], ids: readonly number[], key: string): number => {
|
|
110
|
+
let p = 0;
|
|
111
|
+
for (let i = 1; i < seq.length; i++) {
|
|
112
|
+
const a = ids[i - 1]!;
|
|
113
|
+
const b = ids[i]!;
|
|
114
|
+
const d = dist(seq[i - 1]!, seq[i]!);
|
|
115
|
+
p += a < n && b < n ? d * factor(a, b, key) : d;
|
|
116
|
+
}
|
|
117
|
+
return p + o.tau * o.clearance * turnsOf(seq);
|
|
118
|
+
};
|
|
119
|
+
const floor = Math.max(0.05, 1 - o.gamma);
|
|
120
|
+
const count = (share: number): number => Math.max(1, Math.round(share * found.length));
|
|
121
|
+
for (let round = 0; round < Math.max(1, Math.floor(o.rounds)); round++) {
|
|
122
|
+
const ranked = found
|
|
123
|
+
.map((w) => ({ w, score: priceOf(w.seq, w.ids, w.key) / Math.max(1e-9, dist(w.ps.point, w.pt.point)) }))
|
|
124
|
+
.sort((a, b) => b.score - a.score || (a.w.edge.id < b.w.edge.id ? -1 : a.w.edge.id > b.w.edge.id ? 1 : 0));
|
|
125
|
+
let moved = 0;
|
|
126
|
+
for (const { w } of ranked.slice(0, count(Math.min(0.3, Math.max(0.05, o.q))))) {
|
|
127
|
+
change(w, -1);
|
|
128
|
+
const before = priceOf(w.seq, w.ids, w.key);
|
|
129
|
+
const next = search.find(w, (i, j, d) => d * factor(i, j, w.key), floor);
|
|
130
|
+
if (next && priceOf(next.seq, next.ids, w.key) < before - 1e-9) {
|
|
131
|
+
w.seq = next.seq;
|
|
132
|
+
w.ids = next.ids;
|
|
133
|
+
moved++;
|
|
134
|
+
}
|
|
135
|
+
change(w, 1);
|
|
136
|
+
}
|
|
137
|
+
if (moved === 0) break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* This function makes the router `reroute` (RULE-044). It is the router `shared`, then the rounds of rip-up and reroute of `rerouteWays`.
|
|
143
|
+
* The runs and the bundles come from the ways after the rounds.
|
|
144
|
+
*/
|
|
145
|
+
export function rerouteRouter(options: Partial<RerouteOptions> & { readonly name: string; readonly rule?: GroupRule; readonly maxNodes?: number }): Router {
|
|
146
|
+
const o = { ...REROUTE_DEFAULTS, ...options };
|
|
147
|
+
return sharedRouter({ name: o.name, ...(o.rule === undefined ? {} : { rule: o.rule }), ...(o.maxNodes === undefined ? {} : { maxNodes: o.maxNodes }) }, undefined, {
|
|
148
|
+
refine: (ways, search) => rerouteWays(ways, search, o),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The router strategy `reroute` (RULE-044), with the rule `byCollection` of `shared`. It is not a built-in router, so the benchmark and the hashes of `ROUTERS` do not change.
|
|
154
|
+
* A screen module or a consumer adds it to a registry.
|
|
155
|
+
*/
|
|
156
|
+
export const rerouteStrategy: Strategy<"router", RerouteOptions> = defineStrategy({
|
|
157
|
+
step: "router",
|
|
158
|
+
name: "reroute",
|
|
159
|
+
describe: "Rule prices and rip-up (RULE-044). The ways of `shared` with the largest prices of narrow channels, other keys and turns get a new search.",
|
|
160
|
+
defaults: { ...REROUTE_DEFAULTS },
|
|
161
|
+
schema: REROUTE_SCHEMA,
|
|
162
|
+
maxNodes: 10_000,
|
|
163
|
+
run: (graph: Graph, o: Readonly<RerouteOptions>): Result => rerouteRouter({ ...o, name: "reroute", rule: byCollection }).route(graph),
|
|
164
|
+
});
|
package/src/routers/shared.ts
CHANGED
|
@@ -92,6 +92,35 @@ export interface SharedNegotiation {
|
|
|
92
92
|
readonly laneWidth: number;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The search of the corner graph of one route of `shared`, for the hooks of `SharedHooks`.
|
|
97
|
+
* An index below `corners` is a corner of the graph. Each end of a way gets an index from `corners`, one for each place.
|
|
98
|
+
*/
|
|
99
|
+
export interface SharedSearch {
|
|
100
|
+
/** The graph of the route, without the inner edges of a frame. */
|
|
101
|
+
readonly graph: Graph;
|
|
102
|
+
/** The number of corners of the corner graph. */
|
|
103
|
+
readonly corners: number;
|
|
104
|
+
/** This function gives the key of the link between the indices `i` and `j`, for each order of the two. */
|
|
105
|
+
link(i: number, j: number): number;
|
|
106
|
+
/**
|
|
107
|
+
* This function gives a shortest way of `w` through the corner graph, with its points and their indices, or `undefined`.
|
|
108
|
+
* The function `cost` can change the cost of the link from corner `i` to corner `j`, of length `d`. It must give at least `floor` × `d`.
|
|
109
|
+
*/
|
|
110
|
+
find(w: Way, cost?: (i: number, j: number, d: number) => number, floor?: number): { seq: Point[]; ids: number[] } | undefined;
|
|
111
|
+
/** This function gives the free space across the link from corner `i` to corner `j` (`channelWidth`). The router keeps each width that it finds. */
|
|
112
|
+
width(i: number, j: number): number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The hooks of a router that `shared` runs (WILD-023, RULE-021). The router `shared` itself has no hooks.
|
|
117
|
+
* `order` gives the order of the ways before the first pass. `refine` changes the ways after the passes, before the runs.
|
|
118
|
+
*/
|
|
119
|
+
export interface SharedHooks {
|
|
120
|
+
readonly order?: (ways: readonly Way[], search: SharedSearch) => Way[];
|
|
121
|
+
readonly refine?: (ways: readonly Way[], search: SharedSearch) => void;
|
|
122
|
+
}
|
|
123
|
+
|
|
95
124
|
/** A way of one edge through the corner graph. */
|
|
96
125
|
export interface Way {
|
|
97
126
|
readonly edge: Edge;
|
|
@@ -129,7 +158,7 @@ function turn(from: Point, base: Point, to: Point): number {
|
|
|
129
158
|
* Thus the edges of a key come together on common ways. Each stretch that several edges share has the same points in each route.
|
|
130
159
|
* The result gives each stretch as a run, with the order of its lanes.
|
|
131
160
|
*/
|
|
132
|
-
export function sharedRouter(options: SharedOptions, negotiation?: SharedNegotiation): Router {
|
|
161
|
+
export function sharedRouter(options: SharedOptions, negotiation?: SharedNegotiation, hooks?: SharedHooks): Router {
|
|
133
162
|
const discount = Math.min(1, Math.max(0.05, options.discount ?? 0.5));
|
|
134
163
|
const flow = options.cost === "flow";
|
|
135
164
|
const alpha = Math.min(1, Math.max(0.1, options.alpha ?? 0.6));
|
|
@@ -177,7 +206,18 @@ export function sharedRouter(options: SharedOptions, negotiation?: SharedNegotia
|
|
|
177
206
|
|
|
178
207
|
// The edges of one key go one after the other, by default the longest first, so that the short edges join the long ways.
|
|
179
208
|
const discounted = new Map<string, Map<number, string[]>>();
|
|
180
|
-
const
|
|
209
|
+
const search: SharedSearch = {
|
|
210
|
+
graph,
|
|
211
|
+
corners: corners.points.length,
|
|
212
|
+
link,
|
|
213
|
+
find: (w, cost, floor = 1) => {
|
|
214
|
+
const way = corners.path(ways.out(w.ps), ways.out(w.pt), cost, floor);
|
|
215
|
+
return way && { seq: way, ids: way.map((p, i) => (i === 0 || i === way.length - 1 ? endId(p) : index.get(p)!)) };
|
|
216
|
+
},
|
|
217
|
+
width: (i, j) => widthOf(i, j),
|
|
218
|
+
};
|
|
219
|
+
const sorted = orderWays(all, options.order ?? "span", graph);
|
|
220
|
+
const order = hooks?.order ? hooks.order(sorted, search) : sorted;
|
|
181
221
|
// The largest number of edges on one link of each key. The cost share of that number is the floor of the search.
|
|
182
222
|
const top = new Map<string, number>();
|
|
183
223
|
// The width of the channel of each link, for the option `capacity`. Each link gets its width at its first use.
|
|
@@ -308,6 +348,7 @@ export function sharedRouter(options: SharedOptions, negotiation?: SharedNegotia
|
|
|
308
348
|
}
|
|
309
349
|
}
|
|
310
350
|
|
|
351
|
+
hooks?.refine?.(order, search);
|
|
311
352
|
// With `merge: snell`, each route that joins or leaves a run moves its merge point along the run to the Snell angle.
|
|
312
353
|
if (options.merge === "snell") {
|
|
313
354
|
const pad = ways.margin / 2;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { byText } from "../../compare.ts";
|
|
2
|
+
import { dist, polylineLength } from "../../geom.ts";
|
|
3
|
+
import type { Graph, GroupRule, Result, Router } from "../../model.ts";
|
|
4
|
+
import { defineStrategy, type Strategy } from "../../strategies/model.ts";
|
|
5
|
+
import type { OptionSchema } from "../../strategies/options.ts";
|
|
6
|
+
import { sharedRouter, type SharedSearch, type Way } from "../shared.ts";
|
|
7
|
+
import { byCollection } from "../trunks.ts";
|
|
8
|
+
|
|
9
|
+
/** The edges that go first: the edges with few good ways (`tight`), or the edges with many good ways (`loose`). */
|
|
10
|
+
export type TightFirst = "tight" | "loose";
|
|
11
|
+
|
|
12
|
+
/** The values of `TightFirst`. */
|
|
13
|
+
export const TIGHT_FIRSTS: readonly TightFirst[] = Object.freeze(["tight", "loose"]);
|
|
14
|
+
|
|
15
|
+
/** The order of two edges with the same slack: by the edge id, or the longer edge first. */
|
|
16
|
+
export type TightTies = "id" | "length";
|
|
17
|
+
|
|
18
|
+
/** The values of `TightTies`. */
|
|
19
|
+
export const TIGHT_TIES: readonly TightTies[] = Object.freeze(["id", "length"]);
|
|
20
|
+
|
|
21
|
+
/** The options of the router `tight` (WILD-023: tight edges first, after list scheduling by slack). */
|
|
22
|
+
export interface TightOptions {
|
|
23
|
+
/** The cost factor of the links of the first way in the search of the second way, from 2 to 10. The default is 3. */
|
|
24
|
+
readonly penalty: number;
|
|
25
|
+
/**
|
|
26
|
+
* The edges that go first. With `tight`, the default, an edge whose second way costs much more than its first way goes first.
|
|
27
|
+
* With `loose`, the order turns around: the formula of the record, with the rising slack.
|
|
28
|
+
*/
|
|
29
|
+
readonly first: TightFirst;
|
|
30
|
+
/** The order of two edges with the same slack. The default is `length`: the longer edge first, as in `shared`. */
|
|
31
|
+
readonly ties: TightTies;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The default options of the router `tight`. */
|
|
35
|
+
export const TIGHT_DEFAULTS: Readonly<TightOptions> = Object.freeze({ penalty: 3, first: "tight", ties: "length" });
|
|
36
|
+
|
|
37
|
+
/** The declarations of the options of the router `tight` (WILD-023). */
|
|
38
|
+
export const TIGHT_SCHEMA: OptionSchema<TightOptions> = {
|
|
39
|
+
penalty: { type: "number", min: 2, max: 10, step: 0.5, marks: [2, 3, 5], default: 3, describe: "The cost factor of the links of the first way in the search of the second way." },
|
|
40
|
+
first: { type: "enum", values: ["tight", "loose"], default: "tight", describe: "The edges that go first: the edges with few good ways, or the edges with many good ways." },
|
|
41
|
+
ties: { type: "enum", values: ["id", "length"], default: "length", describe: "The order of two edges with the same slack: by the edge id, or the longer edge first." },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* This function gives the slack of each way (WILD-023): `L2 / L1 - 1`. `L1` is the length of the shortest way.
|
|
46
|
+
* `L2` is the cost of the shortest way when each link of the first way costs `penalty` times its length.
|
|
47
|
+
* A way with no second way has the slack `penalty - 1`. A way with a second way of near-equal length has a slack near 0.
|
|
48
|
+
*/
|
|
49
|
+
export function waySlacks(ways: readonly Way[], search: SharedSearch, penalty: number): Map<Way, number> {
|
|
50
|
+
const out = new Map<Way, number>();
|
|
51
|
+
const n = search.corners;
|
|
52
|
+
for (const w of ways) {
|
|
53
|
+
const first = search.find(w);
|
|
54
|
+
if (!first) {
|
|
55
|
+
out.set(w, 0);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const l1 = polylineLength(first.seq);
|
|
59
|
+
const own = new Set<number>();
|
|
60
|
+
for (let i = 1; i < first.ids.length; i++) {
|
|
61
|
+
const a = first.ids[i - 1]!;
|
|
62
|
+
const b = first.ids[i]!;
|
|
63
|
+
if (a < n && b < n) own.add(search.link(a, b));
|
|
64
|
+
}
|
|
65
|
+
const cost = (i: number, j: number, d: number): number => (own.has(search.link(i, j)) ? d * penalty : d);
|
|
66
|
+
const second = search.find(w, cost, 1);
|
|
67
|
+
let l2 = l1;
|
|
68
|
+
if (second) {
|
|
69
|
+
l2 = 0;
|
|
70
|
+
for (let i = 1; i < second.ids.length; i++) {
|
|
71
|
+
const a = second.ids[i - 1]!;
|
|
72
|
+
const b = second.ids[i]!;
|
|
73
|
+
const d = dist(second.seq[i - 1]!, second.seq[i]!);
|
|
74
|
+
l2 += a < n && b < n ? cost(a, b, d) : d;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
out.set(w, l1 > 1e-9 ? l2 / l1 - 1 : 0);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* This function makes the router `tight` (WILD-023). It is the router `shared` with another order of the edges of each key.
|
|
84
|
+
* With `first: tight`, the edges with the largest slack value go first: their second way costs much more, so they have few good ways.
|
|
85
|
+
* The edges with many ways of near-equal cost go last. The keys keep the order of `shared`.
|
|
86
|
+
*/
|
|
87
|
+
export function tightRouter(options: Partial<TightOptions> & { readonly name: string; readonly rule?: GroupRule; readonly maxNodes?: number }): Router {
|
|
88
|
+
const o = { ...TIGHT_DEFAULTS, ...options };
|
|
89
|
+
const penalty = Math.min(10, Math.max(1, o.penalty));
|
|
90
|
+
const sign = o.first === "loose" ? 1 : -1;
|
|
91
|
+
return sharedRouter(
|
|
92
|
+
{ name: o.name, ...(o.rule === undefined ? {} : { rule: o.rule }), ...(o.maxNodes === undefined ? {} : { maxNodes: o.maxNodes }) },
|
|
93
|
+
undefined,
|
|
94
|
+
{
|
|
95
|
+
order: (ways, search) => {
|
|
96
|
+
const slack = waySlacks(ways, search, penalty);
|
|
97
|
+
const span = (w: Way): number => dist(w.ps.point, w.pt.point);
|
|
98
|
+
return [...ways].sort(
|
|
99
|
+
(p, q) =>
|
|
100
|
+
byText(p.key, q.key) ||
|
|
101
|
+
sign * (slack.get(p)! - slack.get(q)!) ||
|
|
102
|
+
(o.ties === "length" ? span(q) - span(p) : 0) ||
|
|
103
|
+
byText(p.edge.id, q.edge.id),
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The router strategy `tight` (WILD-023), with the rule `byCollection` of `shared`. It is not a built-in router, so the benchmark and the hashes of `ROUTERS` do not change.
|
|
112
|
+
* A screen module or a consumer adds it to a registry.
|
|
113
|
+
*/
|
|
114
|
+
export const tightStrategy: Strategy<"router", TightOptions> = defineStrategy({
|
|
115
|
+
step: "router",
|
|
116
|
+
name: "tight",
|
|
117
|
+
describe: "Tight edges first (WILD-023). The router `shared` routes first the edges whose second way costs much more than their first way.",
|
|
118
|
+
defaults: { ...TIGHT_DEFAULTS },
|
|
119
|
+
schema: TIGHT_SCHEMA,
|
|
120
|
+
maxNodes: 10_000,
|
|
121
|
+
run: (graph: Graph, o: Readonly<TightOptions>): Result => tightRouter({ ...o, name: "tight", rule: byCollection }).route(graph),
|
|
122
|
+
});
|
|
@@ -46,6 +46,10 @@ import { P_HUB_DEFAULTS, pHubPlan, type PHubOptions } from "../highways/p-hub/in
|
|
|
46
46
|
import { SCALE_SPACE_DEFAULTS, scaleSpacePlan, type ScaleSpaceOptions } from "../highways/scale-space/index.ts";
|
|
47
47
|
import { GAP_DEFAULTS, gapPlan, type GapOptions } from "../highways/gaps/index.ts";
|
|
48
48
|
import { INTERCHANGE_DEFAULTS, interchangePlan, type InterchangeOptions } from "../highways/interchanges/index.ts";
|
|
49
|
+
import { KNEE_DEFAULTS, kneePlan, type KneeOptions } from "../highways/knee/index.ts";
|
|
50
|
+
import { BALANCE_DEFAULTS, balancePlan, type BalanceOptions } from "../highways/balance/index.ts";
|
|
51
|
+
import { MANIFOLD_DEFAULTS, manifoldPlan, type ManifoldOptions } from "../highways/manifolds/index.ts";
|
|
52
|
+
import { BRONCHIAL_DEFAULTS, bronchialPlan, type BronchialOptions } from "../highways/bronchial/index.ts";
|
|
49
53
|
import { SEARCH_DEFAULTS, searchPlan, type SearchOptions } from "../highways/search/index.ts";
|
|
50
54
|
import { STEINER_DEFAULTS, steinerPlan, type SteinerOptions } from "../highways/steiner/index.ts";
|
|
51
55
|
import { STREET_DEFAULTS, streetPlan, type StreetOptions } from "../highways/streets/index.ts";
|
|
@@ -72,11 +76,14 @@ import { WEISZFELD_DEFAULTS, weiszfeldPlan, type WeiszfeldOptions } from "../hig
|
|
|
72
76
|
import { WSPD_DEFAULTS, wspdPlan, type WspdOptions } from "../highways/wspd/index.ts";
|
|
73
77
|
import { ZIPPER_DEFAULTS, zipperPlan, type ZipperOptions } from "../highways/zipper/index.ts";
|
|
74
78
|
import { depthLevels, type DepthLevelOptions } from "../levels/depths/index.ts";
|
|
79
|
+
import { VENATION_DEFAULTS, venationLevels, type VenationOptions } from "../levels/venation/index.ts";
|
|
80
|
+
import { DETAIL_DEFAULTS, detailLevels, type DetailOptions } from "../levels/detail/index.ts";
|
|
75
81
|
import type { Graph, Result, Router } from "../model.ts";
|
|
76
82
|
import { facingEnds } from "../plugs/facing/index.ts";
|
|
77
83
|
import { justifyEnds, type JustifyOptions } from "../plugs/justify.ts";
|
|
78
84
|
import { sideEnds, type SidePlugOptions } from "../plugs/side.ts";
|
|
79
85
|
import { SPREAD_DEFAULTS, spreadEnds, type SpreadOptions } from "../plugs/spread/index.ts";
|
|
86
|
+
import { LEADER_DEFAULTS, leaderEnds, type LeaderOptions } from "../plugs/leaders/index.ts";
|
|
80
87
|
import { roomDemand, type RoomMethod, type RoomOptions } from "../room/index.ts";
|
|
81
88
|
import { levelRoom, type LevelRoomOptions } from "../room/levels/index.ts";
|
|
82
89
|
import { cornersRouter, type CornersOptions } from "../routers/corners.ts";
|
|
@@ -87,11 +94,16 @@ import { straightRouter } from "../routers/straight.ts";
|
|
|
87
94
|
import { byCollection, trunksRouter, type TrunksOptions } from "../routers/trunks.ts";
|
|
88
95
|
import { buildSections, SECTION_DEFAULTS, type SectionOptions } from "../sections.ts";
|
|
89
96
|
import { runPipeline, type PipelineOptions } from "../whole/pipeline/index.ts";
|
|
97
|
+
import { BOARD_PORTFOLIO_DEFAULTS, boardPortfolio, type BoardPortfolioOptions, type PortfolioSetting } from "../whole/portfolio/index.ts";
|
|
98
|
+
import { CURRICULUM_DEFAULTS, curriculum, type CurriculumOptions } from "../whole/curriculum/index.ts";
|
|
99
|
+
import { SLOT_DEFAULTS, slotPlan, type SlotOptions } from "../highways/slots/index.ts";
|
|
100
|
+
import { PREFERENCE_DEFAULTS, preferences, type PreferenceOptions } from "../whole/preferences/index.ts";
|
|
101
|
+
import { FIREFLY_DEFAULTS, fireflies, type FireflyOptions } from "../whole/fireflies/index.ts";
|
|
102
|
+
import { TRACING_DEFAULTS, tracing, type TracingOptions } from "../whole/tracing/index.ts";
|
|
90
103
|
import { createRegistry, defineStrategy, type AnyStrategy, type CurveInput, type LaneBoard, type Registry, type Strategy } from "./model.ts";
|
|
91
104
|
import type { OptionSchema } from "./options.ts";
|
|
92
105
|
import { AREA_SCHEMA, CONFLUENT_SCHEMA, CROSSBAR_SCHEMA, FRECHET_SCHEMA, MLSEB_SCHEMA, POWER_SCHEMA, SBEB_SCHEMA } from "./schemas.ts";
|
|
93
|
-
import { DEPTHS_SCHEMA, JUSTIFY_SCHEMA, PIPELINE_SCHEMA, SIDE_PLUG_SCHEMA, SPREAD_SCHEMA } from "./schemas.ts";
|
|
94
|
-
import { CLOTHOID_SCHEMA, CORNERS_SCHEMA, CORRIDOR_SCHEMA, CONSENSUS_SCHEMA, CUBU_SCHEMA, CUT_SCHEMA, DIFFUSION_SCHEMA, DIVIDED_SCHEMA, EDGE_PATH_SCHEMA, FDEB_SCHEMA, FLOW_CLUSTER_SCHEMA, FLOW_FIELD_SCHEMA, GROWTH_SCHEMA, HDBSCAN_SCHEMA, HIERARCHY_SCHEMA, HIGHWAY_SCHEMA, HOUGH_SCHEMA, HUB_ARC_SCHEMA, KD_SCHEMA, KDEEB_SCHEMA, KEY_SCHEMA, LANES_SCHEMA, LEVEL_HIGHWAY_SCHEMA, LEVEL_ROOM_SCHEMA, LINK_COMMUNITY_SCHEMA, MEET_SCHEMA, MESH_SCHEMA, MINGLE_SCHEMA, NESTED_SCHEMA, OD_CELL_SCHEMA, P_HUB_SCHEMA, QUICKBUNDLES_SCHEMA, RADIUS_SCHEMA, RESISTANCE_SCHEMA, RIDGE_SCHEMA, ROAD_CLASS_SCHEMA, ROOM_SCHEMA, SCALE_SPACE_SCHEMA, SECTION_SCHEMA, SELECT_SCHEMA, SECTIONS_SCHEMA, SHARED_ALL_SCHEMA, SHARED_SCHEMA, SMOOTH_SCHEMA, SOFT_KEY_SCHEMA, SPECTRAL_SCHEMA, SPIRAL_SCHEMA, STUB_SCHEMA, TENSION_SCHEMA, TERRITORY_SCHEMA, TRACLUS_SCHEMA, TRUNKS_SCHEMA, WEISZFELD_SCHEMA, WSPD_SCHEMA, ZIPPER_SCHEMA, STEINER_SCHEMA, GAP_SCHEMA, STREET_SCHEMA, SEARCH_SCHEMA, INTERCHANGE_SCHEMA } from "./schemas.ts";
|
|
106
|
+
import { DEPTHS_SCHEMA, JUSTIFY_SCHEMA, PIPELINE_SCHEMA, SIDE_PLUG_SCHEMA, SPREAD_SCHEMA, CLOTHOID_SCHEMA, CORNERS_SCHEMA, CORRIDOR_SCHEMA, CONSENSUS_SCHEMA, CUBU_SCHEMA, CUT_SCHEMA, DIFFUSION_SCHEMA, DIVIDED_SCHEMA, EDGE_PATH_SCHEMA, FDEB_SCHEMA, FLOW_CLUSTER_SCHEMA, FLOW_FIELD_SCHEMA, GROWTH_SCHEMA, HDBSCAN_SCHEMA, HIERARCHY_SCHEMA, HIGHWAY_SCHEMA, HOUGH_SCHEMA, HUB_ARC_SCHEMA, KD_SCHEMA, KDEEB_SCHEMA, KEY_SCHEMA, LANES_SCHEMA, LEVEL_HIGHWAY_SCHEMA, LEVEL_ROOM_SCHEMA, LINK_COMMUNITY_SCHEMA, MEET_SCHEMA, MESH_SCHEMA, MINGLE_SCHEMA, NESTED_SCHEMA, OD_CELL_SCHEMA, P_HUB_SCHEMA, QUICKBUNDLES_SCHEMA, RADIUS_SCHEMA, RESISTANCE_SCHEMA, RIDGE_SCHEMA, ROAD_CLASS_SCHEMA, ROOM_SCHEMA, SCALE_SPACE_SCHEMA, SECTION_SCHEMA, SELECT_SCHEMA, SECTIONS_SCHEMA, SHARED_ALL_SCHEMA, SHARED_SCHEMA, SMOOTH_SCHEMA, SOFT_KEY_SCHEMA, SPECTRAL_SCHEMA, SPIRAL_SCHEMA, STUB_SCHEMA, TENSION_SCHEMA, TERRITORY_SCHEMA, TRACLUS_SCHEMA, TRUNKS_SCHEMA, WEISZFELD_SCHEMA, WSPD_SCHEMA, ZIPPER_SCHEMA, STEINER_SCHEMA, GAP_SCHEMA, STREET_SCHEMA, SEARCH_SCHEMA, INTERCHANGE_SCHEMA, KNEE_SCHEMA, BRONCHIAL_SCHEMA, BOARD_PORTFOLIO_SCHEMA, VENATION_SCHEMA, BALANCE_SCHEMA, MANIFOLD_SCHEMA, LEADER_SCHEMA, DETAIL_SCHEMA, CURRICULUM_SCHEMA, SLOT_SCHEMA, PREFERENCE_SCHEMA, FIREFLY_SCHEMA, TRACING_SCHEMA } from "./schemas.ts";
|
|
95
107
|
|
|
96
108
|
/** The options of a router strategy: the options of its router, without the name and the limit of the benchmark. */
|
|
97
109
|
export type RouterPreset<O> = Omit<O, "name" | "maxNodes">;
|
|
@@ -475,6 +487,11 @@ export const highwayStrategies: {
|
|
|
475
487
|
readonly streets: Strategy<"highway", StreetOptions>;
|
|
476
488
|
readonly search: Strategy<"highway", SearchOptions>;
|
|
477
489
|
readonly interchanges: Strategy<"highway", InterchangeOptions>;
|
|
490
|
+
readonly knee: Strategy<"highway", KneeOptions>;
|
|
491
|
+
readonly bronchial: Strategy<"highway", BronchialOptions>;
|
|
492
|
+
readonly balance: Strategy<"highway", BalanceOptions>;
|
|
493
|
+
readonly manifolds: Strategy<"highway", ManifoldOptions>;
|
|
494
|
+
readonly slots: Strategy<"highway", SlotOptions>;
|
|
478
495
|
readonly "corridors+breakeven": Strategy<"highway", CorridorOptions & BreakEvenOptions>;
|
|
479
496
|
readonly "corridors+stretch": Strategy<"highway", CorridorOptions & StretchOptions>;
|
|
480
497
|
readonly "corridors+ambiguity": Strategy<"highway", CorridorOptions & AmbiguityGuardOptions>;
|
|
@@ -950,6 +967,51 @@ export const highwayStrategies: {
|
|
|
950
967
|
maxNodes: 10_000,
|
|
951
968
|
run: (input: HighwayInput, o: Readonly<InterchangeOptions>) => interchangePlan(input, o),
|
|
952
969
|
}),
|
|
970
|
+
knee: defineStrategy({
|
|
971
|
+
step: "highway",
|
|
972
|
+
name: "knee",
|
|
973
|
+
describe: "The knee of the plant front (NAT-018). A sweep of the balancing factor of NAT-017 gives trees, and the tree at the knee of ink against path length wins.",
|
|
974
|
+
defaults: { ...KNEE_DEFAULTS } as KneeOptions,
|
|
975
|
+
schema: KNEE_SCHEMA,
|
|
976
|
+
maxNodes: 10_000,
|
|
977
|
+
run: (input: HighwayInput, o: Readonly<KneeOptions>) => kneePlan(input, o),
|
|
978
|
+
}),
|
|
979
|
+
bronchial: defineStrategy({
|
|
980
|
+
step: "highway",
|
|
981
|
+
name: "bronchial",
|
|
982
|
+
describe: "Bronchial splits that fill the space (NAT-019). The tree grows toward the center of its targets, then splits the targets in two halves, again and again.",
|
|
983
|
+
defaults: { ...BRONCHIAL_DEFAULTS } as BronchialOptions,
|
|
984
|
+
schema: BRONCHIAL_SCHEMA,
|
|
985
|
+
maxNodes: 10_000,
|
|
986
|
+
run: (input: HighwayInput, o: Readonly<BronchialOptions>) => bronchialPlan(input, o),
|
|
987
|
+
}),
|
|
988
|
+
balance: defineStrategy({
|
|
989
|
+
step: "highway",
|
|
990
|
+
name: "balance",
|
|
991
|
+
describe: "Stretch balance in a bundle (EDA-017). A price for each edge grows while its stretch is above the target, and the price moves its exit forward.",
|
|
992
|
+
defaults: { ...BALANCE_DEFAULTS } as BalanceOptions,
|
|
993
|
+
schema: BALANCE_SCHEMA,
|
|
994
|
+
maxNodes: 10_000,
|
|
995
|
+
run: (input: HighwayInput, o: Readonly<BalanceOptions>) => balancePlan(input, o),
|
|
996
|
+
}),
|
|
997
|
+
manifolds: defineStrategy({
|
|
998
|
+
step: "highway",
|
|
999
|
+
name: "manifolds",
|
|
1000
|
+
describe: "Manifolds for a fan to a row (WILD-036). A straight header in the gap before the row, with taps at right angles and one feed from the node.",
|
|
1001
|
+
defaults: { ...MANIFOLD_DEFAULTS } as ManifoldOptions,
|
|
1002
|
+
schema: MANIFOLD_SCHEMA,
|
|
1003
|
+
maxNodes: 10_000,
|
|
1004
|
+
run: (input: HighwayInput, o: Readonly<ManifoldOptions>) => manifoldPlan(input, o),
|
|
1005
|
+
}),
|
|
1006
|
+
slots: defineStrategy({
|
|
1007
|
+
step: "highway",
|
|
1008
|
+
name: "slots",
|
|
1009
|
+
describe: "A pull toward the lane slots of the trunks (RULE-047). An edge near a trunk joins it on a new lane when the pull is larger than its detour.",
|
|
1010
|
+
defaults: { ...SLOT_DEFAULTS } as SlotOptions,
|
|
1011
|
+
schema: SLOT_SCHEMA,
|
|
1012
|
+
maxNodes: 10_000,
|
|
1013
|
+
run: (input: HighwayInput, o: Readonly<SlotOptions>) => slotPlan(input, o),
|
|
1014
|
+
}),
|
|
953
1015
|
"corridors+breakeven": gateStrategy(corridors, breakEvenGate),
|
|
954
1016
|
"corridors+stretch": gateStrategy(corridors, stretchGate),
|
|
955
1017
|
"corridors+ambiguity": gateStrategy(corridors, ambiguityGate),
|
|
@@ -963,6 +1025,7 @@ export const plugStrategies: {
|
|
|
963
1025
|
readonly side: Strategy<"plugs", SidePlugOptions>;
|
|
964
1026
|
readonly justify: Strategy<"plugs", JustifyOptions>;
|
|
965
1027
|
readonly spread: Strategy<"plugs", SpreadOptions>;
|
|
1028
|
+
readonly leaders: Strategy<"plugs", LeaderOptions>;
|
|
966
1029
|
} = Object.freeze({
|
|
967
1030
|
facing: defineStrategy({
|
|
968
1031
|
step: "plugs",
|
|
@@ -996,11 +1059,21 @@ export const plugStrategies: {
|
|
|
996
1059
|
schema: SPREAD_SCHEMA,
|
|
997
1060
|
run: (input, o) => spreadEnds(input, o),
|
|
998
1061
|
}),
|
|
1062
|
+
leaders: defineStrategy<"plugs", LeaderOptions>({
|
|
1063
|
+
step: "plugs",
|
|
1064
|
+
name: "leaders",
|
|
1065
|
+
describe: "Boundary leaders as plug stubs (EDA-041). The plugs of each wall take the order of po-leaders with no crossing, and keep a least gap.",
|
|
1066
|
+
defaults: { ...LEADER_DEFAULTS },
|
|
1067
|
+
schema: LEADER_SCHEMA,
|
|
1068
|
+
run: (input, o) => leaderEnds(input, o),
|
|
1069
|
+
}),
|
|
999
1070
|
});
|
|
1000
1071
|
|
|
1001
1072
|
/** The built-in level rules: the strategies of the step `levels`. The router `sections` makes its zoom levels with its option `level`. */
|
|
1002
1073
|
export const levelStrategies: {
|
|
1003
1074
|
readonly depths: Strategy<"levels", DepthLevelOptions>;
|
|
1075
|
+
readonly venation: Strategy<"levels", VenationOptions>;
|
|
1076
|
+
readonly detail: Strategy<"levels", DetailOptions>;
|
|
1004
1077
|
} = Object.freeze({
|
|
1005
1078
|
depths: defineStrategy<"levels", DepthLevelOptions>({
|
|
1006
1079
|
step: "levels",
|
|
@@ -1010,14 +1083,57 @@ export const levelStrategies: {
|
|
|
1010
1083
|
schema: DEPTHS_SCHEMA,
|
|
1011
1084
|
run: (input, o) => depthLevels(input, o),
|
|
1012
1085
|
}),
|
|
1086
|
+
venation: defineStrategy<"levels", VenationOptions>({
|
|
1087
|
+
step: "levels",
|
|
1088
|
+
name: "venation",
|
|
1089
|
+
describe: "A closed venation backbone (NAT-016). The links of each level with groups take their ways along the relative neighbourhood graph of the items.",
|
|
1090
|
+
defaults: { ...VENATION_DEFAULTS },
|
|
1091
|
+
schema: VENATION_SCHEMA,
|
|
1092
|
+
run: (input, o) => venationLevels(input, o),
|
|
1093
|
+
}),
|
|
1094
|
+
detail: defineStrategy<"levels", DetailOptions>({
|
|
1095
|
+
step: "levels",
|
|
1096
|
+
name: "detail",
|
|
1097
|
+
describe: "The level of detail of the bundles (WILD-016 with EDA-038). Legs of level links closer than a few pixels at the scale of their level become one stroke.",
|
|
1098
|
+
defaults: { ...DETAIL_DEFAULTS },
|
|
1099
|
+
schema: DETAIL_SCHEMA,
|
|
1100
|
+
run: (input, o) => detailLevels(input, o),
|
|
1101
|
+
}),
|
|
1013
1102
|
});
|
|
1014
1103
|
|
|
1104
|
+
/**
|
|
1105
|
+
* The fixed list of settings of the whole strategy `board-portfolio` (WILD-045), in order. The option `settings` takes the first ones.
|
|
1106
|
+
* The list holds routers and highway strategies with their defaults. It comes from the catalog of the strategies, not from a fit to the screen.
|
|
1107
|
+
*/
|
|
1108
|
+
export const BOARD_PORTFOLIO_SETTINGS: readonly PortfolioSetting[] = Object.freeze(
|
|
1109
|
+
[
|
|
1110
|
+
...(["shared", "corners", "trunks", "sections", "sections-tree", "corners-smooth"] as const).map((name) => ({ name, pipeline: { router: routerStrategies[name] as Strategy<"router"> } })),
|
|
1111
|
+
...(["corridors", "shared-ways", "road-classes", "steiner", "knee", "bronchial"] as const).map((name) => ({
|
|
1112
|
+
name: `highway:${name}`,
|
|
1113
|
+
pipeline: { router: routerStrategies.corners as Strategy<"router">, highway: highwayStrategies[name] as Strategy<"highway"> },
|
|
1114
|
+
})),
|
|
1115
|
+
].map((s) => Object.freeze(s)),
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* The pool of routes of the whole strategy `curriculum` (RULE-048), in order: the routers of the base of the screen. The first one gives the first answer.
|
|
1120
|
+
* The option `settings` takes the first ones.
|
|
1121
|
+
*/
|
|
1122
|
+
export const CURRICULUM_POOL: readonly PortfolioSetting[] = Object.freeze(
|
|
1123
|
+
(["corners", "shared", "trunks"] as const).map((name) => Object.freeze({ name, pipeline: { router: routerStrategies[name] as Strategy<"router"> } })),
|
|
1124
|
+
);
|
|
1125
|
+
|
|
1015
1126
|
/**
|
|
1016
1127
|
* The built-in whole strategies: the strategies of the step `whole`. The strategy `pipeline` runs one strategy of each step, as the demo of the site does.
|
|
1017
1128
|
* Without options, it routes the graph with the router `corners`.
|
|
1018
1129
|
*/
|
|
1019
1130
|
export const wholeStrategies: {
|
|
1020
1131
|
readonly pipeline: Strategy<"whole", PipelineOptions>;
|
|
1132
|
+
readonly "board-portfolio": Strategy<"whole", BoardPortfolioOptions>;
|
|
1133
|
+
readonly curriculum: Strategy<"whole", CurriculumOptions>;
|
|
1134
|
+
readonly preferences: Strategy<"whole", PreferenceOptions>;
|
|
1135
|
+
readonly fireflies: Strategy<"whole", FireflyOptions>;
|
|
1136
|
+
readonly tracing: Strategy<"whole", TracingOptions>;
|
|
1021
1137
|
} = Object.freeze({
|
|
1022
1138
|
pipeline: defineStrategy<"whole", PipelineOptions>({
|
|
1023
1139
|
step: "whole",
|
|
@@ -1027,6 +1143,46 @@ export const wholeStrategies: {
|
|
|
1027
1143
|
schema: PIPELINE_SCHEMA,
|
|
1028
1144
|
run: (graph, o) => runPipeline(graph, o),
|
|
1029
1145
|
}),
|
|
1146
|
+
"board-portfolio": defineStrategy<"whole", BoardPortfolioOptions>({
|
|
1147
|
+
step: "whole",
|
|
1148
|
+
name: "board-portfolio",
|
|
1149
|
+
describe: "A portfolio for each board (WILD-045). The pipeline tries a fixed list of settings, and the least weighted rank of crossings, detour and ink wins.",
|
|
1150
|
+
defaults: { ...BOARD_PORTFOLIO_DEFAULTS },
|
|
1151
|
+
schema: BOARD_PORTFOLIO_SCHEMA,
|
|
1152
|
+
run: (graph, o) => boardPortfolio(graph, BOARD_PORTFOLIO_SETTINGS, o).output,
|
|
1153
|
+
}),
|
|
1154
|
+
curriculum: defineStrategy<"whole", CurriculumOptions>({
|
|
1155
|
+
step: "whole",
|
|
1156
|
+
name: "curriculum",
|
|
1157
|
+
describe: "A curriculum of rules (RULE-048). Each stage adds one rule, and each edge takes the route of a pool that improves it with no loss on the earlier rules.",
|
|
1158
|
+
defaults: { ...CURRICULUM_DEFAULTS },
|
|
1159
|
+
schema: CURRICULUM_SCHEMA,
|
|
1160
|
+
run: (graph, o) => curriculum(graph, CURRICULUM_POOL, o),
|
|
1161
|
+
}),
|
|
1162
|
+
preferences: defineStrategy<"whole", PreferenceOptions>({
|
|
1163
|
+
step: "whole",
|
|
1164
|
+
name: "preferences",
|
|
1165
|
+
describe: "Rule weights from preferences (RULE-038). A fit to made-up pairs gives the weights, and each edge takes the route of a pool with the least weighted cost.",
|
|
1166
|
+
defaults: { ...PREFERENCE_DEFAULTS },
|
|
1167
|
+
schema: PREFERENCE_SCHEMA,
|
|
1168
|
+
run: (graph, o) => preferences(graph, CURRICULUM_POOL, o),
|
|
1169
|
+
}),
|
|
1170
|
+
fireflies: defineStrategy<"whole", FireflyOptions>({
|
|
1171
|
+
step: "whole",
|
|
1172
|
+
name: "fireflies",
|
|
1173
|
+
describe: "Firefly and bee searches (NAT-047). A seeded swarm searches the discount and the margin of `shared` on the board, within a budget of settings.",
|
|
1174
|
+
defaults: { ...FIREFLY_DEFAULTS },
|
|
1175
|
+
schema: FIREFLY_SCHEMA,
|
|
1176
|
+
run: (graph, o) => fireflies(graph, routerStrategies.shared as Strategy<"router">, o).output,
|
|
1177
|
+
}),
|
|
1178
|
+
tracing: defineStrategy<"whole", TracingOptions>({
|
|
1179
|
+
step: "whole",
|
|
1180
|
+
name: "tracing",
|
|
1181
|
+
describe: "The least tracing load (WILD-057). The pipeline tries a fixed list of settings, and the least load of crossings, merges and bends on one edge wins.",
|
|
1182
|
+
defaults: { ...TRACING_DEFAULTS },
|
|
1183
|
+
schema: TRACING_SCHEMA,
|
|
1184
|
+
run: (graph, o) => tracing(graph, BOARD_PORTFOLIO_SETTINGS, o).output,
|
|
1185
|
+
}),
|
|
1030
1186
|
});
|
|
1031
1187
|
|
|
1032
1188
|
/** The built-in strategies of each step, in order. A consumer adds its own: `createRegistry([...BUILT_IN_STRATEGIES, mine])`. */
|