Skip to content

Ng unreacheable - #22

Open
legraina wants to merge 55 commits into
mainfrom
ng-unreacheable
Open

Ng unreacheable#22
legraina wants to merge 55 commits into
mainfrom
ng-unreacheable

Conversation

@legraina

Copy link
Copy Markdown
Contributor

No description provided.

legraina and others added 30 commits March 10, 2026 12:00
Extract DFS-with-backtracking machinery shared by Greedy and Tabu into a
new BacktrackingDiveAlgorithm base, owning path_, dive primitives
(extend_label, backtrack), path lifecycle, and a child_comparator hook.

Pull tabu-list bookkeeping shared by TabuSearchAlgorithm and
DiversificationSearch (arc_id->tenure map, adaptive extra tenure with
optional jitter, aging with optional on-expire callback, grow/shrink)
into a new TabuList helper used by composition in both.

New TabuSearchAlgorithm: peer of GreedyAlgorithm, episodic dives with
arc tabu memory + aspiration. Customises only select_children and
main_loop.

child_comparator on the base: when graph_->are_nodes_sorted(), prefer
forward arcs (destination pos > parent pos) before backward ones; within
each group, cost-ascending. Both Greedy and Tabu inherit this so dives
follow the user-supplied node ordering's intended tour layout instead of
jumping cheaply but late and having to backtrack to early required.

Slim refactor of GreedyAlgorithm onto the new base. DiversificationSearch
now uses TabuList. Minor tweaks in dominance_algorithm.hpp and
pulling_dominance_algorithm.hpp to align with the refactor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…er based on size, Add a solver status, Add a pointer to the previous label with a ref count to easily rebuild a path.
- Replace std::list<size_t> with std::vector<size_t> for
  Solution::path_node_ids/path_arc_ids and VRP Path::visited_nodes,
  eliminating cache-hostile linked-list traversal on the hot path.
- Extend CI matrix to test Python 3.11/3.12/3.13 on macOS and Linux
  with pytest --tb=short; exclude Debug builds for 3.11 and 3.13.
- Add py.typed PEP 561 marker and _core/*.pyi stub files so IDEs
  provide autocompletion for all C++-backed types.
- Update CMakeLists.txt to copy and install .pyi and py.typed alongside
  the Python package.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Four improvements to LabelBuckets<BucketResource, SortResource, ResourceType>:

1. buckets_ list → vector: cache-friendly bucket iteration and random-access
   indexing required by the binary search helpers below.

2. O(log B) bucket lookup: find_first_not_after / find_first_before binary-
   search helpers replace the O(B) linear scans in add_label (find insertion
   bucket), remove_dominated_labels (find first relevant bucket), and
   is_dominated (find last relevant bucket).

3. O(1) erase_label: begin_label_to_bucket_idx_ (unordered_map<Label*, size_t>)
   maps each current bucket-begin label to its bucket index. A single map
   lookup replaces the O(B) std::find_if scan over all bucket begins. The map
   is maintained by insert_bucket / remove_bucket / update_bucket_begin (O(B)
   index-shift cost there, but bucket operations are O(B) total vs. O(N)
   erase_label calls).

4. Symmetric instrumentation: num_dom_labels_ / num_dom_visited_ counters added
   to is_dominated (mirroring the existing remove_dominated stats). print_labels
   reports both visit ratios. Also exposes suggest_range(target_buckets) which
   uses the peak simultaneous bucket count to estimate the resource spread and
   suggest a calibrated range_buckets for subsequent phases.

Bug fixed during implementation: the ++removed increment in
remove_dominated_labels was placed after the break that fires when a full
bucket is emptied, causing dominated labels to go uncounted in that path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add four unit tests covering binary-search and map-maintenance code paths
(RemoveDominatedMultiBucket, IsDominatedMultiBucket, EraseBeginMultiBucket,
SuggestRange). Add BucketS and BucketP benchmark columns by running a second
independent solve per instance with LabelBuckets + SimpleDominanceAlgorithm
and PullingDominanceAlgorithm.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r_each_arc non-const overload

- ExtraSolver type-erased struct + run_algorithm public wrapper enable
  bucket-container algorithms to participate in the same CG solve as
  list-container algorithms, sharing duals and cross-checking costs
- run_boost normalized at top of solve; warns + disables if Boost not
  compiled in; drives num_total_algos and algo_index correctly
- collect_solutions cross-checks costs (not counts) between optimal algos
- benchmark uses --boost flag; bucket algorithms run as ExtraSolvers
- Graph::for_each_arc gains non-const overload for update_reduced_costs
- CMakeLists: Boost optional at compile time via RCSPP_VRP_HAS_BOOST

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…container

Parent-pointer O(hops) reconstruction:
- Label gains public parent_ and child_refcount_ fields (initialized to
  null/0 in both constructors and reset_label)
- LabelPool::release_label checks child_refcount_; if > 0, defers
  recycling. do_release() cascades up the parent chain when a label's
  last child is gone, avoiding any zombie memory retention
- DominanceAlgorithm::extend_label sets parent_ and bumps child_refcount_
  only when the child survives feasibility and dominance checks
- get_path_arc_ids replaced with a simple parent-pointer walk
- GreedyAlgorithm unaffected: never calls DominanceAlgorithm::extend_label

AlgorithmBaseParams::with_container:
- Forward-declare AlgorithmParams before AlgorithmBaseParams so the
  member function template can name its return type
- Define with_container out-of-line after AlgorithmParams is complete
- Allows: base.with_container<LabelList<RT>>() or
  base.with_container(std::move(bucket_container))

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace ternary-constructed vector with a base vector + conditional
insert, making the Boost column addition easier to read.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rkflow

SolveResult:
- Template VRP::solve() now returns SolveResult{timers, lp_cost} instead
  of a bare vector; lp_cost is the final master LP objective after CG

benchmark_common.hpp (new, shared by both benchmark binaries):
- kSolomonBKS: inline map of BKS values for C1/R1/RC1/C2/R2/RC2
- format_benchmark_table(): prints Instance | LP Cost | Gap% | per-algo HH:MM:SS
  Gap% = (lp_cost - bks) / bks * 100; flags FAIL if lp_cost > bks + 1e-3
  Total row accumulates timers; cost/gap columns show '-' there

benchmark_main.cpp (C1/R1/RC1, formerly print_timer_table):
- Uses structured binding auto [timers, lp_cost] = vrp.solve<...>(...)
- Collects rows and prints one table at the end via format_benchmark_table

benchmark_large_main.cpp (new, C2/R2/RC2 + Gehring & Homberger):
- run_vrp() helper avoids duplication; shares all algorithm setup
- --r2-max N: override R2 instance count (R2 has 11, C2/RC2 have 8)
- --gh-dir path: scan a directory of Solomon-format .txt files for
  Gehring & Homberger 200-1000-customer instances

CMakeLists.txt: add rcspp-vrp-benchmark-large target; exclude both
benchmark_*_main.cpp from the shared SOURCE_FILES glob

.github/workflows/benchmark.yml (new):
- workflow_dispatch with inputs: max_instances, run_large, max_large_instances
- Builds Release, runs small and optionally large benchmark
- Uploads results as artifacts keyed by commit SHA
- NOTE: Gurobi must be available on the runner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add MemoryInfo struct (cross-platform RSS / available / total RAM)
- Add MemoryLimitHelper struct (resolve + is_exceeded + is_under_pressure)
  both live in utils/memory.hpp; MemoryLimitHelper takes plain values so
  it has no dependency on AlgorithmBaseParams
- Add kKB / kMB / kGB / kDefaultMemoryPressureFraction to memory.hpp
- Add AlgorithmBaseParams memory fields: max_memory_gb, limit_to_available_ram,
  limit_to_total_ram, memory_limit_fraction, memory_check_interval,
  memory_pressure_fraction, memory_pressure_max_labels_per_node
- Add effective_max_labels_per_node_ and memory_pressure_triggered_ to Algorithm
- Add virtual on_memory_pressure() and release_label_memory() hooks
- Implement on_memory_pressure() in Simple / Pushing / Pulling algorithms:
  tightens per-node cap, two-phase trim (store aside then release)
- Call release_label_memory() at end of every solve() to reclaim RSS
- Add LabelPool::release() (clear + shrink_to_fit)
- Add ResourceGraph::solve(AlgorithmBaseParams, ...) overload
- Add 12 new tests (4 templates x 3 algorithm types):
  MemoryLimitImmediateStop, MemoryLimitAvailableRam, MemoryLimitTotalRam,
  MemoryPressurePruning
- Extend .gitignore to cover build_*/ directories
- Exclude build directories from markdownlint pre-commit hook
- Move fstream/string includes to top of memory.hpp for cpplint IWYU

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the per-row std::vector<Row>{one_element} construction pattern
(N malloc/free pairs) with a single-pass approach over arc_id runs:

- Rows array arrives pre-sorted by arc_id (guaranteed by the Python caller).
- Loop advances j to find the end of each run, reserves capacity once
  per arc (dr.reserve), then push_back-fills directly — no temporary
  vector, no repeated heap allocations.

For N_rows rows across N_arcs arcs this reduces:
  old: N_rows × (1 malloc + 1 free + 1 push_back + bounds check)
  new: N_arcs × 1 reserve  +  N_rows × 1 push_back

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MemoryLimitHelper::resolve() is called on every G.solve() invocation.
With LOG_INFO, a run with thousands of pricing calls emits an identical
'Memory limit: X GB (explicit).' line for each one, flooding the logs.

Demote all resolve() log messages to LOG_DEBUG — they remain visible
when debug logging is enabled but are silent in normal operation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each resolve() debug message now appends the current process RSS
(from MemoryInfo::process_bytes()):

  'Memory limit: 2 GB (explicit); process RSS: 312 MB.'

This makes the message immediately actionable when debugging OOM or
early-exit situations — no need to correlate with an external profiler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolved merge conflicts between quick-improvements (HEAD) and tight-lb,
preferring HEAD's changes when uncertain:

- Keep HEAD's memory-limit infrastructure (MemoryLimitHelper, max_memory_gb,
  memory_check_interval, memory_pressure_fraction, etc.)
- Keep HEAD's AlgorithmBaseParams / AlgorithmParams struct hierarchy
- Keep HEAD's ResourcePrototype-based Resource class architecture
- Adopt tight-lb's prev_label/ref_count/pending_release label tracking
  (replacing parent_/child_refcount_) to match staged code
- Adopt tight-lb's release_with_ref_count() cascade in LabelPool
- Integrate tight-lb's new algorithms: BacktrackingDiveAlgorithm,
  TabuList, TabuSearch (added files)
- Keep HEAD's DiversificationSearch logic, use tight-lb's TabuList member
- Fix duplicate fast_check_dominance overload in dominance_function.hpp
- Fix stray tolerance field usage in dominance_algorithm.hpp

All 46 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
legraina and others added 21 commits May 28, 2026 00:17
…lve to algorithm

- Introduce AlgorithmStatus enum (COMPLETE, TIMEOUT, MAX_SOLUTIONS, MAX_PHASES,
  INTERRUPTED, MEMORY_LIMIT) and SolveResult struct wrapping solutions + status
- Add timeout_s, tolerance, and release_after_solve to AlgorithmBaseParams;
  release_after_solve=false avoids shrink_to_fit overhead in tight inner loops
  (e.g. DiversificationSearch)
- Add is_time_out() and should_stop(iteration) helpers to Algorithm base class
- Update ResourceGraph::solve() to return SolveResult; fix backtracking_dive
  get_path_arc_ids return type (std::list -> std::vector); rename vrp SolveResult
  to CGSolveResult to avoid namespace collision
- Expose AlgorithmStatus, SolveResult, timeout_s, tolerance, release_after_solve
  in Python bindings; add sequence protocol to SolveResult for backward compat

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add cmake-build-*/ for CLion build directories
- Add *.so / *.dylib for macOS/Linux shared library outputs
- Add .venv/, .pytest_cache/, .claude/ (were relying on self-ignoring .gitignore
  inside those dirs; make project intent explicit)
- Remove 306 previously-tracked files from build_asan/ and build_py/ that
  were committed before the build_*/ ignore rule was in place

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The tabu while-loop checked max_iterations, stop_after_X_solutions and
is_interrupted() but not is_time_out(), so params.timeout_s had no
effect on the tabu search. Add !is_time_out() to the condition so a
wall-clock deadline (e.g. tabu_timeout_s=0.5s) is honoured inside the
loop rather than only after main_loop() returns.

Without this fix the tabu solver could run indefinitely on pathological
graph configurations, blocking worker processes and stalling the parent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements A*-style label-correcting that replaces the FIFO frontier
with a min-heap ordered by f = g + h, where h is a per-node admissible
lower bound computed via a backward Bellman-Ford pass using arc costs.
The heuristic is resource-type-agnostic to avoid template instantiation
issues with mixed resource compositions (int+bitset, int+set, etc.).

Registers the algorithm as Algorithm.AStar / "astar" in the Python
bindings and adds it to both VRP benchmarks and the full test suite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AStarDominanceAlgorithm now uses the same cost resource as the labeling
algorithm (via a new CostResourceType 3rd template param) to compute
the backward Bellman-Ford heuristic h(n). This puts g and h on the same
scale — both use reduced costs — making f = g + h a meaningful lower
bound on the total path cost.

Key changes:
- BellmanFordAlgorithm: extract the loop into run_relaxations(); add a
  clean arc-cost-only solve(graph, ids, bool) overload that avoids the
  CostResourceType template constraint issue across all compositions
- AStarDominanceAlgorithm: add CostResourceType as 3rd template param
  (default RealResource); use if constexpr + is_cost_in_composition_v
  to call the resource-based Bellman-Ford when the type is present,
  arc-cost fallback otherwise; catch negative-weight cycles (possible
  with reduced costs) and fall back to arc.cost
- graph_impl.hpp: AStarAlgoBound<CostRC> presents the 3-param algo as
  a 2-param template; AStarAlgoEntry injects cost_index into params and
  binds the correct CostRC at dispatch time
- algorithm.hpp: add heuristic_cost_index to AlgorithmBaseParams
- resource_traits.hpp: add is_cost_in_composition_v trait and the
  missing resource_type_composition.hpp include

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AStarDominanceAlgorithm now has 3 template params so it no longer
satisfies template<typename,typename>; replace with the 2-param wrapper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tar flag

New ImprovingTabuSearch algorithm (two-phase):
  1. GreedyAlgorithm with infinite upper bound finds an initial feasible
     solution and its cost
  2. TabuSearchAlgorithm uses that cost as the upper bound and improves

Benchmark changes (both benchmark_main and benchmark_large_main):
- Replace the single "Diversif" extra solver with two named heuristics:
    ConstructiveTabu = DiversificationSearch (arc-removal diversification)
    Tabu             = ImprovingTabuSearch (greedy init + tabu improvement)
- AStar is now optional behind --astar (off by default because it is slow);
  it runs as an extra solver rather than a main CG algorithm so it does
  not penalise the default benchmark run
- Remove AStarAlgoBound from the vrp.solve<> template pack; main CG
  algorithms are back to Simple / Pushing / Pulling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rewrites ImprovingTabuSearch as a self-contained BacktrackingDiveAlgorithm
subclass (same base as TabuSearchAlgorithm) rather than a sequenced pair of
separate algorithm objects:

Phase 1 — construction (tabu inactive):
  A pure greedy dive finds the initial feasible solution and records its
  cost as the starting upper bound for the improvement phase.

Phase 2 — improvement (tabu active):
  Repeated tabu-filtered dives constrained to the current best cost.
  Classical TS mechanisms:
  - Tabu list: arcs of the last found path are forbidden for tabu_tenure
    iterations to prevent cycling.
  - Aspiration criterion: when all extensions from a node are tabu, the
    cheapest tabu extension is used anyway (last-resort fallback).
  - Intensification: on a strictly improving solution, shrink_extra() is
    called so the search stays near the good region.
  - Diversification: after diversification_tenure consecutive non-improving
    dives, grow_extra() forces exploration of new regions.

Adds AlgorithmBaseParams::diversification_tenure (default 10) to control
the diversification threshold.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace manual loop conditions with the unified should_stop(i) helper in
both TabuSearchAlgorithm and ImprovingTabuSearch, and remove stray blank
lines in the latter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

# Conflicts:
#	src/rcspp/algorithm/tabu_search.hpp
# Conflicts:
#	src/python_interface/CMakeLists.txt
#	src/rcspp/algorithm/algorithm.hpp
#	src/rcspp/algorithm/dominance_algorithm.hpp
#	src/rcspp/algorithm/solution.hpp
#	src/rcspp/rcspp.hpp
…cker noise

- memory.hpp: clang-format sorted <psapi.h> before <windows.h> (alphabetical),
  which breaks the Windows build since psapi.h needs DWORD/HANDLE from
  windows.h. NOLINT only silences cpplint, not clang-format, so wrap the two
  includes in a clang-format off/on barrier to lock the required order.
- __init__.py: apply isort import ordering.
- pricing_pool.py: in SharedPricingPool.attach(), use SharedMemory(track=False)
  on Python 3.13+ so a non-owner worker never registers the segment with the
  shared resource_tracker. This avoids the benign-but-noisy KeyError tracebacks
  at interpreter shutdown (owner's unlink() double-unregistering). Falls back to
  the manual unregister on Python < 3.13. Still spawn-safe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-activate the ng-set resource in the VRP and add an optional dominance
strengthening: when a label settles at node j, fold the resource-unreachable
members of its ng-neighbourhood N_j into the ng-memory Pi. This is pure
acceleration -- the CG LP bound is unchanged while the number of non-dominated
labels drops -- and is gated by a per-build flag (default off).

Reachability is decided the only general way: by building the trial label L->x
along the real arc and checking is_feasible() (reachability couples extension
and feasibility; there is no resource-agnostic node-only shortcut). A
thread_local re-entrancy guard stops the trial extensions (which reuse the
augmented arc extenders) from recursing and makes them measure the plain
extension. Dominance stays the standard InclusionDominanceFunction on the
augmented Pi -- no separate nu field, no custom dominance.

Engine (general, src/rcspp):
- Add a post_extend / post_extend_back hook to CompositionExtensionFunction.
- New NgUnreachableCompositionExtensionFunction implementing the fold.
- Add ResourceGraph::set_composition_extension_function (+ ResourceFactory
  setter) so a custom composition extension function can be installed.
- Expose total_non_dominated_labels() / num_extended_labels() on
  DominanceAlgorithm.

VRP:
- Re-enable the ng resource (fix stale IntersectionFeasibilityFunction args),
  supply the ng extender value per arc, add ng_size + enable_ng_augmentation
  ctor params and build_ng_arcs.
- New benchmark rcspp-vrp-benchmark-ng sweeping ng sizes, logging instance /
  configuration / timing at the start and end of each run.

Test:
- tests/rcspp/resource/test_ng_unreachable.hpp: pure-rcspp regression test
  (no Gurobi) using the exact VRP resource pack; asserts identical optimum and
  strictly fewer labels with the augmentation on, and a no-op when reachable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r tractability

The ng benchmark hung on wider-TW Solomon instances (C102+): exact ng-route
pricing is ~O(n^3) labels, so the first CG iteration explored millions of labels.
This was confirmed in the pure-rcspp harness (1010/3390/8020/15900/28200 labels
for n=10..30 -- a clean n^3 fit), not a logic bug.

Changes:
- Drop the separate zero-dual 'price_once' labeling (its best_cost was a stale
  +inf sentinel and its label count was unrepresentative). The label count now
  comes from the CG run itself: add SolveResult::num_extended_labels, accumulate
  it across pricing iterations into the new CGSolveResult::total_pricing_labels,
  and report it from a single cg_run().
- Add early-stopping caps to keep pricing tractable: num_labels_to_extend_by_node
  (--max-labels, default 100) and stop_after_X_solutions = 4 x cols_per_iter (the
  latter needs return_dominated_solutions=true to take effect).
- Add a VRP::solve max_columns_per_iter cap: add only the K most-negative columns
  per CG iteration (default: add all). The benchmark exposes it as --cols K
  (default #demand customers).

Pricing is then heuristic (the LP bound is an estimate, not exact), but baseline
and augmented runs use identical caps so the comparison stays fair. The cap
mechanics are validated in the pure-rcspp harness (a DISABLED diagnostic test);
SolveResult::num_extended_labels is asserted to match the algorithm getter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 10, 2026 10:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR significantly extends the RCSPP/VRP stack with (1) an ng-route “unreachable-set” dominance augmentation (intended as pure acceleration), (2) new algorithm capabilities (A* dominance ordering, tabu-style heuristics), and (3) infrastructure for memory limiting/pressure pruning, plus updated benchmarks, tests, and Python bindings to exercise/consume these features.

Changes:

  • Add memory-limit/pressure-pruning support to labeling algorithms (with new tests and Python exposure of memory queries).
  • Add ng-route unreachable-set augmentation (composition-level extension hook + VRP integration + regression test + benchmark).
  • Introduce additional algorithms and benchmarking support (A* dominance, tabu list/search variants, heterogeneous “extra solvers”, optional Boost build).

Reviewed changes

Copilot reviewed 63 out of 65 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/rcspp/vrp_subproblem/vrp_subproblem.hpp Plumbs base params through test subproblem solve; adds ref-count consistency helper
tests/rcspp/test_rcspp.hpp Adds A* tests + memory-limit and ref-count regression tests
tests/rcspp/test_main.cpp Reorders/includes new test headers (ng unreachable)
tests/rcspp/test_label_buckets.hpp Adds multi-bucket regression tests + suggest_range test
tests/rcspp/resource/test_ng_unreachable.hpp New regression tests for ng unreachable augmentation
src/vrp/vrp.hpp VRP API changes: CGSolveResult, ExtraSolver, optional Boost, ng augmentation wiring
src/vrp/vrp.cpp Implements ng augmentation setup; makes Boost optional; Path uses vector
src/vrp/main.cpp Makes Boost include conditional
src/vrp/CMakeLists.txt Makes Boost optional; adds ng/large benchmarks; defines RCSPP_VRP_HAS_BOOST
src/vrp/cg/path.hpp Path visited_nodes switched to std::vector
src/vrp/cg/path.cpp Updates Path ctor for std::vector
src/vrp/benchmark_ng_main.cpp New benchmark sweeping ng sizes & augmentation
src/vrp/benchmark_main.cpp Benchmark refactor to new solve result + extra solvers
src/vrp/benchmark_large_main.cpp New/updated large benchmark driver
src/vrp/benchmark_common.hpp Shared benchmark table formatting + Solomon BKS data
src/rcspp/utils/memory.hpp New cross-platform memory query + memory limit helper
src/rcspp/resource/resource_traits.hpp Adds composition membership trait for cost type (A* heuristic)
src/rcspp/resource/resource_graph.hpp Adds composition extension setter; solve() now returns SolveResult; base-params solve overload
src/rcspp/resource/concrete/numerical_resource.hpp Adds leq overload with delta; signature cleanup
src/rcspp/resource/composition/functions/extension/ng_unreachable_composition_extension_function.hpp New unreachable-set augmentation implementation
src/rcspp/resource/composition/functions/extension/composition_extension_function.hpp Adds post_extend hooks for composition-level extension postprocessing
src/rcspp/resource/base/resource_factory.hpp Allows replacing extension function before arc creation
src/rcspp/rcspp.hpp Exposes new algorithms/utilities (A*, tabu, memory) in umbrella header
src/rcspp/preprocessor/connectivity_matrix.hpp Includes in-arcs when building adjacency (SCC/connectivity)
src/rcspp/preprocessor/bellman_ford_algorithm.hpp Refactors Bellman–Ford to share relaxation loop; adds overloads
src/rcspp/label/label.hpp Adds prev_label/ref_count bookkeeping for path reconstruction & safe release
src/rcspp/label/label_pool.hpp Adds ref-count-aware release + consistency check + pool release()
src/rcspp/label/label_factory.hpp Initializes new label bookkeeping fields
src/rcspp/graph/graph.hpp Adds mutable for_each_arc overload
src/rcspp/algorithm/tabu_search.hpp New tabu-search constructive heuristic
src/rcspp/algorithm/tabu_list.hpp New reusable tabu list utility
src/rcspp/algorithm/simple_dominance_algorithm.hpp Ref-count-safe release + memory pressure pruning
src/rcspp/algorithm/pushing_dominance_algorithm.hpp Memory pressure pruning + uses effective per-node cap
src/rcspp/algorithm/pulling_dominance_algorithm.hpp Adds memory limit checks + ref-count-safe release + memory pressure pruning
src/rcspp/algorithm/improving_tabu_search.hpp New improving tabu-search heuristic
src/rcspp/algorithm/greedy.hpp Uses unified should_stop() termination
src/rcspp/algorithm/dominance_algorithm.hpp Adds SolveResult integration, memory checks, prev_label path reconstruction
src/rcspp/algorithm/diversification_search.hpp Refactors diversification to use TabuList + SolveResult
src/rcspp/algorithm/backtracking_dive_algorithm.hpp New shared DFS/backtracking base for heuristics
src/rcspp/algorithm/astar_dominance_algorithm.hpp New A* priority dominance algorithm + dispatch wrapper
src/rcspp/algorithm/algorithm.hpp Introduces AlgorithmStatus/SolveResult + timeout/memory-limit plumbing
src/python/test_vrp.py Fixes sys.path handling for test execution
src/python/test_rcspp.py Updates examples for new algorithms and bucket params; path fixes
src/python/test_rcspp_networkx.py Fixes sys.path handling
src/python/test_graph.py Makes numpy optional via pytest.importorskip; path fixes
src/python/test_clone.py Makes numpy optional via pytest.importorskip; naming/doc tweaks
src/python_interface/rcspp/solution_pool.cpp Braces/formatting cleanup in predicate
src/python_interface/rcspp/rcspp.cpp Exposes process/available memory helper functions
src/python_interface/rcspp/pricing_pool.py Improves SharedMemory attach for Python 3.13+ (track=False)
src/python_interface/rcspp/graph.py Adds Tabu/A* aliases; returns SolveResult; adds BucketAlgorithmParams wrapper
src/python_interface/rcspp/graph.cpp Binds AlgorithmStatus/SolveResult + new params fields + new algorithms
src/python_interface/rcspp/graph_impl.hpp Dispatch now returns SolveResult; adds A* dispatch; optimizes bulk row append
src/python_interface/rcspp/_core/resource.pyi New typing stubs for resource function classes
src/python_interface/rcspp/_core/logger.pyi New typing stubs for logger
src/python_interface/rcspp/_core/graph.pyi New typing stubs for graph + SolveResult
src/python_interface/rcspp/_core/init.pyi Exposes stub submodules
src/python_interface/rcspp/init.py Exposes SolveResult/AlgorithmStatus + memory helpers in package API
src/python_interface/CMakeLists.txt Installs .pyi and py.typed; copies stubs at build time
instances/RC201_12.txt Adds new Git LFS pointer instance file
.pre-commit-config.yaml Excludes build dirs from markdownlint
.gitignore Expands ignored build/python/cache/IDE artifacts
.github/workflows/ci.yml Adds python-version matrix; improves pytest output
.github/workflows/benchmark.yml Adds manual benchmark workflow (small/large)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/vrp/benchmark_main.cpp Outdated
Comment on lines +130 to +134
AlgorithmParams<LabelList<ResourceType>> list_params;
auto [timers, lp_cost] = vrp.solve<SimpleDominanceAlgorithm,
PushingDominanceAlgorithm,
PullingDominanceAlgorithm>(list_params,
std::nullopt,
Comment on lines +182 to +186
std::string path = inst_dir + name + ".txt";
LOG_INFO("Instance: ", path, '\n');
auto [timers, lp_cost] = run_vrp(path, run_boost, run_astar);
if (total_timers.empty()) {
total_timers = timers;
Comment on lines +196 to +200
for (const auto& path : gh_instance_paths) {
std::string name = fs::path(path).stem().string();
LOG_INFO("Instance: ", path, '\n');
auto [timers, lp_cost] = run_vrp(path, run_boost, run_astar);
if (total_timers.empty()) {
Comment thread src/vrp/vrp.hpp
Comment on lines +6 to 10
#include <algorithm>
#include <cstddef>
#include <functional>
#include <limits>
#include <optional>
Comment thread src/vrp/vrp.hpp
"\n");
if (algo_index > first_rcspp_idx && !non_optimal &&
!solutions_rcspp_any.empty()) {
double diff = abs(sols[0].cost - solutions_rcspp_any[0].cost);
Comment thread src/rcspp/label/label.hpp
Comment on lines +84 to +88
// Predecessor label set at extension time; valid as long as ref_count keeps it pinned.
Label<ResourceType>* prev_label = nullptr;
// Number of alive successors that reference this label as their predecessor.
uint8_t ref_count = 0;
// True when the algorithm wanted to release this label but ref_count was > 0.
Comment on lines +14 to +18
#include <gtest/gtest.h>

#include <algorithm>
#include <cmath>
#include <limits>
Comment on lines +8 to +14
class Algorithm(Enum):
Simple: Algorithm
Pushing: Algorithm
Pulling: Algorithm
Greedy: Algorithm
Tabu: Algorithm

legraina and others added 4 commits June 10, 2026 14:36
…sult

- Add <cstddef> to path.hpp for size_t
- Add <memory> to master_problem.hpp for std::unique_ptr
- Update structured bindings to include total_pricing_labels (3rd field)
- Add --instance flag to benchmark_ng_main to run a single named instance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parallelize the ng-route benchmark on SLURM with one job per (instance, ng-size)
by default (or per (family, ng-size) with --group-by family).

- scripts/gen_ng_jobs.py: generate a job manifest (one benchmark CLI line per
  job) for the sbatch array and print the matching --array range.
- scripts/run_ng_benchmark.slurm: array runner; task N executes manifest line
  N+1 via rcspp-vrp-benchmark-ng (guards against missing manifest/binary and an
  out-of-range array index).
- gitignore the generated manifest (scripts/ng_jobs.txt) and logs/.

Relies on the benchmark's --instance / --family selectors (already added).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add t_base, t_aug and t_drop% columns to the ng benchmark summary table
(mirroring nlab_base / nlab_aug / drop%): CgStats now carries the per-run
wall-clock seconds, and t_drop% = 100*(t_base - t_aug)/t_base.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants