From 5ceb22efac1dc75b64835f027b996e393df05d5c Mon Sep 17 00:00:00 2001 From: "pablo.rodriguez.mier" Date: Thu, 7 Nov 2013 12:18:48 +0100 Subject: [PATCH] SetCoverIterator fixed and new test added. Close #35 and #38 --- .../combinatorial/SetCoverIterator.java | 146 +++++++++++------- .../combinatorial/SetCoverIteratorTest.java | 27 +++- 2 files changed, 112 insertions(+), 61 deletions(-) diff --git a/hipster-core/src/main/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIterator.java b/hipster-core/src/main/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIterator.java index d1626d7..ef560ec 100644 --- a/hipster-core/src/main/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIterator.java +++ b/hipster-core/src/main/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIterator.java @@ -59,23 +59,22 @@ */ public class SetCoverIterator implements Iterator>> { - // Ordered map (descending by key) with the available sets - // ordered by size and mapped back to their bitset representation - private final TreeMap, BitSet> orderedSets; + + // Maps bitset with their set representation - private final Map> bitsetMap; + private final Map> bitsetMap = new HashMap>(); // List of subsets, ordered by size private final List bitsetList; // Holds an ordered list with all possible elements - private final List elements; + private List elements; // Bit size used to store the information of all elements private int size; - // This collection stores the solutions found - private List>> solutions; + // Buffer queue with the non-consumed solutions + private Queue>> buffer = new LinkedList>>(); + // List with all solutions. This list is required in order to test dominance + private final List>> solutions = new LinkedList>>(); // Queue used for BFS. A synchronized queue is not required - private Queue queue; - // Next element index - private int nextElementIndex = 0; + private final Queue queue = new LinkedList(); private Set> nextElement = null; // Use parallelization private boolean parallelized = false; @@ -168,17 +167,33 @@ Set candidates() { return candidateStates; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + State state = (State) o; + + if (previous != null ? !previous.equals(state.previous) : state.previous != null) return false; + if (!selected.equals(state.selected)) return false; + if (!statebits.equals(state.statebits)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = previous != null ? previous.hashCode() : 0; + result = 31 * result + selected.hashCode(); + result = 31 * result + statebits.hashCode(); + return result; + } } public SetCoverIterator(Set> sets) { - - this.solutions = new LinkedList>>(); - // Obtain the elements from the sets - this.elements = elements(sets); - this.bitsetList = new ArrayList(); - this.size = this.elements.size(); - // Sort the collection of sets by the their size - this.orderedSets = new TreeMap, BitSet>( + // Ordered map (descending by key) with the available sets + // ordered by size and mapped back to their bitset representation + TreeMap, BitSet> orderedSets = new TreeMap, BitSet>( new Comparator>() { public int compare(Set o1, Set o2) { if (o2.size() > o1.size()) { @@ -187,8 +202,9 @@ public int compare(Set o1, Set o2) { return -1; } }); - - this.bitsetMap = new HashMap>(); + // Obtain the elements from the sets + this.elements = elements(sets); + this.size = this.elements.size(); // Initialize the values of the bitset list (there are m sets) for (Set set : sets) { @@ -198,17 +214,18 @@ public int compare(Set o1, Set o2) { // b(j)=1 if the element j appears on set(i) b.set(j, set.contains(this.elements.get(j))); } - this.orderedSets.put(set, b); + orderedSets.put(set, b); this.bitsetMap.put(b, set); } - + // TODO; Make unmodifiable + this.bitsetList = new ArrayList(orderedSets.size()); // Insert ordered bitsets into a list - for (Entry, BitSet> entry : this.orderedSets.entrySet()) { - this.bitsetList.add(entry.getValue()); + while(!orderedSets.isEmpty()){ + this.bitsetList.add(orderedSets.pollFirstEntry().getValue()); } - // Initialize the queue and put the first states to explore - this.queue = new LinkedList(new State().candidates()); + this.queue.add(new State()); + //this.queue.addAll(new State().candidates()); } @@ -274,26 +291,16 @@ private List elements(Collection> sets) { return new ArrayList(universe); } - /** - * Parallelized version of the Iterative Set Cover - * - * @return Iterator with all combinations of sets - * @throws ExecutionException - * @throws InterruptedException - */ - - public boolean hasNext() { - - // Breadth-First-Search, parallelized and synchronized by levels. - // In each step, take all states in the same level, process them and - // collect all new candidate states to process in the next step - - while (this.solutions.size() <= this.nextElementIndex - && !this.queue.isEmpty()) { - + private void compute(){ + // Start processing until queue is empty or there are solutions in the buffer + while(!this.queue.isEmpty() && this.buffer.isEmpty()){ + // Process all elements in the queue without removing them Collection nextLevel = null; if (parallelized) { try { + // Breadth-First-Search, parallelized and synchronized by levels. + // In each step, take all states in the same level, process them and + // collect all new candidate states to process in the next step nextLevel = parallelSearch(); } catch (ExecutionException e) { e.printStackTrace(); @@ -303,36 +310,59 @@ public boolean hasNext() { } else { nextLevel = sequentialSearch(); } - - - // Empty the queue + // Elements in the queue were processed. Clear it queue.clear(); - - // Take the results obtained by each thread + // Take the results obtained for (Result result : nextLevel) { + // Add the candidates for the next level to the queue this.queue.addAll(result.candidates); + // Fill the buffer with the solutions + this.buffer.addAll(result.solutions); this.solutions.addAll(result.solutions); } } - // Get the next solution - if (this.nextElementIndex < solutions.size()) { - this.nextElement = solutions.get(this.nextElementIndex); - this.nextElementIndex++; + } + + public Set> next() { + // Check buffer + if (!this.buffer.isEmpty()){ + return this.buffer.poll(); } else { - this.nextElement = null; + // Compute and return + compute(); + // Can be null! + return this.buffer.poll(); } + } - return this.nextElement != null; + public boolean hasNext() { + // Check if the is a non consumed solution + if (this.nextElement != null){ + return true; + } + // Check if there are more solutions in the buffer + if (!this.buffer.isEmpty()){ + return true; + } + // At this point there are no solutions, we have to process the node queue + // to find new solutions. If the queue is empty, process is over. + if (this.queue.isEmpty()){ + return false; + } + // To answer the hasNext question, we have to check if there are more + // solutions or not. + compute(); + return !this.buffer.isEmpty(); } private Collection sequentialSearch() { - Collection results = new ArrayList(queue.size()); + Collection results = new ArrayList(this.queue.size()); for (State state : this.queue) { Set candidates = new HashSet(); Collection>> localSolutions = new HashSet>>(); for (State candidate : state.candidates()) { Set> candidateSets = candidate.stateSets(); - if (!isDominated(solutions, candidateSets)) { + if (!isDominated(this.solutions, candidateSets)) { if (candidate.isFinal()) { localSolutions.add(candidateSets); } else { @@ -368,9 +398,7 @@ public Result apply(State state) { }).values(); } - public Set> next() { - return this.nextElement; - } + public void remove() { throw new UnsupportedOperationException(); diff --git a/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIteratorTest.java b/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIteratorTest.java index 18b15d2..ab727aa 100644 --- a/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIteratorTest.java +++ b/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/combinatorial/SetCoverIteratorTest.java @@ -18,15 +18,38 @@ import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Set; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class SetCoverIteratorTest { @Test - public void testIterativeSetCover01() { + public void testOneElement(){ + Set> sets = new HashSet>(); + sets.add(Collections.singleton("A")); + SetCoverIterator it = new SetCoverIterator(sets); + assertTrue(it.hasNext()); + assertEquals(1, it.next().size()); + } + + @Test + public void testTwoElements(){ + Set> sets = new HashSet>(); + sets.add(Collections.singleton("A")); + sets.add(Collections.singleton("B")); + SetCoverIterator it = new SetCoverIterator(sets); + assertTrue(it.hasNext()); + assertEquals(2, it.next().size()); + assertFalse(it.hasNext()); + } + + @Test + public void testOneSingleSolution() { // Single solution [1,2],[3,4],[5,6] Set> solution = new HashSet>(); solution.add(new HashSet(Arrays.asList("1", "2"))); @@ -51,7 +74,7 @@ public void testIterativeSetCover01() { } @Test - public void testIterativeSetCover02() { + public void testMultipleSolutions() { Set> sets = new HashSet>(); sets.add(new HashSet(Arrays.asList("3", "7")));