mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-24 01:15:25 +01:00
got dog leg
This commit is contained in:
parent
1c729a5adc
commit
888c1efb61
6 changed files with 432 additions and 23 deletions
|
|
@ -57,6 +57,38 @@ TCAD.parametric.System.prototype.makeJacobian = function() {
|
|||
return jacobi;
|
||||
};
|
||||
|
||||
TCAD.parametric.System.prototype.fillJacobian = function(jacobi) {
|
||||
for (i=0; i < this.constraints.length; i++) {
|
||||
var c = this.constraints[i];
|
||||
|
||||
var cParams = c.params;
|
||||
var grad = [];
|
||||
c.gradient(grad);
|
||||
|
||||
for (var p = 0; p < cParams.length; p++) {
|
||||
var param = cParams[p];
|
||||
j = param.j;
|
||||
jacobi[i][j] = grad[p];
|
||||
}
|
||||
}
|
||||
return jacobi;
|
||||
};
|
||||
|
||||
TCAD.parametric.System.prototype.calcResidual = function(r) {
|
||||
|
||||
var i=0;
|
||||
var err = 0.;
|
||||
|
||||
for (i=0; i < this.constraints.length; i++) {
|
||||
var c = this.constraints[i];
|
||||
r[i] = c.error();
|
||||
err += r[i]*r[i];
|
||||
}
|
||||
|
||||
err *= 0.5;
|
||||
return err;
|
||||
}
|
||||
|
||||
TCAD.parametric.System.prototype.calcGrad_ = function(out) {
|
||||
var i;
|
||||
for (i = 0; i < out.length || i < this.params.length; ++i) {
|
||||
|
|
@ -201,7 +233,7 @@ TCAD.parametric.solve = function(constrs, locked, fineLevel, alg) {
|
|||
sys.setParams(point);
|
||||
return sys.makeJacobian();
|
||||
};
|
||||
|
||||
alg = 5;
|
||||
if (alg > 0) {
|
||||
switch (alg) {
|
||||
case 1:
|
||||
|
|
@ -217,6 +249,9 @@ TCAD.parametric.solve = function(constrs, locked, fineLevel, alg) {
|
|||
case 4:
|
||||
TCAD.math.solve_UNCMIN(sys);
|
||||
break;
|
||||
case 5:
|
||||
optim.dog_leg(sys);
|
||||
break;
|
||||
}
|
||||
return sys;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,15 @@ TCAD.math._arr = function(size) {
|
|||
return out;
|
||||
};
|
||||
|
||||
TCAD.math._matrix = function(m, n) {
|
||||
var out = [];
|
||||
out.length = m;
|
||||
for (var i = 0; i < m; ++i) {
|
||||
out[i] = TCAD.math._arr(n);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
TCAD.math.distance = function(x1, y1, x2, y2) {
|
||||
var dx = x1 - x2;
|
||||
var dy = y1 - y2;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
optim = {};
|
||||
|
||||
optim.bfgs = function uncmin(f,x0,tol,gradient,maxit,callback,options) {
|
||||
//Added strong wolfe condition to numeric's uncmin
|
||||
optim.bfgs_ = function(f,x0,tol,gradient,maxit,callback,options) {
|
||||
var grad = numeric.gradient;
|
||||
if(typeof options === "undefined") { options = {}; }
|
||||
if(typeof tol === "undefined") { tol = 1e-8; }
|
||||
|
|
@ -29,15 +30,32 @@ optim.bfgs = function uncmin(f,x0,tol,gradient,maxit,callback,options) {
|
|||
df0 = dot(g0,step);
|
||||
// line search
|
||||
x1 = x0;
|
||||
var tL = 0;
|
||||
var tR = 100;
|
||||
while(it < maxit) {
|
||||
if(t*nstep < tol) { break; }
|
||||
s = mul(step,t);
|
||||
x1 = add(x0,s);
|
||||
f1 = f(x1);
|
||||
//Nocadel, 3.7(a,b)
|
||||
if(f1-f0 >= 0.1*t*df0 || isNaN(f1)) {
|
||||
t *= 0.5;
|
||||
tR = t;
|
||||
t = (tL + tR) * 0.5;
|
||||
++it;
|
||||
continue;
|
||||
} else {
|
||||
var slope = dot(gradient(x1), step);
|
||||
if (slope <= 0.9 * Math.abs(df0)){
|
||||
break;
|
||||
}else if ( slope >= 0.9 * df0) {
|
||||
tR = t;
|
||||
t = (tL+ tR) * 0.5;
|
||||
continue;
|
||||
}else{
|
||||
tL = t;
|
||||
t = (tL+ tR)*0.5;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -60,4 +78,330 @@ optim.bfgs = function uncmin(f,x0,tol,gradient,maxit,callback,options) {
|
|||
++it;
|
||||
}
|
||||
return {solution: x0, f: f0, gradient: g0, invHessian: H1, iterations:it, message: msg};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
optim.bfgs = function(f,x0,tol,gradient,maxit,callback,options) {
|
||||
var grad = numeric.gradient;
|
||||
if(typeof options === "undefined") { options = {}; }
|
||||
if(typeof tol === "undefined") { tol = 1e-8; }
|
||||
if(typeof gradient === "undefined") { gradient = function(x) { return grad(f,x); }; }
|
||||
if(typeof maxit === "undefined") maxit = 1000;
|
||||
x0 = numeric.clone(x0);
|
||||
var n = x0.length;
|
||||
var f0 = f(x0),f1,df0;
|
||||
if(isNaN(f0)) throw new Error('uncmin: f(x0) is a NaN!');
|
||||
var max = Math.max, norm2 = numeric.norm2;
|
||||
tol = max(tol,numeric.epsilon);
|
||||
var step,g0,g1,H1 = options.Hinv || numeric.identity(n);
|
||||
var dot = numeric.dot, inv = numeric.inv, sub = numeric.sub, add = numeric.add, ten = numeric.tensor, div = numeric.div, mul = numeric.mul;
|
||||
var all = numeric.all, isfinite = numeric.isFinite, neg = numeric.neg;
|
||||
var it=0,i,s,x1,y,Hy,Hs,ys,i0,t,nstep,t1,t2;
|
||||
var msg = "";
|
||||
g0 = gradient(x0);
|
||||
while(it<maxit) {
|
||||
if(typeof callback === "function") { if(callback(it,x0,f0,g0,H1)) { msg = "Callback returned true"; break; } }
|
||||
if(!all(isfinite(g0))) { msg = "Gradient has Infinity or NaN"; break; }
|
||||
step = neg(dot(H1,g0));
|
||||
if(!all(isfinite(step))) { msg = "Search direction has Infinity or NaN"; break; }
|
||||
nstep = norm2(step);
|
||||
if(nstep < tol) { msg="Newton step smaller than tol"; break; }
|
||||
|
||||
df0 = dot(g0,step);
|
||||
// line search
|
||||
t1 = 0.0;
|
||||
f1 = f0;
|
||||
|
||||
t2 = 1.0;
|
||||
s = mul(step,t2);
|
||||
x1 = add(x0,s);
|
||||
var f2 = f(x1);
|
||||
|
||||
t3 = 2.0;
|
||||
s = mul(step,t3);
|
||||
x1 = add(x0,s);
|
||||
var f3 = f(x1);
|
||||
var tMax = 1e23;
|
||||
|
||||
while( (f2 > f1 || f2 > f3) && it < maxit) {
|
||||
if(t*nstep < tol) { break; }
|
||||
if (f2 > f1) {
|
||||
//If f2 is greater than f1 then we shorten alpha2 and alpha3 closer to f1
|
||||
//Effectively both are shortened by a factor of two.
|
||||
t3 = t2;
|
||||
f3 = f2;
|
||||
t2 = t2 / 2;
|
||||
|
||||
s = mul(step,t2);
|
||||
x1 = add(x0,s);
|
||||
f2 = f(x1);
|
||||
}
|
||||
else if (f2 > f3) {
|
||||
if (t3 >= tMax)
|
||||
break;
|
||||
//If f2 is greater than f3 then we increase alpha2 and alpha3 away from f1
|
||||
//Effectively both are lengthened by a factor of two.
|
||||
t2 = t3;
|
||||
f2 = f3;
|
||||
t3 = t3 * 2;
|
||||
|
||||
s = mul(step,t3);
|
||||
x1 = add(x0,s);
|
||||
f3 = f(x1);
|
||||
}
|
||||
it ++;
|
||||
}
|
||||
|
||||
//Get the alpha for the minimum f of the quadratic approximation
|
||||
var ts = t2 + ((t2-t1)*(f1-f3))/(3*(f1-2*f2+f3));
|
||||
|
||||
//Guarantee that the new alphaStar is within the bracket
|
||||
if (ts >= t3 || ts <= t1)
|
||||
ts = t2;
|
||||
|
||||
if (ts > tMax)
|
||||
ts = tMax;
|
||||
|
||||
if (ts != ts)
|
||||
ts = 0.;
|
||||
|
||||
//Take a final step to alphaStar
|
||||
s = mul(step,ts);
|
||||
x1 = add(x0,s);
|
||||
f1 = f(x1);
|
||||
|
||||
|
||||
if(t*nstep < tol) { msg = "Line search step size smaller than tol"; break; }
|
||||
if(it === maxit) { msg = "maxit reached during line search"; break; }
|
||||
g1 = gradient(x1);
|
||||
y = sub(g1,g0);
|
||||
ys = dot(y,s);
|
||||
Hy = dot(H1,y);
|
||||
|
||||
// BFGS update on H1
|
||||
H1 = sub(add(H1,
|
||||
mul(
|
||||
(ys+dot(y,Hy))/(ys*ys),
|
||||
ten(s,s) )),
|
||||
div(add(ten(Hy,s),ten(s,Hy)),ys));
|
||||
x0 = x1;
|
||||
f0 = f1;
|
||||
g0 = g1;
|
||||
++it;
|
||||
}
|
||||
return {solution: x0, f: f0, gradient: g0, invHessian: H1, iterations:it, message: msg};
|
||||
};
|
||||
|
||||
// this is Gauss-Newton least square algorithm with trust region(dog leg) control/
|
||||
optim.dog_leg = function(subsys) {
|
||||
|
||||
var tolg=1e-80, tolx=1e-80, tolf=1e-10;
|
||||
|
||||
var xsize = subsys.params.length;
|
||||
var csize = subsys.constraints.length;
|
||||
|
||||
if (xsize == 0)
|
||||
return 'Success';
|
||||
|
||||
var vec = TCAD.math._arr;
|
||||
var mx = TCAD.math._matrix;
|
||||
|
||||
var n = numeric;
|
||||
|
||||
var x = vec(xsize);
|
||||
var x_new = vec(xsize);
|
||||
|
||||
var fx = vec(csize);
|
||||
var fx_new = vec(csize);
|
||||
|
||||
var Jx = mx(csize, xsize);
|
||||
var Jx_new = mx(csize, xsize);
|
||||
var g = vec(xsize);
|
||||
var h_sd = vec(xsize);
|
||||
var h_gn = vec(xsize);
|
||||
var h_dl = vec(xsize);
|
||||
|
||||
var r0 = vec(csize);
|
||||
|
||||
var err;
|
||||
subsys.fillParams(x);
|
||||
|
||||
// subsys.setParams(vec(xsize));
|
||||
// subsys.calcResidual(r0);
|
||||
|
||||
subsys.setParams(x);
|
||||
err = subsys.calcResidual(fx);
|
||||
|
||||
subsys.fillJacobian(Jx);
|
||||
|
||||
function lls(A, b) {
|
||||
var At = n.transpose(A);
|
||||
var J = n.dot(At, A);
|
||||
var r = n.dot(At, b);;
|
||||
|
||||
return nocadel_10_18(J, r);
|
||||
}
|
||||
|
||||
function nocadel_10_18(A, r) {
|
||||
//10.18
|
||||
var usv = n.svd(A);
|
||||
var x = vec(xsize);
|
||||
for (var i = 0; i < usv.S.length; ++i) {
|
||||
var u = usv.U[i];
|
||||
var v = usv.V[i];
|
||||
var b = usv.S[i];
|
||||
if (b != 0) {
|
||||
var _t = n.mul(v, n.dot(u, r) / b);
|
||||
x = n.add(x, _t);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function lsolve(A, b) {
|
||||
// if (csize < xsize) {
|
||||
// var At = n.transpose(A);
|
||||
// var J = n.dot(At, A);
|
||||
// var r = n.dot(At, b);;
|
||||
// return n.solve(J, r);
|
||||
// } else {
|
||||
// return n.solve(A, b);
|
||||
// }
|
||||
var At = n.transpose(A);
|
||||
var res = n.dot(n.dot(At, n.inv(n.dot(A, At)) ), b);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
g = n.dot(n.transpose(Jx), n.mul(fx, -1));
|
||||
|
||||
// get the infinity norm fx_inf and g_inf
|
||||
var g_inf = n.norminf(g);
|
||||
var fx_inf = n.norminf(fx);
|
||||
|
||||
var maxIterNumber = 100 * xsize;
|
||||
var divergingLim = 1e6*err + 1e12;
|
||||
|
||||
var delta=0.1;
|
||||
var alpha=0.;
|
||||
var nu=2.;
|
||||
var iter=0, stop=0, reduce=0;
|
||||
while (!stop) {
|
||||
|
||||
// check if finished
|
||||
if (fx_inf <= tolf) // Success
|
||||
stop = 1;
|
||||
else if (g_inf <= tolg)
|
||||
stop = 2;
|
||||
else if (delta <= tolx*(tolx + n.norm2(x)))
|
||||
stop = 2;
|
||||
else if (iter >= maxIterNumber)
|
||||
stop = 4;
|
||||
else if (err > divergingLim || err != err) { // check for diverging and NaN
|
||||
stop = 6;
|
||||
}
|
||||
else {
|
||||
// get the steepest descent direction
|
||||
alpha = n.norm2Squared(g)/n.norm2Squared(n.dot(Jx, g));
|
||||
h_sd = n.mul(g, alpha);
|
||||
|
||||
// get the gauss-newton step
|
||||
// h_gn = n.solve(Jx, n.mul(fx, -1));
|
||||
h_gn = lsolve(Jx, n.mul(fx, -1));
|
||||
|
||||
// solve linear problem using svd formula to get the gauss-newton step
|
||||
// h_gn = lls(Jx, n.mul(fx, -1));
|
||||
|
||||
var rel_error = n.norm2(n.add(n.dot(Jx, h_gn), fx)) / n.norm2(fx);
|
||||
if (rel_error > 1e15)
|
||||
break;
|
||||
|
||||
// compute the dogleg step
|
||||
if (n.norm2(h_gn) < delta) {
|
||||
h_dl = n.clone(h_gn);
|
||||
if (n.norm2(h_dl) <= tolx*(tolx + n.norm2(x))) {
|
||||
stop = 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (alpha*n.norm2(g) >= delta) {
|
||||
h_dl = n.mul( h_sd, delta/(alpha*n.norm2(g)));
|
||||
}
|
||||
else {
|
||||
//compute beta
|
||||
var beta = 0;
|
||||
var b = n.sub(h_gn, h_sd);
|
||||
var bb = Math.abs(n.dot(b, b));
|
||||
var gb = Math.abs(n.dot(h_sd,b));
|
||||
var c = (delta + n.norm2(h_sd))*(delta - n.norm2(h_sd));
|
||||
|
||||
if (gb > 0)
|
||||
beta = c / (gb + Math.sqrt(gb * gb + c * bb));
|
||||
else
|
||||
beta = (Math.sqrt(gb * gb + c * bb) - gb)/bb;
|
||||
|
||||
// and update h_dl and dL with beta
|
||||
h_dl = n.add(h_sd, n.mul(beta,b));
|
||||
}
|
||||
}
|
||||
|
||||
// see if we are already finished
|
||||
if (stop)
|
||||
break;
|
||||
|
||||
// it didn't work in some tests
|
||||
// // restrict h_dl according to maxStep
|
||||
// double scale = subsys->maxStep(h_dl);
|
||||
// if (scale < 1.)
|
||||
// h_dl *= scale;
|
||||
|
||||
// get the new values
|
||||
var err_new;
|
||||
x_new = n.add(x, h_dl);
|
||||
subsys.setParams(x_new);
|
||||
err_new = subsys.calcResidual(fx_new);
|
||||
subsys.fillJacobian(Jx_new);
|
||||
|
||||
// calculate the linear model and the update ratio
|
||||
var dL = err - 0.5* n.norm2Squared(n.add(fx, n.dot(Jx, h_dl)));
|
||||
var dF = err - err_new;
|
||||
var rho = dL/dF;
|
||||
|
||||
if (dF > 0 && dL > 0) {
|
||||
x = n.clone(x_new);
|
||||
Jx = n.clone(Jx_new);
|
||||
fx = n.clone(fx_new);
|
||||
err = err_new;
|
||||
|
||||
g = n.dot(n.transpose(Jx), n.mul(fx, -1));
|
||||
|
||||
// get infinity norms
|
||||
g_inf = n.norminf(g);
|
||||
fx_inf = n.norminf(fx);
|
||||
}
|
||||
else
|
||||
rho = -1;
|
||||
|
||||
// update delta
|
||||
if (Math.abs(rho-1.) < 0.2 && n.norm2(h_dl) > delta/3. && reduce <= 0) {
|
||||
delta = 3*delta;
|
||||
nu = 2;
|
||||
reduce = 0;
|
||||
}
|
||||
else if (rho < 0.25) {
|
||||
delta = delta/nu;
|
||||
nu = 2*nu;
|
||||
reduce = 2;
|
||||
}
|
||||
else
|
||||
reduce--;
|
||||
|
||||
// count this iteration and start again
|
||||
iter++;
|
||||
}
|
||||
|
||||
|
||||
return (stop == 1) ? 'Success' : 'Failed';
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
this.qrDecomposition = function(matrix) {
|
||||
TCAD.math.qr = function(matrix) {
|
||||
|
||||
function arr(size) {
|
||||
var out = [];
|
||||
|
|
@ -19,27 +19,32 @@ this.qrDecomposition = function(matrix) {
|
|||
|
||||
var nR = this.matrix.length;
|
||||
var nC = this.matrix[0].length;
|
||||
|
||||
var k;
|
||||
var norm2;
|
||||
var akk;
|
||||
var j;
|
||||
var i;
|
||||
|
||||
// initializations
|
||||
for (var k = 0; k < nC; ++k) {
|
||||
for (k = 0; k < nC; ++k) {
|
||||
this.permutation[k] = k;
|
||||
var norm2 = 0;
|
||||
for (var i = 0; i < nR; ++i) {
|
||||
var akk = matrix[i][k];
|
||||
norm2 = 0;
|
||||
for (i = 0; i < nR; ++i) {
|
||||
akk = matrix[i][k];
|
||||
norm2 += akk * akk;
|
||||
}
|
||||
this.norm[k] = Math.sqrt(norm2);
|
||||
}
|
||||
|
||||
// transform the matrix column after column
|
||||
for (var k = 0; k < nC; ++k) {
|
||||
for (k = 0; k < nC; ++k) {
|
||||
|
||||
// select the column with the greatest norm on active components
|
||||
var nextColumn = -1;
|
||||
var ak2 = Number.NEGATIVE_INFINITY;
|
||||
for (var i = k; i < nC; ++i) {
|
||||
var norm2 = 0;
|
||||
for (var j = k; j < nR; ++j) {
|
||||
for (i = k; i < nC; ++i) {
|
||||
norm2 = 0;
|
||||
for (j = k; j < nR; ++j) {
|
||||
var aki = matrix[j][this.permutation[i]];
|
||||
norm2 += aki * aki;
|
||||
}
|
||||
|
|
@ -60,7 +65,7 @@ this.qrDecomposition = function(matrix) {
|
|||
this.permutation[k] = pk;
|
||||
|
||||
// choose alpha such that Hk.u = alpha ek
|
||||
var akk = matrix[k][pk];
|
||||
akk = matrix[k][pk];
|
||||
var alpha = (akk > 0) ? -Math.sqrt(ak2) : Math.sqrt(ak2);
|
||||
var betak = 1.0 / (ak2 - akk * alpha);
|
||||
this.beta[pk] = betak;
|
||||
|
|
@ -72,14 +77,14 @@ this.qrDecomposition = function(matrix) {
|
|||
// transform the remaining columns
|
||||
for (var dk = nC - 1 - k; dk > 0; --dk) {
|
||||
var gamma = 0;
|
||||
for (var j = k; j < nR; ++j) {
|
||||
for (j = k; j < nR; ++j) {
|
||||
gamma += matrix[j][pk] * matrix[j][this.permutation[k + dk]];
|
||||
}
|
||||
gamma *= betak;
|
||||
for (var j = k; j < nR; ++j) {
|
||||
for (j = k; j < nR; ++j) {
|
||||
matrix[j][this.permutation[k + dk]] -= gamma * matrix[j][pk];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.rank = this.solvedCols;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,14 +39,27 @@ function testCompare() {
|
|||
var bfgs_ = perpAndCoinc();
|
||||
var lm_ = perpAndCoinc();
|
||||
var tr_ = perpAndCoinc();
|
||||
var dl_ = perpAndCoinc();
|
||||
|
||||
var bfgs = TCAD.parametric.solve(bfgs_, [], 0, 1);
|
||||
// var bfgs = TCAD.parametric.solve(bfgs_, [], 0, 1);
|
||||
var lm = TCAD.parametric.solve(lm_, [], 0, 0);
|
||||
var tr = TCAD.parametric.solve(tr_, [], 0, 2);
|
||||
// var tr = TCAD.parametric.solve(tr_, [], 0, 2);
|
||||
var dl = TCAD.parametric.solve(dl_, [], 0, 5);
|
||||
|
||||
console.log("bfgs: " + bfgs.errorSquare());
|
||||
// console.log("bfgs: " + bfgs.errorSquare());
|
||||
console.log("lm: " + lm.errorSquare());
|
||||
console.log("trusted region: " + tr.errorSquare());
|
||||
// console.log("trusted region: " + tr.errorSquare());
|
||||
console.log("dog leg: " + dl.errorSquare());
|
||||
|
||||
}
|
||||
|
||||
function lsolve() {
|
||||
var n = numeric;
|
||||
A = [[1, 0, 0], [2,3,0]]
|
||||
b = [10,25]
|
||||
var At = n.transpose(A);
|
||||
var res = n.dot(n.dot(At, n.inv(n.dot(A, At)) ), b);
|
||||
console.log(res);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@
|
|||
</style>
|
||||
<script> TCAD = {} </script>
|
||||
<script src="lib/three/three.js"></script>
|
||||
<script src="lib/numeric-1.2.6.js"></script>
|
||||
<script src="app/engine.js"></script>
|
||||
<script src="app/canvas.js"></script>
|
||||
<script src="app/math/math.js"></script>
|
||||
<script src="app/math/matrix.js"></script>
|
||||
<script src="app/math/optim.js"></script>
|
||||
<script src="app/math/noptim.js"></script>
|
||||
|
||||
<script src="app/math/lm.js"></script>
|
||||
<script src="app/constr/constraints.js"></script>
|
||||
|
|
@ -29,7 +31,8 @@
|
|||
new TCAD.App2D();
|
||||
}
|
||||
window.onload = function() {
|
||||
testCompare();
|
||||
testCompare();
|
||||
lsolve();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
|||
Loading…
Reference in a new issue