jsketcher/web/app/sketcher/constr/AlgNumSystem.js
Val Erastov (xibyte) da613a082e polynomial analysis
2020-01-21 00:35:04 -08:00

436 lines
10 KiB
JavaScript

import {createByConstraintName} from "./solverConstraints";
import {Param, prepare} from "./solver";
import {findConstructionCluster} from "./constructions";
import {GCCircle, GCPoint} from "./constractibles";
import {eqEps, eqTol} from "../../brep/geom/tolerance";
import {Polynomial, POW_1_FN} from "./polynomial";
import {compositeFn, NOOP} from "../../../../modules/gems/func";
export class AlgNumSubSystem {
generator = NOOP;//new BoundaryObjectsGenerator();
allConstraints = [];
ownParams = new Set();
readOnlyParams = new Set();
generatedParams = new Set();
beingSolvedParams = new Set();
eliminatedParams = new Map();
// generators = [];
subSystems = []; //subsystems are generated by generators only
polynomials = [];
substitutedParams = [];
polyToConstr = new Map();
conflicting = new Set();
redundant = new Set();
snapshot = new Map();
dof = 0;
SubSystem(generator) {
this.generator = generator;
}
get fullyConstrained() {
return this.dof === 0;
}
validConstraints(callback) {
this.allConstraints.forEach(c => {
if (!this.conflicting.has(c)) {
callback(c);
}
});
}
addConstraint(constraint, _ancestorParams) {
if (this.canBeAdded(constraint.params)) {
// this.constraints.push(constraint);
// this.constraints
}
this.makeSnapshot();
this.allConstraints.push(constraint);
this.prepare();
if (!this.isConflicting(constraint)) {
this.solveFine();
console.log(this.solveStatus);
if (!this.solveStatus.success) {
console.log("adding to conflicts");
this.conflicting.add(constraint);
}
}
if (this.isConflicting(constraint)) {
this.rollback();
// } else if (this.fullyConstrained) {
// this.rollback();
// this.conflicting.add(constraint);
// this.redundant.add(constraint);
} else {
this.updateDOF();
this.updateFullyConstrainedObjects();
}
}
isConflicting(constraint) {
return this.conflicting.has(constraint);
}
updateDOF() {
let vars = 0;
let equations = 0;
this.validConstraints(c => {
equations ++;
c.params.forEach(p => {
if (!this.readOnlyParams.has(p)) {
vars++
}
})
});
this.dof = vars - equations;
}
makeSnapshot() {
this.snapshot.clear();
this.validConstraints(c => c.params.forEach(p => this.snapshot.set(p, p.get())));
}
rollback() {
this.snapshot.forEach((val, param) => param.set(val));
}
reset() {
this.polynomials = [];
this.substitutedParams = [];
this.eliminatedParams.clear();
this.beingSolvedParams.clear();
this.polyToConstr.clear();
}
evaluatePolynomials() {
this.validConstraints(c => {
c.collectPolynomials(this.polynomials);
this.polynomials.forEach(p => this.polyToConstr.set(p, c))
});
console.log('reducing system:');
this.polynomials.forEach(p => console.log(p.toString()));
let requirePass = true;
while (requirePass) {
requirePass = false;
for (let i = 0; i < this.polynomials.length; ++i) {
const polynomial = this.polynomials[i];
if (!polynomial) {
continue;
}
if (polynomial.monomials.length === 0) {
this.conflicting.add(this.polyToConstr.get(polynomial));
console.log("CONFLICT: " + polynomial.toString());
if (eqEps(polynomial.constant, 0)) {
this.redundant.add(this.polyToConstr.get(polynomial));
console.log("REDUNDANT");
}
this.polynomials[i] = null;
} else if (polynomial.isLinear && polynomial.monomials.length === 1) {
const monomial = polynomial.monomials[0];
const terms = monomial.terms;
if (terms.length === 1) {
const term = terms[0];
if (term.fn.degree === 1) {
const p = term.param;
const val = - polynomial.constant / monomial.constant;
p.set(val);
this.eliminatedParams.set(p, val);
for (let polynomial of this.polynomials) {
if (polynomial) {
polynomial.eliminate(p, val);
}
}
requirePass = true;
}
}
this.polynomials[i] = null;
} else if (polynomial.monomials.length === 2 && polynomial.isLinear) {
const [m1, m2] = polynomial.monomials;
let p1 = m1.linearParam;
let p2 = m2.linearParam;
const constant = - m2.constant / m1.constant;
if (eqEps(polynomial.constant, 0)) {
for (let polynomial of this.polynomials) {
if (polynomial) {
polynomial.substitute(p1, p2, constant);
}
}
this.substitutedParams.push([p1, new Polynomial().monomial(constant).term(p2, POW_1_FN)]);
this.polynomials[i] = null;
requirePass = true;
} else {
const b = - polynomial.constant / m1.constant;
let transaction = compositeFn();
for (let polynomial of this.polynomials) {
if (polynomial) {
const polyTransaction = polynomial.linearSubstitution(p1, p2, constant, b);
if (!polyTransaction) {
transaction = null;
break;
}
transaction.push(polyTransaction);
transaction.push(() => {
this.substitutedParams.push([p1, new Polynomial(b).monomial(constant).term(p2, POW_1_FN)]);
this.polynomials[i] = null;
});
}
}
if (transaction) {
transaction();
requirePass = true;
}
}
}
}
if (requirePass) {
this.polynomials.forEach(polynomial => polynomial && polynomial.compact());
}
}
this.polynomials = this.polynomials.filter(p => p);
}
prepare() {
this.reset();
this.evaluatePolynomials();
// this.polynomialClusters = splitByIsolatedClusters(this.polynomials);
console.log('solving system:');
this.polynomials.forEach(p => console.log(p.toString()));
console.log('with respect to:');
this.substitutedParams.forEach(([x, expr]) => console.log('X' + x.id + ' = ' + expr.toString()));
const residuals = [];
this.polynomials.forEach(p => residuals.push(p.asResidual()));
for (let residual of residuals) {
residual.params.forEach(solverParam => {
if (!this.beingSolvedParams.has(solverParam)) {
solverParam.reset(solverParam.objectParam.get());
this.beingSolvedParams.add(solverParam);
}
});
}
this.numericalSolver = prepare(residuals);
}
splitByIsolatedClusters(polynomials) {
const graph = [polynomials.length];
const params = [];
const paramMap = new Map();
polynomials.forEach((pl, i) => pl.visitParams(p => {
let pIndex = paramMap.get(p);
if (pIndex === undefined) {
pIndex = paramMap.size;
paramMap.set(p, pIndex);
params.push(p);
}
graph[i][pIndex] = 1;
}));
const visited = new Set();
const clusters = [];
for (let param of params) {
if (visited.has(param)) {
continue
}
const stack = [param];
const isolation = [];
while (stack.length) {
const p = stack.pop();
if (visited.has(p)) {
continue;
}
isolation.push(p);
const index = paramMap.get(p);
for (let i = 0; i < graph.length; ++i) {
if (graph[i][index] === 1) {
for (let j = 0; j < params.length; j ++) {
if (j !== index) {
stack.push(params[j]);
}
}
}
}
}
if (isolation.length) {
clusters.push(new Cluster(isolation));
}
}
return clusters;
}
solveRough() {
this.solve(true);
}
solveFine() {
this.solve(false);
}
solve(rough) {
this.generator();
this.beingSolvedParams.forEach(solverParam => {
solverParam.set(solverParam.objectParam.get());
});
const solveStatus = this.numericalSolver.solveSystem(rough);
if (!rough) {
this.solveStatus = solveStatus;
}
console.log('numerical result: ' + solveStatus.success);
for (let [p, val] of this.eliminatedParams) {
p.set(val);
}
this.beingSolvedParams.forEach(solverParam => {
solverParam.objectParam.set(solverParam.get());
});
for (let i = this.substitutedParams.length - 1; i >= 0; i--) {
let [param, expression] = this.substitutedParams[i];
param.set(expression.value());
}
}
updateFullyConstrainedObjects() {
this.validConstraints(c => {
c.objects.forEach(obj => {
let allLocked = true;
obj.visitParams(p => {
if (!this.eliminatedParams.has(p)) {
allLocked = false;
}
});
obj.fullyConstrained = allLocked;
});
});
}
canBeAdded(subjectParams, ancestorParams) {
for (let p of subjectParams) {
if (!this.ownParams.has(p) && (!ancestorParams || !ancestorParams.has(p))) {
return false;
}
}
return true;
}
}
class Cluster {
constructor(polynomials) {
this.polynomials = polynomials;
this.beingSolvedParams = new Set();
const residuals = [];
this.polynomials.forEach(p => residuals.push(p.asResidual()));
for (let residual of residuals) {
residual.params.forEach(solverParam => {
if (!this.beingSolvedParams.has(solverParam)) {
solverParam.reset(solverParam.objectParam.get());
this.beingSolvedParams.add(solverParam);
}
});
}
}
solve(rough) {
this.beingSolvedParams.forEach(solverParam => {
solverParam.set(solverParam.objectParam.get());
});
this.solveStatus = this.numericalSolver.solveSystem(rough);
this.beingSolvedParams.forEach(solverParam => {
solverParam.objectParam.set(solverParam.get());
});
}
}
export class AlgNumSystem {
constraints = [];
generators = [];
locked = new Set();
constantParams = new Set();
constructor(visitAllObjects) {
this.visitAllObjects = visitAllObjects;
}
addConstraint(constraint) {
this.constraints.push(constraint);
if (constraint.schema.generator) {
}
}
startTransaction(interactiveLock = []) {
this.systemTransaction.prepare(interactiveLock);
return this.systemTransaction;
}
}