package
SLAM.PoseGraph.ModelicaPoseGraphCovariance
Public declarations
Explicit source declarations; dimensions, modifications and defaults are shown as written, without evaluation.
| Role | Name | Type | Dimensions / attributes / default | Description |
|---|---|---|---|---|
| constant | nodeCapacity | Integer | =128 | |
| constant | edgeCapacity | Integer | =256 | |
| constant | poseDimension | Integer | =6 | |
| constant | selectedDimension | Integer | =12 | |
| constant | maximumIterations | Integer | =96 | |
| constant | pairDimension | Integer | = 2*poseDimension | |
| constant | pairCapacity | Integer | = div(nodeCapacity,2) |
Members
| Name | Kind | Description |
|---|---|---|
| Result | record | |
| Tree | record | |
| Coarse | record | |
| EmptyCoarse | function | |
| Empty | function | |
| Inverse6 | function | |
| BuildTree | function | |
| Backward | function | |
| TreeSolve | function | |
| BuildCoarse | function | |
| BalancedSolve | function | |
| Pairs | record | |
| EmptyPairs | function | |
| FactorPair | function | |
| BuildPairs | function | |
| PairSolve | function | |
| BalancedPairSolve | function | |
| Select | function |
Modelica declarations and implementation
Show parameters, interfaces and source
Parameters, inputs, outputs, defaults and equations from the source file.
package ModelicaPoseGraphCovariance
import PGCholesky = SLAM.PoseGraph.PGCholesky;
import PGDot = SLAM.PoseGraph.PGDot;
import PGEdge = SLAM.PoseGraph.PGEdge;
import PGNormalProduct = SLAM.PoseGraph.PGNormalProduct;
import PGSkew = SLAM.PoseGraph.PGSkew;
import PGSolveBlock = SLAM.PoseGraph.PGSolveBlock;
import PGValidateGraph = SLAM.PoseGraph.PGValidateGraph;
constant Integer nodeCapacity=128;
constant Integer edgeCapacity=256;
constant Integer poseDimension=6;
constant Integer selectedDimension=12;
constant Integer maximumIterations=96;
record Result
Real upper[selectedDimension,selectedDimension];
Real lower[selectedDimension,selectedDimension];
Real residualUpper[selectedDimension,selectedDimension];
Boolean accepted;
Integer status "0 idle;1 numerical result;-1 config/selection;-2 graph;-3 tree/pivot;-4 chart;-5 arithmetic";
Integer iterations[selectedDimension];
Boolean converged[selectedDimension];
Real residualNorm[selectedDimension];
Integer treeEdges;
Integer activeNodes;
Boolean roundoffCertified "Always false: no directed-rounding enclosure in this profile";
end Result;
record Tree
Integer parent[nodeCapacity];
Integer order[nodeCapacity];
Integer count;
Real childInverse[nodeCapacity,poseDimension,poseDimension];
Real parentJacobian[nodeCapacity,poseDimension,poseDimension];
Real weightFactor[nodeCapacity,poseDimension,poseDimension];
Boolean valid;
end Tree;
record Coarse
Real basis[nodeCapacity,poseDimension,poseDimension];
Real product[nodeCapacity,poseDimension,poseDimension];
Real factor[poseDimension,poseDimension];
Boolean valid;
end Coarse;
function EmptyCoarse
output Coarse coarse;
algorithm
coarse.basis:=zeros(nodeCapacity,poseDimension,poseDimension);
coarse.product:=zeros(nodeCapacity,poseDimension,poseDimension);
coarse.factor:=zeros(poseDimension,poseDimension); coarse.valid:=false;
end EmptyCoarse;
function Empty
output Result result;
algorithm
result.upper:=zeros(selectedDimension,selectedDimension);
result.lower:=zeros(selectedDimension,selectedDimension);
result.residualUpper:=zeros(selectedDimension,selectedDimension);
result.accepted:=false; result.status:=0;
result.iterations:=fill(0,selectedDimension); result.converged:=fill(false,selectedDimension);
result.residualNorm:=zeros(selectedDimension); result.treeEdges:=0; result.activeNodes:=0;
result.roundoffCertified:=false;
end Empty;
// Checked small general inverse. Child Jacobians are not SPD matrices.
// The inverse closure is a numerical rank diagnostic, not rounded interval proof.
function Inverse6
input Real A[poseDimension,poseDimension];
output Real inverse[poseDimension,poseDimension];
output Boolean valid;
protected
Real work[poseDimension,poseDimension]; Real scale; Real pivot; Real swap;
Real largest; Real factor; Real closure; Integer selected;
algorithm
inverse:=identity(poseDimension); work:=A; scale:=0.0; valid:=true;
pivot:=0.0; swap:=0.0; largest:=0.0; factor:=0.0; closure:=0.0; selected:=1;
for row in 1:poseDimension loop
for column in 1:poseDimension loop
valid:=valid and abs(A[row,column])<=1e18;
scale:=max(scale,abs(A[row,column]));
end for;
end for;
valid:=valid and scale>1e-12;
for column in 1:poseDimension loop
if valid then
selected:=column; largest:=abs(work[column,column]);
for row in 1:poseDimension loop
if row>column and abs(work[row,column])>largest then
selected:=row; largest:=abs(work[row,column]);
end if;
end for;
valid:=largest>1e-12*scale and largest<=1e18;
if valid then
for k in 1:poseDimension loop
swap:=work[column,k]; work[column,k]:=work[selected,k]; work[selected,k]:=swap;
swap:=inverse[column,k]; inverse[column,k]:=inverse[selected,k]; inverse[selected,k]:=swap;
end for;
pivot:=work[column,column];
for k in 1:poseDimension loop
work[column,k]:=work[column,k]/pivot; inverse[column,k]:=inverse[column,k]/pivot;
end for;
for row in 1:poseDimension loop
if row<>column then
factor:=work[row,column];
for k in 1:poseDimension loop
work[row,k]:=work[row,k]-factor*work[column,k];
inverse[row,k]:=inverse[row,k]-factor*inverse[column,k];
end for;
end if;
end for;
end if;
end if;
end for;
for row in 1:poseDimension loop
for column in 1:poseDimension loop
closure:=sum(inverse[row,k]*A[k,column] for k in 1:poseDimension)
-(if row==column then 1.0 else 0.0);
valid:=valid and abs(closure)<1e-8 and abs(inverse[row,column])<=1e18;
end for;
end for;
if not valid then inverse:=zeros(poseDimension,poseDimension); end if;
end Inverse6;
function BuildTree
input Real nodeMask[nodeCapacity]; input Real edgeMask[edgeCapacity];
input Integer source[edgeCapacity]; input Integer target[edgeCapacity];
input Real Ji[edgeCapacity,poseDimension,poseDimension];
input Real Jj[edgeCapacity,poseDimension,poseDimension];
input Real information[edgeCapacity,poseDimension,poseDimension];
output Tree tree;
protected
Boolean reached[nodeCapacity]; Boolean layer[nodeCapacity]; Boolean inverseValid; Boolean weightValid;
Integer parent; Integer child; Real childJacobian[poseDimension,poseDimension];
Real parentJacobian[poseDimension,poseDimension]; Real inverse[poseDimension,poseDimension];
Real weight[poseDimension,poseDimension];
algorithm
tree.parent:=fill(0,nodeCapacity); tree.order:=fill(1,nodeCapacity); tree.count:=0;
tree.childInverse:=zeros(nodeCapacity,poseDimension,poseDimension);
tree.parentJacobian:=zeros(nodeCapacity,poseDimension,poseDimension);
tree.weightFactor:=zeros(nodeCapacity,poseDimension,poseDimension); tree.valid:=true;
reached:=fill(false,nodeCapacity); reached[1]:=true; layer:=reached; parent:=1; child:=1;
childJacobian:=zeros(poseDimension,poseDimension); parentJacobian:=childJacobian;
inverse:=childJacobian; weight:=childJacobian; inverseValid:=false; weightValid:=false;
for pass in 1:nodeCapacity loop
// Only vertices reached before this pass can parent new vertices. Keeping
// the discovery layer fixed avoids selecting an entire long chain before
// any shortcut edge is examined. Original edge order breaks equal-depth
// ties; every factor remains in the full undamped information operator.
layer:=reached;
for edge in 1:edgeCapacity loop
if tree.valid and edgeMask[edge]==1.0 then
parent:=1; child:=1;
if layer[source[edge]] and not reached[target[edge]] then
parent:=source[edge]; child:=target[edge];
childJacobian:=Jj[edge,:,:]; parentJacobian:=Ji[edge,:,:];
elseif layer[target[edge]] and not reached[source[edge]] then
parent:=target[edge]; child:=source[edge];
childJacobian:=Ji[edge,:,:]; parentJacobian:=Jj[edge,:,:];
end if;
if child>1 then
(inverse,inverseValid):=Inverse6(childJacobian);
(weight,weightValid):=PGCholesky(information[edge,:,:],1e-10);
tree.valid:=inverseValid and weightValid and tree.count<nodeCapacity-1;
if tree.valid then
reached[child]:=true; tree.count:=tree.count+1;
tree.order[tree.count]:=child; tree.parent[child]:=parent;
tree.childInverse[child,:,:]:=inverse;
tree.parentJacobian[child,:,:]:=parentJacobian;
tree.weightFactor[child,:,:]:=weight;
end if;
end if;
end if;
end for;
end for;
for node in 1:nodeCapacity loop tree.valid:=tree.valid and (nodeMask[node]==0.0 or reached[node]); end for;
end BuildTree;
// T' q=f, reversed discovery order, retaining each edge's original direction.
function Backward
input Real f[nodeCapacity,poseDimension]; input Tree tree;
output Real q[nodeCapacity,poseDimension];
protected
Real pending[nodeCapacity,poseDimension]; Integer node; Integer parent;
algorithm
q:=zeros(nodeCapacity,poseDimension); pending:=f; pending[1,:]:=zeros(poseDimension);
node:=1; parent:=1;
for reverseIndex in 1:nodeCapacity loop
if reverseIndex<=tree.count then
node:=tree.order[tree.count+1-reverseIndex]; parent:=tree.parent[node];
q[node,:]:=transpose(tree.childInverse[node,:,:])*pending[node,:];
if parent>1 then pending[parent,:]:=pending[parent,:]-transpose(tree.parentJacobian[node,:,:])*q[node,:]; end if;
end if;
end for;
end Backward;
function TreeSolve
input Real f[nodeCapacity,poseDimension]; input Tree tree;
output Real x[nodeCapacity,poseDimension];
protected
Real q[nodeCapacity,poseDimension]; Real w[poseDimension]; Integer node;
algorithm
q:=Backward(f,tree); x:=zeros(nodeCapacity,poseDimension); w:=zeros(poseDimension); node:=1;
for index in 1:nodeCapacity loop
if index<=tree.count then
node:=tree.order[index]; w:=PGSolveBlock(tree.weightFactor[node,:,:],q[node,:]);
x[node,:]:=tree.childInverse[node,:,:]*(w-tree.parentJacobian[node,:,:]*x[tree.parent[node],:]);
end if;
end for;
end TreeSolve;
// Six global rigid-motion directions in the graph's world-p/right-local-angle
// chart. The root and inactive rows stay zero; column normalization changes
// the basis, not its span. H is the same full undamped operator used by PCG.
function BuildCoarse
input Real position[nodeCapacity,3]; input Real rotation[nodeCapacity,3,3];
input Real nodeMask[nodeCapacity]; input Real edgeMask[edgeCapacity];
input Integer source[edgeCapacity]; input Integer target[edgeCapacity];
input Real Ji[edgeCapacity,poseDimension,poseDimension];
input Real Jj[edgeCapacity,poseDimension,poseDimension];
input Real information[edgeCapacity,poseDimension,poseDimension];
output Coarse coarse;
protected
Real skew[3,3]; Real square; Real E[poseDimension,poseDimension]; Real value;
Boolean factorValid;
algorithm
coarse:=EmptyCoarse(); skew:=zeros(3,3); square:=0.0;
E:=zeros(poseDimension,poseDimension); value:=0.0; factorValid:=false; coarse.valid:=true;
for node in 1:nodeCapacity loop
if node>1 and nodeMask[node]==1.0 then
skew:=PGSkew(position[node,:]-position[1,:]);
for axis in 1:3 loop
coarse.basis[node,axis,axis]:=1.0;
for column in 1:3 loop
coarse.basis[node,axis,column+3]:=-skew[axis,column];
coarse.basis[node,axis+3,column+3]:=rotation[node,column,axis];
end for;
end for;
end if;
end for;
for column in 1:poseDimension loop
square:=PGDot(coarse.basis[:,:,column],coarse.basis[:,:,column]);
coarse.valid:=coarse.valid and square>1e-24 and square<=1e100;
if coarse.valid then coarse.basis[:,:,column]:=coarse.basis[:,:,column]/sqrt(square); end if;
end for;
if coarse.valid then
for column in 1:poseDimension loop
coarse.product[:,:,column]:=PGNormalProduct(coarse.basis[:,:,column],nodeMask,edgeMask,
source,target,Ji,Jj,information,zeros(nodeCapacity,poseDimension),0.0);
end for;
for row in 1:poseDimension loop
for column in 1:poseDimension loop
value:=0.5*(PGDot(coarse.basis[:,:,row],coarse.product[:,:,column])
+PGDot(coarse.basis[:,:,column],coarse.product[:,:,row]));
E[row,column]:=value; coarse.valid:=coarse.valid and abs(value)<=1e100;
end for;
end for;
if coarse.valid then
(coarse.factor,factorValid):=PGCholesky(E,1e-12); coarse.valid:=factorValid;
end if;
end if;
if not coarse.valid then coarse:=EmptyCoarse(); end if;
end BuildCoarse;
// C=ZKZ'+(I-ZKZ'H) TreeInverse (I-HZKZ'), K=(Z'HZ)^-1.
// HZ is precomputed; no full H application occurs in this helper. The same
// fixed factor is used on both sides, preserving the symmetric SPD contract.
function BalancedSolve
input Real f[nodeCapacity,poseDimension]; input Tree tree; input Coarse coarse;
output Real x[nodeCapacity,poseDimension];
protected
Real coefficients[poseDimension]; Real correction[poseDimension];
Real pending[nodeCapacity,poseDimension]; Real work[nodeCapacity,poseDimension];
algorithm
coefficients:=zeros(poseDimension); correction:=zeros(poseDimension);
pending:=f; work:=zeros(nodeCapacity,poseDimension); x:=work;
for column in 1:poseDimension loop coefficients[column]:=PGDot(coarse.basis[:,:,column],f); end for;
coefficients:=PGSolveBlock(coarse.factor,coefficients);
for column in 1:poseDimension loop pending:=pending-coarse.product[:,:,column]*coefficients[column]; end for;
work:=TreeSolve(pending,tree);
for column in 1:poseDimension loop correction[column]:=PGDot(coarse.product[:,:,column],work); end for;
correction:=PGSolveBlock(coarse.factor,correction);
x:=work;
for column in 1:poseDimension loop x:=x+coarse.basis[:,:,column]*(coefficients[column]-correction[column]); end for;
end BalancedSolve;
constant Integer pairDimension = 2*poseDimension;
constant Integer pairCapacity = div(nodeCapacity,2);
record Pairs
Real factor[pairCapacity,pairDimension,pairDimension];
Boolean active[nodeCapacity];
Boolean valid;
end Pairs;
function EmptyPairs
output Pairs pairs;
algorithm
pairs.factor := zeros(pairCapacity,pairDimension,pairDimension);
pairs.active := fill(false,nodeCapacity); pairs.valid := false;
end EmptyPairs;
function FactorPair
input Real A[pairDimension,pairDimension];
output Real L[pairDimension,pairDimension]; output Boolean valid;
protected Real scale; Real value;
algorithm
L := zeros(pairDimension,pairDimension); scale := 0.0; value := 0.0; valid := true;
for i in 1:pairDimension loop scale := max(scale,abs(A[i,i])); end for;
valid := scale > 1e-12 and scale <= 1e18;
for i in 1:pairDimension loop
for j in 1:pairDimension loop
valid := valid and abs(A[i,j]) <= 1e18 and abs(A[i,j]-A[j,i]) <= 1e-10*max(1.0,scale);
value := A[i,j];
for k in 1:pairDimension loop
if j <= i and k < j then value := value-L[i,k]*L[j,k]; end if;
end for;
if j <= i then
if i == j then
valid := valid and value > 1e-12*scale and value <= 1e18;
L[i,j] := sqrt(if value > 1e-12*scale and value <= 1e18 then value else 1.0);
else L[i,j] := value/L[j,j]; end if;
end if;
end for;
end for;
if not valid then L := zeros(pairDimension,pairDimension); end if;
end FactorPair;
// Principal full-H blocks on free rows (2,3),(4,5),...,(128,padding).
// Every active factor contributes diagonal terms; within-pair factors also
// contribute both cross blocks. This changes only the numerical preconditioner.
function BuildPairs
input Real nodeMask[nodeCapacity]; input Real edgeMask[edgeCapacity];
input Integer source[edgeCapacity]; input Integer target[edgeCapacity];
input Real Ji[edgeCapacity,poseDimension,poseDimension];
input Real Jj[edgeCapacity,poseDimension,poseDimension];
input Real information[edgeCapacity,poseDimension,poseDimension];
output Pairs pairs;
protected
Real blocks[pairCapacity,pairDimension,pairDimension]; Real normalBlock[poseDimension,poseDimension];
Real left[poseDimension,poseDimension]; Real right[poseDimension,poseDimension];
Real L[pairDimension,pairDimension]; Real paddingScale; Boolean factorValid;
Integer a; Integer b; Integer pa; Integer pb; Integer oa; Integer ob; Integer node;
algorithm
pairs := EmptyPairs(); pairs.valid := true;
blocks := zeros(pairCapacity,pairDimension,pairDimension); normalBlock := zeros(poseDimension,poseDimension);
left := normalBlock; right := normalBlock; L := zeros(pairDimension,pairDimension); factorValid := false;
paddingScale := 1.0; a := 1; b := 1; pa := 1; pb := 1; oa := 0; ob := 0; node := 1;
for i in 1:nodeCapacity loop
pairs.valid := pairs.valid and (nodeMask[i] == 0.0 or nodeMask[i] == 1.0);
pairs.active[i] := i > 1 and nodeMask[i] == 1.0;
end for;
for edge in 1:edgeCapacity loop
pairs.valid := pairs.valid and (edgeMask[edge] == 0.0 or edgeMask[edge] == 1.0);
if pairs.valid and edgeMask[edge] == 1.0 then
a := source[edge]; b := target[edge];
pairs.valid := a >= 1 and a <= nodeCapacity and b >= 1 and b <= nodeCapacity and a <> b;
if pairs.valid then pairs.valid := nodeMask[a] == 1.0 and nodeMask[b] == 1.0; end if;
if pairs.valid then
for sideA in 1:2 loop
for sideB in 1:2 loop
a := if sideA == 1 then source[edge] else target[edge];
b := if sideB == 1 then source[edge] else target[edge];
if a > 1 and b > 1 then
pa := div(a-2,2)+1; pb := div(b-2,2)+1;
if pa == pb then
oa := mod(a-2,2)*poseDimension; ob := mod(b-2,2)*poseDimension;
left := if sideA == 1 then Ji[edge,:,:] else Jj[edge,:,:];
right := if sideB == 1 then Ji[edge,:,:] else Jj[edge,:,:];
normalBlock := transpose(left)*information[edge,:,:]*right;
for i in 1:poseDimension loop for j in 1:poseDimension loop
blocks[pa,oa+i,ob+j] := blocks[pa,oa+i,ob+j]+normalBlock[i,j];
end for; end for;
end if;
end if;
end for;
end for;
end if;
end if;
end for;
if pairs.valid then
for pair in 1:pairCapacity loop
paddingScale := 0.0;
for row in 1:pairDimension loop
node := 2+2*(pair-1)+div(row-1,poseDimension);
if node <= nodeCapacity then
if pairs.active[node] then paddingScale := max(paddingScale,abs(blocks[pair,row,row])); end if;
end if;
end for;
if paddingScale == 0.0 then paddingScale := 1.0; end if;
for row in 1:pairDimension loop
node := 2+2*(pair-1)+div(row-1,poseDimension);
// Inactive and out-of-range padding never read numerical input cells.
if node > nodeCapacity then blocks[pair,row,row] := paddingScale;
elseif not pairs.active[node] then blocks[pair,row,row] := paddingScale; end if;
end for;
(L,factorValid) := FactorPair(blocks[pair,:,:]); pairs.valid := pairs.valid and factorValid;
pairs.factor[pair,:,:] := L;
end for;
end if;
if not pairs.valid then pairs := EmptyPairs(); end if;
end BuildPairs;
function PairSolve
input Real f[nodeCapacity,poseDimension]; input Pairs pairs;
output Real x[nodeCapacity,poseDimension];
protected Real z[pairDimension]; Real w[pairDimension]; Real value; Integer node; Integer axis; Integer row;
algorithm
x := zeros(nodeCapacity,poseDimension); z := zeros(pairDimension); w := z;
value := 0.0; node := 1; axis := 1; row := 1;
if pairs.valid then
for pair in 1:pairCapacity loop
z := zeros(pairDimension); w := z;
for i in 1:pairDimension loop
node := 2+2*(pair-1)+div(i-1,poseDimension); axis := mod(i-1,poseDimension)+1;
value := 0.0;
if node <= nodeCapacity then if pairs.active[node] then value := f[node,axis]; end if; end if;
for k in 1:pairDimension loop if k < i then value := value-pairs.factor[pair,i,k]*z[k]; end if; end for;
z[i] := value/pairs.factor[pair,i,i];
end for;
for i in 1:pairDimension loop
row := pairDimension+1-i; value := z[row];
for k in 1:pairDimension loop if k > row then value := value-pairs.factor[pair,k,row]*w[k]; end if; end for;
w[row] := value/pairs.factor[pair,row,row];
end for;
for i in 1:pairDimension loop
node := 2+2*(pair-1)+div(i-1,poseDimension); axis := mod(i-1,poseDimension)+1;
if node <= nodeCapacity then if pairs.active[node] then x[node,axis] := w[i]; end if; end if;
end for;
end for;
end if;
end PairSolve;
function BalancedPairSolve
input Real f[nodeCapacity,poseDimension]; input Pairs pairs; input Coarse coarse;
output Real x[nodeCapacity,poseDimension];
protected Real coefficients[poseDimension]; Real correction[poseDimension];
Real pending[nodeCapacity,poseDimension]; Real work[nodeCapacity,poseDimension];
algorithm
coefficients := zeros(poseDimension); correction := zeros(poseDimension);
pending := f; work := zeros(nodeCapacity,poseDimension); x := work;
for column in 1:poseDimension loop coefficients[column] := PGDot(coarse.basis[:,:,column],f); end for;
coefficients := PGSolveBlock(coarse.factor,coefficients);
for column in 1:poseDimension loop pending := pending-coarse.product[:,:,column]*coefficients[column]; end for;
work := PairSolve(pending,pairs);
for column in 1:poseDimension loop correction[column] := PGDot(coarse.product[:,:,column],work); end for;
correction := PGSolveBlock(coarse.factor,correction);
x := work;
for column in 1:poseDimension loop x := x+coarse.basis[:,:,column]*(coefficients[column]-correction[column]); end for;
end BalancedPairSolve;
function Select
input Real position[nodeCapacity,3]; input Real rotation[nodeCapacity,3,3];
input Real nodeMask[nodeCapacity]; input Real edgeMask[edgeCapacity];
input Real fromNode[edgeCapacity]; input Real toNode[edgeCapacity];
input Real translation[edgeCapacity,3]; input Real measuredRotation[edgeCapacity,3,3];
input Real information[edgeCapacity,poseDimension,poseDimension] "Already scaled by PrepareProblem; no second inflation";
input Integer currentNode; input Integer referenceNode;
input Integer maximumPCG=48; input Real tolerance=1e-10; input Boolean requested=true;
output Result result;
protected
Integer source[edgeCapacity]; Integer target[edgeCapacity]; Integer selected[selectedDimension];
Real Ji[edgeCapacity,poseDimension,poseDimension]; Real Jj[edgeCapacity,poseDimension,poseDimension];
Real B[nodeCapacity,poseDimension,selectedDimension]; Real X[nodeCapacity,poseDimension,selectedDimension];
Real HX[nodeCapacity,poseDimension,selectedDimension]; Real residual[nodeCapacity,poseDimension,selectedDimension];
Real whitened[nodeCapacity,poseDimension,selectedDimension];
Real r[nodeCapacity,poseDimension]; Real z[nodeCapacity,poseDimension];
Real direction[nodeCapacity,poseDimension]; Real product[nodeCapacity,poseDimension];
Real q[nodeCapacity,poseDimension]; Real edgeResidual[poseDimension];
Real edgeJi[poseDimension,poseDimension]; Real edgeJj[poseDimension,poseDimension];
Real rho; Real nextRho; Real curvature; Real alpha; Real beta; Real initialNorm;
Real norm; Real value; Real graphStatus; Real activeNodes; Real activeEdges;
Boolean valid; Boolean chartValid; Boolean running; Boolean coarseEnabled;
Tree tree; Coarse coarse; Pairs pairs;
algorithm
result:=Empty(); source:=fill(1,edgeCapacity); target:=source; selected:=fill(1,selectedDimension);
Ji:=zeros(edgeCapacity,poseDimension,poseDimension); Jj:=Ji;
B:=zeros(nodeCapacity,poseDimension,selectedDimension); X:=B; HX:=B; residual:=B; whitened:=B;
r:=zeros(nodeCapacity,poseDimension); z:=r; direction:=r; product:=r; q:=r;
edgeJi:=zeros(poseDimension,poseDimension); edgeJj:=edgeJi; edgeResidual:=zeros(poseDimension);
rho:=0.0; nextRho:=0.0; curvature:=0.0; alpha:=0.0; beta:=0.0; initialNorm:=0.0;
norm:=0.0; value:=0.0; graphStatus:=0.0; activeNodes:=0.0; activeEdges:=0.0;
valid:=false; chartValid:=false; running:=false; coarseEnabled:=false; coarse:=EmptyCoarse(); pairs:=EmptyPairs();
if requested then
result.status:=-1;
valid:=currentNode>=1 and currentNode<=nodeCapacity and referenceNode>=1 and referenceNode<=nodeCapacity
and maximumPCG>=0 and maximumPCG<=maximumIterations and tolerance>=1e-14 and tolerance<=1e-2;
if valid then valid:=nodeMask[currentNode]==1.0 and nodeMask[referenceNode]==1.0; end if;
if valid then
(source,target,graphStatus,activeNodes,activeEdges):=PGValidateGraph(position,rotation,nodeMask,
edgeMask,fromNode,toNode,translation,measuredRotation,information);
result.status:=-2; valid:=graphStatus==1.0;
end if;
if valid then
result.status:=-4;
for edge in 1:edgeCapacity loop
if edgeMask[edge]==1.0 then
(edgeResidual,edgeJi,edgeJj,chartValid):=PGEdge(position[source[edge],:],rotation[source[edge],:,:],
position[target[edge],:],rotation[target[edge],:,:],translation[edge,:],measuredRotation[edge,:,:]);
Ji[edge,:,:]:=edgeJi; Jj[edge,:,:]:=edgeJj;
valid:=valid and chartValid;
for row in 1:poseDimension loop
for column in 1:poseDimension loop
valid:=valid and information[edge,row,column]==information[edge,column,row]
and abs(Ji[edge,row,column])<=1e18 and abs(Jj[edge,row,column])<=1e18;
end for;
end for;
end if;
end for;
end if;
if valid then
result.status:=-3; tree:=BuildTree(nodeMask,edgeMask,source,target,Ji,Jj,information); valid:=tree.valid;
end if;
if valid then
// Preserve X=0/tree-only majorant when no PCG step is requested. With
// only the root active there is no free subspace or coarse rank to test.
coarseEnabled:=maximumPCG>0 and activeNodes>1.0;
if coarseEnabled then
coarse:=BuildCoarse(position,rotation,nodeMask,edgeMask,source,target,Ji,Jj,information); valid:=coarse.valid;
if valid then pairs:=BuildPairs(nodeMask,edgeMask,source,target,Ji,Jj,information); valid:=pairs.valid; end if;
end if;
end if;
if valid then
result.status:=-5;
for column in 1:selectedDimension loop
selected[column]:=if column<=poseDimension then currentNode else referenceNode;
if selected[column]>1 then B[selected[column],mod(column-1,poseDimension)+1,column]:=1.0; end if;
r:=B[:,:,column]; z:=if coarseEnabled then BalancedPairSolve(r,pairs,coarse) else TreeSolve(r,tree); direction:=z;
rho:=PGDot(r,z); initialNorm:=PGDot(r,r);
valid:=valid and rho>=0.0 and rho<=1e100;
running:=valid and initialNorm>0.0;
for iteration in 1:maximumIterations loop
if running and iteration<=maximumPCG then
product:=PGNormalProduct(direction,nodeMask,edgeMask,source,target,Ji,Jj,information,zeros(nodeCapacity,poseDimension),0.0);
curvature:=PGDot(direction,product); valid:=curvature>0.0 and curvature<=1e100 and rho>0.0;
if valid then
alpha:=rho/curvature; X[:,:,column]:=X[:,:,column]+alpha*direction;
r:=r-alpha*product; norm:=PGDot(r,r); result.iterations[column]:=iteration;
valid:=norm>=0.0 and norm<=1e100;
running:=valid and norm>tolerance*tolerance*initialNorm;
if running then
z:=if coarseEnabled then BalancedPairSolve(r,pairs,coarse) else TreeSolve(r,tree); nextRho:=PGDot(r,z); valid:=nextRho>0.0 and nextRho<=1e100;
if valid then beta:=nextRho/rho; direction:=z+beta*direction; rho:=nextRho; else running:=false; end if;
end if;
else running:=false;
end if;
end if;
end for;
HX[:,:,column]:=PGNormalProduct(X[:,:,column],nodeMask,edgeMask,source,target,Ji,Jj,information,zeros(nodeCapacity,poseDimension),0.0);
residual[:,:,column]:=B[:,:,column]-HX[:,:,column];
norm:=PGDot(residual[:,:,column],residual[:,:,column]);
result.residualNorm[column]:=sqrt(max(0.0,norm));
result.converged[column]:=norm<=tolerance*tolerance*initialNorm;
q:=Backward(residual[:,:,column],tree);
for node in 1:nodeCapacity loop
if node>1 and nodeMask[node]==1.0 then
// Only L_W^-1 q is needed for the full residual Gram upper bound.
for axis in 1:poseDimension loop
value:=q[node,axis];
for k in 1:poseDimension loop
if k<axis then value:=value-tree.weightFactor[node,axis,k]*whitened[node,k,column]; end if;
end for;
whitened[node,axis,column]:=value/tree.weightFactor[node,axis,axis];
end for;
end if;
end for;
end for;
for row in 1:selectedDimension loop
for column in 1:selectedDimension loop
result.lower[row,column]:=PGDot(B[:,:,row],X[:,:,column])+PGDot(X[:,:,row],B[:,:,column])
-0.5*(PGDot(X[:,:,row],HX[:,:,column])+PGDot(HX[:,:,row],X[:,:,column]));
result.residualUpper[row,column]:=PGDot(whitened[:,:,row],whitened[:,:,column]);
result.upper[row,column]:=result.lower[row,column]+result.residualUpper[row,column];
valid:=valid and abs(result.lower[row,column])<=1e100
and abs(result.residualUpper[row,column])<=1e100 and abs(result.upper[row,column])<=1e100;
end for;
end for;
if valid then
result.accepted:=true; result.status:=1; result.treeEdges:=tree.count; result.activeNodes:=integer(activeNodes);
end if;
end if;
if not valid then
graphStatus:=result.status; result:=Empty(); result.status:=integer(graphStatus);
end if;
end if;
end Select;
end ModelicaPoseGraphCovariance;