All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dishevelled.analysis.AnalysisUtils Maven / Gradle / Ivy

/*

    dsh-analysis  Data analysis.
    Copyright (c) 2011-2014 held jointly by the individual authors.

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
    License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library;  if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.

    > http://www.fsf.org/licensing/licenses/lgpl.html
    > http://www.opensource.org/licenses/lgpl-license.php

*/
package org.dishevelled.analysis;

import static org.dishevelled.collect.Maps.createMap;
import static org.dishevelled.collect.Sets.createSet;

import static org.dishevelled.graph.impl.GraphUtils.createGraph;

import static org.dishevelled.matrix.impl.SparseMatrixUtils.createSparseMatrix2D;

import static org.dishevelled.multimap.impl.BinaryKeyMaps.createBinaryKeyMap;

import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.ArrayTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;

import org.dishevelled.graph.Edge;
import org.dishevelled.graph.Graph;
import org.dishevelled.graph.Node;

import org.dishevelled.functor.BinaryFunction;
import org.dishevelled.functor.BinaryProcedure;
import org.dishevelled.functor.UnaryFunction;
import org.dishevelled.functor.UnaryPredicate;
import org.dishevelled.functor.UnaryProcedure;
import org.dishevelled.functor.TernaryProcedure;

import org.dishevelled.matrix.BitMatrix2D;
import org.dishevelled.matrix.Matrix2D;

import org.dishevelled.multimap.BinaryKey;
import org.dishevelled.multimap.BinaryKeyMap;

/**
 * Static utility methods for data analysis.
 *
 * @author  Michael Heuer
 */
public final class AnalysisUtils
{
    /** Default size, 16. */
    static final int DEFAULT_SIZE = 16;

    /** Default factor, 0.75f. */
    static final float DEFAULT_FACTOR = 0.75f;


    /**
     * Private no-arg constructor.
     */
    private AnalysisUtils()
    {
        // empty
    }


    // --> binary key map


    /**
     * Convert the specified graph to a binary key map.  Note that only nodes with degree
     * of at least one will be present in the returned binary key map.
     *
     * @param  graph node type and binary key map key type
     * @param  graph edge type and binary key map value type
     * @param graph graph to convert, must not be null
     * @return the specified graph converted to a binary key map
     */
    public static  BinaryKeyMap toBinaryKeyMap(final Graph graph)
    {
        if (graph == null)
        {
            throw new IllegalArgumentException("graph must not be null");
        }
        int e = Math.max(DEFAULT_SIZE, graph.edgeCount());
        final BinaryKeyMap binaryKeyMap = createBinaryKeyMap(e);
        graph.forEachEdge(new UnaryProcedure>()
                          {
                              @Override
                              public void run(final Edge edge)
                              {
                                  // todo:  provide "merge strategy" for multiple edges
                                  binaryKeyMap.put(edge.source().getValue(), edge.target().getValue(), edge.getValue());
                              }
                          });
        return binaryKeyMap;
    }

    /**
     * Convert the specified table to a binary key map.
     *
     * @param  table row and column key type
     * @param  table value type and binary key map value type
     * @param table table to convert, must not be null
     * @return the specified table converted to a binary key map
     */
    public static  BinaryKeyMap toBinaryKeyMap(final Table table)
    {
        if (table == null)
        {
            throw new IllegalArgumentException("table must not be null");
        }
        int e = Math.max(DEFAULT_SIZE, table.size());
        final BinaryKeyMap binaryKeyMap = createBinaryKeyMap(e);
        for (Table.Cell cell : table.cellSet())
        {
            binaryKeyMap.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
        }
        return binaryKeyMap;
    }

    /**
     * Convert the specified matrix to a binary key map with long indices as keys.
     *
     * @param  matrix type and binary key map value type
     * @param matrix matrix to convert, must not be null
     * @return the specified matrix converted to a binary key map with long indices as keys
     */
    public static  BinaryKeyMap toBinaryKeyMap(final Matrix2D matrix)
    {
        return toBinaryKeyMap(matrix, IDENTITY);
    }

    /**
     * Convert the specified matrix to a binary key map.
     *
     * @param  binary key map key type
     * @param  matrix type and binary key map value type
     * @param matrix matrix to convert, must not be null
     * @param keys mapping of keys by long indices, must not be null
     * @return the specified matrix converted to a binary key map
     */
    public static  BinaryKeyMap toBinaryKeyMap(final Matrix2D matrix,
                                                              final UnaryFunction keys)
    {
        if (matrix == null)
        {
            throw new IllegalArgumentException("matrix must not be null");
        }
        if (matrix.rows() != matrix.columns())
        {
            throw new IllegalArgumentException("matrix must be balanced, rows="
                                               + matrix.rows() + ", columns=" + matrix.columns());
        }
        if (matrix.cardinality() > Integer.MAX_VALUE)
        {
            throw new IllegalArgumentException("binary key map size is limited to " + Integer.MAX_VALUE);
        }
        if (keys == null)
        {
            throw new IllegalArgumentException("keys must not be null");
        }
        int e = Math.max(DEFAULT_SIZE, (int) matrix.cardinality());
        final BinaryKeyMap binaryKeyMap = createBinaryKeyMap(e);
        matrix.forEach(new TernaryProcedure()
                       {
                           @Override
                           public void run(final Long row, final Long column, final E value)
                           {
                               N firstKey = keys.evaluate(row);
                               N secondKey = keys.evaluate(column);

                               if (firstKey != null && secondKey != null && value != null)
                               {
                                  // todo:  provide "merge strategy" for multiple mappings
                                   binaryKeyMap.put(firstKey, secondKey, value);
                               }
                           }
                       });
        return binaryKeyMap;
    }

    /**
     * Convert the specified bit matrix to a binary key map with long indices as keys.
     *
     * @param  matrix type and binary key map value type
     * @param bitMatrix bit matrix to convert, must not be null
     * @param values mapping of values by keys, must not be null
     * @return the specified bit matrix converted to a binary key map with long indices as keys
     */
    public static  BinaryKeyMap toBinaryKeyMap(final BitMatrix2D bitMatrix,
                                                                 final BinaryFunction values)
    {
        return toBinaryKeyMap(bitMatrix, IDENTITY, values);
    }

    /**
     * Convert the specified bit matrix to a binary key map.
     *
     * @param  binary key map key type
     * @param  matrix type and binary key map value type
     * @param bitMatrix bit matrix to convert, must not be null
     * @param keys mapping of keys by long indices, must not be null
     * @param values mapping of values by keys, must not be null
     * @return the specified bit matrix converted to a binary key map
     */
    public static  BinaryKeyMap toBinaryKeyMap(final BitMatrix2D bitMatrix,
                                                              final UnaryFunction keys,
                                                              final BinaryFunction values)
    {
        if (bitMatrix == null)
        {
            throw new IllegalArgumentException("bitMatrix must not be null");
        }
        if (bitMatrix.rows() != bitMatrix.columns())
        {
            throw new IllegalArgumentException("bitMatrix must be balanced, rows="
                                               + bitMatrix.rows() + ", columns=" + bitMatrix.columns());
        }
        if (bitMatrix.cardinality() > Integer.MAX_VALUE)
        {
            throw new IllegalArgumentException("binary key map size is limited to " + Integer.MAX_VALUE);
        }
        if (keys == null)
        {
            throw new IllegalArgumentException("keys must not be null");
        }
        if (values == null)
        {
            throw new IllegalArgumentException("values must not be null");
        }
        int e = Math.max(DEFAULT_SIZE, (int) bitMatrix.cardinality());
        final BinaryKeyMap binaryKeyMap = createBinaryKeyMap(e);
        bitMatrix.forEach(true, new BinaryProcedure()
                       {
                           @Override
                           public void run(final Long row, final Long column)
                           {
                               N firstKey = keys.evaluate(row);
                               N secondKey = keys.evaluate(column);
                               E value = values.evaluate(firstKey, secondKey);

                               if (firstKey != null && secondKey != null && value != null)
                               {
                                   // todo:  provide "merge strategy" for multiple mappings
                                   binaryKeyMap.put(firstKey, secondKey, value);
                               }
                           }
                       });
        return binaryKeyMap;
    }


    // --> bit matrix


    /**
     * Convert the specified binary key map to a bit matrix.  The first key value will be
     * used to look up the row index and the second key value the column index in the returned bit matrix.
     *
     * @param  binary key map key type
     * @param  binary key map value type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @param keys list of keys, must not be null
     * @param predicate binary key map value predicate, must not be null
     * @return the specified binary key map converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final BinaryKeyMap binaryKeyMap,
                                                 final List keys,
                                                 final UnaryPredicate predicate)
    {
        return toBitMatrix(binaryKeyMap, new IndexOfKey(keys), predicate);
    }

    /**
     * Convert the specified binary key map to a bit matrix.  The first key value will be
     * used to look up the row index and the second key value the column index in the returned bit matrix.
     *
     * @param  binary key map key type
     * @param  binary key map value type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @param keyIndices map of long indices by keys, must not be null
     * @param predicate binary key map value predicate, must not be null
     * @return the specified binary key map converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final BinaryKeyMap binaryKeyMap,
                                                 final Map keyIndices,
                                                 final UnaryPredicate predicate)
    {
        return toBitMatrix(binaryKeyMap, new KeyIndices(keyIndices), predicate);
    }

    /**
     * Convert the specified binary key map to a bit matrix.  The first key value will be
     * used to look up the row index and the second key value the column index in the returned bit matrix.
     *
     * @param  binary key map key type
     * @param  binary key map value type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @param keyIndices mapping of key indices by key, must not be null
     * @param predicate binary key map value predicate, must not be null
     * @return the specified binary key map converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final BinaryKeyMap binaryKeyMap,
                                                 final UnaryFunction keyIndices,
                                                 final UnaryPredicate predicate)
    {
        if (binaryKeyMap == null)
        {
            throw new IllegalArgumentException("binaryKeyMap must not be null");
        }
        if (keyIndices == null)
        {
            throw new IllegalArgumentException("keyIndices must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        Set uniqueKeys = createSet(binaryKeyMap.size() * 2);
        for (BinaryKey key : binaryKeyMap.keySet())
        {
            uniqueKeys.add(key.getFirstKey());
            uniqueKeys.add(key.getSecondKey());
        }
        long n = uniqueKeys.size();
        BitMatrix2D bitMatrix = new BitMatrix2D(n, n);
        for (Map.Entry, E> entry : binaryKeyMap.entrySet())
        {
            if (predicate.test(entry.getValue()))
            {
                N source = entry.getKey().getFirstKey();
                N target = entry.getKey().getSecondKey();
                Long sourceIndex = keyIndices.evaluate(source);
                Long targetIndex = keyIndices.evaluate(target);

                if (sourceIndex != null && targetIndex != null)
                {
                    bitMatrix.set(sourceIndex, targetIndex, true);
                }
            }
        }
        return bitMatrix;
    }

    /**
     * Convert the specified array table to a bit matrix.  Values in the returned bit
     * matrix will be set to true where a non-null value exists in the specified array table.
     *
     * @param  array table row and column key type
     * @param  array table value type
     * @param table array table to convert, must not be null
     * @return the specified array table converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final ArrayTable table)
    {
        return toBitMatrix(table, new AcceptNonNull());
    }

    /**
     * Convert the specified array table to a bit matrix.  Values in the returned bit
     * matrix will be set to true where a value exists in the specified array table
     * accepted by the specified predicate.
     *
     * @param  array table row and column key type
     * @param  array table value type
     * @param table array table to convert, must not be null
     * @param predicate array table value predicate, must not be null
     * @return the specified array table converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final ArrayTable table,
                                                 final UnaryPredicate predicate)
    {
        if (table == null)
        {
            throw new IllegalArgumentException("table must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        int rows = table.rowKeyList().size();
        int columns = table.columnKeyList().size();
        BitMatrix2D bitMatrix = new BitMatrix2D(rows, columns);
        for (int row = 0; row < rows; row++)
        {
            for (int column = 0; column < columns; column++)
            {
                bitMatrix.set(row, column, predicate.test(table.at(row, column)));
            }
        }
        return bitMatrix;
    }

    /**
     * Convert the specified table to a bit matrix.  The row key value will be
     * used to look up the row index and the column key value the column index in the returned bit matrix.
     *
     * @param  table row and column key type
     * @param  table value type
     * @param table table to convert, must not be null
     * @param keys list of keys, must not be null
     * @param predicate table value predicate, must not be null
     * @return the specified table converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final Table table,
                                                 final List keys,
                                                 final UnaryPredicate predicate)
    {
        return toBitMatrix(table, new IndexOfKey(keys), predicate);
    }

    /**
     * Convert the specified table to a bit matrix.  The row key value will be
     * used to look up the row index and the column key value the column index in the returned bit matrix.
     *
     * @param  table row and column key type
     * @param  table value type
     * @param table table to convert, must not be null
     * @param keyIndices map of long indices by keys, must not be null
     * @param predicate table value predicate, must not be null
     * @return the specified table converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final Table table,
                                                 final Map keyIndices,
                                                 final UnaryPredicate predicate)
    {
        return toBitMatrix(table, new KeyIndices(keyIndices), predicate);
    }

    /**
     * Convert the specified table to a bit matrix.  The row key value will be
     * used to look up the row index and the column key value the column index in the returned bit matrix.
     *
     * @param  table row and column key type
     * @param  table value type
     * @param table table to convert, must not be null
     * @param keyIndices mapping of key indices by key, must not be null
     * @param predicate table value predicate, must not be null
     * @return the specified table converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final Table table,
                                                 final UnaryFunction keyIndices,
                                                 final UnaryPredicate predicate)
    {
        if (table == null)
        {
            throw new IllegalArgumentException("table must not be null");
        }
        if (keyIndices == null)
        {
            throw new IllegalArgumentException("keyIndices must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        // is combining row and column keys necessary?
        Set uniqueKeys = createSet(table.rowKeySet().size(), table.columnKeySet().size());
        uniqueKeys.addAll(table.rowKeySet());
        uniqueKeys.addAll(table.columnKeySet());
        long n = uniqueKeys.size();
        BitMatrix2D bitMatrix = new BitMatrix2D(n, n);
        for (Table.Cell cell : table.cellSet())
        {
            if (predicate.test(cell.getValue()))
            {
                N source = cell.getRowKey();
                N target = cell.getColumnKey();
                Long sourceIndex = keyIndices.evaluate(source);
                Long targetIndex = keyIndices.evaluate(target);

                if (sourceIndex != null && targetIndex != null)
                {
                    bitMatrix.set(sourceIndex, targetIndex, true);
                }
            }
        }
        return bitMatrix;
    }

    /**
     * Convert the specified graph to a bit matrix.
     *
     * @param  graph node value type
     * @param  graph edge value type
     * @param graph graph to convert, must not be null
     * @param nodes list of nodes, must not be null
     * @param predicate edge value predicate, must not be null
     * @return the specified graph converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final Graph graph,
                                                 final List> nodes,
                                                 final UnaryPredicate predicate)
    {
        return toBitMatrix(graph, new IndexOfKey>(nodes), predicate);
    }

    /**
     * Convert the specified graph to a bit matrix.
     *
     * @param  graph node value type
     * @param  graph edge value type
     * @param graph graph to convert, must not be null
     * @param nodeIndices map of long indices by nodes, must not be null
     * @param predicate edge value predicate, must not be null
     * @return the specified graph converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final Graph graph,
                                                 final Map, Long> nodeIndices,
                                                 final UnaryPredicate predicate)
    {
        return toBitMatrix(graph, new KeyIndices>(nodeIndices), predicate);
    }

    /**
     * Convert the specified graph to a bit matrix.
     *
     * @param  graph node value type
     * @param  graph edge value type
     * @param graph graph to convert, must not be null
     * @param nodeIndices mapping of node indices by node, must not be null
     * @param predicate edge value predicate, must not be null
     * @return the specified graph converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final Graph graph,
                                                 final UnaryFunction, Long> nodeIndices,
                                                 final UnaryPredicate predicate)
    {
        if (graph == null)
        {
            throw new IllegalArgumentException("graph must not be null");
        }
        if (nodeIndices == null)
        {
            throw new IllegalArgumentException("nodeIndices must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        long n = graph.nodeCount();
        BitMatrix2D bitMatrix = new BitMatrix2D(n, n);
        for (Edge edge : graph.edges())
        {
            if (predicate.test(edge.getValue()))
            {
                Long rowIndex = nodeIndices.evaluate(edge.source());
                Long columnIndex = nodeIndices.evaluate(edge.target());

                if (rowIndex != null && columnIndex != null)
                {
                    bitMatrix.set(rowIndex, columnIndex, true);
                }
            }
        }
        return bitMatrix;
    }

    /**
     * Convert the specified matrix to a bit matrix.
     *
     * @param  matrix value type
     * @param matrix matrix to convert, must not be null
     * @param predicate matrix value predicate, must not be null
     * @return the specified matrix converted to a bit matrix
     */
    public static  BitMatrix2D toBitMatrix(final Matrix2D matrix, final UnaryPredicate predicate)
    {
        if (matrix == null)
        {
            throw new IllegalArgumentException("matrix must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        final BitMatrix2D bitMatrix = new BitMatrix2D(matrix.rows(), matrix.columns());
        matrix.forEach(new TernaryProcedure()
                       {
                           @Override
                           public void run(final Long row, final Long column, final E value)
                           {
                               if (predicate.test(value))
                               {
                                   bitMatrix.set(row, column, true);
                               }
                           }
                       });
        return bitMatrix;
    }


    // --> table


    /**
     * Convert the specified binary key map to an immutable table.
     *
     * @param  binary key map key type and table row and column key type
     * @param  binary key map value type and table value type
     * @param binaryKeyMap binary key map to convert, must be not null
     * @return the specified binary key map converted to an immutable table
     */
    public static  ImmutableTable toImmutableTable(final BinaryKeyMap binaryKeyMap)
    {
        if (binaryKeyMap == null)
        {
            throw new IllegalArgumentException("binaryKeyMap must not be null");
        }
        ImmutableTable.Builder builder = ImmutableTable.builder();
        for (Map.Entry, E> entry : binaryKeyMap.entrySet())
        {
            N row = entry.getKey().getFirstKey();
            N column = entry.getKey().getSecondKey();
            E value = entry.getValue();
            builder.put(row, column, value);
        }
        return builder.build();
    }

    /**
     * Convert the specified graph to an immutable table.  Note that only nodes with degree
     * of at least one will be present in the returned immutable table.
     *
     * @param  graph node type and immutable table row and column key type
     * @param  graph edge type and immutable table value type
     * @param graph graph to convert, must not be null
     * @return the specified graph converted to an immutable table
     */
    public static  ImmutableTable toImmutableTable(final Graph graph)
    {
        if (graph == null)
        {
            throw new IllegalArgumentException("graph must not be null");
        }
        final ImmutableTable.Builder builder = ImmutableTable.builder();
        graph.forEachEdge(new UnaryProcedure>()
                          {
                              @Override
                              public void run(final Edge edge)
                              {
                                  // todo:  provide "merge strategy" for multiple edges
                                  builder.put(edge.source().getValue(), edge.target().getValue(), edge.getValue());
                              }
                          });
        return builder.build();
    }

    /**
     * Convert the specified binary key map to a hash based table.
     *
     * @param  binary key map key type and table row and column key type
     * @param  binary key map value type and table value type
     * @param binaryKeyMap binary key map to convert, must be not null
     * @return the specified binary key map converted to a hash based table
     */
    public static  HashBasedTable toHashBasedTable(final BinaryKeyMap binaryKeyMap)
    {
        if (binaryKeyMap == null)
        {
            throw new IllegalArgumentException("binaryKeyMap must not be null");
        }
        HashBasedTable table = HashBasedTable.create();
        for (Map.Entry, E> entry : binaryKeyMap.entrySet())
        {
            N row = entry.getKey().getFirstKey();
            N column = entry.getKey().getSecondKey();
            E value = entry.getValue();
            table.put(row, column, value);
        }
        return table;
    }

    /**
     * Convert the specified graph to a hash based table.  Note that only nodes with degree
     * of at least one will be present in the returned hash based table.
     *
     * @param  graph node type and sparse table row and column key type
     * @param  graph edge type and sparse table value type
     * @param graph graph to convert, must not be null
     * @return the specified graph converted to a hash based table
     */
    public static  HashBasedTable toHashBasedTable(final Graph graph)
    {
        if (graph == null)
        {
            throw new IllegalArgumentException("graph must not be null");
        }
        final HashBasedTable table = HashBasedTable.create();
        graph.forEachEdge(new UnaryProcedure>()
                          {
                              @Override
                              public void run(final Edge edge)
                              {
                                  // todo:  provide "merge strategy" for multiple edges
                                  table.put(edge.source().getValue(), edge.target().getValue(), edge.getValue());
                              }
                          });
        return table;
    }


    // --> graph


    /**
     * Convert the specified binary key map to a graph.  The first key value will be the
     * source node and the second key value the target node in edges in the returned graph.
     *
     * @param  binary key map key type and graph node type
     * @param  binary key map value type and graph edge type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @return the specified binary key map converted to a graph
     */
    public static  Graph toGraph(final BinaryKeyMap binaryKeyMap)
    {
        return toGraph(binaryKeyMap, new AcceptAll());
    }

    /**
     * Convert the specified binary key map to a graph, adding edges for values accepted
     * by the specified predicate.  The first key value will be the source node and the second
     * key value the target node in edges in the returned graph.
     *
     * @param  binary key map key type and graph node type
     * @param  binary key map value type and graph edge type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @param predicate binary key map value predicate, must not be null
     * @return the specified binary key map converted to a graph
     */
    public static  Graph toGraph(final BinaryKeyMap binaryKeyMap,
                                             final UnaryPredicate predicate)
    {
        if (binaryKeyMap == null)
        {
            throw new IllegalArgumentException("binaryKeyMap must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        int n = Math.max(DEFAULT_SIZE, binaryKeyMap.keySet().size());
        int e = Math.max(DEFAULT_SIZE, binaryKeyMap.size());
        Map> nodes = createMap(n);
        Graph graph = createGraph(n, e);
        for (Map.Entry, E> entry : binaryKeyMap.entrySet())
        {
            N sourceValue = entry.getKey().getFirstKey();
            N targetValue = entry.getKey().getSecondKey();
            E value = entry.getValue();
            if (!nodes.containsKey(sourceValue))
            {
                nodes.put(sourceValue, graph.createNode(sourceValue));
            }
            if (!nodes.containsKey(targetValue))
            {
                nodes.put(targetValue, graph.createNode(targetValue));
            }
            if (value != null && predicate.test(value))
            {
                graph.createEdge(nodes.get(sourceValue), nodes.get(targetValue), value);
            }
        }
        return graph;
    }

    /**
     * Convert the specified table to a graph.  The row key value will be the source node and
     * the column key value the target node in edges in the returned graph.
     *
     * @param  table row and column key type and graph node type
     * @param  table value type and graph edge type
     * @param table table to convert, must not be null
     * @return the specified table converted to a graph
     */
    public static  Graph toGraph(final Table table)
    {
        return toGraph(table, new AcceptAll());
    }

    /**
     * Convert the specified table to a graph, adding edges for values accepted
     * by the specified predicate.  The row key value will be the source node and the column
     * key value the target node in edges in the returned graph.
     *
     * @param  table row and column key type and graph node type
     * @param  table value type and graph edge type
     * @param table table to convert, must not be null
     * @param predicate table value predicate, must not be null
     * @return the specified table converted to a graph
     */
    public static  Graph toGraph(final Table table, final UnaryPredicate predicate)
    {
        if (table == null)
        {
            throw new IllegalArgumentException("table must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        int n = Math.max(DEFAULT_SIZE, Math.max(table.columnKeySet().size(), table.rowKeySet().size()));
        int e = Math.max(DEFAULT_SIZE, table.size());
        Map> nodes = createMap(n);
        Graph graph = createGraph(n, e);
        for (Table.Cell cell : table.cellSet())
        {
            N sourceValue = cell.getRowKey();
            N targetValue = cell.getColumnKey();
            E value = cell.getValue();
            if (!nodes.containsKey(sourceValue))
            {
                nodes.put(sourceValue, graph.createNode(sourceValue));
            }
            if (!nodes.containsKey(targetValue))
            {
                nodes.put(targetValue, graph.createNode(targetValue));
            }
            if (value != null && predicate.test(value))
            {
                graph.createEdge(nodes.get(sourceValue), nodes.get(targetValue), value);
            }
        }
        return graph;
    }

    /**
     * Convert the specified matrix to a graph with long indices as node values.
     *
     * @param  matrix type and graph edge type
     * @param matrix matrix to convert, must not be null
     * @return the specified matrix converted to a graph with long indices as node values
     */
    public static  Graph toGraph(final Matrix2D matrix)
    {
        return toGraph(matrix, IDENTITY);
    }

    /**
     * Convert the specified matrix to a graph with long indices as node values, adding edges
     * for values accepted by the specified predicate.
     *
     * @param  matrix type and graph edge type
     * @param matrix matrix to convert, must not be null
     * @param predicate matrix value predicate, must not be null
     * @return the specified matrix converted to a graph with long indices as node values
     */
    public static  Graph toGraph(final Matrix2D matrix, final UnaryPredicate predicate)
    {
        return toGraph(matrix, IDENTITY, predicate);
    }

    /**
     * Convert the specified matrix to a graph.
     *
     * @param  graph node type
     * @param  matrix type and graph edge type
     * @param matrix matrix to convert, must not be null
     * @param nodeValues mapping of node values by long indices, must not be null
     * @return the specified matrix converted to a graph
     */
    public static  Graph toGraph(final Matrix2D matrix, final UnaryFunction nodeValues)
    {
        return toGraph(matrix, nodeValues, new AcceptAll());
    }

    /**
     * Convert the specified matrix to a graph, adding edges for values accepted by the specified predicate.
     *
     * @param  graph node type
     * @param  matrix type and graph edge type
     * @param matrix matrix to convert, must not be null
     * @param nodeValues mapping of node values by long indices, must not be null
     * @param predicate matrix value predicate, must not be null
     * @return the specified matrix converted to a graph
     */
    public static  Graph toGraph(final Matrix2D matrix,
                                             final UnaryFunction nodeValues,
                                             final UnaryPredicate predicate)
    {
        if (matrix == null)
        {
            throw new IllegalArgumentException("matrix must not be null");
        }
        if (matrix.rows() != matrix.columns())
        {
            throw new IllegalArgumentException("matrix must be balanced, rows="
                                               + matrix.rows() + ", columns=" + matrix.columns());
        }
        if (matrix.rows() > Integer.MAX_VALUE)
        {
            throw new IllegalArgumentException("graph size in number of nodes is limited to " + Integer.MAX_VALUE);
        }
        if (matrix.cardinality() > Integer.MAX_VALUE)
        {
            throw new IllegalArgumentException("graph size in number of edges is limited to " + Integer.MAX_VALUE);
        }
        if (nodeValues == null)
        {
            throw new IllegalArgumentException("nodeValues must not be null");
        }
        if (predicate == null)
        {
            throw new IllegalArgumentException("predicate must not be null");
        }
        int n = Math.max(DEFAULT_SIZE, (int) matrix.rows());
        int e = Math.max(DEFAULT_SIZE, (int) matrix.cardinality());
        // todo:  compare to createLongNonBlockingMap
        final Map> nodes = createMap(n);
        final Graph graph = createGraph(n, e);
        matrix.forEach(new TernaryProcedure()
                       {
                           @Override
                           public void run(final Long row, final Long column, final E value)
                           {
                               if (!nodes.containsKey(row))
                               {
                                   nodes.put(row, graph.createNode(nodeValues.evaluate(row)));
                               }
                               if (!nodes.containsKey(column))
                               {
                                   nodes.put(column, graph.createNode(nodeValues.evaluate(column)));
                               }
                               if (value != null && predicate.test(value))
                               {
                                   graph.createEdge(nodes.get(row), nodes.get(column), value);
                               }
                           }
                       });
        return graph;
    }

    /**
     * Convert the specified bit matrix to a graph with long indices as node values.
     *
     * @param  graph edge type
     * @param bitMatrix bit matrix to convert, must not be null and must be balanced
     * @param edgeValues mapping of edge values by node values, must not be null
     * @return the specified bit matrix converted to a graph with long indices as node values
     */
    public static  Graph toGraph(final BitMatrix2D bitMatrix,
                                             final BinaryFunction edgeValues)
    {
        return toGraph(bitMatrix, IDENTITY, edgeValues);
    }

    /**
     * Convert the specified bit matrix to a graph.
     *
     * @param  graph node type
     * @param  graph edge type
     * @param bitMatrix bit matrix to convert, must not be null and must be balanced
     * @param nodeValues mapping of node values by long indices, must not be null
     * @param edgeValues mapping of edge values by node values, must not be null
     * @return the specified bit matrix converted to a graph
     */
    public static  Graph toGraph(final BitMatrix2D bitMatrix,
                                             final UnaryFunction nodeValues,
                                             final BinaryFunction edgeValues)
    {
        if (bitMatrix == null)
        {
            throw new IllegalArgumentException("bitMatrix must not be null");
        }
        if (bitMatrix.rows() != bitMatrix.columns())
        {
            throw new IllegalArgumentException("bitMatrix must be balanced, rows="
                                               + bitMatrix.rows() + ", columns=" + bitMatrix.columns());
        }
        if (bitMatrix.rows() > Integer.MAX_VALUE)
        {
            throw new IllegalArgumentException("graph size in number of nodes is limited to " + Integer.MAX_VALUE);
        }
        if (bitMatrix.cardinality() > Integer.MAX_VALUE)
        {
            throw new IllegalArgumentException("graph size in number of edges is limited to " + Integer.MAX_VALUE);
        }
        if (nodeValues == null)
        {
            throw new IllegalArgumentException("nodeValues must not be null");
        }
        if (edgeValues == null)
        {
            throw new IllegalArgumentException("edgeValues must not be null");
        }
        int n = Math.max(DEFAULT_SIZE, (int) bitMatrix.rows());
        int e = Math.max(DEFAULT_SIZE, (int) bitMatrix.cardinality());
        // todo:  compare to createLongNonBlockingMap
        final Map> nodes = createMap(n);
        final Graph graph = createGraph(n, e);
        bitMatrix.forEach(true, new BinaryProcedure()
                          {
                              @Override
                              public void run(final Long row, final Long column)
                              {
                                  N rowValue = nodeValues.evaluate(row);
                                  N columnValue = nodeValues.evaluate(column);
                                  E edgeValue = edgeValues.evaluate(rowValue, columnValue);

                                  if (!nodes.containsKey(row))
                                  {
                                      nodes.put(row, graph.createNode(rowValue));
                                  }
                                  if (!nodes.containsKey(column))
                                  {
                                      nodes.put(column, graph.createNode(columnValue));
                                  }
                                  if (edgeValue != null)
                                  {
                                      graph.createEdge(nodes.get(row), nodes.get(column), edgeValue);
                                  }
                              }
                          });
        return graph;
    }


    // --> sparse matrix


    /**
     * Convert the specified binary key map to a sparse matrix.
     *
     * @param  binary key map key type
     * @param  binary key map value type and sparse matrix type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @param keys list of keys, must not be null
     * @return the specified binary key map converted to a sparse matrix
     */
    public static  Matrix2D toSparseMatrix2D(final BinaryKeyMap binaryKeyMap, final List keys)
    {
        return toSparseMatrix2D(binaryKeyMap, new IndexOfKey(keys));
    }

    /**
     * Convert the specified binary key map to a sparse matrix.
     *
     * @param  binary key map key type
     * @param  binary key map value type and sparse matrix type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @param keyIndices map of long indices by keys, must not be null
     * @return the specified binary key map converted to a sparse matrix
     */
    public static  Matrix2D toSparseMatrix2D(final BinaryKeyMap binaryKeyMap,
                                                      final Map keyIndices)
    {
        return toSparseMatrix2D(binaryKeyMap, new KeyIndices(keyIndices));
    }

    /**
     * Convert the specified binary key map to a sparse matrix.
     *
     * @param  binary key map key type
     * @param  binary key map value type and sparse matrix type
     * @param binaryKeyMap binary key map to convert, must not be null
     * @param keyIndices mapping of long indices by keys, must not be null
     * @return the specified binary key map converted to a sparse matrix
     */
    public static  Matrix2D toSparseMatrix2D(final BinaryKeyMap binaryKeyMap,
                                                      final UnaryFunction keyIndices)
    {
        if (binaryKeyMap == null)
        {
            throw new IllegalArgumentException("binaryKeyMap must not be null");
        }
        if (keyIndices == null)
        {
            throw new IllegalArgumentException("keyIndices must not be null");
        }
        Set uniqueKeys = createSet(binaryKeyMap.size() * 2);
        for (BinaryKey key : binaryKeyMap.keySet())
        {
            uniqueKeys.add(key.getFirstKey());
            uniqueKeys.add(key.getSecondKey());
        }
        long n = uniqueKeys.size();
        int e = binaryKeyMap.size();
        Matrix2D matrix = createSparseMatrix2D(n, n, e, DEFAULT_FACTOR);
        for (Map.Entry, E> entry : binaryKeyMap.entrySet())
        {
            N source = entry.getKey().getFirstKey();
            N target = entry.getKey().getSecondKey();
            Long sourceIndex = keyIndices.evaluate(source);
            Long targetIndex = keyIndices.evaluate(target);

            if (sourceIndex != null && targetIndex != null)
            {
                // todo:  provide "merge strategy" for multiple "edges"
                matrix.set(sourceIndex, targetIndex, entry.getValue());
            }
        }
        return matrix;
    }

    /**
     * Convert the specified graph to a sparse matrix.
     *
     * @param  graph node type
     * @param  graph edge type and sparse matrix type
     * @param graph graph to convert, must not be null
     * @param nodes list of nodes, must not be null
     * @return the specified graph converted to a sparse matrix
     */
    public static  Matrix2D toSparseMatrix2D(final Graph graph, final List> nodes)
    {
        return toSparseMatrix2D(graph, new IndexOfNode(nodes));
    }

    /**
     * Convert the specified graph to a sparse matrix.
     *
     * @param  graph node type
     * @param  graph edge type and sparse matrix type
     * @param graph graph to convert, must not be null
     * @param nodeIndices map of long indicies by nodes, must not be null
     * @return the specified graph converted to a sparse matrix
     */
    public static  Matrix2D toSparseMatrix2D(final Graph graph, final Map, Long> nodeIndices)
    {
        return toSparseMatrix2D(graph, new NodeIndices(nodeIndices));
    }

    /**
     * Convert the specified graph to a sparse matrix.
     *
     * @param  graph node type
     * @param  graph edge type and sparse matrix type
     * @param graph graph to convert, must not be null
     * @param nodeIndices mapping of long indices by nodes, must not be null
     * @return the specified graph converted to a sparse matrix
     */
    public static  Matrix2D toSparseMatrix2D(final Graph graph,
                                                      final UnaryFunction, Long> nodeIndices)
    {
        if (graph == null)
        {
            throw new IllegalArgumentException("graph must not be null");
        }
        if (nodeIndices == null)
        {
            throw new IllegalArgumentException("nodeIndices must not be null");
        }
        long n = graph.nodeCount();
        int e = graph.edgeCount();
        final Matrix2D matrix = createSparseMatrix2D(n, n, e, DEFAULT_FACTOR);
        graph.forEachEdge(new UnaryProcedure>()
                          {
                              @Override
                              public void run(final Edge edge)
                              {
                                  Long sourceIndex = nodeIndices.evaluate(edge.source());
                                  Long targetIndex = nodeIndices.evaluate(edge.target());

                                  if (sourceIndex != null && targetIndex != null)
                                  {
                                      // todo:  provide "merge strategy" for multiple edges
                                      matrix.set(sourceIndex, targetIndex, edge.getValue());
                                  }
                              }
                          });
        return matrix;
    }

    /**
     * Convert the specified bit matrix to a sparse matrix.
     *
     * @param  sparse matrix type
     * @param bitMatrix bit matrix to convert, must not be null
     * @param values mapping of values by long indices, must not be null
     * @return the specified bit matrix converted to a sparse matrix
     */
    public static  Matrix2D toSparseMatrix2D(final BitMatrix2D bitMatrix,
                                                   final BinaryFunction values)
    {
        if (bitMatrix == null)
        {
            throw new IllegalArgumentException("bitMatrix must not be null");
        }
        if (bitMatrix.cardinality() > Integer.MAX_VALUE)
        {
            throw new IllegalArgumentException("matrix cardinality is limited to " + Integer.MAX_VALUE);
        }
        if (values == null)
        {
            throw new IllegalArgumentException("values must not be null");
        }
        int e = (int) bitMatrix.cardinality();
        final Matrix2D matrix = createSparseMatrix2D(bitMatrix.rows(), bitMatrix.columns(), e, DEFAULT_FACTOR);
        bitMatrix.forEach(true, new BinaryProcedure()
                          {
                              @Override
                              public void run(final Long row, final Long column)
                              {
                                  E value = values.evaluate(row, column);
                                  if (value != null)
                                  {
                                      matrix.setQuick(row, column, value);
                                  }
                              }
                          });
        return matrix;
    }


    // helper classes


    /** Identity mapping function. */
    static final UnaryFunction IDENTITY = new UnaryFunction()
    {
        @Override
        public Long evaluate(final Long value)
        {
            return value;
        }
    };

    /**
     * Accept all values predicate.
     *
     * @param  value type
     */
    static class AcceptAll implements UnaryPredicate
    {
        @Override
        public boolean test(final E value)
        {
            return true;
        }
    }

    /**
     * Accept all non-null values predicate.
     *
     * @param  value type
     */
    static class AcceptNonNull implements UnaryPredicate
    {
        @Override
        public boolean test(final E value)
        {
            return value != null;
        }
    }

    /**
     * Index of key mapping function.
     *
     * @param  key type
     */
    private static class IndexOfKey implements UnaryFunction
    {
        /** List of keys. */
        private final List keys;


        /**
         * Create a new index of key mapping function with the specified list of keys.
         *
         * @param keys list of keys, must not be null
         */
        IndexOfKey(final List keys)
        {
            if (keys == null)
            {
                throw new IllegalArgumentException("keys must not be null");
            }
            this.keys = keys;
        }


        @Override
        public Long evaluate(final N key)
        {
            int index = keys.indexOf(key);
            return (index < 0) ? null : Long.valueOf(index);
        }
    }

    /**
     * Index of node mapping function.
     *
     * @param  graph node type
     * @param  graph edge type
     */
    private static class IndexOfNode implements UnaryFunction, Long>
    {
        /** List of nodes. */
        private final List> nodes;


        /**
         * Create a new index of node mapping function with the specified list of nodes.
         *
         * @param nodes list of nodes, must not be null
         */
        IndexOfNode(final List> nodes)
        {
            if (nodes == null)
            {
                throw new IllegalArgumentException("nodes must not be null");
            }
            this.nodes = nodes;
        }


        @Override
        public Long evaluate(final Node node)
        {
            int index = nodes.indexOf(node);
            return (index < 0) ? null : Long.valueOf(index);
        }
    }

    /**
     * Key indices mapping function.
     *
     * @param  key type
     */
    private static class KeyIndices implements UnaryFunction
    {
        /** Map of keys to long indices. */
        private final Map keyIndices;


        /**
         * Create a new key indicies mapping function with the specified map of keys to long indices.
         *
         * @param keyIndices map of keys to long indices, must not be null
         */
        KeyIndices(final Map keyIndices)
        {
            if (keyIndices == null)
            {
                throw new IllegalArgumentException("keyIndices must not be null");
            }
            this.keyIndices = keyIndices;
        }


        @Override
        public Long evaluate(final N key)
        {
            return keyIndices.get(key);
        }
    }

    /**
     * Node indices mapping function.
     *
     * @param  graph node type
     * @param  graph edge type
     */
    private static class NodeIndices implements UnaryFunction, Long>
    {
        /** Map of nodes to long indices. */
        private final Map, Long> nodeIndices;


        /**
         * Create a new node indices mapping function with the specified map of nodes to long indices.
         *
         * @param nodeIndices map of nodes to long indices, must not be null
         */
        NodeIndices(final Map, Long> nodeIndices)
        {
            if (nodeIndices == null)
            {
                throw new IllegalArgumentException("nodeIndices must not be null");
            }
            this.nodeIndices = nodeIndices;
        }


        @Override
        public Long evaluate(final Node node)
        {
            return nodeIndices.get(node);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy