/*******************************************************************************
* 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.model.core;
import java.util.Arrays;
import java.util.UUID;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.dimple.environment.DimpleEnvironment;
import com.analog.lyric.dimple.model.factors.Factor;
import com.analog.lyric.dimple.model.variables.Constant;
import com.analog.lyric.dimple.model.variables.Variable;
import com.analog.lyric.dimple.model.variables.VariableBlock;
import com.analog.lyric.util.misc.IGetId;
import com.analog.lyric.util.misc.Internal;
/**
* Static utility methods for manipulating factor graph child identifiers.
* <p>
* There are a number of different types of identifiers used to index
* nodes and other objects belonging to factor graphs. All are represented
* as a primitive int or long.
* <p>
* <ul>
* <li><b>local id</b> - identifies a node uniquely within the factor graph
* that contains it. The local id also identifies the type of object (e.g. factor, variable).
* {@link FactorGraph} provides methods for retrieving nodes by their local id, such as
* {@linkplain FactorGraph#getNodeByLocalId(int) getNodeByLocalId}.
*
* <li><b>local index</b> - the index component of a local id that does not include its type tag (see internal layouts
* below). This typically is used as a direct index into arrays of data associated with child instances.
*
* <li><b>graph id</b> - uniquely identifies a {@link FactorGraph} within the
* {@link DimpleEnvironment}'s (typically there is only one) {@linkplain DimpleEnvironment#factorGraphs() factor
* graph registry}.
*
* <li><b>global id</b> - combines the local id and graph id to uniquely identify a node within
* the environment. The {@code getNodeByGlobalId} method in both {@linkplain DimpleEnvironment#getNodeByGlobalId(long)
* DimpleEnvironment} and {@linkplain FactorGraph#getNodeByGlobalId(long) FactorGraph} provides relatively fast lookup
* of a node by its global id.
*
* <li><b>graph tree index</b> - is a simple index that uniquely identifies a graph within the
* tree off graphs sharing a common root graph. All of the graphs in the tree are stored in an array
* shared by the graphs in the same tree. This index indicates the position in the array, so lookup by
* this index is fast.
*
* <li><b>graph tree id</b> - combines the local id and graph tree index to uniquely identify
* a node within its graph tree. This provides fast lookup of nodes within the tree using
* {@link FactorGraph#getNodeByGraphTreeId(long)}.
* </ul>
* <p>
* Note that some objects may be represented by more than one identifier. The same variable may
* be represented by its variable id in the graph that owns it, or a boundary variable id in a graph that has
* it as a boundary variable. The same boundary edge will have different identifiers with respect to the two
* graphs that refer to it.
* <p>
* <h2>Internal layout</h2>
*
* Identifiers have the following internal layouts:
* <p>
* <ul>
* <li><b>local id</b>
* <pre>
* +------+-----------------------------+
* | type | index |
* +------+-----------------------------+
* 31 28 0
* </pre>
*
* <li><b>global id</b>
* <pre>
* +----+-------------------------------+--------------------------------------+
* | 01 | graph id | local id |
* +----+-------------------------------+--------------------------------------+
* 63 61 32 0
* </pre>
* <li><b>graph tree id</b>
* <pre>
* +----+-------------------------------+--------------------------------------+
* | 00 | graph tree index | local id |
* +----+-------------------------------+--------------------------------------+
* 63 61 32 0
* </pre>
* </ul>
*
* @since 0.08
*/
public abstract class Ids
{
/*-----------
* Constants
*/
/**
* Maximum value for Dimple environment identifier.
* <p>
* @since 0.08
* @see #ENV_ID_MIN
* @see DimpleEnvironment#getEnvId()
*/
public static final long ENV_ID_MAX = (1L << 60) - 1;
/**
* Minimum value for Dimple environment identifier.
* <p>
* @since 0.08
* @see #ENV_ID_MAX
* @see DimpleEnvironment#getEnvId()
*/
public static final long ENV_ID_MIN = 0;
/**
* UUID version value for Dimple UUIDs.
* <p>
* Note that this is not a standard UUID version value to reflect
* the custom UUID structure.
* <p>
* @see #makeUUID(long, long)
* @see UUID#version()
*/
public static final int DIMPLE_UUID_VERSION = 0xD;
/**
* Maximum value for a Dimple factor graph identifier.
* <p>
* @since 0.08
* @see #GRAPH_ID_MIN
* @see FactorGraph#getGraphId()
*/
public static final int GRAPH_ID_MAX = (1<<30) - 1;
/**
* Minimum value for a Dimple factor graph identifier.
* <p>
* @since 0.08
* @see #GRAPH_ID_MAX
* @see FactorGraph#getGraphId()
*/
public static final int GRAPH_ID_MIN = 1;
/**
* Maximum value for a Dimple global identifier.
* <p>
* @since 0.08
* @see #GLOBAL_ID_MIN
* @see Node#getGlobalId()
*/
public static final long GLOBAL_ID_MAX = (1L<<63) - 1;
/**
* Minimum value for a Dimple global identifier.
* <p>
* @since 0.08
* @see #GLOBAL_ID_MAX
* @see Node#getGlobalId()
*/
public static final long GLOBAL_ID_MIN = ((long)GRAPH_ID_MIN << 32) | 1L<<62;
/**
* Offset of the type index portion of a local identifier.
* @see #typeIndexFromLocalId(int)
* @category internal
*/
@Internal
public static final int LOCAL_ID_TYPE_OFFSET = 28;
public static final int UNKNOWN_TYPE = 0;
/**
* Type index for factor identifiers.
* @see #typeIndexFromLocalId(int)
*/
public static final int FACTOR_TYPE = 1;
/**
* Type index for subgraph identifiers.
* @see #typeIndexFromLocalId(int)
*/
public static final int GRAPH_TYPE = 2;
/**
* Type index for owned (non-boundary) variable identifiers.
* @see #typeIndexFromLocalId(int)
*/
public static final int VARIABLE_TYPE = 3;
/**
* Type index for boundary variable identifiers.
* @see #typeIndexFromLocalId(int)
*/
public static final int BOUNDARY_VARIABLE_TYPE = 4;
/**
* Type index for edge identifiers.
* @see #typeIndexFromLocalId(int)
*/
public static final int EDGE_TYPE = 5;
/**
* Type index for factor port identifiers
* @see #typeIndexFromLocalId(int)
*/
public static final int FACTOR_PORT_TYPE = 6;
/**
* Type index for variable port identifiers
* @see #typeIndexFromLocalId(int)
*/
public static final int VARIABLE_PORT_TYPE = 7;
/**
* Type index for variable block identifiers.
* @see #typeIndexFromLocalId(int)
*/
public static final int VARIABLE_BLOCK_TYPE = 8;
/**
* Type index for constant identifiers.
* @see #typeIndexFromLocalId(int)
*/
public static final int CONSTANT_TYPE = 9;
/**
* Value of minimum type index for local identifiers
*/
public static final int TYPE_MIN = 0;
/**
* Value of max type index for local identifiers
*/
public static final int TYPE_MAX = 9;
/**
* This is only used as the {@link Type#instanceClass() instanceClass} of {@link Type#UNKNOWN}.
* <p>
* It cannot be instantiated.
*
* @since 0.08
*/
public abstract class UnknownChild implements IFactorGraphChild
{
private UnknownChild() {}
}
/**
* Enumerates supported identifier types.
* <p>
* These correspond to the similarly named type index constants, e.g.
* {@link Type#FACTOR} corresponds to {@link Ids#FACTOR_TYPE}.
* <p>
* @since 0.08
* @author Christopher Barber
*/
public enum Type
{
// NOTE: after release 0.08 new entries should be added at the end since the type
// index may be used in external persistent representations.
UNKNOWN(UnknownChild.class),
FACTOR(Factor.class),
GRAPH(FactorGraph.class),
VARIABLE(Variable.class),
BOUNDARY_VARIABLE(Variable.class),
EDGE(Edge.class),
FACTOR_PORT(FactorPort.class),
VARIABLE_PORT(VariablePort.class),
VARIABLE_BLOCK(VariableBlock.class),
CONSTANT(Constant.class);
// Possible future types:
// PARAMETER - T for theta
// UTILITY VARIABLE - U
// ACTION FACTOR - A
private final Class<? extends IFactorGraphChild> _instanceType;
private static final Type[] _values = Type.values();
/**
* Single character type prefixes in same order as enum instances.
*/
private static final String DEFAULT_NAME_PREFIX = "UFGVBEPQKC";
/*--------------
* Construction
*/
private Type(Class<? extends IFactorGraphChild> instanceType)
{
_instanceType = instanceType;
}
/*----------------
* Static methods
*/
/**
* Returns type whose {@link #instanceClass()} matches argument.
* <p>
* @param instanceClass is a {@link IFactorGraphChild} is a subclass of the {@link #instanceClass()}
* of one of the enumerated types.
* @throws IllegalArgumentException if {@code instanceClass} does not match any of the types. This can
* only happen if someone has created a new implementation of {@link IFactorGraphChild} that is not
* registered by this class or {@code instanceClass} is too general (e.g. {@link Node}).
* @since 0.08
*/
public static Type forInstanceClass(Class<? extends IFactorGraphChild> instanceClass)
{
for (Type type : _values)
{
if (type._instanceType.isAssignableFrom(instanceClass))
{
return type;
}
}
throw new IllegalArgumentException(String.format("%s does not have an identifier type",
instanceClass.getSimpleName()));
}
/**
* Returns {@code Type} value with given ordinal value
* @since 0.08
*/
public static Type valueOf(int ordinal)
{
return _values[ordinal];
}
/*-----------------
* Regular methods
*/
/**
* {@link IFactorGraphChild} class used to represent instances of this type.
* @since 0.08
*/
public Class<? extends IFactorGraphChild> instanceClass()
{
return _instanceType;
}
/**
* Prefix character used to identify type in names produced by {@link Ids#defaultNameForLocalId(int)}.
* @since 0.08
*/
public char namePrefix()
{
return DEFAULT_NAME_PREFIX.charAt(ordinal());
}
/**
* Returns type index for this type.
* <p>
* This is the same as {@link #ordinal()}.
* @since 0.08
*/
public int typeIndex()
{
return ordinal();
}
}
/**
* Maximum value for the index portion of a local identifier.
* <p>
* @since 0.08
* @see #LOCAL_ID_INDEX_MIN
* @see #indexFromLocalId(int)
*/
public static final int LOCAL_ID_INDEX_MAX = (1<<LOCAL_ID_TYPE_OFFSET) - 1;
/**
* Minimum value for the index portion of a local identifier.
* <p>
* @since 0.08
* @see #LOCAL_ID_INDEX_MAX
* @see #indexFromLocalId(int)
*/
public static final int LOCAL_ID_INDEX_MIN = 0;
/**
* Default value of local id for a factor that has not yet been added to a {@link FactorGraph}.
* @category internal
*/
@Internal
public static final int INITIAL_FACTOR_ID = FACTOR_TYPE << LOCAL_ID_TYPE_OFFSET | LOCAL_ID_INDEX_MAX;
/**
* Default value of local id for a subgraph that has not yet been added to a {@link FactorGraph}.
* @category internal
*/
@Internal
public static final int INITIAL_GRAPH_ID = GRAPH_TYPE << LOCAL_ID_TYPE_OFFSET | LOCAL_ID_INDEX_MAX;
/**
* Default value of local id for a variable that has not yet been added to a {@link FactorGraph}.
* @category internal
*/
@Internal
public static final int INITIAL_VARIABLE_ID = VARIABLE_TYPE << LOCAL_ID_TYPE_OFFSET | LOCAL_ID_INDEX_MAX;
private static String DEFAULT_GRAPH_NAME_PREFIX = "$Graph";
/**
* Mask for global identifier within UUID.
*/
private static final long GLOBAL_ID_UUID_MASK = (1L<<62) - 1;
/**
* High-order two bits used to distinguish global id from a graph tree id.
*/
private static final long GLOBAL_ID_INDICATOR = 1L<<62;
/**
* Mask for index (non-type) portion of local identifier.
*/
private static final int LOCAL_ID_INDEX_MASK = LOCAL_ID_INDEX_MAX;
private static final int UUID_VERSION_OFFSET = 12;
private static final int UUID_VERSION_WIDTH = 4;
private static final long UUID_NON_VERSION_MASK = (1L << UUID_VERSION_OFFSET) - 1;
/*----------------
* Static methods
*/
/**
* Makes a UUID instance encoding Dimple environment and global identifiers.
* <p>
* The UUID will have the IETF {@link UUID#variant()} value, and a
* non-IETF {@link UUID#version()} set to {@link #DIMPLE_UUID_VERSION}.
* <p>
* @param envId a valid {@linkplain DimpleEnvironment#getEnvId() Dimple environment id}
* @param globalId a valid {@linkplain Node#getGlobalId() global node id}
* @since 0.08
* @see #envIdFromUUID(UUID)
* @see #globalIdFromUUID(UUID)
*/
public static UUID makeUUID(long envId, long globalId)
{
long msb = envId >>> UUID_VERSION_OFFSET;
msb <<= UUID_VERSION_WIDTH;
msb |= DIMPLE_UUID_VERSION;
msb <<= UUID_VERSION_OFFSET;
msb |= envId & UUID_NON_VERSION_MASK;
final long lsb = Long.MIN_VALUE | globalId & GLOBAL_ID_UUID_MASK;
return new UUID(msb, lsb);
}
/**
* Constructs default name for a graph with given graph identifier.
* <p>
* @since 0.08
* @see #graphIdFromDefaultName(String)
* @see FactorGraph#getName()
*/
public static String defaultNameForGraphId(int graphId)
{
return String.format("%s%d", DEFAULT_GRAPH_NAME_PREFIX, graphId);
}
/**
* Constructs default name for a node with given local identifier.
* <p>
* The name will have the format "$" + "<i>type-prefix-character</i>" + "<i>index</i>"
* <p>
* @since 0.08
* @see #localIdFromDefaultName(String)
* @see Node#getName()
*/
public static String defaultNameForLocalId(int id)
{
int type = id >>> LOCAL_ID_TYPE_OFFSET;
char c = type <= TYPE_MAX ? Type.DEFAULT_NAME_PREFIX.charAt(type) : 'X';
return String.format("$%c%d", c, id & LOCAL_ID_INDEX_MASK);
}
/**
* Extracts environment id from Dimple UUID
* <p>
* Returns the {@linkplain DimpleEnvironment#getEnvId() Dimple environment id} encoded in the
* UUID or else -1 if UUID was not created by {@link #makeUUID(long, long)}.
* @since 0.08
*/
public static long envIdFromUUID(UUID uid)
{
if (uid.variant() != 2 || uid.version() != DIMPLE_UUID_VERSION)
{
return -1L;
}
final long msb = uid.getMostSignificantBits();
return msb & UUID_NON_VERSION_MASK | (msb >>> UUID_VERSION_WIDTH) & ~UUID_NON_VERSION_MASK;
}
/**
* Extracts global id from Dimple UUID
* <p>
* Returns the {@linkplain Node#getGlobalId() Dimple global id} encoded in the
* UUID, or else -1 if UUID was not created by {@link #makeUUID(long, long)}.
* @since 0.08
*/
public static long globalIdFromUUID(UUID nodeUid)
{
if (nodeUid.variant() != 2 || nodeUid.version() != DIMPLE_UUID_VERSION)
{
return -1L;
}
return nodeUid.getLeastSignificantBits() & GLOBAL_ID_UUID_MASK | GLOBAL_ID_INDICATOR;
}
/**
* Construct a global identifier from its {@linkplain FactorGraph#getGraphId graph id} and its
* {@linkplain IGetId#getLocalId() local id}.
* @since 0.08
*/
public static long globalIdFromParts(int graphId, int localId)
{
return (long)graphId << 32 | 0xFFFFFFFFL & localId | GLOBAL_ID_INDICATOR;
}
/**
* Construct a global identifier from its {@linkplain FactorGraph#getGraphId graph id}, its
* local id type index and its local index.
* @since 0.08
* @see #globalIdFromParts(int, int)
* @see #localIdFromParts(int, int)
*/
public static long globalIdFromParts(int graphId, int idType, int localIndex)
{
return globalIdFromParts(graphId, localIdFromParts(idType, localIndex));
}
/**
* Extracts graph id from default graph name.
* <p>
* If {@code name} was formatted using {@link #defaultNameForGraphId(int)},
* this will return the graph identifier used to construct the name, otherwise
* returns -1 (which is not a valid graph id).
* @since 0.08
*/
public static int graphIdFromDefaultName(String name)
{
if (name.startsWith(DEFAULT_GRAPH_NAME_PREFIX))
{
try
{
int id = Integer.parseInt(name.substring(DEFAULT_GRAPH_NAME_PREFIX.length()));
if (GRAPH_ID_MIN <= id && id <= GRAPH_ID_MAX)
{
return id;
}
}
catch (NumberFormatException ex)
{
// ignore
}
}
return -1;
}
/**
* Return graph identifier from Dimple global id
* @since 0.08
* @see Node#getGlobalId()
*/
public static int graphIdFromGlobalId(long globalId)
{
return (int)(globalId >>> 32) & 0x3FFFFFFF;
}
public static long graphTreeIdFromParts(int graphTreeIndex, int localId)
{
return (long)graphTreeIndex << 32 | 0xFFFFFFFFL & localId;
}
/**
* Returns {@linkplain FactorGraph#getGraphTreeIndex() graph tree index} portion of
* {@linkplain IFactorGraphChild#getGraphTreeId() graph tree identifier}.
* @since 0.08
*/
public static int graphTreeIndexFromGraphTreeId(long graphTreeId)
{
return (int)(graphTreeId >>> 32);
}
/**
* Constructs local id from type and index.
* @since 0.08
*/
public static int localIdFromParts(int nodeType, int index)
{
return (nodeType << LOCAL_ID_TYPE_OFFSET) | index;
}
/**
* Extract index portion of local identifier.
* <p>
* @since 0.08
*/
public static int indexFromLocalId(int id)
{
return id & LOCAL_ID_INDEX_MASK;
}
public static boolean isGlobalId(long id)
{
return (id >>> 62) == 1L;
}
/**
* Determine if string represents a Dimple UUID
* <p>
* True if {@code str} is in the format produced by {@link UUID#toString}
* on a UUID constructed using {@link #makeUUID(long, long)}.
* @since 0.08
*/
public static boolean isUUIDString(String str)
{
if (str.length() != 36)
{
return false;
}
for (int i = 0; i < 36; ++i)
{
final char c = str.charAt(i);
switch (i)
{
case 8:
case 13:
case 18:
case 23:
if (c != '-')
{
return false;
}
break;
case 14:
if (c != 'D' && c != 'd')
{
return false;
}
break;
case 19:
if (c != '8' && c != '9' && c != 'a' && c != 'b' && c != 'A' && c != 'B')
{
return false;
}
break;
default:
if (c < '0' || c > 'f' || c > '9' && c < 'A' || c > 'F' && c < 'a')
{
return false;
}
}
}
return true;
}
/**
* Extracts local id from default node name.
* <p>
* If {@code name} was formatted using {@link #defaultNameForLocalId(int)},
* this will return the identifier used to construct the name, otherwise
* returns -1 (which is not a valid id).
* @since 0.08
*/
public static int localIdFromDefaultName(String name)
{
final int length = name.length();
if (length > 2 && name.charAt(0) == '$')
{
char c = name.charAt(1);
if ('A' <= c && c <= 'Z')
{
try
{
int index = Integer.parseInt(name.substring(2));
if (LOCAL_ID_INDEX_MIN <= index && index <= LOCAL_ID_INDEX_MAX)
{
int type = Type.DEFAULT_NAME_PREFIX.indexOf(c);
if (type >= 0)
{
return (type << LOCAL_ID_TYPE_OFFSET) | index;
}
}
}
catch (NumberFormatException ex)
{
// Ignore
}
}
}
return -1;
}
/**
* Return local identifier from Dimple global id
* @since 0.08
* @see INode#getGlobalId()
*/
public static int localIdFromGlobalId(long globalId)
{
return (int)(globalId & 0xFFFFFFFFL);
}
/**
* Return local identifier from Dimple graph tree id
* @since 0.08
* @see INode#getGraphTreeId()
*/
public static int localIdFromGraphTreeId(long graphTreeId)
{
return (int)(graphTreeId & 0xFFFFFFFFL);
}
/**
* Returns index of type encoded in local identifier
* <p>
* @return type index portion of local identifier. Valid local ids should return a number
* in the range {@link #TYPE_MIN} to {@link #TYPE_MAX}.
* <p>
* @since 0.08
* @see #typeFromLocalId(int)
*/
public static int typeIndexFromLocalId(int localId)
{
return localId >>> LOCAL_ID_TYPE_OFFSET;
}
/**
* Returns index of type encoded in local identifiers of instances of given class.
*
* @param instanceClass is a {@link IFactorGraphChild} that is specific to one id type.
* @throws IllegalArgumentException if {@code instanceClass} does not match any of the types. This can
* only happen if someone has created a new implementation of {@link IFactorGraphChild} that is not
* registered by this class or {@code instanceClass} is too general (e.g. {@link Node}).
* @since 0.08
*/
public static int typeIndexForInstanceClass(Class<? extends IFactorGraphChild> instanceClass)
{
return Type.forInstanceClass(instanceClass).typeIndex();
}
private static final Type[] _localIdTypes = Arrays.copyOf(Type.values(), 16);
/**
* Returns type encoded in local identifier.
* @param localId a local identifier constructed with {@link Ids#localIdFromParts(int, int)}.
* @return {@link Type} of given id, or null if id has an invalid type index.
* @since 0.08
* @see #typeIndexFromLocalId(int)
*/
public static @Nullable Type typeFromLocalId(int localId)
{
return _localIdTypes[localId >>> LOCAL_ID_TYPE_OFFSET];
}
}