completed the porting from c++ code, also fixed one or two errors that might be introduced during the original porting.
This commit is contained in:
parent
68fd7509f9
commit
c787873a2e
|
@ -1,5 +1,7 @@
|
||||||
package no.birkett.kiwi;
|
package no.birkett.kiwi;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by alex on 30/01/15.
|
* Created by alex on 30/01/15.
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +19,7 @@ public class Constraint {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Constraint(Expression expr, RelationalOperator op, double strength) {
|
public Constraint(Expression expr, RelationalOperator op, double strength) {
|
||||||
this.expression = expr;
|
this.expression = reduce(expr);
|
||||||
this.op = op;
|
this.op = op;
|
||||||
this.strength = Strength.clip(strength);
|
this.strength = Strength.clip(strength);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +28,26 @@ public class Constraint {
|
||||||
this(other.expression, other.op, strength);
|
this(other.expression, other.op, strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Expression reduce(Expression expr){
|
||||||
|
|
||||||
|
Map<Variable, Double> vars = new LinkedHashMap<>();
|
||||||
|
for(Term term: expr.getTerms()){
|
||||||
|
Double value = vars.get(term.getVariable());
|
||||||
|
if(value == null){
|
||||||
|
value = 0.0;
|
||||||
|
}
|
||||||
|
value += term.coefficient;
|
||||||
|
vars.put(term.getVariable(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Term> reducedTerms = new ArrayList<>();
|
||||||
|
for(Variable variable: vars.keySet()){
|
||||||
|
reducedTerms.add(new Term(variable, vars.get(variable)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Expression(reducedTerms, expr.getConstant());
|
||||||
|
}
|
||||||
|
|
||||||
public Expression getExpression() {
|
public Expression getExpression() {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package no.birkett.kiwi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by yongsun on 1/13/16.
|
||||||
|
*/
|
||||||
|
public class DuplicateEditVariableException extends Exception {
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package no.birkett.kiwi;
|
package no.birkett.kiwi;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ public class Row {
|
||||||
|
|
||||||
private double constant;
|
private double constant;
|
||||||
|
|
||||||
private Map<Symbol, Double> cells = new HashMap<Symbol, Double>();
|
private Map<Symbol, Double> cells = new LinkedHashMap<>();
|
||||||
|
|
||||||
public Row() {
|
public Row() {
|
||||||
this(0);
|
this(0);
|
||||||
|
@ -22,10 +23,20 @@ public class Row {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Row(Row other) {
|
public Row(Row other) {
|
||||||
this.cells = other.cells;
|
this.cells = new LinkedHashMap<>(other.cells);
|
||||||
this.constant = other.constant;
|
this.constant = other.constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Row deepCopy(){
|
||||||
|
Row result = new Row();
|
||||||
|
result.constant = this.constant;
|
||||||
|
result.cells = new LinkedHashMap<>();
|
||||||
|
for(Symbol s: this.cells.keySet()){
|
||||||
|
result.cells.put(s, this.cells.get(s));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public double getConstant() {
|
public double getConstant() {
|
||||||
return constant;
|
return constant;
|
||||||
}
|
}
|
||||||
|
@ -60,16 +71,28 @@ public class Row {
|
||||||
*/
|
*/
|
||||||
void insert(Symbol symbol, double coefficient) {
|
void insert(Symbol symbol, double coefficient) {
|
||||||
|
|
||||||
Double existingCoefficient = cells.get(symbol);
|
//this looks different than c++ code
|
||||||
|
// Double existingCoefficient = cells.get(symbol);
|
||||||
|
//
|
||||||
|
// if (existingCoefficient != null) {
|
||||||
|
// coefficient = existingCoefficient;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (Util.nearZero(coefficient)) {
|
||||||
|
// cells.remove(symbol);
|
||||||
|
// } else {
|
||||||
|
// cells.put(symbol, coefficient);
|
||||||
|
// }
|
||||||
|
|
||||||
if (existingCoefficient != null) {
|
//changes start here
|
||||||
coefficient = existingCoefficient;
|
Double value = this.cells.get(symbol);
|
||||||
|
if(value == null){
|
||||||
|
this.cells.put(symbol, 0.0);
|
||||||
}
|
}
|
||||||
|
double temp = this.cells.get(symbol) + coefficient;
|
||||||
if (Util.nearZero(coefficient)) {
|
this.cells.put(symbol, temp);
|
||||||
cells.remove(symbol);
|
if(Util.nearZero(temp)){
|
||||||
} else {
|
this.cells.remove(symbol);
|
||||||
cells.put(symbol, Double.valueOf(coefficient));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,11 +119,21 @@ public class Row {
|
||||||
void insert(Row other, double coefficient) {
|
void insert(Row other, double coefficient) {
|
||||||
this.constant += other.constant * coefficient;
|
this.constant += other.constant * coefficient;
|
||||||
|
|
||||||
Set<Map.Entry<Symbol, Double>> map = other.getCells().entrySet();
|
for(Symbol s: other.cells.keySet()){
|
||||||
|
double coeff = other.cells.get(s) * coefficient;
|
||||||
|
|
||||||
for (Map.Entry<Symbol, Double> entry : map) {
|
//insert(s, coeff); this line looks different than the c++
|
||||||
double coeff = entry.getValue() * coefficient;
|
|
||||||
insert(entry.getKey(), coeff);
|
//changes start here
|
||||||
|
Double value = this.cells.get(s);
|
||||||
|
if(value == null){
|
||||||
|
this.cells.put(s, 0.0);
|
||||||
|
}
|
||||||
|
double temp = this.cells.get(s) + coeff;
|
||||||
|
this.cells.put(s, temp);
|
||||||
|
if(Util.nearZero(temp)){
|
||||||
|
this.cells.remove(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +146,7 @@ public class Row {
|
||||||
* @param other
|
* @param other
|
||||||
*/
|
*/
|
||||||
void insert(Row other) {
|
void insert(Row other) {
|
||||||
insert(other, 0);
|
insert(other, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,11 +167,12 @@ public class Row {
|
||||||
void reverseSign() {
|
void reverseSign() {
|
||||||
this.constant = -this.constant;
|
this.constant = -this.constant;
|
||||||
|
|
||||||
Set<Map.Entry<Symbol, Double>> map = getCells().entrySet();
|
Map<Symbol, Double> newCells = new LinkedHashMap<>();
|
||||||
|
for(Symbol s: cells.keySet()){
|
||||||
for (Map.Entry<Symbol, Double> entry : map) {
|
double value = - cells.get(s);
|
||||||
entry.setValue(-entry.getValue());
|
newCells.put(s, value);
|
||||||
}
|
}
|
||||||
|
this.cells = newCells;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,11 +192,12 @@ public class Row {
|
||||||
cells.remove(symbol);
|
cells.remove(symbol);
|
||||||
this.constant *= coeff;
|
this.constant *= coeff;
|
||||||
|
|
||||||
Set<Map.Entry<Symbol, Double>> map = getCells().entrySet();
|
HashMap<Symbol, Double> newCells = new LinkedHashMap<>();
|
||||||
|
for(Symbol s: cells.keySet()){
|
||||||
for (Map.Entry<Symbol, Double> entry : map) {
|
double value = cells.get(s) * coeff;
|
||||||
entry.setValue(entry.getValue() * coeff);
|
newCells.put(s, value);
|
||||||
}
|
}
|
||||||
|
this.cells = newCells;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package no.birkett.kiwi;
|
package no.birkett.kiwi;
|
||||||
|
|
||||||
|
|
||||||
|
import com.oracle.tools.packager.Log;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,23 +13,29 @@ public class Solver {
|
||||||
private static class Tag {
|
private static class Tag {
|
||||||
Symbol marker;
|
Symbol marker;
|
||||||
Symbol other;
|
Symbol other;
|
||||||
|
|
||||||
|
public Tag(){
|
||||||
|
marker = new Symbol();
|
||||||
|
other = new Symbol();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class EditInfo {
|
private static class EditInfo {
|
||||||
Tag tag;
|
Tag tag;
|
||||||
Constraint constraint;
|
Constraint constraint;
|
||||||
double constant;
|
double constant;
|
||||||
|
|
||||||
|
public EditInfo(Constraint constraint, Tag tag, double constant){
|
||||||
|
this.constraint = constraint;
|
||||||
|
this.tag = tag;
|
||||||
|
this.constant = constant;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RowAndTag {
|
private Map<Constraint, Tag> cns = new LinkedHashMap<Constraint, Tag>();
|
||||||
Tag tag;
|
private Map<Symbol, Row> rows = new LinkedHashMap<Symbol, Row>();
|
||||||
Row row;
|
private Map<Variable, Symbol> vars = new LinkedHashMap<Variable, Symbol>();
|
||||||
}
|
private Map<Variable, EditInfo> edits = new LinkedHashMap<Variable, EditInfo>();
|
||||||
|
|
||||||
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 List<Symbol> infeasibleRows = new ArrayList<Symbol>();
|
||||||
private Row objective = new Row();
|
private Row objective = new Row();
|
||||||
private Row artificial;
|
private Row artificial;
|
||||||
|
@ -47,50 +55,219 @@ public class Solver {
|
||||||
throw new DuplicateConstraintException(constraint);
|
throw new DuplicateConstraintException(constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creating a row causes symbols to reserved for the variables
|
Tag tag = new Tag();
|
||||||
// in the constraint. If this method exits with an exception,
|
Row row = createRow(constraint, tag);
|
||||||
// then its possible those variables will linger in the var map.
|
Symbol subject = chooseSubject(row, tag);
|
||||||
// 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);
|
if(subject.getType() == Symbol.Type.INVALID && allDummies(row)){
|
||||||
|
if (!Util.nearZero(row.getConstant())) {
|
||||||
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);
|
throw new UnsatisfiableConstraintException(constraint);
|
||||||
} else {
|
} else {
|
||||||
subject = rowAndTag.tag.marker;
|
subject = 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 (subject.getType() == Symbol.Type.INVALID) {
|
||||||
if (!addWithArtificialVariable(rowAndTag.row)) {
|
if (!addWithArtificialVariable(row)) {
|
||||||
throw new UnsatisfiableConstraintException(constraint);
|
throw new UnsatisfiableConstraintException(constraint);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rowAndTag.row.solveFor(subject);
|
row.solveFor(subject);
|
||||||
substitute(subject, rowAndTag.row);
|
substitute(subject, row);
|
||||||
this.rows.put(subject, rowAndTag.row);
|
this.rows.put(subject, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cns.put(constraint, rowAndTag.tag);
|
this.cns.put(constraint, tag);
|
||||||
|
|
||||||
// Optimizing after each constraint is added performs less
|
optimize(objective);
|
||||||
// aggregate work due to a smaller average system size. It
|
}
|
||||||
// also ensures the solver remains in a consistent state.
|
|
||||||
optimize(this.objective);
|
void removeConstraint(Constraint constraint) throws UnknownConstraintException, InternalSolverError{
|
||||||
|
Tag tag = cns.get(constraint);
|
||||||
|
if(tag == null){
|
||||||
|
throw new UnknownConstraintException(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
cns.remove(constraint);
|
||||||
|
removeConstraintEffects(constraint, tag);
|
||||||
|
|
||||||
|
Row row = rows.get(tag.marker);
|
||||||
|
if(row != null){
|
||||||
|
rows.remove(tag.marker);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
row = getMarkerLeavingRow(tag.marker);
|
||||||
|
if(row == null){
|
||||||
|
throw new InternalSolverError("internal solver error");
|
||||||
|
}
|
||||||
|
|
||||||
|
//This looks wrong! changes made below
|
||||||
|
//Symbol leaving = tag.marker;
|
||||||
|
//rows.remove(tag.marker);
|
||||||
|
|
||||||
|
Symbol leaving = null;
|
||||||
|
for(Symbol s: rows.keySet()){
|
||||||
|
if(rows.get(s) == row){
|
||||||
|
leaving = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(leaving == null){
|
||||||
|
throw new InternalSolverError("internal solver error");
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.remove(leaving);
|
||||||
|
row.solveFor(leaving, tag.marker);
|
||||||
|
substitute(tag.marker, row);
|
||||||
|
}
|
||||||
|
optimize(objective);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeConstraintEffects(Constraint constraint, Tag tag){
|
||||||
|
if(tag.marker.getType() == Symbol.Type.ERROR){
|
||||||
|
removeMarkerEffects(tag.marker, constraint.getStrength());
|
||||||
|
}
|
||||||
|
else if(tag.other.getType() == Symbol.Type.ERROR){
|
||||||
|
removeMarkerEffects(tag.other, constraint.getStrength());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeMarkerEffects(Symbol marker, double strength){
|
||||||
|
Row row = rows.get(marker);
|
||||||
|
if(row != null){
|
||||||
|
objective.insert(row, -strength);
|
||||||
|
}else {
|
||||||
|
objective.insert(marker, -strength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row getMarkerLeavingRow(Symbol marker){
|
||||||
|
double dmax = Double.MAX_VALUE;
|
||||||
|
double r1 = dmax;
|
||||||
|
double r2 = dmax;
|
||||||
|
|
||||||
|
Row first = null;
|
||||||
|
Row second = null;
|
||||||
|
Row third = null;
|
||||||
|
|
||||||
|
for(Symbol s: rows.keySet()){
|
||||||
|
Row candidateRow = rows.get(s);
|
||||||
|
double c = candidateRow.coefficientFor(marker);
|
||||||
|
if(c == 0.0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(s.getType() == Symbol.Type.EXTERNAL){
|
||||||
|
third = candidateRow;
|
||||||
|
}
|
||||||
|
else if(c < 0.0){
|
||||||
|
double r = - candidateRow.getConstant() / c;
|
||||||
|
if(r < r1){
|
||||||
|
r1 = r;
|
||||||
|
first = candidateRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
double r = candidateRow.getConstant() / c;
|
||||||
|
if(r < r2){
|
||||||
|
r2 = r;
|
||||||
|
second = candidateRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(first != null){
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
if(second != null){
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
return third;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasConstraint(Constraint constraint){
|
||||||
|
return cns.containsKey(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEditVariable(Variable variable, double strength) throws DuplicateEditVariableException, RequiredFailureException{
|
||||||
|
if(edits.containsKey(variable)){
|
||||||
|
throw new DuplicateEditVariableException();
|
||||||
|
}
|
||||||
|
|
||||||
|
strength = Strength.clip(strength);
|
||||||
|
|
||||||
|
if(strength == Strength.REQUIRED){
|
||||||
|
throw new RequiredFailureException();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Term> terms = new ArrayList<>();
|
||||||
|
terms.add(new Term(variable));
|
||||||
|
Constraint constraint = new Constraint(new Expression(terms), RelationalOperator.OP_EQ, strength);
|
||||||
|
|
||||||
|
try {
|
||||||
|
addConstraint(constraint);
|
||||||
|
} catch (DuplicateConstraintException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (UnsatisfiableConstraintException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EditInfo info = new EditInfo(constraint, cns.get(constraint), 0.0);
|
||||||
|
edits.put(variable, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEditVariable(Variable variable) throws UnknownEditVariableException{
|
||||||
|
EditInfo edit = edits.get(variable);
|
||||||
|
if(edit == null){
|
||||||
|
throw new UnknownEditVariableException();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
removeConstraint(edit.constraint);
|
||||||
|
} catch (UnknownConstraintException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
edits.remove(variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEditVariable(Variable variable){
|
||||||
|
return edits.containsKey(variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suggestValue(Variable variable, double value) throws UnknownEditVariableException{
|
||||||
|
EditInfo info = edits.get(variable);
|
||||||
|
if(info == null){
|
||||||
|
throw new UnknownEditVariableException();
|
||||||
|
}
|
||||||
|
|
||||||
|
double delta = value - info.constant;
|
||||||
|
info.constant = value;
|
||||||
|
|
||||||
|
Row row = rows.get(info.tag.marker);
|
||||||
|
if(row != null){
|
||||||
|
if(row.add(-delta) < 0.0){
|
||||||
|
infeasibleRows.add(info.tag.marker);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = rows.get(info.tag.other);
|
||||||
|
if(row != null){
|
||||||
|
if(row.add(delta) < 0.0){
|
||||||
|
infeasibleRows.add(info.tag.other);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Symbol s: rows.keySet()){
|
||||||
|
Row currentRow = rows.get(s);
|
||||||
|
double coefficient = currentRow.coefficientFor(info.tag.marker);
|
||||||
|
if(coefficient != 0.0 && currentRow.add(delta * coefficient) < 0.0 && s.getType() != Symbol.Type.EXTERNAL){
|
||||||
|
infeasibleRows.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dualOptimize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,14 +305,11 @@ public class Solver {
|
||||||
* The tag will be updated with the marker and error symbols to use
|
* The tag will be updated with the marker and error symbols to use
|
||||||
* for tracking the movement of the constraint in the tableau.
|
* for tracking the movement of the constraint in the tableau.
|
||||||
*/
|
*/
|
||||||
RowAndTag createRow(Constraint constraint) {
|
Row createRow(Constraint constraint, Tag tag) {
|
||||||
|
Expression expression = constraint.getExpression();
|
||||||
|
Row row = new Row(expression.getConstant());
|
||||||
|
|
||||||
Expression expr = constraint.getExpression();
|
for (Term term : expression.getTerms()) {
|
||||||
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())) {
|
if (!Util.nearZero(term.getCoefficient())) {
|
||||||
Symbol symbol = getVarSymbol(term.getVariable());
|
Symbol symbol = getVarSymbol(term.getVariable());
|
||||||
|
|
||||||
|
@ -149,7 +323,6 @@ public class Solver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the necessary slack, error, and dummy variables.
|
|
||||||
switch (constraint.getOp()) {
|
switch (constraint.getOp()) {
|
||||||
case OP_LE:
|
case OP_LE:
|
||||||
case OP_GE: {
|
case OP_GE: {
|
||||||
|
@ -189,12 +362,7 @@ public class Solver {
|
||||||
row.reverseSign();
|
row.reverseSign();
|
||||||
}
|
}
|
||||||
|
|
||||||
RowAndTag rowAndTag = new RowAndTag();
|
return row;
|
||||||
rowAndTag.row = row;
|
|
||||||
rowAndTag.tag = tag;
|
|
||||||
|
|
||||||
|
|
||||||
return rowAndTag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,9 +405,9 @@ public class Solver {
|
||||||
// Create and add the artificial variable to the tableau
|
// Create and add the artificial variable to the tableau
|
||||||
|
|
||||||
Symbol art = new Symbol(Symbol.Type.SLACK, idTick++);
|
Symbol art = new Symbol(Symbol.Type.SLACK, idTick++);
|
||||||
rows.put(art, new Row(row));
|
rows.put(art, row.deepCopy());
|
||||||
|
|
||||||
this.artificial = new Row(row);
|
this.artificial = row.deepCopy();
|
||||||
|
|
||||||
// Optimize the artificial objective. This is successful
|
// Optimize the artificial objective. This is successful
|
||||||
// only if the artificial objective is optimized to zero.
|
// only if the artificial objective is optimized to zero.
|
||||||
|
@ -253,10 +421,25 @@ public class Solver {
|
||||||
Row rowptr = this.rows.get(art);
|
Row rowptr = this.rows.get(art);
|
||||||
|
|
||||||
if (rowptr != null) {
|
if (rowptr != null) {
|
||||||
rows.remove(rowptr);
|
|
||||||
|
/**this looks wrong!!!*/
|
||||||
|
//rows.remove(rowptr);
|
||||||
|
|
||||||
|
LinkedList<Symbol> deleteQueue = new LinkedList<>();
|
||||||
|
for(Symbol s: rows.keySet()){
|
||||||
|
if(rows.get(s) == rowptr){
|
||||||
|
deleteQueue.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(!deleteQueue.isEmpty()){
|
||||||
|
rows.remove(deleteQueue.pop());
|
||||||
|
}
|
||||||
|
deleteQueue.clear();
|
||||||
|
|
||||||
if (rowptr.getCells().isEmpty()) {
|
if (rowptr.getCells().isEmpty()) {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol entering = anyPivotableSymbol(rowptr);
|
Symbol entering = anyPivotableSymbol(rowptr);
|
||||||
if (entering.getType() == Symbol.Type.INVALID) {
|
if (entering.getType() == Symbol.Type.INVALID) {
|
||||||
return false; // unsatisfiable (will this ever happen?)
|
return false; // unsatisfiable (will this ever happen?)
|
||||||
|
@ -312,23 +495,46 @@ public class Solver {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map.Entry<Symbol, Row> entry = getLeavingRow(entering);
|
Row entry = getLeavingRow(entering);
|
||||||
|
if(entry == null){
|
||||||
|
throw new InternalSolverError("The objective is unbounded.");
|
||||||
|
}
|
||||||
|
Symbol leaving = null;
|
||||||
|
|
||||||
if (entry == null) {
|
for(Symbol key: rows.keySet()){
|
||||||
throw new InternalSolverError("The objective is unbounded.");
|
if(rows.get(key) == entry){
|
||||||
|
leaving = key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pivot the entering symbol into the basis
|
Symbol entryKey = null;
|
||||||
Symbol leaving = entry.getKey();
|
for(Symbol key: rows.keySet()){
|
||||||
Row row = entry.getValue();
|
if(rows.get(key) == entry){
|
||||||
|
entryKey = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.rows.remove(entry.getKey());
|
rows.remove(entryKey);
|
||||||
|
entry.solveFor(leaving, entering);
|
||||||
|
substitute(entering, entry);
|
||||||
|
rows.put(entering, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
row.solveFor(leaving, entering);
|
void dualOptimize() throws InternalSolverError{
|
||||||
|
while(!infeasibleRows.isEmpty()){
|
||||||
substitute(entering, row);
|
Symbol leaving = infeasibleRows.remove(infeasibleRows.size() - 1);
|
||||||
|
Row row = rows.get(leaving);
|
||||||
this.rows.put(entering, row);
|
if(row != null && row.getConstant() < 0.0){
|
||||||
|
Symbol entering = getDualEnteringSymbol(row);
|
||||||
|
if(entering.getType() == Symbol.Type.INVALID){
|
||||||
|
throw new InternalSolverError("internal solver error");
|
||||||
|
}
|
||||||
|
rows.remove(entering);
|
||||||
|
row.solveFor(leaving, entering);
|
||||||
|
substitute(entering, row);
|
||||||
|
rows.put(entering, row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,6 +559,26 @@ public class Solver {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Symbol getDualEnteringSymbol(Row row){
|
||||||
|
Symbol entering = new Symbol();
|
||||||
|
double ratio = Double.MAX_VALUE;
|
||||||
|
for(Symbol s: row.getCells().keySet()){
|
||||||
|
if(s.getType() != Symbol.Type.DUMMY){
|
||||||
|
double currentCell = row.getCells().get(s);
|
||||||
|
if(currentCell > 0.0){
|
||||||
|
double coefficient = objective.coefficientFor(s);
|
||||||
|
double r = coefficient / currentCell;
|
||||||
|
if(r < ratio){
|
||||||
|
ratio = r;
|
||||||
|
entering = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entering;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first Slack or Error symbol in the row.
|
* Get the first Slack or Error symbol in the row.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -360,7 +586,7 @@ public class Solver {
|
||||||
*/
|
*/
|
||||||
private Symbol anyPivotableSymbol(Row row) {
|
private Symbol anyPivotableSymbol(Row row) {
|
||||||
Symbol symbol = null;
|
Symbol symbol = null;
|
||||||
for (Map.Entry<Symbol, Double> entry : objective.getCells().entrySet()) {
|
for (Map.Entry<Symbol, Double> entry : row.getCells().entrySet()) {
|
||||||
if (entry.getKey().getType() == Symbol.Type.SLACK || entry.getKey().getType() == Symbol.Type.ERROR) {
|
if (entry.getKey().getType() == Symbol.Type.SLACK || entry.getKey().getType() == Symbol.Type.ERROR) {
|
||||||
symbol = entry.getKey();
|
symbol = entry.getKey();
|
||||||
}
|
}
|
||||||
|
@ -382,23 +608,24 @@ public class Solver {
|
||||||
* found, the end() iterator will be returned. This indicates that
|
* found, the end() iterator will be returned. This indicates that
|
||||||
* the objective function is unbounded.
|
* the objective function is unbounded.
|
||||||
*/
|
*/
|
||||||
private Map.Entry<Symbol, Row> getLeavingRow(Symbol entering) {
|
private Row getLeavingRow(Symbol entering) {
|
||||||
// TODO check
|
|
||||||
double ratio = Double.MAX_VALUE;
|
double ratio = Double.MAX_VALUE;
|
||||||
Map.Entry<Symbol, Row> found = null;
|
Row row = null;
|
||||||
for (Map.Entry<Symbol, Row> row : rows.entrySet()) {
|
|
||||||
if (row.getKey().getType() != Symbol.Type.EXTERNAL) {
|
for(Symbol key: rows.keySet()){
|
||||||
double temp = row.getValue().coefficientFor(entering);
|
if(key.getType() != Symbol.Type.EXTERNAL){
|
||||||
if (temp < 0.0) {
|
Row candidateRow = rows.get(key);
|
||||||
double temp_ratio = -row.getValue().getConstant() / temp;
|
double temp = candidateRow.coefficientFor(entering);
|
||||||
if (temp_ratio < ratio) {
|
if(temp < 0){
|
||||||
|
double temp_ratio = (-candidateRow.getConstant() / temp);
|
||||||
|
if(temp_ratio < ratio){
|
||||||
ratio = temp_ratio;
|
ratio = temp_ratio;
|
||||||
found = row;
|
row = candidateRow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return found;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -412,6 +639,7 @@ public class Solver {
|
||||||
symbol = vars.get(variable);
|
symbol = vars.get(variable);
|
||||||
} else {
|
} else {
|
||||||
symbol = new Symbol(Symbol.Type.EXTERNAL, idTick++);
|
symbol = new Symbol(Symbol.Type.EXTERNAL, idTick++);
|
||||||
|
symbol.setVariableName(variable.getName());
|
||||||
vars.put(variable, symbol);
|
vars.put(variable, symbol);
|
||||||
}
|
}
|
||||||
return symbol;
|
return symbol;
|
||||||
|
|
|
@ -15,6 +15,7 @@ public class Symbol {
|
||||||
|
|
||||||
private Type type;
|
private Type type;
|
||||||
private long id;
|
private long id;
|
||||||
|
private String variableName;
|
||||||
|
|
||||||
public Symbol() {
|
public Symbol() {
|
||||||
this(Type.INVALID, 0);
|
this(Type.INVALID, 0);
|
||||||
|
@ -41,6 +42,14 @@ public class Symbol {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getVariableName() {
|
||||||
|
return variableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVariableName(String variableName) {
|
||||||
|
this.variableName = variableName;
|
||||||
|
}
|
||||||
|
|
||||||
boolean lessThan(Symbol other) {
|
boolean lessThan(Symbol other) {
|
||||||
return this.id < other.getId();
|
return this.id < other.getId();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class Symbolics {
|
||||||
public static Expression multiply(Expression expression, double coefficient) {
|
public static Expression multiply(Expression expression, double coefficient) {
|
||||||
|
|
||||||
List<Term> terms = new ArrayList<Term>();
|
List<Term> terms = new ArrayList<Term>();
|
||||||
|
|
||||||
for (Term term : expression.getTerms()) {
|
for (Term term : expression.getTerms()) {
|
||||||
terms.add(multiply(term, coefficient));
|
terms.add(multiply(term, coefficient));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package no.birkett.kiwi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by yongsun on 1/13/16.
|
||||||
|
*/
|
||||||
|
public class UnknownConstraintException extends Exception {
|
||||||
|
|
||||||
|
public UnknownConstraintException(Constraint constraint){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package no.birkett.kiwi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by yongsun on 1/13/16.
|
||||||
|
*/
|
||||||
|
public class UnknownEditVariableException extends Exception {
|
||||||
|
}
|
|
@ -24,4 +24,7 @@ public class Variable {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package no.birkett.kiwi;
|
package no.birkett.kiwi;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Stack;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -31,8 +29,7 @@ public class ConstraintParser {
|
||||||
Expression expression = resolveExpression(matcher.group(3), variableResolver);
|
Expression expression = resolveExpression(matcher.group(3), variableResolver);
|
||||||
double strength = parseStrength(matcher.group(4));
|
double strength = parseStrength(matcher.group(4));
|
||||||
|
|
||||||
return new Constraint(Symbolics.subtract(variable, expression), operator);
|
return new Constraint(Symbolics.subtract(variable, expression), operator, strength);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("could not parse " + constraintString);
|
throw new RuntimeException("could not parse " + constraintString);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,7 @@ package no.birkett.kiwi;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@ -35,100 +33,121 @@ public class RealWorldTests {
|
||||||
|
|
||||||
private static final String[] CONSTRAINTS = {
|
private static final String[] CONSTRAINTS = {
|
||||||
|
|
||||||
"container.columnWidth == container.width * 0.4",
|
// "container.width == 500",
|
||||||
"container.thumbHeight == container.columnWidth / 2",
|
// "container.height == 500",
|
||||||
"container.padding == container.width * (0.2 / 3)",
|
// "container.left == 0",
|
||||||
|
// "container.top == 0",
|
||||||
|
//
|
||||||
|
// "view.left == container.left + 100",
|
||||||
|
// "view.top == container.top + 100",
|
||||||
|
// "view.bottom == container.bottom - 100",
|
||||||
|
// "view.right == container.right - 100",
|
||||||
|
//
|
||||||
|
// "view2.left == view.left",
|
||||||
|
// "view2.right <= view.right",
|
||||||
|
// "view2.width == 250 !weak"
|
||||||
|
|
||||||
|
"container.columnWidth == container.width * 0.4 + 30",
|
||||||
|
"container.thumbHeight == container.columnWidth / 2 - 25",
|
||||||
|
"container.padding == container.width * 0.1",
|
||||||
|
|
||||||
"container.leftPadding == container.padding",
|
"container.leftPadding == container.padding",
|
||||||
"container.rightPadding == container.width - container.padding",
|
|
||||||
|
"container.rightPadding == container.width * 1.5 - 30",
|
||||||
|
|
||||||
"container.paddingUnderThumb == 5",
|
"container.paddingUnderThumb == 5",
|
||||||
"container.rowPadding == 15",
|
"container.rowPadding == 15",
|
||||||
"container.buttonPadding == 20",
|
"container.buttonPadding == 20",
|
||||||
|
"container.width == 200 !medium"
|
||||||
|
|
||||||
"thumb0.left == container.leftPadding",
|
|
||||||
"thumb0.top == container.padding",
|
|
||||||
"thumb0.height == container.thumbHeight",
|
|
||||||
"thumb0.width == container.columnWidth",
|
|
||||||
|
|
||||||
"title0.left == container.leftPadding",
|
|
||||||
"title0.top == thumb0.bottom + container.paddingUnderThumb",
|
|
||||||
"title0.height == title0.intrinsicHeight",
|
|
||||||
"title0.width == container.columnWidth",
|
|
||||||
|
|
||||||
"thumb1.right == container.rightPadding",
|
// "thumb0.left == container.leftPadding",
|
||||||
"thumb1.top == container.padding",
|
// "thumb0.top == container.padding",
|
||||||
"thumb1.height == container.thumbHeight",
|
// "thumb0.height == container.thumbHeight",
|
||||||
"thumb1.width == container.columnWidth",
|
// "thumb0.width == container.columnWidth",
|
||||||
|
//
|
||||||
"title1.right == container.rightPadding",
|
// "title0.left == container.leftPadding",
|
||||||
"title1.top == thumb0.bottom + container.paddingUnderThumb",
|
// "title0.top == thumb0.bottom + container.paddingUnderThumb",
|
||||||
"title1.height == title1.intrinsicHeight",
|
// "title0.height == title0.intrinsicHeight",
|
||||||
"title1.width == container.columnWidth",
|
// "title0.width == container.columnWidth",
|
||||||
|
//
|
||||||
"thumb2.left == container.leftPadding",
|
// "thumb1.right == container.rightPadding",
|
||||||
"thumb2.top >= title0.bottom + container.rowPadding",
|
// "thumb1.top == container.padding",
|
||||||
"thumb2.top == title0.bottom + container.rowPadding !weak",
|
// "thumb1.height == container.thumbHeight",
|
||||||
"thumb2.top >= title1.bottom + container.rowPadding",
|
// "thumb1.width == container.columnWidth",
|
||||||
"thumb2.top == title1.bottom + container.rowPadding !weak",
|
//
|
||||||
"thumb2.height == container.thumbHeight",
|
// "title1.right == container.rightPadding",
|
||||||
"thumb2.width == container.columnWidth",
|
// "title1.top == thumb0.bottom + container.paddingUnderThumb",
|
||||||
|
// "title1.height == title1.intrinsicHeight",
|
||||||
"title2.left == container.leftPadding",
|
// "title1.width == container.columnWidth",
|
||||||
"title2.top == thumb2.bottom + container.paddingUnderThumb",
|
//
|
||||||
"title2.height == title2.intrinsicHeight",
|
// "thumb2.left == container.leftPadding",
|
||||||
"title2.width == container.columnWidth",
|
// "thumb2.top >= title0.bottom + container.rowPadding",
|
||||||
|
// "thumb2.top == title0.bottom + container.rowPadding !weak",
|
||||||
"thumb3.right == container.rightPadding",
|
// "thumb2.top >= title1.bottom + container.rowPadding",
|
||||||
"thumb3.top == thumb2.top",
|
// "thumb2.top == title1.bottom + container.rowPadding !weak",
|
||||||
|
// "thumb2.height == container.thumbHeight",
|
||||||
"thumb3.height == container.thumbHeight",
|
// "thumb2.width == container.columnWidth",
|
||||||
"thumb3.width == container.columnWidth",
|
//
|
||||||
|
// "title2.left == container.leftPadding",
|
||||||
"title3.right == container.rightPadding",
|
// "title2.top == thumb2.bottom + container.paddingUnderThumb",
|
||||||
"title3.top == thumb3.bottom + container.paddingUnderThumb",
|
// "title2.height == title2.intrinsicHeight",
|
||||||
"title3.height == title3.intrinsicHeight",
|
// "title2.width == container.columnWidth",
|
||||||
"title3.width == container.columnWidth",
|
//
|
||||||
|
// "thumb3.right == container.rightPadding",
|
||||||
"thumb4.left == container.leftPadding",
|
// "thumb3.top == thumb2.top",
|
||||||
"thumb4.top >= title2.bottom + container.rowPadding",
|
//
|
||||||
"thumb4.top >= title3.bottom + container.rowPadding",
|
// "thumb3.height == container.thumbHeight",
|
||||||
"thumb4.top == title2.bottom + container.rowPadding !weak",
|
// "thumb3.width == container.columnWidth",
|
||||||
"thumb4.top == title3.bottom + container.rowPadding !weak",
|
//
|
||||||
"thumb4.height == container.thumbHeight",
|
// "title3.right == container.rightPadding",
|
||||||
"thumb4.width == container.columnWidth",
|
// "title3.top == thumb3.bottom + container.paddingUnderThumb",
|
||||||
|
// "title3.height == title3.intrinsicHeight",
|
||||||
"title4.left == container.leftPadding",
|
// "title3.width == container.columnWidth",
|
||||||
"title4.top == thumb4.bottom + container.paddingUnderThumb",
|
//
|
||||||
"title4.height == title4.intrinsicHeight",
|
// "thumb4.left == container.leftPadding",
|
||||||
"title4.width == container.columnWidth",
|
// "thumb4.top >= title2.bottom + container.rowPadding",
|
||||||
|
// "thumb4.top >= title3.bottom + container.rowPadding",
|
||||||
"thumb5.right == container.rightPadding",
|
// "thumb4.top == title2.bottom + container.rowPadding !weak",
|
||||||
"thumb5.top == thumb4.top",
|
// "thumb4.top == title3.bottom + container.rowPadding !weak",
|
||||||
"thumb5.height == container.thumbHeight",
|
// "thumb4.height == container.thumbHeight",
|
||||||
"thumb5.width == container.columnWidth",
|
// "thumb4.width == container.columnWidth",
|
||||||
|
//
|
||||||
"title5.right == container.rightPadding",
|
// "title4.left == container.leftPadding",
|
||||||
"title5.top == thumb5.bottom + container.paddingUnderThumb",
|
// "title4.top == thumb4.bottom + container.paddingUnderThumb",
|
||||||
"title5.height == title5.intrinsicHeight",
|
// "title4.height == title4.intrinsicHeight",
|
||||||
"title5.width == container.columnWidth",
|
// "title4.width == container.columnWidth",
|
||||||
|
//
|
||||||
"line.height == 1",
|
// "thumb5.right == container.rightPadding",
|
||||||
"line.width == container.width",
|
// "thumb5.top == thumb4.top",
|
||||||
"line.top >= title4.bottom + container.rowPadding",
|
// "thumb5.height == container.thumbHeight",
|
||||||
"line.top >= title5.bottom + container.rowPadding",
|
// "thumb5.width == container.columnWidth",
|
||||||
|
//
|
||||||
"more.top == line.bottom + container.buttonPadding",
|
// "title5.right == container.rightPadding",
|
||||||
"more.height == more.intrinsicHeight",
|
// "title5.top == thumb5.bottom + container.paddingUnderThumb",
|
||||||
"more.left == container.leftPadding",
|
// "title5.height == title5.intrinsicHeight",
|
||||||
"more.right == container.rightPadding",
|
// "title5.width == container.columnWidth",
|
||||||
|
//
|
||||||
"container.height == more.bottom + container.buttonPadding"};
|
// "line.height == 1",
|
||||||
|
// "line.width == container.width",
|
||||||
|
// "line.top >= title4.bottom + container.rowPadding",
|
||||||
|
// "line.top >= title5.bottom + container.rowPadding",
|
||||||
|
//
|
||||||
|
// "more.top == line.bottom + container.buttonPadding",
|
||||||
|
// "more.height == more.intrinsicHeight",
|
||||||
|
// "more.left == container.leftPadding",
|
||||||
|
// "more.right == container.rightPadding",
|
||||||
|
//
|
||||||
|
// "container.height == more.bottom + container.buttonPadding"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
public ConstraintParser.CassowaryVariableResolver createVariableResolver(final Solver solver, final HashMap<String, HashMap<String, Variable>> nodeHashMap) {
|
public ConstraintParser.CassowaryVariableResolver createVariableResolver(final Solver solver, final HashMap<String, HashMap<String, Variable>> nodeHashMap) {
|
||||||
ConstraintParser.CassowaryVariableResolver variableResolver = new ConstraintParser.CassowaryVariableResolver() {
|
ConstraintParser.CassowaryVariableResolver variableResolver = new ConstraintParser.CassowaryVariableResolver() {
|
||||||
|
|
||||||
private Variable getVariableFromNode(HashMap<String, Variable> node, String variableName) {
|
private Variable getVariableFromNode(HashMap<String, Variable> node, String variableName) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (node.containsKey(variableName)) {
|
if (node.containsKey(variableName)) {
|
||||||
return node.get(variableName);
|
return node.get(variableName);
|
||||||
|
@ -147,9 +166,9 @@ public class RealWorldTests {
|
||||||
return variable;
|
return variable;
|
||||||
}
|
}
|
||||||
} catch(DuplicateConstraintException e) {
|
} catch(DuplicateConstraintException e) {
|
||||||
|
e.printStackTrace();
|
||||||
} catch (UnsatisfiableConstraintException e) {
|
} catch (UnsatisfiableConstraintException e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -196,48 +215,104 @@ public class RealWorldTests {
|
||||||
return variableResolver;
|
return variableResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimple() {
|
||||||
|
int a = 20;
|
||||||
|
assertEquals(20, a);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGridLayout() throws DuplicateConstraintException, UnsatisfiableConstraintException {
|
public void testGridLayout() throws DuplicateConstraintException, UnsatisfiableConstraintException {
|
||||||
|
|
||||||
final Solver solver = new Solver();
|
final Solver solver = new Solver();
|
||||||
|
final HashMap<String, HashMap<String, Variable>> nodeHashMap = new HashMap<>();
|
||||||
final HashMap<String, HashMap<String, Variable>> nodeHashMap = new HashMap<String, HashMap<String, Variable>>();
|
|
||||||
|
|
||||||
ConstraintParser.CassowaryVariableResolver variableResolver = createVariableResolver(solver, nodeHashMap);
|
ConstraintParser.CassowaryVariableResolver variableResolver = createVariableResolver(solver, nodeHashMap);
|
||||||
|
|
||||||
for (String constraint : CONSTRAINTS) {
|
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint(constraint, variableResolver));
|
|
||||||
}
|
|
||||||
|
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("container.width == 300", variableResolver));
|
List<Constraint> cache = new ArrayList<>();
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("title0.intrinsicHeight == 100", variableResolver));
|
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("title1.intrinsicHeight == 110", variableResolver));
|
for (String constraint : CONSTRAINTS) {
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("title2.intrinsicHeight == 120", variableResolver));
|
Constraint con = ConstraintParser.parseConstraint(constraint, variableResolver);
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("title3.intrinsicHeight == 130", variableResolver));
|
solver.addConstraint(con);
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("title4.intrinsicHeight == 140", variableResolver));
|
cache.add(con);
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("title5.intrinsicHeight == 150", variableResolver));
|
}
|
||||||
solver.addConstraint(ConstraintParser.parseConstraint("more.intrinsicHeight == 160", variableResolver));
|
|
||||||
|
|
||||||
solver.updateVariables();
|
solver.updateVariables();
|
||||||
|
|
||||||
assertEquals(20, nodeHashMap.get("thumb0").get("top").getValue(), EPSILON);
|
// assertEquals(300, nodeHashMap.get("container").get("width").getValue(), EPSILON);
|
||||||
assertEquals(20, nodeHashMap.get("thumb1").get("top").getValue(), EPSILON);
|
// assertEquals(150, nodeHashMap.get("container").get("columnWidth").getValue(), EPSILON);
|
||||||
|
// assertEquals(50, nodeHashMap.get("container").get("thumbHeight").getValue(), EPSILON);
|
||||||
|
// assertEquals(30, nodeHashMap.get("container").get("padding").getValue(), EPSILON);
|
||||||
|
// assertEquals(30, nodeHashMap.get("container").get("leftPadding").getValue(), EPSILON);
|
||||||
|
|
||||||
assertEquals(85, nodeHashMap.get("title0").get("top").getValue(), EPSILON);
|
|
||||||
assertEquals(85, nodeHashMap.get("title1").get("top").getValue(), EPSILON);
|
|
||||||
|
|
||||||
assertEquals(210, nodeHashMap.get("thumb2").get("top").getValue(), EPSILON);
|
// try {
|
||||||
assertEquals(210, nodeHashMap.get("thumb3").get("top").getValue(), EPSILON);
|
// solver.removeConstraint(cache.get(cache.size() -1));
|
||||||
|
// } catch (UnknownConstraintException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// solver.addConstraint(ConstraintParser.parseConstraint("container.width == 300", variableResolver, 8));
|
||||||
|
|
||||||
assertEquals(275, nodeHashMap.get("title2").get("top").getValue(), EPSILON);
|
Variable width = variableResolver.resolveVariable("container.width");
|
||||||
assertEquals(275, nodeHashMap.get("title3").get("top").getValue(), EPSILON);
|
try {
|
||||||
|
solver.addEditVariable(width, Strength.STRONG);
|
||||||
|
} catch (DuplicateEditVariableException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (RequiredFailureException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(420, nodeHashMap.get("thumb4").get("top").getValue(), EPSILON);
|
try {
|
||||||
assertEquals(420, nodeHashMap.get("thumb5").get("top").getValue(), EPSILON);
|
solver.suggestValue(width, 300);
|
||||||
|
} catch (UnknownEditVariableException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(485, nodeHashMap.get("title4").get("top").getValue(), EPSILON);
|
solver.updateVariables();
|
||||||
assertEquals(485, nodeHashMap.get("title5").get("top").getValue(), EPSILON);
|
|
||||||
|
assertEquals(300, nodeHashMap.get("container").get("width").getValue(), EPSILON);
|
||||||
|
|
||||||
|
try {
|
||||||
|
solver.suggestValue(width, 200);
|
||||||
|
} catch (UnknownEditVariableException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
solver.updateVariables();
|
||||||
|
assertEquals(200, nodeHashMap.get("container").get("width").getValue(), EPSILON);
|
||||||
|
|
||||||
|
// assertEquals(150, nodeHashMap.get("container").get("columnWidth").getValue(), EPSILON);
|
||||||
|
// assertEquals(50, nodeHashMap.get("container").get("thumbHeight").getValue(), EPSILON);
|
||||||
|
// assertEquals(30, nodeHashMap.get("container").get("padding").getValue(), EPSILON);
|
||||||
|
// assertEquals(30, nodeHashMap.get("container").get("leftPadding").getValue(), EPSILON);
|
||||||
|
|
||||||
|
// assertEquals(280, nodeHashMap.get("container").get("test").getValue(), EPSILON);
|
||||||
|
|
||||||
|
// assertEquals(270, nodeHashMap.get("container").get("width").getValue() - nodeHashMap.get("container").get("padding").getValue(), EPSILON);
|
||||||
|
// assertEquals(270, nodeHashMap.get("container").get("rightPadding").getValue(), EPSILON);
|
||||||
|
|
||||||
|
|
||||||
|
// assertEquals(100, nodeHashMap.get("view").get("top").getValue(), EPSILON);
|
||||||
|
// assertEquals(400, nodeHashMap.get("view").get("bottom").getValue(), EPSILON);
|
||||||
|
// assertEquals(350, nodeHashMap.get("view2").get("right").getValue(), EPSILON);
|
||||||
|
|
||||||
|
// assertEquals(20, nodeHashMap.get("thumb0").get("top").getValue(), EPSILON);
|
||||||
|
// assertEquals(20, nodeHashMap.get("thumb1").get("top").getValue(), EPSILON);
|
||||||
|
//
|
||||||
|
// assertEquals(85, nodeHashMap.get("title0").get("top").getValue(), EPSILON);
|
||||||
|
// assertEquals(85, nodeHashMap.get("title1").get("top").getValue(), EPSILON);
|
||||||
|
//
|
||||||
|
// assertEquals(210, nodeHashMap.get("thumb2").get("top").getValue(), EPSILON);
|
||||||
|
// assertEquals(210, nodeHashMap.get("thumb3").get("top").getValue(), EPSILON);
|
||||||
|
//
|
||||||
|
// assertEquals(275, nodeHashMap.get("title2").get("top").getValue(), EPSILON);
|
||||||
|
// assertEquals(275, nodeHashMap.get("title3").get("top").getValue(), EPSILON);
|
||||||
|
//
|
||||||
|
// assertEquals(420, nodeHashMap.get("thumb4").get("top").getValue(), EPSILON);
|
||||||
|
// assertEquals(420, nodeHashMap.get("thumb5").get("top").getValue(), EPSILON);
|
||||||
|
//
|
||||||
|
// assertEquals(485, nodeHashMap.get("title4").get("top").getValue(), EPSILON);
|
||||||
|
// assertEquals(485, nodeHashMap.get("title5").get("top").getValue(), EPSILON);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue