Skip to content

perf(vrp): vectorize hot paths in _utils, milp, and data generation - #6

Open
nathan-ferchtandiker wants to merge 4 commits into
sudhan-bhattarai:masterfrom
danielpuri1901:perf/vrp
Open

perf(vrp): vectorize hot paths in _utils, milp, and data generation#6
nathan-ferchtandiker wants to merge 4 commits into
sudhan-bhattarai:masterfrom
danielpuri1901:perf/vrp

Conversation

@nathan-ferchtandiker

@nathan-ferchtandiker nathan-ferchtandiker commented Mar 2, 2026

Copy link
Copy Markdown

Changes

  • _utils._dist_matrix: replace per-pair geopy.geodesic() loop with a single pyproj.Geod.inv() call over all upper-triangle pairs (C-level PROJ library). Results agree with geopy to <2e-12 miles. ~130-255x faster depending on n.
  • _utils.extract_routes: precompute successor array with argmax(axis=1) in one vectorized pass instead of one np.where scan per route step. ~13x faster.
  • milp.__init__: enable Presolve=2 and Cuts=2 for stronger LP relaxations at the Gurobi level.
  • milp._add_vars: replace O(n) per-element self-loop ub=0 assignments with a single setAttr("UB", ...) batch call.
  • milp._preprocess_arcs: batch setAttr for infeasible arc elimination (depot and customer-to-customer).
  • milp._add_constraints / _add_objective: cache self._var['x'], travel_time_matrix, delta as locals before O(n²) generator loops; rewrite big-M expressions from 4 terms to 3.
  • milp._gel_sol: replace n² per-element round() loop with np.rint() over a contiguous list + reshape.
  • _data_generation: np.random.randint instead of np.random.choice(range(...)), vectorized l_t with np.minimum.

Test plan

  • Function signatures and return types are unchanged across all three files.
  • _dist_matrix results agree with the geopy baseline to <2e-12 miles (verified by benchmark assertions).
  • extract_routes output matches per-step np.where baseline (verified by benchmark assertions).
  • Model construction and solve complete without errors with time-window and capacity constraints enabled.

🤖 Generated with Claude Code

nathan-ferchtandiker and others added 4 commits March 2, 2026 15:32
_utils.py:
- _dist_matrix: replace per-pair geopy.geodesic() loop with a single
  pyproj.Geod.inv() call over all upper-triangle pairs at once (C-level
  PROJ). Results agree with geopy to <2e-12 miles (~3 nm). ~130-255x faster.
- extract_routes: precompute successor array with argmax(axis=1) in one
  vectorized pass instead of calling np.where per route step. ~13x faster.

milp.py:
- __init__: enable Gurobi Presolve=2 and Cuts=2 for stronger LP relaxations.
- _add_vars: replace O(n) per-element self-loop ub=0 assignments with a
  single setAttr("UB", ...) batch call, consistent with _preprocess_arcs.
- _preprocess_arcs: batch setAttr for infeasible arc elimination.
- _add_constraints / _add_objective: cache self._var['x'], travel_time_matrix,
  delta etc. as locals before O(n^2) generator loops to eliminate repeated
  dict/attribute lookups; rewrite big-M expressions from 4 terms to 3.
- _gel_sol: replace n^2 per-element round() loop with np.rint() over a
  contiguous list, then reshape — eliminates n^2 Python function calls.

_data_generation.py:
- Replace np.random.choice(range(...)) with np.random.randint (avoids
  constructing a Python range object before sampling).
- Replace list-comprehension l_t with np.minimum(e_t_arr + duration,
  day_end).tolist() (vectorized).

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.

2 participants