/*
* Copyright 2016 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.partitionedsearch.scope;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import org.optaplanner.core.impl.heuristic.move.AbstractMove;
import org.optaplanner.core.impl.score.director.InnerScoreDirector;
import org.optaplanner.core.impl.score.director.ScoreDirector;
/**
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/
public final class PartitionChangeMove<Solution_> extends AbstractMove<Solution_> {
public static <Solution_> PartitionChangeMove<Solution_> createMove(InnerScoreDirector<Solution_> scoreDirector) {
SolutionDescriptor<Solution_> solutionDescriptor = scoreDirector.getSolutionDescriptor();
Solution_ workingSolution = scoreDirector.getWorkingSolution();
int entityCount = solutionDescriptor.getEntityCount(workingSolution);
Map<GenuineVariableDescriptor<Solution_>, List<Pair<Object, Object>>> changeMap
= new LinkedHashMap<>(solutionDescriptor.getEntityDescriptors().size() * 3);
for (EntityDescriptor<Solution_> entityDescriptor : solutionDescriptor.getEntityDescriptors()) {
for (GenuineVariableDescriptor<Solution_> variableDescriptor
: entityDescriptor.getDeclaredGenuineVariableDescriptors()) {
changeMap.put(variableDescriptor, new ArrayList<>(entityCount));
}
}
for (Iterator<Object> it = solutionDescriptor.extractAllEntitiesIterator(workingSolution); it.hasNext();) {
Object entity = it.next();
EntityDescriptor<Solution_> entityDescriptor = solutionDescriptor.findEntityDescriptorOrFail(
entity.getClass());
if (entityDescriptor.isMovable(scoreDirector, entity)) {
for (GenuineVariableDescriptor<Solution_> variableDescriptor
: entityDescriptor.getGenuineVariableDescriptors()) {
Object value = variableDescriptor.getValue(entity);
changeMap.get(variableDescriptor).add(Pair.of(entity, value));
}
}
}
return new PartitionChangeMove<>(changeMap);
}
private final Map<GenuineVariableDescriptor<Solution_>, List<Pair<Object, Object>>> changeMap;
public PartitionChangeMove(Map<GenuineVariableDescriptor<Solution_>, List<Pair<Object, Object>>> changeMap) {
this.changeMap = changeMap;
}
@Override
protected void doMoveOnGenuineVariables(ScoreDirector<Solution_> scoreDirector) {
for (Map.Entry<GenuineVariableDescriptor<Solution_>, List<Pair<Object, Object>>> entry : changeMap.entrySet()) {
GenuineVariableDescriptor<Solution_> variableDescriptor = entry.getKey();
for (Pair<Object, Object> pair : entry.getValue()) {
Object entity = pair.getKey();
Object value = pair.getValue();
scoreDirector.changeVariableFacade(variableDescriptor, entity, value);
}
}
}
@Override
public boolean isMoveDoable(ScoreDirector<Solution_> scoreDirector) {
return true;
}
@Override
protected AbstractMove<Solution_> createUndoMove(ScoreDirector<Solution_> scoreDirector) {
// TODO not yet implemented, returns null to fail fast if it is used
return null;
}
@Override
public Collection<? extends Object> getPlanningEntities() {
throw new UnsupportedOperationException();
}
@Override
public Collection<? extends Object> getPlanningValues() {
throw new UnsupportedOperationException();
}
public PartitionChangeMove<Solution_> rebase(InnerScoreDirector<Solution_> destinationScoreDirector) {
Map<GenuineVariableDescriptor<Solution_>, List<Pair<Object, Object>>> destinationChangeMap
= new LinkedHashMap<>(changeMap.size());
for (Map.Entry<GenuineVariableDescriptor<Solution_>, List<Pair<Object, Object>>> entry : changeMap.entrySet()) {
GenuineVariableDescriptor<Solution_> variableDescriptor = entry.getKey();
List<Pair<Object, Object>> originPairList = entry.getValue();
List<Pair<Object, Object>> destinationPairList = new ArrayList<>(originPairList.size());
for (Pair<Object, Object> pair : originPairList) {
Object originEntity = pair.getKey();
Object destinationEntity = destinationScoreDirector.lookUpWorkingObject(originEntity);
if (destinationEntity == null && originEntity != null) {
throw new IllegalStateException("The destinationEntity (" + destinationEntity
+ ") cannot be null if the originEntity (" + originEntity + ") is not null.");
}
Object originValue = pair.getValue();
Object destinationValue = destinationScoreDirector.lookUpWorkingObject(originValue);
if (destinationValue == null && originValue != null) {
throw new IllegalStateException("The destinationEntity (" + destinationEntity
+ ")'s destinationValue (" + destinationValue
+ ") cannot be null if the originEntity (" + originEntity
+ ")'s originValue (" + originValue + ") is not null.\n"
+ "Maybe add the originValue (" + originValue + ") of class (" + originValue.getClass()
+ ") as problem fact in the planning solution with a "
+ ProblemFactCollectionProperty.class.getSimpleName() + " annotation.");
}
destinationPairList.add(Pair.of(destinationEntity, destinationValue));
}
destinationChangeMap.put(variableDescriptor, destinationPairList);
}
return new PartitionChangeMove<>(destinationChangeMap);
}
// TODO implement toString()
}