/*******************************************************************************
* 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.parameters;
import static org.junit.Assert.*;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.Ignore;
import org.junit.Test;
import com.analog.lyric.dimple.exceptions.DimpleException;
import com.analog.lyric.dimple.model.domains.RealDomain;
import com.analog.lyric.dimple.parameters.IParameterKey;
import com.analog.lyric.dimple.parameters.IParameterList;
import com.analog.lyric.dimple.parameters.Parameter;
import com.analog.lyric.dimple.parameters.ParameterKey;
import com.analog.lyric.dimple.parameters.ParameterList1;
import com.analog.lyric.dimple.parameters.ParameterList2;
import com.analog.lyric.dimple.parameters.ParameterListN;
import com.analog.lyric.dimple.parameters.SharedParameterValue;
import com.analog.lyric.dimple.test.DimpleTestBase;
import com.analog.lyric.options.IOptionHolder;
import com.analog.lyric.options.IOptionKey;
import com.analog.lyric.util.test.SerializationTester;
import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.AtomicDouble;
import com.google.common.util.concurrent.AtomicDoubleArray;
public class TestParameters extends DimpleTestBase
{
/*---------------
* Test classes
*/
public static class ParamN extends ParameterListN<IParameterKey>
{
private static final long serialVersionUID = 1L;
public ParamN(int size)
{
super(size);
}
public ParamN(ParamN that)
{
super(that);
}
public ParamN(double ... values)
{
super(values);
}
public ParamN(boolean fixed, double ... values)
{
super(fixed, values);
}
@Override
public @Nullable IParameterKey[] getKeys()
{
return null;
}
@Override
public ParamN clone()
{
return new ParamN(this);
}
}
@SuppressWarnings("null")
public static enum Alpha implements IParameterKey
{
alpha(0, RealDomain.unbounded());
private final double _defaultValue;
private final RealDomain _domain;
private Alpha(double defaultValue, RealDomain domain)
{
_defaultValue = defaultValue;
_domain = domain;
}
@Override
public Class<Double> type() { return Double.class; }
@Override
public Object convertToExternal(Double value)
{
return value;
}
@Override
public Double convertToValue(Object value) { return type().cast(value); }
@Override
public Double defaultValue() { return _defaultValue; }
@Override
public Double getOrDefault(IOptionHolder holder) { return holder.getOptionOrDefault(this); }
@Override
public @Nullable Double get(IOptionHolder holder) { return holder.getOption(this); }
@Override
public boolean local() { return false; }
@Override
public IOptionKey.Lookup lookupMethod()
{
return IOptionKey.Lookup.NONLOCAL;
}
@Override
public void set(IOptionHolder holder, Double value) { holder.setOption(this, value); }
@Override
public void unset(IOptionHolder holder) { holder.unsetOption(this); }
@Override
public RealDomain domain() { return _domain; }
@Override
public boolean validForDelegator(Double value, IOptionHolder delegator)
{
return true;
}
@Override
public Double validate(Double value, IOptionHolder optionHolder)
{
return type().cast(value);
}
}
public static class Param1 extends ParameterList1<Alpha>
{
private static final long serialVersionUID = 1L;
public Param1()
{
}
public Param1(Param1 that)
{
super(that);
}
@Override
public Alpha[] getKeys()
{
return Alpha.values();
}
@Override
public Param1 clone()
{
return new Param1(this);
}
}
public static class AlphaBeta
{
public static final ParameterKey alpha = new ParameterKey(0, AlphaBeta.class, "alpha");
public static final ParameterKey beta = new ParameterKey(1, AlphaBeta.class, "beta");
public static ParameterKey[] values()
{
return new ParameterKey[] { alpha, beta };
}
}
public static class Param2 extends ParameterList2<ParameterKey>
{
private static final long serialVersionUID = 1L;
public Param2()
{
}
public Param2(Param2 that)
{
super(that);
}
@Override
public ParameterKey[] getKeys()
{
return AlphaBeta.values();
}
@Override
public ParameterList2<ParameterKey> clone()
{
return new Param2(this);
}
}
/*------------
* Test cases
*/
@Test
public void testParameterList()
{
Param1 n1 = new Param1();
assertEquals(1, n1.size());
assertParameterListInvariants(n1);
assertFalse(n1.isShared(Alpha.alpha));
Param2 n2 = new Param2();
assertEquals(2, n2.size());
assertParameterListInvariants(n2);
assertFalse(n2.isShared(0));
assertFalse(n2.isShared(AlphaBeta.beta));
// Parameter sharing
assertTrue(n1.canShare());
assertTrue(n2.canShare());
n2.setShared(AlphaBeta.alpha, true);
assertNotNull(n2.getSharedValue(AlphaBeta.alpha));
n1.setSharedValue(Alpha.alpha, n2.getSharedValue(AlphaBeta.alpha));
assertSame(n1.getSharedValue(Alpha.alpha), n2.getSharedValue(AlphaBeta.alpha));
n1.set(0, 42);
assertEquals(42, n2.get(0), 0.0);
n2.setFixed(AlphaBeta.beta, true);
assertParameterListInvariants(n1);
assertParameterListInvariants(n2);
ParamN n10 = new ParamN(10);
assertEquals(10, n10.size());
assertParameterListInvariants(n10);
assertFalse(n10.canShare());
n10 = new ParamN(1,2,3,4,5,6,7,8,9,10);
assertEquals(10, n10.size());
assertParameterListInvariants(n10);
for (int i = 0; i < n10.size(); ++i)
{
assertEquals(i + 1, n10.get(i), 0.0);
}
ParamN n5 = new ParamN(true, 0, 2, 4, 6, 8);
assertEquals(5, n5.size());
assertParameterListInvariants(n5);
for (int i = 0; i < 5; ++i)
{
assertTrue(n5.isFixed(i));
assertEquals(i * 2, n5.get(i), 0.0);
}
}
private double d;
private double d2;
@Test
@Ignore
public void testAtomicDoublePerformance()
{
// Measure performance of AtomicDouble types used by parameter class implementations.
// Slowdown appears to be about 4.5x for AtomicDoubleArray and 2x for AtomicDouble
boolean verbose = true;
AtomicDoubleArray n100 = new AtomicDoubleArray(100);
double[] d100 = new double[100];
double[] out100 = new double[100];
Stopwatch stopwatch = Stopwatch.createUnstarted();
final int n = 1000;
stopwatch.start();
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 100; ++j)
{
n100.set(j, j);
}
}
stopwatch.stop();
long atomicNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
stopwatch.reset();
stopwatch.start();
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 100; ++j)
{
d100[j] = j;
}
}
stopwatch.stop();
long doubleNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
double atomicNsPerWrite = atomicNs / (100 * n);
double doubleNsPerWrite = doubleNs / (100 * n);
double ratio = atomicNsPerWrite / doubleNsPerWrite;
if (verbose)
{
System.out.format("AtomicDoubleArray/double[] WRITE: %.0f/%.0f ns/write (%.1fX slowdown)\n",
atomicNsPerWrite, doubleNsPerWrite, ratio);
}
stopwatch.start();
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 100; ++j)
{
out100[j] = n100.get(j);
}
}
stopwatch.stop();
atomicNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
stopwatch.reset();
stopwatch.start();
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 100; ++j)
{
out100[j] = d100[j];
}
}
stopwatch.stop();
doubleNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
atomicNsPerWrite = atomicNs / (100 * n);
doubleNsPerWrite = doubleNs / (100 * n);
ratio = atomicNsPerWrite / doubleNsPerWrite;
if (verbose)
{
System.out.format("AtomicDoubleArray/double[] READ: %.0f/%.0f ns/read (%.1fX slowdown)\n",
atomicNsPerWrite, doubleNsPerWrite, ratio);
}
AtomicDouble a = new AtomicDouble();
// double d = 0.0, d2 = 0.0;
final int n2 = n * 100;
stopwatch.reset();
stopwatch.start();
for (int i = 0; i < n2; ++i)
{
a.set(d2);
}
stopwatch.stop();
atomicNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
stopwatch.reset();
stopwatch.start();
for (int i = 0; i < n2; ++i)
{
d = d2;
}
stopwatch.stop();
doubleNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
atomicNsPerWrite = atomicNs / n2;
doubleNsPerWrite = doubleNs / n2;
ratio = atomicNsPerWrite / doubleNsPerWrite;
if (verbose)
{
System.out.format("AtomicDouble/double WRITE: %.0f/%.0f ns/write (%.1fX slowdown)\n",
atomicNsPerWrite, doubleNsPerWrite, ratio);
}
stopwatch.reset();
stopwatch.start();
for (int i = 0; i < n2; ++i)
{
d2 = a.get();
}
stopwatch.stop();
atomicNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
stopwatch.reset();
stopwatch.start();
for (int i = 0; i < n2; ++i)
{
d2 = d;
}
stopwatch.stop();
doubleNs = stopwatch.elapsed(TimeUnit.NANOSECONDS);
atomicNsPerWrite = atomicNs / n2;
doubleNsPerWrite = doubleNs / n2;
ratio = atomicNsPerWrite / doubleNsPerWrite;
if (verbose)
{
System.out.format("AtomicDouble/double READ: %.0f/%.0f ns/read (%.1fX slowdown)\n",
atomicNsPerWrite, doubleNsPerWrite, ratio);
}
}
/*-----------------
* Helper methods
*/
public static <Key extends IParameterKey> void assertParameterListInvariants(IParameterList<Key> list)
{
assertTrue(list.size() > 0);
double[] values = list.getValues();
assertEquals(list.size(), values.length);
for (int i = 0; i < values.length; ++i)
{
double val = list.get(i);
assertEquals(val, values[i], 0.0);
SharedParameterValue sharedValue = list.getSharedValue(i);
assertEquals(list.isShared(i), sharedValue != null);
if (sharedValue != null)
{
assertEquals(val, sharedValue.get(), 0.0);
}
if (list.isFixed(i))
{
try
{
list.set(i, val + 1);
fail("should not get here");
}
catch (DimpleException ex)
{
}
assertEquals(val, list.get(i), 0.0);
}
}
Key[] keys = list.getKeys();
assertEquals(list.hasKeys(), keys != null);
if (keys != null)
{
assertEquals(list.size(), keys.length);
for (int i = 0; i < keys.length; ++i)
{
Key key = keys[i];
double val = list.get(i);
assertEquals(i, key.ordinal());
assertEquals(val, list.get(key), 0.0);
assertEquals(list.getSharedValue(i), list.getSharedValue(key));
assertEquals(list.isFixed(i), list.isFixed(key));
assertEquals(list.isShared(i), list.isShared(key));
if (list.isFixed(key))
{
try
{
list.set(key, val + 1);
fail("should not get here");
}
catch (DimpleException ex)
{
}
assertEquals(val, list.get(key), 0.0);
}
}
}
{
int i = 0;
for (Parameter<Key> param : list)
{
assertEquals(i, param.index());
assertEquals(list.get(i), param.value(), 0.0);
assertSame(list.getSharedValue(i), param.sharedValue());
if (keys != null)
{
assertEquals(keys[i], param.key());
}
assertEquals(!Double.isNaN(list.get(i)), param.known());
++i;
}
}
//
// Errors
//
try
{
list.get(-1);
fail("should not get here");
}
catch (IndexOutOfBoundsException ex)
{
}
try
{
list.get(list.size());
fail("should not get here");
}
catch (IndexOutOfBoundsException ex)
{
}
if (keys == null)
{
try
{
@SuppressWarnings("unchecked")
Key key = (Key) new ParameterKey(0, Object.class, "bogus");
list.get(key);
fail("should not get here");
}
catch (UnsupportedOperationException ex)
{
}
}
if (!list.canShare())
{
try
{
list.setShared(0, true);
fail("should not get here");
}
catch (UnsupportedOperationException ex)
{
}
assertFalse(list.isShared(0));
list.setShared(0, false);
try
{
list.setSharedValue(0, new SharedParameterValue());
fail("should not get here");
}
catch (UnsupportedOperationException ex)
{
}
list.setSharedValue(0, null);
}
//
// Cloning
//
IParameterList<Key> copy1 = list.clone();
assertNotSame(copy1, list);
assertParameterListEquals(list, copy1);
for (int i = 0; i < list.size(); ++i)
{
assertSame(list.getSharedValue(i), copy1.getSharedValue(i));
}
IParameterList<Key> copy2 = SerializationTester.clone(list);
assertNotSame(copy2, list);
assertParameterListEquals(list, copy2);
//
// Test mutable cases on cloned list
//
copy2.setAllFixed(false);
for (int i = 0; i < copy2.size(); ++i)
{
copy2.setShared(i, false);
assertFalse(copy2.isShared(i));
}
for (int i = 0; i < copy2.size(); ++i)
{
assertFalse(copy2.isFixed(i));
copy2.set(i, i);
assertEquals(i, copy2.get(i), 0.0);
}
copy2.setAllMissing();
for (Parameter<Key> param : copy2)
{
assertFalse(param.known());
}
copy2.setAllToDefault();
if (keys != null)
{
for (int i = 0; i <copy2.size(); ++i)
{
assertEquals(keys[i].defaultValue(), copy2.get(i), 0.0);
}
}
copy2.setAll(copy1.getValues());
copy2.setAllFixed(true);
copy2.setAllToDefault(); // Does nothing if everything is fixed
copy2.setAllMissing(); // ditto
try
{
copy2.setAll(new double[list.size()]);
fail("should not get here");
}
catch (DimpleException ex)
{
}
for (int i = 0; i < copy2.size(); ++i)
{
assertTrue(copy2.isFixed(i));
assertEquals(list.get(i), copy2.get(i), 0.0);
}
// Fix every-other parameter, and set non-fixed params.
for (int i = 0; i < copy2.size(); i += 2)
{
copy2.setFixed(i, false);
}
copy2.setAllMissing();
for (Parameter<Key> param : copy2)
{
switch (param.index() % 2)
{
case 0:
assertFalse(param.fixed());
assertFalse(param.known());
break;
case 1:
assertTrue(param.fixed());
assertEquals(copy2.get(param.index()), param.value(), 0.0);
break;
}
}
for (int i = 0; i < copy2.size(); ++i)
{
copy2.setFixed(i, !copy2.isFixed(i));
}
copy2.setAllToDefault();
for (Parameter<Key> param : copy2)
{
switch (param.index() % 2)
{
case 0:
assertTrue(param.fixed());
assertFalse(param.known());
break;
case 1:
assertFalse(param.fixed());
Key key = param.key();
if (key != null)
{
assertEquals(key.defaultValue(), param.value(), 0.0);
}
else
{
assertEquals(copy2.get(param.index()), param.value(), 0.0);
}
break;
}
}
} // assertParameterListInvariants
public static <Key extends IParameterKey> void assertParameterListEquals(IParameterList<Key> list1,
IParameterList<Key> list2)
{
assertEquals(list1.getClass(), list2.getClass());
assertEquals(list1.size(), list2.size());
assertArrayEquals(list1.getValues(), list2.getValues(), 0.0);
assertArrayEquals(list1.getKeys(), list2.getKeys());
for (int i = list1.size(); --i >=0;)
{
assertEquals(list1.isFixed(i), list2.isFixed(i));
assertEquals(list1.isShared(i), list2.isShared(i));
}
}
}