Published
Edited
Feb 10, 2019
9 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
output = {
//** Generate an Algebra with x dual components. **//
var Dualgebra = (x)=>Algebra({
basis : [...Array(x+1)].map((a,i)=>i?'e0'+i:'1'), metric : [1,...Array(x)],
Cayley : [...Array(x+1)].map((a,i)=>[...Array(x+1)].map((y,j)=>i*j==0?((i+j)?'e0'+(i+j):'1'):'0'))
});
//** We'll use one with three dual components, and a clifford algebra over R3 **//
var D3 = Dualgebra(3), R3 = Algebra(3);
//** helper .. easily convert scalars or arrays of scalars to dual vectors. **//
var d3 = (a,b,c,d)=>(a instanceof Array)?a.map(x=>d3(x)):(a instanceof D3)?a:new D3([a||0,b||0,c||0,d||0]);
//** We upgrade the operators of R3 we need to work with D3 coefficients. This is where the functions of R3 **//
//** get translated to work with Dual Vectors for each of their coefficients. **//
var translate = (x)=>{ var F=D3.inline(R3.prototype[x]); return (a,b)=>F.call(a,b); },
[GP,ADD,SUB,SCALE] = ["Mul","Add","Sub","Scale"].map(x=>translate(x));
//** We implement our own exp function using these operators and Taylor **//
var EXP = (X)=>{
var R=d3([1,...Array(X.length-1)]), M, y=1, i=1;
while (i<25) { R=ADD(R, SCALE(M||X, 1/y)); M=GP(M||X,X); y=y*(++i); }
return R;
};

//** random float between -x and x, vector length, rotate, cost function (avg of squared diffs) **//
var r = (x)=>(Math.random()*2-1)*(x||1),
VLEN = R=>R[1].Mul(R[1]).Add(R[2].Mul(R[2])).Add(R[3].Mul(R[3])),
ROT = (R,x)=>GP(GP(EXP(SCALE(R,-0.5)),x),EXP(SCALE(R,0.5))),
COST = (G,Pa,Pb)=>Pa.reduce((C,Pa,x)=>C.Add(VLEN(SUB(ROT(G,Pa),Pb[x]))),d3()).Scale(1/Pa.length);
//** Now we generate some random points, a rotation bivector, the rotated noisy points and our guess+dBV **//
//** For our guess, Each of the coefficients we wish to track (e12,e13,e23) gets a different dual unit added in. **//
var points_a = [...Array(30)].map(x=>[0,r(),r(),r(),0,0,0,0]),
bv = d3([0,0,0,0,r(),r(),r(),0]),
points_b = points_a.map(x=>ADD(ROT(bv,x),[0,r(.01),r(.01),r(.01),0,0,0,0]).map(x=>x[0])),
guess = d3([0,0,0,0,d3(0,1),d3(0,0,1),d3(0,0,0,1),0]);
//** Now we implement Gradient Descent - step in the direction of negative cost **//
var start = performance.now();
for (var alpha=1,i=0; i<100; i++) {
var c = COST(guess,points_a,points_b); if (c[0]<5E-4) break;
guess = ADD(guess,[0,0,0,0,-c[1]*alpha,-c[2]*alpha,-c[3]*alpha,0])
}
var stop = performance.now();
//** We are done.. code below is graphing results.. **//
var graph = Algebra(3,0,1).inline((pa,pb,i,guess)=>{
// convert the arrays to 3D PGA points.
var conv = x=>x.map(x=>!(x[1][0]*1e1+(x[2][0])*1e2+x[3][0]*1e3+1e0));
var trail = (bv,x,n=12)=>[...Array(n).keys()].map(i=>Math.E**(bv*(i/(n-1)))>>>x).map((x,i,a)=>[x,a[i-1]||x]).slice(1);
// initial camera and items to render.
pa = conv(pa);
var trails = []; pa.forEach(pa=>trails.push(...trail(guess*[-.5e12,-.5e13,-.5e23], pa)));
var camera = Math.E**1e23,f=0, items=[...pa,0x88FF0000,...conv(pb),0x880000FF];
// graph in gl, animate and wiggle the camera
return this.graph(()=>{
camera.set(Math.E**(2e03)*Math.E**(Math.sin(performance.now()/5000)*1e13));
var anim = Math.E**((Math.sin(performance.now()/234)*0.5+.5)*( guess*[-.5e12,-.5e13,-.5e23] ));
// nice .. in PGA, we can just render the bivector and get the expected line..
return [0xAAAAFF,...trails,0x0000FF,guess*[1e12,1e13,1e23],0,...items,...anim>>>pa] ;
},{grid:1,camera,animate:1,gl:1,width:'100%',height:'400px'});
})(d3(points_a),d3(points_b),i,guess.map(x=>x[0]).slice(4,7));
//** Also output some statistics. **//
var text = md`
* Nr of iterations : ${i}
* Nr of points : ${points_a.length}
* Time : ${(stop-start)/1000}
* Final Error : ${c[0]}
* Initial guess : 0.0,0.0,0.0
* Final estimate : ${guess[4][0].toFixed(4)},${guess[5][0].toFixed(4)},${guess[6][0].toFixed(4)}
* Ground truth : ${bv[4][0].toFixed(4)},${bv[5][0].toFixed(4)},${bv[6][0].toFixed(4)}
* GT Error (noise) : ${COST(bv,points_a,points_b)[0]}`;
return {graph,text}
}
Insert cell
Algebra = require('ganja.js')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more