From 641dc3321db48c7b087ecc3c873a201637ac1605 Mon Sep 17 00:00:00 2001 From: bedupako12mas <141558656+bedupako12mas@users.noreply.github.com> Date: Sat, 17 Aug 2024 00:20:40 +0530 Subject: [PATCH] Betweenness centrality new function (#2644) * (betweennessCentrality) pgtap tests that need to pass * (betweennessCentrality) sql functions * (betweennessCentrality) C/C++ code * (betweennessCentrality) adding documentation as experimental * (betweennessCentrality) example for the documentation * (betweennessCentrality) adding to the build * (betweennessCentrality) adding to the list of signatures * (NEWS) updating changes --- NEWS.md | 6 + configuration.conf | 1 + doc/metrics/CMakeLists.txt | 12 + doc/metrics/metrics-family.rst | 41 +++ doc/metrics/pgr_betweennessCentrality.rst | 148 +++++++++ doc/src/experimental.rst | 7 + doc/src/release_notes.rst | 6 + docqueries/metrics/CMakeLists.txt | 11 + docqueries/metrics/betweennessCentrality.pg | 9 + .../metrics/betweennessCentrality.result | 21 ++ docqueries/metrics/test.conf | 12 + .../metrics/betweennessCentrality_driver.h | 64 ++++ include/metrics/betweennessCentrality.hpp | 144 +++++++++ .../betweennessCentrality/edge_cases.pg | 282 ++++++++++++++++++ .../betweennessCentrality/inner_query.pg | 46 +++ .../betweennessCentrality/no_crash_test.pg | 72 +++++ .../betweennessCentrality/types_check.pg | 62 ++++ sql/metrics/CMakeLists.txt | 12 + sql/metrics/_betweennessCentrality.sql | 49 +++ sql/metrics/betweennessCentrality.sql | 57 ++++ sql/sigs/pgrouting--3.7.sig | 2 + src/metrics/CMakeLists.txt | 4 + src/metrics/betweennessCentrality.c | 136 +++++++++ src/metrics/betweennessCentrality_driver.cpp | 118 ++++++++ 24 files changed, 1322 insertions(+) create mode 100644 doc/metrics/CMakeLists.txt create mode 100644 doc/metrics/metrics-family.rst create mode 100644 doc/metrics/pgr_betweennessCentrality.rst create mode 100644 docqueries/metrics/CMakeLists.txt create mode 100644 docqueries/metrics/betweennessCentrality.pg create mode 100644 docqueries/metrics/betweennessCentrality.result create mode 100644 docqueries/metrics/test.conf create mode 100644 include/drivers/metrics/betweennessCentrality_driver.h create mode 100644 include/metrics/betweennessCentrality.hpp create mode 100644 pgtap/metrics/betweennessCentrality/edge_cases.pg create mode 100644 pgtap/metrics/betweennessCentrality/inner_query.pg create mode 100644 pgtap/metrics/betweennessCentrality/no_crash_test.pg create mode 100644 pgtap/metrics/betweennessCentrality/types_check.pg create mode 100644 sql/metrics/CMakeLists.txt create mode 100644 sql/metrics/_betweennessCentrality.sql create mode 100644 sql/metrics/betweennessCentrality.sql create mode 100644 src/metrics/CMakeLists.txt create mode 100644 src/metrics/betweennessCentrality.c create mode 100644 src/metrics/betweennessCentrality_driver.cpp diff --git a/NEWS.md b/NEWS.md index 205430b7db7..545ce4eda0a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,12 @@ To see all issues & pull requests closed by this release see the [Git closed milestone for 3.7.0 ](https://github.com/pgRouting/pgrouting/issues?utf8=%E2%9C%93&q=milestone%3A%22Release%203.7.0%22) +**New experimental functions** + +* Metrics + + * pgr_betweennessCentrality + **Official functions changes** * [#2605](https://github.com/pgRouting/pgrouting/pull/2605) Standarize diff --git a/configuration.conf b/configuration.conf index aa53bb647b3..1f63c989983 100644 --- a/configuration.conf +++ b/configuration.conf @@ -46,6 +46,7 @@ planar | Y | Y | Y dominator | Y | Y | Y ordering | Y | Y | Y circuits | Y | Y | Y +metrics | Y | Y | Y #---------------------- # SQL only directories #---------------------- diff --git a/doc/metrics/CMakeLists.txt b/doc/metrics/CMakeLists.txt new file mode 100644 index 00000000000..5adba5a42a6 --- /dev/null +++ b/doc/metrics/CMakeLists.txt @@ -0,0 +1,12 @@ + +SET(LOCAL_FILES + metrics-family.rst + pgr_betweennessCentrality.rst + ) + +foreach (f ${LOCAL_FILES}) + configure_file(${f} "${PGR_DOCUMENTATION_SOURCE_DIR}/${f}") + list(APPEND LOCAL_DOC_FILES ${PGR_DOCUMENTATION_SOURCE_DIR}/${f}) +endforeach() + +set(PROJECT_DOC_FILES ${PROJECT_DOC_FILES} ${LOCAL_DOC_FILES} PARENT_SCOPE) diff --git a/doc/metrics/metrics-family.rst b/doc/metrics/metrics-family.rst new file mode 100644 index 00000000000..66cacabdc67 --- /dev/null +++ b/doc/metrics/metrics-family.rst @@ -0,0 +1,41 @@ +.. + **************************************************************************** + pgRouting Manual + Copyright(c) pgRouting Contributors + + This documentation is licensed under a Creative Commons Attribution-Share + Alike 3.0 License: https://creativecommons.org/licenses/by-sa/3.0/ + **************************************************************************** + +| + + + +Metrics - Family of functions +=============================================================================== + +.. rubric:: Experimental + +.. include:: experimental.rst + :start-after: begin-warn-expr + :end-before: end-warn-expr + +.. index experimental from here + +* :doc:`pgr_betweennessCentrality` - Calculates relative betweenness centrality using Brandes Algorithm + +.. index experimental to here + + +.. toctree:: + :hidden: + + pgr_betweennessCentrality + +See Also +------------------------------------------------------------------------------- + +.. rubric:: Indices and tables + +* :ref:`genindex` +* :ref:`search` diff --git a/doc/metrics/pgr_betweennessCentrality.rst b/doc/metrics/pgr_betweennessCentrality.rst new file mode 100644 index 00000000000..ad4b0c94f00 --- /dev/null +++ b/doc/metrics/pgr_betweennessCentrality.rst @@ -0,0 +1,148 @@ +.. + **************************************************************************** + pgRouting Manual + Copyright(c) pgRouting Contributors + + This documentation is licensed under a Creative Commons Attribution-Share + Alike 3.0 License: https://creativecommons.org/licenses/by-sa/3.0/ + **************************************************************************** + +| + + +``pgr_betweennessCentrality`` +=============================================================================== + +``pgr_betweennessCentrality`` - Calculates the relative betweeness centrality +using Brandes Algorithm + +.. figure:: images/boost-inside.jpeg + :target: https://www.boost.org/doc/libs/1_84_0/libs/graph/doc/betweenness_centrality.html + + Boost Graph Inside + +.. rubric:: Availability + +* Version 3.7.0 + + * New **experimental** function: + + * ``pgr_betweennessCentrality`` + +Description +------------------------------------------------------------------------------- + +The Brandes Algorithm takes advantage of the sparse graphs for evaluating the +betweenness centrality score of all vertices. + + +Betweenness centrality measures the extent to which a vertex lies on the +shortest paths between all other pairs of vertices. Vertices with a high +betweenness centrality score may have considerable influence in a network by the +virtue of their control over the shortest paths passing between them. + +The removal of these vertices will affect the network by disrupting the +it, as most of the shortest paths between vertices pass through them. + +This implementation work for both directed and undirected graphs. + +- Running time: :math:`\Theta(VE)` +- Running space: :math:`\Theta(VE)` +- Throws when there are no edges in the graph + +Signatures +------------------------------------------------------------------------------- + +.. rubric:: Summary + +.. admonition:: \ \ + :class: signatures + + pgr_betweennessCentrality(`Edges SQL`_, [``directed``]) + + | Returns set of ``(vid, centrality)`` + +:Example: For a directed graph with edges :math:`\{1, 2, 3, 4\}`. + +.. literalinclude:: betweennessCentrality.queries + :start-after: -- q1 + :end-before: -- q2 + +.. rubric:: Explanation + +* The betweenness centrality are between parenthesis. +* The leaf vertices have betweenness centrality :math:`0`. +* Betweenness centrality of vertex :math:`6` is higher than of vertex :math:`10`. + + * Removing vertex :math:`6` will create three graph components. + * Removing vertex :math:`10` will create two graph components. + +.. graphviz:: + + digraph G { + 5, 7, 15 [shape=circle;style=filled;width=.5;color=deepskyblue;fontsize=8;fixedsize=true;]; + 6, 10 [shape=circle;style=filled;width=.5;color=green;fontsize=8;fixedsize=true;]; + 5 [pos="0,0!";label="5 (0)"]; + 6 [pos="0,1!"label="6 (0.5)"]; + 7 [pos="0,2!"label="7 (0)"]; + 10 [pos="1,1!"label="10 (0.25)"]; + 15 [pos="2,1!"label="15 (0)"]; + 5 -> 6 [dir=both;label="1 "]; + 6->7 [dir=both;label="4 "]; + 10->6 [label="3"]; + 15->10 [label="4"]; + } + +Parameters +------------------------------------------------------------------------------- + +.. include:: allpairs-family.rst + :start-after: edges_start + :end-before: edges_end + +Optional parameters +............................................................................... + +.. include:: dijkstra-family.rst + :start-after: dijkstra_optionals_start + :end-before: dijkstra_optionals_end + +Inner Queries +------------------------------------------------------------------------------- + +Edges SQL +............................................................................... + +.. include:: pgRouting-concepts.rst + :start-after: no_id_edges_sql_start + :end-before: no_id_edges_sql_end + +Result columns +------------------------------------------------------------------------------- + +.. list-table:: + :width: 81 + :widths: auto + :header-rows: 1 + + * - Column + - Type + - Description + * - ``vid`` + - ``BIGINT`` + - Identifier of the vertex + * - ``centrality`` + - ``FLOAT`` + - Relative betweenness centrality score of the vertex (will be in range [0,1]) + +See Also +------------------------------------------------------------------------------- + +* Boost's `betweenness_centrality + `_ +* Queries use the :doc:`sampledata` network. + +.. rubric:: Indices and tables + +* :ref:`genindex` +* :ref:`search` diff --git a/doc/src/experimental.rst b/doc/src/experimental.rst index 7277a28067e..6f054c9324f 100644 --- a/doc/src/experimental.rst +++ b/doc/src/experimental.rst @@ -83,6 +83,12 @@ Experimental Functions :start-after: index from here :end-before: index to here +:doc:`metrics-family` + +.. include:: metrics-family.rst + :start-after: index experimental from here + :end-before: index experimental to here + :doc:`TRSP-family` .. include:: TRSP-family.rst @@ -96,6 +102,7 @@ Experimental Functions transformation-family components-family ordering-family + metrics-family .. rubric:: categories diff --git a/doc/src/release_notes.rst b/doc/src/release_notes.rst index fd4a98899d6..453042d49c5 100644 --- a/doc/src/release_notes.rst +++ b/doc/src/release_notes.rst @@ -43,6 +43,12 @@ To see all issues & pull requests closed by this release see the `Git closed milestone for 3.7.0 `__ +.. rubric:: New experimental functions + +* Metrics + + * pgr_betweennessCentrality + .. rubric:: Official functions changes * `#2605 `__ Standarize diff --git a/docqueries/metrics/CMakeLists.txt b/docqueries/metrics/CMakeLists.txt new file mode 100644 index 00000000000..27f1bdae576 --- /dev/null +++ b/docqueries/metrics/CMakeLists.txt @@ -0,0 +1,11 @@ +# Do not use extensions +SET(LOCAL_FILES + betweennessCentrality + ) + +foreach (f ${LOCAL_FILES}) + configure_file("${f}.result" "${PGR_DOCUMENTATION_SOURCE_DIR}/${f}.queries") + list(APPEND LOCAL_DOC_FILES "${PGR_DOCUMENTATION_SOURCE_DIR}/${f}.queries") +endforeach() + +set(PROJECT_DOC_FILES ${PROJECT_DOC_FILES} ${LOCAL_DOC_FILES} PARENT_SCOPE) diff --git a/docqueries/metrics/betweennessCentrality.pg b/docqueries/metrics/betweennessCentrality.pg new file mode 100644 index 00000000000..dbd30cc264b --- /dev/null +++ b/docqueries/metrics/betweennessCentrality.pg @@ -0,0 +1,9 @@ +-- CopyRight(c) pgRouting developers +-- Creative Commons Attribution-Share Alike 3.0 License : https://creativecommons.org/licenses/by-sa/3.0/ + +/* -- q1 */ +SELECT * FROM pgr_betweennessCentrality( +'SELECT id, source, target, cost, reverse_cost +FROM edges where id < 5' +) ORDER BY vid; +/* -- q2 */ diff --git a/docqueries/metrics/betweennessCentrality.result b/docqueries/metrics/betweennessCentrality.result new file mode 100644 index 00000000000..be9bb20878e --- /dev/null +++ b/docqueries/metrics/betweennessCentrality.result @@ -0,0 +1,21 @@ +BEGIN; +BEGIN +SET client_min_messages TO NOTICE; +SET +/* -- q1 */ +SELECT * FROM pgr_betweennessCentrality( +'SELECT id, source, target, cost, reverse_cost +FROM edges where id < 5' +) ORDER BY vid; + vid | centrality +-----+------------ + 5 | 0 + 6 | 0.5 + 7 | 0 + 10 | 0.25 + 15 | 0 +(5 rows) + +/* -- q2 */ +ROLLBACK; +ROLLBACK diff --git a/docqueries/metrics/test.conf b/docqueries/metrics/test.conf new file mode 100644 index 00000000000..6afe7ec483b --- /dev/null +++ b/docqueries/metrics/test.conf @@ -0,0 +1,12 @@ +#!/usr/bin/perl -w + +%main::tests = ( + 'any' => { + 'files' => [qw( + betweennessCentrality.pg + )] + }, + +); + +1; diff --git a/include/drivers/metrics/betweennessCentrality_driver.h b/include/drivers/metrics/betweennessCentrality_driver.h new file mode 100644 index 00000000000..29d2a2e9787 --- /dev/null +++ b/include/drivers/metrics/betweennessCentrality_driver.h @@ -0,0 +1,64 @@ +/*PGR-GNU***************************************************************** +File: betweennessCentrality_driver.h + +Generated with Template by: +Copyright (c) 2015 pgRouting developers +Mail: project@pgrouting.org + +Function's developer: +Copyright (c) 2024 Arun Thakur +Mail: bedupako12mas at gmail.com + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#ifndef INCLUDE_DRIVERS_METRICS_BETWEENNESSCENTRALITY_DRIVER_H_ +#define INCLUDE_DRIVERS_METRICS_BETWEENNESSCENTRALITY_DRIVER_H_ +#pragma once + +/* for size-t */ +#ifdef __cplusplus +# include +using IID_t_rt = struct IID_t_rt; +#else +# include +#include +typedef struct IID_t_rt IID_t_rt; +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + +void +pgr_do_betweennessCentrality( + char*, + bool, + + IID_t_rt**, + size_t*, + char**, + char**); + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_DRIVERS_METRICS_BETWEENNESSCENTRALITY_DRIVER_H_ diff --git a/include/metrics/betweennessCentrality.hpp b/include/metrics/betweennessCentrality.hpp new file mode 100644 index 00000000000..1277e94caab --- /dev/null +++ b/include/metrics/betweennessCentrality.hpp @@ -0,0 +1,144 @@ +/*PGR-GNU***************************************************************** +File: betweennessCentrality.hpp + +Copyright (c) 2015 pgRouting developers +Mail: project@pgrouting.org + +Copyright (c) 2024 Arun Thakur +Mail: bedupako12mas at gmail.com + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +********************************************************************PGR-GNU*/ + +#ifndef INCLUDE_METRICS_BETWEENNESSCENTRALITY_HPP_ +#define INCLUDE_METRICS_BETWEENNESSCENTRALITY_HPP_ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "c_types/iid_t_rt.h" +#include "cpp_common/basePath_SSEC.hpp" +#include "cpp_common/pgr_base_graph.hpp" +#include "cpp_common/interruption.hpp" +#include "cpp_common/pgr_alloc.hpp" + +namespace pgrouting { +template class Pgr_metrics; + +template +void +pgr_betweennesscentrality( + G &graph, + size_t &result_tuple_count, + IID_t_rt **postgres_rows) { + Pgr_metrics fn_centrality; + fn_centrality.betweennessCentrality(graph, result_tuple_count, postgres_rows); +} + +template +class Pgr_metrics { + public: + using Graph = typename G::B_G; + using V = typename G::V; + using E = typename G::E; + typedef typename boost::graph_traits::directed_category directed_category; + + void betweennessCentrality( + const G &graph, + size_t &result_tuple_count, + IID_t_rt **postgres_rows) { + // required parameters + std::vector centrality(boost::num_vertices(graph.graph), 0.0); + auto centrality_map = boost::make_iterator_property_map(centrality.begin(), + boost::get(boost::vertex_index, graph.graph)); + std::vector distance(boost::num_vertices(graph.graph), 0.0); + auto distance_map = boost::make_iterator_property_map(distance.begin(), + boost::get(boost::vertex_index, graph.graph)); + // dummy parameters + std::vector edge_centrality(boost::num_edges(graph.graph), 0.0); + auto edge_centrality_map = boost::make_iterator_property_map(edge_centrality.begin(), + boost::get(&pgrouting::Basic_edge::cost, + graph.graph)); + std::vector> incoming(boost::num_vertices(graph.graph)); + auto incoming_map = boost::make_iterator_property_map(incoming.begin(), + boost::get(boost::vertex_index, graph.graph), + std::vector()); + std::vector dependency(boost::num_vertices(graph.graph), 0.0); + auto dependency_map = boost::make_iterator_property_map(dependency.begin(), + boost::get(boost::vertex_index, graph.graph)); + std::vector path_count(boost::num_vertices(graph.graph), 0); + auto path_count_map = boost::make_iterator_property_map(path_count.begin(), + boost::get(boost::vertex_index, graph.graph)); + auto vertex_index = boost::get(boost::vertex_index, graph.graph); + + /* abort in case of an interruption occurs (e.g. the query is being cancelled) */ + CHECK_FOR_INTERRUPTS(); + + boost::brandes_betweenness_centrality( + graph.graph, + centrality_map, + edge_centrality_map, + incoming_map, + distance_map, + dependency_map, + path_count_map, + vertex_index, + get(&pgrouting::Basic_edge::cost, graph.graph)); + + if (boost::num_vertices(graph.graph) > 2) { + boost::relative_betweenness_centrality( + graph.graph, + centrality_map); + } + + generate_results(graph, centrality, result_tuple_count, postgres_rows); + } + + private: + void generate_results( + const G &graph, + const std::vector centrality_results, + size_t &result_tuple_count, + IID_t_rt **postgres_rows) const { + result_tuple_count = centrality_results.size(); + *postgres_rows = pgr_alloc(result_tuple_count, (*postgres_rows)); + + size_t seq = 0; + for (typename G::V v_i = 0; v_i < graph.num_vertices(); ++v_i) { + (*postgres_rows)[seq].from_vid = graph[v_i].id; + (*postgres_rows)[seq].to_vid = 0; + (*postgres_rows)[seq].cost = centrality_results[v_i]; + if (std::is_same::value) { + (*postgres_rows)[seq].cost = centrality_results[v_i]/2.0; + } + seq++; + } + } +}; + +} // namespace pgrouting + +#endif // INCLUDE_METRICS_BETWEENNESSCENTRALITY_HPP_ diff --git a/pgtap/metrics/betweennessCentrality/edge_cases.pg b/pgtap/metrics/betweennessCentrality/edge_cases.pg new file mode 100644 index 00000000000..d4582a48891 --- /dev/null +++ b/pgtap/metrics/betweennessCentrality/edge_cases.pg @@ -0,0 +1,282 @@ +/*PGR-GNU***************************************************************** + +Copyright (c) 2024 pgRouting developers +Mail: project@pgrouting.org + +------ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + ********************************************************************PGR-GNU*/ + +BEGIN; + +UPDATE edges SET cost = sign(cost), reverse_cost = sign(reverse_cost); +SELECT CASE WHEN NOT min_version('3.7.0') THEN plan(1) ELSE plan(24) END; + +CREATE OR REPLACE FUNCTION edge_cases() +RETURNS SETOF TEXT AS +$BODY$ +BEGIN + +IF NOT min_version('3.7.0') THEN + RETURN QUERY + SELECT skip(1, 'Function is new on 3.7.0'); + RETURN; +END IF; + +/* Implicit Test Cases */ + +PREPARE idless5_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 5' + ) ORDER BY vid; + +PREPARE idless5_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.5), + (7 , 0), + (10 , 0.25), + (15 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless5_q'); + +RETURN QUERY +SELECT results_eq('idless5_q', 'idless5_r'); + + +PREPARE idless4_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 4' + ) ORDER BY vid; + +PREPARE idless4_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.3333333333333333), + (10 , 0.3333333333333333), + (15 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless4_q'); + +RETURN QUERY +SELECT results_eq('idless4_q', 'idless4_r'); + + +PREPARE idless3_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 3' + ) ORDER BY vid; + +PREPARE idless3_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.5), + (10 , 0) + ) AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless3_q'); + +RETURN QUERY +SELECT results_eq('idless3_q', 'idless3_r'); + +PREPARE idless2_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 2' + ) ORDER BY vid; + +PREPARE idless2_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0) + ) AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless2_q'); + +RETURN QUERY +SELECT results_eq('idless2_q', 'idless2_r'); + +/* Explicit Undirected Cases */ + +PREPARE idless5ud_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 5', directed => false ) ORDER BY vid; + +PREPARE idless5ud_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.8333333333333333), + (7 , 0), + (10 , 0.5), + (15 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless5ud_q'); + +RETURN QUERY +SELECT results_eq('idless5ud_q', 'idless5ud_r'); + +PREPARE idless4ud_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 4', directed => false ) ORDER BY vid; + +PREPARE idless4ud_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.6666666666666666), + (10 , 0.6666666666666666), (15 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless4ud_q'); + +RETURN QUERY +SELECT results_eq('idless4ud_q', 'idless4ud_r'); + + +PREPARE idless3ud_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 3', directed => false ) ORDER BY vid; + +PREPARE idless3ud_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 1), + (10 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless3ud_q'); + +RETURN QUERY +SELECT results_eq('idless3ud_q', 'idless3ud_r'); + +PREPARE idless2ud_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 2', directed => false + ) ORDER BY vid; + +PREPARE idless2ud_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless2ud_q'); + +RETURN QUERY +SELECT results_eq('idless2ud_q', 'idless2ud_r'); + +/* Explicit Directed Cases */ + + +PREPARE idless5d_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 5', directed => true ) ORDER BY vid; + +PREPARE idless5d_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.5), + (7 , 0), + (10 , 0.25), + (15 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless5d_q'); + +RETURN QUERY +SELECT results_eq('idless5d_q', 'idless5d_r'); + + +PREPARE idless4d_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 4', directed => true ) ORDER BY vid; + +PREPARE idless4d_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.3333333333333333), + (10 , 0.3333333333333333), + (15 , 0)) + AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless4d_q'); + +RETURN QUERY +SELECT results_eq('idless4d_q', 'idless4d_r'); + + +PREPARE idless3d_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 3', directed => true ) ORDER BY vid; + +PREPARE idless3d_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0.5), + (10 , 0) + ) AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless3d_q'); + +RETURN QUERY +SELECT results_eq('idless3d_q', 'idless3d_r'); + +PREPARE idless2d_q AS + SELECT * FROM pgr_betweennessCentrality( + 'SELECT id, source, target, cost, reverse_cost + FROM edges WHERE id < 2', directed => true + ) ORDER BY vid; + +PREPARE idless2d_r AS + SELECT * FROM (VALUES + (5::BIGINT , 0::FLOAT), + (6 , 0) + ) AS t(vid, centrality); + +RETURN QUERY +SELECT lives_ok('idless2d_q'); + +RETURN QUERY +SELECT results_eq('idless2d_q', 'idless2d_r'); + +END; +$BODY$ +LANGUAGE plpgsql; + +SELECT edge_cases(); + +SELECT finish(); +ROLLBACK; diff --git a/pgtap/metrics/betweennessCentrality/inner_query.pg b/pgtap/metrics/betweennessCentrality/inner_query.pg new file mode 100644 index 00000000000..9873cbb703a --- /dev/null +++ b/pgtap/metrics/betweennessCentrality/inner_query.pg @@ -0,0 +1,46 @@ + +/*PGR-GNU***************************************************************** + +Copyright (c) 2024 pgRouting developers +Mail: project@pgrouting.org + +------ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + ********************************************************************PGR-GNU*/ +BEGIN; + +UPDATE edges SET cost = sign(cost), reverse_cost = sign(reverse_cost); +SELECT CASE WHEN NOT min_version('3.7.0') THEN plan(1) ELSE plan(54) END; + +CREATE OR REPLACE FUNCTION inner_query() +RETURNS SETOF TEXT AS +$BODY$ +BEGIN + +IF NOT min_version('3.7.0') THEN + RETURN QUERY + SELECT skip(1, 'Function is new on 3.7.0'); + RETURN; +END IF; + +RETURN QUERY +SELECT style_dijkstra('pgr_betweennessCentrality(', ')'); + +END; +$BODY$ +LANGUAGE plpgsql; + +SELECT inner_query(); + +SELECT finish(); +ROLLBACK; diff --git a/pgtap/metrics/betweennessCentrality/no_crash_test.pg b/pgtap/metrics/betweennessCentrality/no_crash_test.pg new file mode 100644 index 00000000000..c7b37ce9e70 --- /dev/null +++ b/pgtap/metrics/betweennessCentrality/no_crash_test.pg @@ -0,0 +1,72 @@ + +/*PGR-GNU***************************************************************** + +Copyright (c) 2024 pgRouting developers +Mail: project@pgrouting.org + +------ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + ********************************************************************PGR-GNU*/ +BEGIN; + +UPDATE edges SET cost = sign(cost), reverse_cost = sign(reverse_cost); +SELECT CASE WHEN NOT min_version('3.7.0') THEN plan(1) ELSE plan(7) END; + +PREPARE edges_q AS +SELECT id, source, target, cost, reverse_cost FROM edges; + +PREPARE null_ret AS +SELECT id FROM vertices WHERE id IN (-1); + +PREPARE null_ret_arr AS +SELECT array_agg(id) FROM vertices WHERE id IN (-1); + + +CREATE OR REPLACE FUNCTION no_crash() +RETURNS SETOF TEXT AS +$BODY$ +DECLARE +params TEXT[]; +subs TEXT[]; +BEGIN + IF NOT min_version('3.7.0') THEN + RETURN QUERY + SELECT skip(1, 'Function is new on 3.7.0'); + RETURN; + END IF; + + RETURN QUERY + SELECT isnt_empty('edges_q', 'Should be not empty to tests be meaningful'); + RETURN QUERY + SELECT is_empty('null_ret', 'Should be empty to tests be meaningful'); + RETURN QUERY + SELECT set_eq('null_ret_arr', 'SELECT NULL::BIGINT[]', 'Should be empty to tests be meaningful'); + + params = ARRAY[ + '$$SELECT id, source, target, cost, reverse_cost FROM edges$$' + ]::TEXT[]; + subs = ARRAY[ + 'NULL' + ]::TEXT[]; + + RETURN query SELECT * FROM no_crash_test('pgr_betweennessCentrality', params, subs); + +END +$BODY$ +LANGUAGE plpgsql VOLATILE; + + +SELECT * FROM no_crash(); + +SELECT finish(); +ROLLBACK; diff --git a/pgtap/metrics/betweennessCentrality/types_check.pg b/pgtap/metrics/betweennessCentrality/types_check.pg new file mode 100644 index 00000000000..032556df946 --- /dev/null +++ b/pgtap/metrics/betweennessCentrality/types_check.pg @@ -0,0 +1,62 @@ + +/*PGR-GNU***************************************************************** + +Copyright (c) 2024 pgRouting developers +Mail: project@pgrouting.org + +------ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + ********************************************************************PGR-GNU*/ +BEGIN; + +SELECT CASE WHEN NOT min_version('3.7.0') THEN plan(1) ELSE plan(5) END; + +CREATE OR REPLACE FUNCTION types_check() +RETURNS SETOF TEXT AS +$BODY$ +BEGIN + + IF NOT min_version('3.7.0') THEN + RETURN QUERY + SELECT skip(1, 'Function is new on 3.7.0'); + RETURN; + END IF; + +RETURN QUERY +SELECT has_function('pgr_betweennesscentrality'); +RETURN QUERY +SELECT has_function('pgr_betweennesscentrality', ARRAY['text','boolean']); +RETURN QUERY +SELECT function_returns('pgr_betweennesscentrality', ARRAY['text','boolean'],'setof record'); + +RETURN QUERY +SELECT set_eq( + $$SELECT proargnames from pg_proc where proname = 'pgr_betweennesscentrality'$$, + $$VALUES + ('{"","directed","vid","centrality"}'::TEXT[]) + $$); + +RETURN QUERY +SELECT set_eq( + $$SELECT proallargtypes from pg_proc where proname = 'pgr_betweennesscentrality'$$, + $$VALUES + ('{25,16,20,701}'::OID[]) + $$); + +END; +$BODY$ +LANGUAGE plpgsql; +SELECT types_check(); + +SELECT finish(); +ROLLBACK; diff --git a/sql/metrics/CMakeLists.txt b/sql/metrics/CMakeLists.txt new file mode 100644 index 00000000000..c047b395e5a --- /dev/null +++ b/sql/metrics/CMakeLists.txt @@ -0,0 +1,12 @@ + +SET(LOCAL_FILES + _betweennessCentrality.sql + betweennessCentrality.sql +) + +foreach (f ${LOCAL_FILES}) + configure_file(${f} ${f}) + list(APPEND PACKAGE_SQL_FILES ${CMAKE_CURRENT_BINARY_DIR}/${f}) +endforeach() + +set(PROJECT_SQL_FILES ${PROJECT_SQL_FILES} ${PACKAGE_SQL_FILES} PARENT_SCOPE) diff --git a/sql/metrics/_betweennessCentrality.sql b/sql/metrics/_betweennessCentrality.sql new file mode 100644 index 00000000000..4a8b7479c2b --- /dev/null +++ b/sql/metrics/_betweennessCentrality.sql @@ -0,0 +1,49 @@ +/*PGR-GNU***************************************************************** + +File: _betweennessCentrality.sql + +Template: +Copyright (c) 2015 pgRouting developers +Mail: project@pgrouting.org + +Function developer: +Copyright (c) 2024 Arun Thakur +Mail: bedupako12mas at gmail.com + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +-------------------------------- +-- pgr_betweennessCentrality +-------------------------------- + +--v3.7 +CREATE FUNCTION _pgr_betweennessCentrality( + edges_sql TEXT, + directed BOOLEAN, + + OUT vid BIGINT, + OUT centrality FLOAT) +RETURNS SETOF RECORD AS +'MODULE_PATHNAME' +LANGUAGE C VOLATILE STRICT; + +-- COMMENTS + +COMMENT ON FUNCTION _pgr_betweennessCentrality(TEXT, BOOLEAN) +IS 'pgRouting internal function'; diff --git a/sql/metrics/betweennessCentrality.sql b/sql/metrics/betweennessCentrality.sql new file mode 100644 index 00000000000..8da8d6916a4 --- /dev/null +++ b/sql/metrics/betweennessCentrality.sql @@ -0,0 +1,57 @@ +/*PGR-GNU***************************************************************** + +File: betweennessCentrality.sql + +Template: +Copyright (c) 2015 pgRouting developers +Mail: project@pgrouting.org + +Function developer: +Copyright (c) 2024 Arun Thakur +Mail: bedupako12mas at gmail.com + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +--v3.7 +CREATE FUNCTION pgr_betweennessCentrality( + TEXT, -- edges_sql (required) + directed BOOLEAN DEFAULT true, + + OUT vid BIGINT, + OUT centrality FLOAT) +RETURNS SETOF RECORD AS +$BODY$ + + SELECT vid, centrality + FROM _pgr_betweennesscentrality(_pgr_get_statement($1), $2); + +$BODY$ +LANGUAGE SQL VOLATILE STRICT; + +-- COMMENTS + +COMMENT ON FUNCTION pgr_betweennessCentrality(TEXT, BOOLEAN) +IS 'pgr_betweennessCentrality +- Parameters: + - edges SQL with columns: source, target, cost [,reverse_cost]) +- Optional Parameters: + - directed := true +- Documentation: + - ${PROJECT_DOC_LINK}/pgr_centrality.html +'; diff --git a/sql/sigs/pgrouting--3.7.sig b/sql/sigs/pgrouting--3.7.sig index 987a688ccfb..460e9e69d68 100644 --- a/sql/sigs/pgrouting--3.7.sig +++ b/sql/sigs/pgrouting--3.7.sig @@ -51,6 +51,8 @@ pgr_bellmanford(text,bigint,anyarray,boolean) pgr_bellmanford(text,bigint,bigint,boolean) pgr_bellmanford(text,text,boolean) _pgr_bellmanford(text,text,boolean,boolean) +_pgr_betweennesscentrality(text,boolean) +pgr_betweennesscentrality(text,boolean) _pgr_biconnectedcomponents(text) pgr_biconnectedcomponents(text) _pgr_binarybreadthfirstsearch(text,anyarray,anyarray,boolean) diff --git a/src/metrics/CMakeLists.txt b/src/metrics/CMakeLists.txt new file mode 100644 index 00000000000..c88a1dfd8eb --- /dev/null +++ b/src/metrics/CMakeLists.txt @@ -0,0 +1,4 @@ +ADD_LIBRARY(metrics OBJECT + betweennessCentrality.c + betweennessCentrality_driver.cpp + ) diff --git a/src/metrics/betweennessCentrality.c b/src/metrics/betweennessCentrality.c new file mode 100644 index 00000000000..bbebd4f3cd7 --- /dev/null +++ b/src/metrics/betweennessCentrality.c @@ -0,0 +1,136 @@ +/*PGR-GNU***************************************************************** +File: betweennessCentrality.c + +Generated with Template by: +Copyright (c) 2015 pgRouting developers +Mail: project@pgrouting.org + +Function's developer: +Copyright (c) 2024 Arun Thakur +Mail: bedupako12mas at gmail.com + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#include +#include "c_common/postgres_connection.h" + +#include "c_types/iid_t_rt.h" +#include "c_common/debug_macro.h" +#include "c_common/e_report.h" +#include "c_common/time_msg.h" + +#include "drivers/metrics/betweennessCentrality_driver.h" + +PGDLLEXPORT Datum _pgr_betweennesscentrality(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(_pgr_betweennesscentrality); + +static +void +process( + char* edges_sql, + bool directed, + IID_t_rt **result_tuples, + size_t *result_count) { + pgr_SPI_connect(); + char* log_msg = NULL; + char* notice_msg = NULL; + char* err_msg = NULL; + + clock_t start_t = clock(); + pgr_do_betweennessCentrality( + edges_sql, + directed, + result_tuples, + result_count, + &log_msg, + &err_msg); + time_msg(" processing Centrality", start_t, clock()); + + if (err_msg && (*result_tuples)) { + pfree(*result_tuples); + (*result_tuples) = NULL; + (*result_count) = 0; + } + + pgr_global_report(&log_msg, ¬ice_msg, &err_msg); + + pgr_SPI_finish(); +} + + +PGDLLEXPORT Datum +_pgr_betweennesscentrality(PG_FUNCTION_ARGS) { + FuncCallContext *funcctx; + TupleDesc tuple_desc; + + + IID_t_rt *result_tuples = NULL; + size_t result_count = 0; + + if (SRF_IS_FIRSTCALL()) { + MemoryContext oldcontext; + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + + process( + text_to_cstring(PG_GETARG_TEXT_P(0)), + PG_GETARG_BOOL(1), + &result_tuples, + &result_count); + + funcctx->max_calls = result_count; + funcctx->user_fctx = result_tuples; + if (get_call_result_type(fcinfo, NULL, &tuple_desc) + != TYPEFUNC_COMPOSITE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context " + "that cannot accept type record"))); + + funcctx->tuple_desc = tuple_desc; + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + tuple_desc = funcctx->tuple_desc; + result_tuples = (IID_t_rt*) funcctx->user_fctx; + + if (funcctx->call_cntr < funcctx->max_calls) { + HeapTuple tuple; + Datum result; + Datum *values; + bool* nulls; + + values = palloc(2 * sizeof(Datum)); + nulls = palloc(2 * sizeof(bool)); + + // postgres starts counting from 1 + values[0] = Int64GetDatum(result_tuples[funcctx->call_cntr].from_vid); + nulls[0] = false; + values[1] = Float8GetDatum(result_tuples[funcctx->call_cntr].cost); + nulls[1] = false; + + tuple = heap_form_tuple(tuple_desc, values, nulls); + result = HeapTupleGetDatum(tuple); + SRF_RETURN_NEXT(funcctx, result); + } else { + SRF_RETURN_DONE(funcctx); + } +} diff --git a/src/metrics/betweennessCentrality_driver.cpp b/src/metrics/betweennessCentrality_driver.cpp new file mode 100644 index 00000000000..6c6a1aa1ce2 --- /dev/null +++ b/src/metrics/betweennessCentrality_driver.cpp @@ -0,0 +1,118 @@ +/*PGR-GNU***************************************************************** +File: betweennessCentrality_driver.cpp + +Generated with Template by: +Copyright (c) 2015 pgRouting developers +Mail: project@pgrouting.org + +Function's developer: +Copyright (c) 2024 Arun Thakur +Mail: bedupako12mas at gmail.com + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#include "drivers/metrics/betweennessCentrality_driver.h" + +#include +#include +#include +#include +#include "metrics/betweennessCentrality.hpp" +#include "cpp_common/pgdata_getters.hpp" + +#include "cpp_common/pgr_assert.hpp" + + +void +pgr_do_betweennessCentrality( + char *edges_sql, + bool directed, + + IID_t_rt **return_tuples, + size_t *return_count, + char ** log_msg, + char ** err_msg) { + using pgrouting::pgr_msg; + using pgrouting::pgr_free; + + std::ostringstream log; + std::ostringstream err; + char *hint = nullptr; + + try { + pgassert(!(*log_msg)); + pgassert(!(*err_msg)); + pgassert(!(*return_tuples)); + pgassert(*return_count == 0); + + hint = edges_sql; + auto edges = pgrouting::pgget::get_edges(std::string(edges_sql), true, true); + + if (edges.empty()) { + throw std::string("No edges found"); + } + hint = nullptr; + + if (directed) { + log << "Processing Directed graph\n"; + pgrouting::DirectedGraph digraph; + digraph.insert_edges(edges); + pgr_betweennesscentrality(digraph, *return_count, return_tuples); + } else { + log << "Processing Undirected graph\n"; + pgrouting::UndirectedGraph undigraph; + undigraph.insert_edges(edges); + pgr_betweennesscentrality(undigraph, *return_count, return_tuples); + } + + + if (*return_count == 0) { + err << "No result generated, report this error\n"; + *err_msg = pgr_msg(err.str().c_str()); + *return_tuples = NULL; + *return_count = 0; + return; + } + + *log_msg = log.str().empty()? + *log_msg : + pgr_msg(log.str().c_str()); + } catch (AssertFailedException &except) { + (*return_tuples) = pgr_free(*return_tuples); + (*return_count) = 0; + err << except.what(); + *err_msg = pgr_msg(err.str().c_str()); + *log_msg = pgr_msg(log.str().c_str()); + } catch (const std::string &ex) { + *err_msg = pgr_msg(ex.c_str()); + *log_msg = hint? pgr_msg(hint) : pgr_msg(log.str().c_str()); + } catch (std::exception &except) { + (*return_tuples) = pgr_free(*return_tuples); + (*return_count) = 0; + err << except.what(); + *err_msg = pgr_msg(err.str().c_str()); + *log_msg = pgr_msg(log.str().c_str()); + } catch(...) { + (*return_tuples) = pgr_free(*return_tuples); + (*return_count) = 0; + err << "Caught unknown exception!"; + *err_msg = pgr_msg(err.str().c_str()); + *log_msg = pgr_msg(log.str().c_str()); + } +}