Program Listing for File GraphBLAS.hpp

Return to documentation for file (include/networkit/algebraic/GraphBLAS.hpp)

/*
 * GraphBLAS.hpp
 *
 *  Created on: May 31, 2016
 *      Author: Michael Wegner (michael.wegner@student.kit.edu)
 */

#ifndef NETWORKIT_ALGEBRAIC_GRAPH_BLAS_HPP_
#define NETWORKIT_ALGEBRAIC_GRAPH_BLAS_HPP_

#include <limits>
#include <networkit/algebraic/AlgebraicGlobals.hpp>
#include <networkit/algebraic/Semirings.hpp>
#include <networkit/algebraic/SparseAccumulator.hpp>
#include <networkit/algebraic/Vector.hpp>

namespace GraphBLAS {

// ****************************************************
//                      Operations
// ****************************************************

template <class SemiRing, class Matrix, typename L>
Matrix eWiseBinOp(const Matrix &A, const Matrix &B, L binOp) {
    assert(A.numberOfRows() == B.numberOfRows() && A.numberOfColumns() == B.numberOfColumns());
    assert(A.getZero() == B.getZero() && A.getZero() == SemiRing::zero());

    std::vector<int64_t> columnPointer(A.numberOfColumns(), -1);
    std::vector<double> Arow(A.numberOfColumns(), SemiRing::zero());
    std::vector<double> Brow(A.numberOfColumns(), SemiRing::zero());

    std::vector<NetworKit::Triplet> triplets;

    for (NetworKit::index i = 0; i < A.numberOfRows(); ++i) {
        NetworKit::index listHead = 0;
        NetworKit::count nnz = 0;

        // search for nonZeros in matrix A
        A.forNonZeroElementsInRow(i, [&](NetworKit::index j, double value) {
            Arow[j] = value;

            columnPointer[j] = listHead;
            listHead = j;
            nnz++;
        });

        // search for nonZeros in matrix B
        B.forNonZeroElementsInRow(i, [&](NetworKit::index j, double value) {
            Brow[j] = value;

            if (columnPointer[j] == -1) { // matrix A does not have a nonZero entry in column j
                columnPointer[j] = listHead;
                listHead = j;
                nnz++;
            }
        });

        // apply operator on the found nonZeros in A and B
        for (NetworKit::count k = 0; k < nnz; ++k) {
            double value = binOp(Arow[listHead], Brow[listHead]);
            if (value != SemiRing::zero()) {
                triplets.push_back({i, listHead, value});
            }

            NetworKit::index temp = listHead;
            listHead = columnPointer[listHead];

            // reset for next row
            columnPointer[temp] = -1;
            Arow[temp] = SemiRing::zero();
            Brow[temp] = SemiRing::zero();
        }

        nnz = 0;
    }

    return Matrix(A.numberOfRows(), A.numberOfColumns(), triplets, A.getZero());
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
Matrix MxM(const Matrix &A, const Matrix &B) {
    assert(A.numberOfColumns() == B.numberOfRows());
    assert(A.getZero() == SemiRing::zero() && B.getZero() == SemiRing::zero());

    std::vector<NetworKit::Triplet> triplets;
    NetworKit::SparseAccumulator spa(B.numberOfRows());
    for (NetworKit::index i = 0; i < A.numberOfRows(); ++i) {
        A.forNonZeroElementsInRow(i, [&](NetworKit::index k, double w1) {
            B.forNonZeroElementsInRow(k, [&](NetworKit::index j, double w2) {
                spa.scatter(SemiRing::mult(w1, w2), j, *SemiRing::add);
            });
        });

        spa.gather([&](NetworKit::index i, NetworKit::index j, double value) {
            triplets.push_back({i, j, value});
        });

        spa.increaseRow();
    }

    return Matrix(A.numberOfRows(), B.numberOfColumns(), triplets, A.getZero());
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
void MxM(const Matrix &A, const Matrix &B, Matrix &C) {
    assert(A.numberOfColumns() == B.numberOfRows() && A.numberOfRows() == C.numberOfRows()
           && B.numberOfColumns() == C.numberOfColumns());
    assert(A.getZero() == SemiRing::zero() && B.getZero() == SemiRing::zero()
           && C.getZero() == SemiRing::zero());

    std::vector<NetworKit::Triplet> triplets;
    NetworKit::SparseAccumulator spa(B.numberOfRows());
    for (NetworKit::index i = 0; i < A.numberOfRows(); ++i) {
        A.forNonZeroElementsInRow(i, [&](NetworKit::index k, double w1) {
            B.forNonZeroElementsInRow(k, [&](NetworKit::index j, double w2) {
                spa.scatter(SemiRing::mult(w1, w2), j, *SemiRing::add);
            });
        });

        spa.gather([&](NetworKit::index i, NetworKit::index j, double value) {
            triplets.push_back({i, j, value});
        });

        spa.increaseRow();
    }

    Matrix temp(A.numberOfRows(), B.numberOfRows(), triplets, A.getZero());
    C = eWiseBinOp<SemiRing, Matrix>(C, temp, *SemiRing::add);
}

template <class SemiRing = ArithmeticSemiring, typename F, class Matrix>
void MxM(const Matrix &A, const Matrix &B, Matrix &C, F accum) {
    assert(A.numberOfColumns() == B.numberOfRows() && A.numberOfRows() == C.numberOfRows()
           && B.numberOfColumns() == C.numberOfColumns());
    assert(A.getZero() == SemiRing::zero() && B.getZero() == SemiRing::zero()
           && C.getZero() == SemiRing::zero());

    std::vector<NetworKit::Triplet> triplets;
    NetworKit::SparseAccumulator spa(B.numberOfRows());
    for (NetworKit::index i = 0; i < A.numberOfRows(); ++i) {
        A.forNonZeroElementsInRow(i, [&](NetworKit::index k, double w1) {
            B.forNonZeroElementsInRow(k, [&](NetworKit::index j, double w2) {
                spa.scatter(SemiRing::mult(w1, w2), j, *SemiRing::add);
            });
        });

        spa.gather([&](NetworKit::index i, NetworKit::index j, double value) {
            triplets.push_back({i, j, value});
        });

        spa.increaseRow();
    }

    Matrix temp(A.numberOfRows(), B.numberOfRows(), triplets, A.getZero());
    C = eWiseBinOp<SemiRing, Matrix>(C, temp, accum);
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
NetworKit::Vector MxV(const Matrix &A, const NetworKit::Vector &v) {
    assert(!v.isTransposed());
    assert(A.numberOfColumns() == v.getDimension());
    assert(A.getZero() == SemiRing::zero());
    NetworKit::Vector result(A.numberOfRows(), A.getZero());

    A.parallelForNonZeroElementsInRowOrder(
        [&](NetworKit::index i, NetworKit::index j, double value) {
            result[i] = SemiRing::add(result[i], SemiRing::mult(value, v[j]));
        });

    return result;
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
void MxV(const Matrix &A, const NetworKit::Vector &v, NetworKit::Vector &c) {
    assert(!v.isTransposed());
    assert(A.numberOfColumns() == v.getDimension());
    assert(A.getZero() == SemiRing::zero());

    A.parallelForNonZeroElementsInRowOrder(
        [&](NetworKit::index i, NetworKit::index j, double value) {
            c[i] = SemiRing::add(c[i], SemiRing::mult(value, v[j]));
        });
}

template <class SemiRing = ArithmeticSemiring, typename F, class Matrix>
void MxV(const Matrix &A, const NetworKit::Vector &v, NetworKit::Vector &c, F accum) {
    assert(!v.isTransposed());
    assert(A.numberOfColumns() == v.getDimension());
    assert(A.getZero() == SemiRing::zero());

    A.parallelForNonZeroElementsInRowOrder(
        [&](NetworKit::index i, NetworKit::index j, double value) {
            c[i] = accum(c[i], SemiRing::mult(value, v[j]));
        });
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
Matrix eWiseAdd(const Matrix &A, const Matrix &B) {
    return eWiseBinOp<SemiRing, Matrix>(
        A, B, [](const double a, const double b) { return SemiRing::add(a, b); });
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
Matrix eWiseMult(const Matrix &A, const Matrix &B) {
    return eWiseBinOp<SemiRing, Matrix>(
        A, B, [](const double a, const double b) { return SemiRing::mult(a, b); });
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
NetworKit::Vector rowReduce(const Matrix &matrix) {
    assert(matrix.getZero() == SemiRing::zero());
    NetworKit::Vector rowReduction(matrix.numberOfRows(), 0.0);

#pragma omp parallel for
    for (NetworKit::omp_index i = 0; i < static_cast<NetworKit::omp_index>(matrix.numberOfRows());
         ++i) {
        matrix.forNonZeroElementsInRow(i, [&](NetworKit::index, double value) {
            rowReduction[i] = SemiRing::add(rowReduction[i], value);
        });
    }

    return rowReduction;
}

template <class SemiRing = ArithmeticSemiring, class Matrix>
NetworKit::Vector columnReduce(const Matrix &matrix) {
    assert(matrix.getZero() == SemiRing::zero());
    NetworKit::Vector columnReduction(matrix.numberOfColumns(), 0.0);

    matrix.forNonZeroElementsInRowOrder([&](NetworKit::index, NetworKit::index j, double value) {
        columnReduction[j] = SemiRing::add(columnReduction[j], value);
    });

    return columnReduction;
}

} // namespace GraphBLAS

#endif // NETWORKIT_ALGEBRAIC_GRAPH_BLAS_HPP_