Package org.optaplanner.core.impl.heuristic.selector.value.chained

Source Code of org.optaplanner.core.impl.heuristic.selector.value.chained.DefaultSubChainSelector$OriginalSubChainIterator

/*
* Copyright 2012 JBoss Inc
*
* 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.value.chained;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

import org.optaplanner.core.config.heuristic.selector.common.SelectionCacheType;
import org.optaplanner.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import org.optaplanner.core.impl.heuristic.selector.AbstractSelector;
import org.optaplanner.core.impl.heuristic.selector.common.SelectionCacheLifecycleBridge;
import org.optaplanner.core.impl.heuristic.selector.common.SelectionCacheLifecycleListener;
import org.optaplanner.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator;
import org.optaplanner.core.impl.heuristic.selector.entity.pillar.DefaultPillarSelector;
import org.optaplanner.core.impl.heuristic.selector.value.EntityIndependentValueSelector;
import org.optaplanner.core.impl.score.director.InnerScoreDirector;
import org.optaplanner.core.impl.solver.random.RandomUtils;
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope;

/**
* This is the common {@link SubChainSelector} implementation.
*/
public class DefaultSubChainSelector extends AbstractSelector
        implements SubChainSelector, SelectionCacheLifecycleListener {

    protected static final SelectionCacheType CACHE_TYPE = SelectionCacheType.STEP;

    protected final EntityIndependentValueSelector valueSelector;
    protected final boolean randomSelection;

    /**
     * Unlike {@link DefaultPillarSelector#minimumSubPillarSize} and {@link DefaultPillarSelector#maximumSubPillarSize},
     * the sub selection here is a sequence. For example from ABCDE,it can select BCD, but not ACD.
     */
    protected final int minimumSubChainSize;
    protected final int maximumSubChainSize;

    protected List<SubChain> anchorTrailingChainList = null;

    public DefaultSubChainSelector(EntityIndependentValueSelector valueSelector, boolean randomSelection,
            int minimumSubChainSize, int maximumSubChainSize) {
        this.valueSelector = valueSelector;
        this.randomSelection = randomSelection;
        if (!valueSelector.getVariableDescriptor().isChained()) {
            throw new IllegalArgumentException("The selector (" + this
                    + ")'s valueSelector (" + valueSelector
                    + ") must have a chained variableDescriptor chained ("
                    + valueSelector.getVariableDescriptor().isChained() + ").");
        }
        if (valueSelector.isNeverEnding()) {
            throw new IllegalStateException("The selector (" + this
                    + ") has a valueSelector (" + valueSelector
                    + ") with neverEnding (" + valueSelector.isNeverEnding() + ").");
        }
        phaseLifecycleSupport.addEventListener(valueSelector);
        phaseLifecycleSupport.addEventListener(new SelectionCacheLifecycleBridge(CACHE_TYPE, this));
        this.minimumSubChainSize = minimumSubChainSize;
        this.maximumSubChainSize = maximumSubChainSize;
        if (minimumSubChainSize < 1) {
            throw new IllegalStateException("The selector (" + this
                    + ")'s minimumSubChainSize (" + minimumSubChainSize
                    + ") must be at least 1.");
        }
        if (minimumSubChainSize > maximumSubChainSize) {
            throw new IllegalStateException("The minimumSubChainSize (" + minimumSubChainSize
                    + ") must be at least maximumSubChainSize (" + maximumSubChainSize + ").");
        }
    }

    public GenuineVariableDescriptor getVariableDescriptor() {
        return valueSelector.getVariableDescriptor();
    }

    @Override
    public SelectionCacheType getCacheType() {
        return CACHE_TYPE;
    }

    // ************************************************************************
    // Cache lifecycle methods
    // ************************************************************************

    public void constructCache(DefaultSolverScope solverScope) {
        InnerScoreDirector scoreDirector = solverScope.getScoreDirector();
        GenuineVariableDescriptor variableDescriptor = valueSelector.getVariableDescriptor();
        Class<?> entityClass = variableDescriptor.getEntityDescriptor().getEntityClass();
        long valueSize = valueSelector.getSize();
        // Fail-fast when anchorTrailingChainList.size() could ever be too big
        if (valueSize > (long) Integer.MAX_VALUE) {
            throw new IllegalStateException("The selector (" + this
                    + ") has a valueSelector (" + valueSelector
                    + ") with valueSize (" + valueSize
                    + ") which is higher than Integer.MAX_VALUE.");
        }
        // Temporary LinkedList to avoid using a bad initialCapacity
        List<Object> anchorList = new LinkedList<Object>();
        for (Object value : valueSelector) {
            if (!entityClass.isAssignableFrom(value.getClass())) {
                anchorList.add(value);
            }
        }
        anchorTrailingChainList = new ArrayList<SubChain>(anchorList.size());
        int anchorChainInitialCapacity = ((int) valueSize / anchorList.size()) + 1;
        for (Object anchor : anchorList) {
            List<Object> anchorChain = new ArrayList<Object>(anchorChainInitialCapacity);
            Object trailingEntity = scoreDirector.getTrailingEntity(variableDescriptor, anchor);
            while (trailingEntity != null) {
                anchorChain.add(trailingEntity);
                trailingEntity = scoreDirector.getTrailingEntity(variableDescriptor, trailingEntity);
            }
            if (anchorChain.size() >= minimumSubChainSize) {
                anchorTrailingChainList.add(new SubChain(anchorChain));
            }
        }
    }

    public void disposeCache(DefaultSolverScope solverScope) {
        anchorTrailingChainList = null;
    }

    // ************************************************************************
    // Worker methods
    // ************************************************************************

    public boolean isCountable() {
        return true;
    }

    public boolean isNeverEnding() {
        return randomSelection;
    }

    public long getSize() {
        long selectionSize = 0L;
        for (SubChain anchorTrailingChain : anchorTrailingChainList) {
            selectionSize += calculateSubChainSelectionSize(anchorTrailingChain);
        }
        return selectionSize;
    }

    protected long calculateSubChainSelectionSize(SubChain anchorTrailingChain) {
        long anchorTrailingChainSize = (long) anchorTrailingChain.getSize();
        long n = anchorTrailingChainSize - (long) minimumSubChainSize + 1L;
        long m = (maximumSubChainSize >= anchorTrailingChainSize)
                ? 0L : anchorTrailingChainSize - (long) maximumSubChainSize;
        return (n * (n + 1L) / 2L) - (m * (m + 1L) / 2L);
    }

    public Iterator<SubChain> iterator() {
        if (!randomSelection) {
            return new OriginalSubChainIterator(anchorTrailingChainList.listIterator());
        } else {
            return new RandomSubChainIterator();
        }
    }

    public ListIterator<SubChain> listIterator() {
        if (!randomSelection) {
            return new OriginalSubChainIterator(anchorTrailingChainList.listIterator());
        } else {
            throw new IllegalStateException("The selector (" + this
                    + ") does not support a ListIterator with randomSelection (" + randomSelection + ").");
        }
    }

    public ListIterator<SubChain> listIterator(int index) {
        if (!randomSelection) {
            // TODO Implement more efficient ListIterator https://issues.jboss.org/browse/PLANNER-37
            OriginalSubChainIterator it = new OriginalSubChainIterator(anchorTrailingChainList.listIterator());
            for (int i = 0; i < index; i++) {
                it.next();
            }
            return it;
        } else {
            throw new IllegalStateException("The selector (" + this
                    + ") does not support a ListIterator with randomSelection (" + randomSelection + ").");
        }
    }

    private class OriginalSubChainIterator extends UpcomingSelectionIterator<SubChain>
            implements ListIterator<SubChain> {

        private final ListIterator<SubChain> anchorTrailingChainIterator;
        private List<Object> anchorTrailingChain;
        private int fromIndex; // Inclusive
        private int toIndex; // Exclusive

        private int nextListIteratorIndex;

        public OriginalSubChainIterator(ListIterator<SubChain> anchorTrailingChainIterator) {
            this.anchorTrailingChainIterator = anchorTrailingChainIterator;
            fromIndex = 0;
            toIndex = 1;
            anchorTrailingChain = Collections.emptyList();
            nextListIteratorIndex = 0;
        }

        @Override
        protected SubChain createUpcomingSelection() {
            toIndex++;
            if (toIndex - fromIndex > maximumSubChainSize || toIndex > anchorTrailingChain.size()) {
                fromIndex++;
                toIndex = fromIndex + minimumSubChainSize;
                // minimumSubChainSize <= maximumSubChainSize so (toIndex - fromIndex > maximumSubChainSize) is true
                while (toIndex > anchorTrailingChain.size()) {
                    if (!anchorTrailingChainIterator.hasNext()) {
                        return noUpcomingSelection();
                    }
                    anchorTrailingChain = anchorTrailingChainIterator.next().getEntityList();
                    fromIndex = 0;
                    toIndex = fromIndex + minimumSubChainSize;
                }
            }
            return new SubChain(anchorTrailingChain.subList(fromIndex, toIndex));
        }

        @Override
        public SubChain next() {
            nextListIteratorIndex++;
            return super.next();
        }

        public int nextIndex() {
            return nextListIteratorIndex;
        }

        public boolean hasPrevious() {
            throw new UnsupportedOperationException("The operation hasPrevious() is not supported."
                    + " See https://issues.jboss.org/browse/PLANNER-37");
        }

        public SubChain previous() {
            throw new UnsupportedOperationException("The operation previous() is not supported."
                    + " See https://issues.jboss.org/browse/PLANNER-37");
        }

        public int previousIndex() {
            throw new UnsupportedOperationException("The operation previousIndex() is not supported."
                    + " See https://issues.jboss.org/browse/PLANNER-37");
        }

        public void set(SubChain subChain) {
            throw new UnsupportedOperationException("The optional operation set() is not supported.");
        }

        public void add(SubChain subChain) {
            throw new UnsupportedOperationException("The optional operation add() is not supported.");
        }
    }

    private class RandomSubChainIterator extends UpcomingSelectionIterator<SubChain> {

        private RandomSubChainIterator() {
            if (anchorTrailingChainList.isEmpty()) {
                upcomingSelection = noUpcomingSelection();
                upcomingCreated = true;
            }
        }

        @Override
        protected SubChain createUpcomingSelection() {
            SubChain anchorTrailingChain = selectAnchorTrailingChain();
            // Every SubChain has the same probability (from this point on at least).
            // A random fromIndex and random toIndex would not be fair.
            long selectionSize = calculateSubChainSelectionSize(anchorTrailingChain);
            long selectionIndex = RandomUtils.nextLong(workingRandom, selectionSize);
            // Black magic to translate selectionIndex into fromIndex and toIndex
            long fromIndex = selectionIndex;
            long subChainSize = minimumSubChainSize;
            long countInThatSize = anchorTrailingChain.getSize() - subChainSize + 1;
            while (fromIndex >= countInThatSize) {
                fromIndex -= countInThatSize;
                subChainSize++;
                countInThatSize--;
                if (countInThatSize <= 0) {
                    throw new IllegalStateException("Impossible if calculateSubChainSelectionSize() works correctly.");
                }
            }
            return anchorTrailingChain.subChain((int) fromIndex, (int) (fromIndex + subChainSize));
        }

        private SubChain selectAnchorTrailingChain() {
            // Known issue/compromise: Every SubChain should have same probability, but doesn't.
            // Instead, every anchorTrailingChain has the same probability.
            int anchorTrailingChainListIndex = workingRandom.nextInt(anchorTrailingChainList.size());
            return anchorTrailingChainList.get(anchorTrailingChainListIndex);
        }

    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "(" + valueSelector + ")";
    }

}
TOP

Related Classes of org.optaplanner.core.impl.heuristic.selector.value.chained.DefaultSubChainSelector$OriginalSubChainIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.