/*******************************************************************************
* Copyright 2012-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.core;
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.events.SolverEvent;
import com.analog.lyric.dimple.exceptions.DimpleException;
import com.analog.lyric.dimple.factorfunctions.core.IUnaryFactorFunction;
import com.analog.lyric.dimple.model.domains.Domain;
import com.analog.lyric.dimple.model.factors.Factor;
import com.analog.lyric.dimple.model.values.Value;
import com.analog.lyric.dimple.model.variables.Discrete;
import com.analog.lyric.dimple.model.variables.Real;
import com.analog.lyric.dimple.model.variables.Variable;
import com.analog.lyric.dimple.solvers.core.parameterizedMessages.IParameterizedMessage;
import com.analog.lyric.dimple.solvers.interfaces.ISolverEdgeState;
import com.analog.lyric.dimple.solvers.interfaces.ISolverFactor;
import com.analog.lyric.dimple.solvers.interfaces.ISolverFactorGraph;
import com.analog.lyric.dimple.solvers.interfaces.ISolverNode;
import com.analog.lyric.dimple.solvers.interfaces.ISolverVariable;
import com.analog.lyric.dimple.solvers.interfaces.SolverNodeMapping;
/**
* Base implementation solver variables.
* <p>
* @param <MVariable> is the base type of the corresponding model variable (e.g. {@link Real}, {@link Discrete}).
*/
public abstract class SVariableBase<MVariable extends Variable>
extends SNode<MVariable>
implements ISolverVariable
{
/*-----------
* Constants
*/
/**
* Bits in {@link #_flags} reserved by this class and its superclasses.
*/
@SuppressWarnings("hiding")
protected static final int RESERVED_FLAGS = 0xFFF00000;
protected final ISolverFactorGraph _parent;
/*--------------
* Construction
*/
public SVariableBase(MVariable var, ISolverFactorGraph parent)
{
super(var);
_parent = parent;
}
/*---------------------
* ISolverNode methods
*/
@Override
public ISolverFactorGraph getContainingSolverGraph()
{
return _parent;
}
@Override
public ISolverFactorGraph getParentGraph()
{
return _parent;
}
@Override
public ISolverFactor getSibling(int siblingNumber)
{
final Factor sibling = _model.getSibling(siblingNumber);
return getSolverMapping().getSolverFactor(sibling);
}
@Override
public @Nullable ISolverEdgeState getSiblingEdgeState(int siblingNumber)
{
return _parent.getSolverEdge(_model.getSiblingEdgeIndex(siblingNumber));
}
/**
* Subclasses can use this to implement {@link #getSiblingEdgeState(int)}.
* <p>
* Subclasses that cast their return types should call this instead of calling super
* to avoid the chain of super calls.
* @since 0.08
*/
@SuppressWarnings("null")
protected final ISolverEdgeState getSiblingEdgeState_(int siblingNumber)
{
return _parent.getSolverEdge(_model.getSiblingEdgeIndex(siblingNumber));
}
@Override
public SolverNodeMapping getSolverMapping()
{
return _parent.getSolverMapping();
}
@Override
public Object getValue()
{
throw new DimpleException("not supported");
}
@Override
public void setGuess(@Nullable Object guess)
{
if (guess != null)
{
throw new DimpleException("not supported");
}
}
@Override
public Object getGuess()
{
throw new DimpleException("not supported");
}
@Deprecated
@Override
public double getScore()
{
return evalPriorAndConditionEnergy(Value.create(getDomain(), getGuess()));
}
@Override
public double getInternalEnergy()
{
throw new DimpleException("not supported");
}
@Override
public double getBetheEntropy()
{
throw new DimpleException("not supported");
}
@Override
public void moveNonEdgeSpecificState(ISolverNode other)
{
}
@Override
public void createNonEdgeSpecificState()
{
}
/*-------------------------
* ISolverVariable methods
*/
/**
* Gets conditioning value associated with variable from conditioning layer, if any,
* <p>
* This looks up the {@linkplain ISolverFactorGraph#getConditioningLayer() conditioning layer}
* from the {@linkplain #getParentGraph() parent solver graph}, and if it exists returns the
* value associated with the associated {@link #getModelObject() model variable}.
* <p>
* @since 0.08
*/
public @Nullable IDatum getCondition()
{
// NOTE: this should be fast, but if we need to speed it up further, we could
// cache the FactorGraphData in the solver graph and do the lookup directly from there.
final DataLayer<?> layer = _parent.getConditioningLayer();
if (layer != null)
{
return layer.get(_model);
}
return null;
}
/**
* Gets conditioning value if it is a {@link IUnaryFactorFunction}, else null.
* @since 0.08
* @see #getCondition()
*/
public @Nullable IUnaryFactorFunction getConditionFunction()
{
IDatum datum = getCondition();
if (datum instanceof IUnaryFactorFunction)
{
return (IUnaryFactorFunction)datum;
}
return null;
}
@Override
public Domain getDomain()
{
return _model.getDomain();
}
/**
* Returns fixed value from a prior or from the conditioning layer, if any.
* <p>
* @since 0.08
*/
@Override
public @Nullable Value getKnownValue()
{
final IDatum prior = _model.getPrior();
if (prior instanceof Value)
{
return (Value)prior;
}
final IDatum condition = getCondition();
if (condition instanceof Value)
{
return (Value)condition;
}
return null;
}
@Override
public final @Nullable Object getKnownValueObject()
{
final Value value = getKnownValue();
return value == null ? null : value.getObject();
}
@Override
public final int getKnownDiscreteIndex()
{
int result = -1;
final Value value = getKnownValue();
if (value != null)
{
result = value.getIndex();
}
return result;
}
@Override
public final double getKnownReal()
{
double result = Double.NaN;
final Value value = getKnownValue();
if (value != null)
{
result = value.getDouble();
}
return result;
}
@Override
public final @Nullable double[] getKnownRealJoint()
{
double[] result = null;
final Value value = getKnownValue();
if (value != null)
{
result = value.getDoubleArray();
}
return result;
}
@Override
public void updatePriorAndCondition()
{
}
/*---------------
* SNode methods
*/
/**
* {@inheritDoc}
* <p>
* The {@link SVariableBase} implementation returns a {@link VariableToFactorMessageEvent}.
*/
@Override
protected @Nullable SolverEvent createMessageEvent(
int edge,
@Nullable IParameterizedMessage oldMessage,
IParameterizedMessage newMessage)
{
return new VariableToFactorMessageEvent(this, edge, oldMessage, newMessage);
}
@Override
protected Class<? extends SolverEvent> messageEventType()
{
return VariableToFactorMessageEvent.class;
}
/*---------------
* Local methods
*/
public final double evalPriorAndConditionEnergy(Value value)
{
if (!getDomain().valueInDomain(value))
{
return Double.POSITIVE_INFINITY;
}
PriorAndCondition known = getPriorAndCondition();
double energy = known.evalEnergy(value);
known.release();
return energy;
}
public final boolean canHavePriorAndConditionEnergy()
{
boolean canHaveEnergy = false;
if (getDomain().isBounded())
{
canHaveEnergy = true;
}
else
{
PriorAndCondition known = getPriorAndCondition();
canHaveEnergy = known.size() > 0 && known.get(0) instanceof IUnaryFactorFunction;
known.release();
}
return canHaveEnergy;
}
/**
* Returns object holding prior and condition values for this variable.
* <p>
* In the common case where the object is only used within one function call
* use {@link PriorAndCondition#release} to return the instance for reuse.
* @since 0.08
* @see Variable#getPrior()
* @see #getCondition()
*/
public PriorAndCondition getPriorAndCondition()
{
return PriorAndCondition.create(_model.getPrior(), getCondition());
}
/*--------------------
* Deprecated methods
*/
@Deprecated
@Override
public @Nullable Object getInputMsg(int portIndex)
{
ISolverEdgeState sedge = getSiblingEdgeState(portIndex);
return sedge != null ? sedge.getFactorToVarMsg() : null;
}
@Deprecated
@Override
public @Nullable Object getOutputMsg(int portIndex)
{
ISolverEdgeState sedge = getSiblingEdgeState(portIndex);
return sedge != null ? sedge.getVarToFactorMsg() : null;
}
@Deprecated
@Override
public void setInputOrFixedValue(@Nullable Object input, @Nullable Object fixedValue)
{
}
}