Java tutorial
/* * Copyright 2015 by Yields. * * Licensed 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 io.yields.math.concepts.operator; import org.apache.commons.math3.random.MersenneTwister; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.fest.assertions.Delta; import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import io.yields.math.framework.Functional; import io.yields.math.framework.FunctionalContext; import io.yields.math.framework.RandomSequence; import io.yields.math.framework.Variable; import io.yields.math.framework.assertions.Assertions; import io.yields.math.framework.data.CommonsMathDoubleRandomSequence; import io.yields.math.framework.functional.DoubleIdentity; import io.yields.math.framework.junit.Spec; import io.yields.math.framework.junit.SpecRunner; import io.yields.math.framework.property.Properties; import static org.fest.assertions.Assertions.assertThat; @RunWith(SpecRunner.class) public class SmoothnessTest { @Test(expected = IllegalArgumentException.class) public void constructor_orderShouldBeStrictlyPositive() { new Smoothness(0); } @Test(expected = IllegalArgumentException.class) public void constructor_orderShouldBeStrictlyPositive_testWithNegative() { new Smoothness(-10); } public static class LinearData extends Functional<List<Tuple>> { private static final double SCALE = 1e5; @Variable("[-1000,1000]") public double a; @Variable("[-1000,1000]") public double b; private static final RandomSequence<Double> generator = new CommonsMathDoubleRandomSequence( new MersenneTwister(0)); @Override protected List<Tuple> compute() { return IntStream.rangeClosed(0, 100).mapToObj(index -> { double x = (generator.next() - .5) * SCALE; double y = (a * x) + b; return new Tuple(x, y); }).collect(Collectors.toList()); } } @Spec(name = "linear data - first order smoothness", functional = SmoothnessTest.LinearData.class) public void testLinearData_linearSmoothness(FunctionalContext<List<Tuple>> context) { //first order match should work DescriptiveStatistics stats = new Smoothness(1).apply(context.evaluate()); assertThat(stats.getMin()).isEqualTo(stats.getMax(), Delta.delta(1.e-8)).isEqualTo(0d, Delta.delta(1.e-8)); } @Spec(name = "linear data - quadraticSmoothness", functional = SmoothnessTest.LinearData.class) public void testLinearData_quadraticSmoothness(FunctionalContext<List<Tuple>> context) { //second order matching as well DescriptiveStatistics stats = new Smoothness(2).apply(context.evaluate()); assertThat(stats.getMin()).isEqualTo(stats.getMax(), Delta.delta(1.e-8)).isEqualTo(0d, Delta.delta(1.e-8)); } public static class QuadraticData extends Functional<List<Tuple>> { private static final double SCALE = 1e5; @Variable("[-1000,1000]") public double a; @Variable("[-1000,1000]") public double b; @Variable("[-1000,1000]") public double c; private static final RandomSequence<Double> generator = new CommonsMathDoubleRandomSequence( new MersenneTwister(0)); @Override protected List<Tuple> compute() { return IntStream.rangeClosed(0, 100).mapToObj(index -> { double x = (generator.next() - .5) * SCALE; double y = (a * x * x) + (b * x) + c; return new Tuple(x, y); }).collect(Collectors.toList()); } } @Spec(name = "quadratic data - first order smoothness", functional = SmoothnessTest.QuadraticData.class) public void testQuadratic_linearSmoothness(FunctionalContext<List<Tuple>> context) { //first order will not match DescriptiveStatistics stats = new Smoothness(1).apply(context.evaluate()); assertThat(stats.getMin()).isGreaterThan(0); assertThat(stats.getMax()).isLessThan(1); assertThat(stats.getMean()).isLessThan(.5); } @Spec(name = "quadratic data - quadratic smoothness", functional = SmoothnessTest.QuadraticData.class) public void testQuadraticCase_quadraticSmoothness(FunctionalContext<List<Tuple>> context) { //second order matching as well DescriptiveStatistics stats = new Smoothness(2).apply(context.evaluate()); assertThat(stats.getMin()).isEqualTo(stats.getMax(), Delta.delta(1.e-8)).isEqualTo(0d, Delta.delta(1.e-8)); } public static class ScaleInvariant extends DoubleIdentity { private static final double SCALE = 1e5; @Variable("[-1000,1000]") public double a; @Variable("[-1000,1000]") public double b; @Variable("[-1000,1000]") public double c; @Variable("]0,100]") public double scale; private static final RandomSequence<Double> generator = new CommonsMathDoubleRandomSequence( new MersenneTwister(0)); protected List<Tuple> sample(double scale) { RandomSequence<Double> generator = new CommonsMathDoubleRandomSequence(new MersenneTwister(0)); return IntStream.rangeClosed(0, 100).mapToObj(index -> { double x = (generator.next() - .5) * SCALE; double y = ((a * x * x) + (b * x) + c) * scale; return new Tuple(x, y); }).collect(Collectors.toList()); } @Override protected Double originalComputation() { return new Smoothness(2).apply(sample(1d)).getMean(); } @Override protected Double alternativeComputation() { return new Smoothness(2).apply(sample(scale)).getMean(); } } @Spec(name = "test scale invariance", functional = ScaleInvariant.class) public void testScaleInvariance(FunctionalContext<Double> context) { Assertions.assertThat(context).is(Properties.identity(1.e-8)); } @Test(expected = IllegalArgumentException.class) public void notEnoughSamples() { List<Tuple> tuples = IntStream.range(0, 5).mapToObj(index -> new Tuple(index, index)) .collect(Collectors.toList()); new Smoothness(10).apply(tuples); } public static class PolynomialData extends DoubleIdentity { private int polynomialOrder = 0; @Variable("[1,10]") public int addOnSmoothness; private static final RandomSequence<Double> generator = new CommonsMathDoubleRandomSequence( new MersenneTwister(0)); @Override protected Double originalComputation() { Smoothness smoothness = new Smoothness(polynomialOrder + addOnSmoothness); return smoothness.apply(createTuples()).getMax(); } @Override protected Double alternativeComputation() { return 0d; } private List<Tuple> createTuples() { return SmoothnessTest.createTuples(polynomialOrder, generator, 100); } } @Spec(name = "test low rank data", functional = PolynomialData.class) public void testLowRankData(FunctionalContext<Double> context) { Assertions.assertThat(context).is(Properties.identity(1.e-8)); } @Test(expected = IllegalArgumentException.class) public void testSparseData() { RandomSequence<Double> generator = new CommonsMathDoubleRandomSequence(new MersenneTwister(0)); List<Tuple> tuples = createTuples(2, generator, 10); new Smoothness(20).apply(tuples).getMax(); } private static List<Tuple> createTuples(int polynomialOrder, RandomSequence<Double> generator, int nrOfElements) { return IntStream.rangeClosed(0, nrOfElements).mapToObj(index -> { double x = (generator.next() - .5); double y = Math.pow(x, polynomialOrder); return new Tuple(x, y); }).collect(Collectors.toList()); } }