Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defining pow functions for adjacency and transition matrices #463

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
196 changes: 196 additions & 0 deletions include/CXXGraph/Graph/Algorithm/Pow_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/***********************************************************/
/*** ______ ____ ______ _ ***/
/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/
/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/
/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/
/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/
/*** |_| ***/
/***********************************************************/
/*** Header-Only C++ Library for Graph ***/
/*** Representation and Algorithms ***/
/***********************************************************/
/*** Author: ZigRazor ***/
/*** E-Mail: [email protected] ***/
/***********************************************************/
/*** Collaboration: ----------- ***/
/***********************************************************/
/*** License: AGPL v3.0 ***/
/***********************************************************/

#ifndef __CXXGRAPH_POW_IMPL_H__
#define __CXXGRAPH_POW_IMPL_H__

#pragma once

#include "CXXGraph/Graph/Graph_decl.h"

template <typename T>
std::vector<std::vector<T>> matMult(std::vector<std::vector<T>> &a,
std::vector<std::vector<T>> &b) {
static_assert(std::is_arithmetic<T>::value,
"Type T must be either unsigned long long or double");

int n = static_cast<int>(a.size()); // N x N matrix
std::vector<std::vector<T>> res(n, std::vector<T>(n, 0));

// O(n^3) matrix multiplication
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
res[i][j] += a[i][k] * b[k][j];
}
}
}

return res;
}

template <typename T>
std::vector<std::vector<T>> exponentiation(std::vector<std::vector<T>> &mat,
unsigned int k) {
// typechecking
static_assert(std::is_arithmetic<T>::value,
"Type T must be either unsigned long long or double");

int n = static_cast<int>(mat.size());
std::vector<std::vector<T>> res(n, std::vector<T>(n, 0));

// build identity matrix
for (int i = 0; i < n; i++) res[i][i] = 1;

// fast exponentation!
while (k) {
if (k & 1) res = matMult(res, mat);
mat = matMult(mat, mat);
k >>= 1;
}

return res;
}

namespace CXXGraph {

/**
* @brief This function raises the adjacency matrix to some k.
* Best used for counting number of k-length walks from i to j.
* @param k value by which to raise matrix
* @return (success, errorMessage, matrix): where matrix is equivalent to A^k
*/
template <typename T>
const PowAdjResult matrixPow(const shared<AdjacencyMatrix<T>> &adj, unsigned int k) {
PowAdjResult result;
result.success = false;
result.errorMessage = "";
std::unordered_map<std::pair<std::string, std::string>, unsigned long long,
CXXGraph::pair_hash>
powAdj;

// convert back and forth between user ids and index in temporary adj matrix
std::unordered_map<std::string, int> userIdToIdx;
std::unordered_map<int, std::string> idxToUserId;

int n = 0;
for (const auto &[node, list] : *adj) {
userIdToIdx[node->getUserId()] = n;
idxToUserId[n] = node->getUserId();
n++;
}

// adj int will store the temporary (integer) adjacency matrix
std::vector<std::vector<unsigned long long>> tempIntAdj(
n, std::vector<unsigned long long>(n, 0));

// populate temporary adjacency matrix w/ edges
// can handle both directed and undirected
for (const auto &[node, edges] : *adj) {
for (const auto &e : edges) {
const auto edge = e.second->getNodePair();
const auto firstId = edge.first->getUserId();
const auto secondId = edge.second->getUserId();

// if undirected, add both sides
if (!(e.second->isDirected().has_value() && e.second->isDirected().value()))
tempIntAdj[userIdToIdx[secondId]][userIdToIdx[firstId]] = 1;
tempIntAdj[userIdToIdx[firstId]][userIdToIdx[secondId]] = 1;
}
}

// calculate the power matrix
auto powerMatrix = exponentiation(tempIntAdj, k);

for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
auto pr = std::make_pair(idxToUserId[i], idxToUserId[j]);
powAdj[pr] = powerMatrix[i][j];
}
}

result.result = std::move(powAdj);
result.success = true;

return result;
}

/**
* @brief This function raises a transition matrix to some k.
* Best used for finding equilibrium.
* @param k value by which to raise matrix
* @return (success, errorMessage, matrix): where matrix is equivalent to S^k
*/
template <typename T>
const PowTransResult matrixPow(const shared<TransitionMatrix<T>> &trans,
unsigned int k) {
PowTransResult result;
result.success = false;
result.errorMessage = "";
std::unordered_map<std::pair<std::string, std::string>, double,
CXXGraph::pair_hash>
powTrans;

std::unordered_map<std::string, int> userIdToIdx;
std::unordered_map<int, std::string> idxToUserId;

// get a map between index in adj matrix
// and userId
int n = 0;
for (const auto &[node, list] : *trans) {
userIdToIdx[node->getUserId()] = n;
idxToUserId[n] = node->getUserId();
n++;
}

std::vector<std::vector<double>> tempDoubleTrans(n,
std::vector<double>(n,
0));

// given transition matrix, convert it to
// stochastic matrix
for (const auto &it : *trans) {
const auto f = it.first;
const auto idx = userIdToIdx[f->getUserId()];

for (const auto &e : it.second) {
const auto idx2 = userIdToIdx[e.first->getUserId()];
tempDoubleTrans[idx][idx2] = e.second;
}
}

// exponentiate stochastic matrix
auto powerTransMatrix = exponentiation(tempDoubleTrans, k);

// turn back into a map between nodes
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
auto pr = std::make_pair(idxToUserId[i], idxToUserId[j]);
powTrans[pr] = powerTransMatrix[i][j];
}
}

result.result = std::move(powTrans);
result.success = true;

return result;
}
} // namespace CXXGraph

#endif //_CXXGRAPH_POW_IMPL_H__
1 change: 1 addition & 0 deletions include/CXXGraph/Graph/Graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "CXXGraph/Graph/Algorithm/Kahn_impl.hpp"
#include "CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp"
#include "CXXGraph/Graph/Algorithm/Kruskal_impl.hpp"
#include "CXXGraph/Graph/Algorithm/Pow_impl.hpp"
#include "CXXGraph/Graph/Algorithm/Prim_impl.hpp"
#include "CXXGraph/Graph/Algorithm/Tarjan_impl.hpp"
#include "CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp"
Expand Down
20 changes: 20 additions & 0 deletions include/CXXGraph/Utility/Typedef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,26 @@ struct BestFirstSearchResult_struct {
template <typename T>
using BestFirstSearchResult = BestFirstSearchResult_struct<T>;

/// Struct that contains the results from an adjacency matrix exponentiation
/// results
struct PowAdjResult_struct {
bool success = false;
std::string errorMessage = "";
std::unordered_map<std::pair<std::string, std::string>, unsigned long long, pair_hash>
result = {};
};
typedef PowAdjResult_struct PowAdjResult;

/// Struct that contains the results from a transition matrix exponentiation
/// results
struct PowTransResult_struct {
bool success = false;
std::string errorMessage = "";
std::unordered_map<std::pair<std::string, std::string>, double, pair_hash>
result = {};
};
typedef PowTransResult_struct PowTransResult;

///////////////////////////////////////////////////////////////////////////////////
// Using Definition
// ///////////////////////////////////////////////////////////////
Expand Down
Loading
Loading