Source code for optymus.routing._utils

import numpy as np


[docs] def compute_distance_matrix(coordinates): """Compute Euclidean distance matrix from coordinate array. Parameters ---------- coordinates : array-like, shape (n, 2) or (n, 3) Node coordinates. Returns ------- np.ndarray, shape (n, n) Symmetric distance matrix. """ coords = np.asarray(coordinates, dtype=float) diff = coords[:, np.newaxis, :] - coords[np.newaxis, :, :] return np.sqrt(np.sum(diff ** 2, axis=-1))
def validate_vrp_inputs(distance_matrix, demands, vehicle_capacity, num_vehicles, depot): """Validate and normalize CVRP inputs. Returns ------- distance_matrix, demands, num_vehicles : validated arrays and int """ D = np.asarray(distance_matrix, dtype=float) n = D.shape[0] if D.ndim != 2 or D.shape[0] != D.shape[1]: raise ValueError(f"distance_matrix must be square, got shape {D.shape}") demands = np.asarray(demands, dtype=float) if demands.shape != (n,): raise ValueError( f"demands must have length {n} (matching distance_matrix), got {demands.shape}" ) if vehicle_capacity <= 0: raise ValueError("vehicle_capacity must be positive") if np.any(demands[np.arange(n) != depot] > vehicle_capacity): raise ValueError( "At least one customer demand exceeds vehicle_capacity" ) total_demand = np.sum(demands) - demands[depot] if num_vehicles is None: num_vehicles = int(np.ceil(total_demand / vehicle_capacity)) if num_vehicles * vehicle_capacity < total_demand: raise ValueError( f"Infeasible: {num_vehicles} vehicles x {vehicle_capacity} capacity " f"= {num_vehicles * vehicle_capacity} < total demand {total_demand}" ) return D, demands, num_vehicles def route_distance(route, distance_matrix, depot=0): """Total distance for a single route: depot -> customers -> depot. Parameters ---------- route : list[int] Customer indices (excluding depot). distance_matrix : np.ndarray depot : int Returns ------- float """ if not route: return 0.0 d = distance_matrix[depot, route[0]] for i in range(len(route) - 1): d += distance_matrix[route[i], route[i + 1]] d += distance_matrix[route[-1], depot] return float(d) def route_load(route, demands): """Total demand served by a route.""" return float(sum(demands[c] for c in route)) def total_distance(routes, distance_matrix, depot=0): """Sum of distances across all routes.""" return sum(route_distance(r, distance_matrix, depot) for r in routes)