parent
46aded16e7
commit
ae31e0faf9
|
@ -0,0 +1,2 @@
|
|||
.idea/
|
||||
/build
|
|
@ -0,0 +1,17 @@
|
|||
apply plugin: 'java'
|
||||
|
||||
sourceCompatibility = 1.5
|
||||
version = '1.0'
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '1.9'
|
||||
distributionUrl = 'http://services.gradle.org/distributions/gradle-1.9-all.zip'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile group: 'junit', name: 'junit', version: '4.11'
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
#Fri Jan 30 18:41:56 CET 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-all.zip
|
|
@ -0,0 +1,90 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -0,0 +1,11 @@
|
|||
## This file is automatically generated by Android Studio.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must *NOT* be checked into Version Control Systems,
|
||||
# as it contains information specific to your local configuration.
|
||||
#
|
||||
# Location of the SDK. This is only used by Gradle.
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
#Sat Jan 31 12:43:25 CET 2015
|
||||
sdk.dir=/Users/alex/sdk
|
|
@ -0,0 +1,4 @@
|
|||
rootProject.name = 'kiwi-java'
|
||||
|
||||
include 'lib'
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Constraint {
|
||||
|
||||
private Expression expression;
|
||||
private double strength;
|
||||
private RelationalOperator op;
|
||||
|
||||
public Constraint(){
|
||||
}
|
||||
|
||||
public Constraint(Expression expr, RelationalOperator op) {
|
||||
this(expr, op, Strength.REQUIRED);
|
||||
}
|
||||
|
||||
public Constraint(Expression expr, RelationalOperator op, double strength) {
|
||||
this.expression = expr;
|
||||
this.op = op;
|
||||
this.strength = Strength.clip(strength);
|
||||
}
|
||||
|
||||
public Constraint(Constraint other, RelationalOperator op) {
|
||||
this(other.expression, other.op, other.strength);
|
||||
}
|
||||
|
||||
public Expression getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public void setExpression(Expression expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
public double getStrength() {
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void setStrength(double strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public RelationalOperator getOp() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public void setOp(RelationalOperator op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class DuplicateConstraintException extends Exception {
|
||||
|
||||
public DuplicateConstraintException(Constraint constraint) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Expression {
|
||||
|
||||
private List<Term> terms;
|
||||
|
||||
private double constant;
|
||||
|
||||
public Expression() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public Expression(double constant) {
|
||||
this.constant = constant;
|
||||
this.terms = new ArrayList<Term>();
|
||||
}
|
||||
|
||||
public Expression(Term term, double constant) {
|
||||
this.terms = new ArrayList<Term>();
|
||||
terms.add(term);
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
public Expression(Term term) {
|
||||
this (term, 0.0);
|
||||
}
|
||||
|
||||
public Expression(List<Term> terms, double constant) {
|
||||
this.terms = terms;
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
public double getConstant() {
|
||||
return constant;
|
||||
}
|
||||
|
||||
public void setConstant(double constant) {
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
public List<Term> getTerms() {
|
||||
return terms;
|
||||
}
|
||||
|
||||
public void setTerms(List<Term> terms) {
|
||||
this.terms = terms;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
double result = this.constant;
|
||||
|
||||
for (Term term : terms) {
|
||||
result += term.getValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 31/01/15.
|
||||
*/
|
||||
public class InternalSolverError extends RuntimeException {
|
||||
|
||||
public InternalSolverError(String string) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 31/01/15.
|
||||
*/
|
||||
public enum RelationalOperator {
|
||||
OP_LE,
|
||||
OP_GE,
|
||||
OP_EQ
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class RequiredFailureException extends Exception {
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Row {
|
||||
|
||||
private double constant;
|
||||
|
||||
private Map<Symbol, Double> cells = new HashMap<Symbol, Double>();
|
||||
|
||||
public Row() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public Row(double constant) {
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
public Row(Row other) {
|
||||
this.cells = other.cells;
|
||||
this.constant = other.constant;
|
||||
}
|
||||
|
||||
public double getConstant() {
|
||||
return constant;
|
||||
}
|
||||
|
||||
public void setConstant(double constant) {
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
public Map<Symbol, Double> getCells() {
|
||||
return cells;
|
||||
}
|
||||
|
||||
public void setCells(Map<Symbol, Double> cells) {
|
||||
this.cells = cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a constant value to the row constant.
|
||||
*
|
||||
* @return The new value of the constant
|
||||
*/
|
||||
double add(double value) {
|
||||
return this.constant += value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a symbol into the row with a given coefficient.
|
||||
* <p/>
|
||||
* If the symbol already exists in the row, the coefficient will be
|
||||
* added to the existing coefficient. If the resulting coefficient
|
||||
* is zero, the symbol will be removed from the row
|
||||
*/
|
||||
void insert(Symbol symbol, double coefficient) {
|
||||
|
||||
Double existingCoefficient = cells.get(symbol);
|
||||
|
||||
if (existingCoefficient != null) {
|
||||
coefficient = existingCoefficient;
|
||||
}
|
||||
|
||||
if (Util.nearZero(coefficient)) {
|
||||
cells.remove(symbol);
|
||||
} else {
|
||||
cells.put(symbol, Double.valueOf(coefficient));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a symbol into the row with a given coefficient.
|
||||
* <p/>
|
||||
* If the symbol already exists in the row, the coefficient will be
|
||||
* added to the existing coefficient. If the resulting coefficient
|
||||
* is zero, the symbol will be removed from the row
|
||||
*/
|
||||
void insert(Symbol symbol) {
|
||||
insert(symbol, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a row into this row with a given coefficient.
|
||||
* The constant and the cells of the other row will be multiplied by
|
||||
* the coefficient and added to this row. Any cell with a resulting
|
||||
* coefficient of zero will be removed from the row.
|
||||
*
|
||||
* @param other
|
||||
* @param coefficient
|
||||
*/
|
||||
void insert(Row other, double coefficient) {
|
||||
this.constant += other.constant * coefficient;
|
||||
|
||||
Set<Map.Entry<Symbol, Double>> map = other.getCells().entrySet();
|
||||
|
||||
for (Map.Entry<Symbol, Double> entry : map) {
|
||||
double coeff = entry.getValue() * coefficient;
|
||||
insert(entry.getKey(), coeff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a row into this row with a given coefficient.
|
||||
* The constant and the cells of the other row will be multiplied by
|
||||
* the coefficient and added to this row. Any cell with a resulting
|
||||
* coefficient of zero will be removed from the row.
|
||||
*
|
||||
* @param other
|
||||
*/
|
||||
void insert(Row other) {
|
||||
insert(other, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given symbol from the row.
|
||||
*/
|
||||
void remove(Symbol symbol) {
|
||||
|
||||
cells.remove(symbol);
|
||||
// not sure what this does, can the symbol be added more than once?
|
||||
/*CellMap::iterator it = m_cells.find( symbol );
|
||||
if( it != m_cells.end() )
|
||||
m_cells.erase( it );*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the sign of the constant and all cells in the row.
|
||||
*/
|
||||
void reverseSign() {
|
||||
this.constant = -this.constant;
|
||||
|
||||
Set<Map.Entry<Symbol, Double>> map = getCells().entrySet();
|
||||
|
||||
for (Map.Entry<Symbol, Double> entry : map) {
|
||||
entry.setValue(-entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve the row for the given symbol.
|
||||
* <p/>
|
||||
* This method assumes the row is of the form a * x + b * y + c = 0
|
||||
* and (assuming solve for x) will modify the row to represent the
|
||||
* right hand side of x = -b/a * y - c / a. The target symbol will
|
||||
* be removed from the row, and the constant and other cells will
|
||||
* be multiplied by the negative inverse of the target coefficient.
|
||||
* The given symbol *must* exist in the row.
|
||||
*
|
||||
* @param symbol
|
||||
*/
|
||||
void solveFor(Symbol symbol) {
|
||||
double coeff = -1.0 / cells.get(symbol);
|
||||
cells.remove(symbol);
|
||||
this.constant *= coeff;
|
||||
|
||||
Set<Map.Entry<Symbol, Double>> map = getCells().entrySet();
|
||||
|
||||
for (Map.Entry<Symbol, Double> entry : map) {
|
||||
entry.setValue(entry.getValue() * coeff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve the row for the given symbols.
|
||||
* <p/>
|
||||
* This method assumes the row is of the form x = b * y + c and will
|
||||
* solve the row such that y = x / b - c / b. The rhs symbol will be
|
||||
* removed from the row, the lhs added, and the result divided by the
|
||||
* negative inverse of the rhs coefficient.
|
||||
* The lhs symbol *must not* exist in the row, and the rhs symbol
|
||||
* must* exist in the row.
|
||||
*
|
||||
* @param lhs
|
||||
* @param rhs
|
||||
*/
|
||||
void solveFor(Symbol lhs, Symbol rhs) {
|
||||
insert(lhs, -1.0);
|
||||
solveFor(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the coefficient for the given symbol.
|
||||
* <p/>
|
||||
* If the symbol does not exist in the row, zero will be returned.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
double coefficientFor(Symbol symbol) {
|
||||
if (this.cells.containsKey(symbol)) {
|
||||
return this.cells.get(symbol);
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute a symbol with the data from another row.
|
||||
* <p/>
|
||||
* Given a row of the form a * x + b and a substitution of the
|
||||
* form x = 3 * y + c the row will be updated to reflect the
|
||||
* expression 3 * a * y + a * c + b.
|
||||
* If the symbol does not exist in the row, this is a no-op.
|
||||
*/
|
||||
void substitute(Symbol symbol, Row row) {
|
||||
if (cells.containsKey(symbol)) {
|
||||
double coefficient = cells.get(symbol);
|
||||
cells.remove(symbol);
|
||||
insert(row, coefficient);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,432 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Solver {
|
||||
|
||||
private static class Tag {
|
||||
Symbol marker;
|
||||
Symbol other;
|
||||
}
|
||||
|
||||
private static class EditInfo {
|
||||
Tag tag;
|
||||
Constraint constraint;
|
||||
double constant;
|
||||
}
|
||||
|
||||
private static class RowAndTag {
|
||||
Tag tag;
|
||||
Row row;
|
||||
}
|
||||
|
||||
private Map<Constraint, Tag> cns = new HashMap<Constraint, Tag>();
|
||||
private Map<Symbol, Row> rows = new HashMap<Symbol, Row>();
|
||||
private Map<Variable, Symbol> vars = new HashMap<Variable, Symbol>();
|
||||
private Map<Variable, EditInfo> edits = new HashMap<Variable, EditInfo>();
|
||||
private List<Symbol> infeasibleRows = new ArrayList<Symbol>();
|
||||
private Row objective = new Row();
|
||||
private Row artificial;
|
||||
private long idTick = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Add a constraint to the solver.
|
||||
*
|
||||
* @param constraint
|
||||
* @throws DuplicateConstraintException The given constraint has already been added to the solver.
|
||||
* @throws UnsatisfiableConstraintException The given constraint is required and cannot be satisfied.
|
||||
*/
|
||||
public void addConstraint(Constraint constraint) throws DuplicateConstraintException, UnsatisfiableConstraintException {
|
||||
|
||||
if (cns.containsKey(constraint)) {
|
||||
throw new DuplicateConstraintException(constraint);
|
||||
}
|
||||
|
||||
// Creating a row causes symbols to reserved for the variables
|
||||
// in the constraint. If this method exits with an exception,
|
||||
// then its possible those variables will linger in the var map.
|
||||
// Since its likely that those variables will be used in other
|
||||
// constraints and since exceptional conditions are uncommon,
|
||||
// i'm not too worried about aggressive cleanup of the var map.
|
||||
|
||||
RowAndTag rowAndTag = createRow(constraint);
|
||||
|
||||
Symbol subject = chooseSubject(rowAndTag.row, rowAndTag.tag);
|
||||
|
||||
// If chooseSubject could find a valid entering symbol, one
|
||||
// last option is available if the entire row is composed of
|
||||
// dummy variables. If the constant of the row is zero, then
|
||||
// this represents redundant constraints and the new dummy
|
||||
// marker can enter the basis. If the constant is non-zero,
|
||||
// then it represents an unsatisfiable constraint.
|
||||
if (subject.getType() == Symbol.Type.INVALID && allDummies(rowAndTag.row)) {
|
||||
if (!Util.nearZero(rowAndTag.row.getConstant())) {
|
||||
throw new UnsatisfiableConstraintException(constraint);
|
||||
} else {
|
||||
subject = rowAndTag.tag.marker;
|
||||
}
|
||||
}
|
||||
|
||||
// If an entering symbol still isn't found, then the row must
|
||||
// be added using an artificial variable. If that fails, then
|
||||
// the row represents an unsatisfiable constraint.
|
||||
if (subject.getType() == Symbol.Type.INVALID) {
|
||||
if (!addWithArtificialVariable(rowAndTag.row)) {
|
||||
throw new UnsatisfiableConstraintException(constraint);
|
||||
}
|
||||
} else {
|
||||
rowAndTag.row.solveFor(subject);
|
||||
substitute(subject, rowAndTag.row);
|
||||
this.rows.put(subject, rowAndTag.row);
|
||||
}
|
||||
|
||||
this.cns.put(constraint, rowAndTag.tag);
|
||||
|
||||
// Optimizing after each constraint is added performs less
|
||||
// aggregate work due to a smaller average system size. It
|
||||
// also ensures the solver remains in a consistent state.
|
||||
optimize(this.objective);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the values of the external solver variables.
|
||||
*/
|
||||
void updateVariables() {
|
||||
|
||||
for (Map.Entry<Variable, Symbol> varEntry : vars.entrySet()) {
|
||||
Variable variable = varEntry.getKey();
|
||||
Row row = this.rows.get(varEntry.getValue());
|
||||
|
||||
if (row == null) {
|
||||
variable.setValue(0);
|
||||
} else {
|
||||
variable.setValue(row.getConstant());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new Row object for the given constraint.
|
||||
* <p/>
|
||||
* The terms in the constraint will be converted to cells in the row.
|
||||
* Any term in the constraint with a coefficient of zero is ignored.
|
||||
* This method uses the `getVarSymbol` method to get the symbol for
|
||||
* the variables added to the row. If the symbol for a given cell
|
||||
* variable is basic, the cell variable will be substituted with the
|
||||
* basic row.
|
||||
* <p/>
|
||||
* The necessary slack and error variables will be added to the row.
|
||||
* If the constant for the row is negative, the sign for the row
|
||||
* will be inverted so the constant becomes positive.
|
||||
* <p/>
|
||||
* The tag will be updated with the marker and error symbols to use
|
||||
* for tracking the movement of the constraint in the tableau.
|
||||
*/
|
||||
RowAndTag createRow(Constraint constraint) {
|
||||
|
||||
Expression expr = constraint.getExpression();
|
||||
Row row = new Row(expr.getConstant());
|
||||
Tag tag = new Tag();
|
||||
|
||||
// Substitute the current basic variables into the row.
|
||||
for (Term term : expr.getTerms()) {
|
||||
if (!Util.nearZero(term.getCoefficient())) {
|
||||
Symbol symbol = getVarSymbol(term.getVariable());
|
||||
|
||||
Row otherRow = rows.get(symbol);
|
||||
|
||||
if (otherRow == null) {
|
||||
row.insert(symbol, term.getCoefficient());
|
||||
} else {
|
||||
row.insert(otherRow, term.getCoefficient());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the necessary slack, error, and dummy variables.
|
||||
switch (constraint.getOp()) {
|
||||
case OP_LE:
|
||||
case OP_GE: {
|
||||
double coeff = constraint.getOp() == RelationalOperator.OP_LE ? 1.0 : -1.0;
|
||||
Symbol slack = new Symbol(Symbol.Type.SLACK, idTick++);
|
||||
tag.marker = slack;
|
||||
row.insert(slack, coeff);
|
||||
if (constraint.getStrength() < Strength.REQUIRED) {
|
||||
Symbol error = new Symbol(Symbol.Type.ERROR, idTick++);
|
||||
tag.other = error;
|
||||
row.insert(error, -coeff);
|
||||
this.objective.insert(error, constraint.getStrength());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_EQ: {
|
||||
if (constraint.getStrength() < Strength.REQUIRED) {
|
||||
Symbol errplus = new Symbol(Symbol.Type.ERROR, idTick++);
|
||||
Symbol errminus = new Symbol(Symbol.Type.ERROR, idTick++);
|
||||
tag.marker = errplus;
|
||||
tag.other = errminus;
|
||||
row.insert(errplus, -1.0); // v = eplus - eminus
|
||||
row.insert(errminus, 1.0); // v - eplus + eminus = 0
|
||||
this.objective.insert(errplus, constraint.getStrength());
|
||||
this.objective.insert(errminus, constraint.getStrength());
|
||||
} else {
|
||||
Symbol dummy = new Symbol(Symbol.Type.DUMMY, idTick++);
|
||||
tag.marker = dummy;
|
||||
row.insert(dummy);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the row as a positive constant.
|
||||
if (row.getConstant() < 0.0) {
|
||||
row.reverseSign();
|
||||
}
|
||||
|
||||
RowAndTag rowAndTag = new RowAndTag();
|
||||
rowAndTag.row = row;
|
||||
rowAndTag.tag = tag;
|
||||
|
||||
|
||||
return rowAndTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the subject for solving for the row
|
||||
* <p/>
|
||||
* This method will choose the best subject for using as the solve
|
||||
* target for the row. An invalid symbol will be returned if there
|
||||
* is no valid target.
|
||||
* The symbols are chosen according to the following precedence:
|
||||
* 1) The first symbol representing an external variable.
|
||||
* 2) A negative slack or error tag variable.
|
||||
* If a subject cannot be found, an invalid symbol will be returned.
|
||||
*/
|
||||
private static Symbol chooseSubject(Row row, Tag tag) {
|
||||
|
||||
for (Map.Entry<Symbol, Double> cell : row.getCells().entrySet()) {
|
||||
if (cell.getKey().getType() == Symbol.Type.EXTERNAL) {
|
||||
return cell.getKey();
|
||||
}
|
||||
}
|
||||
if (tag.marker.getType() == Symbol.Type.SLACK || tag.marker.getType() == Symbol.Type.ERROR) {
|
||||
if (row.coefficientFor(tag.marker) < 0.0)
|
||||
return tag.marker;
|
||||
}
|
||||
if (tag.other.getType() == Symbol.Type.SLACK || tag.other.getType() == Symbol.Type.ERROR) {
|
||||
if (row.coefficientFor(tag.other) < 0.0)
|
||||
return tag.other;
|
||||
}
|
||||
return new Symbol();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the row to the tableau using an artificial variable.
|
||||
* <p/>
|
||||
* This will return false if the constraint cannot be satisfied.
|
||||
*/
|
||||
private boolean addWithArtificialVariable(Row row) {
|
||||
//TODO check this
|
||||
|
||||
// Create and add the artificial variable to the tableau
|
||||
|
||||
Symbol art = new Symbol(Symbol.Type.SLACK, idTick++);
|
||||
rows.put(art, new Row(row));
|
||||
|
||||
this.artificial = new Row(row);
|
||||
|
||||
// Optimize the artificial objective. This is successful
|
||||
// only if the artificial objective is optimized to zero.
|
||||
optimize(this.artificial);
|
||||
boolean success = Util.nearZero(artificial.getConstant());
|
||||
artificial = null;
|
||||
|
||||
// If the artificial variable is basic, pivot the row so that
|
||||
// it becomes basic. If the row is constant, exit early.
|
||||
|
||||
Row rowptr = this.rows.get(art);
|
||||
|
||||
if (rowptr != null) {
|
||||
rows.remove(rowptr);
|
||||
if (rowptr.getCells().isEmpty()) {
|
||||
return success;
|
||||
}
|
||||
Symbol entering = anyPivotableSymbol(rowptr);
|
||||
if (entering.getType() == Symbol.Type.INVALID) {
|
||||
return false; // unsatisfiable (will this ever happen?)
|
||||
}
|
||||
rowptr.solveFor(art, entering);
|
||||
substitute(entering, rowptr);
|
||||
this.rows.put(entering, rowptr);
|
||||
}
|
||||
|
||||
// Remove the artificial variable from the tableau.
|
||||
for (Map.Entry<Symbol, Row> rowEntry : rows.entrySet()) {
|
||||
rowEntry.getValue().remove(art);
|
||||
}
|
||||
|
||||
objective.remove(art);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute the parametric symbol with the given row.
|
||||
* <p/>
|
||||
* This method will substitute all instances of the parametric symbol
|
||||
* in the tableau and the objective function with the given row.
|
||||
*/
|
||||
void substitute(Symbol symbol, Row row) {
|
||||
for (Map.Entry<Symbol, Row> rowEntry : rows.entrySet()) {
|
||||
rowEntry.getValue().substitute(symbol, row);
|
||||
if (rowEntry.getKey().getType() != Symbol.Type.EXTERNAL && rowEntry.getValue().getConstant() < 0.0) {
|
||||
infeasibleRows.add(rowEntry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
objective.substitute(symbol, row);
|
||||
|
||||
if (artificial != null) {
|
||||
artificial.substitute(symbol, row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize the system for the given objective function.
|
||||
* <p/>
|
||||
* This method performs iterations of Phase 2 of the simplex method
|
||||
* until the objective function reaches a minimum.
|
||||
*
|
||||
* @throws InternalSolverError The value of the objective function is unbounded.
|
||||
*/
|
||||
void optimize(Row objective) {
|
||||
while (true) {
|
||||
Symbol entering = getEnteringSymbol(objective);
|
||||
if (entering.getType() == Symbol.Type.INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map.Entry<Symbol, Row> entry = getLeavingRow(entering);
|
||||
|
||||
if (entry == null) {
|
||||
throw new InternalSolverError("The objective is unbounded.");
|
||||
}
|
||||
|
||||
// pivot the entering symbol into the basis
|
||||
Symbol leaving = entry.getKey();
|
||||
Row row = entry.getValue();
|
||||
|
||||
this.rows.remove(entry.getKey());
|
||||
|
||||
row.solveFor(leaving, entering);
|
||||
|
||||
substitute(entering, row);
|
||||
|
||||
this.rows.put(entering, row);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the entering variable for a pivot operation.
|
||||
* <p/>
|
||||
* This method will return first symbol in the objective function which
|
||||
* is non-dummy and has a coefficient less than zero. If no symbol meets
|
||||
* the criteria, it means the objective function is at a minimum, and an
|
||||
* invalid symbol is returned.
|
||||
*/
|
||||
private static Symbol getEnteringSymbol(Row objective) {
|
||||
|
||||
for (Map.Entry<Symbol, Double> cell : objective.getCells().entrySet()) {
|
||||
|
||||
if (cell.getKey().getType() != Symbol.Type.DUMMY && cell.getValue() < 0.0) {
|
||||
return cell.getKey();
|
||||
}
|
||||
}
|
||||
return new Symbol();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first Slack or Error symbol in the row.
|
||||
* <p/>
|
||||
* If no such symbol is present, and Invalid symbol will be returned.
|
||||
*/
|
||||
private Symbol anyPivotableSymbol(Row row) {
|
||||
Symbol symbol = null;
|
||||
for (Map.Entry<Symbol, Double> entry : objective.getCells().entrySet()) {
|
||||
if (entry.getKey().getType() == Symbol.Type.SLACK || entry.getKey().getType() == Symbol.Type.ERROR) {
|
||||
symbol = entry.getKey();
|
||||
}
|
||||
}
|
||||
if (symbol == null) {
|
||||
symbol = new Symbol();
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the row which holds the exit symbol for a pivot.
|
||||
* <p/>
|
||||
* This documentation is copied from the C++ version and is outdated
|
||||
* <p/>
|
||||
* <p/>
|
||||
* This method will return an iterator to the row in the row map
|
||||
* which holds the exit symbol. If no appropriate exit symbol is
|
||||
* found, the end() iterator will be returned. This indicates that
|
||||
* the objective function is unbounded.
|
||||
*/
|
||||
private Map.Entry<Symbol, Row> getLeavingRow(Symbol entering) {
|
||||
// TODO check
|
||||
double ratio = Double.MAX_VALUE;
|
||||
Map.Entry<Symbol, Row> found = null;
|
||||
for (Map.Entry<Symbol, Row> row : rows.entrySet()) {
|
||||
if (row.getKey().getType() != Symbol.Type.EXTERNAL) {
|
||||
double temp = row.getValue().coefficientFor(entering);
|
||||
if (temp < 0.0) {
|
||||
double temp_ratio = -row.getValue().getConstant() / temp;
|
||||
if (temp_ratio < ratio) {
|
||||
ratio = temp_ratio;
|
||||
found = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the symbol for the given variable.
|
||||
* <p/>
|
||||
* If a symbol does not exist for the variable, one will be created.
|
||||
*/
|
||||
private Symbol getVarSymbol(Variable variable) {
|
||||
Symbol symbol;
|
||||
if (vars.containsKey(variable)) {
|
||||
symbol = vars.get(variable);
|
||||
} else {
|
||||
symbol = new Symbol(Symbol.Type.EXTERNAL, idTick++);
|
||||
vars.put(variable, symbol);
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a row is composed of all dummy variables.
|
||||
*/
|
||||
private static boolean allDummies(Row row) {
|
||||
for (Map.Entry<Symbol, Double> cell : row.getCells().entrySet()) {
|
||||
if (cell.getKey().getType() != Symbol.Type.DUMMY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Strength {
|
||||
|
||||
public static final double REQUIRED = create(1000.0, 1000.0, 1000.0);
|
||||
|
||||
public static final double STRONG = create(1.0, 0.0, 0.0);
|
||||
|
||||
public static final double MEDIUM = create(0.0, 1.0, 0.0);
|
||||
|
||||
public static final double WEAK = create(0.0, 0.0, 1.0);
|
||||
|
||||
|
||||
public static final double create(double a, double b, double c, double w) {
|
||||
double result = 0.0;
|
||||
result += Math.max(0.0, Math.min(1000.0, a * w)) * 1000000.0;
|
||||
result += Math.max(0.0, Math.min(1000.0, b * w)) * 1000.0;
|
||||
result += Math.max(0.0, Math.min(1000.0, c * w));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final double create(double a, double b, double c) {
|
||||
return create(a, b, c, 1.0);
|
||||
}
|
||||
|
||||
public static final double clip(double value) {
|
||||
return Math.max(0.0, Math.min(REQUIRED, value));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Symbol {
|
||||
|
||||
enum Type {
|
||||
INVALID,
|
||||
EXTERNAL,
|
||||
SLACK,
|
||||
ERROR,
|
||||
DUMMY
|
||||
}
|
||||
|
||||
private Type type;
|
||||
private long id;
|
||||
|
||||
public Symbol() {
|
||||
this(Type.INVALID, 0);
|
||||
}
|
||||
|
||||
public Symbol(Type type, long id) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
boolean lessThan(Symbol other) {
|
||||
return this.id < other.getId();
|
||||
}
|
||||
|
||||
boolean equals(Symbol other) {
|
||||
return this.id == other.getId();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Term {
|
||||
|
||||
private Variable variable;
|
||||
double coefficient;
|
||||
|
||||
public Term(Variable variable, double coefficient) {
|
||||
this.variable = variable;
|
||||
this.coefficient = coefficient;
|
||||
}
|
||||
|
||||
public Term(Variable variable) {
|
||||
this(variable, 1.0);
|
||||
}
|
||||
|
||||
public Variable getVariable() {
|
||||
return variable;
|
||||
}
|
||||
|
||||
public void setVariable(Variable variable) {
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
public double getCoefficient() {
|
||||
return coefficient;
|
||||
}
|
||||
|
||||
public void setCoefficient(double coefficient) {
|
||||
this.coefficient = coefficient;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return coefficient * variable.getValue();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class UnsatisfiableConstraintException extends Exception {
|
||||
public UnsatisfiableConstraintException(Constraint constraint) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Util {
|
||||
private static double EPS = 1.0e-8;
|
||||
|
||||
public static boolean nearZero(double value ) {
|
||||
return value < 0.0 ? -value < EPS : value < EPS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
/**
|
||||
* Created by alex on 30/01/15.
|
||||
*/
|
||||
public class Variable {
|
||||
|
||||
private String name;
|
||||
|
||||
private double value;
|
||||
|
||||
public Variable(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Variable(double value) {
|
||||
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Expression times(double value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Expression plus(double value) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
package no.birkett.kiwi;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
public class Tests {
|
||||
|
||||
private static double EPSILON = 1.0e-8;
|
||||
|
||||
@Test
|
||||
public void testKiwi() throws UnsatisfiableConstraintException, DuplicateConstraintException {
|
||||
Solver solver = new Solver();
|
||||
Variable x = new Variable("x");
|
||||
Variable y = new Variable("y");
|
||||
|
||||
|
||||
Term term = new Term(x);
|
||||
|
||||
Expression expression = new Expression(term);
|
||||
|
||||
Constraint constraint = new Constraint(expression, RelationalOperator.OP_EQ);
|
||||
|
||||
solver.addConstraint(constraint);
|
||||
solver.updateVariables();
|
||||
}
|
||||
|
||||
/*@Test
|
||||
public void simple1() {
|
||||
Variable x = new Variable(167);
|
||||
Variable y = new Variable(2);
|
||||
Solver solver = new Solver();
|
||||
|
||||
Constraint eq = new Constraint(x, Constraint.Operator.EQ, new Expression(y));
|
||||
//ClLinearEquation eq = new ClLinearEquation(x, new ClLinearExpression(y));
|
||||
solver.addConstraint(eq);
|
||||
assertEquals(x.value(), y.value(), EPSILON);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void addDelete1() {
|
||||
Variable x = new Variable("x");
|
||||
Solver solver = new Solver();
|
||||
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.EQ, 100, Strength.WEAK));
|
||||
|
||||
Constraint c10 = new Constraint(x, Constraint.Operator.LEQ, 10.0);
|
||||
Constraint c20 = new Constraint(x, Constraint.Operator.LEQ, 20.0);
|
||||
|
||||
solver.addConstraint(c10);
|
||||
solver.addConstraint(c20);
|
||||
|
||||
assertEquals(10, x.value(), EPSILON);
|
||||
|
||||
solver.removeConstraint(c10);
|
||||
assertEquals(20, x.value(), EPSILON);
|
||||
|
||||
solver.removeConstraint(c20);
|
||||
assertEquals(100, x.value(), EPSILON);
|
||||
|
||||
Constraint c10again = new Constraint(x, Constraint.Operator.LEQ, 10.0);
|
||||
|
||||
solver.addConstraint(c10);
|
||||
solver.addConstraint(c10again);
|
||||
|
||||
assertEquals(10, x.value(), EPSILON);
|
||||
|
||||
solver.removeConstraint(c10);
|
||||
assertEquals(10, x.value(), EPSILON);
|
||||
|
||||
solver.removeConstraint(c10again);
|
||||
assertEquals(100, x.value(), EPSILON);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void addDelete2() {
|
||||
Variable x = new Variable("x");
|
||||
Variable y = new Variable("y");
|
||||
Solver solver = new Solver();
|
||||
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.EQ, 100.0, Strength.WEAK));
|
||||
solver.addConstraint(new Constraint(y, Constraint.Operator.EQ, 120.0, Strength.STRONG));
|
||||
|
||||
|
||||
Constraint c10 = new Constraint(x, Constraint.Operator.LEQ, 10.0);
|
||||
Constraint c20 = new Constraint(x, Constraint.Operator.LEQ, 20.0);
|
||||
|
||||
solver.addConstraint(c10);
|
||||
solver.addConstraint(c20);
|
||||
|
||||
assertEquals(10, x.value(), EPSILON);
|
||||
assertEquals(120, y.value(), EPSILON);
|
||||
|
||||
solver.removeConstraint(c10);
|
||||
assertEquals(20, x.value(), EPSILON);
|
||||
assertEquals(120, y.value(), EPSILON);
|
||||
|
||||
Constraint cxy = new Constraint(x.times(2.0), Constraint.Operator.EQ, y);
|
||||
solver.addConstraint(cxy);
|
||||
assertEquals(20, x.value(), EPSILON);
|
||||
assertEquals(40, y.value(), EPSILON);
|
||||
|
||||
solver.removeConstraint(c20);
|
||||
assertEquals(60, x.value(), EPSILON);
|
||||
assertEquals(120, y.value(), EPSILON);
|
||||
|
||||
solver.removeConstraint(cxy);
|
||||
assertEquals(100, x.value(), EPSILON);
|
||||
assertEquals(120, y.value(), EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void casso1() {
|
||||
Variable x = new Variable("x");
|
||||
Variable y = new Variable("y");
|
||||
Solver solver = new Solver();
|
||||
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.LEQ, y));
|
||||
solver.addConstraint(new Constraint(y, Constraint.Operator.EQ, x.plus(3.0)));
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.EQ, 10.0, Strength.WEAK));
|
||||
solver.addConstraint(new Constraint(y, Constraint.Operator.EQ, 10.0, Strength.WEAK));
|
||||
|
||||
if (Math.abs(x.getValue() - 10.0) < EPSILON) {
|
||||
assertEquals(10, x.value(), EPSILON);
|
||||
assertEquals(13, y.value(), EPSILON);
|
||||
} else {
|
||||
assertEquals(7, x.value(), EPSILON);
|
||||
assertEquals(10, y.value(), EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = RequiredFailure.class)
|
||||
public void inconsistent1() throws InternalError {
|
||||
Variable x = new Variable("x");
|
||||
Solver solver = new Solver();
|
||||
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.EQ, 10.0));
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.EQ, 5.0));
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = RequiredFailure.class)
|
||||
public void inconsistent2() {
|
||||
Variable x = new Variable("x");
|
||||
Solver solver = new Solver();
|
||||
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.GEQ, 10.0));
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.LEQ, 5.0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test(expected = RequiredFailure.class)
|
||||
public void inconsistent3() {
|
||||
|
||||
Variable w = new Variable("w");
|
||||
Variable x = new Variable("x");
|
||||
Variable y = new Variable("y");
|
||||
Variable z = new Variable("z");
|
||||
Solver solver = new Solver();
|
||||
|
||||
solver.addConstraint(new Constraint(w, Constraint.Operator.GEQ, 10.0));
|
||||
solver.addConstraint(new Constraint(x, Constraint.Operator.GEQ, w));
|
||||
solver.addConstraint(new Constraint(y, Constraint.Operator.GEQ, x));
|
||||
solver.addConstraint(new Constraint(z, Constraint.Operator.GEQ, y));
|
||||
solver.addConstraint(new Constraint(z, Constraint.Operator.GEQ, 8.0));
|
||||
solver.addConstraint(new Constraint(z, Constraint.Operator.LEQ, 4.0));
|
||||
}*/
|
||||
|
||||
}
|
Loading…
Reference in New Issue