001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math4.legacy.field.linalg; 018 019import java.util.Arrays; 020import java.util.List; 021import java.util.ArrayList; 022import org.apache.commons.numbers.field.Field; 023import org.apache.commons.math4.legacy.linear.AnyMatrix; 024 025/** 026 * Square matrix whose elements define a {@link Field}. 027 * 028 * @param <T> Type of the field elements. 029 * 030 * @since 4.0 031 */ 032public final class FieldDenseMatrix<T> 033 implements AnyMatrix { 034 /** Field. */ 035 private final Field<T> field; 036 /** Number of rows. */ 037 private final int rows; 038 /** Number of columns. */ 039 private final int columns; 040 /** 041 * Data storage (in row-major order). 042 * 043 * <p>Note: This is an Object[] that has been cast to T[] for convenience. It should not be 044 * exposed to avoid heap pollution. It is expected all entries stored in the array are 045 * instances of T. 046 */ 047 private final T[] data; 048 049 /** 050 * @param f Field. 051 * @param r Number of rows. 052 * @param c Number of columns. 053 * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}. 054 */ 055 @SuppressWarnings("unchecked") 056 private FieldDenseMatrix(Field<T> f, 057 int r, 058 int c) { 059 if (r <= 0 || 060 c <= 0) { 061 throw new IllegalArgumentException("Negative size"); 062 } 063 064 field = f; 065 rows = r; 066 columns = c; 067 data = (T[]) new Object[r * c]; 068 } 069 070 /** 071 * Factory method. 072 * 073 * @param <T> Type of the field elements. 074 * @param f Field. 075 * @param r Number of rows. 076 * @param c Number of columns. 077 * @return a new instance. 078 * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}. 079 */ 080 public static <T> FieldDenseMatrix<T> create(Field<T> f, 081 int r, 082 int c) { 083 return new FieldDenseMatrix<>(f, r, c); 084 } 085 086 /** 087 * Factory method. 088 * 089 * @param <T> Type of the field elements. 090 * @param f Field. 091 * @param r Number of rows. 092 * @param c Number of columns. 093 * @return a matrix with elements zet to {@link Field#zero() zero}. 094 * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}. 095 */ 096 public static <T> FieldDenseMatrix<T> zero(Field<T> f, 097 int r, 098 int c) { 099 return create(f, r, c).fill(f.zero()); 100 } 101 102 /** 103 * Factory method. 104 * 105 * @param <T> Type of the field elements. 106 * @param f Field. 107 * @param n Dimension of the matrix. 108 * @throws IllegalArgumentException if {@code n <= 0}. 109 * @return the identity matrix. 110 */ 111 public static <T> FieldDenseMatrix<T> identity(Field<T> f, 112 int n) { 113 final FieldDenseMatrix<T> r = zero(f, n, n); 114 115 for (int i = 0; i < n; i++) { 116 r.set(i, i, f.one()); 117 } 118 119 return r; 120 } 121 122 /** {@inheritDoc} */ 123 @Override 124 public boolean equals(Object other) { 125 if (this == other) { 126 return true; 127 } else { 128 if (other instanceof FieldDenseMatrix) { 129 final FieldDenseMatrix<?> m = (FieldDenseMatrix<?>) other; 130 return field.equals(m.field) && 131 rows == m.rows && 132 columns == m.columns && 133 Arrays.equals(data, m.data); 134 } else { 135 return false; 136 } 137 } 138 } 139 140 /** 141 * Copies this matrix. 142 * 143 * @return a new instance. 144 */ 145 public FieldDenseMatrix<T> copy() { 146 final FieldDenseMatrix<T> r = create(field, rows, columns); 147 System.arraycopy(data, 0, r.data, 0, data.length); 148 return r; 149 } 150 151 /** 152 * Transposes this matrix. 153 * 154 * @return a new instance. 155 */ 156 public FieldDenseMatrix<T> transpose() { 157 final FieldDenseMatrix<T> r = create(field, columns, rows); 158 159 for (int i = 0; i < rows; i++) { 160 for (int j = 0; j < columns; j++) { 161 r.set(j, i, get(i, j)); 162 } 163 } 164 165 return r; 166 } 167 168 /** {@inheritDoc} */ 169 @Override 170 public int getRowDimension() { 171 return rows; 172 } 173 174 /** {@inheritDoc} */ 175 @Override 176 public int getColumnDimension() { 177 return columns; 178 } 179 180 /** 181 * @return the field associated with the matrix entries. 182 */ 183 public Field<T> getField() { 184 return field; 185 } 186 187 /** 188 * Sets all elements to the given value. 189 * 190 * @param value Value of the elements of the matrix. 191 * @return {@code this}. 192 */ 193 public FieldDenseMatrix<T> fill(T value) { 194 Arrays.fill(data, value); 195 return this; 196 } 197 198 /** 199 * Gets an element. 200 * 201 * @param i Row. 202 * @param j Column. 203 * @return the element at (i, j). 204 */ 205 public T get(int i, 206 int j) { 207 return data[i * columns + j]; 208 } 209 210 /** 211 * Sets an element. 212 * 213 * @param i Row. 214 * @param j Column. 215 * @param value Value. 216 */ 217 public void set(int i, 218 int j, 219 T value) { 220 data[i * columns + j] = value; 221 } 222 223 /** 224 * Addition. 225 * 226 * @param other Matrix to add. 227 * @return a new instance with the result of the addition. 228 * @throws IllegalArgumentException if the dimensions do not match. 229 */ 230 public FieldDenseMatrix<T> add(FieldDenseMatrix<T> other) { 231 checkAdd(other); 232 final FieldDenseMatrix<T> r = create(field, rows, columns); 233 234 for (int i = 0; i < data.length; i++) { 235 r.data[i] = field.add(data[i], other.data[i]); 236 } 237 238 return r; 239 } 240 241 /** 242 * Subtraction. 243 * 244 * @param other Matrix to subtract. 245 * @return a new instance with the result of the subtraction. 246 * @throws IllegalArgumentException if the dimensions do not match. 247 */ 248 public FieldDenseMatrix<T> subtract(FieldDenseMatrix<T> other) { 249 checkAdd(other); 250 final FieldDenseMatrix<T> r = create(field, rows, columns); 251 252 for (int i = 0; i < data.length; i++) { 253 r.data[i] = field.subtract(data[i], other.data[i]); 254 } 255 256 return r; 257 } 258 259 /** 260 * Negate. 261 * 262 * @return a new instance with the opposite matrix. 263 */ 264 public FieldDenseMatrix<T> negate() { 265 final FieldDenseMatrix<T> r = create(field, rows, columns); 266 267 for (int i = 0; i < data.length; i++) { 268 r.data[i] = field.negate(data[i]); 269 } 270 271 return r; 272 } 273 274 /** 275 * Multiplication. 276 * 277 * @param other Matrix to multiply with. 278 * @return a new instance with the result of the multiplication. 279 * @throws IllegalArgumentException if the dimensions do not match. 280 */ 281 public FieldDenseMatrix<T> multiply(FieldDenseMatrix<T> other) { 282 checkMultiply(other); 283 final FieldDenseMatrix<T> r = zero(field, rows, other.columns); 284 285 for (int i = 0; i < rows; i++) { 286 final int o1 = i * columns; 287 final int o2 = i * r.columns; 288 for (int j = 0; j < other.columns; j++) { 289 final int o3 = o2 + j; 290 for (int k = 0; k < columns; k++) { 291 r.data[o3] = field.add(r.data[o3], 292 field.multiply(data[o1 + k], 293 other.data[k * other.columns + j])); 294 } 295 } 296 } 297 298 return r; 299 } 300 301 /** 302 * Multiplies the matrix with itself {@code p} times. 303 * 304 * @param p Exponent. 305 * @return a new instance. 306 * @throws IllegalArgumentException if {@code p < 0}. 307 */ 308 public FieldDenseMatrix<T> pow(int p) { 309 checkMultiply(this); 310 311 if (p < 0) { 312 throw new IllegalArgumentException("Negative exponent: " + p); 313 } 314 315 if (p == 0) { 316 return identity(field, rows); 317 } 318 319 if (p == 1) { 320 return copy(); 321 } 322 323 final int power = p - 1; 324 325 // Only log_2(p) operations are necessary by doing as follows: 326 // 5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2 327 // The same approach is used for A^p. 328 329 final char[] binary = Integer.toBinaryString(power).toCharArray(); 330 final ArrayList<Integer> nonZeroPositions = new ArrayList<>(); 331 332 for (int i = 0; i < binary.length; i++) { 333 if (binary[i] == '1') { 334 final int pos = binary.length - i - 1; 335 nonZeroPositions.add(pos); 336 } 337 } 338 339 final List<FieldDenseMatrix<T>> results = new ArrayList<>(binary.length); 340 results.add(this); 341 for (int i = 1; i < binary.length; i++) { 342 final FieldDenseMatrix<T> s = results.get(i - 1); 343 final FieldDenseMatrix<T> r = s.multiply(s); 344 results.add(r); 345 } 346 347 FieldDenseMatrix<T> r = this; 348 for (Integer i : nonZeroPositions) { 349 r = r.multiply(results.get(i)); 350 } 351 352 return r; 353 } 354 355 /** {@inheritDoc} */ 356 @Override 357 public int hashCode() { 358 assert false : "hashCode not designed"; 359 return 42; // Any arbitrary constant will do. 360 } 361}