import {Variable} from './Variable'
import {String} from 'typescript-string-operations'
import {compareNumbers} from '../../utils/compare'
export class Constraint {
// The Left (if horizontal; Top, if vertical) variable of the constraint.
Left: Variable
// The Right (if horizontal; Bottom, if vertical) variable of the constraint.
Right: Variable
// The required separation of the points of the two Variables along the current axis.
Gap: number
// Indicates if the distance between the two variables must be equal to the gap
// (rather than greater or equal to).
IsEquality: boolean
Lagrangian: number
IsActive: boolean
IsUnsatisfiable: boolean
// Index in Solver.AllConstraints, to segregate active from inactive constraints.
VectorIndex: number
SetActiveState(activeState: boolean, newVectorIndex: number) {
// Note: newVectorIndex may be the same as the old one if we are changing the state
// of the last inactive or first active constraint.
/*Assert.assert(
this.IsActive !== activeState,
'Constraint is already set to activationState',
)*/
this.IsActive = activeState
this.VectorIndex = newVectorIndex
if (this.IsActive) {
this.Left.ActiveConstraintCount++
this.Right.ActiveConstraintCount++
} else {
this.Left.ActiveConstraintCount--
this.Right.ActiveConstraintCount--
}
}
SetVectorIndex(vectorIndex: number) {
// This is separate from set_VectorIndex because we can't restrict the caller to a specific
// class and we only want ConstraintVector to be able to call this.
this.VectorIndex = vectorIndex
}
Reinitialize() {
// Called by Qpsc or equivalence-constraint-regapping initial block restructuring.
// All variables have been moved to their own blocks again, so reset solution states.
this.IsActive = false
this.IsUnsatisfiable = false
this.ClearDfDv()
}
// This is an function, not a propset, because we only want it called by the Solver.
UpdateGap(newGap: number) {
this.Gap = newGap
}
// The Constraint constructor takes the two variables and their required distance.
// The constraints will be generated either manually or by ConstraintGenerator,
// both of which know about the sizes when the constraints are generated (as
// well as any necessary padding), so the sizes are accounted for at that time
// and ProjectionSolver classes are not aware of Variable sizes.
static constructorVVNB(left: Variable, right: Variable, gap: number, isEquality: boolean): Constraint {
const v = new Constraint(left)
v.Left = left
v.Right = right
v.Gap = gap
v.IsEquality = isEquality
v.Lagrangian = 0
v.IsActive = false
return v
}
// For Solver.ComputeDfDv's DummyParentNode's constraint only.
constructor(variable: Variable) {
this.Right = variable
this.Left = variable
}
// Generates a string representation of the Constraint.
// A string representation of the Constraint.
ToString(): string {
return String.format(
' Cst: [{0}] [{1}] {2} {3:F5} vio {4:F5} Lm {5:F5}/{6:F5} {7}actv',
this.Left,
this.Right,
this.IsEquality ? '==' : '>=',
this.Gap,
this.Violation,
this.Lagrangian,
this.Lagrangian * 2,
this.IsActive ? '+' : this.IsUnsatisfiable ? '!' : '-',
)
}
get Violation(): number {
return this.Left.ActualPos * this.Left.Scale + (this.Gap - this.Right.ActualPos * this.Right.Scale)
}
ClearDfDv() {
this.Lagrangian = 0
}
// Compare this Constraint to rhs by their Variables in ascending order (this === lhs, other === rhs).
// The object being compared to.
// -1 if this.Left/Right are "less"; +1 if this.Left/Right are "greater"; 0 if this.Left/Right
// and rhs.Left/Right are equal.
public CompareTo(other: Constraint): number {
let cmp: number = this.Left.CompareTo(other.Left)
if (0 === cmp) {
cmp = this.Right.CompareTo(other.Right)
}
if (0 === cmp) {
cmp = compareNumbers(this.Gap, other.Gap)
}
return cmp
}
}