ModifiedLambdaMethod.java

  1. /* Copyright 2002-2020 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.estimation.measurements.gnss;

  18. import org.hipparchus.util.FastMath;

  19. /** Decorrelation/reduction engine for Modified LAMBDA method.
  20.  * <p>
  21.  * This class implements Modified Least Square Ambiguity Decorrelation
  22.  * Adjustment (MLAMBDA) method, as described in <a
  23.  * href="https://www.researchgate.net/publication/225518977_MLAMBDA_a_modified_LAMBDA_method_for_integer_least-squares_estimation">
  24.  * A modified LAMBDA method for integer least-squares estimation</a> by X.-W Chang, X. Yang
  25.  * and T. Zhou, Journal of Geodesy 79(9):552-565, DOI: 10.1007/s00190-005-0004-x
  26.  * </p>
  27.  *
  28.  * @see AmbiguitySolver
  29.  * @author David Soulard
  30.  * @since 10.2
  31.  */
  32. public class ModifiedLambdaMethod extends AbstractLambdaMethod {

  33.     /** Compute the LᵀDL factorization with symmetric pivoting decomposition of Q
  34.      * (symmetric definite positive matrix) with a minimum symmetric pivoting: Q = ZᵀLᵀDLZ.
  35.      */
  36.     @Override
  37.     protected void ltdlDecomposition() {
  38.         final double[] diag = getDiagReference();
  39.         final double[] low  = getLowReference();
  40.         final int[] Z = getZInverseTransformationReference();
  41.         final double[] aNew = getDecorrelatedReference();
  42.         for (int  i = 0; i < this.getSize(); i++) {
  43.             Z[zIndex(i, i)] = 0;
  44.         }
  45.         final int n = diag.length;
  46.         final int[] perm = new int[n];
  47.         for (int i = 0; i < n; i++) {
  48.             perm[i] = i;
  49.         }
  50.         for (int k = n - 1; k >= 0; k--) {
  51.             final int q = posMin(diag, k);
  52.             exchangeIntP1WithIntP2(perm, k, q);
  53.             permRowThenCol(low, diag, k, q);
  54.             if (k > 0) {
  55.                 final double Wkk = diag[k];
  56.                 divide(low, Wkk, k);
  57.                 set(low, diag, k);
  58.             }
  59.             exchangeDoubleP1WithDoubleP2(aNew, k, q);
  60.         }
  61.         for (int j = 0; j < n; j++) {
  62.             Z[zIndex(j, perm[j])] = 1;
  63.         }
  64.     }

  65.     /** Perform MLAMBDA reduction.
  66.      */
  67.     @Override
  68.     protected void reduction() {
  69.         final double[] diag = getDiagReference();
  70.         final double[] low  = getLowReference();
  71.         final int n = diag.length;
  72.         int k = getSize() - 2;
  73.         while ( k > -1) {
  74.             final int kp1 = k + 1;
  75.             double tmp = low[lIndex(kp1, k)];
  76.             final double mu = FastMath.rint(tmp);
  77.             if (Math.abs(mu) > 1e-9) {
  78.                 tmp -= mu;
  79.             }
  80.             final double delta = diag[k] + tmp * tmp * diag[kp1];
  81.             if (delta < diag[kp1]) {
  82.                 integerGaussTransformation(kp1, k);
  83.                 if (mu > 0) {
  84.                     for (int i = k + 2; i < n; i++) {
  85.                         integerGaussTransformation(i, k);
  86.                     }
  87.                 }
  88.                 permutation(k, delta);
  89.                 if (k < n - 2) {
  90.                     k++;
  91.                 }
  92.             } else {
  93.                 k--;
  94.             }
  95.         }
  96.     }

  97.     /** {@inheritDoc} */
  98.     @Override
  99.     protected void discreteSearch() {
  100.         //initialization
  101.         final int n                 = getSize();
  102.         final int maxSolutions      = getMaxSolution();
  103.         final double[] diag         = getDiagReference();
  104.         final double[] decorrelated = getDecorrelatedReference();
  105.         final double[] low          = getLowReference();
  106.         final long[] z              = new long[n];
  107.         final double[] zb           = new double[n];
  108.         final double[] y            = new double[n];
  109.         final int[] path            = new int[n];

  110.         for (int i = 0; i < n; i++) {
  111.             path[i] = n - 1;
  112.         }

  113.         final double[] step    = new double[n];
  114.         final double[] dist    = new double[n + 1];
  115.         final double[] lS      = new double[(n * (n - 1)) / 2];
  116.         final double[] dS      = new double[n];
  117.         double maxDist         = Double.POSITIVE_INFINITY;
  118.         int count              = 0;
  119.         boolean endSearch      = false;
  120.         int ulevel             = 0;

  121.         // Determine which level to move to after z(0) is chosen at level 1.
  122.         final int k0;
  123.         if (maxSolutions == 1) {
  124.             k0 = 1;
  125.         }
  126.         else {
  127.             k0 = 0;
  128.         }

  129.         // Initialization at level n
  130.         zb[n - 1] = decorrelated.clone()[n - 1];
  131.         z[n -  1] = (long) FastMath.rint(zb[n - 1]);
  132.         y[n - 1] = zb[n - 1] - z[n - 1];
  133.         step[n - 1] =  sign(y[n - 1]);
  134.         int k = n - 1;
  135.         while (!endSearch) {
  136.             for (int i = ulevel; i <= k - 1; i++) {
  137.                 path[i] = k;
  138.             }
  139.             for (int j = ulevel - 1; j >= 0; j--) {
  140.                 if (path[j] < k) {
  141.                     path[j] = k;
  142.                 } else {
  143.                     break;
  144.                 }
  145.             }
  146.             double newDist = dist[k] + y[k] * y[k] / diag[k];
  147.             while (newDist < maxDist) {
  148.                 // move to level k-1
  149.                 if (k != 0) {
  150.                     k--;
  151.                     dist[k] = newDist;
  152.                     for (int j = path[k]; j > k; j--) {
  153.                         if (j - 1 == k) {
  154.                             //Update diagonal
  155.                             dS[k] = lS[lIndex(j, k)] - y[j] * low[lIndex(j, k)];
  156.                         } else {
  157.                             //Update low triangular part
  158.                             lS[lIndex(j - 1, k)] = lS[lIndex(j, k)] - y[j] * low[lIndex(j, k)];
  159.                         }
  160.                     }

  161.                     zb[k] = decorrelated[k] + dS[k];
  162.                     z[k] =  (long) FastMath.rint(zb[k]);
  163.                     y[k] = zb[k] - z[k];
  164.                     step[k] =  sign(y[k]);
  165.                 } else {
  166.                     //Save the value of one optimum z
  167.                     if (count < (maxSolutions - 1)) {
  168.                         addSolution(z, newDist);
  169.                         count++;
  170.                     //Save the value of one optimum z
  171.                     } else if (count == (maxSolutions - 1)) {
  172.                         addSolution(z, newDist);
  173.                         maxDist = getMaxDistance();
  174.                         count++;
  175.                     //Replace the new solution with the one with the greatest distance
  176.                     } else {
  177.                         removeSolution();
  178.                         addSolution(z, newDist);
  179.                         maxDist = getMaxDistance();
  180.                     }
  181.                     k = k0;
  182.                     z[k] = z[k] + (long) step[k];
  183.                     y[k] = zb[k] - z[k];
  184.                     step[k] = -step[k] -  sign(step[k]);
  185.                 }
  186.                 newDist = dist[k] + y[k] * y[k] / diag[k];
  187.             }
  188.             ulevel = k;
  189.             //exit or move to level k+1
  190.             while (newDist >= maxDist) {
  191.                 if (k == n - 1) {
  192.                     endSearch = true;
  193.                     break;
  194.                 }
  195.                 //move to level k+1
  196.                 k++;
  197.                 //next integer
  198.                 z[k] = z[k] + (long) step[k];
  199.                 y[k] = zb[k] - z[k];
  200.                 step[k] = -step[k] -  sign(step[k]);
  201.                 newDist = dist[k] + y[k] * y[k] / diag[k];
  202.             }
  203.         }
  204.     }

  205.     /** {@inheritDoc} */
  206.     @Override
  207.     protected void inverseDecomposition() {
  208.         // nothing for M-LAMBDA method
  209.     }

  210.     /** Return the position of the smallest element in the diagonal of a matrix given in parameter.
  211.      * @param k the position of the smallest diagonal element
  212.      * @param D an array of double being the diagonal of the covariance matrix
  213.      * @return the position of the smallest element of mat.
  214.      */
  215.     private int posMin(final double[] D, final int k) {
  216.         int q = 0;
  217.         double value = D[0];
  218.         for (int i = 1; i <= k; i++) {
  219.             if (value > D[i]) {
  220.                 value = D[i];
  221.                 q = i;
  222.             }
  223.         }
  224.         return q;
  225.     }

  226.     /** Perform the following operation on the matrix W in parameters.
  227.      * W(1:p-1,1:p-1) = W(1:p-1,1:p-1) - W(p,1:p-1)'*W(p,p)*W(p,1:p-1);
  228.      * @param L array of double being a lower triangular part of the covariance matrix
  229.      * @param D array of double being the diagonal of the covariance matrix
  230.      * @param p integer at which the computation is done
  231.      */
  232.     private void set(final double[] L, final double[] D, final int p) {
  233.         final double d = D[p];
  234.         final double[] row = new double[p];
  235.         for (int i = 0; i < p; i++) {
  236.             row[i] = L[lIndex(p, i)];
  237.         }
  238.         for (int i = 0; i < p; i++) {
  239.             for (int j = 0; j < i; j++) {
  240.                 L[lIndex(i, j)] = L[lIndex(i, j)] - row[i] * row[j] * d;
  241.             }
  242.             D[i] = D[i] - row[i] * row[i] * d;
  243.         }
  244.     }

  245.     /** Perform a permutation between two row and then two column of the covariance matrix.
  246.      * @param L array of double being the lower triangular part of the covariance matrix
  247.      * @param D array of double being the diagonal of the covariance matrix
  248.      * @param p1 integer: position of the permutation
  249.      * @param p2 integer: position of the permutation
  250.      */
  251.     private void permRowThenCol(final double[] L, final double[] D, final int p1, final int p2) {
  252.         final double[] rowP1 = getRow(L, D, p1);
  253.         final double[] rowP2 = getRow(L, D, p2);
  254.         if (p1 > p2) {
  255.             //Update row P2
  256.             for (int j = 0; j < p2; j++) {
  257.                 L[lIndex(p2, j)] = rowP1[j];
  258.             }
  259.             D[p2] = rowP1[p2];
  260.             //Update row p1
  261.             for (int j = 0; j < p1; j++) {
  262.                 L[lIndex(p1, j)] = rowP2[j];
  263.             }
  264.             D[p1] = rowP2[p1];
  265.             final double[] colP1 = getColPmax(L, D, rowP1, p2, p1);
  266.             //Update column P1
  267.             for (int i = p1 + 1; i < D.length; i++) {
  268.                 L[lIndex(i, p1)] = L[lIndex(i, p2)];
  269.             }
  270.             D[p1] = L[lIndex(p1, p2)];
  271.             //Update column P2
  272.             for (int i = p2 + 1; i < D.length; i++) {
  273.                 L[lIndex(i, p2)] = colP1[i];
  274.             }
  275.             D[p2] = colP1[p2];
  276.         } else {
  277.             //Does nothing when p1 <= p2
  278.         }
  279.     }

  280.     /**Get the row of the covariance matrix at the given position (count from 0).
  281.      * @param L lower part of the covariance matrix
  282.      * @param D diagonal part of the covariance matrix
  283.      * @param pos wanted position
  284.      * @return array of double being the row of covariance matrix at given position
  285.      */
  286.     private double[] getRow(final double[] L, final double[] D, final int pos) {
  287.         final double[] row = new double[D.length];
  288.         for (int j = 0; j < pos; j++) {
  289.             row[j] = L[lIndex(pos, j)];
  290.         }
  291.         row[pos] = D[pos];
  292.         for (int j = pos + 1; j < D.length; j++) {
  293.             row[j] = L[lIndex(j, pos)];
  294.         }
  295.         return row;
  296.     }

  297.     /**Getter for column the greatest at the right side.
  298.      * @param L double array being the lower triangular matrix
  299.      * @param D double array being the diagonal matrix
  300.      * @param row double array being the row of the matrix at given position
  301.      * @param pmin position at which we get the column
  302.      * @param pmax other positions
  303.      * @return array of double
  304.      */
  305.     private double[] getColPmax(final double[] L, final double[] D, final double[] row, final int pmin, final int pmax) {
  306.         final double[] col = new double[D.length];
  307.         col[pmin] = row[pmax];
  308.         for (int i = pmin + 1; i < pmax; i++) {
  309.             col[i] = row[i];
  310.         }
  311.         col[pmax] = D[pmax];
  312.         for (int i = pmax + 1; i < D.length; i++) {
  313.             col[i] = L[lIndex(i, pmax)];
  314.         }
  315.         return col;
  316.     }

  317.     /** Perform the following operation:  W(k,1:k-1) = W(k,1:k-1)/W(k,k) where W is the covariance matrix.
  318.      * @param mat lower triangular part of the covaraince matrix
  319.      * @param diag double: value of the covaraicne matrix at psoition (k;k)
  320.      * @param k integer needed
  321.      */
  322.     private void divide(final double[] mat, final double diag, final int k) {
  323.         for (int j = 0; j < k; j++) {
  324.             mat[lIndex(k, j)] = mat[lIndex(k, j)] / diag;
  325.         }
  326.     }

  327.     /**Inverse the position of 2 element of the array in parameters.
  328.      * @param mat array on which change should be done
  329.      * @param p1 position of the first element to change
  330.      * @param p2 position of the second element to change
  331.      * @return
  332.      */
  333.     private void  exchangeIntP1WithIntP2(final int[] mat, final int p1, final int p2) {
  334.         final int[] Z = mat.clone();
  335.         mat[p1] = Z[p2];
  336.         mat[p2] = Z[p1];
  337.     }

  338.     /** On the array of double mat exchange the element at position p1 with the one at position p2.
  339.      * @param mat array on which the exchange is performed
  340.      * @param p1 first position of exchange (0 is the first element)
  341.      * @param p2 second position of exchange (0 is the first element)
  342.      */
  343.     private void exchangeDoubleP1WithDoubleP2(final double[] mat, final int p1, final int p2) {
  344.         final double[] a = mat.clone();
  345.         mat[p1] = a[p2];
  346.         mat[p2] = a[p1];
  347.     }

  348.     /** Return the symbol of parameter a.
  349.      * @param a the double for which we want the want the symbol
  350.      * @return -1.0 if a is lower than or equal to 0 or 1.0 if a is greater than 0
  351.      */
  352.     protected double sign(final double a) {
  353.         return (a <= 0.0) ? -1.0 : ((a > 0.0) ? 1.0 : a);
  354.     }

  355. }