/*******************************************************************************
* Copyright 2015 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.schedulers;
import static com.analog.lyric.dimple.model.sugar.ModelSyntacticSugar.*;
import static com.analog.lyric.util.test.ExceptionTester.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Test;
import com.analog.lyric.dimple.factorfunctions.Xor;
import com.analog.lyric.dimple.model.core.FactorGraph;
import com.analog.lyric.dimple.model.core.INode;
import com.analog.lyric.dimple.model.core.Port;
import com.analog.lyric.dimple.model.factors.Factor;
import com.analog.lyric.dimple.model.sugar.ModelSyntacticSugar.CurrentModel;
import com.analog.lyric.dimple.model.variables.Bit;
import com.analog.lyric.dimple.model.variables.VariableBlock;
import com.analog.lyric.dimple.options.BPOptions;
import com.analog.lyric.dimple.schedulers.CustomScheduler;
import com.analog.lyric.dimple.schedulers.IScheduler;
import com.analog.lyric.dimple.schedulers.schedule.FixedSchedule;
import com.analog.lyric.dimple.schedulers.schedule.ISchedule;
import com.analog.lyric.dimple.schedulers.schedule.ScheduleValidationException;
import com.analog.lyric.dimple.schedulers.scheduleEntry.BlockScheduleEntry;
import com.analog.lyric.dimple.schedulers.scheduleEntry.EdgeScheduleEntry;
import com.analog.lyric.dimple.schedulers.scheduleEntry.IScheduleEntry;
import com.analog.lyric.dimple.schedulers.scheduleEntry.NodeScheduleEntry;
import com.analog.lyric.dimple.schedulers.scheduleEntry.SubScheduleEntry;
import com.analog.lyric.dimple.solvers.gibbs.GibbsOptions;
import com.analog.lyric.dimple.solvers.gibbs.samplers.block.BlockMHSampler;
import com.analog.lyric.dimple.test.DimpleTestBase;
/**
* Test for {@link CustomScheduler} class.
* <p>
* @since 0.08
* @author Christopher Barber
*/
@SuppressWarnings("deprecation")
public class TestCustomScheduler extends DimpleTestBase
{
@SuppressWarnings("deprecation")
@Test
public void test()
{
FactorGraph fg = new FactorGraph("fg");
try (CurrentModel cur = using(fg))
{
Bit a = bit("a");
Bit b = bit("b");
Bit c = bit("c");
VariableBlock abc = fg.addVariableBlock(a,b,c);
VariableBlock ab = fg.addVariableBlock(a,b);
VariableBlock bc = fg.addVariableBlock(b,c);
Factor axorb = name("axorb", addFactor(new Xor(), a, b));
Factor bxorc = name("bxorc", addFactor(new Xor(), b, c));
@SuppressWarnings("deprecation")
CustomScheduler scheduler = new CustomScheduler(fg);
assertSame(fg, scheduler.getGraph());
assertNull(scheduler.declaredSchedulerType());
assertArrayEquals(new Object[] { BPOptions.scheduler, GibbsOptions.scheduler },
scheduler.applicableSchedulerOptions().toArray());
assertEquals(0, scheduler.createSchedule(fg).size());
assertSchedule(scheduler);
// Basic manual tree schedule
CustomScheduler bpScheduler = new CustomScheduler(fg, BPOptions.scheduler);
assertTrue(bpScheduler.isMutable());
assertTrue(bpScheduler.isCustomScheduler());
assertSame(fg, bpScheduler.getGraph());
assertSame(BPOptions.scheduler, bpScheduler.declaredSchedulerType());
assertArrayEquals(new Object[] { BPOptions.scheduler }, bpScheduler.applicableSchedulerOptions().toArray());
bpScheduler.validateForGraph(fg);
bpScheduler.addEdge(a.getPort(0));
bpScheduler.addEdge(axorb, b);
bpScheduler.addEdge(c.getPort(0));
bpScheduler.addEdge(bxorc, b);
bpScheduler.addNode(b);
bpScheduler.addEdges(port(axorb, a), port(bxorc, c));
assertSchedule(bpScheduler,
port(a, axorb), port(axorb, b), port(c, bxorc), port(bxorc, b), b, port(axorb, a), port(bxorc, c));
// Try again using paths
bpScheduler = new CustomScheduler(fg, BPOptions.scheduler);
bpScheduler.addPath(a, axorb, b, bxorc, c);
bpScheduler.addPath(c, b, a);
bpScheduler.addFactor(axorb);
bpScheduler.addFactors(bxorc, axorb);
assertSchedule(bpScheduler,
port(a, axorb), port(axorb, b), port(b, bxorc), port(bxorc, c),
port(c, bxorc), port(bxorc, b), port(b, axorb), port(axorb, a),
axorb, bxorc, axorb);
bpScheduler.addBlockWithReplacement(new BlockMHSampler(), ab);
bpScheduler.addBlockWithReplacement(new BlockMHSampler(), ab);
assertSchedule(bpScheduler,
port(axorb, b), port(bxorc, c),
port(c, bxorc), port(bxorc, b), port(axorb, a),
axorb, bxorc, axorb, ab, ab);
CustomScheduler bpScheduler2 = new CustomScheduler(fg, BPOptions.scheduler);
bpScheduler2.addAll(bpScheduler.createSchedule(fg));
assertSchedule(bpScheduler2,
port(axorb, b), port(bxorc, c),
port(c, bxorc), port(bxorc, b), port(axorb, a),
axorb, bxorc, axorb, ab, ab);
// Gibbs schedule
CustomScheduler gibbsScheduler = new CustomScheduler(fg, GibbsOptions.scheduler);
gibbsScheduler.addNode(a);
gibbsScheduler.addNodes(b,c);
gibbsScheduler.addVariable(a);
gibbsScheduler.addVariables(b,c);
// When this fails, no entries should be added:
expectThrow(ScheduleValidationException.class, gibbsScheduler, "addVariables", c,c,c,new Bit());
assertSchedule(gibbsScheduler, a,b,c,a,b,c);
gibbsScheduler.addBlock(new BlockMHSampler(), b, c);
assertSchedule(gibbsScheduler, a,b,c,a,b,c,bc);
gibbsScheduler.addBlockWithReplacement(new BlockMHSampler(), abc);
assertSchedule(gibbsScheduler, bc, abc);
// Errors
expectThrow(ScheduleValidationException.class, bpScheduler, "addNode", new Bit());
expectThrow(ScheduleValidationException.class, "Cannot use edge entry with Gibbs.*",
gibbsScheduler, "addEdge", a, axorb);
expectThrow(ScheduleValidationException.class, "Cannot use factor node entry with Gibbs.*",
gibbsScheduler, "addNode", axorb);
expectThrow(ScheduleValidationException.class, ".*can only be used with graph.*",
bpScheduler, "validateForGraph", new FactorGraph());
expectThrow(ScheduleValidationException.class, ".*requires at least two.*", bpScheduler, "addPath", a);
expectThrow(ScheduleValidationException.class, ".*not adjacent.*", bpScheduler, "addPath", a, c);
expectThrow(ScheduleValidationException.class, "Cannot add SubScheduleEntry.*",
bpScheduler, "addAll", Arrays.asList(new SubScheduleEntry(new FixedSchedule())));
}
FactorGraph fg2 = new FactorGraph("fg2");
try (CurrentModel cur = using(fg2))
{
Bit a = bit("a");
Bit b = bit("b");
name("axorb", addFactor(new Xor(), a, b));
Factor aaxorb = name("aaxorb", addFactor(new Xor(), a, a, b));
CustomScheduler scheduler = new CustomScheduler(fg2, BPOptions.scheduler);
expectThrow(ScheduleValidationException.class, ".*not a unique path.*",
scheduler, "addPath", a, aaxorb);
expectThrow(ScheduleValidationException.class, ".*connected by more than one path.*",
scheduler, "addPath", a, b);
}
}
private static Port port(INode from, INode to)
{
return from.getPort(from.findSibling(to));
}
public static void assertSchedule(CustomScheduler scheduler, Object ... entries)
{
assertSchedule(scheduler.createSchedule(scheduler.getGraph()), entries);
}
public static void assertSchedule(IScheduler scheduler, FactorGraph graph, Object ...entries)
{
assertSchedule(scheduler.createSchedule(graph), entries);
}
public static void assertSchedule(ISchedule schedule, Object ... entries)
{
int i = 0;
for (IScheduleEntry entry : schedule)
{
assertTrue(i < entries.length);
Object expected = entries[i];
if (expected instanceof INode)
{
assertTrue(entry instanceof NodeScheduleEntry);
assertSame(expected, ((NodeScheduleEntry)entry).getNode());
}
else if (expected instanceof Port)
{
Port port = (Port)expected;
assertTrue(entry instanceof EdgeScheduleEntry);
EdgeScheduleEntry edgeEntry = (EdgeScheduleEntry)entry;
assertSame(port.getNode(), edgeEntry.getNode());
assertEquals(port.getSiblingNumber(), edgeEntry.getPortNum());
}
else if (expected instanceof VariableBlock)
{
VariableBlock block = (VariableBlock)expected;
assertTrue(entry instanceof BlockScheduleEntry);
BlockScheduleEntry blockEntry = (BlockScheduleEntry)entry;
assertSame(block, blockEntry.getBlock());
}
++i;
}
assertEquals(entries.length, i);
}
}