All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jamesframework.core.search.neigh.subset.adv.DisjointMultiSwapNeighbourhood Maven / Gradle / Ivy

Go to download

The James core module is part of the James framework for optimization using local search metaheuristics in Java. The core contains general components to model problems, objectives and constraints, as well as generic algorithms to solve the problems. Moreover, the core provides implementations of specific utilities for subset selection.

There is a newer version: 1.2
Show newest version
//  Copyright 2014 Herman De Beukelaer
//
//  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.jamesframework.core.search.neigh.subset.adv;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import org.jamesframework.core.problems.solutions.SubsetSolution;
import org.jamesframework.core.search.algo.exh.SubsetSolutionIterator;
import org.jamesframework.core.search.neigh.Move;
import org.jamesframework.core.search.neigh.Neighbourhood;
import org.jamesframework.core.search.neigh.subset.SingleSwapNeighbourhood;
import org.jamesframework.core.util.SetUtilities;

/**
 * 

* A subset neighbourhood that generates moves performing a fixed number of simultaneous swaps of selected and unselected * IDs. When applying moves generated by this neighbourhood to a given subset solution, the set of selected IDs will always * remain of the same size. Therefore, this neighbourhood is only suited for fixed size subset selection problems. If * desired, a set of fixed IDs can be provided which are not allowed to be swapped. *

*

* Note that the number of possible moves quickly becomes very large when the size of the full set and/or selected * subset increase. For example, generating all combinations of 2 simultaneous swaps already yields * \[ * \frac{s(s-1)}{2} \times \frac{(n-s)(n-s-1)}{2} * \] * possibilities, where \(n\) is the size of the full set and \(s\) is the desired subset size. When selecting e.g. * 30 out of 100 items, this value already exceeds one million. Because of the large number of possible moves, * this extended neighbourhood should be used with care, especially in combination with searches that generate * all moves in every step. Furthermore, searches that generate random moves may have few chances to find an * improvement in case of a huge amount of possible neighbours. *

*

* This neighbourhood is thread-safe: it can be safely used to concurrently generate moves in different searches * running in separate threads. *

* * @author Herman De Beukelaer */ public class DisjointMultiSwapNeighbourhood implements Neighbourhood { // number of simultaneous swaps private final int numSwaps; // set of fixed IDs (may be null or empty if no IDs are fixed) private final Set fixedIDs; /** * Creates a multi swap neighbourhood without fixed IDs, indicating the number of (simultaneous) * swaps performed by any generated move. If numSwaps is 1, this neighbourhood * generates exactly the same moves as the {@link SingleSwapNeighbourhood} so in such case * it is advised to use the latter neighbourhood which has been optimized for this specific * scenario. * * @param numSwaps number of swaps performed by any generated move (> 0) * @throws IllegalArgumentException if maxSwaps is not strictly positive */ public DisjointMultiSwapNeighbourhood(int numSwaps){ this(numSwaps, null); } /** * Creates a multi swap neighbourhood with a given set of fixed IDs which are not allowed to be swapped. * None of the generated moves will add nor remove any of these fixed IDs. All generated moves will swap * exactly numSwaps pairs of IDs. If numSwaps is 1, this neighbourhood generates * exactly the same moves as the {@link SingleSwapNeighbourhood} so in such case it is advised * to use the latter neighbourhood which has been optimized for this specific scenario. * * @param numSwaps number of swaps performed by any generated move (> 0) * @param fixedIDs set of fixed IDs which are not allowed to be swapped * @throws IllegalArgumentException if numSwaps is not strictly positive */ public DisjointMultiSwapNeighbourhood(int numSwaps, Set fixedIDs){ if(numSwaps <= 0){ throw new IllegalArgumentException("The number of swaps should be strictly positive."); } this.numSwaps = numSwaps; this.fixedIDs = fixedIDs; } /** * Generates a move for the given subset solution that removes a random subset of \(k\) IDs from the current * selection and replaces them with an equally large random subset of the currently unselected IDs, where * \(k\) is the number of swaps specified at construction. Possible fixed IDs are not considered to be swapped. * If no swaps can be performed, null is returned. * * @param solution solution for which a random multi swap move is generated * @return random multi swap move, null if no swaps can be performed */ @Override public Move getRandomMove(SubsetSolution solution) { // get set of candidate IDs for deletion and addition Set deleteCandidates = solution.getSelectedIDs(); Set addCandidates = solution.getUnselectedIDs(); // remove fixed IDs, if any, from candidates if(fixedIDs != null && !fixedIDs.isEmpty()){ deleteCandidates = new HashSet<>(deleteCandidates); addCandidates = new HashSet<>(addCandidates); deleteCandidates.removeAll(fixedIDs); addCandidates.removeAll(fixedIDs); } // return null if no swaps are possible if(!canSwap(addCandidates, deleteCandidates)){ // impossible to perform a swap return null; } // use thread local random for better concurrent performance Random rg = ThreadLocalRandom.current(); // pick random IDs to remove from selection Set del = SetUtilities.getRandomSubset(deleteCandidates, numSwaps, rg); // pick random IDs to add to selection Set add = SetUtilities.getRandomSubset(addCandidates, numSwaps, rg); // create and return move return new GeneralSubsetMove(add, del); } /** *

* Generates the set of all possible moves that perform exactly \(k\) swaps, where \(k\) is the desired number * of swaps specified at construction. Possible fixed IDs are not considered to be swapped. If \(m < k\) * IDs are currently selected or unselected (excluding any fixed IDs), no swaps can be performed and the * returned set will be empty. * * @param solution solution for which all possible multi swap moves are generated * @return set of all multi swap moves, may be empty */ @Override public Set> getAllMoves(SubsetSolution solution) { // create empty set to store generated moves Set> moves = new HashSet<>(); // get set of candidate IDs for deletion and addition Set deleteCandidates = solution.getSelectedIDs(); Set addCandidates = solution.getUnselectedIDs(); // remove fixed IDs, if any, from candidates if(fixedIDs != null && !fixedIDs.isEmpty()){ deleteCandidates = new HashSet<>(deleteCandidates); addCandidates = new HashSet<>(addCandidates); deleteCandidates.removeAll(fixedIDs); addCandidates.removeAll(fixedIDs); } // possible to perform desired number of swaps? if(!canSwap(addCandidates, deleteCandidates)){ // impossible: return empty set return moves; } // create all moves performing numSwaps swaps SubsetSolutionIterator itDel, itAdd; Set del, add; itDel = new SubsetSolutionIterator(deleteCandidates, numSwaps); while(itDel.hasNext()){ del = itDel.next().getSelectedIDs(); itAdd = new SubsetSolutionIterator(addCandidates, numSwaps); while(itAdd.hasNext()){ add = itAdd.next().getSelectedIDs(); // create and add move moves.add(new GeneralSubsetMove(add, del)); } } // return all moves return moves; } /** * Checks whether it is possible to perform the desired number of swaps, as specified at construction. * * @param addCandidates candidate IDs to be added to the selection * @param deleteCandidates candidate IDs to be removed from the selection * @return true if the desired number of swaps can be performed */ private boolean canSwap(Set addCandidates, Set deleteCandidates){ return Math.min(addCandidates.size(), deleteCandidates.size()) >= numSwaps; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy