/*******************************************************************************
* 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 java.util.Objects.*;
import static org.junit.Assert.*;
import java.util.ArrayDeque;
import java.util.Queue;
import org.junit.Test;
import com.analog.lyric.dimple.events.DimpleEventHandler;
import com.analog.lyric.dimple.events.DimpleEventListener;
import com.analog.lyric.dimple.events.FactorGraphEvent;
import com.analog.lyric.dimple.events.FactorGraphFactorEvent;
import com.analog.lyric.dimple.events.FactorGraphSubgraphEvent;
import com.analog.lyric.dimple.events.FactorGraphVariableEvent;
import com.analog.lyric.dimple.factorfunctions.Normal;
import com.analog.lyric.dimple.model.core.BoundaryVariableAddEvent;
import com.analog.lyric.dimple.model.core.BoundaryVariableRemoveEvent;
import com.analog.lyric.dimple.model.core.FactorAddEvent;
import com.analog.lyric.dimple.model.core.FactorGraph;
import com.analog.lyric.dimple.model.core.FactorRemoveEvent;
import com.analog.lyric.dimple.model.core.Node;
import com.analog.lyric.dimple.model.core.SubgraphAddEvent;
import com.analog.lyric.dimple.model.core.SubgraphRemoveEvent;
import com.analog.lyric.dimple.model.core.VariableAddEvent;
import com.analog.lyric.dimple.model.core.VariableRemoveEvent;
import com.analog.lyric.dimple.model.factors.Factor;
import com.analog.lyric.dimple.model.variables.Real;
import com.analog.lyric.dimple.model.variables.Variable;
import com.analog.lyric.dimple.test.DimpleTestBase;
import com.analog.lyric.util.test.SerializationTester;
/**
* Tests for {@link FactorGraphEvent} generation.
*
* @since 0.06
* @author Christopher Barber
*/
public class TestFactorGraphEvent extends DimpleTestBase
{
@Test
public void test()
{
final FactorGraphEventHandler handler = new FactorGraphEventHandler();
final DimpleEventListener listener = new DimpleEventListener();
FactorGraph root = new FactorGraph();
root.setName("root");
// Initially listen for all factor graph events
root.getEnvironment().setEventListener(listener);
assertFalse(listener.isListeningFor(VariableAddEvent.class, root));
assertFalse(listener.isListeningFor(VariableRemoveEvent.class, root));
assertFalse(listener.isListeningFor(BoundaryVariableAddEvent.class, root));
assertFalse(listener.isListeningFor(BoundaryVariableRemoveEvent.class, root));
assertFalse(listener.isListeningFor(FactorAddEvent.class, root));
assertFalse(listener.isListeningFor(FactorRemoveEvent.class, root));
assertFalse(listener.isListeningFor(SubgraphAddEvent.class, root));
assertFalse(listener.isListeningFor(SubgraphRemoveEvent.class, root));
listener.register(handler, FactorGraphEvent.class, true, root);
root.notifyListenerChanged();
assertTrue(listener.isListeningFor(VariableAddEvent.class, root));
assertTrue(listener.isListeningFor(VariableRemoveEvent.class, root));
assertTrue(listener.isListeningFor(BoundaryVariableAddEvent.class, root));
assertTrue(listener.isListeningFor(BoundaryVariableRemoveEvent.class, root));
assertTrue(listener.isListeningFor(FactorAddEvent.class, root));
assertTrue(listener.isListeningFor(FactorRemoveEvent.class, root));
assertTrue(listener.isListeningFor(SubgraphAddEvent.class, root));
assertTrue(listener.isListeningFor(SubgraphRemoveEvent.class, root));
handler.assertNoEvents();
Real a = new Real();
a.setName("a");
Real b = new Real();
b.setName("b");
root.addVariables(a, b);
VariableAddEvent varAddEvent = handler.assertEvent(VariableAddEvent.class, root, a);
assertFalse(varAddEvent.wasAbsorbedFromSubgraph());
handler.assertEvent(VariableAddEvent.class, root, b);
handler.assertNoEvents();
Real c = new Real();
c.setName("c");
Factor f1 = root.addFactor(new Normal(0.0, 1.0), a, b, c);
f1.setName("f1");
varAddEvent = handler.assertEvent(VariableAddEvent.class, root, c);
assertFalse(varAddEvent.wasAbsorbedFromSubgraph());
FactorAddEvent factorAddEvent = handler.assertEvent(FactorAddEvent.class, root, f1);
assertFalse(factorAddEvent.wasAbsorbedFromSubgraph());
handler.assertNoEvents();
FactorGraph templateGraph = new FactorGraph();
templateGraph.setName("template");
Real tx = new Real();
tx.setName("x");
Real ty = new Real();
ty.setName("y");
Real tz = new Real();
tz.setName("z");
templateGraph.addBoundaryVariables(tz);
Factor tf = templateGraph.addFactor(new Normal(2.0, 2.0), tx ,ty, tz);
tf.setName("f2");
FactorGraph subgraph = root.addGraph(templateGraph, a);
SubgraphAddEvent subgraphAddEvent = handler.assertEvent(SubgraphAddEvent.class, root, subgraph);
assertFalse(subgraphAddEvent.wasAbsorbedFromSubgraph());
handler.assertNoEvents();
Variable x = subgraph.getVariableByName("x");
assertNotNull(x);
Variable y = subgraph.getVariableByName("y");
assertNotNull(y);
Variable z = subgraph.getVariableByName("z");
assertNull(z); // boundary variable from template is not instantiated in generated graph
Factor f2 = subgraph.getFactorByName("f2");
assertNotNull(f2);
root.absorbSubgraph(subgraph);
varAddEvent = handler.assertEvent(VariableAddEvent.class, root, x);
assertTrue(varAddEvent.wasAbsorbedFromSubgraph());
handler.assertEvent(VariableAddEvent.class, root, y);
factorAddEvent = handler.assertEvent(FactorAddEvent.class, root, f2);
assertTrue(factorAddEvent.wasAbsorbedFromSubgraph());
handler.assertEvent(SubgraphRemoveEvent.class, root, subgraph);
handler.assertNoEvents();
root.remove(f1);
handler.assertEvent(FactorRemoveEvent.class, root, f1);
handler.assertNoEvents();
root.remove(b);
handler.assertEvent(VariableRemoveEvent.class, root, b);
handler.assertNoEvents();
root.addBoundaryVariables(b);
handler.assertEvent(VariableAddEvent.class, root, b);
handler.assertEvent(BoundaryVariableAddEvent.class, root, b);
handler.assertNoEvents();
// FIXME: no BoundaryVariableRemoveEvent yet - see BUG 84
//
// Test individual listener flags
//
listener.unregisterAll();
root.notifyListenerChanged();
assertFalse(listener.isListeningFor(VariableAddEvent.class, root));
assertFalse(listener.isListeningFor(VariableRemoveEvent.class, root));
assertFalse(listener.isListeningFor(BoundaryVariableAddEvent.class, root));
assertFalse(listener.isListeningFor(BoundaryVariableRemoveEvent.class, root));
assertFalse(listener.isListeningFor(FactorAddEvent.class, root));
assertFalse(listener.isListeningFor(FactorRemoveEvent.class, root));
assertFalse(listener.isListeningFor(SubgraphAddEvent.class, root));
assertFalse(listener.isListeningFor(SubgraphRemoveEvent.class, root));
root.remove(c);
root.addVariables(c);
assertSame(c, root.getVariableByName("c"));
handler.assertNoEvents();
listener.register(handler, VariableRemoveEvent.class, false, root);
assertTrue(requireNonNull(root.getEventListener()).isListeningFor(VariableRemoveEvent.class, root));
root.notifyListenerChanged();
root.remove(c);
handler.assertEvent(VariableRemoveEvent.class, root, c);
handler.assertNoEvents();
listener.unregisterAll();
listener.register(handler, VariableAddEvent.class, false, root);
root.notifyListenerChanged();
root.addVariables(c);
handler.assertEvent(VariableAddEvent.class, root, c);
handler.assertNoEvents();
Factor f3 = root.addFactor(new Normal(), a, b, c);
root.remove(f3);
handler.assertNoEvents();
listener.unregisterAll();
listener.register(handler, FactorAddEvent.class, false, root);
root.notifyListenerChanged();
f3 = root.addFactor(new Normal(), a, b, c);
handler.assertEvent(FactorAddEvent.class, root, f3);
handler.assertNoEvents();
listener.unregisterAll();
listener.register(handler, FactorRemoveEvent.class, false, root);
root.notifyListenerChanged();
root.remove(f3);
handler.assertEvent(FactorRemoveEvent.class, root, f3);
handler.assertNoEvents();
}
private static class FactorGraphEventHandler extends DimpleEventHandler<FactorGraphEvent>
{
Queue<FactorGraphEvent> _queue = new ArrayDeque<FactorGraphEvent>();
@Override
public void handleEvent(FactorGraphEvent event)
{
_queue.add(event);
assertSame(event.getModelObject(), event.getSource());
assertNotNull(event.getSource());
}
private <Event extends FactorGraphEvent> Event assertEvent(Class<Event> eventType, FactorGraph source, Node node)
{
@SuppressWarnings("unchecked")
Event event = (Event) _queue.remove();
assertTrue(eventType.isAssignableFrom(event.getClass()));
assertSame(source, event.getSource());
if (event instanceof FactorGraphFactorEvent)
{
FactorGraphFactorEvent factorEvent = (FactorGraphFactorEvent)event;
assertSame(node, factorEvent.getFactor());
FactorGraphFactorEvent factorEvent2 = SerializationTester.clone(factorEvent);
assertNotSame(factorEvent2, factorEvent);
assertNull(factorEvent2.getFactor());
assertEquals(factorEvent.getFactorName(), factorEvent2.getFactorName());
}
else if (event instanceof FactorGraphVariableEvent)
{
FactorGraphVariableEvent variableEvent = (FactorGraphVariableEvent)event;
assertSame(node, variableEvent.getVariable());
FactorGraphVariableEvent variableEvent2 = SerializationTester.clone(variableEvent);
assertNotSame(variableEvent2, variableEvent);
assertNull(variableEvent2.getVariable());
assertEquals(variableEvent.getVariableName(), variableEvent2.getVariableName());
}
else if (event instanceof FactorGraphSubgraphEvent)
{
FactorGraphSubgraphEvent subgraphEvent = (FactorGraphSubgraphEvent)event;
assertSame(node, subgraphEvent.getSubgraph());
FactorGraphSubgraphEvent subgraphEvent2 = SerializationTester.clone(subgraphEvent);
assertNotSame(subgraphEvent2, subgraphEvent);
assertNull(subgraphEvent2.getSubgraph());
assertEquals(subgraphEvent.getSubgraphName(), subgraphEvent2.getSubgraphName());
}
return event;
}
private void assertNoEvents()
{
assertEquals(null, _queue.peek());
}
}
}