/*******************************************************************************
* 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.solvers.interfaces;
import static java.util.Objects.*;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.dimple.data.DataLayer;
import com.analog.lyric.dimple.data.IDatum;
import com.analog.lyric.dimple.model.core.FactorGraph;
import com.analog.lyric.dimple.model.core.INode;
import com.analog.lyric.dimple.model.core.Node;
import com.analog.lyric.dimple.model.factors.Factor;
import com.analog.lyric.dimple.model.variables.Variable;
import com.analog.lyric.dimple.model.variables.VariableBlock;
import com.analog.lyric.util.misc.Internal;
/**
* Maps model objects to their corresponding solvers in the tree of {@link FactorGraph}s under a common
* {@linkplain com.analog.lyric.dimple.model.core.INode#getRootGraph() root}.
* <p>
* @since 0.08
* @author Christopher Barber
* @see ISolverFactorGraph#getSolverMapping()
*/
public abstract class SolverNodeMapping
{
/*-------
* State
*/
private @Nullable DataLayer<? extends IDatum> _conditioningLayer = null;
/*-------------------
* Abstract methods
*/
/**
* Add a new subgraph to the mapping..
* <p>
* This is called by the {@link ISolverFactorGraph} implementation when a subgraph is added, and should
* not be invoked in other situations. Solvers that inherit from the standard
* {@linkplain com.analog.lyric.dimple.solvers.core.SFactorGraphBase SFactorGraphBase} class do not have
* to call this method explicitly.
* @param sgraph solver graph that has just been added as a subgraph of a graph already in the tree.
* @since 0.08
* @category internal
*/
@Internal
public abstract void addSolverGraph(ISolverFactorGraph sgraph);
/*-------------------
* Abstract methods
*/
/**
* Returns the root solver graph in the tree.
* <p>
* This will be the solver that is associated with the root graph in the model graph tree.
* <p>
* @since 0.08
*/
public abstract ISolverFactorGraph getRootSolverGraph();
/**
* Get solver graph for given model graph.
* <p>
* @param graph must be in the model graph tree associated with this solver graph tree.
* @param create indicates whether to create the solver graph on demand. If true, this method should return
* a non-null value or throw an exception.
* @return solver graph whose {@linkplain ISolverFactorGraph#getModelObject() model} is the specified {@code graph}
* or else null if it does not exist and {@code create} is false.
* @throws IllegalArgumentException if {@code graph} does not belong to the same model graph tree
* @since 0.08
* @see #inGraphTree(Node)
* @see #getSolverGraphOrNull(FactorGraph)
* @see #getSolverGraph(FactorGraph)
*/
public abstract @Nullable ISolverFactorGraph getSolverGraph(FactorGraph graph, boolean create);
/**
* Remove a subgraph from the mapping.
* <p>
* This is called by the {@link ISolverFactorGraph} implementation when a subgraph is removed, and should
* not be invoked in other situations. Solvers that inherit from the standard
* {@linkplain com.analog.lyric.dimple.solvers.core.SFactorGraphBase SFactorGraphBase} class do not have
* to call this method explicitly.
* @param sgraph is a subgraph of a graph in the tree that has just been removed.
* @since 0.08
* @category internal
*/
@Internal
public abstract void removeSolverGraph(ISolverFactorGraph sgraph);
/*------------------
* Concrete methods
*/
/**
* Layer containing conditioning information for solver.
* @since 0.08
* @see #setConditioningLayer
*/
public final @Nullable DataLayer<? extends IDatum> getConditioningLayer()
{
return _conditioningLayer;
}
/**
* The root of the {@link FactorGraph} tree associated with this state.
* <p>
* This is simply the {@linkplain ISolverFactorGraph#getModelObject() model graph} of
* the {@linkplain #getRootSolverGraph() root solver graph}.
* @since 0.08
*/
public FactorGraph getRootGraph()
{
return getRootSolverGraph().getModelObject();
}
/**
* Return solver graph for given model graph.
* <p>
* Simply invokes {@link #getSolverGraph(FactorGraph, boolean)} with {@code create} set to {@code true}.
* @since 0.08
*/
public ISolverFactorGraph getSolverGraph(FactorGraph graph)
{
return requireNonNull(getSolverGraph(graph, true));
}
/**
* Return solver graph, if it exists, for given model graph.
* <p>
* Simply invokes {@link #getSolverGraph(FactorGraph, boolean)} with {@code create} set to {@code false}.
* @since 0.08
*/
public @Nullable ISolverFactorGraph getSolverGraphOrNull(FactorGraph graph)
{
return getSolverGraph(graph, false);
}
/**
* Get solver factor for given model factor.
* <p>
* @param factor must be in the model graph tree associated with this solver graph tree.
* @param create indicates whether to create the solver factor on demand. If true, this method should return
* a non-null value or throw an exception.
* @return solver factor whose {@linkplain ISolverFactor#getModelObject() model} is the specified {@code factor}
* or else null if it does not exist and {@code create} is false.
* @throws IllegalArgumentException if {@code factor} does not belong to the same model graph tree
* @since 0.08
* @see #inGraphTree(Node)
* @see #getSolverFactorOrNull(Factor)
* @see #getSolverFactor(Factor)
*/
public @Nullable ISolverFactor getSolverFactor(Factor factor, boolean create)
{
ISolverFactorGraph sgraph = getSolverGraph(factor.requireParentGraph(), create);
if (sgraph != null)
{
return sgraph.getSolverFactor(factor, create);
}
return null;
}
/**
* Return solver factor, if it exists, for given model factor.
* <p>
* Simply invokes {@link #getSolverFactor(Factor, boolean)} with {@code create} set to {@code false}.
* @since 0.08
*/
public @Nullable ISolverFactor getSolverFactorOrNull(Factor factor)
{
return getSolverFactor(factor, false);
}
/**
* Return solver factor for given model factor.
* <p>
* Simply invokes {@link #getSolverFactor(Factor, boolean)} with {@code create} set to {@code true}.
* @since 0.08
*/
@SuppressWarnings("null")
public ISolverFactor getSolverFactor(Factor factor)
{
return getSolverGraph(factor.getParentGraph()).getSolverFactor(factor, true);
}
/**
* Return solver node for given model node, creating if necessary.
* @throws NullPointerException if there is no such
* @since 0.08
*/
public ISolverNode getSolverNode(INode node)
{
ISolverNode snode = null;
switch (node.getNodeType())
{
case FACTOR:
snode = getSolverFactor((Factor)node);
break;
case GRAPH:
snode = getSolverGraph((FactorGraph)node);
break;
case VARIABLE:
snode = getSolverVariable((Variable)node);
break;
}
return requireNonNull(snode);
}
/**
* Get solver variable for given model variable.
* <p>
* @param variable must be in the model graph tree associated with this solver graph tree.
* @param create indicates whether to create the solver variable on demand. If true, this method should return
* a non-null value or throw an exception.
* @return solver variable whose {@linkplain ISolverVariable#getModelObject() model} is the specified
* {@code variable} or else null if it does not exist and {@code create} is false.
* @throws IllegalArgumentException if {@code variable} does not belong to the same model graph tree
* @since 0.08
* @see #inGraphTree(Node)
* @see #getSolverVariableOrNull(Variable)
* @see #getSolverVariable(Variable)
*/
public @Nullable ISolverVariable getSolverVariable(Variable variable, boolean create)
{
ISolverFactorGraph sgraph = getSolverGraph(variable.requireParentGraph(), create);
if (sgraph != null)
{
return sgraph.getSolverVariable(variable, create);
}
return null;
}
/**
* Return solver variable, if it exists, for given model variable.
* <p>
* Simply invokes {@link #getSolverVariable(Variable, boolean)} with {@code create} set to {@code false}.
* @since 0.08
*/
public @Nullable ISolverVariable getSolverVariableOrNull(Variable variable)
{
return getSolverVariable(variable, false);
}
/**
* Return solver variable for given model variable.
* <p>
* Simply invokes {@link #getSolverVariable(Variable, boolean)} with {@code create} set to {@code true}.
* @since 0.08
*/
@SuppressWarnings("null")
public ISolverVariable getSolverVariable(Variable variable)
{
return getSolverGraph(variable.getParentGraph()).getSolverVariable(variable, true);
}
/**
* Returns solver variable block for given model block, if any.
* <p>
* @since 0.08
*/
public @Nullable ISolverVariableBlock getSolverVariableBlock(VariableBlock block)
{
return getSolverGraph(block.getParentGraph()).getSolverVariableBlock(block, true);
}
/**
* Indicate if node is in the model graph tree associated with this solver graph tree.
* <p>
* This simply tests whether the node's {@link Node#getRootGraph() root} is the same
* as this graph tree's {@linkplain #getRootGraph() model root}.
* @since 0.08
*/
public boolean inGraphTree(Node node)
{
return node.getRootGraph() == getRootGraph();
}
/**
* Sets {@linkplain #getConditioningLayer() conditioning layer}.
* @since 0.08
*/
public final void setConditioningLayer(@Nullable DataLayer<? extends IDatum> layer)
{
_conditioningLayer = layer;
}
/*-------------------
* Protected methods
*/
protected void assertInGraphTree(Node node)
{
if (!inGraphTree(node))
{
throw new IllegalArgumentException(String.format("'%s' does not belong to model graph tree.", node));
}
}
}