/******************************************************************************* * Copyright 2012 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.gibbs; import static com.analog.lyric.dimple.solvers.gibbs.GibbsSolverVariableEvent.*; import static java.util.Objects.*; import java.util.Arrays; import java.util.Collections; import java.util.Objects; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.collect.ArrayUtil; import com.analog.lyric.collect.ReleasableIterator; import com.analog.lyric.dimple.data.IDatum; import com.analog.lyric.dimple.environment.DimpleEnvironment; import com.analog.lyric.dimple.exceptions.DimpleException; import com.analog.lyric.dimple.model.core.EdgeState; import com.analog.lyric.dimple.model.core.FactorGraph; import com.analog.lyric.dimple.model.domains.DiscreteDomain; import com.analog.lyric.dimple.model.factors.Factor; import com.analog.lyric.dimple.model.values.DiscreteValue; import com.analog.lyric.dimple.model.values.Value; import com.analog.lyric.dimple.model.variables.Discrete; import com.analog.lyric.dimple.solvers.core.SDiscreteVariableBase; import com.analog.lyric.dimple.solvers.core.parameterizedMessages.DiscreteEnergyMessage; import com.analog.lyric.dimple.solvers.core.parameterizedMessages.DiscreteMessage; import com.analog.lyric.dimple.solvers.core.parameterizedMessages.IParameterizedMessage; import com.analog.lyric.dimple.solvers.gibbs.samplers.ISampler; import com.analog.lyric.dimple.solvers.gibbs.samplers.generic.CDFSampler; import com.analog.lyric.dimple.solvers.gibbs.samplers.generic.IDiscreteDirectSampler; import com.analog.lyric.dimple.solvers.gibbs.samplers.generic.IDiscreteSamplerClient; import com.analog.lyric.dimple.solvers.gibbs.samplers.generic.IGenericSampler; import com.analog.lyric.dimple.solvers.gibbs.samplers.generic.IMCMCSampler; import com.analog.lyric.dimple.solvers.interfaces.ISolverFactorGraph; import com.analog.lyric.dimple.solvers.interfaces.ISolverNode; import com.analog.lyric.util.misc.Internal; import com.analog.lyric.util.misc.Matlab; import com.google.common.primitives.Doubles; import cern.colt.list.IntArrayList; /** * Solver variable for Discrete variables in Gibbs solver. * * @since 0.07 */ public class GibbsDiscrete extends SDiscreteVariableBase implements ISolverVariableGibbs, IDiscreteSamplerClient { /*----------- * Constants */ /** * Bits in {@link #_flags} reserved by this class and its superclasses. */ @SuppressWarnings("hiding") protected static final int RESERVED_FLAGS = 0xFFFF0003; @SuppressWarnings("hiding") protected static final int EVENT_MASK = 0x03; public static final String DEFAULT_DISCRETE_SAMPLER_NAME = "CDFSampler"; /*------- * State */ private final class CurrentSample extends DiscreteValue { private static final long serialVersionUID = 1L; private final DiscreteValue _value; CurrentSample(DiscreteDomain domain) { _value = Value.create(domain); } @Override public DiscreteValue clone() { return _value.clone(); } @Override public DiscreteDomain getDomain() { return _value.getDomain(); } @Override public int getIndex() { return _value.getIndex(); } @Override public @NonNull Object getObject() { return _value.getObject(); } @Override public final void setFrom(Value value) { if (fixed() || _value.valueEquals(value)) { return; } final GibbsNeighbors neighbors = _neighbors; boolean hasDeterministicDependents = neighbors != null && neighbors.hasDeterministicDependents(); DiscreteValue oldValue = null; if (hasDeterministicDependents) { oldValue = _value.clone(); } _value.setFrom(value); // If this variable has deterministic dependents, then set their values if (hasDeterministicDependents) { requireNonNull(neighbors).update(requireNonNull(oldValue)); } } @Override public void setIndex(int index) { if (!fixed() && index != _value.getIndex()) { setIndexForce(index); } } @Override public void setObject(@Nullable Object obj) { if (fixed()) { return; } final GibbsNeighbors neighbors = _neighbors; boolean hasDeterministicDependents = neighbors != null && neighbors.hasDeterministicDependents(); DiscreteValue oldValue = null; if (hasDeterministicDependents) { oldValue = _value.clone(); } _value.setObject(obj); // If this variable has deterministic dependents, then set their values if (hasDeterministicDependents && !_value.valueEquals(requireNonNull(oldValue))) { Objects.requireNonNull(neighbors).update(oldValue); } } private void setIndexForce(int index) { final GibbsNeighbors neighbors = _neighbors; boolean hasDeterministicDependents = neighbors != null && neighbors.hasDeterministicDependents(); DiscreteValue oldValue = null; if (hasDeterministicDependents) { oldValue = _value.clone(); } _value.setIndex(index); // If this variable has deterministic dependents, then set their values if (hasDeterministicDependents) { Objects.requireNonNull(neighbors).update(Objects.requireNonNull(oldValue)); } } private final boolean fixed() { return _holdSampleValue || _model.hasFixedValue(); } } // CurrentSample private boolean _visited = false; private final CurrentSample _currentSample; private DiscreteValue _prevSample; private boolean _repeatedVariable; private @Nullable long[] _beliefHistogram; private @Nullable DiscreteEnergyMessage _input = null; private @Nullable IntArrayList _sampleIndexArray; private int _bestSampleIndex; private @Nullable DiscreteValue _initialSampleValue = null; private double _beta = 1; private boolean _holdSampleValue = false; private @Nullable IGenericSampler _sampler; private boolean _samplerSpecificallySpecified = false; private long _updateCount; private long _rejectCount; private long _scoreCount; /** * List of neighbors for sample scoring. Instantiated during initialization. */ private @Nullable GibbsNeighbors _neighbors = null; /*-------------- * Construction */ @SuppressWarnings("null") public GibbsDiscrete(Discrete var, GibbsSolverGraph parent) { super(var, parent); _prevSample = _currentSample = new CurrentSample(_model.getDomain()); } /*--------------------- * ISolverNode methods */ @Override protected void doUpdateEdge(int outPortNum) { throw new DimpleException("Method not supported in Gibbs sampling solver."); } @Override public GibbsSolverGraph getParentGraph() { return (GibbsSolverGraph)_parent; } @Override public ISolverFactorGibbs getSibling(int edge) { return (ISolverFactorGibbs)super.getSibling(edge); } @Override public final void update() { final Discrete model = _model; // Don't bother to re-sample deterministic dependent variables (those that are the output of a directional deterministic factor) if (model.isDeterministicOutput()) return; // If the sample value is being held, don't modify the value if (_holdSampleValue) return; // Also return if the variable is set to a fixed value if (model.hasFixedValue()) return; final int updateEventFlags = GibbsSolverVariableEvent.getVariableUpdateEventFlags(this); Value oldValue = null; double oldSampleScore = 0.0; switch (updateEventFlags) { case UPDATE_EVENT_SCORED: // TODO: non-conjugate samplers already compute sample scores, so we shouldn't have to do here. oldSampleScore = getCurrentSampleScore(); //$FALL-THROUGH$ case UPDATE_EVENT_SIMPLE: oldValue = _currentSample.clone(); break; } final int messageLength = getDomain().size(); final int numPorts = model.getSiblingCount(); double minEnergy = Double.POSITIVE_INFINITY; // Conditional probability in log domain final double[] conditional = DimpleEnvironment.doubleArrayCache.allocateAtLeast(messageLength); DiscreteEnergyMessage input = _input; if (input != null) { input.getEnergies(conditional); } else { Arrays.fill(conditional, 0); } // Compute the conditional probability if (!model.isDeterministicInput()) { // Update all the neighboring factors // If there are no deterministic dependents, then it should be faster to have // each neighboring factor update its entire message to this variable than the alternative, below final ISolverFactorGraph sfg = _parent; final FactorGraph fg = sfg.getModelObject(); for (int port = numPorts; --port>=0;) { final int edgeIndex = model.getSiblingEdgeIndex(port); final EdgeState edgeState = requireNonNull(fg.getGraphEdgeState(edgeIndex)); final GibbsDiscreteEdge sedge = requireNonNull((GibbsDiscreteEdge)sfg.getSolverEdge(edgeIndex)); final ISolverFactorGibbs sfactor = (ISolverFactorGibbs)sfg.getSolverFactorForEdge(edgeState); final double[] inputMsg = sedge.factorToVarMsg.representation(); sfactor.updateEdgeMessage(edgeState, sedge); for (int index = messageLength; --index>=0;) { conditional[index] += inputMsg[index]; } } final double beta = _beta; if (beta != 1.0) { for (int index = messageLength; --index>=0;) { final double out = conditional[index] * beta; conditional[index] = out; minEnergy = Math.min(minEnergy, out); } } else { for (int index = messageLength; --index>=0;) { minEnergy = Math.min(minEnergy, conditional[index]); } } } else // There are deterministic dependents, so must account for these { // TODO: SPEED UP for (int index = 0; index < messageLength; index++) { setCurrentSampleIndex(index); double out = input != null ? input.getEnergy(index) : 0; // Sum of the input prior... ReleasableIterator<ISolverNodeGibbs> scoreNodes = getSampleScoreNodes(); while (scoreNodes.hasNext()) { out += scoreNodes.next().getPotential(); } scoreNodes.release(); out *= _beta; // Apply tempering minEnergy = Math.min(minEnergy, out); // For normalization conditional[index] = out; // Save in log domain representation } } // Sample from the conditional distribution _updateCount++; boolean rejected = false; if (minEnergy < Double.POSITIVE_INFINITY) { if (_sampler instanceof IDiscreteDirectSampler) { ((IDiscreteDirectSampler)Objects.requireNonNull(_sampler)).nextSample(_currentSample, conditional, minEnergy, this); } else if (_sampler instanceof IMCMCSampler) { // FIXME: make sampler save current sample value so that clone is not necessary rejected = !((IMCMCSampler)Objects.requireNonNull(_sampler)).nextSample(_currentSample.clone(), this); } } else { rejected = true; } DimpleEnvironment.doubleArrayCache.release(conditional); if (rejected) _rejectCount++; switch (updateEventFlags) { case UPDATE_EVENT_SCORED: // TODO: non-conjugate samplers already compute sample scores, so we shouldn't have to do here. raiseEvent(new GibbsScoredVariableUpdateEvent(this, Objects.requireNonNull(oldValue), oldSampleScore, _currentSample.clone(), getCurrentSampleScore(), rejected ? 1 : 0)); break; case UPDATE_EVENT_SIMPLE: raiseEvent(new GibbsVariableUpdateEvent(this, Objects.requireNonNull(oldValue), _currentSample.clone(), rejected ? 1 : 0)); break; } } /*--------------------------- * SolverEventSource methods */ @Override protected int getEventMask() { return EVENT_MASK | super.getEventMask(); } /*------------------------- * ISolverVariable methods */ @Override public void updatePriorAndCondition() { _input = knownEnergyMessage(); } /*-------------------------- * ISolverNodeGibbs methods */ @Override public boolean setVisited(boolean visited) { boolean changed = _visited ^ visited; _visited = visited; return changed; } /*------------------------------- * ISolverVariableGibbs methods */ @Override public DiscreteValue getCurrentSampleValue() { return _currentSample; } @Internal @Override public DiscreteValue getPrevSampleValue() { return _prevSample; } @Override public ReleasableIterator<ISolverNodeGibbs> getSampleScoreNodes() { return GibbsNeighbors.iteratorFor(_neighbors, this); } @Override public void randomRestart(int restartCount) { // If the sample value is being held, don't modify the value if (_holdSampleValue) return; // If the variable has a fixed value, then set the current sample to that value and return int fixedIndex = getKnownDiscreteIndex(); if (fixedIndex >= 0) { setCurrentSampleIndex(fixedIndex); return; } final DiscreteValue initialSampleValue = _initialSampleValue; if (initialSampleValue != null && restartCount == 0) { setCurrentSample(initialSampleValue); return; } // Convert the prior back to probabilities to sample from the prior IDatum prior = _model.getPrior(); double[] inputEnergy; double minEnergy; if (prior != null) { DiscreteEnergyMessage input = DiscreteEnergyMessage.convertFrom(getDomain(), Collections.singletonList(prior)); inputEnergy = requireNonNull(input).representation(); minEnergy = input.minEnergy(); } else { // FIXME - shouldn't have to make an empty array to do uniform sampling! inputEnergy = new double[getDomain().size()]; minEnergy = 0; } if (_sampler instanceof CDFSampler) ((CDFSampler)Objects.requireNonNull(_sampler)).nextSample(_currentSample, inputEnergy, minEnergy, this); else // If the actual sampler isn't a CDF sampler, make a CDF sampler to use for random restart { IDiscreteDirectSampler sampler = new CDFSampler(); sampler.initializeFromVariable(this); sampler.nextSample(_currentSample, inputEnergy, minEnergy, this); } } @Override public final double getSampleScore(Value sampleValue) { return getSampleScore(((DiscreteValue)sampleValue).getIndex()); } @Override public final double getSampleScore(int sampleIndex) { // WARNING: Side effect is that the current sample value changes to this sample value // Could change back but less efficient to do this, since we'll be updating the sample value anyway setCurrentSampleIndex(sampleIndex); return getCurrentSampleScore(); } @Override public final double getCurrentSampleScore() { double sampleScore = Double.POSITIVE_INFINITY; _scoreCount++; computeScore: { final DiscreteEnergyMessage input = _input; double potential = input != null ? input.getEnergy(_currentSample.getIndex()) : 0; ReleasableIterator<ISolverNodeGibbs> scoreNodes = getSampleScoreNodes(); while (scoreNodes.hasNext()) { final ISolverNodeGibbs node = scoreNodes.next(); potential += node.getPotential(); if (!Doubles.isFinite(potential)) { break computeScore; } } scoreNodes.release(); sampleScore = potential * _beta; // Incorporate current temperature } return sampleScore; } @Override public final void setNextSampleValue(Value sampleValue) { setNextSampleIndex(((DiscreteValue)sampleValue).getIndex()); } @Override public final void setNextSampleIndex(int sampleIndex) { if (sampleIndex != _currentSample.getIndex()) setCurrentSampleIndex(sampleIndex); } @SuppressWarnings("null") @Override public void updateBelief() { _beliefHistogram[_currentSample.getIndex()]++; } @SuppressWarnings("null") @Override public double[] getBelief() { final int domainLength = getDomain().size(); double[] outBelief = new double[domainLength]; final IDatum prior = getKnownValue(); if (prior instanceof Value) // If there's a fixed value set, use that to generate the belief { Arrays.fill(outBelief, 0); outBelief[((Value)prior).getIndex()] = 1; return outBelief; } // Otherwise, compute the belief long sum = 0; for (int i = 0; i < domainLength; i++) { sum+= _beliefHistogram[i]; } if (sum != 0) { for (int i = 0; i < domainLength; i++) outBelief[i] = (double)_beliefHistogram[i]/(double)sum; } else if (prior instanceof DiscreteMessage) { DiscreteMessage msg = (DiscreteMessage)prior; msg.getWeights(outBelief); } return outBelief; } @SuppressWarnings("null") @Override public void postAddFactor(@Nullable Factor f) { } /** * @deprecated Instead set {@link GibbsOptions#saveAllSamples} to true using {@link #setOption}. */ @Deprecated @Override public final void saveAllSamples() { _sampleIndexArray = new IntArrayList(); setOption(GibbsOptions.saveAllSamples, true); } /** * @deprecated Instead set {@link GibbsOptions#saveAllSamples} to false using {@link #setOption}. */ @Deprecated @Override public void disableSavingAllSamples() { _sampleIndexArray = null; setOption(GibbsOptions.saveAllSamples, false); } @Override public final void saveCurrentSample() { final IntArrayList sampleIndexArray = _sampleIndexArray; if (sampleIndexArray != null) { sampleIndexArray.add(_currentSample.getIndex()); } } @Override public final void saveBestSample() { _bestSampleIndex = _currentSample.getIndex(); } @Override public final double getPotential() { return evalPriorAndConditionEnergy(_currentSample); } @Override public final boolean hasPotential() { return canHavePriorAndConditionEnergy(); } @Override public final void setCurrentSample(Object value) { _currentSample.setObject(value); } @Override public final void setCurrentSample(Value value) { _currentSample.setFrom(value); } /*--------------- * Local methods */ public final void setCurrentSampleIndex(int index) { _currentSample.setIndex(index); } // Sets the sample regardless of whether the value is fixed or held private final void setCurrentSampleIndexForce(int index) { _currentSample.setIndexForce(index); } @Matlab public final Object getCurrentSample() { return _currentSample.getObject(); } @Matlab public final int getCurrentSampleIndex() { return _currentSample.getIndex(); } @Matlab public final Object getBestSample() { return _model.getDiscreteDomain().getElement(_bestSampleIndex); } @Matlab public final int getBestSampleIndex() { return _bestSampleIndex; } @Matlab @Override public final Object[] getAllSamples() { final IntArrayList sampleIndexArray = _sampleIndexArray; if (sampleIndexArray == null) { return ArrayUtil.EMPTY_OBJECT_ARRAY; } int length = sampleIndexArray.size(); DiscreteDomain domain = _model.getDiscreteDomain(); Object[] retval = new Object[length]; for (int i = 0; i < length; i++) retval[i] = domain.getElement(sampleIndexArray.get(i)); return retval; } @Matlab public final int[] getAllSampleIndices() { final IntArrayList sampleIndexArray = _sampleIndexArray; if (sampleIndexArray == null) { return ArrayUtil.EMPTY_INT_ARRAY; } return Arrays.copyOf(sampleIndexArray.elements(), sampleIndexArray.size()); } @Override public final double getRejectionRate() { return (_updateCount > 0) ? (double)_rejectCount / (double)_updateCount : 0; } @Override public final double getNumScoresPerUpdate() { return (_updateCount > 0) ? (double)_scoreCount / (double)_updateCount : 0; } @Override public final void resetRejectionRateStats() { _updateCount = 0; _rejectCount = 0; _scoreCount = 0; } @Override public final long getUpdateCount() { return _updateCount; } @Override public final long getRejectionCount() { return _rejectCount; } public final void setAndHoldSampleValue(Object value) { releaseSampleValue(); setCurrentSample(value); holdSampleValue(); } public final void setAndHoldSampleIndex(int index) { releaseSampleValue(); setCurrentSampleIndex(index); holdSampleValue(); } public final void holdSampleValue() { _holdSampleValue = true; } public final void releaseSampleValue() { _holdSampleValue = false; } @Matlab public final void setInitialSampleValue(Object initialSampleValue) { _initialSampleValue = Value.create(_model.getDomain(), initialSampleValue); } @Matlab public final void setInitialSampleIndex(int initialSampleIndex) { DiscreteValue val = _initialSampleValue = Value.create(_model.getDomain()); val.setIndex(initialSampleIndex); } @Matlab public final @Nullable Object getInitialSampleValue() { final DiscreteValue val = _initialSampleValue; return val != null ? val.getObject() : null; } @Matlab public final int getInitialSampleIndex() { final DiscreteValue val = _initialSampleValue; return val != null ? val.getIndex() : -1; } @Override public final void setBeta(double beta) // beta = 1/temperature { _beta = beta; } // TODO move to bottom /** * @deprecated Will be removed in future release. Instead use {@link GibbsOptions#discreteSampler} * option. */ @Deprecated public final void setDefaultSampler(String samplerName) { GibbsOptions.discreteSampler.convertAndSet(this, samplerName); } /** * @deprecated Will be removed in future release. Instead use {@link GibbsOptions#discreteSampler} * option. */ @Deprecated public final String getDefaultSamplerName() { return getOptionOrDefault(GibbsOptions.discreteSampler).getSimpleName(); } /** * Sets sampler to be used for this variable. * <p> * In general, it is usually easier to configure the sampler using the * {@link GibbsOptions#discreteSampler} option. This method should only be * required when the sampler class is not registered with the * {@linkplain DimpleEnvironment#genericSamplers() generic sampler registry} * for the current environment. * <p> * @param sampler is a non-null sampler. */ public final void setSampler(ISampler sampler) { _sampler = (IGenericSampler)sampler; _samplerSpecificallySpecified = true; } /** * @deprecated Will be removed in future release. Instead set sampler by setting * {@link GibbsOptions#discreteSampler} option using {@link #setOption}. */ @Matlab @Deprecated public final void setSampler(String samplerName) { GibbsOptions.discreteSampler.convertAndSet(this, samplerName); _sampler = GibbsOptions.discreteSampler.instantiate(this); _samplerSpecificallySpecified = true; } @Matlab @Override public final ISampler getSampler() { IGenericSampler sampler = _sampler; if (sampler == null || !_samplerSpecificallySpecified) { IGenericSampler newSampler = GibbsOptions.discreteSampler.instantiate(this); if (newSampler != sampler) { newSampler.initializeFromVariable(this); } sampler = newSampler; } return sampler; } @Matlab public final String getSamplerName() { final ISampler sampler = getSampler(); return sampler.getClass().getSimpleName(); } @Override public void createNonEdgeSpecificState() { // Normally zero, but use fixed value if one has been set Value value = _model.getPriorValue(); _currentSample.setIndex(value != null ? value.getIndex() : 0); if (_sampleIndexArray != null) saveAllSamples(); _beliefHistogram = new long[_model.getDomain().size()]; _bestSampleIndex = -1; } public DiscreteEnergyMessage createDefaultMessage() { return new DiscreteEnergyMessage(_model.getDiscreteDomain().size()); } @Override @Deprecated public double[] getInputMsg(int portIndex) { return getDiscreteEdge(portIndex).factorToVarMsg.representation(); } @Deprecated @Override public DiscreteValue getOutputMsg(int portIndex) { return _currentSample; } @Deprecated @Override public void setInputMsgValues(int portIndex, Object obj) { final IParameterizedMessage message = getSiblingEdgeState(portIndex).factorToVarMsg; if (obj instanceof IParameterizedMessage) { message.setFrom((IParameterizedMessage)obj); } else if (obj instanceof double[]) { double[] target = ((DiscreteMessage)message).representation(); System.arraycopy(obj, 0, target, 0, target.length); } } @Override public void moveNonEdgeSpecificState(ISolverNode other) { GibbsDiscrete ovar = ((GibbsDiscrete)other); ovar._prevSample = _currentSample; ovar._repeatedVariable = true; if (!_repeatedVariable) { if (_prevSample == _currentSample) { // If not already pointing at a different value object, then this must be the // the first variable in the stream, so make a copy of its state. _prevSample = _currentSample.clone(); } else { _prevSample.setFrom(_currentSample); } } Value fixedValue = _model.getPriorValue(); if (fixedValue != null) _currentSample.setIndexForce(fixedValue.getIndex()); else _currentSample.setFrom(ovar._currentSample); _sampleIndexArray = ovar._sampleIndexArray; _beliefHistogram = ovar._beliefHistogram; _bestSampleIndex = ovar._bestSampleIndex; _initialSampleValue = ovar._initialSampleValue; _beta = ovar._beta; _sampler = ovar._sampler; _samplerSpecificallySpecified = ovar._samplerSpecificallySpecified; _input = ovar._input; ovar._input = null; } @Override public void initialize() { final boolean saveAllSamples = getOptionOrDefault(GibbsOptions.saveAllSamples); super.initialize(); // We actually only need to change this if the model has changed in the vicinity of this variable, // but that may not be worth the trouble to figure out. _neighbors = GibbsNeighbors.create(this); // Clear out sample state _bestSampleIndex = -1; IntArrayList sampleIndexArray = null; if (saveAllSamples) { sampleIndexArray = _sampleIndexArray; if (sampleIndexArray == null) { sampleIndexArray = new IntArrayList(); } else { sampleIndexArray.clear(); } } _sampleIndexArray = sampleIndexArray; Arrays.fill(_beliefHistogram, 0); updatePriorAndCondition(); Value value = _model.getPriorValue(); if (value != null) setCurrentSampleIndexForce(value.getIndex()); else setCurrentSampleIndexForce(_currentSample.getIndex()); IGenericSampler sampler = _sampler; if (sampler == null || !_samplerSpecificallySpecified) { // If not specifically specified, use the default sampler sampler = _sampler = GibbsOptions.discreteSampler.instantiate(this); } sampler.initializeFromVariable(this); resetRejectionRateStats(); } @SuppressWarnings("null") @Override public GibbsSolverEdge<?> getSiblingEdgeState(int siblingIndex) { return (GibbsSolverEdge<?>)getSiblingEdgeState_(siblingIndex); } @SuppressWarnings("null") GibbsDiscreteEdge getDiscreteEdge(int siblingIndex) { return (GibbsDiscreteEdge)super.getSiblingEdgeState(siblingIndex); } }