/* * Copyright 2012 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.optaplanner.core.impl.heuristic.selector.move.composite; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.optaplanner.core.impl.heuristic.move.CompositeMove; import org.optaplanner.core.impl.heuristic.move.Move; import org.optaplanner.core.impl.heuristic.move.NoChangeMove; import org.optaplanner.core.impl.heuristic.selector.common.iterator.SelectionIterator; import org.optaplanner.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator; import org.optaplanner.core.impl.heuristic.selector.move.MoveSelector; /** * A {@link CompositeMoveSelector} that cartesian products 2 or more {@link MoveSelector}s. * <p> * For example: a cartesian product of {A, B, C} and {X, Y} will result in {AX, AY, BX, BY, CX, CY}. * <p> * Warning: there is no duplicated {@link Move} check, so union of {A, B} and {B} will result in {AB, BB}. * @see CompositeMoveSelector */ public class CartesianProductMoveSelector extends CompositeMoveSelector { private static final Move EMPTY_MARK = new NoChangeMove(); private final boolean ignoreEmptyChildIterators; public CartesianProductMoveSelector(List<MoveSelector> childMoveSelectorList, boolean ignoreEmptyChildIterators, boolean randomSelection) { super(childMoveSelectorList, randomSelection); this.ignoreEmptyChildIterators = ignoreEmptyChildIterators; } // ************************************************************************ // Worker methods // ************************************************************************ @Override public boolean isNeverEnding() { if (randomSelection) { return true; } else { // Only the last childMoveSelector can be neverEnding return !childMoveSelectorList.isEmpty() && childMoveSelectorList.get(childMoveSelectorList.size() - 1).isNeverEnding(); } } @Override public long getSize() { long size = 0L; for (MoveSelector moveSelector : childMoveSelectorList) { long childSize = moveSelector.getSize(); if (childSize == 0L) { if (!ignoreEmptyChildIterators) { return 0L; } // else ignore that child } else { if (size == 0L) { // There must be at least 1 non-empty child to change the size from 0 size = childSize; } else { size *= childSize; } } } return size; } @Override public Iterator<Move> iterator() { if (!randomSelection) { return new OriginalCartesianProductMoveIterator(); } else { return new RandomCartesianProductMoveIterator(); } } public class OriginalCartesianProductMoveIterator extends UpcomingSelectionIterator<Move> { private List<Iterator<Move>> moveIteratorList; private Move[] subSelections; public OriginalCartesianProductMoveIterator() { moveIteratorList = new ArrayList<>(childMoveSelectorList.size()); for (int i = 0; i < childMoveSelectorList.size(); i++) { moveIteratorList.add(null); } subSelections = null; } @Override protected Move createUpcomingSelection() { int childSize = moveIteratorList.size(); int startingIndex; Move[] moveList = new Move[childSize]; if (subSelections == null) { startingIndex = -1; } else { startingIndex = childSize - 1; while (startingIndex >= 0) { Iterator<Move> moveIterator = moveIteratorList.get(startingIndex); if (moveIterator.hasNext()) { break; } startingIndex--; } if (startingIndex < 0) { return noUpcomingSelection(); } // Clone to avoid CompositeMove corruption System.arraycopy(subSelections, 0, moveList, 0, startingIndex); moveList[startingIndex] = moveIteratorList.get(startingIndex).next(); // Increment the 4 in 004999 } for (int i = startingIndex + 1; i < childSize; i++) { // Increment the 9's in 004999 Iterator<Move> moveIterator = childMoveSelectorList.get(i).iterator(); moveIteratorList.set(i, moveIterator); Move next; if (!moveIterator.hasNext()) { // in case a moveIterator is empty if (ignoreEmptyChildIterators) { next = EMPTY_MARK; } else { return noUpcomingSelection(); } } else { next = moveIterator.next(); } moveList[i] = next; } // No need to clone to avoid CompositeMove corruption because subSelections's elements never change subSelections = moveList; if (ignoreEmptyChildIterators) { // Clone because EMPTY_MARK should survive in subSelections Move[] newMoveList = new Move[childSize]; int newSize = 0; for (int i = 0; i < childSize; i++) { if (moveList[i] != EMPTY_MARK) { newMoveList[newSize] = moveList[i]; newSize++; } } if (newSize == 0) { return noUpcomingSelection(); } else if (newSize == 1) { return newMoveList[0]; } moveList = Arrays.copyOfRange(newMoveList, 0, newSize); } return new CompositeMove(moveList); } } public class RandomCartesianProductMoveIterator extends SelectionIterator<Move> { private List<Iterator<Move>> moveIteratorList; private Boolean empty; public RandomCartesianProductMoveIterator() { moveIteratorList = new ArrayList<>(childMoveSelectorList.size()); empty = null; for (MoveSelector moveSelector : childMoveSelectorList) { moveIteratorList.add(moveSelector.iterator()); } } @Override public boolean hasNext() { if (empty == null) { // Only done in the first call int emptyCount = 0; for (Iterator<Move> moveIterator : moveIteratorList) { if (!moveIterator.hasNext()) { emptyCount++; if (!ignoreEmptyChildIterators) { break; } } } empty = ignoreEmptyChildIterators ? emptyCount == moveIteratorList.size() : emptyCount > 0; } return !empty; } @Override public Move next() { List<Move> moveList = new ArrayList<>(moveIteratorList.size()); for (int i = 0; i < moveIteratorList.size(); i++) { Iterator<Move> moveIterator = moveIteratorList.get(i); boolean skip = false; if (!moveIterator.hasNext()) { MoveSelector moveSelector = childMoveSelectorList.get(i); moveIterator = moveSelector.iterator(); moveIteratorList.set(i, moveIterator); if (!moveIterator.hasNext()) { if (ignoreEmptyChildIterators) { skip = true; } else { throw new NoSuchElementException("The iterator of childMoveSelector (" + moveSelector + ") is empty."); } } } if (!skip) { moveList.add(moveIterator.next()); } } if (ignoreEmptyChildIterators) { if (moveList.isEmpty()) { throw new NoSuchElementException("All iterators of childMoveSelectorList (" + childMoveSelectorList + ") are empty."); } else if (moveList.size() == 1) { return moveList.get(0); } } return new CompositeMove(moveList.toArray(new Move[0])); } } @Override public String toString() { return "CartesianProduct(" + childMoveSelectorList + ")"; } }