@nshiab/simple-data-analysis-core 2.1.5 → 2.1.7

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 (57) hide show
  1. package/esm/class/SimpleTable.d.ts +254 -27
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +248 -27
  4. package/esm/helpers/graphRouteResultSchema.d.ts +1 -1
  5. package/esm/helpers/graphRouteResultSchema.d.ts.map +1 -1
  6. package/esm/helpers/graphRouteResultSchema.js +3 -2
  7. package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
  8. package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
  9. package/esm/helpers/graphRouteResultSelect.js +4 -1
  10. package/esm/helpers/prepareGraphMetricOptions.d.ts +14 -0
  11. package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  12. package/esm/helpers/prepareGraphMetricOptions.js +40 -0
  13. package/esm/helpers/prepareGraphTemporalSql.d.ts +3 -0
  14. package/esm/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  15. package/esm/helpers/prepareGraphTemporalSql.js +7 -0
  16. package/esm/methods/distances.d.ts +2 -0
  17. package/esm/methods/distances.d.ts.map +1 -1
  18. package/esm/methods/distances.js +98 -6
  19. package/esm/methods/findCycles.d.ts +1 -0
  20. package/esm/methods/findCycles.d.ts.map +1 -1
  21. package/esm/methods/findCycles.js +22 -15
  22. package/esm/methods/paths.d.ts +1 -0
  23. package/esm/methods/paths.d.ts.map +1 -1
  24. package/esm/methods/paths.js +21 -14
  25. package/esm/methods/shortestPath.d.ts +2 -0
  26. package/esm/methods/shortestPath.d.ts.map +1 -1
  27. package/esm/methods/shortestPath.js +125 -20
  28. package/llm.md +284 -58
  29. package/llms.txt +1 -1
  30. package/package.json +1 -1
  31. package/script/class/SimpleTable.d.ts +254 -27
  32. package/script/class/SimpleTable.d.ts.map +1 -1
  33. package/script/class/SimpleTable.js +248 -27
  34. package/script/helpers/graphRouteResultSchema.d.ts +1 -1
  35. package/script/helpers/graphRouteResultSchema.d.ts.map +1 -1
  36. package/script/helpers/graphRouteResultSchema.js +3 -2
  37. package/script/helpers/graphRouteResultSelect.d.ts +1 -1
  38. package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
  39. package/script/helpers/graphRouteResultSelect.js +4 -1
  40. package/script/helpers/prepareGraphMetricOptions.d.ts +14 -0
  41. package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  42. package/script/helpers/prepareGraphMetricOptions.js +43 -0
  43. package/script/helpers/prepareGraphTemporalSql.d.ts +3 -0
  44. package/script/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  45. package/script/helpers/prepareGraphTemporalSql.js +7 -0
  46. package/script/methods/distances.d.ts +2 -0
  47. package/script/methods/distances.d.ts.map +1 -1
  48. package/script/methods/distances.js +98 -6
  49. package/script/methods/findCycles.d.ts +1 -0
  50. package/script/methods/findCycles.d.ts.map +1 -1
  51. package/script/methods/findCycles.js +22 -15
  52. package/script/methods/paths.d.ts +1 -0
  53. package/script/methods/paths.d.ts.map +1 -1
  54. package/script/methods/paths.js +21 -14
  55. package/script/methods/shortestPath.d.ts +2 -0
  56. package/script/methods/shortestPath.d.ts.map +1 -1
  57. package/script/methods/shortestPath.js +125 -20
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = shortestPath;
40
40
  const buildGraphTemporalCostStateSql_js_1 = __importDefault(require("../helpers/buildGraphTemporalCostStateSql.js"));
41
+ const prepareGraphMetricOptions_js_1 = __importDefault(require("../helpers/prepareGraphMetricOptions.js"));
41
42
  const prepareGraphRouteSql_js_1 = require("../helpers/prepareGraphRouteSql.js");
42
43
  const graphRouteResultSchema_js_1 = __importDefault(require("../helpers/graphRouteResultSchema.js"));
43
44
  const graphRouteResultSelect_js_1 = __importDefault(require("../helpers/graphRouteResultSelect.js"));
@@ -70,6 +71,7 @@ function shortestPath(simpleTable, sourceColumn, targetColumn, edgeId, start, en
70
71
  typeof options.outputTable !== "boolean") {
71
72
  throw new TypeError("shortestPath() options.outputTable must be a string or boolean.");
72
73
  }
74
+ const metrics = (0, prepareGraphMetricOptions_js_1.default)(options, "shortestPath()", true);
73
75
  const endpoints = (0, prepareGraphRouteSql_js_1.prepareGraphRouteEndpoints)(start, end, "shortestPath()");
74
76
  const direction = options.direction ?? "outgoing";
75
77
  const temporalOptions = (0, prepareGraphTemporalSql_js_1.prepareGraphTemporalOptions)(options, direction, "shortestPath()");
@@ -98,22 +100,22 @@ function shortestPath(simpleTable, sourceColumn, targetColumn, edgeId, start, en
98
100
  ? endpoints.values.values
99
101
  : [temporal.gapParameter, ...endpoints.values.values];
100
102
  },
101
- buildSelect: (input, schema) => shortestPathSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions),
103
+ buildSelect: (input, schema) => shortestPathSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions, metrics),
102
104
  outputSchema: (schema) => {
103
105
  const validated = (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "shortestPath()");
104
106
  if (temporalOptions !== undefined) {
105
107
  (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
106
108
  }
107
- return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "shortestPath()");
109
+ return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "shortestPath()", metrics.elapsedTime);
108
110
  },
109
111
  });
110
112
  }
111
- function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions) {
113
+ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions, metrics) {
112
114
  const route = (0, prepareGraphRouteSql_js_1.prepareGraphRouteSql)(input, schema, source, target, edgeId, routeEndpoints, weight, "shortestPath()");
113
115
  const prepared = route.traversal;
114
116
  if (temporalOptions !== undefined) {
115
117
  const temporal = (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
116
- return temporalShortestPathSelect(route, direction, temporal);
118
+ return temporalShortestPathSelect(route, direction, temporal, metrics);
117
119
  }
118
120
  const distanceType = route.distanceType;
119
121
  // Exact shortest routes have optimal prefixes. Floating addition can erase
@@ -250,7 +252,7 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
250
252
  )
251
253
  ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
252
254
  }
253
- function temporalShortestPathSelect(route, direction, temporal) {
255
+ function temporalShortestPathSelect(route, direction, temporal, metrics) {
254
256
  const prepared = route.traversal;
255
257
  const q = quoteIdentifier_js_1.default;
256
258
  const startsSelect = `SELECT ${q("start")},
@@ -258,10 +260,12 @@ function temporalShortestPathSelect(route, direction, temporal) {
258
260
  FROM (SELECT ${route.endpointValue} AS ${q("start")}) AS ${q("values")}`;
259
261
  // Non-negative weights and no maximum gap let cycles be removed from walks.
260
262
  // Their optimum bounds simple routes; retain separate histories for all ties.
261
- const costs = (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, route.distanceType, route.edgeWeight, [
262
- `${route.typedEdgeId} AS ${q("__edge_id")}`,
263
- `${route.edgeKey} AS ${q("__edge_key")}`,
264
- ]);
263
+ const costs = metrics.minimize === "elapsedTime"
264
+ ? buildElapsedShortestPathCostSql(route, startsSelect, direction, temporal)
265
+ : (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, route.distanceType, route.edgeWeight, [
266
+ `${route.typedEdgeId} AS ${q("__edge_id")}`,
267
+ `${route.edgeKey} AS ${q("__edge_key")}`,
268
+ ]);
265
269
  const relations = prepared.relationNames([
266
270
  "graph_route_endpoints",
267
271
  "graph_nodes",
@@ -287,12 +291,27 @@ function temporalShortestPathSelect(route, direction, temporal) {
287
291
  const routeDistance = `${q("routes")}.${q("distance")}`;
288
292
  const candidateDistance = `CAST(${routeDistance} + ${edgeCost} AS ${route.distanceType})`;
289
293
  const gap = `${q("settings")}.${q("__gap")}`;
290
- const step = (distance) => `struct_pack(
294
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
295
+ const routeAnchor = `${q("routes")}.${q("__journey_anchor")}`;
296
+ const firstElapsed = temporal.journeyElapsed("edges", firstAnchor, direction);
297
+ const candidateElapsed = temporal.journeyElapsed("edges", routeAnchor, direction);
298
+ const firstMetric = metrics.minimize === "elapsedTime"
299
+ ? firstElapsed
300
+ : edgeCost;
301
+ const candidateMetric = metrics.minimize === "elapsedTime"
302
+ ? candidateElapsed
303
+ : candidateDistance;
304
+ const routeMetric = metrics.minimize === "elapsedTime"
305
+ ? `${q("routes")}.${q("__elapsed")}`
306
+ : routeDistance;
307
+ const step = (distance, elapsed) => `struct_pack(
291
308
  ${q("edgeId")} := ${q("edges")}.${q("__edge_id")},
292
309
  ${q("source")} := ${q("edges")}.${q("__from")},
293
310
  ${q("target")} := ${edgeTo},
294
311
  ${q("weight")} := ${edgeCost},
295
- ${q("distance")} := ${distance}
312
+ ${q("distance")} := ${distance}${metrics.elapsedTime
313
+ ? `, ${q("elapsedTimeMs")} := ${temporal.elapsedMilliseconds(elapsed)}`
314
+ : ""}
296
315
  )`;
297
316
  return `${costs.withClause}, ${endpointRelation}(
298
317
  ${q("start")}, ${q("end")}, ${q("__start_key")}, ${q("__end_key")}
@@ -324,37 +343,37 @@ function temporalShortestPathSelect(route, direction, temporal) {
324
343
  ), ${routesRelation}(
325
344
  ${q("node")}, ${q("__node_key")}, ${q("__visited")},
326
345
  ${q("__edge_keys")}, ${q("steps")}, ${q("distance")},
327
- ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}
346
+ ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}${metrics.elapsedTime ? `, ${q("__journey_anchor")}, ${q("__elapsed")}` : ""}
328
347
  ) AS (
329
348
  SELECT ${edgeTo}, ${edgeToKey},
330
349
  [${q("__start_key")}, ${edgeToKey}], [${edgeKey}],
331
- [${step(edgeCost)}], ${edgeCost},
350
+ [${step(edgeCost, firstElapsed)}], ${edgeCost},
332
351
  ${q("edges")}.${q("__event_id")},
333
352
  ${q("edges")}.${q("__event_start")},
334
- ${q("edges")}.${q("__event_end")}
353
+ ${q("edges")}.${q("__event_end")}${metrics.elapsedTime ? `, ${firstAnchor}, ${firstElapsed}` : ""}
335
354
  FROM ${endpointRelation}
336
355
  INNER JOIN ${edgesRelation} AS ${q("edges")}
337
356
  ON ${q("__start_key")} = ${edgeFromKey}
338
357
  INNER JOIN ${toEndRelation} AS ${q("can_reach_end")}
339
358
  ON ${edgeToKey} = ${q("can_reach_end")}.${q("__key")}
340
359
  INNER JOIN ${bestTotalRelation} AS ${q("best_total")}
341
- ON ${edgeCost} <= ${q("best_total")}.${q("distance")}
360
+ ON ${firstMetric} <= ${q("best_total")}.${q("distance")}
342
361
  WHERE ${q("__start_key")} <> ${edgeToKey}
343
362
  UNION ALL
344
363
  SELECT ${edgeTo}, ${edgeToKey},
345
364
  list_append(${q("routes")}.${q("__visited")}, ${edgeToKey}),
346
365
  list_append(${q("routes")}.${q("__edge_keys")}, ${edgeKey}),
347
- list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance)}),
366
+ list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance, candidateElapsed)}),
348
367
  ${candidateDistance}, ${q("edges")}.${q("__event_id")},
349
368
  ${q("edges")}.${q("__event_start")},
350
- ${q("edges")}.${q("__event_end")}
369
+ ${q("edges")}.${q("__event_end")}${metrics.elapsedTime ? `, ${routeAnchor}, ${candidateElapsed}` : ""}
351
370
  FROM ${routesRelation} AS ${q("routes")}
352
371
  INNER JOIN ${edgesRelation} AS ${q("edges")}
353
372
  ON ${q("routes")}.${q("__node_key")} = ${edgeFromKey}
354
373
  INNER JOIN ${toEndRelation} AS ${q("can_reach_end")}
355
374
  ON ${edgeToKey} = ${q("can_reach_end")}.${q("__key")}
356
375
  INNER JOIN ${bestTotalRelation} AS ${q("best_total")}
357
- ON ${candidateDistance} <= ${q("best_total")}.${q("distance")}
376
+ ON ${candidateMetric} <= ${q("best_total")}.${q("distance")}
358
377
  CROSS JOIN ${endpointRelation}
359
378
  CROSS JOIN ${costs.settingsRelation} AS ${q("settings")}
360
379
  WHERE ${q("routes")}.${q("__node_key")} <> ${q("__end_key")}
@@ -366,11 +385,97 @@ function temporalShortestPathSelect(route, direction, temporal) {
366
385
  CROSS JOIN ${endpointRelation}
367
386
  CROSS JOIN ${bestTotalRelation} AS ${q("best_total")}
368
387
  WHERE ${q("__node_key")} = ${q("__end_key")}
369
- AND ${routeDistance} = ${q("best_total")}.${q("distance")}
388
+ AND ${routeMetric} = ${q("best_total")}.${q("distance")}
370
389
  ), ${rankedRelation} AS (
371
390
  SELECT CAST(row_number() OVER (ORDER BY ${q("__edge_keys")}) - 1 AS BIGINT)
372
391
  AS ${q("pathId")}, ${q("steps")}
373
392
  FROM ${shortestRelation}
374
393
  )
375
- ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
394
+ ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn, false, metrics.elapsedTime)}`;
395
+ }
396
+ // At the same physical event a later departure (or earlier incoming arrival)
397
+ // dominates every future elapsed duration. Keyed improvements find the optimum
398
+ // without enumerating route histories; reconstruction still retains all ties.
399
+ function buildElapsedShortestPathCostSql(route, startsSelect, direction, temporal) {
400
+ const prepared = route.traversal;
401
+ const q = quoteIdentifier_js_1.default;
402
+ const relations = prepared.relationNames([
403
+ "graph_starts",
404
+ "graph_temporal_settings",
405
+ "graph_event_rows",
406
+ "graph_edges",
407
+ "graph_cost_states",
408
+ ]);
409
+ const startsRelation = relations.graph_starts;
410
+ const settingsRelation = relations.graph_temporal_settings;
411
+ const eventRowsRelation = relations.graph_event_rows;
412
+ const edgesRelation = relations.graph_edges;
413
+ const costRelation = relations.graph_cost_states;
414
+ const start = `${q("starts")}.${q("start")}`;
415
+ const startKey = `${q("starts")}.${q("__key")}`;
416
+ const reachedStart = `${q("reached")}.${q("start")}`;
417
+ const reachedStartKey = `${q("reached")}.${q("__start_key")}`;
418
+ const reachedNodeKey = `${q("reached")}.${q("__node_key")}`;
419
+ const edgeTo = `${q("edges")}.${q("__to")}`;
420
+ const edgeToKey = `${q("edges")}.${q("__to_key")}`;
421
+ const edgeFromKey = `${q("edges")}.${q("__from_key")}`;
422
+ const eventId = `${q("edges")}.${q("__event_id")}`;
423
+ const eventStart = `${q("edges")}.${q("__event_start")}`;
424
+ const eventEnd = `${q("edges")}.${q("__event_end")}`;
425
+ const bestDistance = `${q("best")}.${q("distance")}`;
426
+ const gap = `${q("settings")}.${q("__gap")}`;
427
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
428
+ const reachedAnchor = `${q("reached")}.${q("__journey_anchor")}`;
429
+ const firstElapsed = temporal.journeyElapsed("edges", firstAnchor, direction);
430
+ const candidateDistance = temporal.journeyElapsed("edges", reachedAnchor, direction);
431
+ const bestAnchor = `${direction === "outgoing" ? "MAX" : "MIN"}(${reachedAnchor})`;
432
+ return {
433
+ costRelation,
434
+ edgesRelation,
435
+ settingsRelation,
436
+ startsRelation,
437
+ withClause: `WITH RECURSIVE ${settingsRelation} AS MATERIALIZED (
438
+ SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
439
+ ), ${eventRowsRelation} AS MATERIALIZED (
440
+ ${prepared.edges(direction, [
441
+ `${route.edgeWeight} AS ${q("__weight")}`,
442
+ `${route.typedEdgeId} AS ${q("__edge_id")}`,
443
+ `${route.edgeKey} AS ${q("__edge_key")}`,
444
+ ...temporal.eventSelections("edges"),
445
+ ])}
446
+ ), ${edgesRelation} AS MATERIALIZED (
447
+ SELECT *
448
+ FROM ${eventRowsRelation} AS ${q("events")}
449
+ WHERE ${temporal.eventValidity("events")}
450
+ ), ${startsRelation} AS MATERIALIZED (
451
+ ${startsSelect}
452
+ ), ${costRelation}(
453
+ ${q("start")}, ${q("node")},
454
+ ${q("__start_key")}, ${q("__node_key")},
455
+ ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")},
456
+ ${q("distance")}, ${q("__journey_anchor")}
457
+ ) USING KEY(${q("__start_key")}, ${q("__event_id")}) AS (
458
+ SELECT ${start}, ${edgeTo}, ${startKey}, ${edgeToKey},
459
+ ${eventId}, ${eventStart}, ${eventEnd}, ${firstElapsed}, ${firstAnchor}
460
+ FROM ${startsRelation} AS ${q("starts")}
461
+ INNER JOIN ${edgesRelation} AS ${q("edges")}
462
+ ON ${startKey} = ${edgeFromKey}
463
+ UNION
464
+ SELECT ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
465
+ ${eventId}, ${eventStart}, ${eventEnd},
466
+ MIN(${candidateDistance}) AS ${q("distance")}, ${bestAnchor}
467
+ FROM ${costRelation} AS ${q("reached")}
468
+ INNER JOIN ${edgesRelation} AS ${q("edges")}
469
+ ON ${reachedNodeKey} = ${edgeFromKey}
470
+ CROSS JOIN ${settingsRelation} AS ${q("settings")}
471
+ LEFT JOIN recurring.${costRelation} AS ${q("best")}
472
+ ON ${reachedStartKey} = ${q("best")}.${q("__start_key")}
473
+ AND ${eventId} = ${q("best")}.${q("__event_id")}
474
+ WHERE ${temporal.transition("reached", "edges", direction, gap)}
475
+ GROUP BY ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
476
+ ${eventId}, ${eventStart}, ${eventEnd}, ${bestDistance}
477
+ HAVING ${bestDistance} IS NULL OR
478
+ MIN(${candidateDistance}) < ${bestDistance}
479
+ )`,
480
+ };
376
481
  }