@mark1russell7/highways 0.5.0 → 0.5.1

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.
Files changed (56) hide show
  1. package/README.md +43 -2
  2. package/build/index.d.ts +1472 -63
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js +10007 -3406
  5. package/build/index.js.map +1 -1
  6. package/package.json +11 -2
  7. package/src/bench/exact/index.ts +72 -11
  8. package/src/bench/exact/optima-exact-v1-bounds.json +14409 -0
  9. package/src/bench/exact/optima-exact-v1.json +4809 -0
  10. package/src/bench/exact/solve.ts +129 -0
  11. package/src/bench/screen/activation.ts +3 -0
  12. package/src/bench/screen/bounds.ts +140 -0
  13. package/src/bench/screen/compare.ts +26 -0
  14. package/src/bench/screen/exact-set.ts +100 -0
  15. package/src/bench/screen/exercise.ts +290 -0
  16. package/src/bench/screen/forms.ts +14 -0
  17. package/src/bench/screen/gap.ts +316 -0
  18. package/src/bench/screen/hash.ts +7 -76
  19. package/src/bench/screen/index.ts +6 -0
  20. package/src/bench/screen/measures.ts +71 -1
  21. package/src/bench/screen/portfolio-pick.ts +20 -0
  22. package/src/bench/screen/screen.ts +50 -2
  23. package/src/bench/screen/set.ts +3 -2
  24. package/src/highways/areas/index.ts +160 -0
  25. package/src/highways/confluent/index.ts +154 -0
  26. package/src/highways/confluent/k23.test-util.ts +8 -0
  27. package/src/highways/consensus/index.ts +129 -0
  28. package/src/highways/crossbars/index.ts +184 -0
  29. package/src/highways/cubu/index.ts +26 -0
  30. package/src/highways/divided/index.ts +111 -0
  31. package/src/highways/fdeb/index.ts +193 -0
  32. package/src/highways/frechet/index.ts +232 -0
  33. package/src/highways/hierarchy/index.ts +140 -0
  34. package/src/highways/index.ts +64 -0
  35. package/src/highways/kdeeb/index.ts +355 -0
  36. package/src/highways/link-communities/index.ts +189 -0
  37. package/src/highways/mesh/index.ts +209 -0
  38. package/src/highways/mlseb/index.ts +204 -0
  39. package/src/highways/p-hub/index.ts +248 -0
  40. package/src/highways/power/index.ts +117 -0
  41. package/src/highways/resistance/index.ts +159 -0
  42. package/src/highways/sbeb/index.ts +303 -0
  43. package/src/highways/scale-space/index.ts +135 -0
  44. package/src/highways/spectral/index.ts +391 -0
  45. package/src/highways/territories/index.ts +150 -0
  46. package/src/highways/trace.ts +153 -0
  47. package/src/index.ts +1 -0
  48. package/src/portfolio/bands.ts +33 -0
  49. package/src/portfolio/fit.ts +29 -0
  50. package/src/portfolio/index.ts +6 -0
  51. package/src/portfolio/key.ts +98 -0
  52. package/src/portfolio/members.ts +171 -0
  53. package/src/portfolio/router.ts +133 -0
  54. package/src/portfolio/sets.ts +28 -0
  55. package/src/strategies/builtins.ts +222 -1
  56. package/src/strategies/schemas.ts +280 -1
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "directory": "packages/core"
10
10
  },
11
11
  "homepage": "https://mark1russell7.github.io/highways/",
12
- "version": "0.5.0",
12
+ "version": "0.5.1",
13
13
  "type": "module",
14
14
  "main": "./build/index.js",
15
15
  "types": "./build/index.d.ts",
@@ -28,9 +28,11 @@
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  },
31
+ "dependencies": {
32
+ "@mark1russell7/layout-contract": "^0.2.6"
33
+ },
31
34
  "devDependencies": {
32
35
  "@mark1russell7/cue": "github:mark1russell7/cue",
33
- "@mark1russell7/layout-contract": "github:mark1russell7/layout-contract#69933a61b0270c5915b82ab563d24908ade708af&path:/packages/layout-contract",
34
36
  "@types/node": "^26.6.4",
35
37
  "highs": "^1.15.4",
36
38
  "typescript": "^7.0.2",
@@ -45,6 +47,13 @@
45
47
  "profile": "node scripts/profile.ts",
46
48
  "levels": "node scripts/levels.ts",
47
49
  "screen": "node scripts/screen.ts",
50
+ "exact": "node scripts/exact.ts",
51
+ "oracle": "node scripts/oracle.ts",
52
+ "portfolio": "node scripts/portfolio.ts",
53
+ "portfolio:time": "node scripts/portfolio-time.ts",
54
+ "portfolio:sets": "node scripts/portfolio-sets.ts",
55
+ "portfolio:api": "node scripts/portfolio-api-report.ts",
56
+ "bounds": "node scripts/bounds.ts",
48
57
  "screen:inputs": "node scripts/screen-inputs.ts",
49
58
  "test:watch": "vitest",
50
59
  "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.test.json"
@@ -1,7 +1,7 @@
1
- import { dist, type Point } from "../../geom.ts";
1
+ import { dist, polylineLength, type Point } from "../../geom.ts";
2
2
  import { edgePlugs, sharedStretches, type KeyedWay } from "../../highways/stretches.ts";
3
3
  import type { HighwayPlan } from "../../highways/model.ts";
4
- import type { Graph } from "../../model.ts";
4
+ import type { Edge, Graph, Route } from "../../model.ts";
5
5
  import { byCollection } from "../../routers/trunks.ts";
6
6
  import { WayFinder, type WayOptions } from "../../ways.ts";
7
7
 
@@ -28,6 +28,8 @@ export interface ExactModel {
28
28
  readonly rows: number;
29
29
  /** The number of edges in the model. An edge without a way in the stretch limit is not in the model. */
30
30
  readonly edges: number;
31
+ /** The length of the arc or the link of each binary variable, by its name. A name `x…` is an arc of an edge, and a name `y…` is a link of a key. */
32
+ readonly lengths: ReadonlyMap<string, number>;
31
33
  /** This function gives the plan of a solution. `value` gives the value of a variable by its name. */
32
34
  decode(value: (name: string) => number): HighwayPlan;
33
35
  }
@@ -54,13 +56,19 @@ function distances(adj: readonly (readonly { to: number; len: number }[])[], sta
54
56
  }
55
57
  }
56
58
 
57
- /**
58
- * This function gives the model of fixed-charge network design (Magnanti and Wong 1984) for the edges of a graph (HWY-040).
59
- * The network is the corner graph, plus the point out of each plug. Each link costs its length once for each key that uses it.
60
- * Each edge goes one way between the points out of its two plugs. The way is `stretch` times its shortest way or shorter.
61
- * The optimum has the least ink for the stretch limit. The ways of the solution give highways with `sharedStretches`.
62
- */
63
- export function exactModel(graph: Graph, o: Readonly<ExactOptions>): ExactModel {
59
+ /** The network of the exact model: the corner graph, plus the point out of each plug of each edge. */
60
+ interface ExactNetwork {
61
+ readonly points: readonly Point[];
62
+ readonly links: readonly { readonly u: number; readonly v: number; readonly len: number }[];
63
+ readonly adj: readonly (readonly { to: number; len: number }[])[];
64
+ /** The two end points of each edge: the points out of its plugs. */
65
+ readonly ends: readonly { readonly edge: Edge; readonly a: number; readonly b: number }[];
66
+ /** The distance from a plug to the point out of it. */
67
+ readonly margin: number;
68
+ }
69
+
70
+ /** This function makes the network of the exact model for the edges of a graph. */
71
+ function exactNetwork(graph: Graph, o: Readonly<WayOptions>): ExactNetwork {
64
72
  const finder = new WayFinder(graph.nodes, { ...(o.margin === undefined ? {} : { margin: o.margin }), ...(o.k === undefined ? {} : { k: o.k }) });
65
73
  const corners = finder.corners;
66
74
  const k = o.k ?? 8;
@@ -79,7 +87,7 @@ export function exactModel(graph: Graph, o: Readonly<ExactOptions>): ExactModel
79
87
  };
80
88
  corners.points.forEach((p, i) => ids.set(`${p.x},${p.y}`, i));
81
89
  const plugs = edgePlugs(graph);
82
- const ends = plugs.map(({ edge, ps, pt }) => ({ edge: edge.id, key: o.rule === "all" ? "*" : (byCollection(edge) ?? `\u0000${edge.id}`), a: nodeOf(finder.out(ps)), b: nodeOf(finder.out(pt)) }));
90
+ const ends = plugs.map(({ edge, ps, pt }) => ({ edge, a: nodeOf(finder.out(ps)), b: nodeOf(finder.out(pt)) }));
83
91
 
84
92
  // The links: each corner to its k nearest visible corners, as in the corner graph, and each end point to its 2k nearest visible corners.
85
93
  const linkIds = new Map<string, number>();
@@ -99,12 +107,25 @@ export function exactModel(graph: Graph, o: Readonly<ExactOptions>): ExactModel
99
107
  adj[l.u]!.push({ to: l.v, len: l.len });
100
108
  adj[l.v]!.push({ to: l.u, len: l.len });
101
109
  }
110
+ return { points, links, adj, ends, margin: finder.margin };
111
+ }
112
+
113
+ /**
114
+ * This function gives the model of fixed-charge network design (Magnanti and Wong 1984) for the edges of a graph (HWY-040).
115
+ * The network is the corner graph, plus the point out of each plug. Each link costs its length once for each key that uses it.
116
+ * Each edge goes one way between the points out of its two plugs. The way is `stretch` times its shortest way or shorter.
117
+ * The optimum has the least ink for the stretch limit. The ways of the solution give highways with `sharedStretches`.
118
+ */
119
+ export function exactModel(graph: Graph, o: Readonly<ExactOptions>): ExactModel {
120
+ const { points, links, adj, ends: outs } = exactNetwork(graph, o);
121
+ const ends = outs.map((e) => ({ edge: e.edge.id, key: o.rule === "all" ? "*" : (byCollection(e.edge) ?? `\u0000${e.edge.id}`), a: e.a, b: e.b }));
102
122
 
103
123
  // The arcs of each edge: the arcs on some way within the stretch limit.
104
124
  const eps = o.epsilon ?? 0.001;
105
125
  const objective: string[] = [];
106
126
  const rows: string[] = [];
107
127
  const binaries: string[] = [];
128
+ const lengths = new Map<string, number>();
108
129
  const keys = [...new Set(ends.map((e) => e.key))];
109
130
  const used = new Map<string, Set<number>>();
110
131
  const arcsOf: { e: number; arcs: Arc[] }[] = [];
@@ -134,6 +155,7 @@ export function exactModel(graph: Graph, o: Readonly<ExactOptions>): ExactModel
134
155
  for (const a of arcs) {
135
156
  const x = `x${ei}_${a.link}_${a.from < a.to ? 0 : 1}`;
136
157
  binaries.push(x);
158
+ lengths.set(x, a.len);
137
159
  objective.push(`${(eps * a.len).toFixed(6)} ${x}`);
138
160
  term(a.from, `+ ${x}`);
139
161
  term(a.to, `- ${x}`);
@@ -156,6 +178,7 @@ export function exactModel(graph: Graph, o: Readonly<ExactOptions>): ExactModel
156
178
  for (const li of used.get(key) ?? []) {
157
179
  const y = `y${ki}_${li}`;
158
180
  binaries.push(y);
181
+ lengths.set(y, links[li]!.len);
159
182
  objective.push(`${links[li]!.len.toFixed(6)} ${y}`);
160
183
  }
161
184
  });
@@ -193,5 +216,43 @@ export function exactModel(graph: Graph, o: Readonly<ExactOptions>): ExactModel
193
216
  }
194
217
  return sharedStretches(ways, { minSize: 2 });
195
218
  };
196
- return { lp, columns: binaries.length, rows: rows.length, edges: arcsOf.length, decode };
219
+ return { lp, columns: binaries.length, rows: rows.length, edges: arcsOf.length, lengths, decode };
220
+ }
221
+
222
+ /**
223
+ * This function gives the stretch of each route as the exact model counts it, by the edge id.
224
+ * The stretch of a route is its length without its two plug ends, divided by the shortest way in the network of the model.
225
+ * The function builds the network one time. The result function then measures each list of routes of the graph, so a portfolio pays for the network once.
226
+ * An edge without a route or without a way in the network has no value.
227
+ */
228
+ export function exactStretches(graph: Graph, o: Readonly<WayOptions> = {}): (routes: readonly Route[]) => Map<string, number> {
229
+ const { adj, ends, margin } = exactNetwork(graph, o);
230
+ const best = new Map<number, Float64Array>();
231
+ const from = (a: number): Float64Array => {
232
+ if (!best.has(a)) best.set(a, distances(adj, a));
233
+ return best.get(a)!;
234
+ };
235
+ return (routes) => {
236
+ const byEdge = new Map(routes.map((r) => [r.edge, r]));
237
+ const out = new Map<string, number>();
238
+ for (const e of ends) {
239
+ const r = byEdge.get(e.edge.id);
240
+ if (!r || r.points.length < 2 || e.a === e.b) continue;
241
+ const b = from(e.a)[e.b]!;
242
+ if (!Number.isFinite(b) || b <= 1) continue;
243
+ out.set(e.edge.id, (polylineLength(r.points) - 2 * margin) / b);
244
+ }
245
+ return out;
246
+ };
247
+ }
248
+
249
+ /**
250
+ * This function gives the largest stretch of routes as the exact model counts it (`exactStretches`).
251
+ * The cap of the model is a limit on this value.
252
+ * An edge without a route or without a way in the network does not count. Without such an edge, the value is 1.
253
+ */
254
+ export function exactStretch(graph: Graph, routes: readonly Route[], o: Readonly<WayOptions> = {}): number {
255
+ let max = 1;
256
+ for (const v of exactStretches(graph, o)(routes).values()) max = Math.max(max, v);
257
+ return max;
197
258
  }