hglib
hglib - Hypergraph library

What is hglib?

hglib is an open-source C++ library to manipulate hypergraphs. A hypergraph is a generalization of a graph where an edge can join any number of vertices.

hglib allows to associate user defined properties to vertices, hyperedges/hyperarcs and to the hypergraph itself. Thus, it can be used in a wide range of problems arising e.g. in operations research, computer science, and computational biology.

Download

hglib is available for download from source only, see the "Git repository" section below

Git Repository

The git repository of the hglib library can be found at https://gitlab.inria.fr/kirikomics/hglib.git.

Type the following on the command line to get a copy of the repository:

git clone https://gitlab.inria.fr/kirikomics/hglib.git

Documentation

The documentation is available at https://kirikomics.gitlabpages.inria.fr/hglib/

Development

You can find information concerning the development of the hglib library in doc/devGuide.md

Licensing

hglib is distributed under the GNU AGPLv3 license. Please see the LICENSE file for more details.

Contacts

How to use hglib?

By default a directed hypergraph contains vertices of type DirectedVertex and hyperarcs of type DirectedHyperedge. A directed hypergraph can be declared as follows:

#include "path_to_hglib/hglib.h"
#include <iostream>

int main( int argc, const char* argv[] ) {
  hglib::DirectedHypergraph<> dhg;
  // Add vertices with given name
  dhg.addVertex("A");  // vertex id = 0
  dhg.addVertex("B");  // vertex id = 1
  dhg.addVertex("Foo");  // vertex id = 2
  dhg.addVertex("Bar");  // vertex id = 3
  dhg.addVertex("");  // vertex id = 4
  dhg.addVertex("6");  // vertex id = 5
  // Add vertices with default name
  dhg.addVertex();  // vertex id = 6
  dhg.addVertex();  // vertex id = 7

  // add hyperarcs using the vertex names
  dhg.addHyperarc(hglib::NAME, {{"A"}, {"Foo"}});
  dhg.addHyperarc(hglib::NAME, {{"A", "B"}, {"Bar", ""}});
  dhg.addHyperarc(hglib::NAME, {{"6"}, {""}});
  // add hyperarcs using the vertex ids
  dhg.addHyperarc({{0, 1}, {2, 3}});
  dhg.addHyperarc({{3}, {6, 7}});

  // print the directed hypergraph
  std::cout << dhg << std::endl;
  return 0;
}

In the following code example we declare a directed hypergraph using named directed hyperedges. It is also shown how to iterate over the vertices and hyperarcs of the hypergraph.

#include "path_to_hglib/hglib.h"

int main( int argc, const char* argv[] ) {
    typedef hglib::DirectedHypergraph<hglib::DirectedVertex, hglib::NamedDirectedHyperedge> hypergraph;
    hypergraph dhg;
    // Add vertices with given name
    dhg.addVertex("A");  // vertex id = 0
    dhg.addVertex("B");  // vertex id = 1
    dhg.addVertex("Foo");  // vertex id = 2
    dhg.addVertex("Bar");  // vertex id = 3
    dhg.addVertex("");  // vertex id = 4
    dhg.addVertex("6");  // vertex id = 5
    // Add vertices with default name
    dhg.addVertex();  // vertex id = 6
    dhg.addVertex();  // vertex id = 7

    // add hyperarcs using the vertex names
    dhg.addHyperarc(hglib::NAME, {{"A"}, {"Foo"}}, {"hyperarc1"});
    dhg.addHyperarc(hglib::NAME, {{"A", "B"}, {"Bar", ""}}, {"hyperarc2"});
    dhg.addHyperarc(hglib::NAME, {{"6"}, {""}}, {"hyperarc3"});
    // add hyperarcs using the vertex ids
    dhg.addHyperarc({{0, 1}, {2, 3}}, {"hyperarc4"});
    dhg.addHyperarc({{3}, {6, 7}}, {"hyperarc5"});


    // Iterating over the vertices of the hypergraph...
    // ... with range based for loop
    for (const auto& vertex : dhg.vertices()) {
      std::cout << "Vertex: " << vertex->name() << '\n';
    }
    // ... with iterators I
    for (auto it = dhg.verticesBegin(); it != dhg.verticesEnd(); ++it) {
      std::cout << "Vertex: " << (*it)->name() << '\n';
    }
    // ... with iterators II
    hypergraph::vertex_iterator itVertex, endVertices;
    std::tie(itVertex, endVertices) = dhg.verticesBeginEnd();
    for(; itVertex != endVertices; ++itVertex) {
      std::cout << "Vertex: " << (*itVertex)->name() << '\n';
    }


   // Iterating over the hyperarcs of the hypergraph...
   // ... with range based for loop
   for (const auto& hyperarc : dhg.hyperarcs()) {
     std::cout << "Hyperarc: " << hyperarc->name() << '\n';
   }
   // ... with iterators I
   for (auto it = dhg.hyperarcsBegin(); it != dhg.hyperarcsEnd(); ++it) {
     std::cout << "Hyperarc: " << (*it)->name() << '\n';
   }
   // ... with iterators II
   hypergraph::hyperarc_iterator itHyperarc, endHyperarcs;
   std::tie(itHyperarc, endHyperarcs) = dhg.hyperarcsBeginEnd();
   for(; itHyperarc != endHyperarcs; ++itHyperarc) {
     std::cout << "Hyperarc: " << (*itHyperarc)->name() << '\n';
   }

   // Iterating over the in-/out-arcs of a vertex...
   for (const auto& vertex : dhg.vertices()) {
     const auto& vertexId = vertex->id();
     // Get in-degree
     size_t inDegree = dhg.inDegree(vertexId);
     // Iterate over in-hyperarcs
     // ... with iterators I
     for (auto it = dhg.inHyperarcsBegin(vertexId); it != dhg.inHyperarcsEnd(vertexId); ++it) {
       std::cout << "In-arc: " << (*it)->name() << '\n';
     }
     // ... with iterators II
     hypergraph::hyperarc_iterator it, end;
     std::tie(it, end) = dhg.inHyperarcsBeginEnd(vertexId);
     for (; it != end; ++it) {
       std::cout << "In-arc: " << (*it)->name() << '\n';
     }
     /// ... with range based for loop
     auto inHyperarcsPtr = dhg.inHyperarcs(vertexId);
     for (const auto& arc : * inHyperarcsPtr) {
       std::cout << "In-arc: " << arc->name() << '\n';
     }

     // Get out-degree
     size_t outDegree = dhg.outDegree(vertexId);
     // Iterate over out-hyperarcs
     // ... with iterators I
     for (auto it = dhg.outHyperarcsBegin(vertexId); it != dhg.outHyperarcsEnd(vertexId); ++it) {
       std::cout << "Out-arc: " << (*it)->name() << '\n';
     }
     // ... with iterators II
     std::tie(it, end) = dhg.outHyperarcsBeginEnd(vertexId);
     for (; it != end; ++it) {
       std::cout << "Out-arc: " << (*it)->name() << '\n';
     }
     /// ... with range based for loop
     auto outHyperarcsPtr = dhg.outHyperarcs(vertexId);
     for (const auto& arc : *outHyperarcsPtr) {
       std::cout << "Out-arc: " << arc->name() << '\n';
     }
   }

   // Iterate over tails/heads of a given hyperarc
   for (const auto& hyperarc : dhg.hyperarcs()) {
     const auto& arcId = hyperarc->id();
     // Get number of tail vertices
     size_t nbTails = dhg.nbTailVertices(arcId);
     // Iterate over tails...
     // ... with iterators I
     for (auto it = dhg.tailsBegin(arcId); it != dhg.tailsEnd(arcId); ++it) {
       std::cout << "Tail: " << (*it)->name() << '\n';
     }
     // ... with iterators II
     hypergraph::vertex_iterator it, end;
     std::tie(it, end) = dhg.tailsBeginEnd(arcId);
     for (; it != end; ++it) {
       std::cout << "Tail: " << (*it)->name() << '\n';
     }
     /// ... with range based for loop
     auto tailsPtr = dhg.tails(arcId);
     for (const auto& tail : *tailsPtr) {
       std::cout << "Tail: " << tail->name() << '\n';
     }

     // Get number of head vertices
    size_t nbHeads = dhg.nbHeadVertices(arcId);
    // Iterate over heads...
    // ... with iterators I
    for (auto it = dhg.headsBegin(arcId); it != dhg.headsEnd(arcId); ++it) {
      std::cout << "Head: " << (*it)->name() << '\n';
    }
    // ... with iterators II
    std::tie(it, end) = dhg.headsBeginEnd(arcId);
    for (; it != end; ++it) {
      std::cout << "Head: " << (*it)->name() << '\n';
    }
    /// ... with range based for loop
    auto headsPtr = dhg.heads(arcId);
    for (const auto& head : *headsPtr) {
      std::cout << "Head: " << head->name() << '\n';
    }
  }
  return 0;
}

To assign properties to the vertices, hyperarcs, and the hypergraph, you can define a directed hypergraph as in the following code snippet where we create a directed hypergraph of six vertices and three hyperarcs (hyperarc1: A -> Foo, hyperarc2: A + B -> Bar + '', hyperarc3: 6 -> ''). To each vertex and hyperarc a property (struct with optional default values) is associated. The vertex property is composed of a name, and a colour. The hyperarc property contains a name, a weight, and a capacity. Furthermore, the properties called 'name', and 'colour' are associated to the hypergraph object (struct HypergraphProperty).

#include "path_to_hglib/hglib.h"
#include <iostream>

int main( int argc, const char* argv[] ) {
  enum Colour {red, blue, green};
  struct VertexProperty {
    std::string name = "Default";
    Colour colour = Colour::green;
  };
  struct HyperarcProperty {
    std::string name = "Default";
    double weight = 0.0;
    double capacity = 0.0;
  };
  struct HypergraphProperty {
    std::string name;
    Colour colour = Colour::red;
  };

  // DirectedHypergraph is a class template with five parameters:
  // 1. Vertex type (default DirectedVertex)
  // 2. Hyperarc type (default Hyperarc)
  // 3. Vertex property (optional)
  // 4. Hyperarc property (optional)
  // 5. Graph property (optional)
  hglib::DirectedHypergraph<hglib::DirectedVertex, hglib::NamedDirectedHyperedge, VertexProperty, HyperarcProperty, HypergraphProperty> dhg;
  // Add vertices
  dhg.addVertex("A");
  dhg.addVertex("B");
  dhg.addVertex("Foo");
  dhg.addVertex("Bar");
  dhg.addVertex("");
  dhg.addVertex("6");

  // add hyperarcs
  dhg.addHyperarc(hglib::NAME, {{"A"}, {"Foo"}}, {"hyperarc1"});
  dhg.addHyperarc(hglib::NAME, {{"A", "B"}, {"Bar", ""}}, {"hyperarc2"});
  dhg.addHyperarc(hglib::NAME, {{"6"}, {""}}, {"hyperarc3"});

  // Access to vertex properties
  for (const auto& vertex : dhg.vertices()) {
    // Read access
    const auto vProp = dhg.getVertexProperties(vertex->id());
    std::cout << "Colour of vertex " << vertex->name() << ": " << vProp->colour << '\n';
    // Write access to properties (Notice the '_' at the end of the function name)
    auto vProp2 = dhg.getVertexProperties_(vertex->id());
    vProp2->name = "Changed from default";
    vProp2->colour = Colour::red;
  }

  // Access to hyperarc properties
  for (const auto& hyperarc : dhg.hyperarcs()) {
    // Read access
    const auto aProp = dhg.getHyperarcProperties(hyperarc->id());
    std::cout << "Capacity of hyperarc " << hyperarc->name() << ": " << aProp->capacity << '\n';
    // Write access to properties (Notice the '_' at the end of the function name)
    auto aProp2 = dhg.getHyperarcProperties_(hyperarc->id());
    aProp2->weight = 1.0;
    aProp2->capacity = 2.0;
  }

  // Read/Write access to hypergraph property
  const auto hypergraphProp = dhg.getHypergraphProperties();
  std::cout << "Colour of hypergraph: " << hypergraphProp->colour << '\n';
  auto hypergraphProp2 = dhg.getHypergraphProperties_();
  hypergraphProp2->colour = Colour::blue;

  return 0;
}

A subgraph can be built providing a set of whitelisted vertices/hyperarcs:

#include "path_to_hglib/hglib.h"

int main( int argc, const char* argv[] ) {
  hglib::DirectedHypergraph<> dhg;
  for (auto vertexName : vertices) {
    dhg.addVertex(vertexName);
  }
  // Add hyperarcs
  for (auto hyperarc : hyperarcs) {
    dhg.addHyperarc(hglib::NAME, hyperarc.first);
  }

  // The subgraph contains the vertices with the ids 1, 2, and 3.
  // The subgraph contains the hyperarcs with the ids 1, and 2.
  std::unordered_set<hglib::VertexIdType> whitelistedVertices = {1, 2, 3};
  std::unordered_set<hglib::HyperedgeIdType> whitelistedHyperarcs = {1, 2};
  // a unique_ptr is returned
  auto subgraph = hglib::createDirectedSubHypergraph(&dhg, whitelistedVertices, whitelistedHyperarcs);

  // Iterate over vertices
  for (const auto& vertex : dhg.vertices()) {
    std::cout << "Vertex " << vertex->id() << " is in subgraph: " << (subgraph->vertexById(vertex->id()) == nullptr ? "no" : "yes") << '\n';
  }

  // Iterate over hyperarcs
  for (const auto& hyperarc : dhg.hyperarcs()) {
    std::cout << "Hyperarc " << hyperarc->id() << " is in subgraph: " << (subgraph->hyperarcById(hyperarc->id()) == nullptr ? "no" : "yes") << '\n';
  }
  // The above statements should print
  // Vertex 0 is in subgraph:no
  // Vertex 1 is in subgraph:yes
  // Vertex 2 is in subgraph:yes
  // Vertex 3 is in subgraph:yes
  // Vertex 4 is in subgraph:no
  // Vertex 5 is in subgraph:no
  // Hyperarc 0 is in subgraph: no
  // Hyperarc 1 is in subgraph: yes
  // Hyperarc 2 is in subgraph: yes

  return 0;
}

The library can be used in a similar way for undirected hypergraphs:

#include "path_to_hglib/hglib.h"

int main( int argc, const char* argv[] ) {
  hglib::UndirectedHypergraph<hglib::UndirectedVertex, hglib::NamedUndirectedHyperedge> uhg;
  // Add vertices
  uhg.addVertex("A");
  uhg.addVertex("B");
  uhg.addVertex("Foo");
  uhg.addVertex("Bar");
  uhg.addVertex("");
  uhg.addVertex("6");

  // add named undirected hyperedges
  uhg.addHyperedge(hglib::NAME, {"A", "Foo"}, "hyperedge1");
  uhg.addHyperedge(hglib::NAME, {"A", "B", "Bar", ""}, "hyperedge2");
  uhg.addHyperedge(hglib::NAME, {"6", ""}, "hyperedge3");

  // Iterate over vertices
  for (const auto& vertex : uhg.vertices()) {
    std::cout << "Vertex: " << vertex->name() << '\n';
    // Iterate over implied hyperedges
    auto it = uhg.hyperedgesBegin(vertex->id());
    auto end = uhg.hyperedgesEnd(vertex->id());
    for (; it != end; ++it) {
      std::cout << "Is contained in hyperedge: " << (*it)->name() << '\n';
    }
    // with range based for loop
    auto impliedHyperedgesPtr = uhg.hyperedges(vertex->id());
    for (const auto& edge : *impliedHyperedgesPtr) {
        std::cout << "Is contained in hyperedge: " << edge->name() << '\n';
    }
  }

  // Iterate over hyperedges
  for (const auto& hyperedge : uhg.hyperedges()) {
    std::cout << "Hyperedge: " << hyperedge->name() << '\n';
    // Iterate over implied vertices
    auto it = uhg.impliedVerticesBegin(hyperedge->id());
    auto end = uhg.impliedVerticesEnd(hyperedge->id());
    for (; it != end; ++it) {
      std::cout << "Contains vertex: " << (*it)->name() << '\n';
    }
    // with range based for loop
    auto impliedVerticesPtr = uhg.impliedVertices(hyperedge->id());
    for (const auto& vertex : *impliedVerticesPtr) {
        std::cout << "Contains vertex: " << vertex->name() << '\n';
    }
  }

  return 0;
}