diff --git a/CHANGELOG.md b/CHANGELOG.md index d618c5e1..ba915717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Test cases for CommonDuedateScheduling * Test cases for WeightedStaticSchedulingWithSetups * Test cases for the parallel metaheuristic and parallel multistarter classes + * Test cases for the timed parallel multistarters ### Dependencies * Bump core from 2.4.3 to 2.4.4 diff --git a/src/test/java/org/cicirello/search/concurrent/TimedParallelMultistarterTests.java b/src/test/java/org/cicirello/search/concurrent/TimedParallelMultistarterTests.java index 36489b87..fd0abd38 100644 --- a/src/test/java/org/cicirello/search/concurrent/TimedParallelMultistarterTests.java +++ b/src/test/java/org/cicirello/search/concurrent/TimedParallelMultistarterTests.java @@ -1,6 +1,6 @@ /* * Chips-n-Salsa: A library of parallel self-adaptive local search algorithms. - * Copyright (C) 2002-2022 Vincent A. Cicirello + * Copyright (C) 2002-2023 Vincent A. Cicirello * * This file is part of Chips-n-Salsa (https://chips-n-salsa.cicirello.org/). * @@ -24,20 +24,15 @@ import java.util.ArrayList; import java.util.List; -import java.util.SplittableRandom; import org.cicirello.search.ProgressTracker; -import org.cicirello.search.ReoptimizableMetaheuristic; import org.cicirello.search.SolutionCostPair; -import org.cicirello.search.problems.OptimizationProblem; import org.cicirello.search.restarts.ConstantRestartSchedule; import org.cicirello.search.restarts.Multistarter; import org.cicirello.search.restarts.ParallelVariableAnnealingLength; -import org.cicirello.search.restarts.ReoptimizableMultistarter; -import org.cicirello.util.Copyable; import org.junit.jupiter.api.*; /** JUnit tests for TimedParallelMultistarter. */ -public class TimedParallelMultistarterTests { +public class TimedParallelMultistarterTests extends TimedParallelMultistarterValidator { @Test public void testParallelOptimizeSomeThreadFindsBest() { @@ -87,94 +82,6 @@ public void close() { search.close(); } - @Test - public void testParallelOptimizeSomeThreadFindsBest_Reopt() { - class ParallelSearch implements Runnable { - - TimedParallelReoptimizableMultistarter restarter; - ArrayList metaheuristics; - ProgressTracker tracker; - - ParallelSearch() { - tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - metaheuristics = new ArrayList(); - metaheuristics.add(new TestInterrupted(1, problem, tracker)); - restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - } - - @Override - public void run() { - restarter.setTimeUnit(10); - restarter.optimize(1000); - } - } - - ParallelSearch search = new ParallelSearch(); - Thread t = new Thread(search); - t.start(); - while (search.metaheuristics.get(0).count < 1) { - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - break; - } - } - // replaced deprecated call to setFoundBest() - search.tracker.update(-10000.0, new TestObject(-10000), true); - try { - t.join(1000); - } catch (InterruptedException ex) { - } - assertFalse(t.isAlive()); - search.restarter.close(); - } - - @Test - public void testParallelReoptimizeSomeThreadFindsBest_Reopt() { - class ParallelSearch implements Runnable { - - TimedParallelReoptimizableMultistarter restarter; - ArrayList metaheuristics; - ProgressTracker tracker; - - ParallelSearch() { - tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - metaheuristics = new ArrayList(); - metaheuristics.add(new TestInterrupted(1, problem, tracker)); - restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - } - - @Override - public void run() { - restarter.setTimeUnit(10); - restarter.reoptimize(1000); - } - } - - ParallelSearch search = new ParallelSearch(); - Thread t = new Thread(search); - t.start(); - while (search.metaheuristics.get(0).count < 1) { - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - break; - } - } - // replaced deprecated call to setFoundBest() - search.tracker.update(-10000.0, new TestObject(-10000), true); - try { - t.join(1000); - } catch (InterruptedException ex) { - } - assertFalse(t.isAlive()); - search.restarter.close(); - } - @Test public void testParallelOptimizeImprovementMade() { ProgressTracker tracker = new ProgressTracker(); @@ -191,38 +98,6 @@ public void testParallelOptimizeImprovementMade() { restarter.close(); } - @Test - public void testParallelOptimizeImprovementMade_Reopt() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - ArrayList metaheuristics = new ArrayList(); - metaheuristics.add(new TestImprovementMade(1000, problem, tracker)); - metaheuristics.add(new TestImprovementMade(1001, problem, tracker)); - metaheuristics.add(new TestImprovementMade(1002, problem, tracker)); - TimedParallelReoptimizableMultistarter restarter = - new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - restarter.setTimeUnit(100); - assertNotNull(restarter.optimize(1)); - restarter.close(); - } - - @Test - public void testParallelReoptimizeImprovementMade_Reopt() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - ArrayList metaheuristics = new ArrayList(); - metaheuristics.add(new TestImprovementMade(1000, problem, tracker)); - metaheuristics.add(new TestImprovementMade(1001, problem, tracker)); - metaheuristics.add(new TestImprovementMade(1002, problem, tracker)); - TimedParallelReoptimizableMultistarter restarter = - new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - restarter.setTimeUnit(50); - assertNotNull(restarter.reoptimize(1)); - restarter.close(); - } - @Test public void testInterruptParallelOptimize() { class ParallelSearch implements Runnable { @@ -267,94 +142,6 @@ public void run() { search.restarter.close(); } - @Test - public void testInterruptParallelOptimize_Reopt() { - class ParallelSearch implements Runnable { - - TimedParallelReoptimizableMultistarter restarter; - ArrayList metaheuristics; - - ParallelSearch() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - metaheuristics = new ArrayList(); - metaheuristics.add(new TestInterrupted(1, problem, tracker)); - metaheuristics.add(new TestInterrupted(2, problem, tracker)); - metaheuristics.add(new TestInterrupted(3, problem, tracker)); - restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - } - - @Override - public void run() { - restarter.setTimeUnit(10); - restarter.optimize(1000); - } - } - - ParallelSearch search = new ParallelSearch(); - Thread t = new Thread(search); - t.start(); - while (search.metaheuristics.get(0).count < 1) { - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - break; - } - } - t.interrupt(); - try { - t.join(1000); - } catch (InterruptedException ex) { - } - assertFalse(t.isAlive()); - search.restarter.close(); - } - - @Test - public void testInterruptParallelReoptimize_Reopt() { - class ParallelSearch implements Runnable { - - TimedParallelReoptimizableMultistarter restarter; - ArrayList metaheuristics; - - ParallelSearch() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - metaheuristics = new ArrayList(); - metaheuristics.add(new TestInterrupted(1, problem, tracker)); - metaheuristics.add(new TestInterrupted(2, problem, tracker)); - metaheuristics.add(new TestInterrupted(3, problem, tracker)); - restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - } - - @Override - public void run() { - restarter.setTimeUnit(10); - restarter.reoptimize(1000); - } - } - - ParallelSearch search = new ParallelSearch(); - Thread t = new Thread(search); - t.start(); - while (search.metaheuristics.get(0).count < 1) { - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - break; - } - } - t.interrupt(); - try { - t.join(1000); - } catch (InterruptedException ex) { - } - assertFalse(t.isAlive()); - search.restarter.close(); - } - @Test public void testOptimizeExceptions() { ProgressTracker tracker = new ProgressTracker(); @@ -384,53 +171,6 @@ public void testOptimizeMetaheuristicThrowsException() { restarter.close(); } - @Test - public void testOptimizeMetaheuristicThrowsException_Reopt() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - ArrayList metaheuristics = new ArrayList(); - metaheuristics.add(new TestOptThrowsExceptions(1, tracker, problem)); - metaheuristics.add(new TestOptThrowsExceptions(2, tracker, problem)); - metaheuristics.add(new TestOptThrowsExceptions(3, tracker, problem)); - TimedParallelReoptimizableMultistarter restarter = - new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - restarter.setTimeUnit(100); - SolutionCostPair solution = restarter.optimize(1); - assertTrue(solution == null || 0 == solution.getCost()); - restarter.close(); - } - - @Test - public void testReoptimizeMetaheuristicThrowsException_Reopt() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - - ArrayList metaheuristics = new ArrayList(); - metaheuristics.add(new TestOptThrowsExceptions(1, tracker, problem)); - metaheuristics.add(new TestOptThrowsExceptions(2, tracker, problem)); - metaheuristics.add(new TestOptThrowsExceptions(3, tracker, problem)); - TimedParallelReoptimizableMultistarter restarter = - new TimedParallelReoptimizableMultistarter(metaheuristics, 1); - restarter.setTimeUnit(100); - SolutionCostPair solution = restarter.reoptimize(1); - assertTrue(solution == null || 0 == solution.getCost()); - restarter.close(); - } - - @Test - public void testOptimizeExceptions_Reopt() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); - TimedParallelReoptimizableMultistarter restarter = - new TimedParallelReoptimizableMultistarter(heur, 1, 1); - restarter.close(); - IllegalStateException thrown = - assertThrows(IllegalStateException.class, () -> restarter.optimize(1)); - thrown = assertThrows(IllegalStateException.class, () -> restarter.reoptimize(1)); - } - @Test public void testSetProgressTrackerNull() { ProgressTracker tracker = new ProgressTracker(); @@ -443,18 +183,6 @@ public void testSetProgressTrackerNull() { restarter.close(); } - @Test - public void testSetProgressTrackerNull_Reopt() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); - TimedParallelReoptimizableMultistarter restarter = - new TimedParallelReoptimizableMultistarter(heur, 1, 1); - restarter.setProgressTracker(null); - assertEquals(tracker, restarter.getProgressTracker()); - restarter.close(); - } - @Test public void testOptimizeStoppedFoundBest() { ProgressTracker tracker = new ProgressTracker(); @@ -470,23 +198,6 @@ public void testOptimizeStoppedFoundBest() { restarter.close(); } - @Test - public void testOptimizeStoppedFoundBest_Reopt() { - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); - TimedParallelReoptimizableMultistarter restarter = - new TimedParallelReoptimizableMultistarter(heur, 1, 1); - long expected = restarter.getTotalRunLength(); - // replaced call to deprecated setFoundBest() - restarter.getProgressTracker().update(0, new TestObject(0), true); - restarter.optimize(1); - assertEquals(expected, restarter.getTotalRunLength()); - restarter.reoptimize(1); - assertEquals(expected, restarter.getTotalRunLength()); - restarter.close(); - } - @Test public void testSetTimeUnitException() { final int T = 1; @@ -506,31 +217,13 @@ public void testSetTimeUnitException() { } @Test - public void testSetTimeUnitException_Reopt() { - final int T = 1; - ArrayList searches = new ArrayList(T); + public void testTimedParallelMultistarterOne() { + int numThreads = 1; + ArrayList searches = + new ArrayList(numThreads); ProgressTracker tracker = new ProgressTracker(); TestProblem problem = new TestProblem(); - for (int i = 1; i <= T; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - List schedules = - ParallelVariableAnnealingLength.createRestartSchedules(T); - final TimedParallelReoptimizableMultistarter tpm = - new TimedParallelReoptimizableMultistarter(searches, schedules); - IllegalArgumentException thrown = - assertThrows(IllegalArgumentException.class, () -> tpm.setTimeUnit(0)); - tpm.close(); - } - - @Test - public void testTimedParallelMultistarterOne() { - int numThreads = 1; - ArrayList searches = - new ArrayList(numThreads); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= numThreads; i++) { + for (int i = 1; i <= numThreads; i++) { searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); } TimedParallelMultistarter tpm = @@ -807,103 +500,6 @@ public void testTimedParallelMultistarterVariousConstructorsExceptions() { searches.get(0), new ArrayList())); } - @Test - public void testTimedParallelMultistarterVariousConstructorsExceptions_Reopt() { - final int T = 3; - ArrayList searches = new ArrayList(T); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= T; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - List schedules = - ParallelVariableAnnealingLength.createRestartSchedules(T + 1); - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> new TimedParallelReoptimizableMultistarter(searches, schedules)); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> new TimedParallelReoptimizableMultistarter(searches, 0)); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> { - searches.add( - new TestRestartedMetaheuristic( - T + 1, new ProgressTracker(), problem)); - new TimedParallelReoptimizableMultistarter(searches, schedules); - }); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> { - new TimedParallelReoptimizableMultistarter(searches, 1); - }); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> { - ArrayList> starters = - new ArrayList>(T); - for (int i = 0; i <= T; i++) { - starters.add(new ReoptimizableMultistarter(searches.get(i), 1000)); - } - new TimedParallelReoptimizableMultistarter(starters); - }); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> { - searches.set(T, new TestRestartedMetaheuristic(T + 1, tracker, new TestProblem())); - new TimedParallelReoptimizableMultistarter(searches, schedules); - }); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> { - new TimedParallelReoptimizableMultistarter(searches, 1); - }); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> { - ArrayList> starters = - new ArrayList>(T); - for (int i = 0; i <= T; i++) { - starters.add(new ReoptimizableMultistarter(searches.get(i), 1000)); - } - new TimedParallelReoptimizableMultistarter(starters); - }); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> new TimedParallelReoptimizableMultistarter(searches.get(0), 0, T)); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> new TimedParallelReoptimizableMultistarter(searches.get(0), 1, 0)); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> - new TimedParallelReoptimizableMultistarter( - searches.get(0), new ConstantRestartSchedule(1000), 0)); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> - new TimedParallelReoptimizableMultistarter( - new ReoptimizableMultistarter(searches.get(0), 1000), 0)); - thrown = - assertThrows( - IllegalArgumentException.class, - () -> - new TimedParallelReoptimizableMultistarter( - searches.get(0), new ArrayList())); - } - @Test public void testTimedParallelMultistarterSetProgressTracker() { final int T = 3; @@ -925,584 +521,4 @@ public void testTimedParallelMultistarterSetProgressTracker() { assertTrue(tracker2 == s.getProgressTracker()); } } - - @Test - public void testTimedParallelReoptimizableMultistarterVariousConstructors() { - final int T = 3; - ArrayList searches = new ArrayList(T); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= T; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - List schedules = - ParallelVariableAnnealingLength.createRestartSchedules(T); - TimedParallelReoptimizableMultistarter tpm = - new TimedParallelReoptimizableMultistarter(searches, schedules); - tpm.close(); - tpm = new TimedParallelReoptimizableMultistarter(searches.get(0), 1000, T); - tpm.close(); - tpm = - new TimedParallelReoptimizableMultistarter( - searches.get(0), new ConstantRestartSchedule(1000), T); - tpm.close(); - tpm = new TimedParallelReoptimizableMultistarter(searches.get(0), schedules); - tpm.close(); - tpm = - new TimedParallelReoptimizableMultistarter( - new ReoptimizableMultistarter(searches.get(0), 1000), T); - tpm.close(); - ArrayList> starters = - new ArrayList>(T); - for (int i = 0; i < T; i++) { - starters.add(new ReoptimizableMultistarter(searches.get(0), 1000)); - } - tpm = new TimedParallelReoptimizableMultistarter(starters); - tpm.close(); - TimedParallelReoptimizableMultistarter split = tpm.split(); - split.close(); - assertTrue(split != tpm); - tpm = new TimedParallelReoptimizableMultistarter(starters); - split = tpm.split(); - tpm.close(); - split.close(); - assertTrue(split != tpm); - } - - @Test - public void testTimedParallelReoptimizableMultistarterSetProgressTracker() { - final int T = 3; - ArrayList searches = new ArrayList(T); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= T; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - List schedules = - ParallelVariableAnnealingLength.createRestartSchedules(T); - TimedParallelReoptimizableMultistarter tpm = - new TimedParallelReoptimizableMultistarter(searches, schedules); - ProgressTracker tracker2 = new ProgressTracker(); - tpm.setProgressTracker(tracker2); - tpm.close(); - assertTrue(tracker2 == tpm.getProgressTracker()); - for (TestRestartedMetaheuristic s : searches) { - assertTrue(tracker2 == s.getProgressTracker()); - } - } - - @Test - public void testTimedParallelReoptimizableMultistarterOne() { - int numThreads = 1; - ArrayList searches = - new ArrayList(numThreads); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= numThreads; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - TimedParallelReoptimizableMultistarter tpm = - new TimedParallelReoptimizableMultistarter(searches, 1000); - assertEquals(1000, tpm.getTimeUnit()); - tpm.setTimeUnit(10); - assertEquals(10, tpm.getTimeUnit()); - assertTrue(tracker == tpm.getProgressTracker()); - assertTrue(problem == tpm.getProblem()); - assertEquals(0, tpm.getTotalRunLength()); - assertNull(tpm.getSearchHistory()); - long time1 = System.nanoTime(); - SolutionCostPair solution = tpm.optimize(8); - long time2 = System.nanoTime(); - int combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.reoptimizeCalled); - assertTrue(search.optimizeCalled > 0); - assertTrue(search.totalRunLength >= (search.optimizeCalled - 1) * 1001); - assertTrue(search.totalRunLength <= search.optimizeCalled * 1001); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - ArrayList> history = tpm.getSearchHistory(); - assertEquals(8, history.size()); - for (int i = 1; i < history.size(); i++) { - assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); - assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(i).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); - } - } - assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); - long actualRunTime = time2 - time1; - assertTrue( - actualRunTime >= 80000000, - "verifying runtime, actual=" + actualRunTime + " ns, should be at least 80000000 ns"); - - // verify can call optimize again - solution = tpm.optimize(1); - assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); - combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.reoptimizeCalled); - assertTrue(search.optimizeCalled > 0); - String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.optimizeCalled; - assertTrue(search.totalRunLength >= (search.optimizeCalled - 2) * 1001, msg); - assertTrue(search.totalRunLength <= search.optimizeCalled * 1001, msg); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - history = tpm.getSearchHistory(); - assertEquals(1, history.size()); - assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(0).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); - } - - // Close the parallel multistarter - tpm.close(); - } - - @Test - public void testTimedParallelReoptimizableMultistarterThree() { - int numThreads = 3; - final int NUM_TIME_CYCLES = 20; - ArrayList searches = - new ArrayList(numThreads); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= numThreads; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - TimedParallelReoptimizableMultistarter tpm = - new TimedParallelReoptimizableMultistarter(searches, 1000); - assertEquals(1000, tpm.getTimeUnit()); - tpm.setTimeUnit(10); - assertEquals(10, tpm.getTimeUnit()); - assertTrue(tracker == tpm.getProgressTracker()); - assertTrue(problem == tpm.getProblem()); - assertEquals(0, tpm.getTotalRunLength()); - assertNull(tpm.getSearchHistory()); - long time1 = System.nanoTime(); - SolutionCostPair solution = tpm.optimize(NUM_TIME_CYCLES); - long time2 = System.nanoTime(); - int combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.reoptimizeCalled); - assertTrue(search.optimizeCalled > 0); - assertTrue(search.totalRunLength >= (search.optimizeCalled - 1) * 1001); - assertTrue(search.totalRunLength <= search.optimizeCalled * 1001); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - ArrayList> history = tpm.getSearchHistory(); - assertEquals(NUM_TIME_CYCLES, history.size()); - for (int i = 1; i < history.size(); i++) { - assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); - assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(i).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); - } - } - assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); - long actualRunTime = time2 - time1; - assertTrue( - actualRunTime >= 80000000, - "verifying runtime, actual=" + actualRunTime + " ns, should be at least 80000000 ns"); - - // verify can call optimize again - tpm.setTimeUnit(20); - solution = tpm.optimize(1); - assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); - combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.reoptimizeCalled); - assertTrue(search.optimizeCalled > 0); - String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.optimizeCalled; - assertTrue(search.totalRunLength >= (search.optimizeCalled - 2) * 1001, msg); - assertTrue(search.totalRunLength <= search.optimizeCalled * 1001, msg); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - history = tpm.getSearchHistory(); - assertEquals(1, history.size()); - assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(0).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); - } - - // Close the parallel multistarter - tpm.close(); - } - - @Test - public void testTimedParallelReoptimizableMultistarterOneReopt() { - int numThreads = 1; - ArrayList searches = - new ArrayList(numThreads); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= numThreads; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - TimedParallelReoptimizableMultistarter tpm = - new TimedParallelReoptimizableMultistarter(searches, 1000); - assertEquals(1000, tpm.getTimeUnit()); - tpm.setTimeUnit(10); - assertEquals(10, tpm.getTimeUnit()); - assertTrue(tracker == tpm.getProgressTracker()); - assertTrue(problem == tpm.getProblem()); - assertEquals(0, tpm.getTotalRunLength()); - assertNull(tpm.getSearchHistory()); - long time1 = System.nanoTime(); - SolutionCostPair solution = tpm.reoptimize(8); - long time2 = System.nanoTime(); - int combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.optimizeCalled); - assertTrue(search.reoptimizeCalled > 0); - assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 1) * 1001); - assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - ArrayList> history = tpm.getSearchHistory(); - assertEquals(8, history.size()); - for (int i = 1; i < history.size(); i++) { - assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); - assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(i).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); - } - } - assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); - long actualRunTime = time2 - time1; - assertTrue( - actualRunTime >= 80000000, - "verifying runtime, actual=" + actualRunTime + " ns, should be at least 80000000 ns"); - - // verify can call reoptimize again - solution = tpm.reoptimize(1); - assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); - combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.optimizeCalled); - assertTrue(search.reoptimizeCalled > 0); - String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.reoptimizeCalled; - assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 2) * 1001, msg); - assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001, msg); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - history = tpm.getSearchHistory(); - assertEquals(1, history.size()); - assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(0).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); - } - - // Close the parallel multistarter - tpm.close(); - } - - @Test - public void testTimedParallelReoptimizableMultistarterThreeReopt() { - int numThreads = 3; - ArrayList searches = - new ArrayList(numThreads); - ProgressTracker tracker = new ProgressTracker(); - TestProblem problem = new TestProblem(); - for (int i = 1; i <= numThreads; i++) { - searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); - } - TimedParallelReoptimizableMultistarter tpm = - new TimedParallelReoptimizableMultistarter(searches, 1000); - assertEquals(1000, tpm.getTimeUnit()); - int timeUnit = 20; - tpm.setTimeUnit(timeUnit); - assertEquals(timeUnit, tpm.getTimeUnit()); - assertTrue(tracker == tpm.getProgressTracker()); - assertTrue(problem == tpm.getProblem()); - assertEquals(0, tpm.getTotalRunLength()); - assertNull(tpm.getSearchHistory()); - int reoptCount = 8; - long time1 = System.nanoTime(); - SolutionCostPair solution = tpm.reoptimize(reoptCount); - long time2 = System.nanoTime(); - int combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.optimizeCalled); - assertTrue(search.reoptimizeCalled > 0); - assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 1) * 1001); - assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - ArrayList> history = tpm.getSearchHistory(); - assertEquals(reoptCount, history.size()); - for (int i = 1; i < history.size(); i++) { - assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); - assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(i).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); - } - } - assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); - long actualRunTime = time2 - time1; - int minRunTime = timeUnit * reoptCount * 1000000; - assertTrue( - actualRunTime >= minRunTime, - "verifying runtime, actual=" - + actualRunTime - + " ns, should be at least " - + minRunTime - + " ns"); - - // verify can call reoptimize again - solution = tpm.reoptimize(1); - assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); - combinedRun = 0; - for (TestRestartedMetaheuristic search : searches) { - assertEquals(0, search.optimizeCalled); - assertTrue(search.reoptimizeCalled > 0); - String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.reoptimizeCalled; - assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 2) * 1001, msg); - assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001, msg); - combinedRun += search.totalRunLength; - } - assertEquals(combinedRun, tpm.getTotalRunLength()); - history = tpm.getSearchHistory(); - assertEquals(1, history.size()); - assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); - TestObject s = history.get(0).getSolution(); - if (s != null) { - assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); - } - - // Close the parallel multistarter - tpm.close(); - } - - private static class TestOptThrowsExceptions extends TestRestartedMetaheuristic { - - boolean throwException; - boolean returnsNull; - - public TestOptThrowsExceptions( - int id, ProgressTracker tracker, TestProblem problem) { - super(id, tracker, problem); - throwException = id == 2; - returnsNull = id == 3; - } - - @Override - public SolutionCostPair optimize(int runLength) { - optimizeCalled++; - if (throwException) { - throw new RuntimeException("Testing exception handling"); - } else if (returnsNull) { - return null; - } else { - TestObject obj = new TestObject(0); - return new SolutionCostPair(obj, problem.cost(obj), false); - } - } - - @Override - public SolutionCostPair reoptimize(int runLength) { - reoptimizeCalled++; - if (throwException) { - throw new RuntimeException("Testing exception handling"); - } else if (returnsNull) { - return null; - } else { - TestObject obj = new TestObject(0); - return new SolutionCostPair(obj, problem.cost(obj), false); - } - } - } - - private static class TestRestartedMetaheuristic - implements ReoptimizableMetaheuristic { - - private ProgressTracker tracker; - int id; - public volatile int optimizeCalled; - public volatile int reoptimizeCalled; - private SplittableRandom r; - public TestProblem problem; - public volatile int totalRunLength; - - public TestRestartedMetaheuristic( - int id, ProgressTracker tracker, TestProblem problem) { - this.id = id; - this.tracker = tracker; - this.problem = problem; - optimizeCalled = 0; - reoptimizeCalled = 0; - totalRunLength = 0; - r = new SplittableRandom(id); - } - - @Override - public SolutionCostPair optimize(int runLength) { - optimizeCalled++; - TestObject threadBest = new TestObject(r.nextInt(10000)); - totalRunLength++; - double bestCost = problem.cost(threadBest); - tracker.update(bestCost, threadBest, false); - while (!tracker.isStopped() && runLength > 0) { - runLength--; - TestObject candidate = new TestObject(r.nextInt(10000)); - totalRunLength++; - double cost = problem.cost(candidate); - if (cost < bestCost) { - threadBest = candidate; - bestCost = cost; - tracker.update(bestCost, threadBest, false); - } - } - return new SolutionCostPair(threadBest, problem.cost(threadBest), false); - } - - @Override - public SolutionCostPair reoptimize(int runLength) { - reoptimizeCalled++; - TestObject threadBest = new TestObject(r.nextInt(10000)); - totalRunLength++; - double bestCost = problem.cost(threadBest); - tracker.update(bestCost, threadBest, false); - while (!tracker.isStopped() && runLength > 0) { - runLength--; - TestObject candidate = new TestObject(r.nextInt(10000)); - totalRunLength++; - double cost = problem.cost(candidate); - if (cost < bestCost) { - threadBest = candidate; - bestCost = cost; - tracker.update(bestCost, threadBest, false); - } - } - return new SolutionCostPair(threadBest, problem.cost(threadBest), false); - } - - public TestRestartedMetaheuristic split() { - return new TestRestartedMetaheuristic(10 * id, tracker, problem); - } - - public ProgressTracker getProgressTracker() { - return tracker; - } - - public void setProgressTracker(ProgressTracker tracker) { - if (tracker != null) this.tracker = tracker; - } - - public OptimizationProblem getProblem() { - return problem; - } - - public long getTotalRunLength() { - return totalRunLength; - } - } - - private static class TestObject implements Copyable { - private int value; - - public TestObject(int value) { - this.value = value; - } - - public TestObject copy() { - return new TestObject(value); - } - - public int getValue() { - return value; - } - } - - private static class TestProblem implements OptimizationProblem { - public double cost(TestObject o) { - return o.getValue(); - } - - public boolean isMinCost(double c) { - return false; - } - - public double minCost() { - return -10000; - } - - public double value(TestObject o) { - return o.getValue(); - } - } - - private static class TestInterrupted extends TestRestartedMetaheuristic { - - public volatile int count; - - public TestInterrupted(int id, TestProblem problem, ProgressTracker tracker) { - super(id, tracker, problem); - } - - @Override - public SolutionCostPair optimize(int runLength) { - count++; - for (int i = 0; i < runLength; i++) { - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - TestObject obj = new TestObject(0); - return new SolutionCostPair(obj, problem.cost(obj), false); - } - } - return null; - } - - @Override - public SolutionCostPair reoptimize(int runLength) { - return optimize(runLength); - } - } - - private static class TestImprovementMade extends TestRestartedMetaheuristic { - - public volatile int count; - - public TestImprovementMade(int id, TestProblem problem, ProgressTracker tracker) { - super(id, tracker, problem); - } - - @Override - public SolutionCostPair optimize(int runLength) { - count++; - if (id == 1000) { - TestObject sol = new TestObject(10); - getProgressTracker().update(10, sol, false); - return new SolutionCostPair(sol, 10, false); - } else { - while (!getProgressTracker().containsIntCost()) { - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - } - } - return new SolutionCostPair(new TestObject(id - 1000), id - 1000, false); - } - } - - @Override - public SolutionCostPair reoptimize(int runLength) { - return optimize(runLength); - } - } } diff --git a/src/test/java/org/cicirello/search/concurrent/TimedParallelMultistarterValidator.java b/src/test/java/org/cicirello/search/concurrent/TimedParallelMultistarterValidator.java new file mode 100644 index 00000000..c863248b --- /dev/null +++ b/src/test/java/org/cicirello/search/concurrent/TimedParallelMultistarterValidator.java @@ -0,0 +1,252 @@ +/* + * Chips-n-Salsa: A library of parallel self-adaptive local search algorithms. + * Copyright (C) 2002-2023 Vincent A. Cicirello + * + * This file is part of Chips-n-Salsa (https://chips-n-salsa.cicirello.org/). + * + * Chips-n-Salsa is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chips-n-Salsa is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.cicirello.search.concurrent; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.SplittableRandom; +import org.cicirello.search.ProgressTracker; +import org.cicirello.search.ReoptimizableMetaheuristic; +import org.cicirello.search.SolutionCostPair; +import org.cicirello.search.problems.OptimizationProblem; +import org.cicirello.util.Copyable; +import org.junit.jupiter.api.*; + +/** Test validation common to multiple test classes for testing timed parallel multistarters. */ +public class TimedParallelMultistarterValidator { + + static class TestOptThrowsExceptions extends TestRestartedMetaheuristic { + + boolean throwException; + boolean returnsNull; + + public TestOptThrowsExceptions( + int id, ProgressTracker tracker, TestProblem problem) { + super(id, tracker, problem); + throwException = id == 2; + returnsNull = id == 3; + } + + @Override + public SolutionCostPair optimize(int runLength) { + optimizeCalled++; + if (throwException) { + throw new RuntimeException("Testing exception handling"); + } else if (returnsNull) { + return null; + } else { + TestObject obj = new TestObject(0); + return new SolutionCostPair(obj, problem.cost(obj), false); + } + } + + @Override + public SolutionCostPair reoptimize(int runLength) { + reoptimizeCalled++; + if (throwException) { + throw new RuntimeException("Testing exception handling"); + } else if (returnsNull) { + return null; + } else { + TestObject obj = new TestObject(0); + return new SolutionCostPair(obj, problem.cost(obj), false); + } + } + } + + static class TestRestartedMetaheuristic implements ReoptimizableMetaheuristic { + + private ProgressTracker tracker; + int id; + public volatile int optimizeCalled; + public volatile int reoptimizeCalled; + private SplittableRandom r; + public TestProblem problem; + public volatile int totalRunLength; + + public TestRestartedMetaheuristic( + int id, ProgressTracker tracker, TestProblem problem) { + this.id = id; + this.tracker = tracker; + this.problem = problem; + optimizeCalled = 0; + reoptimizeCalled = 0; + totalRunLength = 0; + r = new SplittableRandom(id); + } + + @Override + public SolutionCostPair optimize(int runLength) { + optimizeCalled++; + TestObject threadBest = new TestObject(r.nextInt(10000)); + totalRunLength++; + double bestCost = problem.cost(threadBest); + tracker.update(bestCost, threadBest, false); + while (!tracker.isStopped() && runLength > 0) { + runLength--; + TestObject candidate = new TestObject(r.nextInt(10000)); + totalRunLength++; + double cost = problem.cost(candidate); + if (cost < bestCost) { + threadBest = candidate; + bestCost = cost; + tracker.update(bestCost, threadBest, false); + } + } + return new SolutionCostPair(threadBest, problem.cost(threadBest), false); + } + + @Override + public SolutionCostPair reoptimize(int runLength) { + reoptimizeCalled++; + TestObject threadBest = new TestObject(r.nextInt(10000)); + totalRunLength++; + double bestCost = problem.cost(threadBest); + tracker.update(bestCost, threadBest, false); + while (!tracker.isStopped() && runLength > 0) { + runLength--; + TestObject candidate = new TestObject(r.nextInt(10000)); + totalRunLength++; + double cost = problem.cost(candidate); + if (cost < bestCost) { + threadBest = candidate; + bestCost = cost; + tracker.update(bestCost, threadBest, false); + } + } + return new SolutionCostPair(threadBest, problem.cost(threadBest), false); + } + + public TestRestartedMetaheuristic split() { + return new TestRestartedMetaheuristic(10 * id, tracker, problem); + } + + public ProgressTracker getProgressTracker() { + return tracker; + } + + public void setProgressTracker(ProgressTracker tracker) { + if (tracker != null) this.tracker = tracker; + } + + public OptimizationProblem getProblem() { + return problem; + } + + public long getTotalRunLength() { + return totalRunLength; + } + } + + static class TestObject implements Copyable { + private int value; + + public TestObject(int value) { + this.value = value; + } + + public TestObject copy() { + return new TestObject(value); + } + + public int getValue() { + return value; + } + } + + static class TestProblem implements OptimizationProblem { + public double cost(TestObject o) { + return o.getValue(); + } + + public boolean isMinCost(double c) { + return false; + } + + public double minCost() { + return -10000; + } + + public double value(TestObject o) { + return o.getValue(); + } + } + + static class TestInterrupted extends TestRestartedMetaheuristic { + + public volatile int count; + + public TestInterrupted(int id, TestProblem problem, ProgressTracker tracker) { + super(id, tracker, problem); + } + + @Override + public SolutionCostPair optimize(int runLength) { + count++; + for (int i = 0; i < runLength; i++) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + TestObject obj = new TestObject(0); + return new SolutionCostPair(obj, problem.cost(obj), false); + } + } + return null; + } + + @Override + public SolutionCostPair reoptimize(int runLength) { + return optimize(runLength); + } + } + + static class TestImprovementMade extends TestRestartedMetaheuristic { + + public volatile int count; + + public TestImprovementMade(int id, TestProblem problem, ProgressTracker tracker) { + super(id, tracker, problem); + } + + @Override + public SolutionCostPair optimize(int runLength) { + count++; + if (id == 1000) { + TestObject sol = new TestObject(10); + getProgressTracker().update(10, sol, false); + return new SolutionCostPair(sol, 10, false); + } else { + while (!getProgressTracker().containsIntCost()) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + } + } + return new SolutionCostPair(new TestObject(id - 1000), id - 1000, false); + } + } + + @Override + public SolutionCostPair reoptimize(int runLength) { + return optimize(runLength); + } + } +} diff --git a/src/test/java/org/cicirello/search/concurrent/TimedParallelReoptimizableMultistarterOptimizeTests.java b/src/test/java/org/cicirello/search/concurrent/TimedParallelReoptimizableMultistarterOptimizeTests.java new file mode 100644 index 00000000..f456f969 --- /dev/null +++ b/src/test/java/org/cicirello/search/concurrent/TimedParallelReoptimizableMultistarterOptimizeTests.java @@ -0,0 +1,327 @@ +/* + * Chips-n-Salsa: A library of parallel self-adaptive local search algorithms. + * Copyright (C) 2002-2023 Vincent A. Cicirello + * + * This file is part of Chips-n-Salsa (https://chips-n-salsa.cicirello.org/). + * + * Chips-n-Salsa is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chips-n-Salsa is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.cicirello.search.concurrent; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import org.cicirello.search.ProgressTracker; +import org.cicirello.search.SolutionCostPair; +import org.junit.jupiter.api.*; + +/** JUnit tests for the TimedParallelReoptimizableMultistarter optimize method. */ +public class TimedParallelReoptimizableMultistarterOptimizeTests + extends TimedParallelMultistarterValidator { + + @Test + public void testParallelOptimizeSomeThreadFindsBest_Reopt() { + class ParallelSearch implements Runnable { + + TimedParallelReoptimizableMultistarter restarter; + ArrayList metaheuristics; + ProgressTracker tracker; + + ParallelSearch() { + tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + metaheuristics = new ArrayList(); + metaheuristics.add(new TestInterrupted(1, problem, tracker)); + restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + } + + @Override + public void run() { + restarter.setTimeUnit(10); + restarter.optimize(1000); + } + } + + ParallelSearch search = new ParallelSearch(); + Thread t = new Thread(search); + t.start(); + while (search.metaheuristics.get(0).count < 1) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + break; + } + } + // replaced deprecated call to setFoundBest() + search.tracker.update(-10000.0, new TestObject(-10000), true); + try { + t.join(1000); + } catch (InterruptedException ex) { + } + assertFalse(t.isAlive()); + search.restarter.close(); + } + + @Test + public void testParallelOptimizeImprovementMade_Reopt() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + ArrayList metaheuristics = new ArrayList(); + metaheuristics.add(new TestImprovementMade(1000, problem, tracker)); + metaheuristics.add(new TestImprovementMade(1001, problem, tracker)); + metaheuristics.add(new TestImprovementMade(1002, problem, tracker)); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + restarter.setTimeUnit(100); + assertNotNull(restarter.optimize(1)); + restarter.close(); + } + + @Test + public void testInterruptParallelOptimize_Reopt() { + class ParallelSearch implements Runnable { + + TimedParallelReoptimizableMultistarter restarter; + ArrayList metaheuristics; + + ParallelSearch() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + metaheuristics = new ArrayList(); + metaheuristics.add(new TestInterrupted(1, problem, tracker)); + metaheuristics.add(new TestInterrupted(2, problem, tracker)); + metaheuristics.add(new TestInterrupted(3, problem, tracker)); + restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + } + + @Override + public void run() { + restarter.setTimeUnit(10); + restarter.optimize(1000); + } + } + + ParallelSearch search = new ParallelSearch(); + Thread t = new Thread(search); + t.start(); + while (search.metaheuristics.get(0).count < 1) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + break; + } + } + t.interrupt(); + try { + t.join(1000); + } catch (InterruptedException ex) { + } + assertFalse(t.isAlive()); + search.restarter.close(); + } + + @Test + public void testOptimizeMetaheuristicThrowsException_Reopt() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + ArrayList metaheuristics = new ArrayList(); + metaheuristics.add(new TestOptThrowsExceptions(1, tracker, problem)); + metaheuristics.add(new TestOptThrowsExceptions(2, tracker, problem)); + metaheuristics.add(new TestOptThrowsExceptions(3, tracker, problem)); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + restarter.setTimeUnit(100); + SolutionCostPair solution = restarter.optimize(1); + assertTrue(solution == null || 0 == solution.getCost()); + restarter.close(); + } + + @Test + public void testTimedParallelReoptimizableMultistarterOne() { + int numThreads = 1; + ArrayList searches = + new ArrayList(numThreads); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= numThreads; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + TimedParallelReoptimizableMultistarter tpm = + new TimedParallelReoptimizableMultistarter(searches, 1000); + assertEquals(1000, tpm.getTimeUnit()); + tpm.setTimeUnit(10); + assertEquals(10, tpm.getTimeUnit()); + assertTrue(tracker == tpm.getProgressTracker()); + assertTrue(problem == tpm.getProblem()); + assertEquals(0, tpm.getTotalRunLength()); + assertNull(tpm.getSearchHistory()); + long time1 = System.nanoTime(); + SolutionCostPair solution = tpm.optimize(8); + long time2 = System.nanoTime(); + int combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.reoptimizeCalled); + assertTrue(search.optimizeCalled > 0); + assertTrue(search.totalRunLength >= (search.optimizeCalled - 1) * 1001); + assertTrue(search.totalRunLength <= search.optimizeCalled * 1001); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + ArrayList> history = tpm.getSearchHistory(); + assertEquals(8, history.size()); + for (int i = 1; i < history.size(); i++) { + assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); + assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(i).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); + } + } + assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); + long actualRunTime = time2 - time1; + assertTrue( + actualRunTime >= 80000000, + "verifying runtime, actual=" + actualRunTime + " ns, should be at least 80000000 ns"); + + // verify can call optimize again + solution = tpm.optimize(1); + assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); + combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.reoptimizeCalled); + assertTrue(search.optimizeCalled > 0); + String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.optimizeCalled; + assertTrue(search.totalRunLength >= (search.optimizeCalled - 2) * 1001, msg); + assertTrue(search.totalRunLength <= search.optimizeCalled * 1001, msg); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + history = tpm.getSearchHistory(); + assertEquals(1, history.size()); + assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(0).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); + } + + // Close the parallel multistarter + tpm.close(); + } + + @Test + public void testTimedParallelReoptimizableMultistarterThree() { + int numThreads = 3; + final int NUM_TIME_CYCLES = 20; + ArrayList searches = + new ArrayList(numThreads); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= numThreads; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + TimedParallelReoptimizableMultistarter tpm = + new TimedParallelReoptimizableMultistarter(searches, 1000); + assertEquals(1000, tpm.getTimeUnit()); + tpm.setTimeUnit(10); + assertEquals(10, tpm.getTimeUnit()); + assertTrue(tracker == tpm.getProgressTracker()); + assertTrue(problem == tpm.getProblem()); + assertEquals(0, tpm.getTotalRunLength()); + assertNull(tpm.getSearchHistory()); + long time1 = System.nanoTime(); + SolutionCostPair solution = tpm.optimize(NUM_TIME_CYCLES); + long time2 = System.nanoTime(); + int combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.reoptimizeCalled); + assertTrue(search.optimizeCalled > 0); + assertTrue(search.totalRunLength >= (search.optimizeCalled - 1) * 1001); + assertTrue(search.totalRunLength <= search.optimizeCalled * 1001); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + ArrayList> history = tpm.getSearchHistory(); + assertEquals(NUM_TIME_CYCLES, history.size()); + for (int i = 1; i < history.size(); i++) { + assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); + assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(i).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); + } + } + assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); + long actualRunTime = time2 - time1; + assertTrue( + actualRunTime >= 80000000, + "verifying runtime, actual=" + actualRunTime + " ns, should be at least 80000000 ns"); + + // verify can call optimize again + tpm.setTimeUnit(20); + solution = tpm.optimize(1); + assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); + combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.reoptimizeCalled); + assertTrue(search.optimizeCalled > 0); + String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.optimizeCalled; + assertTrue(search.totalRunLength >= (search.optimizeCalled - 2) * 1001, msg); + assertTrue(search.totalRunLength <= search.optimizeCalled * 1001, msg); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + history = tpm.getSearchHistory(); + assertEquals(1, history.size()); + assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(0).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); + } + + // Close the parallel multistarter + tpm.close(); + } + + @Test + public void testOptimizeStoppedFoundBest() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(heur, 1, 1); + long expected = restarter.getTotalRunLength(); + // replaced call to deprecated setFoundBest() + restarter.getProgressTracker().update(0, new TestObject(0), true); + restarter.optimize(1); + assertEquals(expected, restarter.getTotalRunLength()); + restarter.close(); + } + + @Test + public void testOptimizeExceptions() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(heur, 1, 1); + restarter.close(); + IllegalStateException thrown = + assertThrows(IllegalStateException.class, () -> restarter.optimize(1)); + } +} diff --git a/src/test/java/org/cicirello/search/concurrent/TimedParallelReoptimizableMultistarterTests.java b/src/test/java/org/cicirello/search/concurrent/TimedParallelReoptimizableMultistarterTests.java new file mode 100644 index 00000000..fa83237d --- /dev/null +++ b/src/test/java/org/cicirello/search/concurrent/TimedParallelReoptimizableMultistarterTests.java @@ -0,0 +1,528 @@ +/* + * Chips-n-Salsa: A library of parallel self-adaptive local search algorithms. + * Copyright (C) 2002-2023 Vincent A. Cicirello + * + * This file is part of Chips-n-Salsa (https://chips-n-salsa.cicirello.org/). + * + * Chips-n-Salsa is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chips-n-Salsa is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.cicirello.search.concurrent; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; +import org.cicirello.search.ProgressTracker; +import org.cicirello.search.SolutionCostPair; +import org.cicirello.search.restarts.ConstantRestartSchedule; +import org.cicirello.search.restarts.ParallelVariableAnnealingLength; +import org.cicirello.search.restarts.ReoptimizableMultistarter; +import org.junit.jupiter.api.*; + +/** JUnit tests for the TimedParallelReoptimizableMultistarter. */ +public class TimedParallelReoptimizableMultistarterTests + extends TimedParallelMultistarterValidator { + + @Test + public void testParallelReoptimizeSomeThreadFindsBest_Reopt() { + class ParallelSearch implements Runnable { + + TimedParallelReoptimizableMultistarter restarter; + ArrayList metaheuristics; + ProgressTracker tracker; + + ParallelSearch() { + tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + metaheuristics = new ArrayList(); + metaheuristics.add(new TestInterrupted(1, problem, tracker)); + restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + } + + @Override + public void run() { + restarter.setTimeUnit(10); + restarter.reoptimize(1000); + } + } + + ParallelSearch search = new ParallelSearch(); + Thread t = new Thread(search); + t.start(); + while (search.metaheuristics.get(0).count < 1) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + break; + } + } + // replaced deprecated call to setFoundBest() + search.tracker.update(-10000.0, new TestObject(-10000), true); + try { + t.join(1000); + } catch (InterruptedException ex) { + } + assertFalse(t.isAlive()); + search.restarter.close(); + } + + @Test + public void testParallelReoptimizeImprovementMade_Reopt() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + ArrayList metaheuristics = new ArrayList(); + metaheuristics.add(new TestImprovementMade(1000, problem, tracker)); + metaheuristics.add(new TestImprovementMade(1001, problem, tracker)); + metaheuristics.add(new TestImprovementMade(1002, problem, tracker)); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + restarter.setTimeUnit(50); + assertNotNull(restarter.reoptimize(1)); + restarter.close(); + } + + @Test + public void testInterruptParallelReoptimize_Reopt() { + class ParallelSearch implements Runnable { + + TimedParallelReoptimizableMultistarter restarter; + ArrayList metaheuristics; + + ParallelSearch() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + metaheuristics = new ArrayList(); + metaheuristics.add(new TestInterrupted(1, problem, tracker)); + metaheuristics.add(new TestInterrupted(2, problem, tracker)); + metaheuristics.add(new TestInterrupted(3, problem, tracker)); + restarter = new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + } + + @Override + public void run() { + restarter.setTimeUnit(10); + restarter.reoptimize(1000); + } + } + + ParallelSearch search = new ParallelSearch(); + Thread t = new Thread(search); + t.start(); + while (search.metaheuristics.get(0).count < 1) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + break; + } + } + t.interrupt(); + try { + t.join(1000); + } catch (InterruptedException ex) { + } + assertFalse(t.isAlive()); + search.restarter.close(); + } + + @Test + public void testReoptimizeMetaheuristicThrowsException_Reopt() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + + ArrayList metaheuristics = new ArrayList(); + metaheuristics.add(new TestOptThrowsExceptions(1, tracker, problem)); + metaheuristics.add(new TestOptThrowsExceptions(2, tracker, problem)); + metaheuristics.add(new TestOptThrowsExceptions(3, tracker, problem)); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(metaheuristics, 1); + restarter.setTimeUnit(100); + SolutionCostPair solution = restarter.reoptimize(1); + assertTrue(solution == null || 0 == solution.getCost()); + restarter.close(); + } + + @Test + public void testReoptimizeExceptions() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(heur, 1, 1); + restarter.close(); + IllegalStateException thrown = + assertThrows(IllegalStateException.class, () -> restarter.reoptimize(1)); + } + + @Test + public void testSetProgressTrackerNull_Reopt() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(heur, 1, 1); + restarter.setProgressTracker(null); + assertEquals(tracker, restarter.getProgressTracker()); + restarter.close(); + } + + @Test + public void testReoptimizeStoppedFoundBest() { + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + TestRestartedMetaheuristic heur = new TestRestartedMetaheuristic(1, tracker, problem); + TimedParallelReoptimizableMultistarter restarter = + new TimedParallelReoptimizableMultistarter(heur, 1, 1); + long expected = restarter.getTotalRunLength(); + // replaced call to deprecated setFoundBest() + restarter.getProgressTracker().update(0, new TestObject(0), true); + restarter.reoptimize(1); + assertEquals(expected, restarter.getTotalRunLength()); + restarter.close(); + } + + @Test + public void testSetTimeUnitException_Reopt() { + final int T = 1; + ArrayList searches = new ArrayList(T); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= T; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + List schedules = + ParallelVariableAnnealingLength.createRestartSchedules(T); + final TimedParallelReoptimizableMultistarter tpm = + new TimedParallelReoptimizableMultistarter(searches, schedules); + IllegalArgumentException thrown = + assertThrows(IllegalArgumentException.class, () -> tpm.setTimeUnit(0)); + tpm.close(); + } + + @Test + public void testTimedParallelMultistarterVariousConstructorsExceptions_Reopt() { + final int T = 3; + ArrayList searches = new ArrayList(T); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= T; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + List schedules = + ParallelVariableAnnealingLength.createRestartSchedules(T + 1); + IllegalArgumentException thrown = + assertThrows( + IllegalArgumentException.class, + () -> new TimedParallelReoptimizableMultistarter(searches, schedules)); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> new TimedParallelReoptimizableMultistarter(searches, 0)); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + searches.add( + new TestRestartedMetaheuristic( + T + 1, new ProgressTracker(), problem)); + new TimedParallelReoptimizableMultistarter(searches, schedules); + }); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + new TimedParallelReoptimizableMultistarter(searches, 1); + }); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + ArrayList> starters = + new ArrayList>(T); + for (int i = 0; i <= T; i++) { + starters.add(new ReoptimizableMultistarter(searches.get(i), 1000)); + } + new TimedParallelReoptimizableMultistarter(starters); + }); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + searches.set(T, new TestRestartedMetaheuristic(T + 1, tracker, new TestProblem())); + new TimedParallelReoptimizableMultistarter(searches, schedules); + }); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + new TimedParallelReoptimizableMultistarter(searches, 1); + }); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> { + ArrayList> starters = + new ArrayList>(T); + for (int i = 0; i <= T; i++) { + starters.add(new ReoptimizableMultistarter(searches.get(i), 1000)); + } + new TimedParallelReoptimizableMultistarter(starters); + }); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> new TimedParallelReoptimizableMultistarter(searches.get(0), 0, T)); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> new TimedParallelReoptimizableMultistarter(searches.get(0), 1, 0)); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> + new TimedParallelReoptimizableMultistarter( + searches.get(0), new ConstantRestartSchedule(1000), 0)); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> + new TimedParallelReoptimizableMultistarter( + new ReoptimizableMultistarter(searches.get(0), 1000), 0)); + thrown = + assertThrows( + IllegalArgumentException.class, + () -> + new TimedParallelReoptimizableMultistarter( + searches.get(0), new ArrayList())); + } + + @Test + public void testTimedParallelReoptimizableMultistarterVariousConstructors() { + final int T = 3; + ArrayList searches = new ArrayList(T); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= T; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + List schedules = + ParallelVariableAnnealingLength.createRestartSchedules(T); + TimedParallelReoptimizableMultistarter tpm = + new TimedParallelReoptimizableMultistarter(searches, schedules); + tpm.close(); + tpm = new TimedParallelReoptimizableMultistarter(searches.get(0), 1000, T); + tpm.close(); + tpm = + new TimedParallelReoptimizableMultistarter( + searches.get(0), new ConstantRestartSchedule(1000), T); + tpm.close(); + tpm = new TimedParallelReoptimizableMultistarter(searches.get(0), schedules); + tpm.close(); + tpm = + new TimedParallelReoptimizableMultistarter( + new ReoptimizableMultistarter(searches.get(0), 1000), T); + tpm.close(); + ArrayList> starters = + new ArrayList>(T); + for (int i = 0; i < T; i++) { + starters.add(new ReoptimizableMultistarter(searches.get(0), 1000)); + } + tpm = new TimedParallelReoptimizableMultistarter(starters); + tpm.close(); + TimedParallelReoptimizableMultistarter split = tpm.split(); + split.close(); + assertTrue(split != tpm); + tpm = new TimedParallelReoptimizableMultistarter(starters); + split = tpm.split(); + tpm.close(); + split.close(); + assertTrue(split != tpm); + } + + @Test + public void testTimedParallelReoptimizableMultistarterSetProgressTracker() { + final int T = 3; + ArrayList searches = new ArrayList(T); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= T; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + List schedules = + ParallelVariableAnnealingLength.createRestartSchedules(T); + TimedParallelReoptimizableMultistarter tpm = + new TimedParallelReoptimizableMultistarter(searches, schedules); + ProgressTracker tracker2 = new ProgressTracker(); + tpm.setProgressTracker(tracker2); + tpm.close(); + assertTrue(tracker2 == tpm.getProgressTracker()); + for (TestRestartedMetaheuristic s : searches) { + assertTrue(tracker2 == s.getProgressTracker()); + } + } + + @Test + public void testTimedParallelReoptimizableMultistarterOneReopt() { + int numThreads = 1; + ArrayList searches = + new ArrayList(numThreads); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= numThreads; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + TimedParallelReoptimizableMultistarter tpm = + new TimedParallelReoptimizableMultistarter(searches, 1000); + assertEquals(1000, tpm.getTimeUnit()); + tpm.setTimeUnit(10); + assertEquals(10, tpm.getTimeUnit()); + assertTrue(tracker == tpm.getProgressTracker()); + assertTrue(problem == tpm.getProblem()); + assertEquals(0, tpm.getTotalRunLength()); + assertNull(tpm.getSearchHistory()); + long time1 = System.nanoTime(); + SolutionCostPair solution = tpm.reoptimize(8); + long time2 = System.nanoTime(); + int combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.optimizeCalled); + assertTrue(search.reoptimizeCalled > 0); + assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 1) * 1001); + assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + ArrayList> history = tpm.getSearchHistory(); + assertEquals(8, history.size()); + for (int i = 1; i < history.size(); i++) { + assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); + assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(i).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); + } + } + assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); + long actualRunTime = time2 - time1; + assertTrue( + actualRunTime >= 80000000, + "verifying runtime, actual=" + actualRunTime + " ns, should be at least 80000000 ns"); + + // verify can call reoptimize again + solution = tpm.reoptimize(1); + assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); + combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.optimizeCalled); + assertTrue(search.reoptimizeCalled > 0); + String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.reoptimizeCalled; + assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 2) * 1001, msg); + assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001, msg); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + history = tpm.getSearchHistory(); + assertEquals(1, history.size()); + assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(0).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); + } + + // Close the parallel multistarter + tpm.close(); + } + + @Test + public void testTimedParallelReoptimizableMultistarterThreeReopt() { + int numThreads = 3; + ArrayList searches = + new ArrayList(numThreads); + ProgressTracker tracker = new ProgressTracker(); + TestProblem problem = new TestProblem(); + for (int i = 1; i <= numThreads; i++) { + searches.add(new TestRestartedMetaheuristic(i, tracker, problem)); + } + TimedParallelReoptimizableMultistarter tpm = + new TimedParallelReoptimizableMultistarter(searches, 1000); + assertEquals(1000, tpm.getTimeUnit()); + int timeUnit = 20; + tpm.setTimeUnit(timeUnit); + assertEquals(timeUnit, tpm.getTimeUnit()); + assertTrue(tracker == tpm.getProgressTracker()); + assertTrue(problem == tpm.getProblem()); + assertEquals(0, tpm.getTotalRunLength()); + assertNull(tpm.getSearchHistory()); + int reoptCount = 8; + long time1 = System.nanoTime(); + SolutionCostPair solution = tpm.reoptimize(reoptCount); + long time2 = System.nanoTime(); + int combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.optimizeCalled); + assertTrue(search.reoptimizeCalled > 0); + assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 1) * 1001); + assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + ArrayList> history = tpm.getSearchHistory(); + assertEquals(reoptCount, history.size()); + for (int i = 1; i < history.size(); i++) { + assertTrue(history.get(i).getCostDouble() <= history.get(i - 1).getCostDouble()); + assertTrue(history.get(i).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(i).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(i).getCostDouble(), 0.0); + } + } + assertEquals(solution.getCostDouble(), tracker.getCostDouble(), 0.0); + long actualRunTime = time2 - time1; + int minRunTime = timeUnit * reoptCount * 1000000; + assertTrue( + actualRunTime >= minRunTime, + "verifying runtime, actual=" + + actualRunTime + + " ns, should be at least " + + minRunTime + + " ns"); + + // verify can call reoptimize again + solution = tpm.reoptimize(1); + assertTrue(solution.getCostDouble() >= tracker.getCostDouble()); + combinedRun = 0; + for (TestRestartedMetaheuristic search : searches) { + assertEquals(0, search.optimizeCalled); + assertTrue(search.reoptimizeCalled > 0); + String msg = "trl=" + search.totalRunLength + ", #optCalled=" + search.reoptimizeCalled; + assertTrue(search.totalRunLength >= (search.reoptimizeCalled - 2) * 1001, msg); + assertTrue(search.totalRunLength <= search.reoptimizeCalled * 1001, msg); + combinedRun += search.totalRunLength; + } + assertEquals(combinedRun, tpm.getTotalRunLength()); + history = tpm.getSearchHistory(); + assertEquals(1, history.size()); + assertTrue(history.get(0).getCostDouble() >= tracker.getCostDouble()); + TestObject s = history.get(0).getSolution(); + if (s != null) { + assertEquals(problem.cost(s), history.get(0).getCostDouble(), 0.0); + } + + // Close the parallel multistarter + tpm.close(); + } +}