org.ojalgo.optimisation.integer.IntegerStrategy Maven / Gradle / Ivy
Show all versions of ojalgo Show documentation
/*
* Copyright 1997-2022 Optimatika
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.ojalgo.optimisation.integer;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.IntSupplier;
import org.ojalgo.concurrent.Parallelism;
import org.ojalgo.function.constant.BigMath;
import org.ojalgo.function.constant.PrimitiveMath;
import org.ojalgo.optimisation.ExpressionsBasedModel;
import org.ojalgo.optimisation.integer.ModelStrategy.DefaultStrategy;
import org.ojalgo.type.context.NumberContext;
public interface IntegerStrategy {
final class ConfigurableStrategy implements IntegerStrategy {
private final BiFunction myFactory;
private final NumberContext myGapTolerance;
private final GMICutConfiguration myGMICutConfiguration;
private final IntSupplier myParallelism;
private final Comparator[] myPriorityDefinitions;
private transient List> myWorkerPriorities = null;
ConfigurableStrategy(final IntSupplier parallelism, final Comparator[] definitions, final NumberContext gap,
final BiFunction factory, final GMICutConfiguration configuration) {
super();
myParallelism = parallelism;
myPriorityDefinitions = definitions;
myGapTolerance = gap;
myFactory = factory;
myGMICutConfiguration = configuration;
}
/**
* Retains any existing definitions, but adds these to be used rather than the existing. If there are
* enough threads both these additional and the previously existing definitions will be used.
*/
public ConfigurableStrategy addPriorityDefinitions(final Comparator... additionalDefinitions) {
Comparator[] totalDefinitions = (Comparator[]) new Comparator>[additionalDefinitions.length + myPriorityDefinitions.length];
for (int i = 0; i < additionalDefinitions.length; i++) {
totalDefinitions[i] = additionalDefinitions[i];
}
for (int i = 0; i < myPriorityDefinitions.length; i++) {
totalDefinitions[additionalDefinitions.length + i] = myPriorityDefinitions[i];
}
return new ConfigurableStrategy(myParallelism, totalDefinitions, myGapTolerance, myFactory, myGMICutConfiguration);
}
public NumberContext getGapTolerance() {
return myGapTolerance;
}
public GMICutConfiguration getGMICutConfiguration() {
return myGMICutConfiguration;
}
public List> getWorkerPriorities() {
if (myWorkerPriorities == null || myWorkerPriorities.size() <= 0) {
int parallelism = myParallelism.getAsInt();
myWorkerPriorities = new ArrayList<>(parallelism);
for (int i = 0; i < parallelism; i++) {
myWorkerPriorities.add(myPriorityDefinitions[i % myPriorityDefinitions.length]);
}
}
return myWorkerPriorities;
}
public ModelStrategy newModelStrategy(final ExpressionsBasedModel model) {
return myFactory.apply(model, this);
}
/**
* Change the MIP gap
*/
public ConfigurableStrategy withGapTolerance(final NumberContext newTolerance) {
return new ConfigurableStrategy(myParallelism, myPriorityDefinitions, newTolerance, myFactory, myGMICutConfiguration);
}
public ConfigurableStrategy withGMICutConfiguration(final GMICutConfiguration newConfiguration) {
return new ConfigurableStrategy(myParallelism, myPriorityDefinitions, myGapTolerance, myFactory, newConfiguration);
}
public ConfigurableStrategy withModelStrategyFactory(final BiFunction newFactory) {
return new ConfigurableStrategy(myParallelism, myPriorityDefinitions, myGapTolerance, newFactory, myGMICutConfiguration);
}
/**
* How many threads will be used? Perhaps use {@link Parallelism} to obtain a suitable value.
*/
public ConfigurableStrategy withParallelism(final IntSupplier newParallelism) {
return new ConfigurableStrategy(newParallelism, myPriorityDefinitions, myGapTolerance, myFactory, myGMICutConfiguration);
}
/**
* Replace the priority definitions with these ones.
*/
public ConfigurableStrategy withPriorityDefinitions(final Comparator... newDefinitions) {
return new ConfigurableStrategy(myParallelism, newDefinitions, myGapTolerance, myFactory, myGMICutConfiguration);
}
}
/**
* Gomory Mixed Integer Cut Configuration
*
* @author apete
*/
public static final class GMICutConfiguration {
/**
* The minimum fractionality of the integer variable used to generate the cut. Less than this, and the
* (potential) cut is never generated.
*/
public final double fractionality;
/**
* After the cut is generated it is transformed to be expresssed in the original model variables. In
* this process the RHS of the cut inequality changes. This parameter controls how much the RHS is
* allowed to grow in magnitude. If it grows/expands to much the cut is discarded.
*
* The cut/constraint violation is always exactly 1 (due to how the cut is generated). That means the
* magnitude of the RHS becomes a meassure of the relative cut violation. Allowing large RHS values is
* equivalent to accepting small relative cut violations. The number you specify here is the inverse
* of the relative cut violation (the absolute value of the max RHS allowed).
*/
public final BigDecimal violation;
public GMICutConfiguration() {
this(PrimitiveMath.ELEVENTH, BigMath.TWELVE);
}
private GMICutConfiguration(final double newAway, final BigDecimal newExpansion) {
super();
fractionality = newAway;
violation = newExpansion;
}
public GMICutConfiguration withFractionality(final double newFractionality) {
return new GMICutConfiguration(Math.min(Math.abs(newFractionality), 0.5), violation);
}
public GMICutConfiguration withViolation(final BigDecimal newViolation) {
return new GMICutConfiguration(fractionality, newViolation.abs());
}
}
ConfigurableStrategy DEFAULT = IntegerStrategy.newConfigurable();
static ConfigurableStrategy newConfigurable() {
Comparator[] definitions = (Comparator[]) new Comparator>[] { NodeKey.EARLIEST_SEQUENCE, NodeKey.LARGEST_DISPLACEMENT,
NodeKey.SMALLEST_DISPLACEMENT, NodeKey.LATEST_SEQUENCE };
return new ConfigurableStrategy(Parallelism.CORES, definitions, NumberContext.of(7, 8), DefaultStrategy::new, new GMICutConfiguration());
}
/**
* The MIP gap is the difference between the best integer solution found so far and a node's relaxed
* non-integer solution. The relative MIP gap is that difference divided by the optimal value
* (approximated by the currently best integer solution). If the gap (absolute or relative) is too small,
* then the corresponding branch is terminated as it is deemed unlikely or too "expensive" to find better
* integer solutions there.
*
* @return The tolerance context used to determine if the gap is too small or not
*/
NumberContext getGapTolerance();
GMICutConfiguration getGMICutConfiguration();
/**
* There will be 1 worker thread per item in the returned {@link List}. The {@link Comparator} instances
* need not be unique. Used to prioritise among the nodes waiting to be evaluated.
*/
List> getWorkerPriorities();
ModelStrategy newModelStrategy(final ExpressionsBasedModel model);
}