/* * 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.generic.chained; import java.util.Collections; import java.util.Iterator; import org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableDemand; import org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableSupply; import org.optaplanner.core.impl.domain.variable.supply.SupplyManager; import org.optaplanner.core.impl.heuristic.move.Move; import org.optaplanner.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator; import org.optaplanner.core.impl.heuristic.selector.move.generic.GenericMoveSelector; import org.optaplanner.core.impl.heuristic.selector.value.EntityIndependentValueSelector; import org.optaplanner.core.impl.heuristic.selector.value.chained.SubChain; import org.optaplanner.core.impl.heuristic.selector.value.chained.SubChainSelector; import org.optaplanner.core.impl.solver.scope.DefaultSolverScope; public class SubChainChangeMoveSelector extends GenericMoveSelector { protected final SubChainSelector subChainSelector; protected final EntityIndependentValueSelector valueSelector; protected final boolean randomSelection; protected final boolean selectReversingMoveToo; protected SingletonInverseVariableSupply inverseVariableSupply = null; public SubChainChangeMoveSelector(SubChainSelector subChainSelector, EntityIndependentValueSelector valueSelector, boolean randomSelection, boolean selectReversingMoveToo) { this.subChainSelector = subChainSelector; this.valueSelector = valueSelector; this.randomSelection = randomSelection; this.selectReversingMoveToo = selectReversingMoveToo; if (subChainSelector.getVariableDescriptor() != valueSelector.getVariableDescriptor()) { throw new IllegalStateException("The selector (" + this + ") has a subChainSelector (" + subChainSelector + ") with variableDescriptor (" + subChainSelector.getVariableDescriptor() + ") which is not the same as the valueSelector (" + valueSelector + ")'s variableDescriptor(" + valueSelector.getVariableDescriptor() + ")."); } if (!randomSelection) { if (subChainSelector.isNeverEnding()) { throw new IllegalStateException("The selector (" + this + ") has a subChainSelector (" + subChainSelector + ") with neverEnding (" + subChainSelector.isNeverEnding() + ")."); } if (valueSelector.isNeverEnding()) { throw new IllegalStateException("The selector (" + this + ") has a valueSelector (" + valueSelector + ") with neverEnding (" + valueSelector.isNeverEnding() + ")."); } } phaseLifecycleSupport.addEventListener(subChainSelector); phaseLifecycleSupport.addEventListener(valueSelector); } @Override public void solvingStarted(DefaultSolverScope solverScope) { super.solvingStarted(solverScope); SupplyManager supplyManager = solverScope.getScoreDirector().getSupplyManager(); inverseVariableSupply = supplyManager.demand(new SingletonInverseVariableDemand(valueSelector.getVariableDescriptor())); } @Override public void solvingEnded(DefaultSolverScope solverScope) { super.solvingEnded(solverScope); inverseVariableSupply = null; } // ************************************************************************ // Worker methods // ************************************************************************ @Override public boolean isCountable() { return subChainSelector.isCountable() && valueSelector.isCountable(); } @Override public boolean isNeverEnding() { return randomSelection; } @Override public long getSize() { return subChainSelector.getSize() * valueSelector.getSize(); } @Override public Iterator<Move> iterator() { if (!randomSelection) { return new OriginalSubChainChangeMoveIterator(); } else { return new RandomSubChainChangeMoveIterator(); } } private class OriginalSubChainChangeMoveIterator extends UpcomingSelectionIterator<Move> { private Iterator<SubChain> subChainIterator; private Iterator<Object> valueIterator = null; private SubChain upcomingSubChain; private Move nextReversingSelection = null; private OriginalSubChainChangeMoveIterator() { subChainIterator = subChainSelector.iterator(); // Don't do hasNext() in constructor (to avoid upcoming selections breaking mimic recording) valueIterator = Collections.emptyIterator(); } @Override protected Move createUpcomingSelection() { if (selectReversingMoveToo && nextReversingSelection != null) { Move upcomingSelection = nextReversingSelection; nextReversingSelection = null; return upcomingSelection; } if (!valueIterator.hasNext()) { if (!subChainIterator.hasNext()) { return noUpcomingSelection(); } upcomingSubChain = subChainIterator.next(); valueIterator = valueSelector.iterator(); if (!valueIterator.hasNext()) { // valueSelector is completely empty return noUpcomingSelection(); } } Object toValue = valueIterator.next(); Move upcomingSelection = new SubChainChangeMove( upcomingSubChain, valueSelector.getVariableDescriptor(), inverseVariableSupply, toValue); if (selectReversingMoveToo) { nextReversingSelection = new SubChainReversingChangeMove( upcomingSubChain, valueSelector.getVariableDescriptor(), inverseVariableSupply, toValue); } return upcomingSelection; } } private class RandomSubChainChangeMoveIterator extends UpcomingSelectionIterator<Move> { private Iterator<SubChain> subChainIterator; private Iterator<Object> valueIterator; private RandomSubChainChangeMoveIterator() { subChainIterator = subChainSelector.iterator(); valueIterator = valueSelector.iterator(); // Don't do hasNext() in constructor (to avoid upcoming selections breaking mimic recording) valueIterator = Collections.emptyIterator(); } @Override protected Move createUpcomingSelection() { // Ideally, this code should have read: // SubChain subChain = subChainIterator.next(); // Object toValue = valueIterator.next(); // But empty selectors and ending selectors (such as non-random or shuffled) make it more complex if (!subChainIterator.hasNext()) { subChainIterator = subChainSelector.iterator(); if (!subChainIterator.hasNext()) { // subChainSelector is completely empty return noUpcomingSelection(); } } SubChain subChain = subChainIterator.next(); if (!valueIterator.hasNext()) { valueIterator = valueSelector.iterator(); if (!valueIterator.hasNext()) { // valueSelector is completely empty return noUpcomingSelection(); } } Object toValue = valueIterator.next(); boolean reversing = selectReversingMoveToo && workingRandom.nextBoolean(); return reversing ? new SubChainReversingChangeMove(subChain, valueSelector.getVariableDescriptor(), inverseVariableSupply, toValue) : new SubChainChangeMove(subChain, valueSelector.getVariableDescriptor(), inverseVariableSupply, toValue); } } @Override public String toString() { return getClass().getSimpleName() + "(" + subChainSelector + ", " + valueSelector + ")"; } }