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

ISSUE 87: vf2 algorithm refactor #185

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
88e416f
typo in graph.h on a comment about method description, I am assuming …
Sep 4, 2023
961c455
working version of the algorithm
Oct 24, 2023
51c20a2
error in interpreting the core sets for the final mapping, fixed it
Oct 24, 2023
ac10f06
added comments to the header file, need to finish adding comments for…
Oct 25, 2023
6f62549
refactoring code
Nov 3, 2023
7c41e14
added comments and formatted code according to clang-format style Google
Nov 4, 2023
6276f24
Delete include/graaflib/algorithm/.graph_isomorphism.h.swp
sracha4355 Nov 8, 2023
f8d7978
created vf2_mapping and vf2_target_sets classes for the refactor
Dec 24, 2023
208b5b1
finished translating code into classes and putting the classes togeth…
Dec 30, 2023
d31bd22
dummy commit so I can safely switch to prev branch
Dec 30, 2023
6cab701
added inital check for isomorphism function, without it large graphs …
Dec 31, 2023
b2196fc
updated tests
Jan 10, 2024
71e91f9
made newly requested pr changes
Jan 16, 2024
8169a25
Merge remote-tracking branch 'upstream/main' into vf2_refactor
Jan 16, 2024
8366090
added pr changes and created folders to mimic the wanted fil structure
Jan 16, 2024
4bc5d2c
Delete include/graaflib/algorithm/graph_isomorphism.h
sracha4355 Jan 16, 2024
019c399
Delete include/graaflib/algorithm/graph_isomorphism.tpp
sracha4355 Jan 16, 2024
03c1676
Delete include/graaflib/algorithm/graph_isomorphism_util.h
sracha4355 Jan 16, 2024
667673f
Delete include/graaflib/algorithm/graph_isomorphism_util.tpp
sracha4355 Jan 16, 2024
be8b0a9
Delete test/graaflib/algorithm/graph_isomorphism.cpp
sracha4355 Jan 16, 2024
2a876bc
Delete test/graaflib/algorithm/graph_isomorphism_test.cpp
sracha4355 Jan 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions include/graaflib/algorithm/graph_isomorphism.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <graaflib/graph.h>
#include <graaflib/types.h>
#include <graaflib/algorithm/graph_isomorphism_util.h>

#include <algorithm>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>

// Delete later
#include <iostream>


namespace graaf::algorithm::vf2 {
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved

/**
* @brief Checks if two graphs are isomorphic.
*
* This function checks whether two given graphs are isomorphic, meaning
* they are structurally identical up to vertex and edge relabeling.
* If the graphs are isomorphic, the function returns an optional containing
* a mapping from the vertices of the first graph to the vertices of the second
* graph. If the graphs are not isomorphic, the function returns std::nullopt.
*
* @tparam V The vertex type of the graphs.
* @tparam E The edge type of the graphs.
* @tparam T The graph type of the graphs (directed or undirected).
* @param lhs The first graph to compare.
* @param rhs The second graph to compare.
* @return An optional containing a mapping from the vertices of lhs to those of
* rhs if isomorphic; otherwise std::nullopt.
*/

template <typename V, typename E, graph_type T>
std::optional<std::unordered_map<vertex_id_t, vertex_id_t>> check_isomorphism(
const graph<V, E, T>& lhs, const graph<V, E, T>& rhs);
} // namespace graaf::algorithm
#include "graph_isomorphism.tpp"
61 changes: 61 additions & 0 deletions include/graaflib/algorithm/graph_isomorphism.tpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace graaf::algorithm::vf2{


template <typename V, typename E, graph_type T>
bool check_for_possibility_of_isomorphism(const graph<V,E,T>& lhs, const graph<V,E,T>& rhs){
if (lhs.vertex_count() != rhs.vertex_count() ||
lhs.edge_count() != rhs.edge_count())
return false;
std::vector<std::size_t> lhs_node_degrees, rhs_node_degrees;
for (const auto& vertex : lhs.get_vertices()) {
std::size_t number_of_conn = lhs.get_neighbors(vertex.first).size();
lhs_node_degrees.push_back(number_of_conn);
}
for (const auto& vertex : rhs.get_vertices()) {
std::size_t number_of_conn = rhs.get_neighbors(vertex.first).size();
rhs_node_degrees.push_back(number_of_conn);
}
std::sort(lhs_node_degrees.begin(), lhs_node_degrees.end());
std::sort(rhs_node_degrees.begin(), rhs_node_degrees.end());
for (int i = 0; i < lhs_node_degrees.size(); i++)
if (lhs_node_degrees[i] != rhs_node_degrees[i]) return false;
return true;
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved
}


template <typename V, typename E, graph_type T>
std::optional<std::unordered_map<vertex_id_t, vertex_id_t>> check_isomorphism(
const graph<V, E, T>& graph1,
const graph<V, E, T>& graph2
){
if(!check_for_possibility_of_isomorphism(graph1, graph2)) return std::nullopt;


std::unique_ptr<vf2_information<V,E,T>> state = std::make_unique<vf2_information<V,E,T>>(graph1, graph2);
std::cout << "FOR DEBUGGING Listing the values of the vertices" << std::endl;
std::cout << "graph 1" << std::endl << "format actual val: mapping val " << std::endl;
for(const auto&[id, mapped_id] : state -> mapper -> graph1_mapping){
std::cout << id << ":" << mapped_id << std::endl;
}

std::cout << "graph 2" << std::endl << "format actual val: mapping val " << std::endl;
for(const auto&[id, mapped_id] : state -> mapper -> graph2_mapping){
std::cout << id << ":" << mapped_id << std::endl;
}


vf2_isomorphism_feasibility_checker<V,E,T> checker;
bool valid_mapping_found = check_isomorphism(graph1, graph2, state, checker, 0);
if(valid_mapping_found){
vertex_mapping isomorphic_mapping;
for(int i = 0; i < graph1.vertex_count(); i++){
isomorphic_mapping = state -> sets -> generate_final_mapping(*(state -> mapper));
}
return isomorphic_mapping;
}
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved
return std::nullopt;

Check warning on line 56 in include/graaflib/algorithm/graph_isomorphism.tpp

View check run for this annotation

Codecov / codecov/patch

include/graaflib/algorithm/graph_isomorphism.tpp#L56

Added line #L56 was not covered by tests
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved

}


}
165 changes: 165 additions & 0 deletions include/graaflib/algorithm/graph_isomorphism_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#pragma once

#include <graaflib/graph.h>
#include <graaflib/types.h>
#include <cstddef>
#include <memory>
#include <optional>

// Delete later
#include <iostream>

/**
HELPER CLASSES
*/

namespace graaf::algorithm::vf2{

struct predecessors_and_successors_of_vertex {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this name is quite verbose. What about vertex_neighbors?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand it is verbose, but for someone who has an idea of how the algorithm works, this name is pretty descriptive and says exactly what the struct is for. I would like to keep this as the name, if you would allow it.

std::unordered_set<vertex_id_t> predecessors;
std::unordered_set<vertex_id_t> successors;
};

using vertex_mapping = std::unordered_map<vertex_id_t, vertex_id_t>;

enum class WhichGraph {
GRAPH_1,
GRAPH_2
};

template <typename V, typename E, graph_type T>
class vf2_isomorphism_feasibility_checker;


// maps vertices in a graph to a value between [0,N] where N = number of vertices in a graph
template <typename V, typename E, graph_type T>
class vf2_vertex_id_mapper {
public:
vf2_vertex_id_mapper(const graph<V, E, T>& _graph1, const graph<V, E, T>& _graph2);
std::unordered_set<vertex_id_t> get_predecessors(vertex_id_t, WhichGraph);
std::unordered_set<vertex_id_t> get_successors(vertex_id_t, WhichGraph);
friend class vf2_target_sets;

// move to private later
vertex_mapping graph1_mapping;
vertex_mapping graph1_reverse_mapping;
vertex_mapping graph2_mapping;
vertex_mapping graph2_reverse_mapping;

private:
void create_mapping(vertex_mapping&, const graph<V, E, T>& graph);
void create_reverse_mapping(vertex_mapping& map, vertex_mapping& reverse_map);

const graph<V, E, T>* graph1;
const graph<V, E, T>* graph2;


};

using target_in = std::vector<int>; // index corresponds to a node in the graph
using target_out = std::vector<int>; // index corresponds to a node in the graph
using target = std::vector<int>;
using graph1_to_graph2_mapping = std::vector<vertex_id_t>;
using graph2_to_graph1_mapping = std::vector<vertex_id_t>;
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved

class vf2_target_sets{
public:
vf2_target_sets(size_t, size_t);
void update_mappings(vertex_id_t, vertex_id_t, int);
void restore_mappings(vertex_id_t, vertex_id_t, int);
void update_target_sets(const predecessors_and_successors_of_vertex&, const predecessors_and_successors_of_vertex&, int);
void restore_target_sets(int);
std::vector<std::pair<vertex_id_t, vertex_id_t>> generate_potential_vertex_pairings();
size_t get_tin_1_length();
size_t get_tin_2_length();
size_t get_tout_1_length();
size_t get_tout_2_length();
size_t get_core_1_length();
size_t get_core_2_length();

template <typename V, typename E, graph_type T>
vertex_mapping generate_final_mapping(const vf2_vertex_id_mapper<V,E,T>&);

template <typename V, typename E, graph_type T>
friend class vf2_isomorphism_feasibility_checker;

private:
void update_target_set(const std::unordered_set<vertex_id_t>&, target&, int, size_t&);
void restore_target_set(target&, int, size_t&);

target_in tin_1;
target_in tin_2;
target_out tout_1;
target_out tout_2;
graph1_to_graph2_mapping core_1;
graph2_to_graph1_mapping core_2;

size_t tin_1_length = 0;
size_t tin_2_length = 0;
size_t tout_1_length = 0;
size_t tout_2_length = 0;
size_t core_1_length = 0;
size_t core_2_length = 0;
};

// Utility Functions
std::unordered_set<vertex_id_t> intersection_set(const std::unordered_set<vertex_id_t>& set_A, const std::unordered_set<vertex_id_t>& set_B) {
std::unordered_set<vertex_id_t> intersection;
for (const auto& element : set_A) {
if (set_B.count(element) > 0) intersection.insert(element);
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved
}
return std::move(intersection);
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved
}

template <typename V, typename E, graph_type T>
struct vf2_information{
std::unique_ptr<vf2_target_sets> sets;
std::unique_ptr<vf2_vertex_id_mapper<V, E, T>> mapper;

vf2_information(const graph<V, E, T>& graph1, const graph<V, E, T>& graph2){
sets = std::make_unique<vf2_target_sets>(graph1.vertex_count(), graph2.vertex_count());
mapper = std::make_unique<vf2_vertex_id_mapper<V,E,T>>(graph1, graph2);
}
};

template <typename V, typename E, graph_type T>
class vf2_isomorphism_feasibility_checker {
public:
bool checkFeasibility(const std::unique_ptr<vf2_information<V,E,T>>&, const std::pair<vertex_id_t, vertex_id_t>&) const ;

private:
bool predecessors_consistency_rule(
const std::unique_ptr<vf2_information<V,E,T>>&,
const struct predecessors_and_successors_of_vertex&,
const struct predecessors_and_successors_of_vertex&
sracha4355 marked this conversation as resolved.
Show resolved Hide resolved
) const;
bool successors_consistency_rule(
const std::unique_ptr<vf2_information<V,E,T>>&,
const struct predecessors_and_successors_of_vertex&,
const struct predecessors_and_successors_of_vertex&
) const;
bool lookahead_tin_rule(
const std::unique_ptr<vf2_information<V,E,T>>&,
const struct predecessors_and_successors_of_vertex&,
const struct predecessors_and_successors_of_vertex&
) const;
bool lookahead_tout_rule(
const std::unique_ptr<vf2_information<V,E,T>>&,
const struct predecessors_and_successors_of_vertex&,
const struct predecessors_and_successors_of_vertex&
) const;
bool lookahead_new_rule(
const std::unique_ptr<vf2_information<V,E,T>>&,
const struct predecessors_and_successors_of_vertex&,
const struct predecessors_and_successors_of_vertex&
) const;
};

template <typename V, typename E, graph_type T>
bool check_isomorphism(const graph<V, E, T>& graph1, const graph<V, E, T>& graph2, const std::unique_ptr<vf2_information<V,E,T>>& state, const vf2_isomorphism_feasibility_checker<V,E,T>&, size_t depth);
}//namespace graaf::algorithm::vf2


#include "graph_isomorphism_util.tpp"


Loading
Loading