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

org.ojalgo.optimisation.Expression Maven / Gradle / Ivy

Go to download

oj! Algorithms - ojAlgo - is Open Source Java code that has to do with mathematics, linear algebra and optimisation.

There is a newer version: 55.0.1
Show newest version
/*
 * 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;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.ojalgo.ProgrammingError;
import org.ojalgo.function.BinaryFunction;
import org.ojalgo.function.UnaryFunction;
import org.ojalgo.function.aggregator.AggregatorFunction;
import org.ojalgo.function.aggregator.AggregatorSet;
import org.ojalgo.function.aggregator.BigAggregator;
import org.ojalgo.function.constant.BigMath;
import org.ojalgo.function.constant.PrimitiveMath;
import org.ojalgo.function.multiary.AffineFunction;
import org.ojalgo.function.multiary.ConstantFunction;
import org.ojalgo.function.multiary.MultiaryFunction;
import org.ojalgo.function.multiary.PureQuadraticFunction;
import org.ojalgo.function.multiary.QuadraticFunction;
import org.ojalgo.matrix.store.MatrixStore;
import org.ojalgo.matrix.store.Primitive64Store;
import org.ojalgo.structure.Access1D;
import org.ojalgo.structure.Access2D;
import org.ojalgo.structure.Structure1D;
import org.ojalgo.structure.Structure1D.IntIndex;
import org.ojalgo.structure.Structure2D.IntRowColumn;

/**
 * 

* Think of an Expression as one constraint or a component to the objective function. An expression becomes a * linear expression as soon as you set a linear factor. Setting a quadratic factor turns it into a quadratic * expression. If you set both linear and quadratic factors it is a compound expression, and if you set * neither it is an empty expression. Currently the solvers supplied by ojAlgo can only handle linear * constraint expressions. The objective function can be linear, quadratic or compound. Empty expressions * makes no sense... *

*

* An expression is turned into a constraint by setting a lower and/or upper limit. Use * {@linkplain Expression#lower(Comparable)}, {@linkplain Expression#upper(Comparable)} or * {@linkplain Expression#level(Comparable)}. An expression is made part of (contributing to) the objective * function by setting a contribution weight. Use {@linkplain Expression#weight(Comparable)}. The contribution * weight can be set to anything except zero (0.0). Often you may just want to set it to one (1.0). Other * values can be used to balance multiple expressions contributing to the objective function. *

* * @author apete */ public final class Expression extends ModelEntity { private BigDecimal myConstant = null; private transient boolean myInfeasible = false; private transient Boolean myInteger = null; private final Map myLinear; private final ExpressionsBasedModel myModel; private final Map myQuadratic; private transient boolean myRedundant = false; /** * A shallow copy (typically created by presolver or integer solver) shares the Map:s holding the * paramaters with other Expressions. They will only differ on the lower/upper limits and on meta data * like flags indicating redundancy or infeasibility. */ private final boolean myShallowCopy; @SuppressWarnings("unused") private Expression(final Expression entityToCopy) { this(entityToCopy, null, false); ProgrammingError.throwForIllegalInvocation(); } protected Expression(final Expression expressionToCopy, final ExpressionsBasedModel destinationModel, final boolean deep) { super(expressionToCopy); myModel = destinationModel; myConstant = expressionToCopy.getConstant(); if (deep) { myShallowCopy = false; myLinear = new HashMap<>(); myLinear.putAll(expressionToCopy.getLinear()); myQuadratic = new HashMap<>(); myQuadratic.putAll(expressionToCopy.getQuadratic()); } else { myShallowCopy = true; myLinear = expressionToCopy.getLinear(); myQuadratic = expressionToCopy.getQuadratic(); } if (expressionToCopy.isInteger()) { myInteger = Boolean.TRUE; } else { myInteger = null; } } Expression(final String name, final ExpressionsBasedModel model) { super(name); ProgrammingError.throwIfNull(name, model); myModel = model; myShallowCopy = false; myLinear = new HashMap<>(); myQuadratic = new HashMap<>(); } /** * @see #add(Variable, Comparable) */ public Expression add(final int index, final Comparable value) { return this.add(myModel.getVariable(index), value); } /** * @see #add(Variable, Comparable) */ public Expression add(final int index, final double value) { return this.add(index, BigDecimal.valueOf(value)); } /** * @see #add(Variable, Comparable) */ public Expression add(final int row, final int column, final Comparable value) { return this.add(new IntRowColumn(row, column), value); } /** * @see #add(Variable, Comparable) */ public Expression add(final int row, final int column, final double value) { return this.add(row, column, BigDecimal.valueOf(value)); } /** * @see #add(Variable, Comparable) */ public Expression add(final int row, final int column, final long value) { return this.add(row, column, BigDecimal.valueOf(value)); } /** * @see #add(Variable, Comparable) */ public Expression add(final int index, final long value) { return this.add(index, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression add(final IntIndex key, final Comparable value) { BigDecimal existing = myLinear.get(key); if (existing != null) { this.set(key, ModelEntity.toBigDecimal(value).add(existing)); } else { this.set(key, value); } return this; } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression add(final IntIndex key, final double value) { return this.add(key, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression add(final IntIndex row, final IntIndex column, final Comparable value) { return this.add(new IntRowColumn(row, column), value); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression add(final IntIndex row, final IntIndex column, final double value) { return this.add(row, column, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression add(final IntIndex row, final IntIndex column, final long value) { return this.add(row, column, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression add(final IntIndex key, final long value) { return this.add(key, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with {@link Variable}:s or int:s as the key instead. */ @Deprecated public Expression add(final IntRowColumn key, final Comparable value) { BigDecimal existing = myQuadratic.get(key); if (existing != null) { this.set(key, ModelEntity.toBigDecimal(value).add(existing)); } else { this.set(key, value); } return this; } /** * @deprecated Use the alternatives with {@link Variable}:s or int:s as the key instead. */ @Deprecated public Expression add(final IntRowColumn key, final double value) { return this.add(key, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with {@link Variable}:s or int:s as the key instead. */ @Deprecated public Expression add(final IntRowColumn key, final long value) { return this.add(key, BigDecimal.valueOf(value)); } /** * Will add the value to this variable's factor. */ public Expression add(final Variable variable, final Comparable value) { return this.add(variable.getIndex(), value); } /** * @see #add(Variable, Comparable) */ public Expression add(final Variable variable, final double value) { return this.add(variable, BigDecimal.valueOf(value)); } /** * @see #add(Variable, Comparable) */ public Expression add(final Variable variable, final long value) { return this.add(variable, BigDecimal.valueOf(value)); } /** * @see #add(Variable, Comparable) */ public Expression add(final Variable variable1, final Variable variable2, final Comparable value) { return this.add(variable1.getIndex().index, variable2.getIndex().index, value); } /** * @see #add(Variable, Comparable) */ public Expression add(final Variable variable1, final Variable variable2, final double value) { return this.add(variable1, variable2, BigDecimal.valueOf(value)); } /** * @see #add(Variable, Comparable) */ public Expression add(final Variable variable1, final Variable variable2, final long value) { return this.add(variable1, variable2, BigDecimal.valueOf(value)); } @Override public void addTo(final Expression target, final BigDecimal scale) { for (Entry entry : myLinear.entrySet()) { BigDecimal value = entry.getValue().multiply(scale); target.add(entry.getKey(), value); } for (Entry entry : myQuadratic.entrySet()) { BigDecimal value = entry.getValue().multiply(scale); target.add(entry.getKey(), value); } } public int compareTo(final Expression obj) { return this.getName().compareTo(obj.getName()); } /** * Will return an Expression with factors corresponding to fixed variables removed, and lower/upper limits * compensated for the fixed part of the expression. Factors corresponding to bilinear variables, where * one is fixed and the other is not, are linearized. * * @param fixedVariables A set of (by the presolver) fixed variable indices * @return The reduced/modified expression */ public Expression compensate(final Set fixedVariables) { if (fixedVariables.size() == 0 || !this.isAnyQuadraticFactorNonZero() && Collections.disjoint(fixedVariables, this.getLinearKeySet())) { return this; // No need to copy/compensate anything } ExpressionsBasedModel tmpModel = this.getModel(); Expression retVal = new Expression(this.getName(), tmpModel); BigDecimal tmpFixedValue = BigMath.ZERO; for (Entry tmpEntry : myLinear.entrySet()) { IntIndex tmpKey = tmpEntry.getKey(); BigDecimal tmpFactor = tmpEntry.getValue(); if (fixedVariables.contains(tmpKey)) { // Fixed Variable variable = tmpModel.getVariable(tmpKey.index); BigDecimal tmpValue = variable.getValue(); tmpFixedValue = tmpFixedValue.add(tmpFactor.multiply(tmpValue)); } else { // Not fixed retVal.set(tmpKey, tmpFactor); } } for (Entry tmpEntry : myQuadratic.entrySet()) { IntRowColumn tmpKey = tmpEntry.getKey(); BigDecimal tmpFactor = tmpEntry.getValue(); Variable tmpRowVariable = tmpModel.getVariable(tmpKey.row); Variable tmpColVariable = tmpModel.getVariable(tmpKey.column); IntIndex tmpRowKey = tmpRowVariable.getIndex(); IntIndex tmpColKey = tmpColVariable.getIndex(); if (fixedVariables.contains(tmpRowKey)) { BigDecimal tmpRowValue = tmpRowVariable.getValue(); if (fixedVariables.contains(tmpColKey)) { // Both fixed BigDecimal tmpColValue = tmpColVariable.getValue(); tmpFixedValue = tmpFixedValue.add(tmpFactor.multiply(tmpRowValue).multiply(tmpColValue)); } else { // Row fixed retVal.add(tmpColKey, tmpFactor.multiply(tmpRowValue)); } } else if (fixedVariables.contains(tmpColKey)) { // Column fixed BigDecimal tmpColValue = tmpColVariable.getValue(); retVal.add(tmpRowKey, tmpFactor.multiply(tmpColValue)); } else { // Neither fixed retVal.set(tmpKey, tmpFactor); } } if (this.isLowerLimitSet()) { retVal.lower(this.getLowerLimit().subtract(tmpFixedValue)); } if (this.isUpperLimitSet()) { retVal.upper(this.getUpperLimit().subtract(tmpFixedValue)); } if (this.isInteger()) { retVal.setInteger(); } return retVal; } public BigDecimal evaluate(final Access1D point) { BigDecimal retVal = this.getConstant(); BigDecimal factor; for (IntRowColumn quadKey : this.getQuadraticKeySet()) { factor = this.get(quadKey); retVal = retVal.add(factor.multiply(point.get(quadKey.row)).multiply(point.get(quadKey.column))); } for (IntIndex linKey : this.getLinearKeySet()) { factor = this.get(linKey); retVal = retVal.add(factor.multiply(point.get(linKey.index))); } return retVal; } public BigDecimal get(final IntIndex key) { return this.getLinearFactor(key, false); } public BigDecimal get(final IntRowColumn key) { return this.getQuadraticFactor(key, false); } public BigDecimal get(final Variable variable) { IntIndex tmpIndex = variable.getIndex(); if (tmpIndex != null) { return this.get(tmpIndex); } throw new IllegalStateException("Variable not part of (this) model!"); } public MatrixStore getAdjustedGradient(final Access1D point) { Primitive64Store retVal = Primitive64Store.FACTORY.make(myModel.countVariables(), 1); BinaryFunction tmpBaseFunc = PrimitiveMath.ADD; double tmpAdjustedFactor; UnaryFunction tmpModFunc; for (IntRowColumn tmpKey : this.getQuadraticKeySet()) { tmpAdjustedFactor = this.getAdjustedQuadraticFactor(tmpKey); tmpModFunc = tmpBaseFunc.second(tmpAdjustedFactor * point.doubleValue(tmpKey.column)); retVal.modifyOne(tmpKey.row, 0, tmpModFunc); tmpModFunc = tmpBaseFunc.second(tmpAdjustedFactor * point.doubleValue(tmpKey.row)); retVal.modifyOne(tmpKey.column, 0, tmpModFunc); } for (IntIndex tmpKey : this.getLinearKeySet()) { tmpAdjustedFactor = this.getAdjustedLinearFactor(tmpKey); tmpModFunc = tmpBaseFunc.second(tmpAdjustedFactor); retVal.modifyOne(tmpKey.index, 0, tmpModFunc); } return retVal; } public MatrixStore getAdjustedHessian() { int tmpCountVariables = myModel.countVariables(); Primitive64Store retVal = Primitive64Store.FACTORY.make(tmpCountVariables, tmpCountVariables); BinaryFunction tmpBaseFunc = PrimitiveMath.ADD; UnaryFunction tmpModFunc; for (IntRowColumn tmpKey : this.getQuadraticKeySet()) { tmpModFunc = tmpBaseFunc.second(this.getAdjustedQuadraticFactor(tmpKey)); retVal.modifyOne(tmpKey.row, tmpKey.column, tmpModFunc); retVal.modifyOne(tmpKey.column, tmpKey.row, tmpModFunc); } return retVal; } public double getAdjustedLinearFactor(final int aVar) { return this.getAdjustedLinearFactor(new IntIndex(aVar)); } public double getAdjustedLinearFactor(final IntIndex key) { return this.getLinearFactor(key, true).doubleValue(); } public double getAdjustedLinearFactor(final Variable aVar) { return this.getAdjustedLinearFactor(aVar.getIndex()); } public double getAdjustedQuadraticFactor(final int aVar1, final int aVar2) { return this.getAdjustedQuadraticFactor(new IntRowColumn(aVar1, aVar2)); } public double getAdjustedQuadraticFactor(final IntRowColumn key) { return this.getQuadraticFactor(key, true).doubleValue(); } public double getAdjustedQuadraticFactor(final Variable aVar1, final Variable aVar2) { return this.getAdjustedQuadraticFactor(myModel.indexOf(aVar1), myModel.indexOf(aVar2)); } public Set> getLinearEntrySet() { return myLinear.entrySet(); } public Set getLinearKeySet() { return myLinear.keySet(); } public Set> getQuadraticEntrySet() { return myQuadratic.entrySet(); } public Set getQuadraticKeySet() { return myQuadratic.keySet(); } public boolean isAnyLinearFactorNonZero() { return myLinear.size() > 0; } public boolean isAnyQuadraticFactorNonZero() { return myQuadratic.size() > 0; } public boolean isFunctionConstant() { return !this.isAnyQuadraticFactorNonZero() && !this.isAnyLinearFactorNonZero(); } public boolean isFunctionLinear() { return !this.isAnyQuadraticFactorNonZero() && this.isAnyLinearFactorNonZero(); } public boolean isFunctionPureQuadratic() { return this.isAnyQuadraticFactorNonZero() && !this.isAnyLinearFactorNonZero(); } public boolean isFunctionQuadratic() { return this.isAnyQuadraticFactorNonZero() && this.isAnyLinearFactorNonZero(); } @Override public boolean isInteger() { if (myInteger == null) { this.doIntegerRounding(); } return myInteger.booleanValue(); } /** * @return Are all the (linear) variables binary */ public boolean isLinearAndAllBinary() { return myQuadratic.size() == 0 && myLinear.size() > 0 && myLinear.keySet().stream().allMatch(i -> myModel.getVariable(i).isBinary()); } /** * @return Are all the (linear) variables integer */ public boolean isLinearAndAllInteger() { return myQuadratic.size() == 0 && myLinear.size() > 0 && myLinear.keySet().stream().allMatch(i -> myModel.getVariable(i).isInteger()); } /** * @return Are any of the (linear) variables binary */ public boolean isLinearAndAnyBinary() { return myQuadratic.size() == 0 && myLinear.size() > 0 && myLinear.keySet().stream().anyMatch(i -> myModel.getVariable(i).isBinary()); } /** * @return Are any of the (linear) variables integer */ public boolean isLinearAndAnyInteger() { return myQuadratic.size() == 0 && myLinear.size() > 0 && myLinear.keySet().stream().anyMatch(i -> myModel.getVariable(i).isInteger()); } /** * @see #set(Variable, Comparable) */ public Expression set(final int index, final Comparable value) { return this.set(myModel.getVariable(index), value); } /** * @see #set(Variable, Comparable) */ public Expression set(final int index, final double value) { return this.set(index, BigDecimal.valueOf(value)); } /** * @see #set(Variable, Comparable) */ public Expression set(final int row, final int column, final Comparable value) { return this.set(new IntRowColumn(row, column), value); } /** * @see #set(Variable, Comparable) */ public Expression set(final int row, final int column, final double value) { return this.set(row, column, BigDecimal.valueOf(value)); } /** * @see #set(Variable, Comparable) */ public Expression set(final int row, final int column, final long value) { return this.set(row, column, BigDecimal.valueOf(value)); } /** * @see #set(Variable, Comparable) */ public Expression set(final int index, final long value) { return this.set(index, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression set(final IntIndex key, final Comparable value) { if (key == null) { throw new IllegalArgumentException(); } BigDecimal tmpValue = ModelEntity.toBigDecimal(value); if (tmpValue.signum() != 0) { myLinear.put(key, tmpValue); myModel.addReference(key); } else { myLinear.remove(key); } return this; } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression set(final IntIndex key, final double value) { return this.set(key, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression set(final IntIndex row, final IntIndex column, final Comparable value) { return this.set(new IntRowColumn(row, column), value); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression set(final IntIndex row, final IntIndex column, final double value) { return this.set(row, column, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression set(final IntIndex row, final IntIndex column, final long value) { return this.set(row, column, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with a {@link Variable} or an int as the key/index instead. */ @Deprecated public Expression set(final IntIndex key, final long value) { return this.set(key, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with {@link Variable}:s or int:s as the key instead. */ @Deprecated public Expression set(final IntRowColumn key, final Comparable value) { if (key == null) { throw new IllegalArgumentException(); } BigDecimal tmpValue = ModelEntity.toBigDecimal(value); if (tmpValue.signum() != 0) { myQuadratic.put(key, tmpValue); myModel.addReference(key.row()); myModel.addReference(key.column()); } else { myQuadratic.remove(key); } return this; } /** * @deprecated Use the alternatives with {@link Variable}:s or int:s as the key instead. */ @Deprecated public Expression set(final IntRowColumn key, final double value) { return this.set(key, BigDecimal.valueOf(value)); } /** * @deprecated Use the alternatives with {@link Variable}:s or int:s as the key instead. */ @Deprecated public Expression set(final IntRowColumn key, final long value) { return this.set(key, BigDecimal.valueOf(value)); } /** * Will set (replace) the variable's factor to this value */ public Expression set(final Variable variable, final Comparable value) { return this.set(variable.getIndex(), value); } /** * @see #set(Variable, Comparable) */ public Expression set(final Variable variable, final double value) { return this.set(variable, BigDecimal.valueOf(value)); } /** * @see #set(Variable, Comparable) */ public Expression set(final Variable variable, final long value) { return this.set(variable, BigDecimal.valueOf(value)); } /** * @see #set(Variable, Comparable) */ public Expression set(final Variable variable1, final Variable variable2, final Comparable value) { return this.set(variable1.getIndex().index, variable2.getIndex().index, value); } /** * @see #set(Variable, Comparable) */ public Expression set(final Variable variable1, final Variable variable2, final double value) { return this.set(variable1, variable2, BigDecimal.valueOf(value)); } /** * @see #set(Variable, Comparable) */ public Expression set(final Variable variable1, final Variable variable2, final long value) { return this.set(variable1, variable2, BigDecimal.valueOf(value)); } /** * Will set the quadratic and linear factors to an expression that measures (the square of) the distance * from the given point. * * @param variables The relevant variables * @param point The point to measure from */ public void setCompoundFactorsOffset(final List variables, final Access1D point) { int tmpLength = variables.size(); if (point.count() != tmpLength) { throw new IllegalArgumentException(); } BigDecimal tmpLinearWeight = BigMath.TWO.negate(); Variable tmpVariable; BigDecimal tmpVal; for (int ij = 0; ij < tmpLength; ij++) { tmpVariable = variables.get(ij); tmpVal = ModelEntity.toBigDecimal(point.get(ij)); this.set(tmpVariable, tmpVariable, BigMath.ONE); this.set(tmpVariable, tmpVal.multiply(tmpLinearWeight)); } } public void setLinearFactors(final List variables, final Access1D factors) { int tmpLimit = variables.size(); if (factors.count() != tmpLimit) { throw new IllegalArgumentException(); } for (int i = 0; i < tmpLimit; i++) { this.set(variables.get(i), factors.get(i)); } } /** * Will set the linear factors to a simple sum expression - all factors equal 1.0. * * @param variables The relevant variables */ public void setLinearFactorsSimple(final List variables) { for (Variable tmpVariable : variables) { this.set(tmpVariable, BigMath.ONE); } } public void setQuadraticFactors(final List variables, final Access2D factors) { int tmpLimit = variables.size(); if (factors.countRows() != tmpLimit || factors.countColumns() != tmpLimit) { throw new IllegalArgumentException(); } for (int j = 0; j < tmpLimit; j++) { Variable tmpVar2 = variables.get(j); for (int i = 0; i < tmpLimit; i++) { this.set(variables.get(i), tmpVar2, factors.get(i, j)); } } } /** * Will attempt to exploit integer property to tighten the lower and/or upper limits (integer rounding). */ public void tighten() { if (this.isConstraint()) { this.isInteger(); } } public MultiaryFunction.TwiceDifferentiable toFunction() { if (this.isFunctionQuadratic()) { return this.makeQuadraticFunction(); } if (this.isFunctionPureQuadratic()) { return this.makePureQuadraticFunction(); } if (this.isFunctionLinear()) { return this.makeAffineFunction(); } return this.makeConstantFunction(); } private BigDecimal convert(final BigDecimal value, final boolean adjusted) { if (value == null) { return BigMath.ZERO; } if (!adjusted) { return value; } int tmpAdjExp = this.getAdjustmentExponent(); if (tmpAdjExp != 0) { return value.movePointRight(tmpAdjExp); } return value; } private BigDecimal getConstant() { return myConstant != null ? myConstant : BigMath.ZERO; } private AffineFunction makeAffineFunction() { AffineFunction retVal = AffineFunction.makePrimitive(myModel.countVariables()); if (this.isAnyLinearFactorNonZero()) { for (Entry entry : myLinear.entrySet()) { retVal.linear().set(entry.getKey().index, entry.getValue().doubleValue()); } } retVal.setConstant(this.getConstant()); return retVal; } private ConstantFunction makeConstantFunction() { return ConstantFunction.makePrimitive(myModel.countVariables(), this.getConstant()); } private PureQuadraticFunction makePureQuadraticFunction() { PureQuadraticFunction retVal = PureQuadraticFunction.makePrimitive(myModel.countVariables()); if (this.isAnyQuadraticFactorNonZero()) { for (Entry entry : myQuadratic.entrySet()) { retVal.quadratic().set(entry.getKey().row, entry.getKey().column, entry.getValue().doubleValue()); } } retVal.setConstant(this.getConstant()); return retVal; } private QuadraticFunction makeQuadraticFunction() { QuadraticFunction retVal = QuadraticFunction.makePrimitive(myModel.countVariables()); if (this.isAnyQuadraticFactorNonZero()) { for (Entry entry : myQuadratic.entrySet()) { retVal.quadratic().set(entry.getKey().row, entry.getKey().column, entry.getValue().doubleValue()); } } if (this.isAnyLinearFactorNonZero()) { for (Entry entry : myLinear.entrySet()) { retVal.linear().set(entry.getKey().index, entry.getValue().doubleValue()); } } retVal.setConstant(this.getConstant()); return retVal; } private BigDecimal toPositiveFraction(final BigDecimal noninteger) { BigDecimal intPart = noninteger.setScale(0, RoundingMode.FLOOR); return noninteger.subtract(intPart); } protected void appendMiddlePart(final StringBuilder builder, final Access1D currentSolution) { builder.append(this.getName()); builder.append(": "); builder.append(ModelEntity.DISPLAY.enforce(this.toFunction().invoke(Access1D.asPrimitive1D(currentSolution)))); if (this.isObjective()) { builder.append(" ("); builder.append(ModelEntity.DISPLAY.enforce(this.getContributionWeight())); builder.append(")"); } } @Override protected void destroy() { super.destroy(); if (!myShallowCopy) { myLinear.clear(); myQuadratic.clear(); } } void addObjectiveConstant(final BigDecimal value) { BigDecimal weight = this.getContributionWeight(); if (weight != null && weight.signum() != 0) { myModel.addObjectiveConstant(value.multiply(weight)); } else { myModel.addObjectiveConstant(value); } } void appendToString(final StringBuilder aStringBuilder, final Access1D aCurrentState) { this.appendLeftPart(aStringBuilder); if (aCurrentState != null) { this.appendMiddlePart(aStringBuilder, aCurrentState); } else { this.appendMiddlePart(aStringBuilder); } this.appendRightPart(aStringBuilder); } /** * Calculates this expression's value - the subset variables' part of this expression. Will never return * null. */ BigDecimal calculateSetValue(final Collection subset) { BigDecimal retVal = BigMath.ZERO; if (subset.size() > 0) { for (IntIndex linKey : myLinear.keySet()) { if (subset.contains(linKey)) { BigDecimal coefficient = this.get(linKey); BigDecimal value = myModel.getVariable(linKey.index).getValue(); retVal = retVal.add(coefficient.multiply(value)); } } for (IntRowColumn quadKey : myQuadratic.keySet()) { if (subset.contains(quadKey.row()) && subset.contains(quadKey.column())) { BigDecimal coefficient = this.get(quadKey); BigDecimal rowValue = myModel.getVariable(quadKey.row).getValue(); BigDecimal colValue = myModel.getVariable(quadKey.column).getValue(); retVal = retVal.add(coefficient.multiply(rowValue).multiply(colValue)); } } } return retVal; } Expression copy(final ExpressionsBasedModel destinationModel, final boolean deep) { return new Expression(this, destinationModel, deep); } long countIntegerFactors() { return myLinear.keySet().stream().map(this::resolve).filter(Variable::isInteger).count(); } int countLinearFactors() { return myLinear.size(); } int countQuadraticFactors() { return myQuadratic.size(); } @Override int deriveAdjustmentExponent() { if (this.isInteger()) { return 0; } AggregatorSet aggregators = BigAggregator.getSet(); AggregatorFunction largest = aggregators.largest(); AggregatorFunction smallest = aggregators.smallest(); if (this.isAnyQuadraticFactorNonZero()) { for (BigDecimal quadraticFactor : myQuadratic.values()) { largest.invoke(quadraticFactor); smallest.invoke(quadraticFactor); } return ModelEntity.deriveAdjustmentExponent(largest, smallest, 6); } if (!this.isAnyLinearFactorNonZero()) { return 0; } for (BigDecimal linearFactor : myLinear.values()) { largest.invoke(linearFactor); smallest.invoke(linearFactor); } return ModelEntity.deriveAdjustmentExponent(largest, smallest, 16); } /** * Assumes at least 1 variable, and all variables integer! * * @see org.ojalgo.optimisation.ModelEntity#doIntegerRounding() */ @Override void doIntegerRounding() { this.doIntegerRounding(this.getLinearKeySet(), this.getLowerLimit(), this.getUpperLimit()); } void doIntegerRounding(final Set remaining, final BigDecimal lower, final BigDecimal upper) { if (myInteger != null) { return; } if (remaining.size() == 0 || !myModel.isInteger(remaining) || myQuadratic.size() > 0) { myInteger = Boolean.FALSE; return; } BigInteger gcd = null; int maxScale = Integer.MIN_VALUE; for (IntIndex index : remaining) { BigDecimal coeff = myLinear.get(index); BigDecimal abs = coeff.stripTrailingZeros().abs(); maxScale = Math.max(maxScale, abs.scale()); if (gcd != null) { gcd = gcd.gcd(abs.unscaledValue()); } else { gcd = abs.unscaledValue(); } if (maxScale > 8 || (gcd.equals(BigInteger.ONE) && maxScale > 0)) { myInteger = Boolean.FALSE; return; } } BigDecimal divisor = new BigDecimal(gcd, maxScale); boolean full = myLinear.size() == remaining.size(); BigDecimal newLower = null, newUpper = null; if (lower != null) { BigDecimal tmpVal = lower.divide(divisor, 0, RoundingMode.CEILING); newLower = tmpVal.multiply(divisor); if (full) { this.lower(newLower); } } if (upper != null) { BigDecimal tmpVal = upper.divide(divisor, 0, RoundingMode.FLOOR); newUpper = tmpVal.multiply(divisor); if (full) { this.upper(newUpper); } } if (ModelEntity.isInfeasible(newLower, newUpper)) { this.setInfeasible(); } myInteger = Boolean.TRUE; } Expression doMixedIntegerRounding() { if (!this.isEqualityConstraint()) { return null; } BigDecimal posFracLevel = this.toPositiveFraction(this.getLowerLimit()); if (posFracLevel.signum() <= 0) { return null; } BigDecimal cmpFracLevel = BigMath.ONE.subtract(posFracLevel); Expression retVal = myModel.addExpression(this.getName() + "(MIR)"); for (Entry entry : myLinear.entrySet()) { Variable variable = this.resolve(entry.getKey()); if (!variable.isLowerLimitSet() || variable.getLowerLimit().compareTo(BigMath.ZERO) < 0) { return null; } BigDecimal coeff = entry.getValue(); if (variable.isInteger()) { BigDecimal posFracCoeff = this.toPositiveFraction(coeff); if (posFracCoeff.compareTo(posFracLevel) <= 0) { retVal.set(variable, BigMath.DIVIDE.invoke(posFracCoeff, posFracLevel)); } else { BigDecimal cmpFracCoeff = BigMath.ONE.subtract(posFracCoeff); retVal.set(variable, BigMath.DIVIDE.invoke(cmpFracCoeff, cmpFracLevel)); } } else if (coeff.signum() == 1) { retVal.set(variable, BigMath.DIVIDE.invoke(coeff, posFracLevel)); } else if (coeff.signum() == -1) { BigDecimal negCoeff = coeff.negate(); retVal.set(variable, BigMath.DIVIDE.invoke(negCoeff, cmpFracLevel)); } } return retVal.lower(BigMath.ONE); } Set getBinaryVariables(final Set subset) { HashSet retVal = new HashSet<>(); for (IntIndex varInd : myLinear.keySet()) { if (subset.contains(varInd)) { Variable variable = myModel.getVariable(varInd.index); if (variable.isBinary()) { retVal.add(variable); } } } return retVal; } Map getLinear() { return myLinear; } BigDecimal getLinearFactor(final IntIndex key, final boolean adjusted) { return this.convert(myLinear.get(key), adjusted); } ExpressionsBasedModel getModel() { return myModel; } Map getQuadratic() { return myQuadratic; } BigDecimal getQuadraticFactor(final IntRowColumn key, final boolean adjusted) { return this.convert(myQuadratic.get(key), adjusted); } boolean includes(final Variable variable) { IntIndex tmpVarInd = variable.getIndex(); return myLinear.containsKey(tmpVarInd) || myQuadratic.size() > 0 && myQuadratic.keySet().stream().anyMatch(k -> (k.row == tmpVarInd.index || k.column == tmpVarInd.index)); } boolean isConstantSet() { return myConstant != null && myConstant.signum() != 0; } @Override boolean isInfeasible() { return myInfeasible || super.isInfeasible(); } /** * @param subset The indices of a variable subset * @return true if none of the variables in the subset can make a positve contribution to the expression * value */ boolean isNegativeOn(final Set subset) { if (!this.isAnyQuadraticFactorNonZero()) { for (IntIndex index : subset) { Variable setVar = myModel.getVariable(index); int signum = myLinear.get(index).signum(); if (signum < 0 && setVar.isLowerLimitSet() && setVar.getLowerLimit().signum() >= 0) { } else if (signum > 0 && setVar.isUpperLimitSet() && setVar.getUpperLimit().signum() <= 0) { } else { return false; } } } return true; } /** * @param subset The indices of a variable subset * @return true if none of the variables in the subset can make a negative contribution to the expression * value */ boolean isPositiveOn(final Set subset) { if (!this.isAnyQuadraticFactorNonZero()) { for (IntIndex index : subset) { Variable setVar = myModel.getVariable(index); int signum = myLinear.get(index).signum(); if (signum > 0 && setVar.isLowerLimitSet() && setVar.getLowerLimit().signum() >= 0) { } else if (signum < 0 && setVar.isUpperLimitSet() && setVar.getUpperLimit().signum() <= 0) { } else { return false; } } } return true; } boolean isRedundant() { return myRedundant; } Variable resolve(final Structure1D.IntIndex index) { return myModel.getVariable(index); } void setConstant(final Comparable value) { myConstant = ModelEntity.toBigDecimal(value); } void setConstant(final double value) { myConstant = BigDecimal.valueOf(value); } void setConstant(final long value) { myConstant = BigDecimal.valueOf(value); } void setInfeasible() { myInfeasible = true; myModel.setInfeasible(); } void setInteger() { myInteger = Boolean.TRUE; } void setRedundant() { myRedundant = true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy