Skip to content

Commit

Permalink
Add closeness_centrality app and test case of duplicated mode modific…
Browse files Browse the repository at this point in the history
…ation (#336)
  • Loading branch information
acezen committed Jun 3, 2021
1 parent 909e98f commit 6a98c7e
Show file tree
Hide file tree
Showing 20 changed files with 677 additions and 102 deletions.
143 changes: 143 additions & 0 deletions analytical_engine/apps/centrality/closeness/closeness_centrality.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/** Copyright 2020 Alibaba Group Holding Limited.
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.
*/

#ifndef ANALYTICAL_ENGINE_APPS_CENTRALITY_CLOSENESS_CLOSENESS_CENTRALITY_H_
#define ANALYTICAL_ENGINE_APPS_CENTRALITY_CLOSENESS_CLOSENESS_CENTRALITY_H_

#include <limits>
#include <map>
#include <queue>
#include <utility>
#include <vector>

#include "grape/grape.h"

#include "apps/centrality/closeness/closeness_centrality_context.h"

#include "core/utils/app_utils.h"

namespace gs {

/**
* @brief Compute the closeness centrality of vertices.
* Closeness centrality 1 of a node u is the reciprocal of the average shortest
* path distance to u over all n-1 reachable nodes.
* */
template <typename FRAG_T>
class ClosenessCentrality
: public grape::ParallelAppBase<FRAG_T, ClosenessCentralityContext<FRAG_T>>,
public grape::ParallelEngine {
public:
INSTALL_PARALLEL_WORKER(ClosenessCentrality<FRAG_T>,
ClosenessCentralityContext<FRAG_T>, FRAG_T)
static constexpr grape::MessageStrategy message_strategy =
grape::MessageStrategy::kSyncOnOuterVertex;
static constexpr grape::LoadStrategy load_strategy =
grape::LoadStrategy::kBothOutIn;
using vertex_t = typename fragment_t::vertex_t;
using vid_t = typename fragment_t::vid_t;
using edata_t = typename fragment_t::edata_t;

void PEval(const fragment_t& frag, context_t& ctx,
message_manager_t& messages) {
auto inner_vertices = frag.InnerVertices();
auto vertices = frag.Vertices();
ctx.length.resize(thread_num());
for (auto& unit : ctx.length) {
unit.Init(vertices);
}

ForEach(inner_vertices, [&frag, &ctx, this](int tid, vertex_t v) {
ctx.length[tid].SetValue(std::numeric_limits<double>::max());
this->reversedDijkstraLength(frag, v, ctx, tid);
this->compute(frag, v, ctx, tid);
});
}

void IncEval(const fragment_t& frag, context_t& ctx,
message_manager_t& messages) {
return;
}

private:
// sequential single source Dijkstra length algorithm.
void reversedDijkstraLength(const fragment_t& frag, vertex_t& s,
context_t& ctx, int tid) {
{
auto vertices = frag.Vertices();
std::priority_queue<std::pair<double, vertex_t>> heap;
typename FRAG_T::template vertex_array_t<bool> modified(vertices, false);
ctx.length[tid][s] = 0.0;
heap.emplace(0, s);

double distu, distv, ndistv;
vertex_t v, u;
while (!heap.empty()) {
u = heap.top().second;
distu = -heap.top().first;
heap.pop();

if (modified[u]) {
continue;
}
modified[u] = true;

auto es = frag.directed() ? frag.GetIncomingAdjList(u)
: frag.GetOutgoingAdjList(u);
for (auto& e : es) {
v = e.get_neighbor();
distv = ctx.length[tid][v];
double edata = 1.0;
static_if<!std::is_same<edata_t, grape::EmptyType>{}>(
[&](auto& e, auto& data) {
data = static_cast<double>(e.get_data());
})(e, edata);
ndistv = distu + edata;
if (distv > ndistv) {
ctx.length[tid][v] = ndistv;
heap.emplace(-ndistv, v);
}
}
}
}
}

void compute(const fragment_t& frag, vertex_t& u, context_t& ctx, int tid) {
double tot_sp = 0.0;
int connected_nodes_num = 0;
int total_node_num = 0;
auto vertices = frag.Vertices();
double closeness_centrality = 0.0;
for (auto& v : vertices) {
if (ctx.length[tid][v] < std::numeric_limits<double>::max()) {
tot_sp += ctx.length[tid][v];
++connected_nodes_num;
}
++total_node_num;
}
if (tot_sp > 0 && total_node_num > 1) {
closeness_centrality = (connected_nodes_num - 1.0) / tot_sp;
if (ctx.wf_improve) {
closeness_centrality *=
((connected_nodes_num - 1.0) / (total_node_num - 1));
}
}
ctx.centrality[u] = closeness_centrality;
}
};

} // namespace gs

#endif // ANALYTICAL_ENGINE_APPS_CENTRALITY_CLOSENESS_CLOSENESS_CENTRALITY_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/** Copyright 2020 Alibaba Group Holding Limited.
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.
*/

#ifndef ANALYTICAL_ENGINE_APPS_CENTRALITY_CLOSENESS_CLOSENESS_CENTRALITY_CONTEXT_H_
#define ANALYTICAL_ENGINE_APPS_CENTRALITY_CLOSENESS_CLOSENESS_CENTRALITY_CONTEXT_H_

#include <limits>
#include <map>
#include <queue>
#include <utility>
#include <vector>

#include "grape/grape.h"

namespace gs {

template <typename FRAG_T>
class ClosenessCentralityContext
: public grape::VertexDataContext<FRAG_T, double> {
public:
using oid_t = typename FRAG_T::oid_t;
using vid_t = typename FRAG_T::vid_t;
using vertex_t = typename FRAG_T::vertex_t;

explicit ClosenessCentralityContext(const FRAG_T& fragment)
: grape::VertexDataContext<FRAG_T, double>(fragment),
centrality(this->data()) {}

void Init(grape::ParallelMessageManager& messages, bool wf) {
auto& frag = this->fragment();
auto vertices = frag.Vertices();
wf_improve = wf;
centrality.SetValue(0.0);
}

void Output(std::ostream& os) override {
auto& frag = this->fragment();
auto inner_vertices = frag.InnerVertices();

for (auto& u : inner_vertices) {
os << frag.GetId(u) << "\t" << centrality[u] << std::endl;
}
}

bool wf_improve; // use Wasserman-Faust improved formula.
std::vector<typename FRAG_T::template vertex_array_t<double>> length;
typename FRAG_T::template vertex_array_t<double>& centrality;
};
} // namespace gs

#endif // ANALYTICAL_ENGINE_APPS_CENTRALITY_CLOSENESS_CLOSENESS_CENTRALITY_CONTEXT_H_
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ limitations under the License.
#include "apps/centrality/eigenvector/eigenvector_centrality_context.h"

#include "core/app/app_base.h"
#include "core/utils/app_utils.h"
#include "core/worker/default_worker.h"

namespace gs {
Expand Down Expand Up @@ -109,7 +110,12 @@ class EigenvectorCentrality
for (auto& v : inner_vertices) {
auto es = frag.GetIncomingAdjList(v);
for (auto& e : es) {
x[v] += x_last[e.get_neighbor()] * e.get_data();
double edata = 1.0;
static_if<!std::is_same<edata_t, grape::EmptyType>{}>(
[&](auto& e, auto& data) {
data = static_cast<double>(e.get_data());
})(e, edata);
x[v] += x_last[e.get_neighbor()] * edata;
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion analytical_engine/apps/centrality/katz/katz_centrality.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ limitations under the License.
#include "apps/centrality/katz/katz_centrality_context.h"

#include "core/app/app_base.h"
#include "core/utils/app_utils.h"
#include "core/worker/default_worker.h"

namespace gs {
Expand Down Expand Up @@ -104,7 +105,12 @@ class KatzCentrality : public AppBase<FRAG_T, KatzCentralityContext<FRAG_T>>,
x[v] = 0;
for (auto& e : es) {
// do the multiplication y^T = Alpha * x^T A - Beta
x[v] += x_last[e.get_neighbor()] * e.get_data();
double edata = 1.0;
static_if<!std::is_same<edata_t, grape::EmptyType>{}>(
[&](auto& e, auto& data) {
data = static_cast<double>(e.get_data());
})(e, edata);
x[v] += x_last[e.get_neighbor()] * edata;
}
x[v] = x[v] * ctx.alpha + ctx.beta;
messages.SendMsgThroughEdges(frag, v, ctx.x[v]);
Expand Down
9 changes: 8 additions & 1 deletion analytical_engine/apps/projected/sssp_projected.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "grape/grape.h"

#include "core/app/app_base.h"
#include "core/utils/app_utils.h"
#include "core/worker/default_worker.h"

namespace gs {
Expand Down Expand Up @@ -69,6 +70,7 @@ class SSSPProjected : public AppBase<FRAG_T, SSSPProjectedContext<FRAG_T>> {
INSTALL_DEFAULT_WORKER(SSSPProjected<FRAG_T>, SSSPProjectedContext<FRAG_T>,
FRAG_T)
using vertex_t = typename fragment_t::vertex_t;
using edata_t = typename fragment_t::edata_t;

private:
// sequential Dijkstra algorithm for SSSP.
Expand All @@ -93,7 +95,12 @@ class SSSPProjected : public AppBase<FRAG_T, SSSPProjectedContext<FRAG_T>> {
for (auto& e : es) {
v = e.neighbor();
distv = ctx.partial_result[v];
ndistv = distu + e.data();
double edata = 1.0;
static_if<!std::is_same<edata_t, grape::EmptyType>{}>(
[&](auto& e, auto& data) {
data = static_cast<double>(e.get_data());
})(e, edata);
ndistv = distu + edata;
if (distv > ndistv) {
ctx.partial_result[v] = ndistv;
if (frag.IsInnerVertex(v)) {
Expand Down
14 changes: 12 additions & 2 deletions analytical_engine/apps/sssp/sssp_average_length.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ limitations under the License.
#include "grape/grape.h"

#include "core/app/app_base.h"
#include "core/utils/app_utils.h"
#include "core/worker/default_worker.h"
#include "sssp/sssp_average_length_context.h"

Expand All @@ -49,6 +50,7 @@ class SSSPAverageLength
grape::LoadStrategy::kBothOutIn;
using vertex_t = typename fragment_t::vertex_t;
using vid_t = typename fragment_t::vid_t;
using edata_t = typename fragment_t::vid_t;
// vertex msg: [source, v, sssp_length]
// OR sum msg: [fid, fid, sssp_length_sum]
using tuple_t = typename std::tuple<vid_t, vid_t, double>;
Expand Down Expand Up @@ -194,7 +196,11 @@ class SSSPAverageLength
for (auto& e : oes) {
auto u = e.get_neighbor();
if (frag.IsOuterVertex(u)) {
double v_u = (ctx.weight) ? e.get_data() : 1;
double v_u = 1.0;
static_if<!std::is_same<edata_t, grape::EmptyType>{}>(
[&](auto& e, auto& data) {
data = static_cast<double>(e.get_data());
})(e, v_u);
double dist = ctx.path_distance[v][src_vid] + v_u;
vid_t u_vid = frag.Vertex2Gid(u);
messages.SendToFragment(
Expand Down Expand Up @@ -239,7 +245,11 @@ class SSSPAverageLength
for (auto& e : oes) {
auto u = e.get_neighbor();
if (frag.IsInnerVertex(u)) {
double v_u = (ctx.weight) ? e.get_data() : 1;
double v_u = 1.0;
static_if<!std::is_same<edata_t, grape::EmptyType>{}>(
[&](auto& e, auto& data) {
data = static_cast<double>(e.get_data());
})(e, v_u);
updateVertexState(u, src_vid, dist_v + v_u, ctx);
}
}
Expand Down
5 changes: 1 addition & 4 deletions analytical_engine/apps/sssp/sssp_average_length_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ class SSSPAverageLengthContext : public TensorContext<FRAG_T, double> {
explicit SSSPAverageLengthContext(const FRAG_T& fragment)
: TensorContext<FRAG_T, double>(fragment) {}

void Init(grape::DefaultMessageManager& messages, bool w) {
void Init(grape::DefaultMessageManager& messages) {
auto& frag = this->fragment();

weight = w;
inner_sum = 0.0;
path_distance.Init(frag.InnerVertices());
updated.Init(frag.InnerVertices());
Expand Down Expand Up @@ -72,8 +71,6 @@ class SSSPAverageLengthContext : public TensorContext<FRAG_T, double> {
#endif
}

bool weight;

// length sum of each fragment, only maintained by frag 0
std::map<fid_t, double> all_sums;

Expand Down
12 changes: 8 additions & 4 deletions analytical_engine/apps/sssp/sssp_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ limitations under the License.
#include "grape/grape.h"

#include "core/app/app_base.h"
#include "core/utils/app_utils.h"
#include "core/worker/default_worker.h"
#include "sssp/sssp_path_context.h"

Expand All @@ -48,6 +49,7 @@ class SSSPPath : public AppBase<FRAG_T, SSSPPathContext<FRAG_T>>,
grape::LoadStrategy::kBothOutIn;
using vertex_t = typename fragment_t::vertex_t;
using vid_t = typename fragment_t::vid_t;
using edata_t = typename fragment_t::edata_t;
using pair_msg_t = typename std::pair<vid_t, double>;

void PEval(const fragment_t& frag, context_t& ctx,
Expand Down Expand Up @@ -149,10 +151,12 @@ class SSSPPath : public AppBase<FRAG_T, SSSPPathContext<FRAG_T>>,
for (auto& e : oes) {
auto u = e.get_neighbor();
double new_distu;
if (ctx.weight)
new_distu = ctx.path_distance[v] + e.get_data();
else
new_distu = ctx.path_distance[v] + 1;
double edata = 1.0;
static_if<!std::is_same<edata_t, grape::EmptyType>{}>(
[&](auto& e, auto& data) {
data = static_cast<double>(e.get_data());
})(e, edata);
new_distu = ctx.path_distance[v] + edata;
if (frag.IsOuterVertex(u)) {
messages.SyncStateOnOuterVertex<fragment_t, pair_msg_t>(
frag, u, std::make_pair(v_vid, new_distu));
Expand Down
4 changes: 1 addition & 3 deletions analytical_engine/apps/sssp/sssp_path_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ class SSSPPathContext : public TensorContext<FRAG_T, typename FRAG_T::oid_t> {
explicit SSSPPathContext(const FRAG_T& fragment)
: TensorContext<FRAG_T, typename FRAG_T::oid_t>(fragment) {}

void Init(grape::DefaultMessageManager& messages, oid_t source, bool w) {
void Init(grape::DefaultMessageManager& messages, oid_t source) {
auto& frag = this->fragment();

source_id = source;
weight = w;
predecessor.Init(frag.InnerVertices());
path_distance.Init(frag.InnerVertices(),
std::numeric_limits<double>::max());
Expand Down Expand Up @@ -68,7 +67,6 @@ class SSSPPathContext : public TensorContext<FRAG_T, typename FRAG_T::oid_t> {
}

oid_t source_id;
bool weight;

typename FRAG_T::template vertex_array_t<vertex_t> predecessor;
typename FRAG_T::template vertex_array_t<double> path_distance;
Expand Down
Loading

0 comments on commit 6a98c7e

Please sign in to comment.