/*******************************************************************************
* 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.util.BitSet;
import java.util.Random;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.dimple.exceptions.DimpleException;
import com.analog.lyric.dimple.model.domains.DiscreteDomain;
import com.analog.lyric.dimple.model.domains.JointDiscreteDomain;
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 IFactorTable extends IFactorTableBase
{
@Override
public IFactorTable clone();
/**
* Removes entries from sparse representation of table that have zero weights.
* <p>
* This may affect the {@link #sparseSize()} and the relationship between sparse and
* joint indexes.
* <p>
* @return the number of sparse entries that were removed.
*/
public int compact();
@Override
public IFactorTable convert(JointDomainReindexer converter);
public void copy(IFactorTable that);
/**
* Constructs a new factor table by conditioning one or more dimensions
* conditioned on specified values.
* <p>
* The new table will not be directed and will have the same representation as the original
* table except that {@link FactorTableRepresentation#DETERMINISTIC} will become
* {@link FactorTableRepresentation#SPARSE_ENERGY}.
* <p>
* @param valueIndices is an array of length {@link #getDimensions()} that specifies which
* dimensions are to be conditioned away. Each entry in the array should either be
* a negative value if the dimension is to be retained, or a non-negative value in
* the range [0, <i>dimension-size</i> - 1].
*
* @since 0.05
* @see #createTableConditionedOn(int[], boolean)
*/
public IFactorTable createTableConditionedOn(int[] valueIndices);
/**
* Constructs a new factor table by conditioning one or more dimensions
* conditioned on specified values.
* <p>
* The new table will only retain its directedness if {@code retainDimensions} is true
* and will have the same representation as the original
* table except that {@link FactorTableRepresentation#DETERMINISTIC} will become
* {@link FactorTableRepresentation#SPARSE_ENERGY}.
* <p>
* @param valueIndices is an array of length {@link #getDimensions()} that specifies which
* dimensions are to be conditioned away. Each entry in the array should either be
* a negative value if the dimension is to be retained, or a non-negative value in
* the range [0, <i>dimension-size</i> - 1].
* @param retainDimensions if true, the new table will retain the same number of dimensions as
* the original table, but the domains of the conditioned dimensions will become single-element
* domains. Otherwise conditioned dimensions will be removed from the new table entirely.
* <p>
* @since 0.08
* @see #createTableConditionedOn(int[])
*/
public IFactorTable createTableConditionedOn(int[] valueIndices, boolean retainDimensions);
/**
* Constructs a new factor table by appending dimensions for the specified {@code newDomains}.
*/
public IFactorTable createTableWithNewVariables(DiscreteDomain[] newDomains);
/**
* Returns an enum indicating the underlying representation of the factor table.
* <p>
* The representation may be changed via {@link #setRepresentation(FactorTableRepresentation)} but
* can also be changed implicitly as a side effect of certain other methods:
* <ul>
* <li>{@link #getEnergiesSparseUnsafe()}
* <li>{@link #getEnergyForSparseIndex(int)}
* <li>{@link #getIndicesSparseUnsafe()}
* <li>{@link #getWeightForSparseIndex(int)}
* <li>{@link #getWeightsSparseUnsafe()}
* <li>{@link #setEnergyForJointIndex(double, int)}
* <li>{@link #setEnergyForSparseIndex(double, int)}
* <li>{@link #setWeightForJointIndex(double, int)}
* <li>{@link #setWeightForSparseIndex(double, int)}
* <li>{@link #sparseIndexFromJointIndex(int)}
* <li>{@link #sparseIndexToJointIndex(int)}
* </ul>
*/
public FactorTableRepresentation getRepresentation();
/**
* Creates a new factor table that replaces two or more of its domains with
* a joint domain.
* <p>
* @param varIndices contains the indices of the domains to be joined. Must have at least two entries
* in the range [0,{@link #getDimensions()}-1].
* @param indexToJointIndex specifies the order in which the joined domains are to be incorporated
* into the new joint domain.
* @param allDomains is the list of all domains before joining
* @param jointDomain is the new joined domain. Its size must match the product of the sizes of the
* joined domains, and it is expected to be of type {@link JointDiscreteDomain}. It will be the
* last domain in the new table's domain list.
*/
// Only used by DiscreteFactor.replaceVariablesWithJoint
public IFactorTable joinVariablesAndCreateNewTable(
int [] varIndices,
int [] indexToJointIndex,
DiscreteDomain [] allDomains,
DiscreteDomain jointDomain); // REFACTOR: keep (for now)
/**
* True if table currently has a deterministic directed representation (either
* {@link FactorTableRepresentation#DETERMINISTIC} or {@link FactorTableRepresentation#DETERMINISTIC_WITH_INDICES}).
* Unlike invoking {@link #isDeterministicDirected()}, this will not check the values of the table
* or change the representation.
*/
public boolean hasDeterministicRepresentation();
/**
* Returns the underlying array of sparse energies without copying for speed.
* <p>
* <b>IMPORTANT</b>: modifying the contents of the array may put the factor table into
* an invalid state. This should be treated as a read-only value.
* <p>
* If necessary, this method will implicitly modify the representation to include sparse energies
* (@link {@link #hasSparseEnergies()}).
* <p>
* @see #getWeightsSparseUnsafe()
*/
public double[] getEnergiesSparseUnsafe();
/**
* Returns the underlying array of dense energies without copying for speed.
* <p>
* <b>IMPORTANT</b>: modifying the contents of the array may put the factor table into
* an invalid state. This should be treated as a read-only value.
* <p>
* If necessary, this method will implicitly modify the representation to include dense weights
* <p>
* @since 0.07
*/
public double[] getEnergiesDenseUnsafe();
/**
* Returns an array of energies for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
* <p>
* Same as {@link #getEnergySlice(double[], int, int[])} but always allocates a new array.
* <p>
* @see #getWeightSlice(int, int[])
*/
public double[] getEnergySlice(int sliceDimension, int ... indices);
/**
* Returns an array of energies for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
* <p>
* Same as {@link #getEnergySlice(int, int[])} but always allocates a new array.
* <p>
* @see #getWeightSlice(int, Value...)
*/
public double[] getEnergySlice(int sliceDimension, Value ... values);
/**
* Returns an array of energies for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
*
* @param sliceDimension is an integer in the range [0, {@link #getDimensions()}-1] that identifies which
* domain the slice is for.
* @param indices specifies the element indices that are to be fixed. The element index at position
* {@code sliceDimension} will be ignored.
* @param slice if non-null and large enough to accommodate the values will be used as the return value,
* otherwise a new array will be allocated.
* @return an array of energy values with size equal to the size of the {@code sliceDimension} of
* the table (i.e. {@code getDomainIndexer().getDomainSize(sliceDimension)}) holding the energy values
* from the table
* <p>
* @see #getEnergySlice(int, int[])
* @see #getWeightSlice(double[], int, int[])
*/
public double[] getEnergySlice(@Nullable double[] slice, int sliceDimension, int ... indices);
/**
* Returns an array of energies for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
*
* @param sliceDimension is an integer in the range [0, {@link #getDimensions()}-1] that identifies which
* domain the slice is for.
* @param values specifies the values of the fixed dimensions. The element value at position
* {@code sliceDimension} will be ignored.
* @param slice if non-null and large enough to accommodate the values will be used as the return value,
* otherwise a new array will be allocated.
* @return an array of energy values with size equal to the size of the {@code sliceDimension} of
* the table (i.e. {@code getDomainIndexer().getDomainSize(sliceDimension)}) holding the energy values
* from the table
* <p>
* @see #getEnergySlice(double[], int, int[])
* @see #getWeightSlice(double[], int, Value...)
*/
public double[] getEnergySlice(@Nullable double[] slice, int sliceDimension, Value ... values);
/**
* Get factor function used to populate the table, if any.
* <p>
* This will be the function provided to the last call of {@link #populateFromFunction(FactorFunction)},
* if any. The function may also be cleared subsequent calls that replace all of the table entries
* (e.g. {@link #setEnergiesDense(double[])}.
* @since 0.08
* @see #populateFromFunction(FactorFunction)
*/
public @Nullable FactorFunction getFactorFunction();
/**
* Returns the underlying array of sparse element indices.
* <p>
* <b>IMPORTANT</b>: modifying the contents of the array may put the factor table into
* an invalid state. This should be treated as a read-only value.
* <p>
* If necessary, this method will implicitly modify the representation to include sparse indices
* (see {@link #hasSparseIndices()}) and if the table did not previously only had a dense value
* representation (see {@link #hasDenseRepresentation()}) it will implicitly add a sparse
* representation introducing sparse weights if the table had dense weights and otherwise introducing
* sparse energies.
*/
public int[][] getIndicesSparseUnsafe();
/**
* {@inheritDoc}
* <p>
* If necessary, this method will implicitly modify the representation to include a sparse
* form if it previously only held dense values; in this case, if the table only held dense energies, then
* sparse energies will be added, otherwise sparse weights will be added.
*/
@Override
public double getWeightForSparseIndex(int sparseIndex);
/**
* Returns the underlying array of sparse weights without copying for speed.
* <p>
* <b>IMPORTANT</b>: modifying the contents of the array may put the factor table into
* an invalid state. This should be treated as a read-only value.
* <p>
* If necessary, this method will implicitly modify the representation to include sparse weights
* (@link {@link #hasSparseEnergies()}).
* <p>
* @see #getEnergiesSparseUnsafe()
*/
public double[] getWeightsSparseUnsafe();
/**
* Returns the underlying array of dense weights without copying for speed.
* <p>
* <b>IMPORTANT</b>: modifying the contents of the array may put the factor table into
* an invalid state. This should be treated as a read-only value.
* <p>
* If necessary, this method will implicitly modify the representation to include dense weights
* <p>
* @since 0.06
*/
public double[] getWeightsDenseUnsafe();
/**
* Returns an array of weights for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
* <p>
* Same as {@link #getWeightSlice(double[], int, int[])} but always allocates a new array.
* <p>
* @see #getEnergySlice(int, int[])
*/
public double[] getWeightSlice(int sliceDimension, int ... indices);
/**
* Returns an array of weights for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
* <p>
* Same as {@link #getWeightSlice(int, Value[])} but always allocates a new array.
* <p>
* @see #getEnergySlice(int, Value[])
*/
public double[] getWeightSlice(int sliceDimension, Value ... values);
/**
* Returns an array of weights for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
*
* @param sliceDimension is an integer in the range [0, {@link #getDimensions()}-1] that identifies which
* domain the slice is for.
* @param indices specifies the element indices that are to be fixed. The element index at position
* {@code sliceDimension} will be ignored.
* @param slice if non-null and large enough to accommodate the values will be used as the return value,
* otherwise a new array will be allocated.
* @return an array of weight values with size equal to the size of the {@code sliceDimension} of
* the table (i.e. {@code getDomainIndexer().getDomainSize(sliceDimension)}) holding the weight values
* from the table
* <p>
* @see #getWeightSlice(int, int[])
* @see #getEnergySlice(double[], int, int[])
*/
public double[] getWeightSlice(@Nullable double[] slice, int sliceDimension, int ... indices);
/**
* Returns an array of weights for the {@code sliceDimension} of the factor table with all other
* dimensions fixed to provided values.
*
* @param sliceDimension is an integer in the range [0, {@link #getDimensions()}-1] that identifies which
* domain the slice is for.
* @param values specifies the values of the fixed dimensions. The element value at position
* {@code sliceDimension} will be ignored.
* @param slice if non-null and large enough to accommodate the values will be used as the return value,
* otherwise a new array will be allocated.
* @return an array of weight values with size equal to the size of the {@code sliceDimension} of
* the table (i.e. {@code getDomainIndexer().getDomainSize(sliceDimension)}) holding the weight values
* from the table
* <p>
* @see #getWeightSlice(int, Value[])
* @see #getEnergySlice(double[], int, Value[])
*/
public double[] getWeightSlice(@Nullable double[] slice, int sliceDimension, Value ... values);
/**
* True if the current factor table representation supports {@link #getIndicesSparseUnsafe}.
*/
public boolean hasSparseIndices();
/**
* Populates entries of table from given factor function.
* @param function
* @since 0.08
* @see #getFactorFunction()
*/
public void populateFromFunction(FactorFunction function);
/**
* Replace the energies/weights of the table from the given array of {@code energies} in
* order of sparse index. This does not alter the sparse to joint index mapping.
* <p>
* @see #replaceWeightsSparse(double[])
*/
public void replaceEnergiesSparse(double[] energies);
/**
* Replace the energies/weights of the table from the given array of {@code weights} in
* order of sparse index. This does not alter the sparse to joint index mapping.
* <p>
* @see #replaceEnergiesSparse(double[])
*/
public void replaceWeightsSparse(double[] weights);
/**
* Sets representation to {@link FactorTableRepresentation#DENSE_ENERGY} with
* provided energies replacing the existing contents of the table.
* @param energies specifies the energies of the table in dense joint-index order. Must have length
* equal to {@link #getDomainIndexer()}.getCardinality().
*/
public void setEnergiesDense(double[] energies);
/**
* Sets representation to {@link FactorTableRepresentation#DENSE_WEIGHT} with
* provided weights replacing the existing contents of the table.
* @param weights specifies the weights of the table in dense joint-index order. Must have length
* equal to {@link #getDomainIndexer()}.getCardinality().
*/
public void setWeightsDense(double[] weights);
/**
* Makes table directed and verifies that its weights are normalized correctly.
* <p>
* Similar to {@link #setDirected(BitSet)} but does not allow a null argument and
* will throw an exception if not {@link #isConditional()}.
*/
public void setConditional(BitSet outputSet);
/**
* Makes table directed and normalizes its weights to conditional form if necessary.
* <p>
* Equivalent to calling {@link #setDirected(BitSet)} followed by {@link #normalizeConditional()}
* but requires that {@code outputSet} is non-null.
*/
public void makeConditional(BitSet outputSet);
/**
* Randomize the weights of the table depending on underlying representation.
* <p>
* If table {@link #hasDenseRepresentation()} this will randomly assign weights/energies
* to entries for all joint indexes, otherwise it will only assign values for all sparse
* indexes. Weights will be assigned uniformly from the range (0,1].
*/
public void randomizeWeights(Random rand);
/**
* Sets representation to {@link FactorTableRepresentation#DETERMINISTIC} with given set of
* outputs.
* <p>
* @param outputIndices any array mapping input indices, representing the joint value of all input
* values, to output indices, representing the joint value of all outputs. The length of the array
* must be equal to the value of {@link JointDomainIndexer#getInputCardinality()} on {@link #getDomainIndexer()}.
* @throws UnsupportedOperationException if not {@link #isDirected()}.
*/
public void setDeterministicOutputIndices(int[] outputIndices);
/**
* Designates directionality of factor table by specifying which of its domains are outputs.
* <p>
* Note that this may change the indexing of the table's values in both sparse and dense formats.
* You can only rely on the indexing not to change if all output domains are at the front of the
* domain list (i.e. {@link JointDomainIndexer#hasCanonicalDomainOrder()}).
* <p>
* @param outputSet if null, makes the table undirected otherwise this should turn on bits corresponding
* to the domains that are to be designated as outputs. The highest bit must be less than the number of
* domains ({@link #getDimensions()}).
*/
public void setDirected(@Nullable BitSet outputSet);
/**
* {@inheritDoc}
* <p>
* If the representation started out as deterministic ({@link #hasDeterministicRepresentation()})
* and the energy changed, it will be
* implicitly converted to {@link FactorTableRepresentation#DENSE_ENERGY} and if
* {@link FactorTableRepresentation#DETERMINISTIC_WITH_INDICES} it will be converted to
* {@link FactorTableRepresentation#ALL_ENERGY_WITH_INDICES}.
*/
@Override
public void setEnergyForJointIndex(double energy, int jointIndex);
/**
* {@inheritDoc}
* <p>
* If the representation started out as deterministic ({@link #hasDeterministicRepresentation()})
* and the energy changed, it will be converted to sparse energies (retaining sparse indices if they exist).
*/
@Override
public void setEnergyForSparseIndex(double energy, int sparseIndex);
/**
* {@inheritDoc}
* <p>
* If the representation started out as {@link FactorTableRepresentation#DETERMINISTIC}, it will be
* and the weight changed, it will be implicitly converted to {@link FactorTableRepresentation#DENSE_WEIGHT} and if
* {@link FactorTableRepresentation#DETERMINISTIC_WITH_INDICES} it will be converted to
* {@link FactorTableRepresentation#ALL_WEIGHT_WITH_INDICES}.
*/
@Override
public void setWeightForJointIndex(double weight, int jointIndex);
/**
* {@inheritDoc}
* <p>
* If the representation started out as deterministic ({@link #hasDeterministicRepresentation()})
* and the weight changed, it will be converted to sparse weights (retaining sparse indices if they exist).
*/
@Override
public void setWeightForSparseIndex(double weight, int sparseIndex);
/**
* Sets the underlying representation of the table to the specified value.
* <p>
* @throws DimpleException If setting representation to a deterministic representation, this method will throw an
* exception if the table cannot be represented as deterministic (see {@link #isDeterministicDirected()}) or
* table does not support joint indexing and a dense representation is requested
* (see {@link #supportsJointIndexing()}.
*/
public void setRepresentation(FactorTableRepresentation representation);
/**
* Sets representation to {@link FactorTableRepresentation#SPARSE_ENERGY} with
* provided energies for each joint index.
* <p>
* @param jointIndices are the joint indexes of the entries to put in the table.
* @param energies specifies the energies of the table in the same order as {@code jointIndices}.
* @throws IllegalArgumentException if {@code jointIndices} and {@code energies} have different lengths,
* if there are duplicate indices or any of the indices is not in a valid range for the table.
* @see #setEnergiesSparse(int[][], double[])
* @see #setWeightsSparse(int[], double[])
*/
public void setEnergiesSparse(int[] jointIndices, double[] energies);
/**
* Sets representation to {@link FactorTableRepresentation#SPARSE_ENERGY} with
* provided weights for each set of indices.
* <p>
* @param indices are the combined element indices of the entries to put in the table.
* @param energies specifies the energies of the table in the same order as {@code indices}.
* @throws IllegalArgumentException if {@code indices} and {@code energies} have different lengths,
* if there are duplicate indices or any of the indices is not in a valid range for the table.
* @see #setEnergiesSparse(int[], double[])
* @see #setWeightsSparse(int[][], double[])
* @since 0.05
*/
public void setEnergiesSparse(int[][] indices, double[] energies);
/**
* Sets representation to {@link FactorTableRepresentation#SPARSE_WEIGHT} with
* provided weights for each joint index.
* <p>
* @param jointIndices are the joint indexes of the entries to put in the table.
* @param weights specifies the weights of the table in the same order as {@code jointIndices}.
* @throws IllegalArgumentException if {@code jointIndices} and {@code weights} have different lengths,
* if there are duplicate indices or any of the indices is not in a valid range for the table.
* @see #setWeightsSparse(int[][], double[])
* @see #setEnergiesSparse(int[], double[])
*/
public void setWeightsSparse(int[] jointIndices, double[] weights);
/**
* Sets representation to {@link FactorTableRepresentation#SPARSE_WEIGHT} with
* provided weights for each set of indices.
* <p>
* @param indices are the combined element indices of the entries to put in the table.
* @param weights specifies the weights of the table in the same order as {@code indices}.
* @throws IllegalArgumentException if {@code indices} and {@code weights} have different lengths,
* if there are duplicate indices or any of the indices is not in a valid range for the table.
* @see #setWeightsSparse(int[], double[])
* @see #setEnergiesSparse(int[][], double[])
*/
public void setWeightsSparse(int[][] indices, double[] weights);
}