# Migration Guide: jsprit 1.x to 2.0 This guide helps you upgrade from jsprit 1.x to version 2.0. ## Prerequisites ### Java 21 Required jsprit 2.0 requires **Java 21** (was Java 8). Update your build configuration: **Maven:** ```xml 21 21 ``` **Gradle:** ```groovy java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } ``` ### JUnit 5 for Tests If you have tests extending jsprit test classes: ```xml org.junit.jupiter junit-jupiter-api 5.10.2 test ``` --- ## Breaking Changes ### Index Management **Why this changed:** Previously, indices were stored on Job and Vehicle objects. This caused problems when you needed to solve variations of a problem - for example, testing different fleet configurations or job subsets. Reusing Job objects across multiple VRPs led to index conflicts, forcing you to recreate all objects from scratch for each variation. **What's new:** Each VRP now manages its own indices internally. When you call `build()`, indices are assigned automatically (0 to n-1). The same Job or Vehicle objects can be safely reused across multiple VRP instances. **Example - solving problem variations:** ```java // Define jobs once Service job1 = Service.Builder.newInstance("job1").setLocation(loc1).build(); Service job2 = Service.Builder.newInstance("job2").setLocation(loc2).build(); // Create different problem variations reusing the same jobs VehicleRoutingProblem vrp1 = VehicleRoutingProblem.Builder.newInstance() .addJob(job1).addJob(job2) .addVehicle(smallFleet) .build(); VehicleRoutingProblem vrp2 = VehicleRoutingProblem.Builder.newInstance() .addJob(job1).addJob(job2) .addVehicle(largeFleet) // Different fleet .build(); // Solve both independently - no index conflicts ``` **Retrieving indices:** Use the VRP instance to get indices: ```java int jobIndex = vrp.getJobIndex(job); int vehicleIndex = vrp.getVehicleIndex(vehicle); ``` **Impact:** For most users, this is transparent. `Job.getIndex()` and `Vehicle.getIndex()` are deprecated but still work (returning the index from the last VRP they were added to). --- ## New Features ### Independent Operator Selection (Recommended) The new API allows selecting ruin and insertion operators independently with weights: **Before (1.x):** ```java VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(vrp); ``` **After (2.0) - Basic:** ```java VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(vrp); // Works exactly as before - no changes required ``` **After (2.0) - With Custom Operators:** ```java VehicleRoutingAlgorithm algorithm = Jsprit.Builder.newInstance(vrp) // Ruin operators with weights .addRuinOperator(0.3, Ruin.radial(0.3, 0.5, 10, 70)) .addRuinOperator(0.3, Ruin.random(0.3, 0.5, 10, 70)) .addRuinOperator(0.2, Ruin.worst()) .addRuinOperator(0.2, Ruin.kruskalCluster()) // Insertion operators with weights .addInsertionOperator(0.7, Insertion.regretFast()) .addInsertionOperator(0.3, Insertion.best()) .buildAlgorithm(); ``` ### Available Ruin Strategies | Factory Method | Description | |---------------|-------------| | `Ruin.random()` | Random job removal | | `Ruin.random(minFrac, maxFrac, minBound, maxBound)` | Random with bounded share | | `Ruin.radial()` | Remove nearby jobs | | `Ruin.radial(minFrac, maxFrac, minBound, maxBound)` | Radial with bounded share | | `Ruin.worst()` | Remove jobs with highest removal benefit | | `Ruin.worst(minFrac, maxFrac, minBound, maxBound)` | Worst with bounded share | | `Ruin.cluster()` | DBSCAN-based cluster removal | | `Ruin.cluster(minFrac, maxFrac, minBound, maxBound)` | Cluster with bounded share | | `Ruin.kruskalCluster()` | MST-based cluster removal | | `Ruin.string()` | Remove sequences from routes | | `Ruin.string(minJobs, maxJobs, minStrings, maxStrings)` | String with parameters | | `Ruin.timeRelated()` | Remove jobs with similar time windows | | `Ruin.timeRelated(minFrac, maxFrac, minBound, maxBound)` | Time-related with bounded share | **Bounded fractions** clamp the share to a range: ```java // Remove 30-50% of jobs, but at least 10 and at most 70 Ruin.random(0.3, 0.5, 10, 70) ``` ### Available Insertion Strategies | Factory Method | Description | |---------------|-------------| | `Insertion.regretFast()` | Fast regret-2 insertion (default) | | `Insertion.regretFast(k, spatialFilterK, affectedJobTracking)` | Configurable regret-k | | `Insertion.regret()` | Standard regret-2 | | `Insertion.regret(k)` | Standard regret-k | | `Insertion.best()` | Greedy best insertion | | `Insertion.cheapest()` | Cheapest insertion | | `Insertion.positionRegret()` | Position-based regret (experimental) | ### Algorithm Event System Monitor algorithm execution with the new unified event system. **Setup requires the AlgorithmEventAdapter:** ```java VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(vrp); // Create and register the event adapter AlgorithmEventAdapter adapter = new AlgorithmEventAdapter(algorithm); algorithm.addListener(adapter); // Now add event listeners for unified events algorithm.addEventListener(event -> { switch (event) { case IterationStarted e -> System.out.println("Iteration " + e.iteration()); case StrategyExecuted e -> System.out.println("Strategy: " + e.strategyId()); case JobInserted e -> System.out.println("Inserted: " + e.job().getId() + " into " + e.routeId()); case JobRemoved e -> System.out.println("Removed: " + e.job().getId()); default -> {} } }); ``` **Available events:** - `IterationStarted`, `IterationCompleted` - `StrategySelected`, `StrategyExecuted` - `RuinStarted`, `RuinCompleted`, `JobRemoved` - `RecreateStarted`, `RecreateCompleted`, `JobInserted`, `JobUnassigned` - `InsertionEvaluated`, `AcceptanceDecision` ### SolutionSpec for Initial Solutions Declaratively specify initial solutions: ```java SolutionSpec spec = SolutionSpec.builder() .addRoute("vehicle1", "job1", "job2", "job3") .addRoute("vehicle2", "job4", "job5") .build(); VehicleRoutingAlgorithm algorithm = Jsprit.Builder.newInstance(vrp) .addInitialSolution(spec) .buildAlgorithm(); ``` ### Job Type Enum Replace `instanceof` checks with the new `Job.Type` enum: **Before:** ```java if (job instanceof Service) { // handle service } else if (job instanceof Shipment) { // handle shipment } ``` **After:** ```java switch (job.getType()) { case SERVICE -> handleService((Service) job); case SHIPMENT -> handleShipment((Shipment) job); case PICKUP -> handlePickup((Pickup) job); case DELIVERY -> handleDelivery((Delivery) job); } ``` --- ## Deprecations ### Replace Deprecated Classes | Deprecated | Replacement | |-----------|-------------| | `InsertionBuilder` | `InsertionStrategyBuilder` | | `RuinRadialMultipleCenters` | `Ruin.radial()` | | `ServiceInsertionOnRouteLevelCalculator` | Use standard insertion calculators | | `RouteLevelActivityInsertionCostsEstimator` | Use standard cost estimators | | `CalculationUtils` | Inline calculations or use `SolutionAnalyser` | ### Fixed Costs Configuration **Before:** ```java Jsprit.Builder.newInstance(vrp) .considerFixedCosts(true) .buildAlgorithm(); ``` **After:** ```java // Fixed costs are now handled through the objective function // The default objective already considers fixed costs Jsprit.Builder.newInstance(vrp) .buildAlgorithm(); ``` --- ## Experimental Features These features are available but may change in future releases: ### Spatial Filtering Limits route evaluation to nearby routes: ```java Insertion.regretFast(2, 5, false) // k=2, filter to 5 nearest routes ``` ### Affected-Job Tracking Only recalculates insertion costs for jobs affected by last insertion: ```java Insertion.regretFast(2, 0, true) // Enable affected-job tracking ``` ### Position-Based Regret More accurate regret calculation considering all positions: ```java Insertion.positionRegret() Insertion.positionRegret(3, 3) // k=3, expand top 3 routes ``` --- ## Common Migration Scenarios ### Scenario 1: Simple Algorithm Creation No changes required: ```java // This still works exactly as before VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(vrp); VehicleRoutingProblemSolution solution = Solutions.bestOf(algorithm.searchSolutions()); ``` ### Scenario 2: Custom Strategy Weights **Before (1.x with SearchStrategyModule):** ```java // Complex configuration with coupled strategies ``` **After (2.0):** ```java VehicleRoutingAlgorithm algorithm = Jsprit.Builder.newInstance(vrp) .addRuinOperator(0.4, Ruin.radial()) .addRuinOperator(0.4, Ruin.random()) .addRuinOperator(0.2, Ruin.worst()) .addInsertionOperator(0.7, Insertion.regretFast()) .addInsertionOperator(0.3, Insertion.best()) .buildAlgorithm(); ``` ### Scenario 3: Monitoring Algorithm Progress **Before (1.x):** ```java algorithm.addListener(new IterationStartsListener() { @Override public void informIterationStarts(int iteration, ...) { System.out.println("Iteration " + iteration); } }); ``` **After (2.0) - Still works, plus new events:** ```java // Old listeners still work algorithm.addListener(new IterationStartsListener() { ... }); // New unified event system (requires adapter setup) AlgorithmEventAdapter adapter = new AlgorithmEventAdapter(algorithm); algorithm.addListener(adapter); algorithm.addEventListener(event -> { if (event instanceof StrategyExecuted e) { System.out.println("Used: " + e.strategyId()); } }); ``` --- ## Getting Help - [GitHub Issues](https://github.com/graphhopper/jsprit/issues) - [API Documentation](https://github.com/graphhopper/jsprit) - [Examples](https://github.com/graphhopper/jsprit/tree/master/jsprit-examples)