NewcombOperators.java

  1. /* Copyright 2002-2013 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.propagation.semianalytical.dsst.utilities;

  18. import java.util.ArrayList;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.SortedMap;
  22. import java.util.TreeMap;

  23. import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
  24. import org.apache.commons.math3.analysis.polynomials.PolynomialsUtils;
  25. import org.apache.commons.math3.util.FastMath;

  26. /** Implementation of the Modified Newcomb Operators.
  27.  *  <p>
  28.  *  From equations 2.7.3 - (12)(13) of the Danielson paper, those operators are defined as:
  29.  *  <pre>
  30.  *  4(&rho; + &sigma;)Y<sub>&rho;,&sigma;</sub><sup>n,s</sup> =
  31.  *     2(2s - n)Y<sub>&rho;-1,&sigma;</sub><sup>n,s+1</sup> + (s - n)Y<sub>&rho;-2,&sigma;</sub><sup>n,s+2</sup>
  32.  *   - 2(2s + n)Y<sub>&rho;,&sigma;-1</sub><sup>n,s-1</sup> - (s+n)Y<sub>&rho;,&sigma;-2</sub><sup>n,s-2</sup>
  33.  *   + 2(2&rho; + 2&sigma; + 2 + 3n)Y<sub>&rho;-1,&sigma;-1</sub><sup>n,s</sup>
  34.  *  </pre>
  35.  *  Initialization is given by : <pre>Y<sub>0,0</sub><sup>n,s</sup> = 1</pre>
  36.  *  </p>
  37.  *
  38.  *  Internally, the Modified Newcomb Operators are stored as an array of {@link PolynomialFunction} :
  39.  *
  40.  *  <pre>
  41.  *  Y<sub>&rho;,&sigma;</sub><sup>n,s</sup> = P<sub>k<sub>0</sub></sub> + P<sub>k<sub>1</sub></sub>n + ... + P<sub>k<sub>j</sub></sub>n<sup>j</sup>
  42.  *  </pre>
  43.  *
  44.  * where the P<sub>k<sub>j</sub></sub> are given by
  45.  *
  46.  * <pre>
  47.  *  P<sub>k<sub>j</sub></sub> = &sum;<sub>j=0;&rho;</sub> a<sub>j</sub>s<sup>j</sup>
  48.  * </pre>
  49.  *
  50.  * @author Romain Di Costanzo
  51.  * @author Pascal Parraud
  52.  */
  53. public class NewcombOperators {

  54.     /** Storage map. */
  55.     private static final Map<NewKey, Double> MAP = new TreeMap<NewKey, Double>();

  56.     /** Private constructor as class is a utility.
  57.      */
  58.     private NewcombOperators() {
  59.     }

  60.     /** Get the Newcomb operator evaluated at n, s, &rho;, &sigma;.
  61.      * <p>
  62.      * This method is guaranteed to be thread-safe
  63.      * </p>
  64.      *  @param rho &rho; index
  65.      *  @param sigma &sigma; index
  66.      *  @param n n index
  67.      *  @param s s index
  68.      *  @return Y<sub>&rho;,&sigma;</sub><sup>n,s</sup>
  69.      */
  70.     public static double getValue(final int rho, final int sigma, final int n, final int s) {

  71.         final NewKey key = new NewKey(n, s, rho, sigma);
  72.         synchronized (MAP) {
  73.             if (MAP.containsKey(key)) {
  74.                 return MAP.get(key);
  75.             }
  76.         }

  77.         // Get the Newcomb polynomials for the given rho and sigma
  78.         final List<PolynomialFunction> polynomials = PolynomialsGenerator.getPolynomials(rho, sigma);

  79.         // Compute the value from the list of polynomials for the given n and s
  80.         double nPower = 1.;
  81.         double value = 0.0;
  82.         for (final PolynomialFunction polynomial : polynomials) {
  83.             value += polynomial.value(s) * nPower;
  84.             nPower = n * nPower;
  85.         }
  86.         synchronized (MAP) {
  87.             MAP.put(key, value);
  88.         }

  89.         return value;

  90.     }

  91.     /** Generator for Newcomb polynomials. */
  92.     private static class PolynomialsGenerator {

  93.         /** Polynomials storage. */
  94.         private static final SortedMap<Couple, List<PolynomialFunction>> POLYNOMIALS =
  95.                 new TreeMap<Couple, List<PolynomialFunction>>();

  96.         /** Private constructor as class is a utility.
  97.          */
  98.         private PolynomialsGenerator() {
  99.         }

  100.         /** Get the list of polynomials representing the Newcomb Operator for the (&rho;,&sigma;) couple.
  101.          * <p>
  102.          * This method is guaranteed to be thread-safe
  103.          * </p>
  104.          *  @param rho &rho; value
  105.          *  @param sigma &sigma; value
  106.          *  @return Polynomials representing the Newcomb Operator for the (&rho;,&sigma;) couple.
  107.          */
  108.         private static List<PolynomialFunction> getPolynomials(final int rho, final int sigma) {

  109.             final Couple couple = new Couple(rho, sigma);

  110.             synchronized (POLYNOMIALS) {

  111.                 if (POLYNOMIALS.isEmpty()) {
  112.                     // Initialize lists
  113.                     final List<PolynomialFunction> l00 = new ArrayList<PolynomialFunction>();
  114.                     final List<PolynomialFunction> l01 = new ArrayList<PolynomialFunction>();
  115.                     final List<PolynomialFunction> l10 = new ArrayList<PolynomialFunction>();
  116.                     final List<PolynomialFunction> l11 = new ArrayList<PolynomialFunction>();

  117.                     // Y(rho = 0, sigma = 0) = 1
  118.                     l00.add(new PolynomialFunction(new double[] {
  119.                         1.
  120.                     }));
  121.                     // Y(rho = 0, sigma = 1) =  -s - n/2
  122.                     l01.add(new PolynomialFunction(new double[] {
  123.                         0, -1.
  124.                     }));
  125.                     l01.add(new PolynomialFunction(new double[] {
  126.                         -0.5
  127.                     }));
  128.                     // Y(rho = 1, sigma = 0) =  s - n/2
  129.                     l10.add(new PolynomialFunction(new double[] {
  130.                         0, 1.
  131.                     }));
  132.                     l10.add(new PolynomialFunction(new double[] {
  133.                         -0.5
  134.                     }));
  135.                     // Y(rho = 1, sigma = 1) = 3/2 - s² + 5n/4 + n²/4
  136.                     l11.add(new PolynomialFunction(new double[] {
  137.                         1.5, 0., -1.
  138.                     }));
  139.                     l11.add(new PolynomialFunction(new double[] {
  140.                         1.25
  141.                     }));
  142.                     l11.add(new PolynomialFunction(new double[] {
  143.                         0.25
  144.                     }));

  145.                     // Initialize polynomials
  146.                     POLYNOMIALS.put(new Couple(0, 0), l00);
  147.                     POLYNOMIALS.put(new Couple(0, 1), l01);
  148.                     POLYNOMIALS.put(new Couple(1, 0), l10);
  149.                     POLYNOMIALS.put(new Couple(1, 1), l11);

  150.                 }

  151.                 // If order hasn't been computed yet, update the Newcomb polynomials
  152.                 if (!POLYNOMIALS.containsKey(couple)) {
  153.                     PolynomialsGenerator.computeFor(rho, sigma);
  154.                 }

  155.                 return POLYNOMIALS.get(couple);

  156.             }
  157.         }

  158.         /** Compute the Modified Newcomb Operators up to a given (&rho;, &sigma;) couple.
  159.          *  <p>
  160.          *  The recursive computation uses equation 2.7.3-(12) of the Danielson paper.
  161.          *  </p>
  162.          *  @param rho &rho; value to reach
  163.          *  @param sigma &sigma; value to reach
  164.          */
  165.         private static void computeFor(final int rho, final int sigma) {

  166.             // Initialize result :
  167.             List<PolynomialFunction> result = new ArrayList<PolynomialFunction>();

  168.             // Get the coefficient from the recurrence relation
  169.             final Map<Integer, List<PolynomialFunction>> map = generateRecurrenceCoefficients(rho, sigma);

  170.             // Compute (s - n) * Y[rho - 2, sigma][n, s + 2]
  171.             if (rho >= 2) {
  172.                 final List<PolynomialFunction> poly = map.get(0);
  173.                 final List<PolynomialFunction> list = getPolynomials(rho - 2, sigma);
  174.                 result = multiplyPolynomialList(poly, shiftList(list, 2));
  175.             }

  176.             // Compute 2(2rho + 2sigma + 2 + 3n) * Y[rho - 1, sigma - 1][n, s]
  177.             if (rho >= 1 && sigma >= 1) {
  178.                 final List<PolynomialFunction> poly = map.get(1);
  179.                 final List<PolynomialFunction> list = getPolynomials(rho - 1, sigma - 1);
  180.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, list));
  181.             }

  182.             // Compute 2(2s - n) * Y[rho - 1, sigma][n, s + 1]
  183.             if (rho >= 1) {
  184.                 final List<PolynomialFunction> poly = map.get(2);
  185.                 final List<PolynomialFunction> list = getPolynomials(rho - 1, sigma);
  186.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, shiftList(list, 1)));
  187.             }

  188.             // Compute -(s + n) * Y[rho, sigma - 2][n, s - 2]
  189.             if (sigma >= 2) {
  190.                 final List<PolynomialFunction> poly = map.get(3);
  191.                 final List<PolynomialFunction> list = getPolynomials(rho, sigma - 2);
  192.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, shiftList(list, -2)));
  193.             }

  194.             // Compute -2(2s + n) * Y[rho, sigma - 1][n, s - 1]
  195.             if (sigma >= 1) {
  196.                 final List<PolynomialFunction> poly = map.get(4);
  197.                 final List<PolynomialFunction> list = getPolynomials(rho, sigma - 1);
  198.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, shiftList(list, -1)));
  199.             }

  200.             // Save polynomials for current (rho, sigma) couple
  201.             final Couple couple = new Couple(rho, sigma);
  202.             POLYNOMIALS.put(couple, result);
  203.         }

  204.         /** Multiply two lists of polynomials defined as the internal representation of the Newcomb Operator.
  205.          *  <p>
  206.          *  Let's call R<sub>s</sub>(n) the result returned by the method :
  207.          *  <pre>
  208.          *  R<sub>s</sub>(n) = (P<sub>s<sub>0</sub></sub> + P<sub>s<sub>1</sub></sub>n + ... + P<sub>s<sub>j</sub></sub>n<sup>j</sup>) *(Q<sub>s<sub>0</sub></sub> + Q<sub>s<sub>1</sub></sub>n + ... + Q<sub>s<sub>k</sub></sub>n<sup>k</sup>)
  209.          *  </pre>
  210.          *
  211.          *  where the P<sub>s<sub>j</sub></sub> and Q<sub>s<sub>k</sub></sub> are polynomials in s,
  212.          *  s being the index of the Y<sub>&rho;,&sigma;</sub><sup>n,s</sup> function
  213.          *
  214.          *  @param poly1 first list of polynomials
  215.          *  @param poly2 second list of polynomials
  216.          *  @return R<sub>s</sub>(n) as a list of {@link PolynomialFunction}
  217.          */
  218.         private static List<PolynomialFunction> multiplyPolynomialList(final List<PolynomialFunction> poly1,
  219.                                                                        final List<PolynomialFunction> poly2) {
  220.             // Initialize the result list of polynomial function
  221.             final List<PolynomialFunction> result = new ArrayList<PolynomialFunction>();
  222.             initializeListOfPolynomials(poly1.size() + poly2.size() - 1, result);

  223.             int i = 0;
  224.             // Iterate over first polynomial list
  225.             for (PolynomialFunction f1 : poly1) {
  226.                 // Iterate over second polynomial list
  227.                 for (int j = i; j < poly2.size() + i; j++) {
  228.                     final PolynomialFunction p2 = poly2.get(j - i);
  229.                     // Get previous polynomial for current 'n' order
  230.                     final PolynomialFunction previousP2 = result.get(j);
  231.                     // Replace the current order by summing the product of both of the polynomials
  232.                     result.set(j, previousP2.add(f1.multiply(p2)));
  233.                 }
  234.                 // shift polynomial order in 'n'
  235.                 i++;
  236.             }
  237.             return result;
  238.         }

  239.         /** Sum two lists of {@link PolynomialFunction}.
  240.          *  @param poly1 first list
  241.          *  @param poly2 second list
  242.          *  @return the summed list
  243.          */
  244.         private static List<PolynomialFunction> sumPolynomialList(final List<PolynomialFunction> poly1,
  245.                                                                   final List<PolynomialFunction> poly2) {
  246.             // identify the lowest degree polynomial
  247.             final int lowLength  = FastMath.min(poly1.size(), poly2.size());
  248.             final int highLength = FastMath.max(poly1.size(), poly2.size());
  249.             // Initialize the result list of polynomial function
  250.             final List<PolynomialFunction> result = new ArrayList<PolynomialFunction>();
  251.             initializeListOfPolynomials(highLength, result);

  252.             for (int i = 0; i < lowLength; i++) {
  253.                 // Add polynomials by increasing order of 'n'
  254.                 result.set(i, poly1.get(i).add(poly2.get(i)));
  255.             }
  256.             // Complete the list if lists are of different size:
  257.             for (int i = lowLength; i < highLength; i++) {
  258.                 if (poly1.size() < poly2.size()) {
  259.                     result.set(i, poly2.get(i));
  260.                 } else {
  261.                     result.set(i, poly1.get(i));
  262.                 }
  263.             }
  264.             return result;
  265.         }

  266.         /** Initialize an empty list of polynomials.
  267.          *  @param i order
  268.          *  @param result list into which polynomials should be added
  269.          */
  270.         private static void initializeListOfPolynomials(final int i,
  271.                                                         final List<PolynomialFunction> result) {
  272.             for (int k = 0; k < i; k++) {
  273.                 result.add(new PolynomialFunction(new double[] {0.}));
  274.             }
  275.         }

  276.         /** Shift a list of {@link PolynomialFunction}, from the
  277.          *  {@link PolynomialsUtils#shift(double[], double)} method.
  278.          *
  279.          *  @param polynomialList list of {@link PolynomialFunction}
  280.          *  @param shift shift value
  281.          *  @return new list of shifted {@link PolynomialFunction}
  282.          */
  283.         private static List<PolynomialFunction> shiftList(final List<PolynomialFunction> polynomialList,
  284.                                                           final int shift) {
  285.             final List<PolynomialFunction> shiftedList = new ArrayList<PolynomialFunction>();
  286.             for (PolynomialFunction function : polynomialList) {
  287.                 shiftedList.add(new PolynomialFunction(PolynomialsUtils.shift(function.getCoefficients(), shift)));
  288.             }
  289.             return shiftedList;
  290.         }

  291.         /** Generate recurrence coefficients.
  292.          *
  293.          *  @param rho &rho; value
  294.          *  @param sigma &sigma; value
  295.          *  @return recurrence coefficients
  296.          */
  297.         private static Map<Integer, List<PolynomialFunction>> generateRecurrenceCoefficients(final int rho, final int sigma) {
  298.             final double den   = 1. / (4. * (rho + sigma));
  299.             final double denx2 = 2. * den;
  300.             final double denx4 = 4. * den;
  301.             // Initialization :
  302.             final Map<Integer, List<PolynomialFunction>> list = new TreeMap<Integer, List<PolynomialFunction>>();
  303.             final List<PolynomialFunction> poly0 = new ArrayList<PolynomialFunction>();
  304.             final List<PolynomialFunction> poly1 = new ArrayList<PolynomialFunction>();
  305.             final List<PolynomialFunction> poly2 = new ArrayList<PolynomialFunction>();
  306.             final List<PolynomialFunction> poly3 = new ArrayList<PolynomialFunction>();
  307.             final List<PolynomialFunction> poly4 = new ArrayList<PolynomialFunction>();
  308.             // (s - n)
  309.             poly0.add(new PolynomialFunction(new double[] {0., den}));
  310.             poly0.add(new PolynomialFunction(new double[] {-den}));
  311.             // 2(2 * rho + 2 * sigma + 2 + 3*n)
  312.             poly1.add(new PolynomialFunction(new double[] {1. + denx4}));
  313.             poly1.add(new PolynomialFunction(new double[] {denx2 + denx4}));
  314.             // 2(2s - n)
  315.             poly2.add(new PolynomialFunction(new double[] {0., denx4}));
  316.             poly2.add(new PolynomialFunction(new double[] {-denx2}));
  317.             // - (s + n)
  318.             poly3.add(new PolynomialFunction(new double[] {0., -den}));
  319.             poly3.add(new PolynomialFunction(new double[] {-den}));
  320.             // - 2(2s + n)
  321.             poly4.add(new PolynomialFunction(new double[] {0., -denx4}));
  322.             poly4.add(new PolynomialFunction(new double[] {-denx2}));

  323.             // Fill the map :
  324.             list.put(0, poly0);
  325.             list.put(1, poly1);
  326.             list.put(2, poly2);
  327.             list.put(3, poly3);
  328.             list.put(4, poly4);
  329.             return list;
  330.         }

  331.     }

  332.     /** Private class to define a couple of value. */
  333.     private static class Couple implements Comparable<Couple> {

  334.         /** first couple value. */
  335.         private final int rho;

  336.         /** second couple value. */
  337.         private final int sigma;

  338.         /** Constructor.
  339.          * @param rho first couple value
  340.          * @param sigma second couple value
  341.          */
  342.         private Couple(final int rho, final int sigma) {
  343.             this.rho = rho;
  344.             this.sigma = sigma;
  345.         }

  346.         /** {@inheritDoc} */
  347.         public int compareTo(final Couple c) {
  348.             int result = 1;
  349.             if (rho == c.rho) {
  350.                 if (sigma < c.sigma) {
  351.                     result = -1;
  352.                 } else if (sigma == c.sigma) {
  353.                     result = 0;
  354.                 }
  355.             } else if (rho < c.rho) {
  356.                 result = -1;
  357.             }
  358.             return result;
  359.         }

  360.         /** {@inheritDoc} */
  361.         public boolean equals(final Object couple) {

  362.             if (couple == this) {
  363.                 // first fast check
  364.                 return true;
  365.             }

  366.             if ((couple != null) && (couple instanceof Couple)) {
  367.                 return (rho == ((Couple) couple).rho) && (sigma == ((Couple) couple).sigma);
  368.             }

  369.             return false;

  370.         }

  371.         /** {@inheritDoc} */
  372.         public int hashCode() {
  373.             return 0x7ab17c0c ^ (rho << 8) ^ sigma;
  374.         }

  375.     }

  376.     /** Newcomb operator's key. */
  377.     private static class NewKey implements Comparable<NewKey> {

  378.         /** n value. */
  379.         private final int n;

  380.         /** s value. */
  381.         private final int s;

  382.         /** &rho; value. */
  383.         private final int rho;

  384.         /** &sigma; value. */
  385.         private final int sigma;

  386.         /** Simpleconstructor.
  387.          * @param n n
  388.          * @param s s
  389.          * @param rho &rho;
  390.          * @param sigma &sigma;
  391.          */
  392.         public NewKey(final int n, final int s, final int rho, final int sigma) {
  393.             this.n = n;
  394.             this.s = s;
  395.             this.rho = rho;
  396.             this.sigma = sigma;
  397.         }

  398.         /** {@inheritDoc} */
  399.         public int compareTo(final NewKey key) {
  400.             int result = 1;
  401.             if (n == key.n) {
  402.                 if (s == key.s) {
  403.                     if (rho == key.rho) {
  404.                         if (sigma < key.sigma) {
  405.                             result = -1;
  406.                         } else if (sigma == key.sigma) {
  407.                             result = 0;
  408.                         } else {
  409.                             result = 1;
  410.                         }
  411.                     } else if (rho < key.rho) {
  412.                         result = -1;
  413.                     } else {
  414.                         result = 1;
  415.                     }
  416.                 } else if (s < key.s) {
  417.                     result = -1;
  418.                 } else {
  419.                     result = 1;
  420.                 }
  421.             } else if (n < key.n) {
  422.                 result = -1;
  423.             }
  424.             return result;
  425.         }

  426.         /** {@inheritDoc} */
  427.         public boolean equals(final Object key) {

  428.             if (key == this) {
  429.                 // first fast check
  430.                 return true;
  431.             }

  432.             if ((key != null) && (key instanceof NewKey)) {
  433.                 return (n     == ((NewKey) key).n) &&
  434.                        (s     == ((NewKey) key).s) &&
  435.                        (rho   == ((NewKey) key).rho) &&
  436.                        (sigma == ((NewKey) key).sigma);
  437.             }

  438.             return false;

  439.         }

  440.         /** {@inheritDoc} */
  441.         public int hashCode() {
  442.             return 0x25baa451 ^ (n << 24) ^ (s << 16) ^ (rho << 8) ^ sigma;
  443.         }

  444.     }

  445. }