/*
* File: YaleFormatWeightedNeighbors.java
* Authors: Jeremy D. Wendt
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright 2016, Sandia Corporation.
* Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
* license for use of this work by or on behalf of the U.S. Government.
* Export of this program may require a license from the United States
* Government. See CopyrightHistory.txt for complete details.
*
*/
package gov.sandia.cognition.graph.community;
import gov.sandia.cognition.annotation.PublicationReference;
import gov.sandia.cognition.annotation.PublicationType;
import gov.sandia.cognition.graph.DirectedNodeEdgeGraph;
import gov.sandia.cognition.graph.DirectedWeightedNodeEdgeGraph;
import gov.sandia.cognition.collection.DoubleArrayList;
import gov.sandia.cognition.collection.IntArrayList;
import gov.sandia.cognition.util.Pair;
import java.util.HashMap;
import java.util.Map;
/**
* This class initializes the neighbors set of a graph as a Yale-format-like
* graph. As this was needed by at least two algorithms already, it was
* extracted to its own class. However, as it's fairly hard to interface with
* directly if you don't know what you're doing (and those two classes do and
* want into the guts) this is package private.
*
* @author jdwendt
*/
@PublicationReference(type = PublicationType.WebPage, author = "Wikipedia",
title = "Sparse matrix - Compressed sparse row (CSR, CRS, or Yale format)",
year = 2016, url
= "https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR.2C_CRS_or_Yale_format.29")
class YaleFormatWeightedNeighbors<NodeNameType>
{
/**
* Yale-format-like representation of the neighbors of each node (see
* https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR.2C_CRS_or_Yale_format.29
* ). This specifies the index of the first neighbor in the neighbors list.
*/
private final IntArrayList neighborsFirstIdx;
/**
* Yale-format-like representation of the neighbors of each node (see
* https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR.2C_CRS_or_Yale_format.29
* ). This contains the ids of all neighbors of all nodes in node-order. To
* figure out a specific node's neighbors, look from indices
* neighborsFirstIdx.get(i) to neighborsFirstIdx.get(i+1).
*/
private final IntArrayList neighbors;
/**
* Yale-format-like representation of the neighbors of each node (see
* https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR.2C_CRS_or_Yale_format.29
* ). This contains the weights of all neighbors of all nodes in node-order.
* Follows the same order as IntVector neighbors.
*/
private final DoubleArrayList wNeighbors;
/**
* Initializes the three parts of the weighted Yale format for the neighbors
* of all nodes in the input graph.
*
* @param graph The graph whose parameters should be initialized.
* @param removeSelfLoops If true, self loops won't be in the computed
* neighbors
*/
public YaleFormatWeightedNeighbors(DirectedNodeEdgeGraph<NodeNameType> graph,
boolean removeSelfLoops)
{
int numNodes = graph.getNumNodes();
this.neighborsFirstIdx = new IntArrayList(numNodes + 1);
// Initialize the per-node values
int neighborsSoFar = 0;
Map<Integer, HashMap<Integer, Double>> edges = new HashMap<>(
graph.getNumNodes());
for (int i = 0; i < graph.getNumNodes(); ++i)
{
edges.put(i, new HashMap<>());
}
for (int i = 0; i < graph.getNumEdges(); ++i)
{
Pair<Integer, Integer> edge = graph.getEdgeEndpointIds(i);
int l = edge.getFirst();
int r = edge.getSecond();
if (removeSelfLoops && (l == r))
{
continue;
}
double w = 1.0;
if (graph instanceof DirectedWeightedNodeEdgeGraph)
{
w = ((DirectedWeightedNodeEdgeGraph) graph).getEdgeWeight(i);
}
if (!edges.get(l).containsKey(r))
{
edges.get(l).put(r, 0.0);
}
edges.get(l).put(r, w + edges.get(l).get(r));
if (!edges.get(r).containsKey(l))
{
edges.get(r).put(l, 0.0);
}
edges.get(r).put(l, w + edges.get(r).get(l));
}
for (int i = 0; i < graph.getNumNodes(); ++i)
{
// This is to optimize the nieghbors list
this.neighborsFirstIdx.add(neighborsSoFar);
neighborsSoFar += edges.get(i).size();
}
this.neighborsFirstIdx.add(neighborsSoFar);
// Initialize neighbors to null values (for filling on next loop)
this.neighbors = new IntArrayList(neighborsSoFar);
this.wNeighbors = new DoubleArrayList(neighborsSoFar);
for (int i = 0; i < neighborsSoFar; ++i)
{
this.neighbors.add(-1);
this.wNeighbors.add(0);
}
// Initialize the per-edge values
for (Map.Entry<Integer, HashMap<Integer, Double>> edgeMap
: edges.entrySet())
{
int l = edgeMap.getKey();
for (Map.Entry<Integer, Double> edge : edgeMap.getValue().entrySet())
{
int r = edge.getKey();
if (removeSelfLoops && (l == r))
{
continue;
}
double w = edge.getValue();
int idx = findNextEmptyNeighbor(l);
this.neighbors.set(idx, r);
this.wNeighbors.set(idx, w);
}
}
}
/**
* Private helper for adding neighbors to the internal neighbors list (which
* is initialized to all -1s).
*
* @param nodeNum The node whose first empty neighbor spot is sought after
* @return The index in neighbors where there's an opening for a neighbor
* for nodeNum
* @throws IllegalArgumentException if nodeNum has no more openings for
* neighbors. This point should never be reached in this code.
*/
private int findNextEmptyNeighbor(int nodeNum)
{
for (int i = neighborsFirstIdx.get(nodeNum); i < neighborsFirstIdx.get(
nodeNum + 1); ++i)
{
if (neighbors.get(i) == -1)
{
return i;
}
}
throw new IllegalArgumentException(
"This node is full, but such shouldn't be possible");
}
/**
* Returns the int vector containing the neighbors for all nodes stored in
* Yale format. Alter this at your own risk!
*
* @return the int vector containing the neighbors for all nodes stored in
* Yale format
*/
public IntArrayList getNeighbors()
{
return neighbors;
}
/**
* Returns the int vector containing the first index into the neighbors
* vector for all nodes stored in Yale format. Alter this at your own risk!
*
* @return the int vector containing the first index into the neighbors
* vector for all nodes stored in Yale format
*/
public IntArrayList getNeighborsFirstIndex()
{
return neighborsFirstIdx;
}
/**
* Returns the double vector containing the weights for all neighbors for
* all nodes stored in Yale format. Alter this at your own risk!
*
* @return the double vector containing the weights for all neighbors for
* all nodes stored in Yale format
*/
public DoubleArrayList getNeighborsWeights()
{
return wNeighbors;
}
}