/******************************************************************************* * Copyright 2014 Analog Devices, Inc. * * 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 com.analog.lyric.dimple.test.model; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.lang.reflect.Array; import java.math.BigInteger; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.junit.Test; import com.analog.lyric.dimple.exceptions.DomainException; import com.analog.lyric.dimple.model.domains.ComplexDomain; import com.analog.lyric.dimple.model.domains.DiscreteDomain; import com.analog.lyric.dimple.model.domains.Domain; import com.analog.lyric.dimple.model.domains.DoubleRangeDomain; import com.analog.lyric.dimple.model.domains.EnumDomain; import com.analog.lyric.dimple.model.domains.FiniteFieldDomain; import com.analog.lyric.dimple.model.domains.IntDomain; import com.analog.lyric.dimple.model.domains.IntRangeDomain; import com.analog.lyric.dimple.model.domains.JointDiscreteDomain; import com.analog.lyric.dimple.model.domains.ObjectDomain; import com.analog.lyric.dimple.model.domains.RealDomain; import com.analog.lyric.dimple.model.domains.RealJointDomain; import com.analog.lyric.dimple.model.domains.TypedDiscreteDomain; import com.analog.lyric.dimple.model.values.Value; import com.analog.lyric.dimple.test.DimpleTestBase; import com.analog.lyric.util.test.SerializationTester; public class TestDomain extends DimpleTestBase { enum Bogus { X } enum E { A, B, C } @Test public void test() { // // Test reals // assertReal(RealDomain.unbounded(), Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); assertSame(RealDomain.unbounded(), RealDomain.create(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); assertReal(RealDomain.nonNegative(), 0.0, Double.POSITIVE_INFINITY); assertSame(RealDomain.nonNegative(), RealDomain.create(0.0, Double.POSITIVE_INFINITY)); assertReal(RealDomain.nonPositive(), Double.NEGATIVE_INFINITY, 0.0); assertSame(RealDomain.nonPositive(), RealDomain.create(Double.NEGATIVE_INFINITY, 0.0)); assertNotEquals(RealDomain.unbounded(), RealDomain.nonNegative()); assertNotEquals(RealDomain.unbounded(), RealDomain.nonPositive()); assertNotEquals(RealDomain.unbounded().hashCode(), RealDomain.nonNegative().hashCode()); assertFalse(RealDomain.unbounded().hasIntCompatibleValues()); RealDomain unit = RealDomain.create(0.0, 1.0); assertReal(unit, 0.0, 1.0); RealDomain percentile = RealDomain.create(0.0, 100.0); assertSame(percentile, RealDomain.create(0.0, 100.0)); assertReal(percentile, 0.0, 100.0); // // Test discretes // // Single-element discrete domains are allowed (and are used for constants) DiscreteDomain trivial = DiscreteDomain.create(Math.PI); assertInvariants(trivial); assertEquals(1, trivial.size()); assertEquals(Math.PI, trivial.getDoubleElement(0), 0.0); IntRangeDomain bit = DiscreteDomain.bit(); assertInvariants(bit); assertEquals(2, bit.size()); assertEquals(0, bit.getIntElement(0)); assertEquals(1, bit.getIntElement(1)); assertSame(bit, DiscreteDomain.create(0, 1)); assertTrue(bit.isIntegral()); assertEquals("{0,1}", bit.toString()); TypedDiscreteDomain<Integer> reverseBit = DiscreteDomain.create(1, 0); assertNotEquals(bit, reverseBit); assertInvariants(reverseBit); assertTrue(reverseBit.isIntegral()); assertEquals("{1,0}", reverseBit.toString()); TypedDiscreteDomain<Boolean> bool = DiscreteDomain.bool(); assertInvariants(bool); assertEquals(2, bool.size()); assertFalse(bool.getElement(0)); assertTrue(bool.getElement(1)); assertNotEquals(bit, bool); assertNotEquals(bit.hashCode(), bool.hashCode()); assertSame(bool, DiscreteDomain.create(false, true)); assertNotEquals(bool, DiscreteDomain.create(true, false)); assertFalse(bool.isIntegral()); assertEquals("{false,true}", bool.toString()); EnumDomain<E> e = DiscreteDomain.forEnum(E.class); assertInvariants(e); assertNotEquals(e, DiscreteDomain.forEnum(Bogus.class)); assertSame(e, DiscreteDomain.create(E.A, E.B, E.C)); assertEquals("{A,B,C}", e.toString()); try { DiscreteDomain.range(1,2,0); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("Non-positive interval")); } try { DiscreteDomain.range(2,1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("upper bound lower than lower bound")); } IntRangeDomain r1to5 = DiscreteDomain.range(1,5); assertInvariants(r1to5); assertTrue(r1to5.getIndex(1.01) < 0); assertTrue(r1to5.getIndex(6) < 0); for (int i = 0; i < 5; ++i) { assertEquals(i + 1, r1to5.getIntElement(i)); } assertSame(r1to5, DiscreteDomain.create(1,2,3,4,5)); assertSame(r1to5, DiscreteDomain.create(Arrays.asList(1,2,3,4,5))); assertSame(r1to5, DiscreteDomain.create(new Integer[] { 1,2,3,4,5 })); assertEquals("{1,2,3,4,5}", r1to5.toString()); IntRangeDomain r0to4 = DiscreteDomain.range(0,4); assertInvariants(r0to4); assertEquals(5, r0to4.size()); assertNotEquals(r0to4, r1to5); assertNotEquals(r0to4.hashCode(), r1to5.hashCode()); for (int i = 0; i < 5; ++i) { assertEquals(i, r0to4.getIndex(i)); } assertSame(r0to4, DiscreteDomain.create(0, 1, 2, 3, 4)); IntRangeDomain evenDigits = DiscreteDomain.range(0, 8, 2); assertInvariants(evenDigits); assertEquals(5, evenDigits.size()); assertNotEquals(evenDigits, r0to4); for (int i = 0; i <5; ++i) { assertEquals(i*2, evenDigits.getIntElement(i)); assertEquals(i, evenDigits.getIndex(i*2)); } assertSame(evenDigits, DiscreteDomain.create(0,2,4,6,8)); assertEquals("{0,2,4,6,8}", evenDigits.toString()); IntRangeDomain r0to3 = DiscreteDomain.range(0, 3); assertInvariants(r0to3); assertNotEquals(r0to3, r0to4); assertSame(r0to3, DiscreteDomain.create(0,1,2,3)); assertNotEquals(r0to3, DiscreteDomain.create(0,1.0,2,3)); TypedDiscreteDomain<Integer> r3to0 = DiscreteDomain.create(3,2,1,0); assertInvariants(r3to0); assertNotEquals(r0to3, r3to0); IntRangeDomain r1to99 = DiscreteDomain.range(1,99); assertInvariants(r1to99); assertEquals("{1,2,3,4,5,6,7,8,9,...,99}", r1to99.toString()); try { DiscreteDomain.range(1.0, 2.0, 0.0); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("Non-positive interval")); } try { DiscreteDomain.range(2.0,1.0); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("upper bound lower than lower bound")); } try { DiscreteDomain.range(1.0, 5.0, 1.0, .5); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("too large for interval")); } try { DiscreteDomain.range(1.0, 5.0, 1.0, -1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("Negative tolerance")); } try { DiscreteDomain.range(0, Integer.MAX_VALUE); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("more than MAX_INTEGER")); } DoubleRangeDomain halves = DiscreteDomain.range(0.0, 2.5, .5); assertInvariants(halves); assertSame(halves, DiscreteDomain.create(0.0, 0.5, 1.0, 1.5, 2.0, 2.5)); DoubleRangeDomain d0to3 = DiscreteDomain.range(0.0, 3.0); assertInvariants(d0to3); assertNotEquals(d0to3, halves); DoubleRangeDomain d0to3Loose = DiscreteDomain.range(0.0, 3.0, 1.0, .3); assertInvariants(d0to3Loose); assertNotEquals(d0to3, d0to3Loose); TypedDiscreteDomain<Double> d3to0 = DiscreteDomain.create(3.0, 2.0, 1.0); assertInvariants(d3to0); assertNotEquals(d0to3, d3to0); JointDiscreteDomain<?> joint1 = DiscreteDomain.joint(e, evenDigits); assertInvariants(joint1); assertEquals("{A,B,C}x{0,2,4,6,8}", joint1.toString()); JointDiscreteDomain<E> joint2 = DiscreteDomain.joint(e, e); assertInvariants(joint2); assertNotEquals(joint1, joint2); JointDiscreteDomain<Integer> joint3 = DiscreteDomain.joint(r1to5, r0to3); assertInvariants(joint2); assertEquals(0, joint3.getIndex(new int[] { 1, 0 })); assertEquals(-1, joint3.getIndex(new int[] { 0, 1 })); TypedDiscreteDomain<Integer> primes = DiscreteDomain.create(2,3,5,7,11,13); assertInvariants(primes); TypedDiscreteDomain<Double> powerOfTwo = DiscreteDomain.create(.125, .25, .5, 1, 2, 4, 8); assertInvariants(powerOfTwo); // Test for -0.0 TypedDiscreteDomain<Double> doublesWithZero = DiscreteDomain.create(0.0, 1.0, 3.0); assertInvariants(doublesWithZero); assertEquals(0, doublesWithZero.getIndex(0.0)); assertEquals(0, doublesWithZero.getIndex(-0.0)); TypedDiscreteDomain<Float> floatsWithZero = DiscreteDomain.create((float)0.0, (float)1.0, (float)3.0); assertInvariants(floatsWithZero); assertEquals(0, floatsWithZero.getIndex((float)0.0)); assertEquals(0, floatsWithZero.getIndex((float)-0.0)); // // FiniteFieldDomain // FiniteFieldDomain ffd = FiniteFieldDomain.create(0x2f); assertSame(ffd, FiniteFieldDomain.create(0x2f)); assertEquals(0x2f, ffd.getPrimitivePolynomial()); assertTrue(ffd.hasIntCompatibleValues()); assertTrue(ffd.isIntegral()); assertEquals(5, ffd.getN()); assertEquals(32, ffd.size()); assertInvariants(ffd); assertNotEquals(ffd, FiniteFieldDomain.create(0x1f)); // // Test RealJoint // try { RealJointDomain.create(); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("requires at least one domain")); } RealJointDomain realPlane = RealJointDomain.create(RealDomain.unbounded(), RealDomain.unbounded()); assertInvariants(realPlane); RealJointDomain realPlane2 = RealJointDomain.create(RealDomain.unbounded(), RealDomain.unbounded()); assertEquals(realPlane, realPlane2); RealJointDomain unitCube = RealJointDomain.create(unit, unit, unit); assertInvariants(unitCube); assertNotEquals(unitCube, realPlane); assertFalse(unitCube.inDomain(.5, 0.0, 1.5)); assertFalse(unitCube.inDomain(new Object[] { .5, 0.0, 1.5})); assertFalse(unitCube.valueInDomain(Value.createRealJoint(.5, 0.0, 1.5))); assertFalse(unitCube.valueInDomain(Value.createReal(.5))); assertTrue(unitCube.valueInDomain(Value.createRealJoint(.5, 0.0, .5))); assertSame(RealJointDomain.create(unit,3), unitCube); // // Test IntDomain // IntDomain intDomain = IntDomain.unbounded(); assertSame(intDomain, IntDomain.unbounded()); assertInvariants(intDomain); assertTrue(intDomain.isIntegral()); assertFalse(intDomain.isDiscrete()); assertTrue(intDomain.inDomain(42)); assertTrue(intDomain.inDomain((short)23)); assertTrue(intDomain.inDomain(-23.0)); assertFalse(intDomain.inDomain(1.5)); assertTrue(intDomain.valueInDomain(Value.createReal(1.0))); assertFalse(intDomain.valueInDomain(Value.createReal(1.0000001))); assertTrue(intDomain.valueInDomain(Value.create(DiscreteDomain.range(1, 10)))); // // Test ObjectDomain // ObjectDomain objDomain = ObjectDomain.instance(); assertSame(objDomain, ObjectDomain.instance()); assertInvariants(objDomain); assertTrue(objDomain.inDomain(42)); assertTrue(objDomain.inDomain("foo")); assertTrue(objDomain.inDomain(null)); assertTrue(objDomain.valueInDomain(Value.create("foo"))); // // Test static helper methods // assertTrue(Domain.isIntCompatibleClass(Integer.class)); assertTrue(Domain.isIntCompatibleClass(Short.class)); assertTrue(Domain.isIntCompatibleClass(Byte.class)); assertFalse(Domain.isIntCompatibleClass(Character.class)); assertFalse(Domain.isIntCompatibleClass(Integer.TYPE)); assertFalse(Domain.isIntCompatibleClass(Long.class)); assertFalse(Domain.isIntCompatibleClass(Float.class)); assertTrue(Domain.isIntCompatibleValue(4.0)); assertFalse(Domain.isIntCompatibleValue(4.01)); assertFalse(Domain.isIntCompatibleValue((double)(Integer.MAX_VALUE + 1))); assertFalse(Domain.isIntCompatibleValue((long)Integer.MAX_VALUE + 1)); assertFalse(Domain.isIntCompatibleValue(new Long(Integer.MAX_VALUE + 1))); assertTrue(Domain.isIntCompatibleValue(BigInteger.ONE)); } public static void assertReal(RealDomain real, double lower, double upper) { assertInvariants(real); assertEquals(lower, real.getLowerBound(), 0.0); assertEquals(upper, real.getUpperBound(), 0.0); assertFalse(real.isIntegral()); assertFalse(real.isDiscrete()); } @SuppressWarnings("deprecation") public static void assertInvariants(Domain domain) { assertNotEquals(domain, "foo"); assertEquals(domain, domain); assertEquals(domain.isNumber(), domain.isNumeric() && domain.isScalar()); Domain serializedCopy = SerializationTester.clone(domain); assertSame(domain, serializedCopy); if (domain != ObjectDomain.instance()) { assertFalse(domain.inDomain(Bogus.X)); assertFalse(domain.containsValueWithRepresentation(Bogus.X)); } if (domain.isReal()) { assertFalse(domain.isDiscrete()); assertFalse(domain.isRealJoint()); assertTrue(domain instanceof RealDomain); assertTrue(domain.isScalar()); assertTrue(domain.isNumeric()); assertFalse(domain.isIntegral()); assertFalse(domain.hasIntCompatibleValues()); RealDomain real = (RealDomain)domain; assertSame(real, domain.asReal()); double lower = real.getLowerBound(); double upper = real.getUpperBound(); assertEquals(real, RealDomain.create(lower, upper)); assertEquals(real.hashCode(), RealDomain.create(lower, upper).hashCode()); assertTrue(real.inDomain(lower)); assertTrue(real.inDomain(upper)); assertTrue(real.inDomain(lower)); assertTrue(real.inDomain(upper)); double tooLow = lower - 1; if (!Double.isInfinite(tooLow)) { assertFalse(real.inDomain(tooLow)); } double tooHigh = upper + 1; if (!Double.isInfinite(tooHigh)) { assertFalse(real.inDomain(tooHigh)); } assertTrue(real.isSubsetOf(real)); assertTrue(real.isSubsetOf(RealDomain.unbounded())); if (!real.equals(RealDomain.unbounded())) { assertFalse(RealDomain.unbounded().isSubsetOf(real)); } } else { assertNull(domain.asReal()); } if (domain.isDiscrete()) { assertFalse(domain.isRealJoint()); DiscreteDomain discrete = (DiscreteDomain)domain; assertSame(discrete, domain.asDiscrete()); TypedDiscreteDomain<Object> discreteObj = discrete.asTypedDomain(Object.class); assertSame(discreteObj, discrete); TypedDiscreteDomain<Number> discreteNum = discrete.asTypedDomain(Number.class); if (Number.class.isAssignableFrom(discrete.getElementClass())) { assertSame(discrete, discreteNum); assertTrue(domain.isNumeric()); } else { assertNull(discreteNum); assertFalse(domain.isNumeric()); } final int size = discrete.size(); assertTrue(size >= 0); final Object[] elements = discrete.getElements(); assertEquals(size, elements.length); assertFalse(discrete.containsValueWithRepresentation(-1)); assertFalse(discrete.containsValueWithRepresentation(size)); assertFalse(discrete.isElementOf(Bogus.X)); assertTrue(discrete.getIndex(Bogus.X) < 0); try { discrete.getIndexOrThrow(Bogus.X); fail("should not get here"); } catch (DomainException ex) { } Iterator<?> iter = null; if (discrete instanceof Iterable) { iter = ((Iterable<?>)discrete).iterator(); } List<?> list = discrete.asList(); assertEquals(size, list.size()); boolean allInt = true; for (int i = 0; i < size; ++i) { Object element = elements[i]; assertTrue(discrete.inDomain(element)); assertTrue(discrete.valueInDomain(Value.createWithIndex(discrete, i))); assertTrue(discrete.valueInDomain(Value.create(ObjectDomain.instance(), element))); assertTrue(discrete.containsValueWithRepresentation(i)); assertTrue(discrete.isElementOf(element)); assertElementEquals(element, discrete.getElement(i)); assertEquals(i, discrete.getIndex(element)); assertEquals(i, discrete.getIndexOrThrow(element)); assertElementEquals(element, list.get(i)); allInt &= Domain.isIntCompatibleValue(element); if (iter != null) { assertTrue(iter.hasNext()); assertElementEquals(element, iter.next()); } } if (iter != null) { assertFalse(iter.hasNext()); try { iter.remove(); fail("should not get here"); } catch (UnsupportedOperationException ex) { } } assertEquals(allInt, discrete.hasIntCompatibleValues()); try { discrete.getElement(-1); fail("expected exception"); } catch (IndexOutOfBoundsException ex) { } try { discrete.getElement(size); fail("expected exception"); } catch (IndexOutOfBoundsException ex) { } if (discrete instanceof IntRangeDomain) { IntRangeDomain range = (IntRangeDomain)discrete; assertTrue(range.getLowerBound() <= range.getUpperBound()); assertEquals(range.getLowerBound(), range.getIntElement(0)); assertTrue((range.size()-1) * range.getInterval() + range.getLowerBound() <= range.getUpperBound()); assertTrue(range.size() * range.getInterval() + range.getLowerBound() > range.getUpperBound()); assertTrue(discrete.hasIntCompatibleValues()); } if (discrete instanceof DoubleRangeDomain) { DoubleRangeDomain range = (DoubleRangeDomain)discrete; assertTrue(range.getLowerBound() <= range.getUpperBound()); assertEquals(range.getLowerBound(), range.getDoubleElement(0), 0.0); assertTrue(range.getTolerance() > 0.0); assertTrue(range.getTolerance() < range.getInterval()/2); assertTrue((range.size()-1) * range.getInterval() + range.getLowerBound() <= range.getUpperBound()); assertTrue(range.size() * range.getInterval() + range.getLowerBound() > range.getUpperBound()); for (int i = range.size(); --i>=0;) { double d = range.getDoubleElement(i); assertEquals(i, range.getIndex(d)); assertEquals(i, range.getIndex(d + range.getTolerance()/2)); assertEquals(i, range.getIndex(d - range.getTolerance()/2)); assertEquals(-1, range.getIndex(d + range.getInterval()/2)); } } if (discrete instanceof EnumDomain) { EnumDomain<?> e = (EnumDomain<?>)discrete; Class<?> eclass = e.getElementClass(); assertTrue(eclass.isEnum()); Object[] evalues = eclass.getEnumConstants(); assertEquals(evalues.length, e.size()); assertArrayEquals(evalues, e.getElements()); for (int i = 0; i < size; ++i) { assertEquals(i, e.getIndex(evalues[i])); } assertFalse(discrete.hasIntCompatibleValues()); } if (discrete instanceof JointDiscreteDomain) { assertFalse(discrete.isScalar()); assertFalse(discrete.hasIntCompatibleValues()); JointDiscreteDomain<?> joint = (JointDiscreteDomain<?>)discrete; assertEquals(joint.getDomainIndexer().size(), joint.getDimensions()); Object[] element = new Object[joint.getDimensions()]; int[] indices = new int[joint.getDimensions()]; for (int i = 0, end = joint.size(); i < end; ++i) { assertSame(element, joint.getElement(i, element)); assertArrayEquals(element, joint.getElement(i)); assertEquals(i, joint.getIndexFromSubelements(element)); assertEquals(indices, joint.getElementIndices(i, indices)); assertArrayEquals(indices, joint.getElementIndices(i)); assertEquals(i, joint.getIndexFromIndices(indices)); assertTrue(joint.containsValueWithRepresentation(indices)); assertFalse(joint.inDomain(Arrays.copyOf(elements, elements.length - 1))); assertFalse(joint.containsValueWithRepresentation(Arrays.copyOf(indices, indices.length - 1))); } Arrays.fill(element, Bogus.X); assertFalse(joint.inDomain(element)); Arrays.fill(indices, -1); assertFalse(joint.containsValueWithRepresentation(indices)); Arrays.fill(indices, joint.size()); assertFalse(joint.containsValueWithRepresentation(indices)); } else { assertTrue(discrete.isScalar()); } } else // Not Discrete { assertNull(domain.asDiscrete()); } if (domain instanceof RealJointDomain) { assertFalse(domain.isScalar()); assertFalse(domain.hasIntCompatibleValues()); RealJointDomain realJoint = (RealJointDomain)domain; assertSame(realJoint, domain.asRealJoint()); assertTrue(realJoint.isRealJoint()); assertFalse(realJoint.isReal()); assertFalse(realJoint.isDiscrete()); assertSame(realJoint, domain.asRealJoint()); int size = realJoint.getDimensions(); assertTrue(size > 1); assertEquals(size, realJoint.getRealDomains().length); assertEquals(size, realJoint.getNumVars()); double[] lower = new double[size]; double[] upper = new double[size]; for (int i = realJoint.getDimensions(); --i>=0;) { RealDomain subdomain = realJoint.getRealDomain(i); assertSame(realJoint.getRealDomains()[i], subdomain); lower[i] = subdomain.getLowerBound(); upper[i] = subdomain.getUpperBound(); } assertTrue(domain.inDomain(lower)); assertTrue(domain.inDomain(upper)); assertTrue(realJoint.inDomain(lower)); assertTrue(realJoint.inDomain(upper)); double[] lowerTooLong = Arrays.copyOf(lower, lower.length + 1); assertFalse(domain.inDomain(lowerTooLong)); assertFalse(realJoint.inDomain(lowerTooLong)); if (domain instanceof ComplexDomain) { ComplexDomain complex = (ComplexDomain)domain; assertSame(complex, domain.asComplex()); assertTrue(domain.isComplex()); } else { assertNull(domain.asComplex()); assertFalse(domain.isComplex()); } } else { assertNull(domain.asRealJoint()); assertNull(domain.asComplex()); assertFalse(domain.isComplex()); } } private static void assertElementEquals(Object elt1, Object elt2) { if (elt1.getClass().isArray() && elt2.getClass().isArray()) { assertEquals(Array.getLength(elt1), Array.getLength(elt2)); for (int i = 0, end = Array.getLength(elt1); i < end; ++i) { assertEquals(Array.get(elt1, i), Array.get(elt2, i)); } } else { assertEquals(elt1, elt2); } } }