1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF 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 * https://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 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.stat.correlation; 23 24 import org.hipparchus.exception.LocalizedCoreFormats; 25 import org.hipparchus.exception.MathIllegalArgumentException; 26 import org.hipparchus.exception.MathRuntimeException; 27 import org.hipparchus.linear.MatrixUtils; 28 import org.hipparchus.linear.RealMatrix; 29 import org.hipparchus.util.MathUtils; 30 31 /** 32 * Covariance implementation that does not require input data to be 33 * stored in memory. The size of the covariance matrix is specified in the 34 * constructor. Specific elements of the matrix are incrementally updated with 35 * calls to incrementRow() or increment Covariance(). 36 * <p> 37 * This class is based on a paper written by Philippe Pébay: 38 * <a href="http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf"> 39 * Formulas for Robust, One-Pass Parallel Computation of Covariances and 40 * Arbitrary-Order Statistical Moments</a>, 2008, Technical Report SAND2008-6212, 41 * Sandia National Laboratories. 42 * <p> 43 * Note: the underlying covariance matrix is symmetric, thus only the 44 * upper triangular part of the matrix is stored and updated each increment. 45 */ 46 public class StorelessCovariance extends Covariance { 47 48 /** the square covariance matrix (upper triangular part) */ 49 private final StorelessBivariateCovariance[] covMatrix; 50 51 /** dimension of the square covariance matrix */ 52 private final int dimension; 53 54 /** 55 * Create a bias corrected covariance matrix with a given dimension. 56 * 57 * @param dim the dimension of the square covariance matrix 58 */ 59 public StorelessCovariance(final int dim) { 60 this(dim, true); 61 } 62 63 /** 64 * Create a covariance matrix with a given number of rows and columns and the 65 * indicated bias correction. 66 * 67 * @param dim the dimension of the covariance matrix 68 * @param biasCorrected if <code>true</code> the covariance estimate is corrected 69 * for bias, i.e. n-1 in the denominator, otherwise there is no bias correction, 70 * i.e. n in the denominator. 71 */ 72 public StorelessCovariance(final int dim, final boolean biasCorrected) { 73 dimension = dim; 74 covMatrix = new StorelessBivariateCovariance[dimension * (dimension + 1) / 2]; 75 initializeMatrix(biasCorrected); 76 } 77 78 /** 79 * Initialize the internal two-dimensional array of 80 * {@link StorelessBivariateCovariance} instances. 81 * 82 * @param biasCorrected if the covariance estimate shall be corrected for bias 83 */ 84 private void initializeMatrix(final boolean biasCorrected) { 85 for(int i = 0; i < dimension; i++){ 86 for(int j = 0; j < dimension; j++){ 87 setElement(i, j, new StorelessBivariateCovariance(biasCorrected)); 88 } 89 } 90 } 91 92 /** 93 * Returns the index (i, j) translated into the one-dimensional 94 * array used to store the upper triangular part of the symmetric 95 * covariance matrix. 96 * 97 * @param i the row index 98 * @param j the column index 99 * @return the corresponding index in the matrix array 100 */ 101 private int indexOf(final int i, final int j) { 102 return j < i ? i * (i + 1) / 2 + j : j * (j + 1) / 2 + i; 103 } 104 105 /** 106 * Gets the element at index (i, j) from the covariance matrix 107 * @param i the row index 108 * @param j the column index 109 * @return the {@link StorelessBivariateCovariance} element at the given index 110 */ 111 private StorelessBivariateCovariance getElement(final int i, final int j) { 112 return covMatrix[indexOf(i, j)]; 113 } 114 115 /** 116 * Sets the covariance element at index (i, j) in the covariance matrix 117 * @param i the row index 118 * @param j the column index 119 * @param cov the {@link StorelessBivariateCovariance} element to be set 120 */ 121 private void setElement(final int i, final int j, 122 final StorelessBivariateCovariance cov) { 123 covMatrix[indexOf(i, j)] = cov; 124 } 125 126 /** 127 * Get the covariance for an individual element of the covariance matrix. 128 * 129 * @param xIndex row index in the covariance matrix 130 * @param yIndex column index in the covariance matrix 131 * @return the covariance of the given element 132 * @throws MathIllegalArgumentException if the number of observations 133 * in the cell is < 2 134 */ 135 public double getCovariance(final int xIndex, final int yIndex) 136 throws MathIllegalArgumentException { 137 138 return getElement(xIndex, yIndex).getResult(); 139 } 140 141 /** 142 * Increment the covariance matrix with one row of data. 143 * 144 * @param data array representing one row of data. 145 * @throws MathIllegalArgumentException if the length of <code>rowData</code> 146 * does not match with the covariance matrix 147 */ 148 public void increment(final double[] data) 149 throws MathIllegalArgumentException { 150 151 int length = data.length; 152 MathUtils.checkDimension(length, dimension); 153 154 // only update the upper triangular part of the covariance matrix 155 // as only these parts are actually stored 156 for (int i = 0; i < length; i++){ 157 for (int j = i; j < length; j++){ 158 getElement(i, j).increment(data[i], data[j]); 159 } 160 } 161 162 } 163 164 /** 165 * Appends {@code sc} to this, effectively aggregating the computations in {@code sc} 166 * with this. After invoking this method, covariances returned should be close 167 * to what would have been obtained by performing all of the {@link #increment(double[])} 168 * operations in {@code sc} directly on this. 169 * 170 * @param sc externally computed StorelessCovariance to add to this 171 * @throws MathIllegalArgumentException if the dimension of sc does not match this 172 */ 173 public void append(StorelessCovariance sc) throws MathIllegalArgumentException { 174 MathUtils.checkDimension(sc.dimension, dimension); 175 176 // only update the upper triangular part of the covariance matrix 177 // as only these parts are actually stored 178 for (int i = 0; i < dimension; i++) { 179 for (int j = i; j < dimension; j++) { 180 getElement(i, j).append(sc.getElement(i, j)); 181 } 182 } 183 } 184 185 /** 186 * {@inheritDoc} 187 * @throws MathIllegalArgumentException if the number of observations 188 * in a cell is < 2 189 */ 190 @Override 191 public RealMatrix getCovarianceMatrix() throws MathIllegalArgumentException { 192 return MatrixUtils.createRealMatrix(getData()); 193 } 194 195 /** 196 * Return the covariance matrix as two-dimensional array. 197 * 198 * @return a two-dimensional double array of covariance values 199 * @throws MathIllegalArgumentException if the number of observations 200 * for a cell is < 2 201 */ 202 public double[][] getData() throws MathIllegalArgumentException { 203 final double[][] data = new double[dimension][dimension]; 204 for (int i = 0; i < dimension; i++) { 205 for (int j = 0; j < dimension; j++) { 206 data[i][j] = getElement(i, j).getResult(); 207 } 208 } 209 return data; 210 } 211 212 /** 213 * This {@link Covariance} method is not supported by a {@link StorelessCovariance}, 214 * since the number of bivariate observations does not have to be the same for different 215 * pairs of covariates - i.e., N as defined in {@link Covariance#getN()} is undefined. 216 * 217 * @return nothing as this implementation always throws a 218 * {@link MathRuntimeException} 219 * @throws MathRuntimeException in all cases 220 */ 221 @Override 222 public int getN() throws MathRuntimeException { 223 throw new MathRuntimeException(LocalizedCoreFormats.UNSUPPORTED_OPERATION); 224 } 225 }