This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git

commit 128523112bdbad85b6b5c925004c0e975be5d553
Author: Gilles Sadowski <[email protected]>
AuthorDate: Sun Oct 27 14:59:19 2019 +0100

    MATH-1499: Matrix whose entries are elements of a field.
---
 .../math4/field/linalg/FieldDenseMatrix.java       | 343 +++++++++++++++++++++
 .../commons/math4/field/linalg/package-info.java   |  21 ++
 .../apache/commons/math4/field/package-info.java   |  22 ++
 .../field/linalg/FP64FieldDenseMatrixTest.java     | 285 +++++++++++++++++
 4 files changed, 671 insertions(+)

diff --git 
a/src/main/java/org/apache/commons/math4/field/linalg/FieldDenseMatrix.java 
b/src/main/java/org/apache/commons/math4/field/linalg/FieldDenseMatrix.java
new file mode 100644
index 0000000..8fd0dc1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math4/field/linalg/FieldDenseMatrix.java
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.field.linalg;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import org.apache.commons.numbers.field.Field;
+import org.apache.commons.math4.linear.AnyMatrix;
+
+/**
+ * Square matrix whose elements define a {@link Field}.
+ *
+ * @param <T> Type of the field elements.
+ *
+ * @since 4.0
+ */
+public class FieldDenseMatrix<T>
+    implements AnyMatrix {
+    /** Field. */
+    private final Field<T> field;
+    /** Number of rows. */
+    private final int rows;
+    /** Number of columns. */
+    private final int columns;
+    /** Data storage (in row-major order). */
+    private final T[] data;
+
+    /**
+     * @param f Field.
+     * @param r Number of rows.
+     * @param c Number of columns.
+     * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
+     */
+    protected FieldDenseMatrix(Field<T> f,
+                               int r,
+                               int c) {
+        if (r <= 0 ||
+            c <= 0) {
+            throw new IllegalArgumentException("Negative size");
+        }
+
+        field = f;
+        rows = r;
+        columns = c;
+        data = (T[]) new Object[r * c];
+    }
+
+    /**
+     * Factory method.
+     *
+     * @param f Field.
+     * @param r Number of rows.
+     * @param c Number of columns.
+     * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
+     */
+    public static <T> FieldDenseMatrix<T> create(Field<T> f,
+                                                 int r,
+                                                 int c) {
+        return new FieldDenseMatrix<>(f, r, c);
+    }
+
+    /**
+     * Factory method.
+     *
+     * @param f Field.
+     * @param r Number of rows.
+     * @param c Number of columns.
+     * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
+     * @return a matrix with elements zet to {@link Field#zero() zero}.
+     */
+    public static <T> FieldDenseMatrix<T> zero(Field<T> f,
+                                               int r,
+                                               int c) {
+        return create(f, r, c).fill(f.zero());
+    }
+
+    /**
+     * Factory method.
+     *
+     * @param f Field.
+     * @param n Dimension of the matrix.
+     * @return the identity matrix.
+     * @throws IllegalArgumentException if {@code n <= 0}.
+     */
+    public static <T> FieldDenseMatrix<T> identity(Field<T> f,
+                                                   int n) {
+        final FieldDenseMatrix<T> r = zero(f, n, n);
+
+        for (int i = 0; i < n; i++) {
+            r.set(i, i, f.one());
+        }
+
+        return r;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else {
+            if (other instanceof FieldDenseMatrix) {
+                final FieldDenseMatrix<?> m = (FieldDenseMatrix<?>) other;
+                return field.equals(m.field) &&
+                    rows == m.rows &&
+                    columns == m.columns &&
+                    Arrays.equals(data, m.data);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Copies this matrix.
+     *
+     * @return a new instance.
+     */
+    public FieldDenseMatrix<T> copy() {
+        final FieldDenseMatrix<T> r = create(field, rows, columns);
+        System.arraycopy(data, 0, r.data, 0, data.length);
+        return r;
+    }
+
+    /**
+     * Transposes this matrix.
+     *
+     * @return a new instance.
+     */
+    public FieldDenseMatrix<T> transpose() {
+        final FieldDenseMatrix<T> r = create(field, columns, rows);
+
+        for (int i = 0; i < rows; i++) {
+            for (int j = 0; j < columns; j++) {
+                r.set(j, i, get(i, j));
+            }
+        }
+
+        return r;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getRowDimension() {
+        return rows;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getColumnDimension() {
+        return columns;
+    }
+
+    /**
+     * @return the field associated with the matrix entries.
+     */
+    public Field<T> getField() {
+        return field;
+    }
+
+    /**
+     * Sets all elements to the given value.
+     *
+     * @param value Value of the elements of the matrix.
+     * @return {@code this}.
+     */
+    public FieldDenseMatrix<T> fill(T value) {
+        Arrays.fill(data, value);
+        return this;
+    }
+
+    /**
+     * Gets an element.
+     *
+     * @param i Row.
+     * @param j Column.
+     * @return the element at (i, j).
+     */
+    public T get(int i,
+                 int j) {
+        return data[i * columns + j];
+    }
+
+    /**
+     * Sets an element.
+     *
+     * @param i Row.
+     * @param j Column.
+     * @param value Value.
+     */
+    public void set(int i,
+                    int j,
+                    T value) {
+        data[i * columns + j] = value;
+    }
+
+    /**
+     * Addition.
+     *
+     * @param other Matrix to add.
+     * @return a new instance with the result of the addition.
+     * @throws IllegalArgumentException if the dimensions do not match.
+     */
+    public FieldDenseMatrix<T> add(FieldDenseMatrix<T> other) {
+        checkAdd(other);
+        final FieldDenseMatrix<T> r = create(field, rows, columns);
+
+        for (int i = 0; i < data.length; i++) {
+            r.data[i] = field.add(data[i], other.data[i]);
+        }
+
+        return r;
+    }
+
+    /**
+     * Subtraction.
+     *
+     * @param other Matrix to subtract.
+     * @return a new instance with the result of the subtraction.
+     * @throws IllegalArgumentException if the dimensions do not match.
+     */
+    public FieldDenseMatrix<T> subtract(FieldDenseMatrix<T> other) {
+        checkAdd(other);
+        final FieldDenseMatrix<T> r = create(field, rows, columns);
+
+        for (int i = 0; i < data.length; i++) {
+            r.data[i] = field.subtract(data[i], other.data[i]);
+        }
+
+        return r;
+    }
+
+    /**
+     * Negate.
+     *
+     * @return a new instance with the opposite matrix.
+     */
+    public FieldDenseMatrix<T> negate() {
+        final FieldDenseMatrix<T> r = create(field, rows, columns);
+
+        for (int i = 0; i < data.length; i++) {
+            r.data[i] = field.negate(data[i]);
+        }
+
+        return r;
+    }
+
+    /**
+     * Multiplication.
+     *
+     * @param other Matrix to multiply with.
+     * @return a new instance with the result of the multiplication.
+     * @throws IllegalArgumentException if the dimensions do not match.
+     */
+    public FieldDenseMatrix<T> multiply(FieldDenseMatrix<T> other) {
+        checkMultiply(other);
+        final FieldDenseMatrix<T> r = zero(field, rows, other.columns);
+
+        for (int i = 0; i < rows; i++) {
+            final int o1 = i * columns;
+            final int o2 = i * r.columns;
+            for (int j = 0; j < other.columns; j++) {
+                final int o3 = o2 + j;
+                for (int k = 0; k < columns; k++) {
+                    r.data[o3] = field.add(r.data[o3],
+                                           field.multiply(data[o1 + k],
+                                                          other.data[k * 
other.columns + j]));
+                }
+            }
+        }
+
+        return r;
+    }
+
+    /**
+     * Multiplies the matrix with itself {@code p} times.
+     *
+     * @param p Exponent.
+     * @return a new instance.
+     * @throws IllegalArgumentException if {@code p < 0}.
+     */
+    public FieldDenseMatrix<T> pow(int p) {
+        checkMultiply(this);
+
+        if (p < 0) {
+            throw new IllegalArgumentException("Negative exponent: " + p);
+        }
+
+        if (p == 0) {
+            return identity(field, rows);
+        }
+
+        if (p == 1) {
+            return copy();
+        }
+
+        final int power = p - 1;
+
+        // Only log_2(p) operations are necessary by doing as follows:
+        //    5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
+        // The same approach is used for A^p.
+
+        final char[] binary = Integer.toBinaryString(power).toCharArray();
+        final ArrayList<Integer> nonZeroPositions = new ArrayList<>();
+
+        for (int i = 0; i < binary.length; i++) {
+            if (binary[i] == '1') {
+                final int pos = binary.length - i - 1;
+                nonZeroPositions.add(pos);
+            }
+        }
+
+        final List<FieldDenseMatrix<T>> results = new 
ArrayList<>(binary.length);
+        results.add(this);
+        for (int i = 1; i < binary.length; i++) {
+            final FieldDenseMatrix<T> s = results.get(i - 1);
+            final FieldDenseMatrix<T> r = s.multiply(s);
+            results.add(r);
+        }
+
+        FieldDenseMatrix<T> r = this;
+        for (Integer i : nonZeroPositions) {
+            r = r.multiply(results.get(i));
+        }
+
+        return r;
+    }
+}
diff --git 
a/src/main/java/org/apache/commons/math4/field/linalg/package-info.java 
b/src/main/java/org/apache/commons/math4/field/linalg/package-info.java
new file mode 100644
index 0000000..3ee0946
--- /dev/null
+++ b/src/main/java/org/apache/commons/math4/field/linalg/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Linear algebra defined in term of matrices whose entries are elements
+ * of a {@link org.apache.commons.numbers.field.Field field}.
+ */
+package org.apache.commons.math4.field.linalg;
diff --git a/src/main/java/org/apache/commons/math4/field/package-info.java 
b/src/main/java/org/apache/commons/math4/field/package-info.java
new file mode 100644
index 0000000..f800a67
--- /dev/null
+++ b/src/main/java/org/apache/commons/math4/field/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Utilities based on the {@link org.apache.commons.numbers.field.Field Field}
+ * functionality defined in <a href="http://commons.apache.org/numbers";>
+ * Commons Numbers</a>.
+ */
+package org.apache.commons.math4.field;
diff --git 
a/src/test/java/org/apache/commons/math4/field/linalg/FP64FieldDenseMatrixTest.java
 
b/src/test/java/org/apache/commons/math4/field/linalg/FP64FieldDenseMatrixTest.java
new file mode 100644
index 0000000..55a1ea2
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/math4/field/linalg/FP64FieldDenseMatrixTest.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.field.linalg;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.apache.commons.numbers.field.FP64;
+import org.apache.commons.numbers.field.FP64Field;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Tests for {@link FieldDenseMatrix} (using {@link FP64} as field elements).
+ */
+public class FP64FieldDenseMatrixTest {
+    @Test
+    public void testGetRowDimension() {
+        final int r = 6;
+        final int c = 9;
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), r, c);
+        Assert.assertEquals(r, a.getRowDimension());
+    }
+
+    @Test
+    public void testGetColumnDimension() {
+        final int r = 6;
+        final int c = 9;
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), r, c);
+        Assert.assertEquals(c, a.getColumnDimension());
+    }
+
+    @Test
+    public void testSetGet() {
+        final int r = 17;
+        final int c = 20;
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), r, c);
+
+        int count = 0;
+        for (int i = 0; i < r; i++) {
+            for (int j = 0; j < c; j++) {
+                a.set(i, j, FP64.of(count++));
+            }
+        }
+        Assert.assertEquals(r * c, count);
+
+        count = 0;
+        for (int i = 0; i < r; i++) {
+            for (int j = 0; j < c; j++) {
+                Assert.assertEquals((double) count++,
+                                    a.get(i, j).doubleValue(),
+                                    0d);
+            }
+        }
+    }
+
+    @Test
+    public void testAdd() {
+        final int r = 5;
+        final int c = 3;
+        final double scale = 1e3;
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p1 = createRandom(r, c, 
scale);
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p2 = createRandom(r, c, 
scale);
+
+        assertEquals(p1.getFirst().add(p2.getFirst()),
+                     p1.getSecond().add(p2.getSecond()),
+                     0d);
+    }
+
+    @Test
+    public void testSubtract() {
+        final int r = 2;
+        final int c = 6;
+        final double scale = 1e3;
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p1 = createRandom(r, c, 
scale);
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p2 = createRandom(r, c, 
scale);
+
+        assertEquals(p1.getFirst().subtract(p2.getFirst()),
+                     p1.getSecond().subtract(p2.getSecond()),
+                     0d);
+    }
+
+    @Test
+    public void testMultiply() {
+        final int r = 7;
+        final int c1 = 4;
+        final int c2 = 5;
+        final double scale = 1e2;
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p1 = createRandom(r, 
c1, scale);
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p2 = createRandom(c1, 
c2, scale);
+
+        assertEquals(p1.getFirst().multiply(p2.getFirst()),
+                     p1.getSecond().multiply(p2.getSecond()),
+                     0d);
+    }
+
+    @Test
+    public void testNegate() {
+        final int dim = 13;
+        final double scale = 1;
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p = createRandom(dim, 
dim, scale);
+
+        assertEquals(p.getFirst().negate(),
+                     p.getSecond().scalarMultiply(-1),
+                     0d);
+    }
+
+    @Test
+    public void testPowZero() {
+        final int dim = 5;
+        final double scale = 1e100;
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p = createRandom(dim, 
dim, scale);
+
+        final int exp = 0;
+        assertEquals(p.getFirst().pow(exp),
+                     p.getSecond().power(exp),
+                     0d);
+    }
+
+    @Test
+    public void testPowOne() {
+        final int dim = 5;
+        final double scale = 1e100;
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p = createRandom(dim, 
dim, scale);
+
+        final int exp = 1;
+        assertEquals(p.getFirst().pow(exp),
+                     p.getSecond().power(exp),
+                     0d);
+    }
+
+    @Test
+    public void testPow() {
+        final int dim = 5;
+        final double scale = 1e2;
+        final Pair<FieldDenseMatrix<FP64>, RealMatrix> p = createRandom(dim, 
dim, scale);
+
+        final int exp = 4;
+        assertEquals(p.getFirst().pow(exp),
+                     p.getSecond().power(exp),
+                     0d);
+    }
+
+    @Test
+    public void testGetField() {
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), 7, 5);
+        Assert.assertEquals(FP64Field.get(), a.getField());
+    }
+
+    @Test
+    public void testEquals() {
+        // Reference equality.
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), 7, 2);
+        Assert.assertEquals(a, a);
+
+        // Dimension mismatch
+        final FieldDenseMatrix<FP64> b = 
FieldDenseMatrix.create(FP64Field.get(), 7, 3);
+        Assert.assertNotEquals(a, b);
+        final FieldDenseMatrix<FP64> c = 
FieldDenseMatrix.create(FP64Field.get(), 6, 2);
+        Assert.assertNotEquals(a, c);
+
+        // Contents.        
+        final FieldDenseMatrix<FP64> d = 
FieldDenseMatrix.create(FP64Field.get(), 7, 2);
+        Assert.assertEquals(a, d); // Unitialized contents.
+        a.fill(FP64.of(1.23456789));
+        d.fill(FP64.of(1.23456789));
+        Assert.assertEquals(a, d); // Initialized contents.
+        d.set(6, 1, d.get(6, 1).add(FP64.of(1e-15)));
+        Assert.assertNotEquals(a, d);
+    }
+
+    @Test
+    public void testCopy() {
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), 7, 3)
+            .fill(FP64Field.get().one());
+        final FieldDenseMatrix<FP64> b = a.copy();
+        Assert.assertEquals(a, b);
+
+        b.set(0, 0, FP64Field.get().zero());
+        Assert.assertNotEquals(a, b);
+    }
+
+    @Test
+    public void testTranspose() {
+        final int r = 4;
+        final int c = 5;
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), r, c);
+        for (int i = 0; i < r; i++) {
+            for (int j = 0; j < c; j++) {
+                final double j2 = j * j;
+                a.set(i, j,
+                      FP64.of(1.2 * i + 3.4 * j2));
+            }
+        }
+
+        final FieldDenseMatrix<FP64> b = a.transpose();
+        for (int i = 0; i < r; i++) {
+            for (int j = 0; j < c; j++) {
+                Assert.assertEquals(a.get(i, j), b.get(j, i));
+            }
+        }
+
+        Assert.assertEquals(a, b.transpose());
+    }
+
+    @Test
+    public void testIdentity() {
+        final int dim = 3;
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.identity(FP64Field.get(), dim);
+        for (int i = 0; i < dim; i++) {
+            for (int j = 0; j < dim; j++) {
+                if (i == j) {
+                    Assert.assertEquals(FP64Field.get().one(),                 
                       a.get(i, j));
+                } else {
+                    Assert.assertEquals(FP64Field.get().zero(),
+                                        a.get(i, j));
+                }
+            }
+        }
+    }
+
+    /**
+     * Compares with result obtained from "Commons Math".
+     *
+     * @param a "o.a.c.m.field.linalg" result.
+     * @param b "o.a.c.m.linear" result.
+     * @param tol Tolerance.
+     */
+    private void assertEquals(FieldDenseMatrix<FP64> a,
+                              RealMatrix b,
+                              double tol) {
+        if (a.getRowDimension() != b.getRowDimension() ||
+            a.getColumnDimension() != b.getColumnDimension()) {
+            Assert.fail("Dimension mismatch"); 
+        }
+
+        for (int i = 0; i < a.getRowDimension(); i++) {
+            for (int j = 0; j < a.getColumnDimension(); j++) {
+                Assert.assertEquals("(" + i + ", " + j + ")",
+                                    a.get(i, j).doubleValue(),
+                                    b.getEntry(i, j),
+                                    tol);
+            }
+        }
+    }
+
+    /**
+     * Creates test matrices with random entries.
+     *
+     * @param r Rows.
+     * @param c Columns.
+     * @param scale Range of the entries.
+     * @return a pair of matrices whose entries are in the interval
+     * {@code [-scale, scale]}.
+     */
+    private Pair<FieldDenseMatrix<FP64>, RealMatrix> createRandom(int r,
+                                                                  int c,
+                                                                  double 
scale) {
+        final FieldDenseMatrix<FP64> a = 
FieldDenseMatrix.create(FP64Field.get(), r, c);
+        final RealMatrix b = new Array2DRowRealMatrix(r, c);
+        for (int i = 0; i < r; i++) {
+            for (int j = 0; j < c; j++) {
+                final double v = scale * (2 * Math.random() - 1);
+                a.set(i, j, FP64.of(v));
+                b.setEntry(i, j, v);
+            }
+        }
+
+        return new Pair<>(a, b);
+    }
+}

Reply via email to