/******************************************************************************* * 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) { } }