/** * For a set of N-1 "fit" parameters, produces N "model" parameters that sum to one. * @param n The number of "model" parameters, N */ public SumToOne(int n) { _set = getSet(n); _n = n; }
@Override public DoubleMatrix apply(DoubleArray theta) { return trans.jacobian(theta); } };
@Override public DoubleArray apply(DoubleArray theta) { return trans.transform(theta); } };
@Test public void inverseTransformTest() { for (int n = 2; n < 13; n++) { double[] theta = new double[n - 1]; for (int j = 0; j < n - 1; j++) { theta[j] = RANDOM.nextDouble() * Math.PI / 2; } SumToOne trans = new SumToOne(n); DoubleArray w = trans.transform(DoubleArray.copyOf(theta)); DoubleArray theta2 = trans.inverseTransform(w); for (int j = 0; j < n - 1; j++) { assertEquals("element " + j + ", of vector length " + n, theta[j], theta2.get(j), 1e-9); } } }
@Test public void transformTest() { for (int n = 2; n < 13; n++) { double[] from = new double[n - 1]; for (int j = 0; j < n - 1; j++) { from[j] = RANDOM.nextDouble() * Math.PI / 2; } SumToOne trans = new SumToOne(n); DoubleArray to = trans.transform(DoubleArray.copyOf(from)); assertEquals(n, to.size()); double sum = 0; for (int i = 0; i < n; i++) { sum += to.get(i); } assertEquals("vector length " + n, 1.0, sum, 1e-9); } }
double[] w = new double[] {0.01, 0.5, 0.3, 0.19 }; final int n = w.length; final SumToOne trans = new SumToOne(n); Function<DoubleArray, DoubleArray> func = new Function<DoubleArray, DoubleArray>() { double[] expected = trans.inverseTransform(w); for (int i = 0; i < n - 1; i++) {
/** * Inverse transform from the N "model" parameters to the N-1 "fit" parameters. * Used mainly to find the start position of a optimisation routine. * * @param modelParms The N "model" parameters. <b>These must sum to one</b> * @return The N-1 "fit" parameters */ public DoubleArray inverseTransform(DoubleArray modelParms) { return DoubleArray.copyOf(inverseTransform(modelParms.toArray())); }
private void cal(double[] cum, double factor, int d, int n, int p1, double[] res) { if (n == 1) { return; } int n1 = n / 2; int n2 = n - n1; double s = (cum[p1 + n1] - cum[p1]) * factor; double c = 1 - s; res[d] = s; cal(cum, factor / s, d + 1, n1, p1, res); cal(cum, factor / c, d + n1, n2, p1 + n1, res); }
final int n = 5; final SumToOne trans = new SumToOne(n); Function<DoubleArray, DoubleArray> func = new Function<DoubleArray, DoubleArray>() { @Override
/** * Inverse transform from the N "model" parameters to the N-1 "fit" parameters. * Used mainly to find the start position of a optimisation routine. * * @param modelParms The N "model" parameters. <b>These must sum to one</b> * @return The N-1 "fit" parameters */ public double[] inverseTransform(double[] modelParms) { ArgChecker.isTrue(modelParms.length == _n, "length of modelParms is {}, but must be {} ", modelParms.length, _n); double[] res = new double[_n - 1]; double[] cum = new double[_n + 1]; double sum = 0.0; for (int i = 0; i < _n; i++) { sum += modelParms[i]; cum[i + 1] = sum; } ArgChecker.isTrue(Math.abs(sum - 1.0) < TOL, "sum of elements is {}. Must be 1.0", sum); cal(cum, 1.0, 0, _n, 0, res); for (int i = 0; i < _n - 1; i++) { res[i] = Math.asin(Math.sqrt(res[i])); } return res; }
@Override public DoubleArray apply(DoubleArray theta) { return trans.transform(theta); } };
/** * The N by N-1 Jacobian matrix between the N "model" parameters (that sum to one) and the N-1 "fit" parameters. * @param fitParms The N-1 "fit" parameters * @return The N by N-1 Jacobian matrix */ public DoubleMatrix jacobian(DoubleArray fitParms) { return DoubleMatrix.copyOf(jacobian(fitParms.toArray())); }
@Test public void setTest() { int n = 7; int[][] sets = SumToOne.getSet(n); assertEquals(n, sets.length); }
/** * Transform from the N-1 "fit" parameters to the N "model" parameters. * @param fitParms The N-1 "fit" parameters * @return The N "model" parameters */ public DoubleArray transform(DoubleArray fitParms) { return DoubleArray.copyOf(transform(fitParms.toArray())); }
@Test public void setTest2() { int n = 13; int[][] sets = SumToOne.getSet(n); assertEquals(n, sets.length); }