diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a38a470 --- /dev/null +++ b/.gitignore @@ -0,0 +1,483 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +tools/ +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +#*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp +.azure +/.idea \ No newline at end of file diff --git a/POC.pyproj b/POC.pyproj new file mode 100644 index 0000000..b3a18ef --- /dev/null +++ b/POC.pyproj @@ -0,0 +1,25 @@ + + + + Debug + 2.0 + {e10619c7-7881-4ab5-ba2a-0968e3e2e09e} + + poc.py + + . + . + {888888a0-9f3d-457c-b088-3a5042f75d52} + Standard Python launcher + + + + + + 10.0 + + + + + + \ No newline at end of file diff --git a/POC.sln b/POC.sln new file mode 100644 index 0000000..21a7acd --- /dev/null +++ b/POC.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35728.132 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "POC", "POC.pyproj", "{E10619C7-7881-4AB5-BA2A-0968E3E2E09E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "POCDotnet", "POCDotnet\POCDotnet.csproj", "{177848A3-D46D-4302-9C2E-03E2E15A573B}" +EndProject +Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "POCNodeTypescript", "POCNodeTypescript\POCNodeTypescript.esproj", "{B84BF435-A0BA-4A96-BBDD-74F66733F96F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E10619C7-7881-4AB5-BA2A-0968E3E2E09E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E10619C7-7881-4AB5-BA2A-0968E3E2E09E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {177848A3-D46D-4302-9C2E-03E2E15A573B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {177848A3-D46D-4302-9C2E-03E2E15A573B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {177848A3-D46D-4302-9C2E-03E2E15A573B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {177848A3-D46D-4302-9C2E-03E2E15A573B}.Release|Any CPU.Build.0 = Release|Any CPU + {B84BF435-A0BA-4A96-BBDD-74F66733F96F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B84BF435-A0BA-4A96-BBDD-74F66733F96F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B84BF435-A0BA-4A96-BBDD-74F66733F96F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {B84BF435-A0BA-4A96-BBDD-74F66733F96F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B84BF435-A0BA-4A96-BBDD-74F66733F96F}.Release|Any CPU.Build.0 = Release|Any CPU + {B84BF435-A0BA-4A96-BBDD-74F66733F96F}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/POCCPlus/CMakeLists.txt b/POCCPlus/CMakeLists.txt new file mode 100644 index 0000000..85e2b04 --- /dev/null +++ b/POCCPlus/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.16) +project(BMSSP LANGUAGES CXX) + +# Require C++17 (needed for structured bindings, etc.) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Enable warnings +if (MSVC) + add_compile_options(/W4 /permissive-) +else() + add_compile_options(-Wall -Wextra -pedantic) +endif() + +# Main executable +add_executable(bmssp + bmssp.cpp +) + +# If you want timing/benchmark builds, enable optimization flags +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() diff --git a/POCCPlus/bmssp.cpp b/POCCPlus/bmssp.cpp new file mode 100644 index 0000000..39aa3c9 --- /dev/null +++ b/POCCPlus/bmssp.cpp @@ -0,0 +1,550 @@ +// bmssp_full_impl.cpp +// C++17 single-file port of the Python BMSSP practical implementation. +// Includes: graph generator, Dijkstra, DataStructureD, FIND_PIVOTS, BASECASE, BMSSP recursion, instrumentation, and test harness. + +#include +using namespace std; + +using Node = int; +using Weight = double; +using Edge = tuple; +using AdjList = vector>>; + +static inline double INF_D() { return numeric_limits::infinity(); } + +// --------------------------- +// Instrumentation +// --------------------------- +struct Instrument { + uint64_t relaxations = 0; + uint64_t heap_ops = 0; + void reset() { relaxations = heap_ops = 0; } +}; + +// --------------------------- +// Utilities & Graph generator +// --------------------------- +pair> generate_sparse_directed_graph(int n, int m, double max_w = 100.0, optional seed = nullopt) { + std::mt19937_64 rng; + if (seed.has_value()) rng.seed(seed.value()); + else rng.seed(std::random_device{}()); + + uniform_real_distribution wdist(1.0, max_w); + uniform_real_distribution unit(0.0, 1.0); + + AdjList graph(n); + vector edges; + // weak backbone + for (int i = 1; i < n; ++i) { + uniform_int_distribution r(0, i - 1); + int u = r(rng); + double w = wdist(rng); + graph[u].emplace_back(i, w); + edges.emplace_back(u, i, w); + } + int remaining = max(0, m - (n - 1)); + uniform_int_distribution rn(0, max(0, n - 1)); + for (int i = 0; i < remaining; ++i) { + int u = rn(rng); + int v = rn(rng); + double w = wdist(rng); + graph[u].emplace_back(v, w); + edges.emplace_back(u, v, w); + } + return {graph, edges}; +} + +// --------------------------- +// Dijkstra (standard) +// --------------------------- +unordered_map dijkstra(const AdjList &graph, Node source, Instrument *instr = nullptr) { + if (!instr) instr = new Instrument(); // if nullptr, avoid using but create temp to keep code simple + unordered_map dist; + int n = (int)graph.size(); + dist.reserve(n * 2); + for (int i = 0; i < n; ++i) dist[i] = INF_D(); + dist[source] = 0.0; + + // min-heap: pair + priority_queue, vector>, greater>> pq; + pq.emplace(0.0, source); + instr->heap_ops += 1; + + while (!pq.empty()) { + auto [d_u, u] = pq.top(); pq.pop(); + instr->heap_ops += 1; + if (d_u > dist[u]) continue; + for (auto &e : graph[u]) { + Node v = e.first; + Weight w = e.second; + instr->relaxations += 1; + double alt = d_u + w; + auto it = dist.find(v); + double cur = (it != dist.end() ? it->second : INF_D()); + if (alt < cur) { + dist[v] = alt; + pq.emplace(alt, v); + instr->heap_ops += 1; + } + } + } + return dist; +} + +// --------------------------- +// DataStructure D (practical) +// --------------------------- +class DataStructureD { +public: + DataStructureD(int M, double B_upper, optional block_size = nullopt) + : M_(max(1, M)), B_upper_(B_upper) { + block_size_ = block_size.has_value() ? block_size.value() : max(1, M_ / 8); + } + + void insert(Node v, Weight key) { + auto it = best_.find(v); + if (it == best_.end() || key < it->second) { + best_[v] = key; + heap_.emplace(key, v); + } + } + + // iterable of (v, key) + void batch_prepend(const vector>& items) { + for (const auto &p : items) insert(p.first, p.second); + } + + bool empty() { + cleanup(); + return heap_.empty(); + } + + // pull -> (Bi, Si) + pair> pull() { + cleanup(); + if (heap_.empty()) throw out_of_range("pull from empty D"); + Weight Bi = heap_.top().first; + unordered_set Si; + while (!heap_.empty() && (int)Si.size() < block_size_) { + auto pr = heap_.top(); heap_.pop(); + Weight key = pr.first; + Node v = pr.second; + auto it = best_.find(v); + if (it != best_.end() && it->second == key) { + Si.insert(v); + best_.erase(it); + } + } + return {Bi, Si}; + } + +private: + void cleanup() { + while (!heap_.empty()) { + auto pr = heap_.top(); + Node v = pr.second; Weight key = pr.first; + auto it = best_.find(v); + if (it == best_.end() || it->second != key) { + heap_.pop(); + } else break; + } + } + + // min-heap (pair) using greater comparator + priority_queue, vector>, greater>> heap_; + unordered_map best_; + int M_; + double B_upper_; + int block_size_; +}; + +// --------------------------- +// FIND_PIVOTS (bounded BF-like) +// --------------------------- +pair, unordered_set> find_pivots( + const AdjList &graph, + const unordered_map &dist, + const unordered_set &S, + double B, + int n, + int k_steps, + int p_limit, + Instrument *instr = nullptr +) { + if (!instr) instr = new Instrument(); + + // S_filtered = nodes in S with dist < B + vector s_filtered; + s_filtered.reserve(S.size()); + for (Node v : S) { + auto it = dist.find(v); + if (it != dist.end() && it->second < B) s_filtered.push_back(v); + } + + unordered_set P; + if (s_filtered.empty()) { + // fallback: up to p_limit arbitrary samples from S + if (!S.empty()) { + int take = max(1, min((int)S.size(), p_limit)); + int count = 0; + for (Node v : S) { + P.insert(v); + if (++count >= take) break; + } + } + } else { + sort(s_filtered.begin(), s_filtered.end(), [&](Node a, Node b) { + double da = dist.at(a), db = dist.at(b); + return da < db; + }); + int take = max(1, min((int)s_filtered.size(), p_limit)); + for (int i = 0; i < take; ++i) P.insert(s_filtered[i]); + } + + unordered_set source_frontier = (!P.empty() ? P : S); + unordered_set discovered = source_frontier; + unordered_set frontier = source_frontier; + + int steps = max(1, k_steps); + for (int it_step = 0; it_step < steps; ++it_step) { + if (frontier.empty()) break; + unordered_set next_front; + for (Node u : frontier) { + double du = INF_D(); + auto itdu = dist.find(u); + if (itdu != dist.end()) du = itdu->second; + if (du >= B) continue; + if ((size_t)u >= graph.size()) continue; + for (const auto &pr : graph[u]) { + Node v = pr.first; Weight w = pr.second; + instr->relaxations += 1; + double nd = du + w; + if (nd < B && discovered.find(v) == discovered.end()) { + discovered.insert(v); + next_front.insert(v); + } + } + } + frontier.swap(next_front); + } + + unordered_set W = discovered; + if (P.empty() && !S.empty()) { + P.insert(*S.begin()); + } + return {P, W}; +} + +// --------------------------- +// BASECASE (limited Dijkstra) + // --------------------------- +pair> basecase( + const AdjList &graph, + unordered_map &dist, + double B, + const unordered_set &S, + int k, + Instrument *instr = nullptr +) { + if (!instr) instr = new Instrument(); + + if (S.empty()) return {B, {}}; + + // choose source x in S with smallest dist + Node x = -1; + double bestd = INF_D(); + for (Node v : S) { + auto it = dist.find(v); + double dv = (it != dist.end() ? it->second : INF_D()); + if (dv < bestd) { bestd = dv; x = v; } + } + if (x == -1) return {B, {}}; + + priority_queue, vector>, greater>> pq; + double start_d = (dist.find(x) != dist.end()) ? dist[x] : INF_D(); + pq.emplace(start_d, x); + instr->heap_ops += 1; + + unordered_set Uo; + unordered_set visited; + + while (!pq.empty() && (int)Uo.size() < (k + 1)) { + auto [d_u, u] = pq.top(); pq.pop(); + instr->heap_ops += 1; + auto itu = dist.find(u); + double currentDu = (itu != dist.end() ? itu->second : INF_D()); + if (d_u > currentDu) continue; + if (Uo.find(u) == Uo.end()) Uo.insert(u); + if ((size_t)u >= graph.size()) continue; + for (auto &pr : graph[u]) { + Node v = pr.first; Weight w = pr.second; + instr->relaxations += 1; + double newd = dist[u] + w; + double dv = dist.find(v) != dist.end() ? dist[v] : INF_D(); + if (newd < dv && newd < B) { + dist[v] = newd; + pq.emplace(newd, v); + instr->heap_ops += 1; + } + } + } + + if ((int)Uo.size() <= k) { + return {B, Uo}; + } else { + vector finite_dists; + finite_dists.reserve(Uo.size()); + for (Node v : Uo) { + auto it = dist.find(v); + if (it != dist.end() && isfinite(it->second)) finite_dists.push_back(it->second); + } + if (finite_dists.empty()) return {B, {}}; + double maxd = *max_element(finite_dists.begin(), finite_dists.end()); + unordered_set U_filtered; + for (Node v : Uo) { + auto it = dist.find(v); + if (it != dist.end() && it->second < maxd) U_filtered.insert(v); + } + return {maxd, U_filtered}; + } +} + +// --------------------------- +// BMSSP (practical recursive) +// --------------------------- +pair> bmssp( + const AdjList &graph, + unordered_map &dist, + const vector &edges, + int l, + double B, + const unordered_set &S, + int n, + Instrument *instr = nullptr +) { + if (!instr) instr = new Instrument(); + + int t_param, k_param; + if (n <= 2) { t_param = 1; k_param = 2; } + else { + double ln = log(max(3, n)); + t_param = max(1, (int)round(pow(ln, 2.0 / 3.0))); + k_param = max(2, (int)round(pow(ln, 1.0 / 3.0))); + } + + if (l <= 0) { + if (S.empty()) return {B, {}}; + return basecase(graph, dist, B, S, k_param, instr); + } + + int p_limit = max(1, 1 << min(10, t_param)); + int k_steps = max(1, k_param); + + auto piv = find_pivots(graph, dist, S, B, n, k_steps, p_limit, instr); + unordered_set P = move(piv.first); + unordered_set W = move(piv.second); + + int M = 1 << max(0, (l - 1) * t_param); + DataStructureD D(M, B, max(1, (int)min((int)max(1, (int)P.size()), 64))); + for (Node x : P) { + double dx = INF_D(); + auto it = dist.find(x); + if (it != dist.end()) dx = it->second; + D.insert(x, dx); + } + + double B_prime_initial = B; + if (!P.empty()) { + double minp = INF_D(); + for (Node x : P) { + auto it = dist.find(x); + double dx = (it != dist.end() ? it->second : INF_D()); + if (dx < minp) minp = dx; + } + if (isfinite(minp)) B_prime_initial = minp; + } + + unordered_set U; + vector B_prime_sub_values; + + int loop_guard = 0; + int limit = k_param * (1 << (l * max(1, t_param))); + + while ((int)U.size() < limit && !D.empty()) { + loop_guard++; + if (loop_guard > 20000) break; + Weight Bi; + unordered_set Si; + try { + tie(Bi, Si) = D.pull(); + } catch (...) { + break; + } + + auto subres = bmssp(graph, dist, edges, l - 1, Bi, Si, n, instr); + double B_prime_sub = subres.first; + unordered_set Ui = move(subres.second); + B_prime_sub_values.push_back(B_prime_sub); + for (auto u : Ui) U.insert(u); + + // Prepare batch set + vector> K_for_batch; + K_for_batch.reserve(64); + + for (Node u : Ui) { + auto itu = dist.find(u); + if (itu == dist.end()) continue; + double du = itu->second; + if (!isfinite(du)) continue; + if ((size_t)u >= graph.size()) continue; + for (auto &pr : graph[u]) { + Node v = pr.first; Weight w_uv = pr.second; + instr->relaxations += 1; + double newd = du + w_uv; + double dv = dist.find(v) != dist.end() ? dist[v] : INF_D(); + if (newd <= dv) { + dist[v] = newd; + if (Bi <= newd && newd < B) { + D.insert(v, newd); + } else if (B_prime_sub <= newd && newd < Bi) { + K_for_batch.emplace_back(v, newd); + } + } + } + } + + for (Node x : Si) { + auto itx = dist.find(x); + double dx = (itx != dist.end() ? itx->second : INF_D()); + if (B_prime_sub <= dx && dx < Bi) { + K_for_batch.emplace_back(x, dx); + } + } + + if (!K_for_batch.empty()) D.batch_prepend(K_for_batch); + } + + double B_prime_final = B_prime_initial; + if (!B_prime_sub_values.empty()) { + double minsub = *min_element(B_prime_sub_values.begin(), B_prime_sub_values.end()); + B_prime_final = min(B_prime_initial, minsub); + } + + unordered_set U_final = U; + for (Node x : W) { + auto it = dist.find(x); + double dx = (it != dist.end() ? it->second : INF_D()); + if (dx < B_prime_final) U_final.insert(x); + } + + return {B_prime_final, U_final}; +} + +// --------------------------- +// Test harness +// --------------------------- +struct TestResult { + int n; int m; unsigned int seed; + double dijkstra_time; + uint64_t dijkstra_relax; + double bmssp_time; + uint64_t bmssp_relax; + int dijkstra_reachable; + int bmssp_reachable; + double max_diff; +}; + +TestResult run_single_test(int n, int m, unsigned int seed = 0, Node source = 0) { + cout << "Generating graph: n=" << n << ", m=" << m << ", seed=" << seed << "\n"; + auto gen = generate_sparse_directed_graph(n, m, 100.0, seed); + AdjList graph = move(gen.first); + vector edges = move(gen.second); + double avg_deg = 0.0; + for (auto &adj : graph) avg_deg += (double)adj.size(); + avg_deg /= (double)max(1, n); + cout << "Graph generated. avg out-degree ≈ " << fixed << setprecision(3) << avg_deg << "\n"; + + // Dijkstra + Instrument instr_dij; + auto t0 = chrono::high_resolution_clock::now(); + auto dist_dij_map = dijkstra(graph, source, &instr_dij); + auto t1 = chrono::high_resolution_clock::now(); + double dij_time = chrono::duration(t1 - t0).count(); + int reachable_dij = 0; + for (auto &kv : dist_dij_map) if (isfinite(kv.second)) ++reachable_dij; + cout << "Dijkstra: time=" << dij_time << "s, relaxations=" << instr_dij.relaxations << ", heap_ops=" << instr_dij.heap_ops << ", reachable=" << reachable_dij << "\n"; + + // BMSSP + unordered_map dist_bm; + dist_bm.reserve(n * 2); + for (int i = 0; i < n; ++i) dist_bm[i] = INF_D(); + dist_bm[source] = 0.0; + + Instrument instr_bm; + int l; + if (n <= 2) l = 1; + else { + double ln = log(max(3, n)); + int t_guess = max(1, (int)round(pow(ln, 2.0 / 3.0))); + l = max(1, (int)max(1.0, round(ln / (double)t_guess))); + } + cout << "BMSSP params: top-level l=" << l << "\n"; + + auto t2 = chrono::high_resolution_clock::now(); + auto res = bmssp(graph, dist_bm, edges, l, INF_D(), unordered_set{source}, n, &instr_bm); + auto t3 = chrono::high_resolution_clock::now(); + double bm_time = chrono::duration(t3 - t2).count(); + int reachable_bm = 0; + for (auto &kv : dist_bm) if (isfinite(kv.second)) ++reachable_bm; + cout << "BMSSP: time=" << bm_time << "s, relaxations=" << instr_bm.relaxations << ", reachable=" << reachable_bm << ", B'=" << res.first << ", |U_final|=" << res.second.size() << "\n"; + + // Compare distances on commonly reachable nodes + vector diffs; + for (int v = 0; v < n; ++v) { + double dv = INF_D(); + double db = INF_D(); + auto it1 = dist_dij_map.find(v); + if (it1 != dist_dij_map.end()) dv = it1->second; + auto it2 = dist_bm.find(v); + if (it2 != dist_bm.end()) db = it2->second; + if (isfinite(dv) && isfinite(db)) diffs.push_back(fabs(dv - db)); + } + double max_diff = 0.0; + if (!diffs.empty()) max_diff = *max_element(diffs.begin(), diffs.end()); + cout << "Distance agreement (max abs diff on commonly reachable nodes): " << scientific << setprecision(6) << max_diff << "\n"; + + TestResult tr; + tr.n = n; tr.m = m; tr.seed = seed; + tr.dijkstra_time = dij_time; tr.dijkstra_relax = instr_dij.relaxations; + tr.bmssp_time = bm_time; tr.bmssp_relax = instr_bm.relaxations; + tr.dijkstra_reachable = reachable_dij; tr.bmssp_reachable = reachable_bm; + tr.max_diff = max_diff; + return tr; +} + +// --------------------------- +// CLI main +// --------------------------- +int main(int argc, char** argv) { + int n = 200000; + int m = 800000; + unsigned int seed = 0; + for (int i = 1; i < argc; ++i) { + string a = argv[i]; + if ((a == "-n" || a == "--nodes") && i + 1 < argc) { n = stoi(argv[++i]); } + else if ((a == "-m" || a == "--edges") && i + 1 < argc) { m = stoi(argv[++i]); } + else if ((a == "-s" || a == "--seed") && i + 1 < argc) { seed = (unsigned int)stoul(argv[++i]); } + } + + // To keep runtime reasonable by default for C++ demo, clamp default n/m if user doesn't want huge run: + if (n > 5000000) n = 5000000; + if (m > 50000000) m = 50000000; + + try { + run_single_test(n, m, seed, 0); + } catch (const exception &ex) { + cerr << "Error: " << ex.what() << endl; + return 2; + } + return 0; +} diff --git a/POCDart/bmssp_full_impl.dart b/POCDart/bmssp_full_impl.dart new file mode 100644 index 0000000..e03a84c --- /dev/null +++ b/POCDart/bmssp_full_impl.dart @@ -0,0 +1,623 @@ +// bmssp_full_impl.dart +// Dart 2.12+ (null-safety) single-file port of the Python BMSSP practical implementation. +// +// Provides: graph generator, Dijkstra, DataStructureD, FIND_PIVOTS, BASECASE, BMSSP recursion, +// instrumentation, and a simple test harness / CLI. + +import 'dart:collection'; +import 'dart:io'; +import 'dart:math'; + +// Type aliases +typedef Node = int; +typedef Weight = double; +typedef Edge = List; // [u, v, w] +// Graph: Map> -- We'll use List> pairs +// But better typed: Map> + +class _Neighbor { + final int v; + final double w; + _Neighbor(this.v, this.w); +} + +// --------------------------- +// Utilities & Graph generator +// --------------------------- +class GraphAndEdges { + final Map> graph; + final List edges; + GraphAndEdges(this.graph, this.edges); +} + +GraphAndEdges generateSparseDirectedGraph(int n, int m, {double maxW = 100.0, int? seed}) { + final rng = (seed != null) ? Random(seed) : Random(); + final graph = >{}; + for (var i = 0; i < n; ++i) graph[i] = <_Neighbor>[]; + final edges = []; + + // weak backbone to avoid isolated nodes + for (var i = 1; i < n; ++i) { + var u = rng.nextInt(i); + var w = rng.nextDouble() * (maxW - 1.0) + 1.0; + graph[u]!.add(_Neighbor(i, w)); + edges.add([u, i, w]); + } + var remaining = max(0, m - (n - 1)); + for (var i = 0; i < remaining; ++i) { + var u = rng.nextInt(n); + var v = rng.nextInt(n); + var w = rng.nextDouble() * (maxW - 1.0) + 1.0; + graph[u]!.add(_Neighbor(v, w)); + edges.add([u, v, w]); + } + return GraphAndEdges(graph, edges); +} + +// --------------------------- +// Instrumentation +// --------------------------- +class Instrument { + int relaxations = 0; + int heapOps = 0; + void reset() { + relaxations = 0; + heapOps = 0; + } +} + +// --------------------------- +// Simple generic min-heap / priority queue +// --------------------------- +// We'll implement a simple binary heap keyed by double (priority). +class PriorityQueue { + final List<_PQEntry> _data = []; + int get length => _data.length; + bool get isEmpty => _data.isEmpty; + + void add(T value, double priority) { + _data.add(_PQEntry(value, priority)); + _siftUp(_data.length - 1); + } + + // returns (value, priority) + _PQEntry pop() { + if (_data.isEmpty) throw StateError('pop from empty priority queue'); + final top = _data.first; + final last = _data.removeLast(); + if (_data.isNotEmpty) { + _data[0] = last; + _siftDown(0); + } + return top; + } + + _PQEntry peek() { + if (_data.isEmpty) throw StateError('peek from empty priority queue'); + return _data.first; + } + + void _siftUp(int idx) { + while (idx > 0) { + final parent = (idx - 1) >> 1; + if (_data[idx].priority < _data[parent].priority) { + final tmp = _data[idx]; + _data[idx] = _data[parent]; + _data[parent] = tmp; + idx = parent; + } else { + break; + } + } + } + + void _siftDown(int idx) { + final n = _data.length; + while (true) { + final left = idx * 2 + 1; + final right = left + 1; + var smallest = idx; + if (left < n && _data[left].priority < _data[smallest].priority) smallest = left; + if (right < n && _data[right].priority < _data[smallest].priority) smallest = right; + if (smallest == idx) break; + final tmp = _data[idx]; + _data[idx] = _data[smallest]; + _data[smallest] = tmp; + idx = smallest; + } + } +} + +class _PQEntry { + final T value; + final double priority; + _PQEntry(this.value, this.priority); +} + +// --------------------------- +// Dijkstra +// --------------------------- +Map dijkstra(Map> graph, int source, [Instrument? instr]) { + instr ??= Instrument(); + final dist = {}; + for (final k in graph.keys) dist[k] = double.infinity; + dist[source] = 0.0; + final pq = PriorityQueue(); + pq.add(source, 0.0); + instr.heapOps++; + + while (!pq.isEmpty) { + final entry = pq.pop(); + instr.heapOps++; + final dU = entry.priority; + final u = entry.value; + final cur = dist[u] ?? double.infinity; + if (dU > cur) continue; + final adj = graph[u]!; + for (final nb in adj) { + instr.relaxations++; + final v = nb.v; + final w = nb.w; + final alt = dU + w; + final dv = dist[v] ?? double.infinity; + if (alt < dv) { + dist[v] = alt; + pq.add(v, alt); + instr.heapOps++; + } + } + } + return dist; +} + +// --------------------------- +// DataStructure D (practical) +// --------------------------- +class DataStructureD { + final PriorityQueue _heap = PriorityQueue(); + final Map _best = {}; + final int M; + final double BUpper; + final int blockSize; + + DataStructureD(int M, double BUpper, {int? blockSize}) + : M = M < 1 ? 1 : M, + BUpper = BUpper, + blockSize = blockSize ?? max(1, (M < 1 ? 1 : M) ~/ 8); + + void insert(int v, double key) { + final prev = _best[v]; + if (prev == null || key < prev) { + _best[v] = key; + _heap.add(v, key); + } + } + + void batchPrepend(Iterable> iterablePairs) { + for (final p in iterablePairs) { + final v = p[0] as int; + final key = p[1] as double; + insert(v, key); + } + } + + void _cleanup() { + // We can't directly inspect heap internals, so emulate lazy drain: + // Pop until top matches _best (this requires peeking). + // Our PQ exposes peek via peek; catch error if empty. + try { + while (_heap.length > 0) { + final top = _heap.peek(); + final v = top.value; + final key = top.priority; + final bestVal = _best[v]; + if (bestVal == null || bestVal != key) { + _heap.pop(); + } else { + break; + } + } + } catch (e) { + // empty, ignore + } + } + + bool isEmpty() { + _cleanup(); + return _heap.isEmpty; + } + + // pull returns Bi and Si set of up to blockSize nodes + Map pull() { + _cleanup(); + if (_heap.isEmpty) throw StateError('pull from empty D'); + final Bi = _heap.peek().priority; + final Si = {}; + while (!_heap.isEmpty && Si.length < blockSize) { + final entry = _heap.pop(); + final key = entry.priority; + final v = entry.value; + final bestVal = _best[v]; + if (bestVal != null && bestVal == key) { + Si.add(v); + _best.remove(v); + } + } + return {'Bi': Bi, 'Si': Si}; + } +} + +// --------------------------- +// FIND_PIVOTS (bounded Bellman-Ford-like) +// --------------------------- +Map> findPivots( + Map> graph, + Map dist, + Set S, + double B, + int n, + int kSteps, + int pLimit, + [Instrument? instr]) { + instr ??= Instrument(); + final sFiltered = S.where((v) => (dist[v] ?? double.infinity) < B).toList(); + + Set P; + if (sFiltered.isEmpty) { + if (S.isEmpty) { + P = {}; + } else { + final take = max(1, min(S.length, pLimit)); + P = S.take(take).toSet(); + } + } else { + sFiltered.sort((a, b) => (dist[a] ?? double.infinity).compareTo(dist[b] ?? double.infinity)); + final take = max(1, min(sFiltered.length, pLimit)); + P = sFiltered.take(take).toSet(); + } + + final sourceFrontier = P.isNotEmpty ? P.toSet() : S.toSet(); + final discovered = sourceFrontier.toSet(); + var frontier = sourceFrontier.toSet(); + + for (var step = 0; step < max(1, kSteps); ++step) { + if (frontier.isEmpty) break; + final nextFront = {}; + for (final u in frontier) { + final du = dist[u] ?? double.infinity; + if (du >= B) continue; + for (final nb in graph[u] ?? []) { + instr.relaxations++; + final nd = du + nb.w; + if (nd < B && !discovered.contains(nb.v)) { + discovered.add(nb.v); + nextFront.add(nb.v); + } + } + } + frontier = nextFront; + } + + final W = discovered.toSet(); + if (P.isEmpty && S.isNotEmpty) { + P = {S.first}; + } + return {'P': P, 'W': W}; +} + +// --------------------------- +// BASECASE (Dijkstra-like limited) +// --------------------------- +Map basecase( + Map> graph, Map dist, double B, Set S, int k, [Instrument? instr]) { + instr ??= Instrument(); + if (S.isEmpty) return {'Bprime': B, 'Uo': {}}; + + // choose x in S with smallest dist + int x = S.first; + double bestd = dist[x] ?? double.infinity; + for (final v in S) { + final dv = dist[v] ?? double.infinity; + if (dv < bestd) { + bestd = dv; + x = v; + } + } + + final heap = PriorityQueue(); + final startD = dist[x] ?? double.infinity; + heap.add(x, startD); + instr.heapOps++; + + final Uo = {}; + + while (!heap.isEmpty && Uo.length < (k + 1)) { + final entry = heap.pop(); + instr.heapOps++; + final dU = entry.priority; + final u = entry.value; + final cur = dist[u] ?? double.infinity; + if (dU > cur) continue; + if (!Uo.contains(u)) Uo.add(u); + for (final nb in graph[u] ?? []) { + instr.relaxations++; + final newd = (dist[u] ?? double.infinity) + nb.w; + final dv = dist[nb.v] ?? double.infinity; + if (newd < dv && newd < B) { + dist[nb.v] = newd; + heap.add(nb.v, newd); + instr.heapOps++; + } + } + } + + if (Uo.length <= k) { + return {'Bprime': B, 'Uo': Uo}; + } else { + final finiteDists = Uo.where((v) => (dist[v] ?? double.infinity).isFinite).map((v) => dist[v]!).toList(); + if (finiteDists.isEmpty) return {'Bprime': B, 'Uo': {}}; + final maxd = finiteDists.reduce(max); + final uFiltered = Uo.where((v) => (dist[v] ?? double.infinity) < maxd).toSet(); + return {'Bprime': maxd, 'Uo': uFiltered}; + } +} + +// --------------------------- +// BMSSP (practical recursive) +// --------------------------- +Map bmssp( + Map> graph, + Map dist, + List edges, + int l, + double B, + Set S, + int n, + [Instrument? instr]) { + instr ??= Instrument(); + + int tParam, kParam; + if (n <= 2) { + tParam = 1; + kParam = 2; + } else { + final ln = log(max(3, n)); + tParam = max(1, (ln.pow(2.0 / 3.0)).round()); + kParam = max(2, (ln.pow(1.0 / 3.0)).round()); + } + + if (l <= 0) { + if (S.isEmpty) return {'Bprime': B, 'Ufinal': {}}; + final bc = basecase(graph, dist, B, S, kParam, instr); + return {'Bprime': bc['Bprime'] as double, 'Ufinal': bc['Uo'] as Set}; + } + + final pLimit = max(1, 1 << min(10, tParam)); + final kSteps = max(1, kParam); + + final piv = findPivots(graph, dist, S, B, n, kSteps, pLimit, instr); + var P = (piv['P'] as Set).toSet(); + final W = (piv['W'] as Set).toSet(); + + final M = 1 << max(0, (l - 1) * tParam); + final D = DataStructureD(M, B, blockSize: max(1, min(P.isEmpty ? 1 : P.length, 64))); + + for (final x in P) { + final dx = dist[x] ?? double.infinity; + D.insert(x, dx); + } + + double bPrimeInitial = B; + if (P.isNotEmpty) { + double minv = double.infinity; + for (final x in P) { + final dx = dist[x] ?? double.infinity; + if (dx < minv) minv = dx; + } + if (minv.isFinite) bPrimeInitial = minv; + } + + final U = {}; + final bPrimeSubs = []; + + var loopGuard = 0; + final limit = kParam * (1 << (l * max(1, tParam))); + + while (U.length < limit && !D.isEmpty()) { + loopGuard++; + if (loopGuard > 20000) break; + Map pulled; + try { + pulled = D.pull(); + } catch (e) { + break; + } + final Bi = pulled['Bi'] as double; + final Si = pulled['Si'] as Set; + + final sub = bmssp(graph, dist, edges, l - 1, Bi, Si, n, instr); + final bPrimeSub = sub['Bprime'] as double; + final Ui = (sub['Ufinal'] as Set).toSet(); + bPrimeSubs.add(bPrimeSub); + U.addAll(Ui); + + final batch = >[]; + + for (final u in Ui) { + final du = dist[u] ?? double.infinity; + if (!du.isFinite) continue; + for (final nb in graph[u] ?? []) { + instr.relaxations++; + final newd = du + nb.w; + final dv = dist[nb.v] ?? double.infinity; + if (newd <= dv) { + dist[nb.v] = newd; + if (Bi <= newd && newd < B) { + D.insert(nb.v, newd); + } else if (bPrimeSub <= newd && newd < Bi) { + batch.add([nb.v, newd]); + } + } + } + } + + for (final x in Si) { + final dx = dist[x] ?? double.infinity; + if (bPrimeSub <= dx && dx < Bi) { + batch.add([x, dx]); + } + } + + if (batch.isNotEmpty) D.batchPrepend(batch); + } + + double bPrimeFinal = bPrimeInitial; + if (bPrimeSubs.isNotEmpty) { + final minSub = bPrimeSubs.reduce(min); + bPrimeFinal = min(bPrimeInitial, minSub); + } + + final uFinal = U.toSet(); + for (final x in W) { + final dx = dist[x] ?? double.infinity; + if (dx < bPrimeFinal) uFinal.add(x); + } + + return {'Bprime': bPrimeFinal, 'Ufinal': uFinal}; +} + +// --------------------------- +// Test harness +// --------------------------- +class TestResult { + int n = 0; + int m = 0; + int seed = 0; + double dijkstraTime = 0.0; + int dijkstraRelax = 0; + double bmsspTime = 0.0; + int bmsspRelax = 0; + int dijkstraReachable = 0; + int bmsspReachable = 0; + double maxDiff = 0.0; +} + +TestResult runSingleTest(int n, int m, {int seed = 0, int source = 0}) { + print('Generating graph: n=$n, m=$m, seed=$seed'); + final ge = generateSparseDirectedGraph(n, m, seed: seed); + final graph = ge.graph; + final edges = ge.edges; + final avgDeg = graph.values.fold(0, (p, e) => p + e.length) / n; + print('Graph generated. avg out-degree ≈ ${avgDeg.toStringAsFixed(3)}'); + + // Dijkstra timing + final instrDij = Instrument(); + final t0 = DateTime.now().millisecondsSinceEpoch; + final distDij = dijkstra(graph, source, instrDij); + final t1 = DateTime.now().millisecondsSinceEpoch; + final dijTime = (t1 - t0) / 1000.0; + final dijReachable = distDij.values.where((v) => v.isFinite).length; + print('Dijkstra: time=${dijTime.toStringAsFixed(6)}s, relaxations=${instrDij.relaxations}, heap_ops=${instrDij.heapOps}, reachable=$dijReachable'); + + // BMSSP practical + final distBm = {}; + for (final k in graph.keys) distBm[k] = double.infinity; + distBm[source] = 0.0; + final instrBm = Instrument(); + + int l; + if (n <= 2) l = 1; + else { + final ln = log(max(3, n)); + final tGuess = max(1, (ln.pow(2.0 / 3.0)).round()); + l = max(1, max(1, (ln / tGuess).round())); + } + print('BMSSP params: top-level l=$l'); + + final t2 = DateTime.now().millisecondsSinceEpoch; + final res = bmssp(graph, distBm, edges, l, double.infinity, {source}, n, instrBm); + final t3 = DateTime.now().millisecondsSinceEpoch; + final bmTime = (t3 - t2) / 1000.0; + final bmReachable = distBm.values.where((v) => v.isFinite).length; + final Bp = res['Bprime'] as double; + final Ufinal = res['Ufinal'] as Set; + print('BMSSP: time=${bmTime.toStringAsFixed(6)}s, relaxations=${instrBm.relaxations}, reachable=$bmReachable, B\'=$Bp, |U_final|=${Ufinal.length}'); + + final diffs = []; + for (final v in graph.keys) { + final dv = distDij[v] ?? double.infinity; + final db = distBm[v] ?? double.infinity; + if (dv.isFinite && db.isFinite) diffs.add((dv - db).abs()); + } + final maxDiff = diffs.isEmpty ? 0.0 : diffs.reduce(max); + print('Distance agreement (max abs diff on commonly reachable nodes): ${maxDiff.toStringAsExponential(6)}'); + + final tr = TestResult() + ..n = n + ..m = m + ..seed = seed + ..dijkstraTime = dijTime + ..dijkstraRelax = instrDij.relaxations + ..bmsspTime = bmTime + ..bmsspRelax = instrBm.relaxations + ..dijkstraReachable = dijReachable + ..bmsspReachable = bmReachable + ..maxDiff = maxDiff; + return tr; +} + +// --------------------------- +// CLI main +// --------------------------- +void printUsage() { + print('Usage: dart bmssp_full_impl.dart [-n nodes] [-m edges] [-s seed]'); +} + +void main(List args) { + int n = 200000; + int m = 800000; + int seed = 0; + + for (var i = 0; i < args.length; ++i) { + final a = args[i]; + if ((a == '-n' || a == '--nodes') && i + 1 < args.length) { + n = int.tryParse(args[++i]) ?? n; + } else if ((a == '-m' || a == '--edges') && i + 1 < args.length) { + m = int.tryParse(args[++i]) ?? m; + } else if ((a == '-s' || a == '--seed') && i + 1 < args.length) { + seed = int.tryParse(args[++i]) ?? seed; + } else if (a == '-h' || a == '--help') { + printUsage(); + return; + } + } + + // For interactive testing on small machines you may want to set smaller defaults: + if (Platform.environment.containsKey('BMSSP_SMALL')) { + n = 2000; + m = 8000; + } + + // run test + try { + runSingleTest(n, m, seed: seed, source: 0); + } catch (e, st) { + stderr.writeln('Error: $e\n$st'); + exit(2); + } +} + +// --------------------------- +// Extensions for math helpers +// --------------------------- +extension DoublePow on double { + double pow(double p) => mathPow(this, p); +} +double mathPow(double a, double b) => powDouble(a, b); + +double powDouble(double a, double b) { + return math_pow(a, b).toDouble(); +} + +// Because dart:math.pow returns num and may be slow to cast, we use a wrapper +num math_pow(num a, num b) => pow(a, b); diff --git a/POCDart/run.bat b/POCDart/run.bat new file mode 100644 index 0000000..10e4a5f --- /dev/null +++ b/POCDart/run.bat @@ -0,0 +1 @@ +dart bmssp_full_impl.dart -n 2000 -m 8000 -s 42 \ No newline at end of file diff --git a/POCDotnet/BaseCaseRunner.cs b/POCDotnet/BaseCaseRunner.cs new file mode 100644 index 0000000..04d71a6 --- /dev/null +++ b/POCDotnet/BaseCaseRunner.cs @@ -0,0 +1,67 @@ +using Graph = System.Collections.Generic.Dictionary>; +using Node = System.Int32; + +namespace POCDotnet +{ + /// + /// BASECASE (Dijkstra-like limited) + /// + public static class BaseCaseRunner + { + public static (double BPrime, HashSet Uo) Run( + Graph graph, + Dictionary dist, + double B, + HashSet S, + int k, + Instrument? instr = null) + { + instr ??= new Instrument(); + + if (S.Count == 0) return (B, new HashSet()); + + var x = S.MinBy(v => dist.TryGetValue(v, out var dv) ? dv : double.PositiveInfinity); + var heap = new PriorityQueue(); + + var startD = dist.TryGetValue(x, out var sd) ? sd : double.PositiveInfinity; + heap.Enqueue(x, startD); + instr.HeapOps++; + + var Uo = new HashSet(); + + while (heap.Count > 0 && Uo.Count < (k + 1)) + { + heap.TryDequeue(out var u, out var dU); + instr.HeapOps++; + if (dU > (dist.TryGetValue(u, out var du) ? du : double.PositiveInfinity)) continue; + + if (!Uo.Contains(u)) Uo.Add(u); + + foreach (var (v, w) in graph[u]) + { + instr.Relaxations++; + var newd = dist[u] + w; + if (newd < (dist.TryGetValue(v, out var dv) ? dv : double.PositiveInfinity) && newd < B) + { + dist[v] = newd; + heap.Enqueue(v, newd); + instr.HeapOps++; + } + } + } + + if (Uo.Count <= k) + { + return (B, Uo); + } + else + { + var finite = Uo.Where(v => !double.IsPositiveInfinity(dist[v])).Select(v => dist[v]).ToList(); + if (finite.Count == 0) return (B, new HashSet()); + var maxd = finite.Max(); + var Ufiltered = new HashSet(Uo.Where(v => dist[v] < maxd)); + return (maxd, Ufiltered); + } + } + } +} diff --git a/POCDotnet/BmsspRunner.cs b/POCDotnet/BmsspRunner.cs new file mode 100644 index 0000000..af0b466 --- /dev/null +++ b/POCDotnet/BmsspRunner.cs @@ -0,0 +1,131 @@ +using Edge = System.ValueTuple; +using Graph = System.Collections.Generic.Dictionary>; +using Node = System.Int32; + +namespace POCDotnet +{ + /// + /// BMSSP (practical recursive) + /// + public static class BmsspRunner + { + private static (int tParam, int kParam) GetParams(int n) + { + if (n <= 2) return (1, 2); + double ln = Math.Log(Math.Max(3, n)); + int t = Math.Max(1, (int)Math.Round(Math.Pow(ln, 2.0 / 3.0))); + int k = Math.Max(2, (int)Math.Round(Math.Pow(ln, 1.0 / 3.0))); + return (t, k); + } + + public static (double BPrime, HashSet UFinal) Run( + Graph graph, + Dictionary dist, + List edges, + int l, + double B, + HashSet S, + int n, + Instrument? instr = null) + { + instr ??= new Instrument(); + + var (tParam, kParam) = GetParams(n); + + if (l <= 0) + { + if (S.Count == 0) return (B, new HashSet()); + return BaseCaseRunner.Run(graph, dist, B, S, kParam, instr); + } + + int pLimit = Math.Max(1, 1 << Math.Min(10, tParam)); + int kSteps = Math.Max(1, kParam); + + var (P, W) = PivotFinder.FindPivots(graph, dist, S, B, n, kSteps, pLimit, instr); + + int M = 1 << Math.Max(0, (l - 1) * tParam); + var D = new DataStructureD(M, B, blockSize: Math.Max(1, Math.Min(Math.Max(P.Count, 1), 64))); + + foreach (var x in P) + { + dist.TryGetValue(x, out var dx); + D.Insert(x, dx); + } + + double bPrimeInitial = P.Count > 0 ? P.Min(x => dist.TryGetValue(x, out var dx) ? dx : double.PositiveInfinity) : B; + var U = new HashSet(); + var bPrimeSubs = new List(); + + int loopGuard = 0; + int limit = kParam * (1 << (l * Math.Max(1, tParam))); + + while (U.Count < limit && !D.Empty()) + { + loopGuard++; + if (loopGuard > 20000) break; + + double Bi; + HashSet Si; + try + { + (Bi, Si) = D.Pull(); + } + catch + { + break; + } + + var (bPrimeSub, Ui) = Run(graph, dist, edges, l - 1, Bi, Si, n, instr); + bPrimeSubs.Add(bPrimeSub); + U.UnionWith(Ui); + + var batch = new HashSet<(Node v, double key)>(); + + foreach (var u in Ui) + { + if (!dist.TryGetValue(u, out var du) || double.IsPositiveInfinity(du)) continue; + + foreach (var (v, wuv) in graph[u]) + { + instr.Relaxations++; + var newd = du + wuv; + + if (newd <= (dist.TryGetValue(v, out var dv) ? dv : double.PositiveInfinity)) + { + dist[v] = newd; + if (Bi <= newd && newd < B) + { + D.Insert(v, newd); + } + else if (bPrimeSub <= newd && newd < Bi) + { + batch.Add((v, newd)); + } + } + } + } + + foreach (var x in Si) + { + var dx = dist.TryGetValue(x, out var dxx) ? dxx : double.PositiveInfinity; + if (bPrimeSub <= dx && dx < Bi) + { + batch.Add((x, dx)); + } + } + + if (batch.Count > 0) D.BatchPrepend(batch); + } + + double bPrimeFinal = bPrimeSubs.Count > 0 ? Math.Min(bPrimeSubs.Prepend(bPrimeInitial).Min(), bPrimeInitial) : bPrimeInitial; + + var UFinal = new HashSet(U); + foreach (var x in W) + { + if (dist.TryGetValue(x, out var dx) && dx < bPrimeFinal) UFinal.Add(x); + } + + return (bPrimeFinal, UFinal); + } + } +} diff --git a/POCDotnet/DataStructureD.cs b/POCDotnet/DataStructureD.cs new file mode 100644 index 0000000..28e9033 --- /dev/null +++ b/POCDotnet/DataStructureD.cs @@ -0,0 +1,76 @@ +using Node = System.Int32; + +namespace POCDotnet +{ + /// + /// DataStructure D (practical) + /// + public class DataStructureD + { + private readonly PriorityQueue _heap = new(); + private readonly Dictionary _best = new(); + private readonly int _blockSize; + private readonly int _M; + private readonly double _BUpper; + + public DataStructureD(int M, double BUpper, int? blockSize = null) + { + _M = Math.Max(1, M); + _BUpper = BUpper; + _blockSize = blockSize ?? Math.Max(1, _M / 8); + } + + public void Insert(Node v, double key) + { + if (!_best.TryGetValue(v, out var prev) || key < prev) + { + _best[v] = key; + _heap.Enqueue(v, key); + } + } + + public void BatchPrepend(IEnumerable<(Node v, double key)> items) + { + foreach (var (v, key) in items) Insert(v, key); + } + + private void Cleanup() + { + // remove stale heap entries + while (_heap.Count > 0) + { + if (_heap.TryPeek(out var v, out var key)) + { + if (_best.TryGetValue(v, out var val) && val == key) break; + _heap.Dequeue(); // stale + } + } + } + + public bool Empty() + { + Cleanup(); + return _heap.Count == 0; + } + + public (double Bi, HashSet Si) Pull() + { + Cleanup(); + if (_heap.Count == 0) throw new InvalidOperationException("pull from empty D"); + _heap.TryPeek(out _, out var Bi); + var Si = new HashSet(); + while (_heap.Count > 0 && Si.Count < _blockSize) + { + if (_heap.TryDequeue(out var v, out var key)) + { + if (_best.TryGetValue(v, out var val) && val == key) + { + Si.Add(v); + _best.Remove(v); + } + } + } + return (Bi, Si); + } + } +} diff --git a/POCDotnet/GraphGenerator.cs b/POCDotnet/GraphGenerator.cs new file mode 100644 index 0000000..2769d80 --- /dev/null +++ b/POCDotnet/GraphGenerator.cs @@ -0,0 +1,39 @@ +using Edge = System.ValueTuple; +using Graph = System.Collections.Generic.Dictionary>; + +namespace POCDotnet +{ + /// + /// Utilities & Graph generator + /// + public static class GraphGenerator + { + public static (Graph, List) GenerateSparseDirectedGraph(int n, int m, double maxW = 100.0, int? seed = null) + { + var rand = seed.HasValue ? new Random(seed.Value) : new Random(); + var graph = new Graph(); + for (int i = 0; i < n; i++) graph[i] = new List<(int, double)>(); + var edges = new List(); + + // weak backbone to avoid isolated nodes + for (int i = 1; i < n; i++) + { + int u = rand.Next(i); + double w = rand.NextDouble() * maxW + 1.0; + graph[u].Add((i, w)); + edges.Add((u, i, w)); + } + + int remaining = Math.Max(0, m - (n - 1)); + for (int i = 0; i < remaining; i++) + { + int u = rand.Next(n); + int v = rand.Next(n); + double w = rand.NextDouble() * maxW + 1.0; + graph[u].Add((v, w)); + edges.Add((u, v, w)); + } + return (graph, edges); + } + } +} diff --git a/POCDotnet/Harness.cs b/POCDotnet/Harness.cs new file mode 100644 index 0000000..98e48dc --- /dev/null +++ b/POCDotnet/Harness.cs @@ -0,0 +1,59 @@ +using System.Diagnostics; + +namespace POCDotnet +{ + /// + /// Test harness (similar to run_single_test) + /// + public static class Harness + { + public static void RunSingleTest(int n, int m, int seed = 0, int source = 0) + { + Console.WriteLine($"Generating graph: n={n}, m={m}, seed={seed}"); + var (graph, edges) = GraphGenerator.GenerateSparseDirectedGraph(n, m, seed: seed); + double avgDeg = graph.Values.Sum(adj => adj.Count) / (double)n; + Console.WriteLine($"Graph generated. avg out-degree ≈ {avgDeg:F3}"); + + var distDij = graph.Keys.ToDictionary(v => v, _ => double.PositiveInfinity); + distDij[source] = 0.0; + + var instrDij = new Instrument(); + var sw = Stopwatch.StartNew(); + distDij = ShortestPath.Dijkstra(graph, source, instrDij); + sw.Stop(); + Console.WriteLine($"Dijkstra: time={sw.Elapsed.TotalSeconds:F6}s, relaxations={instrDij.Relaxations}, heap_ops={instrDij.HeapOps}, reachable={distDij.Values.Count(v => !double.IsInfinity(v))}"); + + var distBm = graph.Keys.ToDictionary(v => v, _ => double.PositiveInfinity); + distBm[source] = 0.0; + + var instrBm = new Instrument(); + int l; + if (n <= 2) l = 1; + else + { + double ln = Math.Log(Math.Max(3, n)); + int tGuess = Math.Max(1, (int)Math.Round(Math.Pow(ln, 2.0 / 3.0))); + l = Math.Max(1, (int)Math.Max(1, Math.Round(ln / tGuess))); + } + Console.WriteLine($"BMSSP params: top-level l={l}"); + + sw.Restart(); + var (Bp, Ufinal) = BmsspRunner.Run(graph, distBm, edges, l, double.PositiveInfinity, new HashSet { source }, n, instrBm); + sw.Stop(); + Console.WriteLine($"BMSSP: time={sw.Elapsed.TotalSeconds:F6}s, relaxations={instrBm.Relaxations}, reachable={distBm.Values.Count(v => !double.IsInfinity(v))}, B'={Bp}, |U_final|={Ufinal.Count}"); + + var diffs = new List(); + foreach (var v in graph.Keys) + { + var dv = distDij[v]; + var db = distBm[v]; + if (!double.IsInfinity(dv) && !double.IsInfinity(db)) + { + diffs.Add(Math.Abs(dv - db)); + } + } + double maxDiff = diffs.Count > 0 ? diffs.Max() : 0.0; + Console.WriteLine($"Distance agreement (max abs diff on commonly reachable nodes): {maxDiff:E6}"); + } + } +} diff --git a/POCDotnet/Instrument.cs b/POCDotnet/Instrument.cs new file mode 100644 index 0000000..ea0ac94 --- /dev/null +++ b/POCDotnet/Instrument.cs @@ -0,0 +1,12 @@ +namespace POCDotnet +{ + /// + /// Instrumentation + /// + public class Instrument + { + public long Relaxations { get; set; } + public long HeapOps { get; set; } + public void Reset() { Relaxations = 0; HeapOps = 0; } + } +} diff --git a/POCDotnet/POCDotnet.csproj b/POCDotnet/POCDotnet.csproj new file mode 100644 index 0000000..fd4bd08 --- /dev/null +++ b/POCDotnet/POCDotnet.csproj @@ -0,0 +1,10 @@ + + + + Exe + net9.0 + enable + enable + + + diff --git a/POCDotnet/PivotFinder.cs b/POCDotnet/PivotFinder.cs new file mode 100644 index 0000000..a0aa8db --- /dev/null +++ b/POCDotnet/PivotFinder.cs @@ -0,0 +1,71 @@ +using Graph = System.Collections.Generic.Dictionary>; +using Node = System.Int32; + +namespace POCDotnet +{ + /// + /// FIND_PIVOTS (practical bounded BF) + /// + public static class PivotFinder + { + public static (HashSet P, HashSet W) FindPivots( + Graph graph, + Dictionary dist, + HashSet S, + double B, + int n, + int kSteps, + int pLimit, + Instrument? instr = null) + { + instr ??= new Instrument(); + + var sFiltered = S.Where(v => dist.TryGetValue(v, out var dv) && dv < B).ToList(); + + HashSet P; + if (sFiltered.Count == 0) + { + var take = Math.Max(1, Math.Min(S.Count, pLimit)); + P = new HashSet(S.Take(take)); + } + else + { + sFiltered.Sort((a, b) => dist[a].CompareTo(dist[b])); + P = new HashSet(sFiltered.Take(Math.Max(1, Math.Min(sFiltered.Count, pLimit)))); + } + + var sourceFrontier = P.Count > 0 ? new HashSet(P) : new HashSet(S); + var discovered = new HashSet(sourceFrontier); + var frontier = new HashSet(sourceFrontier); + + for (int step = 0; step < Math.Max(1, kSteps); step++) + { + if (frontier.Count == 0) break; + var nextFront = new HashSet(); + foreach (var u in frontier) + { + var du = dist.TryGetValue(u, out var vdu) ? vdu : double.PositiveInfinity; + if (du >= B) continue; + foreach (var (v, w) in graph[u]) + { + instr.Relaxations++; + var nd = du + w; + if (nd < B && !discovered.Contains(v)) + { + discovered.Add(v); + nextFront.Add(v); + } + } + } + frontier = nextFront; + } + + var W = new HashSet(discovered); + if (P.Count == 0 && S.Count > 0) + { + P.Add(S.First()); + } + return (P, W); + } + } +} diff --git a/POCDotnet/Program.cs b/POCDotnet/Program.cs new file mode 100644 index 0000000..51bfce9 --- /dev/null +++ b/POCDotnet/Program.cs @@ -0,0 +1,18 @@ +namespace POCDotnet +{ + public static class Program + { + public static void Main(string[] args) + { + int n = 200_000, m = 800_000, seed = 0; + for (int i = 0; i < args.Length - 1; i++) + { + if (args[i] == "-n" || args[i] == "--nodes") int.TryParse(args[++i], out n); + else if (args[i] == "-m" || args[i] == "--edges") int.TryParse(args[++i], out m); + else if (args[i] == "-s" || args[i] == "--seed") int.TryParse(args[++i], out seed); + } + + Harness.RunSingleTest(n, m, seed, source: 0); + } + } +} diff --git a/POCDotnet/ShortestPath.cs b/POCDotnet/ShortestPath.cs new file mode 100644 index 0000000..5d1a8a3 --- /dev/null +++ b/POCDotnet/ShortestPath.cs @@ -0,0 +1,43 @@ +using Graph = System.Collections.Generic.Dictionary>; +using Node = System.Int32; +using Weight = System.Double; + +namespace POCDotnet +{ + /// + /// Dijkstra + /// + public static class ShortestPath + { + public static Dictionary Dijkstra(Graph graph, Node source, Instrument? instr = null) + { + instr ??= new Instrument(); + var dist = graph.Keys.ToDictionary(v => v, _ => double.PositiveInfinity); + dist[source] = 0.0; + + var pq = new PriorityQueue(); + pq.Enqueue(source, 0.0); + instr.HeapOps++; + + while (pq.Count > 0) + { + pq.TryDequeue(out var u, out var dU); + instr.HeapOps++; + if (dU > dist[u]) continue; + + foreach (var (v, w) in graph[u]) + { + instr.Relaxations++; + double alt = dU + w; + if (alt < dist[v]) + { + dist[v] = alt; + pq.Enqueue(v, alt); + instr.HeapOps++; + } + } + } + return dist; + } + } +} diff --git a/POCNodeTypescript/.vscode/launch.json b/POCNodeTypescript/.vscode/launch.json new file mode 100644 index 0000000..9a761b1 --- /dev/null +++ b/POCNodeTypescript/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ "/**" ], + "program": "${workspaceFolder}/app.js", + "cwd": "${workspaceFolder}", + "console": "externalTerminal" + } + ] +} \ No newline at end of file diff --git a/POCNodeTypescript/CHANGELOG.md b/POCNodeTypescript/CHANGELOG.md new file mode 100644 index 0000000..96f0f3b --- /dev/null +++ b/POCNodeTypescript/CHANGELOG.md @@ -0,0 +1,15 @@ +This file explains how Visual Studio created the project. + +The following tools were used to generate this project: +- TypeScript Compiler (tsc) + +The following steps were used to generate this project: +- Create project file (`POCNodeTypescript.esproj`). +- Create `launch.json` to enable debugging. +- Install npm packages and create `tsconfig.json`: `npm init && npm i --save-dev eslint typescript @types/node && npx tsc --init --sourceMap true`. +- Create `app.ts`. +- Update `package.json` entry point. +- Update TypeScript build scripts in `package.json`. +- Create `eslint.config.js` to enable linting. +- Add project to solution. +- Write this file. diff --git a/POCNodeTypescript/POCNodeTypescript.esproj b/POCNodeTypescript/POCNodeTypescript.esproj new file mode 100644 index 0000000..a25c7c1 --- /dev/null +++ b/POCNodeTypescript/POCNodeTypescript.esproj @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/POCNodeTypescript/app.d.ts b/POCNodeTypescript/app.d.ts new file mode 100644 index 0000000..1ab2332 --- /dev/null +++ b/POCNodeTypescript/app.d.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env ts-node +/** + * app.ts + * + * Full TypeScript port of the Python BMSSP practical implementation. + * - Graph generator + * - Instrumentation + * - Dijkstra + * - DataStructureD (heap + map, lazy deletion) + * - findPivots (bounded BF-like) + * - basecase (Dijkstra-like limited) + * - Recursive bmssp + * - Test harness + minimal CLI + * + * Usage: + * ts-node bmssp.ts -n 2000 -m 8000 -s 42 + * + * Note: For big graphs, increase node memory/stack or use Node with --stack-size if needed. + */ +export {}; +//# sourceMappingURL=app.d.ts.map \ No newline at end of file diff --git a/POCNodeTypescript/app.d.ts.map b/POCNodeTypescript/app.d.ts.map new file mode 100644 index 0000000..6e2c391 --- /dev/null +++ b/POCNodeTypescript/app.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG"} \ No newline at end of file diff --git a/POCNodeTypescript/app.js b/POCNodeTypescript/app.js new file mode 100644 index 0000000..aa99a24 --- /dev/null +++ b/POCNodeTypescript/app.js @@ -0,0 +1,502 @@ +#!/usr/bin/env ts-node +/** + * app.ts + * + * Full TypeScript port of the Python BMSSP practical implementation. + * - Graph generator + * - Instrumentation + * - Dijkstra + * - DataStructureD (heap + map, lazy deletion) + * - findPivots (bounded BF-like) + * - basecase (Dijkstra-like limited) + * - Recursive bmssp + * - Test harness + minimal CLI + * + * Usage: + * ts-node bmssp.ts -n 2000 -m 8000 -s 42 + * + * Note: For big graphs, increase node memory/stack or use Node with --stack-size if needed. + */ +const INF = Number.POSITIVE_INFINITY; +/* ---------------------------- + Simple min-heap for (key,node) pairs + ---------------------------- */ +class MinHeap { + data = []; + size() { + return this.data.length; + } + isEmpty() { + return this.data.length === 0; + } + peek() { + return this.data.length ? this.data[0] ?? null : null; + } + push(key, node) { + this.data.push({ key, node }); + this.siftUp(this.data.length - 1); + } + pop() { + if (!this.data.length) + return null; + const top = this.data[0]; + const last = this.data.pop(); + if (this.data.length) { + this.data[0] = last; + this.siftDown(0); + } + return top ?? null; + } + siftUp(i) { + while (i > 0) { + const p = (i - 1) >> 1; + if (this.data[i]?.key ?? 0 < (this.data[p] ? this.data[p].key : 0)) { + const temp = this.data[i]; + this.data[i] = this.data[p]; + this.data[p] = temp; + i = p; + } + else + break; + } + } + siftDown(i) { + const n = this.data.length; + while (true) { + const l = i * 2 + 1; + const r = l + 1; + let smallest = i; + if (l < n && (this.data[l]?.key ?? 0) < (this.data[smallest]?.key ?? 0)) + smallest = l; + if (r < n && (this.data[r]?.key ?? 0) < (this.data[smallest]?.key ?? 0)) + smallest = r; + if (smallest === i) + break; + const temp = this.data[i]; + this.data[i] = this.data[smallest]; + this.data[smallest] = temp; + i = smallest; + } + } +} +/* ---------------------------- + RNG (seedable) - mulberry32 + ---------------------------- */ +function mulberry32(seed) { + let t = seed >>> 0; + return function () { + t += 0x6D2B79F5; + let r = Math.imul(t ^ (t >>> 15), t | 1); + r ^= r + Math.imul(r ^ (r >>> 7), r | 61); + return ((r ^ (r >>> 14)) >>> 0) / 4294967296; + }; +} +/* ---------------------------- + Graph generator + ---------------------------- */ +function generateSparseDirectedGraph(n, m, maxW = 100.0, seed) { + const rand = seed !== undefined ? mulberry32(seed) : Math.random; + const graph = new Map(); + for (let i = 0; i < n; i++) + graph.set(i, []); + const edges = []; + // backbone to avoid isolated nodes + for (let i = 1; i < n; i++) { + const u = Math.floor(rand() * i); + const w = rand() * (maxW - 1.0) + 1.0; + graph.get(u).push([i, w]); + edges.push([u, i, w]); + } + const remaining = Math.max(0, m - (n - 1)); + for (let i = 0; i < remaining; i++) { + const u = Math.floor(rand() * n); + const v = Math.floor(rand() * n); + const w = rand() * (maxW - 1.0) + 1.0; + graph.get(u).push([v, w]); + edges.push([u, v, w]); + } + return [graph, edges]; +} +/* ---------------------------- + Instrumentation + ---------------------------- */ +class Instrument { + relaxations = 0; + heapOps = 0; + reset() { + this.relaxations = 0; + this.heapOps = 0; + } +} +/* ---------------------------- + Dijkstra (standard) + ---------------------------- */ +function dijkstra(graph, source, instr) { + instr = instr ?? new Instrument(); + const dist = new Map(); + for (const k of graph.keys()) + dist.set(k, INF); + dist.set(source, 0); + const heap = new MinHeap(); + heap.push(0, source); + instr.heapOps++; + while (!heap.isEmpty()) { + const top = heap.pop(); + const d_u = top.key; + const u = top.node; + instr.heapOps++; + const cur = dist.get(u) ?? INF; + if (d_u > cur) + continue; + for (const [v, w] of graph.get(u) ?? []) { + instr.relaxations++; + const alt = d_u + w; + const dv = dist.get(v) ?? INF; + if (alt < dv) { + dist.set(v, alt); + heap.push(alt, v); + instr.heapOps++; + } + } + } + return dist; +} +/* ---------------------------- + DataStructureD (heap + map) + ---------------------------- */ +class DataStructureD { + heap = new MinHeap(); + best = new Map(); + M; + BUpper; + blockSize; + constructor(M, BUpper, blockSize) { + this.M = Math.max(1, M); + this.BUpper = BUpper; + this.blockSize = blockSize ?? Math.max(1, Math.floor(this.M / 8)); + } + insert(v, key) { + const prev = this.best.get(v); + if (prev === undefined || key < prev) { + this.best.set(v, key); + this.heap.push(key, v); + } + } + batchPrepend(iterablePairs) { + for (const [v, key] of iterablePairs) + this.insert(v, key); + } + cleanup() { + while (!this.heap.isEmpty()) { + const top = this.heap.peek(); + const node = top.node; + const key = top.key; + const cur = this.best.get(node); + if (cur === undefined || cur !== key) { + this.heap.pop(); // stale + } + else + break; + } + } + empty() { + this.cleanup(); + return this.heap.isEmpty(); + } + pull() { + this.cleanup(); + if (this.heap.isEmpty()) + throw new Error("pull from empty D"); + const Bi = this.heap.peek().key; + const Si = new Set(); + while (!this.heap.isEmpty() && Si.size < this.blockSize) { + const top = this.heap.pop(); + const key = top.key; + const v = top.node; + const cur = this.best.get(v); + if (cur !== undefined && cur === key) { + Si.add(v); + this.best.delete(v); + } + } + return [Bi, Si]; + } +} +/* ---------------------------- + findPivots (bounded BF-like) + ---------------------------- */ +function findPivots(graph, dist, S, B, n, k_steps, p_limit, instr) { + instr = instr ?? new Instrument(); + const S_filtered = []; + for (const v of S) { + const dv = dist.get(v) ?? INF; + if (dv < B) + S_filtered.push(v); + } + let P; + if (S_filtered.length === 0) { + // fallback: choose up to p_limit arbitrary from S + P = new Set(); + if (S.size) { + let count = 0; + for (const v of S) { + P.add(v); + if (++count >= Math.max(1, Math.min(S.size, p_limit))) + break; + } + } + } + else { + S_filtered.sort((a, b) => (dist.get(a) ?? INF) - (dist.get(b) ?? INF)); + const take = Math.max(1, Math.min(S_filtered.length, p_limit)); + P = new Set(S_filtered.slice(0, take)); + } + const source_frontier = P.size ? new Set(P) : new Set(S); + const discovered = new Set(source_frontier); + let frontier = new Set(source_frontier); + for (let step = 0; step < Math.max(1, k_steps); step++) { + if (!frontier.size) + break; + const nextFront = new Set(); + for (const u of frontier) { + const du = dist.get(u) ?? INF; + if (du >= B) + continue; + for (const [v, w] of graph.get(u) ?? []) { + instr.relaxations++; + const nd = du + w; + if (nd < B && !discovered.has(v)) { + discovered.add(v); + nextFront.add(v); + } + } + } + frontier = nextFront; + } + const W = new Set(discovered); + if (!P.size && S.size) + P.add(S.values().next().value ?? 0); + return [P, W]; +} +/* ---------------------------- + basecase (Dijkstra-like limited) + ---------------------------- */ +function basecase(graph, dist, B, S, k, instr) { + instr = instr ?? new Instrument(); + if (!S.size) + return [B, new Set()]; + // choose x with smallest dist + let x = null; + let bestd = INF; + for (const v of S) { + const dv = dist.get(v) ?? INF; + if (dv < bestd) { + bestd = dv; + x = v; + } + } + if (x === null) + return [B, new Set()]; + const heap = new MinHeap(); + const start_d = dist.get(x) ?? INF; + heap.push(start_d, x); + instr.heapOps++; + const Uo = new Set(); + while (!heap.isEmpty() && Uo.size < (k + 1)) { + const top = heap.pop(); + instr.heapOps++; + const d_u = top.key; + const u = top.node; + const cur = dist.get(u) ?? INF; + if (d_u > cur) + continue; + if (!Uo.has(u)) + Uo.add(u); + for (const [v, w] of graph.get(u) ?? []) { + instr.relaxations++; + const newd = (dist.get(u) ?? INF) + w; + const dv = dist.get(v) ?? INF; + if (newd < dv && newd < B) { + dist.set(v, newd); + heap.push(newd, v); + instr.heapOps++; + } + } + } + if (Uo.size <= k) + return [B, Uo]; + const finiteDists = []; + for (const v of Uo) { + const dv = dist.get(v) ?? INF; + if (Number.isFinite(dv)) + finiteDists.push(dv); + } + if (!finiteDists.length) + return [B, new Set()]; + const maxd = Math.max(...finiteDists); + const Ufiltered = new Set(Array.from(Uo).filter(v => (dist.get(v) ?? INF) < maxd)); + return [maxd, Ufiltered]; +} +/* ---------------------------- + bmssp (recursive) + ---------------------------- */ +function bmssp(graph, dist, edges, l, B, S, n, instr) { + instr = instr ?? new Instrument(); + // parameter heuristics + let t_param, k_param; + if (n <= 2) { + t_param = 1; + k_param = 2; + } + else { + const ln = Math.log(Math.max(3, n)); + t_param = Math.max(1, Math.round(Math.pow(ln, 2.0 / 3.0))); + k_param = Math.max(2, Math.round(Math.pow(ln, 1.0 / 3.0))); + } + if (l <= 0) { + if (!S.size) + return [B, new Set()]; + return basecase(graph, dist, B, S, k_param, instr); + } + const p_limit = Math.max(1, 1 << Math.min(10, t_param)); + const k_steps = Math.max(1, k_param); + const [P, W] = findPivots(graph, dist, S, B, n, k_steps, p_limit, instr); + const M = 1 << Math.max(0, (l - 1) * t_param); + const D = new DataStructureD(M, B, Math.max(1, Math.min(P.size || 1, 64))); + for (const x of P) { + D.insert(x, dist.get(x) ?? INF); + } + const B_prime_initial = (P.size ? Math.min(...Array.from(P).map(x => dist.get(x) ?? INF)) : B); + const U = new Set(); + const B_prime_sub_values = []; + let loop_guard = 0; + const limit = k_param * (1 << (l * Math.max(1, t_param))); + while (U.size < limit && !D.empty()) { + loop_guard++; + if (loop_guard > 20000) + break; + let Bi, Si; + try { + [Bi, Si] = D.pull(); + } + catch (e) { + break; + } + const [B_prime_sub, Ui] = bmssp(graph, dist, edges, l - 1, Bi, Si, n, instr); + B_prime_sub_values.push(B_prime_sub); + for (const u of Ui) + U.add(u); + // K_for_batch as Set of tuples -> represent as Map to avoid duplicates + const K_for_batchArr = []; + for (const u of Ui) { + const du = dist.get(u) ?? INF; + if (!Number.isFinite(du)) + continue; + for (const [v, w_uv] of graph.get(u) ?? []) { + instr.relaxations++; + const newd = du + w_uv; + const dv = dist.get(v) ?? INF; + if (newd <= dv) { + dist.set(v, newd); + if (Bi <= newd && newd < B) { + D.insert(v, newd); + } + else if (B_prime_sub <= newd && newd < Bi) { + K_for_batchArr.push([v, newd]); + } + } + } + } + for (const x of Si) { + const dx = dist.get(x) ?? INF; + if (B_prime_sub <= dx && dx < Bi) + K_for_batchArr.push([x, dx]); + } + if (K_for_batchArr.length) + D.batchPrepend(K_for_batchArr); + } + const B_prime_final = (B_prime_sub_values.length ? Math.min(B_prime_initial, ...B_prime_sub_values) : B_prime_initial); + const U_final = new Set(U); + for (const x of W) { + if ((dist.get(x) ?? INF) < B_prime_final) + U_final.add(x); + } + return [B_prime_final, U_final]; +} +/* ---------------------------- + Test harness (runSingleTest) + ---------------------------- */ +function runSingleTest(n, m, seed = 0, source = 0) { + console.log(`Generating graph: n=${n}, m=${m}, seed=${seed}`); + const [graph, edges] = generateSparseDirectedGraph(n, m, 100.0, seed); + const avgDeg = Array.from(graph.values()).reduce((s, arr) => s + arr.length, 0) / n; + console.log(`Graph generated. avg out-degree ≈ ${avgDeg.toFixed(3)}`); + // Dijkstra + const instr_dij = new Instrument(); + const t0 = Date.now(); + const dist_dij = dijkstra(graph, source, instr_dij); + const t1 = Date.now(); + console.log(`Dijkstra: time=${((t1 - t0) / 1000).toFixed(6)}s, relaxations=${instr_dij.relaxations}, heap_ops=${instr_dij.heapOps}, reachable=${Array.from(dist_dij.values()).filter(v => Number.isFinite(v)).length}`); + // BMSSP + const dist_bm = new Map(); + for (const k of graph.keys()) + dist_bm.set(k, INF); + dist_bm.set(source, 0); + const instr_bm = new Instrument(); + let l; + if (n <= 2) + l = 1; + else { + const t_guess = Math.max(1, Math.round(Math.pow(Math.log(Math.max(3, n)), 2.0 / 3.0))); + l = Math.max(1, Math.max(1, Math.round(Math.log(Math.max(3, n)) / t_guess))); + } + console.log(`BMSSP params: top-level l=${l}`); + const t2 = Date.now(); + const [Bp, U_final] = bmssp(graph, dist_bm, edges, l, INF, new Set([source]), n, instr_bm); + const t3 = Date.now(); + console.log(`BMSSP: time=${((t3 - t2) / 1000).toFixed(6)}s, relaxations=${instr_bm.relaxations}, reachable=${Array.from(dist_bm.values()).filter(v => Number.isFinite(v)).length}, B'=${Bp}, |U_final|=${U_final.size}`); + const diffs = []; + for (const v of graph.keys()) { + const dv = dist_dij.get(v) ?? INF; + const db = dist_bm.get(v) ?? INF; + if (Number.isFinite(dv) && Number.isFinite(db)) + diffs.push(Math.abs(dv - db)); + } + const maxDiff = diffs.length ? Math.max(...diffs) : 0; + console.log(`Distance agreement (max abs diff on commonly reachable nodes): ${maxDiff.toExponential(6)}`); + return { + n, m, seed, + dijkstra_time: (t1 - t0) / 1000, + dijkstra_relax: instr_dij.relaxations, + bmssp_time: (t3 - t2) / 1000, + bmssp_relax: instr_bm.relaxations, + dijkstra_reachable: Array.from(dist_dij.values()).filter(v => Number.isFinite(v)).length, + bmssp_reachable: Array.from(dist_bm.values()).filter(v => Number.isFinite(v)).length, + max_diff: maxDiff + }; +} +/* ---------------------------- + CLI parsing + ---------------------------- */ +function parseArgs(argv) { + let n = 200000, m = 800000, seed = 0; + for (let i = 2; i < argv.length; i++) { + const a = argv[i]; + if ((a === '-n' || a === '--nodes') && i + 1 < argv.length) { + n = parseInt(argv[++i] ?? '', 10); + } + else if ((a === '-m' || a === '--edges') && i + 1 < argv.length) { + m = parseInt(argv[++i] ?? '', 10); + } + else if ((a === '-s' || a === '--seed') && i + 1 < argv.length) { + seed = parseInt(argv[++i] ?? '', 10); + } + } + return { n, m, seed }; +} +const { n, m, seed } = parseArgs(process.argv); +// For quicker local test, you may want to reduce defaults: +// const testDefaults = { n: 2000, m: 8000, seed: 42 }; +runSingleTest(n, m, seed, 0); +export {}; +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/POCNodeTypescript/app.js.map b/POCNodeTypescript/app.js.map new file mode 100644 index 0000000..12e4d9e --- /dev/null +++ b/POCNodeTypescript/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/POCNodeTypescript/app.ts b/POCNodeTypescript/app.ts new file mode 100644 index 0000000..41c6e73 --- /dev/null +++ b/POCNodeTypescript/app.ts @@ -0,0 +1,539 @@ +#!/usr/bin/env ts-node +/** + * app.ts + * + * Full TypeScript port of the Python BMSSP practical implementation. + * - Graph generator + * - Instrumentation + * - Dijkstra + * - DataStructureD (heap + map, lazy deletion) + * - findPivots (bounded BF-like) + * - basecase (Dijkstra-like limited) + * - Recursive bmssp + * - Test harness + minimal CLI + * + * Usage: + * ts-node bmssp.ts -n 2000 -m 8000 -s 42 + * + * Note: For big graphs, increase node memory/stack or use Node with --stack-size if needed. + */ + +type NodeId = number; +type Weight = number; +type Edge = [NodeId, NodeId, Weight]; +type Graph = Map>; + +const INF = Number.POSITIVE_INFINITY; + +/* ---------------------------- + Simple min-heap for (key,node) pairs + ---------------------------- */ +class MinHeap { + private data: Array<{ key: number; node: number }> = []; + + size(): number { + return this.data.length; + } + isEmpty(): boolean { + return this.data.length === 0; + } + peek(): { key: number; node: number } | null { + return this.data.length ? this.data[0] ?? null : null; + } + push(key: number, node: number) { + this.data.push({ key, node }); + this.siftUp(this.data.length - 1); + } + pop(): { key: number; node: number } | null { + if (!this.data.length) return null; + const top = this.data[0]; + const last = this.data.pop()!; + if (this.data.length) { + this.data[0] = last; + this.siftDown(0); + } + return top ?? null; + } + private siftUp(i: number) { + while (i > 0) { + const p = (i - 1) >> 1; + if (this.data[i]?.key ?? 0 < (this.data[p] ? this.data[p].key : 0)) { + const temp = this.data[i]!; + this.data[i] = this.data[p]!; + this.data[p] = temp; + i = p; + } else break; + } + } + private siftDown(i: number) { + const n = this.data.length; + while (true) { + const l = i * 2 + 1; + const r = l + 1; + let smallest = i; + if (l < n && (this.data[l]?.key ?? 0) < (this.data[smallest]?.key ?? 0)) smallest = l; + if (r < n && (this.data[r]?.key ?? 0) < (this.data[smallest]?.key ?? 0)) smallest = r; + if (smallest === i) break; + const temp = this.data[i]!; + this.data[i] = this.data[smallest]!; + this.data[smallest] = temp; + i = smallest; + } + } +} + +/* ---------------------------- + RNG (seedable) - mulberry32 + ---------------------------- */ +function mulberry32(seed: number) { + let t = seed >>> 0; + return function () { + t += 0x6D2B79F5; + let r = Math.imul(t ^ (t >>> 15), t | 1); + r ^= r + Math.imul(r ^ (r >>> 7), r | 61); + return ((r ^ (r >>> 14)) >>> 0) / 4294967296; + }; +} + +/* ---------------------------- + Graph generator + ---------------------------- */ +function generateSparseDirectedGraph(n: number, m: number, maxW = 100.0, seed?: number): [Graph, Edge[]] { + const rand = seed !== undefined ? mulberry32(seed) : Math.random; + const graph: Graph = new Map(); + for (let i = 0; i < n; i++) graph.set(i, []); + const edges: Edge[] = []; + + // backbone to avoid isolated nodes + for (let i = 1; i < n; i++) { + const u = Math.floor(rand() * i); + const w = rand() * (maxW - 1.0) + 1.0; + graph.get(u)!.push([i, w]); + edges.push([u, i, w]); + } + + const remaining = Math.max(0, m - (n - 1)); + for (let i = 0; i < remaining; i++) { + const u = Math.floor(rand() * n); + const v = Math.floor(rand() * n); + const w = rand() * (maxW - 1.0) + 1.0; + graph.get(u)!.push([v, w]); + edges.push([u, v, w]); + } + return [graph, edges]; +} + +/* ---------------------------- + Instrumentation + ---------------------------- */ +class Instrument { + relaxations = 0; + heapOps = 0; + reset() { + this.relaxations = 0; + this.heapOps = 0; + } +} + +/* ---------------------------- + Dijkstra (standard) + ---------------------------- */ +function dijkstra(graph: Graph, source: NodeId, instr?: Instrument): Map { + instr = instr ?? new Instrument(); + const dist: Map = new Map(); + for (const k of graph.keys()) dist.set(k, INF); + dist.set(source, 0); + + const heap = new MinHeap(); + heap.push(0, source); + instr.heapOps++; + + while (!heap.isEmpty()) { + const top = heap.pop()!; + const d_u = top.key; + const u = top.node; + instr.heapOps++; + const cur = dist.get(u) ?? INF; + if (d_u > cur) continue; + + for (const [v, w] of graph.get(u) ?? []) { + instr.relaxations++; + const alt = d_u + w; + const dv = dist.get(v) ?? INF; + if (alt < dv) { + dist.set(v, alt); + heap.push(alt, v); + instr.heapOps++; + } + } + } + return dist; +} + +/* ---------------------------- + DataStructureD (heap + map) + ---------------------------- */ +class DataStructureD { + private heap = new MinHeap(); + private best: Map = new Map(); + private M: number; + private BUpper: number; + private blockSize: number; + + constructor(M: number, BUpper: number, blockSize?: number) { + this.M = Math.max(1, M); + this.BUpper = BUpper; + this.blockSize = blockSize ?? Math.max(1, Math.floor(this.M / 8)); + } + + insert(v: NodeId, key: number) { + const prev = this.best.get(v); + if (prev === undefined || key < prev) { + this.best.set(v, key); + this.heap.push(key, v); + } + } + + batchPrepend(iterablePairs: Iterable<[NodeId, number]>) { + for (const [v, key] of iterablePairs) this.insert(v, key); + } + + private cleanup() { + while (!this.heap.isEmpty()) { + const top = this.heap.peek()!; + const node = top.node; + const key = top.key; + const cur = this.best.get(node); + if (cur === undefined || cur !== key) { + this.heap.pop(); // stale + } else break; + } + } + + empty(): boolean { + this.cleanup(); + return this.heap.isEmpty(); + } + + pull(): [number, Set] { + this.cleanup(); + if (this.heap.isEmpty()) throw new Error("pull from empty D"); + const Bi = this.heap.peek()!.key; + const Si = new Set(); + while (!this.heap.isEmpty() && Si.size < this.blockSize) { + const top = this.heap.pop()!; + const key = top.key; + const v = top.node; + const cur = this.best.get(v); + if (cur !== undefined && cur === key) { + Si.add(v); + this.best.delete(v); + } + } + return [Bi, Si]; + } +} + +/* ---------------------------- + findPivots (bounded BF-like) + ---------------------------- */ +function findPivots( + graph: Graph, + dist: Map, + S: Set, + B: number, + n: number, + k_steps: number, + p_limit: number, + instr?: Instrument +): [Set, Set] { + instr = instr ?? new Instrument(); + + const S_filtered: NodeId[] = []; + for (const v of S) { + const dv = dist.get(v) ?? INF; + if (dv < B) S_filtered.push(v); + } + + let P: Set; + if (S_filtered.length === 0) { + // fallback: choose up to p_limit arbitrary from S + P = new Set(); + if (S.size) { + let count = 0; + for (const v of S) { + P.add(v); + if (++count >= Math.max(1, Math.min(S.size, p_limit))) break; + } + } + } else { + S_filtered.sort((a, b) => (dist.get(a) ?? INF) - (dist.get(b) ?? INF)); + const take = Math.max(1, Math.min(S_filtered.length, p_limit)); + P = new Set(S_filtered.slice(0, take)); + } + + const source_frontier = P.size ? new Set(P) : new Set(S); + const discovered = new Set(source_frontier); + let frontier = new Set(source_frontier); + + for (let step = 0; step < Math.max(1, k_steps); step++) { + if (!frontier.size) break; + const nextFront = new Set(); + for (const u of frontier) { + const du = dist.get(u) ?? INF; + if (du >= B) continue; + for (const [v, w] of graph.get(u) ?? []) { + instr.relaxations++; + const nd = du + w; + if (nd < B && !discovered.has(v)) { + discovered.add(v); + nextFront.add(v); + } + } + } + frontier = nextFront; + } + + const W = new Set(discovered); + if (!P.size && S.size) P.add(S.values().next().value ?? 0); + return [P, W]; +} + +/* ---------------------------- + basecase (Dijkstra-like limited) + ---------------------------- */ +function basecase( + graph: Graph, + dist: Map, + B: number, + S: Set, + k: number, + instr?: Instrument +): [number, Set] { + instr = instr ?? new Instrument(); + if (!S.size) return [B, new Set()]; + + // choose x with smallest dist + let x: NodeId | null = null; + let bestd = INF; + for (const v of S) { + const dv = dist.get(v) ?? INF; + if (dv < bestd) { bestd = dv; x = v; } + } + if (x === null) return [B, new Set()]; + + const heap = new MinHeap(); + const start_d = dist.get(x) ?? INF; + heap.push(start_d, x); + instr.heapOps++; + + const Uo = new Set(); + + while (!heap.isEmpty() && Uo.size < (k + 1)) { + const top = heap.pop()!; + instr.heapOps++; + const d_u = top.key; + const u = top.node; + const cur = dist.get(u) ?? INF; + if (d_u > cur) continue; + if (!Uo.has(u)) Uo.add(u); + + for (const [v, w] of graph.get(u) ?? []) { + instr.relaxations++; + const newd = (dist.get(u) ?? INF) + w; + const dv = dist.get(v) ?? INF; + if (newd < dv && newd < B) { + dist.set(v, newd); + heap.push(newd, v); + instr.heapOps++; + } + } + } + + if (Uo.size <= k) return [B, Uo]; + + const finiteDists: number[] = []; + for (const v of Uo) { + const dv = dist.get(v) ?? INF; + if (Number.isFinite(dv)) finiteDists.push(dv); + } + if (!finiteDists.length) return [B, new Set()]; + const maxd = Math.max(...finiteDists); + const Ufiltered = new Set(Array.from(Uo).filter(v => (dist.get(v) ?? INF) < maxd)); + return [maxd, Ufiltered]; +} + +/* ---------------------------- + bmssp (recursive) + ---------------------------- */ +function bmssp( + graph: Graph, + dist: Map, + edges: Edge[], + l: number, + B: number, + S: Set, + n: number, + instr?: Instrument +): [number, Set] { + instr = instr ?? new Instrument(); + + // parameter heuristics + let t_param: number, k_param: number; + if (n <= 2) { t_param = 1; k_param = 2; } + else { + const ln = Math.log(Math.max(3, n)); + t_param = Math.max(1, Math.round(Math.pow(ln, 2.0 / 3.0))); + k_param = Math.max(2, Math.round(Math.pow(ln, 1.0 / 3.0))); + } + + if (l <= 0) { + if (!S.size) return [B, new Set()]; + return basecase(graph, dist, B, S, k_param, instr); + } + + const p_limit = Math.max(1, 1 << Math.min(10, t_param)); + const k_steps = Math.max(1, k_param); + + const [P, W] = findPivots(graph, dist, S, B, n, k_steps, p_limit, instr); + + const M = 1 << Math.max(0, (l - 1) * t_param); + const D = new DataStructureD(M, B, Math.max(1, Math.min(P.size || 1, 64))); + + for (const x of P) { + D.insert(x, dist.get(x) ?? INF); + } + + const B_prime_initial = (P.size ? Math.min(...Array.from(P).map(x => dist.get(x) ?? INF)) : B); + + const U = new Set(); + const B_prime_sub_values: number[] = []; + + let loop_guard = 0; + const limit = k_param * (1 << (l * Math.max(1, t_param))); + + while (U.size < limit && !D.empty()) { + loop_guard++; + if (loop_guard > 20000) break; + + let Bi: number, Si: Set; + try { + [Bi, Si] = D.pull(); + } catch (e) { + break; + } + + const [B_prime_sub, Ui] = bmssp(graph, dist, edges, l - 1, Bi, Si, n, instr); + B_prime_sub_values.push(B_prime_sub); + for (const u of Ui) U.add(u); + + // K_for_batch as Set of tuples -> represent as Map to avoid duplicates + const K_for_batchArr: Array<[NodeId, number]> = []; + + for (const u of Ui) { + const du = dist.get(u) ?? INF; + if (!Number.isFinite(du)) continue; + for (const [v, w_uv] of graph.get(u) ?? []) { + instr.relaxations++; + const newd = du + w_uv; + const dv = dist.get(v) ?? INF; + if (newd <= dv) { + dist.set(v, newd); + if (Bi <= newd && newd < B) { + D.insert(v, newd); + } else if (B_prime_sub <= newd && newd < Bi) { + K_for_batchArr.push([v, newd]); + } + } + } + } + + for (const x of Si) { + const dx = dist.get(x) ?? INF; + if (B_prime_sub <= dx && dx < Bi) K_for_batchArr.push([x, dx]); + } + + if (K_for_batchArr.length) D.batchPrepend(K_for_batchArr); + } + + const B_prime_final = (B_prime_sub_values.length ? Math.min(B_prime_initial, ...B_prime_sub_values) : B_prime_initial); + + const U_final = new Set(U); + for (const x of W) { + if ((dist.get(x) ?? INF) < B_prime_final) U_final.add(x); + } + return [B_prime_final, U_final]; +} + +/* ---------------------------- + Test harness (runSingleTest) + ---------------------------- */ +function runSingleTest(n: number, m: number, seed = 0, source = 0) { + console.log(`Generating graph: n=${n}, m=${m}, seed=${seed}`); + const [graph, edges] = generateSparseDirectedGraph(n, m, 100.0, seed); + const avgDeg = Array.from(graph.values()).reduce((s, arr) => s + arr.length, 0) / n; + console.log(`Graph generated. avg out-degree ≈ ${avgDeg.toFixed(3)}`); + + // Dijkstra + const instr_dij = new Instrument(); + const t0 = Date.now(); + const dist_dij = dijkstra(graph, source, instr_dij); + const t1 = Date.now(); + console.log(`Dijkstra: time=${((t1 - t0) / 1000).toFixed(6)}s, relaxations=${instr_dij.relaxations}, heap_ops=${instr_dij.heapOps}, reachable=${Array.from(dist_dij.values()).filter(v => Number.isFinite(v)).length}`); + + // BMSSP + const dist_bm: Map = new Map(); + for (const k of graph.keys()) dist_bm.set(k, INF); + dist_bm.set(source, 0); + const instr_bm = new Instrument(); + let l: number; + if (n <= 2) l = 1; + else { + const t_guess = Math.max(1, Math.round(Math.pow(Math.log(Math.max(3, n)), 2.0 / 3.0))); + l = Math.max(1, Math.max(1, Math.round(Math.log(Math.max(3, n)) / t_guess))); + } + console.log(`BMSSP params: top-level l=${l}`); + + const t2 = Date.now(); + const [Bp, U_final] = bmssp(graph, dist_bm, edges, l, INF, new Set([source]), n, instr_bm); + const t3 = Date.now(); + console.log(`BMSSP: time=${((t3 - t2) / 1000).toFixed(6)}s, relaxations=${instr_bm.relaxations}, reachable=${Array.from(dist_bm.values()).filter(v => Number.isFinite(v)).length}, B'=${Bp}, |U_final|=${U_final.size}`); + + const diffs: number[] = []; + for (const v of graph.keys()) { + const dv = dist_dij.get(v) ?? INF; + const db = dist_bm.get(v) ?? INF; + if (Number.isFinite(dv) && Number.isFinite(db)) diffs.push(Math.abs(dv - db)); + } + const maxDiff = diffs.length ? Math.max(...diffs) : 0; + console.log(`Distance agreement (max abs diff on commonly reachable nodes): ${maxDiff.toExponential(6)}`); + return { + n, m, seed, + dijkstra_time: (t1 - t0) / 1000, + dijkstra_relax: instr_dij.relaxations, + bmssp_time: (t3 - t2) / 1000, + bmssp_relax: instr_bm.relaxations, + dijkstra_reachable: Array.from(dist_dij.values()).filter(v => Number.isFinite(v)).length, + bmssp_reachable: Array.from(dist_bm.values()).filter(v => Number.isFinite(v)).length, + max_diff: maxDiff + }; +} + +/* ---------------------------- + CLI parsing + ---------------------------- */ +function parseArgs(argv: string[]) { + let n = 200000, m = 800000, seed = 0; + for (let i = 2; i < argv.length; i++) { + const a = argv[i]; + if ((a === '-n' || a === '--nodes') && i + 1 < argv.length) { n = parseInt(argv[++i] ?? '', 10); } + else if ((a === '-m' || a === '--edges') && i + 1 < argv.length) { m = parseInt(argv[++i] ?? '', 10); } + else if ((a === '-s' || a === '--seed') && i + 1 < argv.length) { seed = parseInt(argv[++i] ?? '', 10); } + } + return { n, m, seed }; +} + +const { n, m, seed } = parseArgs(process.argv); +// For quicker local test, you may want to reduce defaults: +// const testDefaults = { n: 2000, m: 8000, seed: 42 }; +runSingleTest(n, m, seed, 0); diff --git a/POCNodeTypescript/eslint.config.js b/POCNodeTypescript/eslint.config.js new file mode 100644 index 0000000..c91642f --- /dev/null +++ b/POCNodeTypescript/eslint.config.js @@ -0,0 +1,7 @@ +module.exports = [ + { + rules: { + // Add rules here. + } + } +]; \ No newline at end of file diff --git a/POCNodeTypescript/package-lock.json b/POCNodeTypescript/package-lock.json new file mode 100644 index 0000000..4c5dc71 --- /dev/null +++ b/POCNodeTypescript/package-lock.json @@ -0,0 +1,1124 @@ +{ + "name": "pocnodetypescript", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pocnodetypescript", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@datastructures-js/priority-queue": "^6.3.4" + }, + "devDependencies": { + "@types/node": "^24.3.1", + "eslint": "^9.35.0", + "typescript": "^5.9.2" + } + }, + "node_modules/@datastructures-js/heap": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@datastructures-js/heap/-/heap-4.3.5.tgz", + "integrity": "sha512-zgO8KO26WnZdCxe72Ck+55mhUYmw/1CL/TueKSIIU6aqoOcXAqeaL2kRldJcXtCdJ43rqgIKYdEkiL07sQ+2VQ==", + "license": "MIT" + }, + "node_modules/@datastructures-js/priority-queue": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@datastructures-js/priority-queue/-/priority-queue-6.3.4.tgz", + "integrity": "sha512-m8Jt1OkwyHsKRz6L1FWerz3Hw8iU5vqCdBI6khC8YNssF1kXKX6cs8Xc9Zt9ocPCxO/vZhAu/KnQ+uQ4Btw6AA==", + "license": "MIT", + "dependencies": { + "@datastructures-js/heap": "^4.3.3" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/POCNodeTypescript/package.json b/POCNodeTypescript/package.json new file mode 100644 index 0000000..916b040 --- /dev/null +++ b/POCNodeTypescript/package.json @@ -0,0 +1,23 @@ +{ + "name": "pocnodetypescript", + "version": "1.0.0", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc --build", + "clean": "tsc --build --clean" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@types/node": "^24.3.1", + "eslint": "^9.35.0", + "typescript": "^5.9.2" + }, + "dependencies": { + "@datastructures-js/priority-queue": "^6.3.4" + }, + "type": "module" +} diff --git a/POCNodeTypescript/tsconfig.json b/POCNodeTypescript/tsconfig.json new file mode 100644 index 0000000..9b4737f --- /dev/null +++ b/POCNodeTypescript/tsconfig.json @@ -0,0 +1,36 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "exactOptionalPropertyTypes": true, + "isolatedModules": true, + // For nodejs: + "lib": [ "esnext" ], + // File Layout + // "rootDir": "./src", + // "outDir": "./dist", + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "moduleDetection": "force", + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "noUncheckedSideEffectImports": true, + "skipLibCheck": true, + // Other Outputs + "sourceMap": true, + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + // Recommended Options + "strict": true, + "target": "esnext", + "types": [ "node" ], + "verbatimModuleSyntax": true + } +} diff --git a/POCNodeTypescript/tsconfig.tsbuildinfo b/POCNodeTypescript/tsconfig.tsbuildinfo new file mode 100644 index 0000000..81c6866 --- /dev/null +++ b/POCNodeTypescript/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./app.ts"],"version":"5.9.2"} \ No newline at end of file diff --git a/README.md b/README.md index 5fea3c4..96e93da 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,42 @@ Distance agreement (max abs diff on commonly reachable nodes): 8.540939e+01 [alphaxiv](https://papers-pdfs.assets.alphaxiv.org/2504.17033v2.pdf) + +# POC Dotnet Project + +## Overview +This project demonstrates a .NET-based implementation of a graph processing application. It includes a CLI interface for configuring the number of nodes, edges, and a random seed for generating graph data. + +## Features +- Command-line interface for input configuration. +- Efficient graph processing using .NET 9 features. +- Supports large-scale graph data with customizable parameters. + +## Requirements +- .NET 9 SDK +- C# 13.0 + +## Usage + +### Build the Project +1. Clone the repository. +2. Open the project in Visual Studio. +3. Build the solution. + +### Run the Application +Use the following command to run the application: +### Example +### Parameters +- `-n` or `--nodes`: Number of nodes in the graph (default: 200,000). +- `-m` or `--edges`: Number of edges in the graph (default: 800,000). +- `-s` or `--seed`: Random seed for graph generation (default: 0). + +### C# Output => running faster than python +```shell +Generating graph: n=200000, m=800000, seed=0 +Graph generated. avg out-degree ≈ 4.000 +Dijkstra: time=0.356301s, relaxations=800000, heap_ops=533202, reachable=200000 +BMSSP params: top-level l=2 +BMSSP: time=0.054503s, relaxations=26910, reachable=8788, B'=0, |U_final|=2048 +Distance agreement (max abs diff on commonly reachable nodes): 8.073533E+001 +```