| /* |
| * 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.numbers.arrays; |
| |
| import org.junit.jupiter.api.Assertions; |
| import org.junit.jupiter.api.Test; |
| |
| import org.apache.commons.rng.UniformRandomProvider; |
| import org.apache.commons.rng.simple.RandomSource; |
| import org.apache.commons.numbers.fraction.BigFraction; |
| |
| /** |
| * Test cases for the {@link LinearCombination} class. |
| */ |
| public class LinearCombinationTest { |
| // MATH-1005 |
| @Test |
| public void testSingleElementArray() { |
| final double[] a = {1.23456789}; |
| final double[] b = {98765432.1}; |
| |
| Assertions.assertEquals(a[0] * b[0], LinearCombination.value(a, b), 0d); |
| } |
| |
| @Test |
| public void testTwoSums() { |
| final BigFraction[] aF = new BigFraction[] { |
| BigFraction.of(-1321008684645961L, 268435456L), |
| BigFraction.of(-5774608829631843L, 268435456L), |
| BigFraction.of(-7645843051051357L, 8589934592L) |
| }; |
| final BigFraction[] bF = new BigFraction[] { |
| BigFraction.of(-5712344449280879L, 2097152L), |
| BigFraction.of(-4550117129121957L, 2097152L), |
| BigFraction.of(8846951984510141L, 131072L) |
| }; |
| |
| final int len = aF.length; |
| final double[] a = new double[len]; |
| final double[] b = new double[len]; |
| for (int i = 0; i < len; i++) { |
| a[i] = aF[i].getNumerator().doubleValue() / aF[i].getDenominator().doubleValue(); |
| b[i] = bF[i].getNumerator().doubleValue() / bF[i].getDenominator().doubleValue(); |
| } |
| |
| // Ensure "array" and "inline" implementations give the same result. |
| final double abSumInline = LinearCombination.value(a[0], b[0], |
| a[1], b[1], |
| a[2], b[2]); |
| final double abSumArray = LinearCombination.value(a, b); |
| Assertions.assertEquals(abSumInline, abSumArray, 0); |
| |
| // Compare with arbitrary precision computation. |
| BigFraction result = BigFraction.ZERO; |
| for (int i = 0; i < a.length; i++) { |
| result = result.add(aF[i].multiply(bF[i])); |
| } |
| final double expected = result.doubleValue(); |
| Assertions.assertEquals(expected, abSumInline, 1e-15); |
| |
| final double naive = a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; |
| Assertions.assertTrue(Math.abs(naive - abSumInline) > 1.5); |
| } |
| |
| @Test |
| public void testArrayVsInline() { |
| final UniformRandomProvider rng = RandomSource.create(RandomSource.XO_SHI_RO_256_PP); |
| |
| double sInline; |
| double sArray; |
| final double scale = 1e17; |
| for (int i = 0; i < 10000; ++i) { |
| final double u1 = scale * rng.nextDouble(); |
| final double u2 = scale * rng.nextDouble(); |
| final double u3 = scale * rng.nextDouble(); |
| final double u4 = scale * rng.nextDouble(); |
| final double v1 = scale * rng.nextDouble(); |
| final double v2 = scale * rng.nextDouble(); |
| final double v3 = scale * rng.nextDouble(); |
| final double v4 = scale * rng.nextDouble(); |
| |
| // One sum. |
| sInline = LinearCombination.value(u1, v1, u2, v2); |
| sArray = LinearCombination.value(new double[] {u1, u2}, |
| new double[] {v1, v2}); |
| Assertions.assertEquals(sInline, sArray, 0); |
| |
| // Two sums. |
| sInline = LinearCombination.value(u1, v1, u2, v2, u3, v3); |
| sArray = LinearCombination.value(new double[] {u1, u2, u3}, |
| new double[] {v1, v2, v3}); |
| Assertions.assertEquals(sInline, sArray, 0); |
| |
| // Three sums. |
| sInline = LinearCombination.value(u1, v1, u2, v2, u3, v3, u4, v4); |
| sArray = LinearCombination.value(new double[] {u1, u2, u3, u4}, |
| new double[] {v1, v2, v3, v4}); |
| Assertions.assertEquals(sInline, sArray, 0); |
| } |
| } |
| |
| @Test |
| public void testHuge() { |
| int scale = 971; |
| final double[] a = new double[] { |
| -1321008684645961.0 / 268435456.0, |
| -5774608829631843.0 / 268435456.0, |
| -7645843051051357.0 / 8589934592.0 |
| }; |
| final double[] b = new double[] { |
| -5712344449280879.0 / 2097152.0, |
| -4550117129121957.0 / 2097152.0, |
| 8846951984510141.0 / 131072.0 |
| }; |
| |
| final int len = a.length; |
| final double[] scaledA = new double[len]; |
| final double[] scaledB = new double[len]; |
| for (int i = 0; i < len; ++i) { |
| scaledA[i] = Math.scalb(a[i], -scale); |
| scaledB[i] = Math.scalb(b[i], scale); |
| } |
| final double abSumInline = LinearCombination.value(scaledA[0], scaledB[0], |
| scaledA[1], scaledB[1], |
| scaledA[2], scaledB[2]); |
| final double abSumArray = LinearCombination.value(scaledA, scaledB); |
| |
| Assertions.assertEquals(abSumInline, abSumArray, 0); |
| Assertions.assertEquals(-1.8551294182586248737720779899, abSumInline, 1e-15); |
| |
| final double naive = scaledA[0] * scaledB[0] + scaledA[1] * scaledB[1] + scaledA[2] * scaledB[2]; |
| Assertions.assertTrue(Math.abs(naive - abSumInline) > 1.5); |
| } |
| |
| @Test |
| public void testInfinite() { |
| final double[][] a = new double[][] { |
| {1, 2, 3, 4}, |
| {1, Double.POSITIVE_INFINITY, 3, 4}, |
| {1, 2, Double.POSITIVE_INFINITY, 4}, |
| {1, Double.POSITIVE_INFINITY, 3, Double.NEGATIVE_INFINITY}, |
| {1, 2, 3, 4}, |
| {1, 2, 3, 4}, |
| {1, 2, 3, 4}, |
| {1, 2, 3, 4} |
| }; |
| final double[][] b = new double[][] { |
| {1, -2, 3, 4}, |
| {1, -2, 3, 4}, |
| {1, -2, 3, 4}, |
| {1, -2, 3, 4}, |
| {1, Double.POSITIVE_INFINITY, 3, 4}, |
| {1, -2, Double.POSITIVE_INFINITY, 4}, |
| {1, Double.POSITIVE_INFINITY, 3, Double.NEGATIVE_INFINITY}, |
| {Double.NaN, -2, 3, 4} |
| }; |
| |
| Assertions.assertEquals(-3, |
| LinearCombination.value(a[0][0], b[0][0], |
| a[0][1], b[0][1]), |
| 1e-10); |
| Assertions.assertEquals(6, |
| LinearCombination.value(a[0][0], b[0][0], |
| a[0][1], b[0][1], |
| a[0][2], b[0][2]), |
| 1e-10); |
| Assertions.assertEquals(22, |
| LinearCombination.value(a[0][0], b[0][0], |
| a[0][1], b[0][1], |
| a[0][2], b[0][2], |
| a[0][3], b[0][3]), |
| 1e-10); |
| Assertions.assertEquals(22, LinearCombination.value(a[0], b[0]), 1e-10); |
| |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, |
| LinearCombination.value(a[1][0], b[1][0], |
| a[1][1], b[1][1]), |
| 1e-10); |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, |
| LinearCombination.value(a[1][0], b[1][0], |
| a[1][1], b[1][1], |
| a[1][2], b[1][2]), |
| 1e-10); |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, |
| LinearCombination.value(a[1][0], b[1][0], |
| a[1][1], b[1][1], |
| a[1][2], b[1][2], |
| a[1][3], b[1][3]), |
| 1e-10); |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, LinearCombination.value(a[1], b[1]), 1e-10); |
| |
| Assertions.assertEquals(-3, |
| LinearCombination.value(a[2][0], b[2][0], |
| a[2][1], b[2][1]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[2][0], b[2][0], |
| a[2][1], b[2][1], |
| a[2][2], b[2][2]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[2][0], b[2][0], |
| a[2][1], b[2][1], |
| a[2][2], b[2][2], |
| a[2][3], b[2][3]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, LinearCombination.value(a[2], b[2]), 1e-10); |
| |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, |
| LinearCombination.value(a[3][0], b[3][0], |
| a[3][1], b[3][1]), |
| 1e-10); |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, |
| LinearCombination.value(a[3][0], b[3][0], |
| a[3][1], b[3][1], |
| a[3][2], b[3][2]), |
| 1e-10); |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, |
| LinearCombination.value(a[3][0], b[3][0], |
| a[3][1], b[3][1], |
| a[3][2], b[3][2], |
| a[3][3], b[3][3]), |
| 1e-10); |
| Assertions.assertEquals(Double.NEGATIVE_INFINITY, LinearCombination.value(a[3], b[3]), 1e-10); |
| |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[4][0], b[4][0], |
| a[4][1], b[4][1]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[4][0], b[4][0], |
| a[4][1], b[4][1], |
| a[4][2], b[4][2]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[4][0], b[4][0], |
| a[4][1], b[4][1], |
| a[4][2], b[4][2], |
| a[4][3], b[4][3]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, LinearCombination.value(a[4], b[4]), 1e-10); |
| |
| Assertions.assertEquals(-3, |
| LinearCombination.value(a[5][0], b[5][0], |
| a[5][1], b[5][1]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[5][0], b[5][0], |
| a[5][1], b[5][1], |
| a[5][2], b[5][2]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[5][0], b[5][0], |
| a[5][1], b[5][1], |
| a[5][2], b[5][2], |
| a[5][3], b[5][3]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, LinearCombination.value(a[5], b[5]), 1e-10); |
| |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[6][0], b[6][0], |
| a[6][1], b[6][1]), |
| 1e-10); |
| Assertions.assertEquals(Double.POSITIVE_INFINITY, |
| LinearCombination.value(a[6][0], b[6][0], |
| a[6][1], b[6][1], |
| a[6][2], b[6][2]), |
| 1e-10); |
| Assertions.assertTrue(Double.isNaN(LinearCombination.value(a[6][0], b[6][0], |
| a[6][1], b[6][1], |
| a[6][2], b[6][2], |
| a[6][3], b[6][3]))); |
| Assertions.assertTrue(Double.isNaN(LinearCombination.value(a[6], b[6]))); |
| |
| Assertions.assertTrue(Double.isNaN(LinearCombination.value(a[7][0], b[7][0], |
| a[7][1], b[7][1]))); |
| Assertions.assertTrue(Double.isNaN(LinearCombination.value(a[7][0], b[7][0], |
| a[7][1], b[7][1], |
| a[7][2], b[7][2]))); |
| Assertions.assertTrue(Double.isNaN(LinearCombination.value(a[7][0], b[7][0], |
| a[7][1], b[7][1], |
| a[7][2], b[7][2], |
| a[7][3], b[7][3]))); |
| Assertions.assertTrue(Double.isNaN(LinearCombination.value(a[7], b[7]))); |
| } |
| } |