/******************************************************************************* * 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.factorfunctions.core; import java.io.Serializable; import java.util.BitSet; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.dimple.exceptions.DimpleException; import com.analog.lyric.dimple.model.domains.JointDomainIndexer; import com.analog.lyric.dimple.model.domains.JointDomainReindexer; import com.analog.lyric.dimple.model.values.Value; public interface IFactorTableBase extends Cloneable, Serializable, Iterable<FactorTableEntry> { /*------------------ * Iterator methods */ /** * Returns an iterator over the non-zero entries in the table in increasing order * of sparse/joint index. * @see #fullIterator() */ @Override public abstract IFactorTableIterator iterator(); /** * Returns an iterator over the joint indexes in the table in increasing order. * @see #iterator() */ public abstract IFactorTableIterator fullIterator(); /*------------- * New methods */ /** * Returns a deep copy of this factor table. */ public abstract IFactorTableBase clone(); /** * Computes the number of entries in the table with non-zero weight (or non-infinite energy). */ public int countNonZeroWeights(); /** * Returns a new factor table converted from this one using the specified converter. */ public IFactorTableBase convert(JointDomainReindexer converter); /** * That ratio of non-zero weights to {@link #jointSize()}. Will be 1.0 if table contains * no entries with zero weight. */ public double density(); /** * Deterministically compute output arguments from input arguments. * <p> * If table {@link #isDeterministicDirected()}, this method looks at the input arguments * designated by the set bits of {@link #getInputSet()} and sets the remaining output arguments * from them. * @throws DimpleException not {@link #isDeterministicDirected()}. */ public abstract void evalDeterministic(Value[] arguments); /** * The number of dimensions in the table. * <p> * The same as {@link #getDomainIndexer()}.size(). */ public abstract int getDimensions(); /** * Returns energy for given set of indices assuming that the table has a dense * representation for energies. This provides the fastest possible lookup for energies. * <p> * @throws ArrayIndexOutOfBoundsException may throw if {@code indices} are out of bound * or table does not have dense energies. * @see #hasDenseEnergies() * @see #getEnergyForIndices(int...) * @see #getWeightForIndicesDense(int...) */ public double getEnergyForIndicesDense(int ... indices); public double getEnergyForValuesDense(Value ... values); /** * Returns weight for given set of indices assuming that the table has a dense * representation for weights. This provides the fastest possible lookup for weights. * <p> * @throws ArrayIndexOutOfBoundsException may throw if {@code indices} are out of bound * or table does not have dense weights. * @see #hasDenseWeights() * @see #getWeightForIndices(int...) * @see #getEnergyForIndicesDense(int...) */ public double getWeightForIndicesDense(int ... indices); public double getWeightForValuesDense(Value ... values); /** * The domain indexer for the table represents the domains of the table dimensions and denotes the * directionality of the table dimensions if table is directed. * <p> * It is used to map between different * representations identifying a table entry: i.e. array of elements of each respective domain, * array of discrete domain indices of each respective domain, or a single integer representing * the location of the entry in the dense representation of the table. */ public abstract JointDomainIndexer getDomainIndexer(); /** * Returns energy of factor table entry for given {@code elements}. * <p> * @see #getEnergyForIndices(int...) * @see #getWeightForElements(Object...) */ public abstract double getEnergyForElements(Object ... elements); /** * Returns energy of factor table entry for given {@code jointIndex}. * <p> * @see #getWeightForJointIndex(int) */ public abstract double getEnergyForJointIndex(int jointIndex); /** * Returns energy of factor table entry at given {@code sparseIndex}. * <p> * The energy is the same as the negative log of the weight for the same {@code sparseIndex}. * <p> * @param sparseIndex should be value less than {@link #sparseSize()} specifying which * table entry to access. * @throws ArrayIndexOutOfBoundsException if {@code sparseIndex} is not in range [0,{@link #sparseSize}). * @see #getEnergyForIndices(int...) * @see #getWeightForSparseIndex(int) */ public abstract double getEnergyForSparseIndex(int sparseIndex); /** * Returns the energy of factor table entry with given {@code indices}. * <p> * @see #getEnergyForElements(Object...) * @see #getEnergyForSparseIndex(int) * @see #getWeightForIndices(int...) */ public abstract double getEnergyForIndices(int ... indices); public abstract double getEnergyForValues(Value ... values); /** * If {@link #isDirected()} returns object indicating the indices of the subset of dimensions/domains * that represent inputs or the "from" size of the directionality. Returns null if table is not * directed. The output set is simply the inverse of the input set (i.e. represented by the clear * bits instead of the set bits). * @see #getOutputSet */ public @Nullable BitSet getInputSet(); /** * If {@link #isDirected()} returns object indicating the indices of the subset of dimensions/domains * that represent outputs or the "to" size of the directionality. Returns null if table is not * directed. The input set is simply the inverse of the output set (i.e. represented by the clear * bits instead of the set bits). * @see #getInputSet() */ public @Nullable BitSet getOutputSet(); /** * Returns weight of factor table entry for given {@code elements}. * <p> * @see #getWeightForIndices(int...) * @see #getEnergyForElements(Object...) */ public abstract double getWeightForElements(Object ... elements); /** * Returns weight of factor table entry for given {@code jointIndex}. * <p> * @see #getEnergyForJointIndex(int) */ public abstract double getWeightForJointIndex(int jointIndex); /** * Returns weight of factor table entry at given {@code sparseIndex}. * <p> * @param sparseIndex should be value less than {@link #sparseSize()} specifying which * table entry to access. * @throws ArrayIndexOutOfBoundsException if {@code sparseIndex} is not in range [0, {@link #sparseSize}). * @see #getWeightForIndices(int...) * @see #getEnergyForSparseIndex(int) */ public abstract double getWeightForSparseIndex(int sparseIndex); /** * Returns the weight of factor table entry with given {@code indices}. * <p> * @see #getWeightForElements(Object[]) * @see #getWeightForSparseIndex(int) * @see #getEnergyForIndices(int[]) */ public abstract double getWeightForIndices(int ... indices); public abstract double getWeightForValues(Value ... values); /** * True if either {@link #hasDenseEnergies()} or {@link #hasDenseWeights()} is true. */ public abstract boolean hasDenseRepresentation(); /** * True if underlying representation of table values includes an array of energies * indexed by joint index. * <p> * This is the optimal representation for methods that get/set energy by joint index * or combined element indices. * <p> * @see #hasDenseWeights() * @see #hasSparseEnergies() */ public abstract boolean hasDenseEnergies(); /** * True if underlying representation of table values includes an array of weights * indexed by joint index. * <p> * This is the optimal representation for methods that get/set weight by joint index * or combined element indices. * <p> * @see #hasDenseEnergies() * @see #hasSparseWeights() */ public abstract boolean hasDenseWeights(); /** * True if table has {@link #density()} is 1.0 and there are no zero weights. * * @since 0.05 */ public abstract boolean hasMaximumDensity(); /** * True if underlying representation contains a sparse representation of non-zero weights. * <p> * True if {@link #hasSparseEnergies()}, {@link #hasSparseWeights()} or {@link #isDeterministicDirected()}. */ public abstract boolean hasSparseRepresentation(); /** * True if underlying representation of table values includes an array of energies * indexed by a sparse index and a second array that maps sparse indices to joint indices. * <p> * This is the optimal representation for methods that get/set energy by sparse index. * <p> * @see #hasSparseWeights() * @see #hasDenseEnergies() */ public abstract boolean hasSparseEnergies(); /** * True if underlying representation of table values includes an array of weights * indexed by a sparse index and a second array that maps sparse indices to joint indices. * <p> * This is the optimal representation for methods that get/set weight by sparse index. * <p> * @see #hasSparseEnergies() * @see #hasDenseWeights() */ public abstract boolean hasSparseWeights(); /** * True if table {@link #isDirected()} and has exactly one entry for each combination of * input indices with a non-zero weight. * @see #evalDeterministic(Value[]) */ public abstract boolean isDeterministicDirected(); /** * True if table is in form appropriate for conditional distribution, i.e. if it is directed and the * sum of the weights of entries for any given combination of input elements is a constant. * <p> * @see #isDirected() * @see #normalizeConditional() */ public abstract boolean isConditional(); /** * True if table has designated input/output domains directed, in which case * {@link #getInputSet()} will be non-null. * <p> * For most applications the table should have weights normalized so that {@link #isConditional()} * also is true. */ public abstract boolean isDirected(); /** * True if the table is not directed and its weights add up to 1.0 * <p> * @see #isDirected() */ public abstract boolean isNormalized(); /** * The number of possible combinations of the values of all the domains in this table. * Same as {@link JointDomainIndexer#getCardinality()} of {@link #getDomainIndexer()}. * @see #sparseSize() */ public abstract int jointSize(); /** * Computes sparse index for the table entry associated with the specified arguments. * <p> * @param elements must have length equal to {@link #getDimensions()} and each argument must * be an element of the corresponding domain. * @see #sparseIndexFromIndices(int[]) * @see #sparseIndexFromElements(Object[]) */ public abstract int sparseIndexFromElements(Object ... elements); /** * Computes a sparse index for the table entry associated with the specified {@code indices}. * * @param indices must have length equal to {@link #getDimensions()} and each index must be a non-negative * value less than the corresponding domain size otherwise the function could return an * incorrect result. * @see #sparseIndexFromElements * @see #sparseIndexToIndices * @return sparse index for table entry with given set of indices. Returns negative value if there is no such * entry. */ public abstract int sparseIndexFromIndices(int... indices); public abstract int sparseIndexFromValues(Value ... values); /** * Converts joint index (oner per valid combination of domain indices) to sparse index. * <p> * @return if {@code joint} has a corresponding table entry its location is returned as * a number in the range [0,{@link #sparseSize}), otherwise it returns -1-{@code location} where * {@code location} is the location where the entry would be if it were in the table. * @see #sparseIndexToJointIndex */ public abstract int sparseIndexFromJointIndex(int joint); /** * Computes domain values corresponding to given joint index. * <p> * @param sparseIndex index in the range [0,{@link #sparseSize}). * @param elements if this is an array of length {@link #getDimensions()}, the computed values will * be placed in this array, otherwise a new array will be allocated. * @see #sparseIndexToIndices(int, int[]) * @see #sparseIndexFromElements(Object...) */ public abstract Object[] sparseIndexToElements(int sparseIndex, @Nullable Object[] elements); /** * Converts sparse index (one per table entry) to joint index (one per valid combination * of domain indices). * <p> * The sparse and joint index values should have the same ordering relationship, so that * <pre> * sparse1 < sparse2</pre> * implies that * <pre> * t.sparseIndexToJointIndex(sparse1) < t.sparseIndexToJointIndex(sparse2) * </pre> * <p> * @return joint index in range [0,{@link #jointSize}). * @see #sparseIndexFromJointIndex(int) */ public abstract int sparseIndexToJointIndex(int sparseIndex); /** * Computes domain indices corresponding to given sparse index. * * @param sparseIndex index in range [0,{@link #sparseSize}). * @param indices if this is an array of length {@link #getDimensions()}, the computed values will * be placed in this array, otherwise a new array will be allocated. * @see #sparseIndexToElements(int, Object[]) * @see #sparseIndexFromIndices(int...) */ public abstract int[] sparseIndexToIndices(int sparseIndex, @Nullable int[] indices); public abstract int[] sparseIndexToIndices(int sparseIndex); /** * Normalizes the weights/energies of the table by dividing by a constant to ensure that * weights add up to one. * <p> * @throws UnsupportedOperationException if {@link #isDirected()}, use {@link #normalizeConditional()} instead. * <p> * @see #isNormalized() */ public abstract void normalize(); /** * Normalizes the weights/energies of directed table to ensure that weights applicable to any * combination of input elements add up to one. * <p> * @throws UnsupportedOperationException if not {@link #isDirected()}, use {@link #normalize()} instead. * <p> * @see #isConditional() * @see #normalizeConditional() */ public abstract void normalizeConditional(); /** * Normalizes the weights/energies of directed table to ensure that weights applicable to any * combination of input elements add up to one. * <p> * @param ignoreZeroWeightInputs if true, then any input whose entries adds up to zero will not * be normalized, if false a {@link DimpleException} will be thrown. * @return number of inputs that were not normalized (always zero if {@code ignoreZeroWeightInputs} is false. * @since 0.08 * @see #normalizeConditional() */ public abstract int normalizeConditional(boolean ignoreZeroWeightInputs); /** * Sets the table value indexed by the specified {@code elements} to the given {@code energy} value. * <p> * @see #getEnergyForElements(Object...) * @see #setEnergyForIndices(double, int...) * @see #setEnergyForJointIndex(double, int) * @see #setWeightForElements(double, Object...) */ public void setEnergyForElements(double energy, Object ... elements); /** * Sets the table value indexed by the specified {@code indices} to the given {@code energy} value. * <p> * @see #getEnergyForJointIndex(int jointIndex) * @see #setEnergyForSparseIndex(double, int) * @see #setWeightForJointIndex(double, int) */ public void setEnergyForIndices(double energy, int ... indices); /** * Sets the table value indexed by the specified {@code values} to the given {@code energy} value. * <p> * @see #getEnergyForJointIndex(int jointIndex) * @see #setEnergyForSparseIndex(double, int) * @see #setWeightForJointIndex(double, int) */ public void setEnergyForValues(double energy, Value ... values); /** * Sets the table value indexed by the specified {@code sparseIndex} to the given {@code energy} value. * <p> * @see #getEnergyForJointIndex(int jointIndex) * @see #setEnergyForSparseIndex(double, int) * @see #setWeightForJointIndex(double, int) */ public void setEnergyForSparseIndex(double energy, int sparseIndex); /** * Sets the table value indexed by the specified {@code jointIndex} to the given {@code energy} value. * <p> * @see #getEnergyForJointIndex(int jointIndex) * @see #setEnergyForSparseIndex(double, int) * @see #setWeightForJointIndex(double, int) */ public void setEnergyForJointIndex(double energy, int jointIndex); /** * Sets the table value indexed by the specified {@code elements} to the given {@code weight} value. * <p> * @see #getWeightForElements(Object...) * @see #setWeightForIndices(double, int...) * @see #setWeightForJointIndex(double, int) * @see #setEnergyForElements(double, Object...) */ public void setWeightForElements(double weight, Object ... elements); /** * Sets the table value indexed by the specified {@code indices} to the given {@code weight} value. * <p> * @see #getWeightForJointIndex(int jointIndex) * @see #setWeightForSparseIndex(double, int) * @see #setEnergyForJointIndex(double, int) */ public void setWeightForIndices(double weight, int ... indices); /** * Sets the table value indexed by the specified {@code values} to the given {@code weight} value. * <p> * @see #getWeightForJointIndex(int jointIndex) * @see #setWeightForSparseIndex(double, int) * @see #setEnergyForJointIndex(double, int) */ public void setWeightForValues(double weight, Value ... values); /** * Sets the table value indexed by the specified {@code sparseIndex} to the given {@code weight} value. * <p> * @see #getWeightForJointIndex(int jointIndex) * @see #setWeightForSparseIndex(double, int) * @see #setEnergyForJointIndex(double, int) */ public void setWeightForSparseIndex(double weight, int sparseIndex); /** * Sets the table value indexed by the specified {@code jointIndex} to the given {@code energy} value. * <p> * @see #getEnergyForJointIndex(int jointIndex) * @see #setEnergyForSparseIndex(double, int) * @see #setWeightForJointIndex(double, int) */ public void setWeightForJointIndex(double weight, int jointIndex); /** * The number of entries in the table that can be accessed by a sparse index. * This can be no larger than {@link #jointSize()} and if smaller, indicates that * the table has a sparse representation that does not include combinations with * zero weight/infinite energy. The actual number of non-zero weight entries may * be less than the sparse size. */ public abstract int sparseSize(); /** * True if table supports operations involving {@code jointIndex} values or dense representation. If false, * then methods that return or use such arguments will throw an {@link UnsupportedOperationException}. */ public boolean supportsJointIndexing(); }