NewcombOperators.java

  1. /* Copyright 2002-2022 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.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.hipparchus.analysis.polynomials.PolynomialFunction;
  24. import org.hipparchus.util.FastMath;

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

  53.     /** Storage map. */
  54.     private static final Map<NewKey, Double> MAP = new TreeMap<>((k1, k2) -> {
  55.         if (k1.n == k2.n) {
  56.             if (k1.s == k2.s) {
  57.                 if (k1.rho == k2.rho) {
  58.                     if (k1.sigma < k2.sigma) {
  59.                         return  -1;
  60.                     } else if (k1.sigma == k2.sigma) {
  61.                         return 0;
  62.                     } else {
  63.                         return 1;
  64.                     }
  65.                 } else if (k1.rho < k2.rho) {
  66.                     return -1;
  67.                 } else {
  68.                     return 1;
  69.                 }
  70.             } else if (k1.s < k2.s) {
  71.                 return -1;
  72.             } else {
  73.                 return 1;
  74.             }
  75.         } else if (k1.n < k2.n) {
  76.             return -1;
  77.         }
  78.         return 1;
  79.     });

  80.     /** Private constructor as class is a utility.
  81.      */
  82.     private NewcombOperators() {
  83.     }

  84.     /** Get the Newcomb operator evaluated at n, s, ρ, σ.
  85.      * <p>
  86.      * This method is guaranteed to be thread-safe
  87.      * </p>
  88.      *  @param rho ρ index
  89.      *  @param sigma σ index
  90.      *  @param n n index
  91.      *  @param s s index
  92.      *  @return Y<sub>ρ,σ</sub><sup>n,s</sup>
  93.      */
  94.     public static double getValue(final int rho, final int sigma, final int n, final int s) {

  95.         final NewKey key = new NewKey(n, s, rho, sigma);
  96.         synchronized (MAP) {
  97.             if (MAP.containsKey(key)) {
  98.                 return MAP.get(key);
  99.             }
  100.         }

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

  103.         // Compute the value from the list of polynomials for the given n and s
  104.         double nPower = 1.;
  105.         double value = 0.0;
  106.         for (final PolynomialFunction polynomial : polynomials) {
  107.             value += polynomial.value(s) * nPower;
  108.             nPower = n * nPower;
  109.         }
  110.         synchronized (MAP) {
  111.             MAP.put(key, value);
  112.         }

  113.         return value;

  114.     }

  115.     /** Generator for Newcomb polynomials. */
  116.     private static class PolynomialsGenerator {

  117.         /** Polynomials storage. */
  118.         private static final SortedMap<Couple, List<PolynomialFunction>> POLYNOMIALS =
  119.                 new TreeMap<>((c1, c2) -> {
  120.                     if (c1.rho == c2.rho) {
  121.                         if (c1.sigma < c2.sigma) {
  122.                             return -1;
  123.                         } else if (c1.sigma == c2.sigma) {
  124.                             return 0;
  125.                         }
  126.                     } else if (c1.rho < c2.rho) {
  127.                         return -1;
  128.                     }
  129.                     return 1;
  130.                 });

  131.         /** Private constructor as class is a utility.
  132.          */
  133.         private PolynomialsGenerator() {
  134.         }

  135.         /** Get the list of polynomials representing the Newcomb Operator for the (ρ,σ) couple.
  136.          * <p>
  137.          * This method is guaranteed to be thread-safe
  138.          * </p>
  139.          *  @param rho ρ value
  140.          *  @param sigma σ value
  141.          *  @return Polynomials representing the Newcomb Operator for the (ρ,σ) couple.
  142.          */
  143.         private static List<PolynomialFunction> getPolynomials(final int rho, final int sigma) {

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

  145.             synchronized (POLYNOMIALS) {

  146.                 if (POLYNOMIALS.isEmpty()) {
  147.                     // Initialize lists
  148.                     final List<PolynomialFunction> l00 = new ArrayList<PolynomialFunction>();
  149.                     final List<PolynomialFunction> l01 = new ArrayList<PolynomialFunction>();
  150.                     final List<PolynomialFunction> l10 = new ArrayList<PolynomialFunction>();
  151.                     final List<PolynomialFunction> l11 = new ArrayList<PolynomialFunction>();

  152.                     // Y(rho = 0, sigma = 0) = 1
  153.                     l00.add(new PolynomialFunction(new double[] {
  154.                         1.
  155.                     }));
  156.                     // Y(rho = 0, sigma = 1) =  -s - n/2
  157.                     l01.add(new PolynomialFunction(new double[] {
  158.                         0, -1.
  159.                     }));
  160.                     l01.add(new PolynomialFunction(new double[] {
  161.                         -0.5
  162.                     }));
  163.                     // Y(rho = 1, sigma = 0) =  s - n/2
  164.                     l10.add(new PolynomialFunction(new double[] {
  165.                         0, 1.
  166.                     }));
  167.                     l10.add(new PolynomialFunction(new double[] {
  168.                         -0.5
  169.                     }));
  170.                     // Y(rho = 1, sigma = 1) = 3/2 - s² + 5n/4 + n²/4
  171.                     l11.add(new PolynomialFunction(new double[] {
  172.                         1.5, 0., -1.
  173.                     }));
  174.                     l11.add(new PolynomialFunction(new double[] {
  175.                         1.25
  176.                     }));
  177.                     l11.add(new PolynomialFunction(new double[] {
  178.                         0.25
  179.                     }));

  180.                     // Initialize polynomials
  181.                     POLYNOMIALS.put(new Couple(0, 0), l00);
  182.                     POLYNOMIALS.put(new Couple(0, 1), l01);
  183.                     POLYNOMIALS.put(new Couple(1, 0), l10);
  184.                     POLYNOMIALS.put(new Couple(1, 1), l11);

  185.                 }

  186.                 // If order hasn't been computed yet, update the Newcomb polynomials
  187.                 if (!POLYNOMIALS.containsKey(couple)) {
  188.                     PolynomialsGenerator.computeFor(rho, sigma);
  189.                 }

  190.                 return POLYNOMIALS.get(couple);

  191.             }
  192.         }

  193.         /** Compute the Modified Newcomb Operators up to a given (ρ, σ) couple.
  194.          *  <p>
  195.          *  The recursive computation uses equation 2.7.3-(12) of the Danielson paper.
  196.          *  </p>
  197.          *  @param rho ρ value to reach
  198.          *  @param sigma σ value to reach
  199.          */
  200.         private static void computeFor(final int rho, final int sigma) {

  201.             // Initialize result :
  202.             List<PolynomialFunction> result = new ArrayList<PolynomialFunction>();

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

  205.             // Compute (s - n) * Y[rho - 2, sigma][n, s + 2]
  206.             if (rho >= 2) {
  207.                 final List<PolynomialFunction> poly = map.get(0);
  208.                 final List<PolynomialFunction> list = getPolynomials(rho - 2, sigma);
  209.                 result = multiplyPolynomialList(poly, shiftList(list, 2));
  210.             }

  211.             // Compute 2(2rho + 2sigma + 2 + 3n) * Y[rho - 1, sigma - 1][n, s]
  212.             if (rho >= 1 && sigma >= 1) {
  213.                 final List<PolynomialFunction> poly = map.get(1);
  214.                 final List<PolynomialFunction> list = getPolynomials(rho - 1, sigma - 1);
  215.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, list));
  216.             }

  217.             // Compute 2(2s - n) * Y[rho - 1, sigma][n, s + 1]
  218.             if (rho >= 1) {
  219.                 final List<PolynomialFunction> poly = map.get(2);
  220.                 final List<PolynomialFunction> list = getPolynomials(rho - 1, sigma);
  221.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, shiftList(list, 1)));
  222.             }

  223.             // Compute -(s + n) * Y[rho, sigma - 2][n, s - 2]
  224.             if (sigma >= 2) {
  225.                 final List<PolynomialFunction> poly = map.get(3);
  226.                 final List<PolynomialFunction> list = getPolynomials(rho, sigma - 2);
  227.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, shiftList(list, -2)));
  228.             }

  229.             // Compute -2(2s + n) * Y[rho, sigma - 1][n, s - 1]
  230.             if (sigma >= 1) {
  231.                 final List<PolynomialFunction> poly = map.get(4);
  232.                 final List<PolynomialFunction> list = getPolynomials(rho, sigma - 1);
  233.                 result = sumPolynomialList(result, multiplyPolynomialList(poly, shiftList(list, -1)));
  234.             }

  235.             // Save polynomials for current (rho, sigma) couple
  236.             final Couple couple = new Couple(rho, sigma);
  237.             POLYNOMIALS.put(couple, result);
  238.         }

  239.         /** Multiply two lists of polynomials defined as the internal representation of the Newcomb Operator.
  240.          *  <p>
  241.          *  Let's call R<sub>s</sub>(n) the result returned by the method :
  242.          *  <pre>
  243.          *  R<sub>s</sub>(n) = (P<sub>s₀</sub> + P<sub>s₁</sub>n + ... + P<sub>s<sub>j</sub></sub>n<sup>j</sup>) *(Q<sub>s₀</sub> + Q<sub>s₁</sub>n + ... + Q<sub>s<sub>k</sub></sub>n<sup>k</sup>)
  244.          *  </pre>
  245.          *
  246.          *  where the P<sub>s<sub>j</sub></sub> and Q<sub>s<sub>k</sub></sub> are polynomials in s,
  247.          *  s being the index of the Y<sub>ρ,σ</sub><sup>n,s</sup> function
  248.          *
  249.          *  @param poly1 first list of polynomials
  250.          *  @param poly2 second list of polynomials
  251.          *  @return R<sub>s</sub>(n) as a list of {@link PolynomialFunction}
  252.          */
  253.         private static List<PolynomialFunction> multiplyPolynomialList(final List<PolynomialFunction> poly1,
  254.                                                                        final List<PolynomialFunction> poly2) {
  255.             // Initialize the result list of polynomial function
  256.             final List<PolynomialFunction> result = new ArrayList<PolynomialFunction>();
  257.             initializeListOfPolynomials(poly1.size() + poly2.size() - 1, result);

  258.             int i = 0;
  259.             // Iterate over first polynomial list
  260.             for (PolynomialFunction f1 : poly1) {
  261.                 // Iterate over second polynomial list
  262.                 for (int j = i; j < poly2.size() + i; j++) {
  263.                     final PolynomialFunction p2 = poly2.get(j - i);
  264.                     // Get previous polynomial for current 'n' order
  265.                     final PolynomialFunction previousP2 = result.get(j);
  266.                     // Replace the current order by summing the product of both of the polynomials
  267.                     result.set(j, previousP2.add(f1.multiply(p2)));
  268.                 }
  269.                 // shift polynomial order in 'n'
  270.                 i++;
  271.             }
  272.             return result;
  273.         }

  274.         /** Sum two lists of {@link PolynomialFunction}.
  275.          *  @param poly1 first list
  276.          *  @param poly2 second list
  277.          *  @return the summed list
  278.          */
  279.         private static List<PolynomialFunction> sumPolynomialList(final List<PolynomialFunction> poly1,
  280.                                                                   final List<PolynomialFunction> poly2) {
  281.             // identify the lowest degree polynomial
  282.             final int lowLength  = FastMath.min(poly1.size(), poly2.size());
  283.             final int highLength = FastMath.max(poly1.size(), poly2.size());
  284.             // Initialize the result list of polynomial function
  285.             final List<PolynomialFunction> result = new ArrayList<PolynomialFunction>();
  286.             initializeListOfPolynomials(highLength, result);

  287.             for (int i = 0; i < lowLength; i++) {
  288.                 // Add polynomials by increasing order of 'n'
  289.                 result.set(i, poly1.get(i).add(poly2.get(i)));
  290.             }
  291.             // Complete the list if lists are of different size:
  292.             for (int i = lowLength; i < highLength; i++) {
  293.                 if (poly1.size() < poly2.size()) {
  294.                     result.set(i, poly2.get(i));
  295.                 } else {
  296.                     result.set(i, poly1.get(i));
  297.                 }
  298.             }
  299.             return result;
  300.         }

  301.         /** Initialize an empty list of polynomials.
  302.          *  @param i order
  303.          *  @param result list into which polynomials should be added
  304.          */
  305.         private static void initializeListOfPolynomials(final int i,
  306.                                                         final List<PolynomialFunction> result) {
  307.             for (int k = 0; k < i; k++) {
  308.                 result.add(new PolynomialFunction(new double[] {0.}));
  309.             }
  310.         }

  311.         /** Shift a list of {@link PolynomialFunction}.
  312.          *
  313.          *  @param polynomialList list of {@link PolynomialFunction}
  314.          *  @param shift shift value
  315.          *  @return new list of shifted {@link PolynomialFunction}
  316.          */
  317.         private static List<PolynomialFunction> shiftList(final List<PolynomialFunction> polynomialList,
  318.                                                           final int shift) {
  319.             final List<PolynomialFunction> shiftedList = new ArrayList<PolynomialFunction>();
  320.             for (PolynomialFunction function : polynomialList) {
  321.                 shiftedList.add(new PolynomialFunction(shift(function.getCoefficients(), shift)));
  322.             }
  323.             return shiftedList;
  324.         }

  325.         /**
  326.          * Compute the coefficients of the polynomial \(P_s(x)\)
  327.          * whose values at point {@code x} will be the same as the those from the
  328.          * original polynomial \(P(x)\) when computed at {@code x + shift}.
  329.          * <p>
  330.          * More precisely, let \(\Delta = \) {@code shift} and let
  331.          * \(P_s(x) = P(x + \Delta)\).  The returned array
  332.          * consists of the coefficients of \(P_s\).  So if \(a_0, ..., a_{n-1}\)
  333.          * are the coefficients of \(P\), then the returned array
  334.          * \(b_0, ..., b_{n-1}\) satisfies the identity
  335.          * \(\sum_{i=0}^{n-1} b_i x^i = \sum_{i=0}^{n-1} a_i (x + \Delta)^i\) for all \(x\).
  336.          * </p>
  337.          * <p>
  338.          * This method is a modified version of the method with the same name
  339.          * in Hipparchus {@code PolynomialsUtils} class, simply changing
  340.          * computation of binomial coefficients so degrees higher than 66 can be used.
  341.          * </p>
  342.          *
  343.          * @param coefficients Coefficients of the original polynomial.
  344.          * @param shift Shift value.
  345.          * @return the coefficients \(b_i\) of the shifted
  346.          * polynomial.
  347.          */
  348.         public static double[] shift(final double[] coefficients,
  349.                                      final double shift) {
  350.             final int dp1 = coefficients.length;
  351.             final double[] newCoefficients = new double[dp1];

  352.             // Pascal triangle.
  353.             final double[][] coeff = new double[dp1][dp1];
  354.             coeff[0][0] = 1;
  355.             for (int i = 1; i < dp1; i++) {
  356.                 coeff[i][0] = 1;
  357.                 for (int j = 1; j < i; j++) {
  358.                     coeff[i][j] = coeff[i - 1][j - 1] + coeff[i - 1][j];
  359.                 }
  360.                 coeff[i][i] = 1;
  361.             }

  362.             // First polynomial coefficient.
  363.             double shiftI = 1;
  364.             for (int i = 0; i < dp1; i++) {
  365.                 newCoefficients[0] += coefficients[i] * shiftI;
  366.                 shiftI *= shift;
  367.             }

  368.             // Superior order.
  369.             final int d = dp1 - 1;
  370.             for (int i = 0; i < d; i++) {
  371.                 double shiftJmI = 1;
  372.                 for (int j = i; j < d; j++) {
  373.                     newCoefficients[i + 1] += coeff[j + 1][j - i] * coefficients[j + 1] * shiftJmI;
  374.                     shiftJmI *= shift;
  375.                 }
  376.             }

  377.             return newCoefficients;
  378.         }

  379.         /** Generate recurrence coefficients.
  380.          *
  381.          *  @param rho ρ value
  382.          *  @param sigma σ value
  383.          *  @return recurrence coefficients
  384.          */
  385.         private static Map<Integer, List<PolynomialFunction>> generateRecurrenceCoefficients(final int rho, final int sigma) {
  386.             final double den   = 1. / (4. * (rho + sigma));
  387.             final double denx2 = 2. * den;
  388.             final double denx4 = 4. * den;
  389.             // Initialization :
  390.             final Map<Integer, List<PolynomialFunction>> list = new TreeMap<Integer, List<PolynomialFunction>>();
  391.             final List<PolynomialFunction> poly0 = new ArrayList<PolynomialFunction>();
  392.             final List<PolynomialFunction> poly1 = new ArrayList<PolynomialFunction>();
  393.             final List<PolynomialFunction> poly2 = new ArrayList<PolynomialFunction>();
  394.             final List<PolynomialFunction> poly3 = new ArrayList<PolynomialFunction>();
  395.             final List<PolynomialFunction> poly4 = new ArrayList<PolynomialFunction>();
  396.             // (s - n)
  397.             poly0.add(new PolynomialFunction(new double[] {0., den}));
  398.             poly0.add(new PolynomialFunction(new double[] {-den}));
  399.             // 2(2 * rho + 2 * sigma + 2 + 3*n)
  400.             poly1.add(new PolynomialFunction(new double[] {1. + denx4}));
  401.             poly1.add(new PolynomialFunction(new double[] {denx2 + denx4}));
  402.             // 2(2s - n)
  403.             poly2.add(new PolynomialFunction(new double[] {0., denx4}));
  404.             poly2.add(new PolynomialFunction(new double[] {-denx2}));
  405.             // - (s + n)
  406.             poly3.add(new PolynomialFunction(new double[] {0., -den}));
  407.             poly3.add(new PolynomialFunction(new double[] {-den}));
  408.             // - 2(2s + n)
  409.             poly4.add(new PolynomialFunction(new double[] {0., -denx4}));
  410.             poly4.add(new PolynomialFunction(new double[] {-denx2}));

  411.             // Fill the map :
  412.             list.put(0, poly0);
  413.             list.put(1, poly1);
  414.             list.put(2, poly2);
  415.             list.put(3, poly3);
  416.             list.put(4, poly4);
  417.             return list;
  418.         }

  419.     }

  420.     /** Private class to define a couple of value. */
  421.     private static class Couple {

  422.         /** first couple value. */
  423.         private final int rho;

  424.         /** second couple value. */
  425.         private final int sigma;

  426.         /** Constructor.
  427.          * @param rho first couple value
  428.          * @param sigma second couple value
  429.          */
  430.         private Couple(final int rho, final int sigma) {
  431.             this.rho = rho;
  432.             this.sigma = sigma;
  433.         }

  434.     }

  435.     /** Newcomb operator's key. */
  436.     private static class NewKey {

  437.         /** n value. */
  438.         private final int n;

  439.         /** s value. */
  440.         private final int s;

  441.         /** ρ value. */
  442.         private final int rho;

  443.         /** σ value. */
  444.         private final int sigma;

  445.         /** Simpleconstructor.
  446.          * @param n n
  447.          * @param s s
  448.          * @param rho ρ
  449.          * @param sigma σ
  450.          */
  451.         NewKey(final int n, final int s, final int rho, final int sigma) {
  452.             this.n = n;
  453.             this.s = s;
  454.             this.rho = rho;
  455.             this.sigma = sigma;
  456.         }

  457.     }

  458. }