From c0b766c86e7bbef18ce8b5ee756e4565cad757bf Mon Sep 17 00:00:00 2001 From: Jun Kawahara Date: Sat, 14 Dec 2024 23:27:14 +0900 Subject: [PATCH] Add files for digraphs --- src/graphillion/digraphs/Digraph.hpp | 664 ++++++++++++++++++ src/graphillion/digraphs/FrontierData.hpp | 34 + .../digraphs/FrontierDegreeSpecified.hpp | 180 +++++ .../FrontierDirectedHamiltonianCycle.hpp | 245 +++++++ .../digraphs/FrontierDirectedSTPath.hpp | 295 ++++++++ .../digraphs/FrontierDirectedSingleCycle.hpp | 238 +++++++ src/graphillion/digraphs/FrontierManager.hpp | 264 +++++++ .../digraphs/FrontierRootedForest.hpp | 222 ++++++ .../digraphs/FrontierRootedTree.hpp | 289 ++++++++ 9 files changed, 2431 insertions(+) create mode 100644 src/graphillion/digraphs/Digraph.hpp create mode 100644 src/graphillion/digraphs/FrontierData.hpp create mode 100644 src/graphillion/digraphs/FrontierDegreeSpecified.hpp create mode 100644 src/graphillion/digraphs/FrontierDirectedHamiltonianCycle.hpp create mode 100644 src/graphillion/digraphs/FrontierDirectedSTPath.hpp create mode 100644 src/graphillion/digraphs/FrontierDirectedSingleCycle.hpp create mode 100644 src/graphillion/digraphs/FrontierManager.hpp create mode 100644 src/graphillion/digraphs/FrontierRootedForest.hpp create mode 100644 src/graphillion/digraphs/FrontierRootedTree.hpp diff --git a/src/graphillion/digraphs/Digraph.hpp b/src/graphillion/digraphs/Digraph.hpp new file mode 100644 index 0000000..57428a5 --- /dev/null +++ b/src/graphillion/digraphs/Digraph.hpp @@ -0,0 +1,664 @@ +/* + * TdZdd: a Top-down/Breadth-first Decision Diagram Manipulation Framework + * by Hiroaki Iwashita + * Copyright (c) 2014 ERATO MINATO Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "subsetting/util/MessageHandler.hpp" + +namespace graphillion { + +class Digraph { + public: + typedef int VertexNumber; + typedef int EdgeNumber; + typedef int ColorNumber; + typedef std::pair VertexNumberPair; + + struct EdgeInfo { + VertexNumber v0; + VertexNumber v1; + VertexNumber v2; + bool v1final; + bool v2final; + bool v1final2; + bool v2final2; + bool allColorsSeen; + bool finalEdge; + + EdgeInfo(VertexNumber v1, VertexNumber v2) + : v0(0), + v1(v1), + v2(v2), + v1final(false), + v2final(false), + v1final2(false), + v2final2(false), + allColorsSeen(false), + finalEdge(false) {} + + friend std::ostream& operator<<(std::ostream& os, EdgeInfo const& o) { + os << o.v0 << "--" << o.v1; + if (o.v1final) os << "$"; + if (o.v1final2) os << "-"; + os << "--" << o.v2; + if (o.v2final) os << "$"; + if (o.v2final2) os << "-"; + if (o.allColorsSeen) os << "*"; + if (o.finalEdge) os << "$"; + return os; + } + }; + + static VertexNumber const MAX_VERTICES = USHRT_MAX; + static EdgeNumber const MAX_EDGES = INT_MAX; + static ColorNumber const MAX_COLORS = USHRT_MAX; + + private: + std::vector > edgeNames; + std::map name2label; + std::map name2color; + std::map name2vertex; + std::vector vertex2name; + std::map, EdgeNumber> name2edge; + std::vector > edge2name; + std::vector edgeInfo_; + std::map edgeIndex; + std::vector virtualMate_; + std::vector colorNumber_; + VertexNumber vMax; + ColorNumber numColor_; + bool hasColorPairs_; + + public: + void addEdge(std::string vertexName1, std::string vertexName2) { + edgeNames.push_back(std::make_pair(vertexName1, vertexName2)); + } + + void setColor(std::string v, std::string color) { name2color[v] = color; } + + void setColor(std::string v, int color) { name2color[v] = getColor(color); } + + void readEdges(std::string const& filename) { + tdzdd::MessageHandler mh; + mh.begin("reading"); + + if (filename.empty()) { + mh << " STDIN ..."; + readEdges(std::cin); + } else { + mh << " \"" << filename << "\" ..."; + std::ifstream fin(filename.c_str(), std::ios::in); + if (!fin) throw std::runtime_error(strerror(errno)); + readEdges(fin); + } + + mh.end(); + update(); + } + + void readAdjacencyList(std::string const& filename) { + tdzdd::MessageHandler mh; + mh.begin("reading"); + + if (filename.empty()) { + mh << " STDIN ..."; + readAdjacencyList(std::cin); + } else { + mh << " \"" << filename << "\" ..."; + std::ifstream fin(filename.c_str(), std::ios::in); + if (!fin) throw std::runtime_error(strerror(errno)); + readAdjacencyList(fin); + } + + mh.end(); + update(); + } + + void readVertexGroups(std::string const& filename) { + tdzdd::MessageHandler mh; + mh.begin("reading"); + + if (filename.empty()) { + mh << " STDIN ..."; + readVertexGroups(std::cin); + } else { + mh << " \"" << filename << "\" ..."; + std::ifstream fin(filename.c_str(), std::ios::in); + if (!fin) throw std::runtime_error(strerror(errno)); + readVertexGroups(fin); + } + + mh.end(); + update(); + } + + private: + void readEdges(std::istream& is) { + std::string v, v1, v2; + + while (is) { + char c = is.get(); + + if (isspace(c)) { + if (!v.empty()) { + if (v1.empty()) { + v1 = v; + v.clear(); + } else if (v2.empty()) { + v2 = v; + v.clear(); + } else { + throw std::runtime_error("ERROR: More than two tokens in a line"); + } + } + + if (c == '\n') { + if (!v1.empty() && !v2.empty()) { + edgeNames.push_back(std::make_pair(v1, v2)); + v1.clear(); + v2.clear(); + } else if (!v1.empty()) { + throw std::runtime_error("ERROR: Only one token in a line"); + } + } + } else { + v += c; + } + } + + if (!v1.empty() && !v2.empty()) { + edgeNames.push_back(std::make_pair(v1, v2)); + } else if (!v1.empty()) { + throw std::runtime_error("ERROR: Only one token in a line"); + } + } + + void readAdjacencyList(std::istream& is) { + edgeNames.clear(); + name2label.clear(); + name2color.clear(); + + VertexNumber v1 = 1; + VertexNumber v2; + + while (is) { + char c; + while (isspace(c = is.get())) { + if (c == '\n') ++v1; + } + if (!is) break; + is.unget(); + is >> v2; + + edgeNames.push_back(std::make_pair(to_string(v1), to_string(v2))); + } + } + + void readVertexGroups(std::istream& is) { + std::vector group; + int n = 0; + std::string color = getColor(n++); + name2color.clear(); + + while (is) { + char c; + while (isspace(c = is.get())) { + if (c == '\n') { + color = getColor(n++); + } + } + if (!is) break; + is.unget(); + + VertexNumber v; + is >> v; + + name2color[to_string(v)] = color; + } + } + + std::string getColor(int n) { + char const HEX[] = "0123456789abcdef"; + std::string color("#000000"); + + color[2] = HEX[(n / 256) % 16]; + color[4] = HEX[(n / 16) % 16]; + color[6] = HEX[n % 16]; + color[1] = HEX[15 - ((n * 3) % 11)]; + color[3] = HEX[(n * 5 + 5) % 11 + 5]; + color[5] = HEX[15 - ((n * 2 + 7) % 11)]; + return color; + } + + public: + /* + * INPUT: + * edgeNames + * name2label + * name2color + */ + void update() { + name2vertex.clear(); + vertex2name.clear(); + name2edge.clear(); + edge2name.clear(); + edgeInfo_.clear(); + edgeIndex.clear(); + vMax = 0; + + // Make unique edge name list + for (size_t i = 0; i < edgeNames.size(); ++i) { + std::pair const& e = edgeNames[i]; + + if (name2edge.count(e) == 0) { + edge2name.push_back(e); + name2edge[e] = -1; + // name2edge[std::make_pair(e.second, e.first)] = -1; + } + } + + // Sort vertices by leaving order + { + std::vector stack; + stack.reserve(edge2name.size() * 2); + + for (size_t i = edge2name.size() - 1; i + 1 > 0; --i) { + std::string const& s1 = edge2name[i].first; + std::string const& s2 = edge2name[i].second; + + if (name2vertex.count(s2) == 0) { + name2vertex[s2] = 0; + stack.push_back(s2); + } + + if (name2vertex.count(s1) == 0) { + name2vertex[s1] = 0; + stack.push_back(s1); + } + } + + vertex2name.push_back(""); // begin vertex number with 1 + + while (!stack.empty()) { + std::string const& s = stack.back(); + name2vertex[s] = vertex2name.size(); + vertex2name.push_back(s); + if (vertex2name.size() > size_t(MAX_VERTICES)) + throw std::runtime_error("ERROR: Vertex number > " + + to_string(MAX_VERTICES)); + stack.pop_back(); + } + } + + for (size_t i = 0; i < edge2name.size(); ++i) { + std::pair const& e = edge2name[i]; + std::string s1 = e.first; + std::string s2 = e.second; + + // if (name2vertex[s1] > name2vertex[s2]) { + // std::swap(s1, s2); + //} + + VertexNumber v1 = name2vertex[s1]; + VertexNumber v2 = name2vertex[s2]; + + if (v1 == 0) + throw std::runtime_error("ERROR: " + s1 + ": No such vertex"); + if (v2 == 0) + throw std::runtime_error("ERROR: " + s2 + ": No such vertex"); + + VertexNumberPair vp(v1, v2); + + if (edgeIndex.count(vp) == 0) { + EdgeNumber a = edgeInfo_.size(); + edgeInfo_.push_back(EdgeInfo(v1, v2)); + edgeIndex[vp] = a; + name2edge[std::make_pair(s1, s2)] = a; + name2edge[std::make_pair(s2, s1)] = a; + if (vMax < v2) vMax = v2; + } + + if (edgeInfo_.size() > size_t(MAX_EDGES)) + throw std::runtime_error("ERROR: Edge number > " + + to_string(MAX_EDGES)); + } + + { + std::map > color2vertices; + + for (std::map::iterator t = name2color.begin(); + t != name2color.end(); ++t) { + VertexNumber v = name2vertex[t->first]; + if (v == 0) + throw std::runtime_error("ERROR: " + t->first + ": No such vertex"); + color2vertices[t->second].insert(v); // color => set of vertices + } + + if (color2vertices.size() > size_t(MAX_COLORS)) { + throw std::runtime_error("ERROR: Target paths > " + + to_string(MAX_COLORS)); + } + + virtualMate_.resize(vMax + 1); + colorNumber_.resize(vMax + 1); + for (VertexNumber v = 1; v <= vMax; ++v) { + virtualMate_[v] = 0; + colorNumber_[v] = 0; + } + numColor_ = 0; + hasColorPairs_ = !color2vertices.empty(); + + for (std::map >::iterator t = + color2vertices.begin(); + t != color2vertices.end(); ++t) { + ++numColor_; + + if (t->second.size() == 2) { + std::set::iterator tt = t->second.begin(); + VertexNumber v1 = *tt++; + VertexNumber v2 = *tt; + virtualMate_[v1] = v2; + virtualMate_[v2] = v1; + } else { + hasColorPairs_ = false; + } + + for (std::set::iterator tt = t->second.begin(); + tt != t->second.end(); ++tt) { + VertexNumber v = *tt; + colorNumber_[v] = numColor_; + } + } + + // sort colorNumber + std::vector colorMap(numColor_ + 1); + ColorNumber cn = 0; + for (VertexNumber v = 1; v <= vMax; ++v) { + ColorNumber c = colorNumber_[v]; + if (c == 0) continue; + ColorNumber& cc = colorMap[c]; + if (cc == 0) cc = ++cn; + colorNumber_[v] = cc; + } + } + + { + std::vector lastEdge(vMax + 1); + std::vector secondLastEdge(vMax + 1); + for (VertexNumber v = 1; v <= vMax; ++v) { + lastEdge[v] = -1; + secondLastEdge[v] = -1; + } + for (EdgeNumber a = 0; a < edgeSize(); ++a) { + EdgeInfo& e = edgeInfo_[a]; + secondLastEdge[e.v1] = lastEdge[e.v1]; + secondLastEdge[e.v2] = lastEdge[e.v2]; + lastEdge[e.v1] = a; + lastEdge[e.v2] = a; + } + + std::vector finalEdgeToColor(numColor_ + 1); + EdgeNumber fitstEdgeToFinalColor = 0; + std::vector touched(numColor_ + 1); + touched[0] = true; + ColorNumber k = numColor_; + for (EdgeNumber a = 0; a < edgeSize(); ++a) { + EdgeInfo const& e = edgeInfo(a); + ColorNumber n1 = colorNumber(e.v1); + ColorNumber n2 = colorNumber(e.v2); + finalEdgeToColor[n1] = a; + finalEdgeToColor[n2] = a; + if (!touched[n1]) { + if (--k == 0) { + fitstEdgeToFinalColor = a; + break; + } + touched[n1] = true; + } + if (!touched[n2]) { + if (--k == 0) { + fitstEdgeToFinalColor = a; + break; + } + touched[n2] = true; + } + } + + VertexNumber v0 = 1; + + for (EdgeNumber a = 0; a < edgeSize(); ++a) { + EdgeInfo& e = edgeInfo_[a]; + while (lastEdge[v0] < a) { + ++v0; + assert(v0 <= vMax); + } + e.v0 = v0; + e.v1final = (a == lastEdge[e.v1]); + e.v2final = (a == lastEdge[e.v2]); + e.v1final2 = (a == secondLastEdge[e.v1]); + e.v2final2 = (a == secondLastEdge[e.v2]); + e.allColorsSeen = (a >= fitstEdgeToFinalColor); + e.finalEdge = (a == edgeSize() - 1); + } + } + } + + VertexNumber vertexSize() const { return vMax; } + + EdgeNumber edgeSize() const { return edgeInfo_.size(); } + + EdgeInfo const& edgeInfo(EdgeNumber a) const { + assert(0 <= a && size_t(a) < edgeInfo_.size()); + return edgeInfo_[a]; + } + + VertexNumber getVertex(std::string const& name) const { + std::map::const_iterator found = + name2vertex.find(name); + if (found == name2vertex.end()) + throw std::runtime_error("ERROR: " + name + ": No such vertex"); + return found->second; + } + + std::string vertexName(VertexNumber v) const { + if (v < 1 || vertexSize() < v) return "?"; + return vertex2name[v]; + } + + std::string vertexLabel(VertexNumber v) const { + std::string label = vertexName(v); + + std::map::const_iterator found = + name2label.find(label); + if (found != name2label.end()) { + label = found->second; + } + + return label; + } + + EdgeNumber getEdge(std::pair const& name) const { + std::map, EdgeNumber>::const_iterator + found = name2edge.find(name); + if (found == name2edge.end()) + throw std::runtime_error("ERROR: " + name.first + "," + name.second + + ": No such edge"); + return found->second; + } + + EdgeNumber getEdge(std::string const& name1, std::string const& name2) const { + return getEdge(std::make_pair(name1, name2)); + } + + std::pair edgeName(EdgeNumber e) const { + if (e < 0 || edgeSize() <= e) return std::make_pair("?", "?"); + return edge2name[e]; + } + + std::string edgeLabel(EdgeNumber e) const { + std::pair name = edgeName(e); + std::string label = name.first + "," + name.second; + + std::map::const_iterator found = + name2label.find(label); + if (found != name2label.end()) { + label = found->second; + } + + return label; + } + + VertexNumber virtualMate(VertexNumber v) const { + return (1 <= v && v <= vMax) ? virtualMate_[v] : 0; + } + + EdgeNumber getEdge(VertexNumber v1, VertexNumber v2) const { + assert(1 <= v1 && v1 <= vMax); + assert(1 <= v2 && v2 <= vMax); + if (v1 > v2) std::swap(v1, v2); + VertexNumberPair vp(v1, v2); + std::map::const_iterator found = + edgeIndex.find(vp); + if (found == edgeIndex.end()) + throw std::runtime_error("ERROR: (" + to_string(v1) + "," + + to_string(v2) + "): No such edge"); + return found->second; + } + + VertexNumber maxFrontierSize() const { + VertexNumber n = 0; + for (EdgeNumber a = 0; a < edgeSize(); ++a) { + EdgeInfo const& e = edgeInfo(a); + VertexNumber m = e.v2 - e.v0 + 1; + if (n < m) n = m; + } + return n; + } + + void clearColors() { + name2color.clear(); + virtualMate_.clear(); + virtualMate_.resize(vMax + 1); + colorNumber_.clear(); + colorNumber_.resize(vMax + 1); + numColor_ = 0; + } + + void setDefaultPathColor() { + name2color.clear(); + name2color[to_string(1)] = "#ff7777"; + name2color[to_string(vMax)] = "#ff7777"; + update(); + } + + ColorNumber colorNumber(VertexNumber v) const { + return (1 <= v && v <= vMax) ? colorNumber_[v] : 0; + } + + ColorNumber numColor() const { return numColor_; } + + bool hasColorPairs() const { return hasColorPairs_; } + + template + std::ostream& dump(std::ostream& os, E const& edgeDecorator) const { + os << "digraph {\n"; + // os << " layout=neato;\n"; + + for (std::vector::const_iterator t = vertex2name.begin(); + t != vertex2name.end(); ++t) { + if (t->empty()) continue; + os << " \"" << *t << "\""; + std::map::const_iterator e = + name2label.find(*t); + if (e != name2label.end()) { + os << "[label=\"" << e->second << "\"]"; + } + e = name2color.find(*t); + if (e != name2color.end()) { + os << "[color=\"" << e->second << "\",style=filled]"; + } + os << ";\n"; + } + + for (EdgeNumber a = 0; a < edgeSize(); ++a) { + EdgeInfo const& e = edgeInfo(a); + std::string s1 = vertex2name[e.v1]; + std::string s2 = vertex2name[e.v2]; + os << " \"" << s1 << "\"--\"" << s2 << "\""; + std::map::const_iterator t = + name2label.find(s1 + "," + s2); + if (t != name2label.end()) { + os << "[label=\"" << t->second << "\"]"; + } + t = name2color.find(s1 + "," + s2); + if (t != name2color.end()) { + os << "[color=\"" << t->second << "\",style=bold]"; + } + os << edgeDecorator(a); + os << ";\n"; + } + + os << "}\n"; + os.flush(); + return os; + } + + private: + struct NoEdgeDecorator { + std::string operator()(EdgeNumber a) const { return ""; } + }; + + public: + std::ostream& dump(std::ostream& os) const { + return dump(os, NoEdgeDecorator()); + } + + friend std::ostream& operator<<(std::ostream& os, Digraph const& g) { + return g.dump(os); + } + + private: + static std::string to_string(int i) { + std::ostringstream oss; + oss << i; + return oss.str(); + } +}; + +} // namespace graphillion diff --git a/src/graphillion/digraphs/FrontierData.hpp b/src/graphillion/digraphs/FrontierData.hpp new file mode 100644 index 0000000..e5ac5b2 --- /dev/null +++ b/src/graphillion/digraphs/FrontierData.hpp @@ -0,0 +1,34 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef DIGRAPHILLION_FRONTIER_DATA_HPP_ +#define DIGRAPHILLION_FRONTIER_DATA_HPP_ + +// data associated with each vertex on the frontier +class DirectedFrontierData { + public: + short indeg; + short outdeg; + short comp; +}; + +#endif diff --git a/src/graphillion/digraphs/FrontierDegreeSpecified.hpp b/src/graphillion/digraphs/FrontierDegreeSpecified.hpp new file mode 100644 index 0000000..f6a84da --- /dev/null +++ b/src/graphillion/digraphs/FrontierDegreeSpecified.hpp @@ -0,0 +1,180 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FRONTIER_DEGREE_SPECIFIED_HPP +#define FRONTIER_DEGREE_SPECIFIED_HPP + +#include +#include + +#include "FrontierData.hpp" +#include "FrontierManager.hpp" +#include "subsetting/DdSpec.hpp" +#include "Digraph.hpp" + +using namespace tdzdd; + +class FrontierDegreeSpecifiedSpec + : public tdzdd::PodArrayDdSpec { + private: + // input graph + const graphillion::Digraph& graph_; + // number of vertices + const int n_; + // number of edges + const int m_; + + const FrontierManager fm_; + + std::vector in_constraints; + std::vector out_constraints; + + // This function gets deg of v. + short getIndeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].indeg; + } + + short getOutdeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].outdeg; + } + + // This function sets deg of v to be d. + void setIndeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].indeg = d; + } + + void setOutdeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].outdeg = d; + } + + void initializeData(DirectedFrontierData* data) const { + for (int i = 0; i < fm_.getMaxFrontierSize(); ++i) { + data[i].indeg = 0; + data[i].outdeg = 0; + data[i].comp = 0; + } + } + + public: + FrontierDegreeSpecifiedSpec(const graphillion::Digraph& graph) + : graph_(graph), + n_(static_cast(graph_.vertexSize())), + m_(graph_.edgeSize()), + fm_(graph_) { + if (graph_.vertexSize() > SHRT_MAX) { // SHRT_MAX == 32767 + std::cerr << "The number of vertices should be at most " << SHRT_MAX + << std::endl; + exit(1); + } + + // todo: check all the degrees is at most 256 + + setArraySize(fm_.getMaxFrontierSize()); + int m = graph_.vertexSize(); + in_constraints.resize(m + 1); + out_constraints.resize(m + 1); + for (int v = 1; v <= m; v++) { + in_constraints.at(v) = graphillion::Range(0, INT_MAX); + out_constraints.at(v) = graphillion::Range(0, INT_MAX); + } + } + + void setIndegConstraint(graphillion::Digraph::VertexNumber v, const graphillion::Range& c) { + if (v < 1 || graph_.vertexSize() < v) + throw std::runtime_error("ERROR: Vertex number is out of range"); + in_constraints.at(v) = c; + } + + void setOutdegConstraint(graphillion::Digraph::VertexNumber v, const graphillion::Range& c) { + if (v < 1 || graph_.vertexSize() < v) + throw std::runtime_error("ERROR: Vertex number is out of range"); + out_constraints.at(v) = c; + } + + int getRoot(DirectedFrontierData* data) const { + initializeData(data); + return m_; + } + + int getChild(DirectedFrontierData* data, int level, int value) const { + assert(1 <= level && level <= m_); + + // edge index (starting from 0) + const int edge_index = m_ - level; + // edge that we are processing. + // The endpoints of "edge" are edge.v1 and edge.v2. + const graphillion::Digraph::EdgeInfo& edge = graph_.edgeInfo(edge_index); + + // initialize deg of the vertices newly entering the frontier + const std::vector& entering_vs = fm_.getEnteringVs(edge_index); + for (size_t i = 0; i < entering_vs.size(); ++i) { + int v = entering_vs.at(i); + // initially the value of deg is 0 + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + } + + if (value == 1) { // if we take the edge (go to 1-arc) + // increment deg of v1 and v2 (recall that edge = {v1, v2}) + auto outdeg1 = getOutdeg(data, edge.v1); + assert(1 <= edge.v1 && edge.v1 < (int)out_constraints.size()); + if (!out_constraints.at(edge.v1).contains(outdeg1 + 1)) { + return 0; + } + + auto indeg2 = getIndeg(data, edge.v2); + assert(1 <= edge.v2 && edge.v2 < (int)in_constraints.size()); + if (!in_constraints.at(edge.v2).contains(indeg2 + 1)) { + return 0; + } + setIndeg(data, edge.v2, indeg2 + 1); + setOutdeg(data, edge.v1, outdeg1 + 1); + } + + // vertices that are leaving the frontier + const std::vector& leaving_vs = fm_.getLeavingVs(edge_index); + for (size_t i = 0; i < leaving_vs.size(); ++i) { + int v = leaving_vs.at(i); + + int indeg = getIndeg(data, v), outdeg = getOutdeg(data, v); + assert(1 <= v && v < (int)in_constraints.size()); + assert(1 <= v && v < (int)out_constraints.size()); + if (!in_constraints.at(v).contains(indeg) || + !out_constraints.at(v).contains(outdeg)) { + return 0; + } + + // Since deg of v is never used until the end, + // we erase the values. + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + } + if (level == 1) { + return -1; + } + assert(level - 1 > 0); + return level - 1; + } +}; + +#endif // FRONTIER_DEGREE_SPECIFIED_HPP diff --git a/src/graphillion/digraphs/FrontierDirectedHamiltonianCycle.hpp b/src/graphillion/digraphs/FrontierDirectedHamiltonianCycle.hpp new file mode 100644 index 0000000..a07265b --- /dev/null +++ b/src/graphillion/digraphs/FrontierDirectedHamiltonianCycle.hpp @@ -0,0 +1,245 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FRONTIER_SINGLE_HAMILTONIAN_CYCLE_HPP +#define FRONTIER_SINGLE_HAMILTONIAN_CYCLE_HPP + +#include +#include + +#include "FrontierData.hpp" +#include "FrontierManager.hpp" +#include "subsetting/DdSpec.hpp" +#include "Digraph.hpp" + +using namespace tdzdd; + +class FrontierDirectedSingleHamiltonianCycleSpec + : public tdzdd::PodArrayDdSpec { + private: + // input graph + const graphillion::Digraph& graph_; + // number of vertices + const short n_; + // number of edges + const int m_; + + const FrontierManager fm_; + + // the level where all vertices enter the frontier + const int all_entered_level_; + + // This function gets deg of v. + short getIndeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].indeg; + } + + short getOutdeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].outdeg; + } + + // This function sets deg of v to be d. + void setIndeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].indeg = d; + } + + void setOutdeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].outdeg = d; + } + + // This function gets comp of v. + short getComp(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].comp; + } + + // This function sets comp of v to be c. + void setComp(DirectedFrontierData* data, short v, short c) const { + data[fm_.vertexToPos(v)].comp = c; + } + + void initializeDegComp(DirectedFrontierData* data) const { + for (int i = 0; i < fm_.getMaxFrontierSize(); ++i) { + data[i].indeg = 0; + data[i].outdeg = 0; + data[i].comp = 0; + } + } + + public: + FrontierDirectedSingleHamiltonianCycleSpec(const graphillion::Digraph& graph) + : graph_(graph), + n_(static_cast(graph_.vertexSize())), + m_(graph_.edgeSize()), + fm_(graph_), + all_entered_level_(m_ - fm_.getAllVerticesEnteringLevel()) { + if (graph_.vertexSize() > SHRT_MAX) { // SHRT_MAX == 32767 + std::cerr << "The number of vertices should be at most " << SHRT_MAX + << std::endl; + exit(1); + } + setArraySize(fm_.getMaxFrontierSize()); + } + + int getRoot(DirectedFrontierData* data) const { + initializeDegComp(data); + return m_; + } + + int getChild(DirectedFrontierData* data, int level, int value) const { + assert(1 <= level && level <= m_); + + // edge index (starting from 0) + const int edge_index = m_ - level; + // edge that we are processing. + // The endpoints of "edge" are edge.v1 and edge.v2. + const graphillion::Digraph::EdgeInfo& edge = graph_.edgeInfo(edge_index); + + // initialize deg and comp of the vertices newly entering the frontier + const std::vector& entering_vs = fm_.getEnteringVs(edge_index); + for (size_t i = 0; i < entering_vs.size(); ++i) { + int v = entering_vs[i]; + // initially the value of deg is 0 + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + // initially the value of comp is the vertex number itself + setComp(data, v, v); + } + + // vertices on the frontier + const std::vector& frontier_vs = fm_.getFrontierVs(edge_index); + + if (value == 1) { // if we take the edge (go to 1-arc) + // increment deg of v1 and v2 (recall that edge = {v1, v2}) + auto outdeg1 = getOutdeg(data, edge.v1); + auto indeg2 = getIndeg(data, edge.v2); + + setIndeg(data, edge.v2, indeg2 + 1); + setOutdeg(data, edge.v1, outdeg1 + 1); + + short c1 = getComp(data, edge.v1); + short c2 = getComp(data, edge.v2); + if (c1 != c2) { // connected components c1 and c2 become connected + short cmin = std::min(c1, c2); + short cmax = std::max(c1, c2); + + // replace component number cmin with cmax + for (size_t i = 0; i < frontier_vs.size(); ++i) { + int v = frontier_vs[i]; + if (getComp(data, v) == cmin) { + setComp(data, v, cmax); + } + } + } + } + + // vertices that are leaving the frontier + const std::vector& leaving_vs = fm_.getLeavingVs(edge_index); + for (size_t i = 0; i < leaving_vs.size(); ++i) { + int v = leaving_vs[i]; + + // The degree of v must be 0 or 2. + // in/out = 0/0 or 1/1 + bool ok = (getIndeg(data, v) == 0 && getOutdeg(data, v) == 0) || + (getIndeg(data, v) == 1 && getOutdeg(data, v) == 1); + if (!ok) { + return 0; + } + + bool samecomp_found = false; + bool nonisolated_found = false; + bool frontier_exists = false; + + // Search a vertex that has the component number same as that of v. + // Also check whether a vertex whose degree is at least 1 exists + // on the frontier. + for (size_t j = 0; j < frontier_vs.size(); ++j) { + int w = frontier_vs[j]; + if (w == v) { // skip if w is the leaving vertex + continue; + } + // skip if w is one of the vertices that + // has already leaved the frontier + bool found_leaved = false; + for (size_t k = 0; k < i; ++k) { + if (w == leaving_vs[k]) { + found_leaved = true; + break; + } + } + if (found_leaved) { + continue; + } + frontier_exists = true; + // w has the component number same as that of v + if (getComp(data, w) == getComp(data, v)) { + samecomp_found = true; + } + // The degree of w is at least 1. + if (getIndeg(data, w) > 0 || getOutdeg(data, w) > 0) { + nonisolated_found = true; + } + if (nonisolated_found && samecomp_found) { + break; + } + } + // There is no vertex that has the component number + // same as that of v. That is, the connected component + // of v becomes determined. + if (!samecomp_found) { + // Here, deg of v is 2. + assert(getIndeg(data, v) == 1 && getOutdeg(data, v) == 1); + + // Check whether there is a connected component + // other than that of v, that is, the generated subgraph + // is not connected. + // If so, we return the 0-terminal. + if (nonisolated_found) { + return 0; // return the 0-terminal. + } else { + // Here, a single Hamiltonian cycle is completed. + if (frontier_exists) { + return 0; // return the 0-terminal + } else if (level > all_entered_level_) { + // Some vertices have not entered the frontier yet. + return 0; // return the 0-terminal + } else { + return -1; // return the 1-terminal + } + } + } + // Since deg and comp of v are never used until the end, + // we erase the values. + setIndeg(data, v, -1); + setOutdeg(data, v, -1); + setComp(data, v, -1); + } + if (level == 1) { + // If we come here, the edge set is empty (taking no edge). + return 0; + } + assert(level - 1 > 0); + return level - 1; + } +}; + +#endif // FRONTIER_SINGLE_HAMILTONIAN_CYCLE_HPP diff --git a/src/graphillion/digraphs/FrontierDirectedSTPath.hpp b/src/graphillion/digraphs/FrontierDirectedSTPath.hpp new file mode 100644 index 0000000..8ce3e10 --- /dev/null +++ b/src/graphillion/digraphs/FrontierDirectedSTPath.hpp @@ -0,0 +1,295 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FRONTIER_ST_PATH_HPP +#define FRONTIER_ST_PATH_HPP + +#include +#include + +#include "FrontierData.hpp" +#include "FrontierManager.hpp" +#include "subsetting/DdSpec.hpp" +#include "Digraph.hpp" + +using namespace tdzdd; + +class FrontierDirectedSTPathSpec + : public tdzdd::PodArrayDdSpec { + private: + // input graph + const graphillion::Digraph& graph_; + // number of vertices + const short n_; + // number of edges + const int m_; + + const bool isHamiltonian_; + + // endpoints of a path + const short s_; + const short t_; + + const FrontierManager fm_; + + const int s_entered_level_; + const int t_entered_level_; + const int min_entered_level_; + + // This function gets deg of v. + short getIndeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].indeg; + } + + short getOutdeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].outdeg; + } + + // This function sets deg of v to be d. + void setIndeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].indeg = d; + } + + void setOutdeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].outdeg = d; + } + + // This function gets comp of v. + short getComp(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].comp; + } + + // This function sets comp of v to be c. + void setComp(DirectedFrontierData* data, short v, short c) const { + data[fm_.vertexToPos(v)].comp = c; + } + + void initializeDegComp(DirectedFrontierData* data) const { + for (int i = 0; i < fm_.getMaxFrontierSize(); ++i) { + data[i].indeg = 0; + data[i].outdeg = 0; + data[i].comp = 0; + } + } + + int computeEnteredLevel(short v) const { + return m_ - fm_.getVerticesEnteringLevel(v); + } + + public: + FrontierDirectedSTPathSpec(const graphillion::Digraph& graph, bool isHamiltonian, + short s, short t) + : graph_(graph), + n_(static_cast(graph_.vertexSize())), + m_(graph_.edgeSize()), + isHamiltonian_(isHamiltonian), + s_(s), + t_(t), + fm_(graph_), + s_entered_level_(computeEnteredLevel(s)), + t_entered_level_(computeEnteredLevel(t)), + min_entered_level_(m_ - fm_.getAllVerticesEnteringLevel()) { + if (graph_.vertexSize() > SHRT_MAX) { // SHRT_MAX == 32767 + std::cerr << "The number of vertices should be at most " << SHRT_MAX + << std::endl; + exit(1); + } + setArraySize(fm_.getMaxFrontierSize()); + } + + int getRoot(DirectedFrontierData* data) const { + initializeDegComp(data); + return m_; + } + + int getChild(DirectedFrontierData* data, int level, int value) const { + assert(1 <= level && level <= m_); + + // edge index (starting from 0) + const int edge_index = m_ - level; + // edge that we are processing. + // The endpoints of "edge" are edge.v1 and edge.v2. + const graphillion::Digraph::EdgeInfo& edge = graph_.edgeInfo(edge_index); + + // initialize deg and comp of the vertices newly entering the frontier + const std::vector& entering_vs = fm_.getEnteringVs(edge_index); + for (size_t i = 0; i < entering_vs.size(); ++i) { + int v = entering_vs[i]; + // initially the value of deg is 0 + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + // initially the value of comp is the vertex number itself + setComp(data, v, v); + } + + // vertices on the frontier + const std::vector& frontier_vs = fm_.getFrontierVs(edge_index); + + if (value == 1) { // if we take the edge (go to 1-arc) + // increment deg of v1 and v2 (recall that edge = {v1, v2}) + auto outdeg1 = getOutdeg(data, edge.v1); + auto indeg2 = getIndeg(data, edge.v2); + + setIndeg(data, edge.v2, indeg2 + 1); + setOutdeg(data, edge.v1, outdeg1 + 1); + + short c1 = getComp(data, edge.v1); + short c2 = getComp(data, edge.v2); + if (c1 != c2) { // connected components c1 and c2 become connected + short cmin = std::min(c1, c2); + short cmax = std::max(c1, c2); + + // replace component number cmin with cmax + for (size_t i = 0; i < frontier_vs.size(); ++i) { + int v = frontier_vs[i]; + if (getComp(data, v) == cmin) { + setComp(data, v, cmax); + } + } + } + } + + // vertices that are leaving the frontier + const std::vector& leaving_vs = fm_.getLeavingVs(edge_index); + for (size_t i = 0; i < leaving_vs.size(); ++i) { + int v = leaving_vs[i]; + + if (v == s_) { + if (getOutdeg(data, v) != 1 || getIndeg(data, v) != 0) { + return 0; + } + } else if (v == t_) { + // The degree of s and t must be 1. + if (getIndeg(data, v) != 1 || getOutdeg(data, v) != 0) { + return 0; + } + } else { + if (isHamiltonian_) { + // The degree of v (!= s, t) must be 2. + if (getIndeg(data, v) != 1 || getOutdeg(data, v) != 1) { + return 0; + } + } else { + // The degree of v (!= s, t) must be 0 or 2. + bool ok = (getIndeg(data, v) == 0 && getOutdeg(data, v) == 0) || + (getIndeg(data, v) == 1 && getOutdeg(data, v) == 1); + if (!ok) { + return 0; + } + } + } + bool comp_found = false; + bool deg_found = false; + bool frontier_exists = false; + // there is a endpoint on the remaind frontier. + bool endpoint_exists = false; + // Search a vertex that has the component number same as that of v. + // Also check whether a vertex whose degree is at least 1 exists + // on the frontier. + for (size_t j = 0; j < frontier_vs.size(); ++j) { + int w = frontier_vs[j]; + if (w == v) { // skip if w is the leaving vertex + continue; + } + // skip if w is one of the vertices that + // has already leaved the frontier + bool found_leaved = false; + for (size_t k = 0; k < i; ++k) { + if (w == leaving_vs[k]) { + found_leaved = true; + break; + } + } + if (found_leaved) { + continue; + } + frontier_exists = true; + // w has the component number same as that of v + if (getComp(data, w) == getComp(data, v)) { + comp_found = true; + } + // The degree of w is at least 1. + if (getIndeg(data, w) > 0 || getOutdeg(data, w) > 0) { + deg_found = true; + } + if (w == s_ || w == t_) { + endpoint_exists = true; + } + if (deg_found && comp_found && endpoint_exists) { + break; + } + } + // There is no vertex that has the component number + // same as that of v. That is, the connected component + // of v becomes determined. + if (!comp_found) { + // If deg of v is 0, this means that v is isolated. + // If deg of v is at least 1, and there is a vertex whose + // deg is at least 1, this means that there is a + // connected component other than that of v. + // That is, the generated subgraph is not connected. + // Then, we return the 0-terminal. + assert(getIndeg(data, v) <= 1 && getOutdeg(data, v) <= 1); + if (getIndeg(data, v) + getOutdeg(data, v) > 0 && + (deg_found || endpoint_exists)) { + return 0; // return the 0-terminal. + } else if (getIndeg(data, v) + getOutdeg(data, v) > + 0) { // If deg of v is 2, + // and there is no vertex whose deg is at least 1, + // a single cycle is completed. + // Then, we return the 1-terminal. + if (isHamiltonian_) { + // unconnected vertices remain. + if (level > min_entered_level_) { + return 0; + } + if (frontier_exists) { + return 0; + } else { + return -1; // return the 1-terminal + } + } else { + if (level > s_entered_level_ || level > t_entered_level_) { + return 0; + } else { + return -1; // return the 1-terminal + } + } + } + } + // Since deg and comp of v are never used until the end, + // we erase the values. + setIndeg(data, v, -1); + setOutdeg(data, v, -1); + setComp(data, v, -1); + } + if (level == 1) { + // If we come here, the edge set is empty (taking no edge). + return 0; + } + assert(level - 1 > 0); + return level - 1; + } +}; + +#endif // FRONTIER_ST_PATH_HPP diff --git a/src/graphillion/digraphs/FrontierDirectedSingleCycle.hpp b/src/graphillion/digraphs/FrontierDirectedSingleCycle.hpp new file mode 100644 index 0000000..5e441c3 --- /dev/null +++ b/src/graphillion/digraphs/FrontierDirectedSingleCycle.hpp @@ -0,0 +1,238 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FRONTIER_DIRECTED_SINGLE_CYCLE_HPP +#define FRONTIER_DIRECTED_SINGLE_CYCLE_HPP + +#include +#include + +#include "FrontierData.hpp" +#include "FrontierManager.hpp" +#include "subsetting/DdSpec.hpp" +#include "Digraph.hpp" + +using namespace tdzdd; + +class FrontierDirectedSingleCycleSpec + : public tdzdd::PodArrayDdSpec { + private: + // input graph + const graphillion::Digraph& graph_; + // number of vertices + const short n_; + // number of edges + const int m_; + + const FrontierManager fm_; + + // This function gets deg of v. + short getIndeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].indeg; + } + + short getOutdeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].outdeg; + } + + // This function sets deg of v to be d. + void setIndeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].indeg = d; + } + + void setOutdeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].outdeg = d; + } + + // This function gets comp of v. + short getComp(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].comp; + } + + // This function sets comp of v to be c. + void setComp(DirectedFrontierData* data, short v, short c) const { + data[fm_.vertexToPos(v)].comp = c; + } + + void initializeDegComp(DirectedFrontierData* data) const { + for (int i = 0; i < fm_.getMaxFrontierSize(); ++i) { + data[i].indeg = 0; + data[i].outdeg = 0; + data[i].comp = 0; + } + } + + public: + FrontierDirectedSingleCycleSpec(const graphillion::Digraph& graph) + : graph_(graph), + n_(static_cast(graph_.vertexSize())), + m_(graph_.edgeSize()), + fm_(graph_) { + if (graph_.vertexSize() > SHRT_MAX) { // SHRT_MAX == 32767 + std::cerr << "The number of vertices should be at most " << SHRT_MAX + << std::endl; + exit(1); + } + setArraySize(fm_.getMaxFrontierSize()); + } + + int getRoot(DirectedFrontierData* data) const { + initializeDegComp(data); + return m_; + } + + int getChild(DirectedFrontierData* data, int level, int value) const { + assert(1 <= level && level <= m_); + + // edge index (starting from 0) + const int edge_index = m_ - level; + // edge that we are processing. + // The endpoints of "edge" are edge.v1 and edge.v2. + const graphillion::Digraph::EdgeInfo& edge = graph_.edgeInfo(edge_index); + + // initialize deg and comp of the vertices newly entering the frontier + const std::vector& entering_vs = fm_.getEnteringVs(edge_index); + for (size_t i = 0; i < entering_vs.size(); ++i) { + int v = entering_vs[i]; + // initially the value of deg is 0 + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + // initially the value of comp is the vertex number itself + setComp(data, v, v); + } + + // vertices on the frontier + const std::vector& frontier_vs = fm_.getFrontierVs(edge_index); + + if (value == 1) { // if we take the edge (go to 1-arc) + // increment deg of v1 and v2 (recall that edge = {v1, v2}) + auto outdeg1 = getOutdeg(data, edge.v1); + auto indeg2 = getIndeg(data, edge.v2); + + setIndeg(data, edge.v2, indeg2 + 1); + setOutdeg(data, edge.v1, outdeg1 + 1); + + short c1 = getComp(data, edge.v1); + short c2 = getComp(data, edge.v2); + if (c1 != c2) { // connected components c1 and c2 become connected + short cmin = std::min(c1, c2); + short cmax = std::max(c1, c2); + + // replace component number cmin with cmax + for (size_t i = 0; i < frontier_vs.size(); ++i) { + int v = frontier_vs[i]; + if (getComp(data, v) == cmin) { + setComp(data, v, cmax); + } + } + } + } + + // vertices that are leaving the frontier + const std::vector& leaving_vs = fm_.getLeavingVs(edge_index); + for (size_t i = 0; i < leaving_vs.size(); ++i) { + int v = leaving_vs[i]; + + // The degree of v must be 0 or 2. + // in/out = 0/0 or 1/1 + bool ok = (getIndeg(data, v) == 0 && getOutdeg(data, v) == 0) || + (getIndeg(data, v) == 1 && getOutdeg(data, v) == 1); + if (!ok) { + return 0; + } + + bool samecomp_found = false; + bool nonisolated_found = false; + + // Search a vertex that has the component number same as that of v. + // Also check whether a vertex whose degree is at least 1 exists + // on the frontier. + for (size_t j = 0; j < frontier_vs.size(); ++j) { + int w = frontier_vs[j]; + if (w == v) { // skip if w is the leaving vertex + continue; + } + // skip if w is one of the vertices that + // has already leaved the frontier + bool found_leaved = false; + for (size_t k = 0; k < i; ++k) { + if (w == leaving_vs[k]) { + found_leaved = true; + break; + } + } + if (found_leaved) { + continue; + } + // w has the component number same as that of v + if (getComp(data, w) == getComp(data, v)) { + samecomp_found = true; + } + // The degree of w is at least 1. + if (getIndeg(data, w) > 0 || getOutdeg(data, w) > 0) { + nonisolated_found = true; + } + if (nonisolated_found && samecomp_found) { + break; + } + } + // There is no vertex that has the component number + // same as that of v. That is, the connected component + // of v becomes determined. + if (!samecomp_found) { + // Here, deg of v is 0 or 2. + assert((getIndeg(data, v) == 0 && getOutdeg(data, v) == 0) || + (getIndeg(data, v) == 1 && getOutdeg(data, v) == 1)); + + // Check whether v is isolated. + // If v is isolated (deg of v is 0), nothing occurs. + if (getIndeg(data, v) > 0 || getOutdeg(data, v) > 0) { + // Check whether there is a + // connected component other than that of v, + // that is, the generated subgraph is not connected. + // If so, we return the 0-terminal. + if (nonisolated_found) { + return 0; // return the 0-terminal. + } else { + // Here, a single cycle is completed. + // Then, we return the 1-terminal. + return -1; // return the 1-terminal + } + } + } + // Since deg and comp of v are never used until the end, + // we erase the values. + setIndeg(data, v, -1); + setOutdeg(data, v, -1); + setComp(data, v, -1); + } + if (level == 1) { + // If we come here, the edge set is empty (taking no edge). + return 0; + } + assert(level - 1 > 0); + return level - 1; + } +}; + +#endif // FRONTIER_DIRECTED_SINGLE_CYCLE_HPP diff --git a/src/graphillion/digraphs/FrontierManager.hpp b/src/graphillion/digraphs/FrontierManager.hpp new file mode 100644 index 0000000..e4cfd75 --- /dev/null +++ b/src/graphillion/digraphs/FrontierManager.hpp @@ -0,0 +1,264 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef __FRONTIER_MANAGER_HPP +#define __FRONTIER_MANAGER_HPP + +#include + +#include "Digraph.hpp" + +using namespace tdzdd; + +// This class manages vertex numbers on the frontier +// and where deg/comp of each vertex is stored. +class FrontierManager { + private: + // input graph + const graphillion::Digraph& graph_; + + // frontier_vss_[i] stores the vertices each of + // which is incident to both at least one of e_0, e_1,...,e_{i-1} + // and at least one of e_{i+1},e_{i+2},...,e_{m-1}, and also stores + // both endpoints of e_i, where m is the number of edges. + // Note that the definition of the frontier is different from + // that in the paper [Kawahara+ 2017]. + // "vss" stands for "vertex set set". + std::vector > frontier_vss_; + + // entering_vss_[i] stores the vertex numbers + // that newly enter the frontier when processing the i-th edge. + std::vector > entering_vss_; + + // leaving_vss_[i] stores the vertex numbers + // that leave the frontier after the i-th edge is processed. + std::vector > leaving_vss_; + + std::vector > remaining_vss_; + + // translate the vertex number to the position in the PodArray + std::vector vertex_to_pos_; + std::vector > pos_to_vertex_; + + // the maximum frontier size + int max_frontier_size_; + + void constructEnteringAndLeavingVss() { + const int m = graph_.edgeSize(); + + entering_vss_.resize(m); + leaving_vss_.resize(m); + + // compute entering_vss_ + std::set entered_vs; + for (int i = 0; i < m; ++i) { + const graphillion::Digraph::EdgeInfo& e = graph_.edgeInfo(i); + if (entered_vs.count(e.v1) == 0) { + entering_vss_[i].push_back(e.v1); + entered_vs.insert(e.v1); + } + if (entered_vs.count(e.v2) == 0) { + entering_vss_[i].push_back(e.v2); + entered_vs.insert(e.v2); + } + } + assert(static_cast(entered_vs.size()) == n); + + // compute leaving_vss_ + std::set leaved_vs; + for (int i = m - 1; i >= 0; --i) { + const graphillion::Digraph::EdgeInfo& e = graph_.edgeInfo(i); + if (leaved_vs.count(e.v1) == 0) { + leaving_vss_[i].push_back(e.v1); + leaved_vs.insert(e.v1); + } + if (leaved_vs.count(e.v2) == 0) { + leaving_vss_[i].push_back(e.v2); + leaved_vs.insert(e.v2); + } + } + assert(static_cast(leaved_vs.size()) == n); + } + + void construct() { + const int n = graph_.vertexSize(); + const int m = graph_.edgeSize(); + max_frontier_size_ = 0; + + constructEnteringAndLeavingVss(); + + std::vector unused; + for (int i = n - 1; i >= 0; --i) { + unused.push_back(i); + } + + vertex_to_pos_.resize(n + 1); + pos_to_vertex_.resize(m); + for (int i = 0; i < m; ++i) { + pos_to_vertex_[i].resize(n + 1); + } + + std::set current_vs; + for (int i = 0; i < m; ++i) { + if (i > 0) { + for (int j = 0; j < n + 1; ++j) { + pos_to_vertex_[i][j] = pos_to_vertex_[i - 1][j]; + } + } + const std::vector& entering_vs = entering_vss_[i]; + for (size_t j = 0; j < entering_vs.size(); ++j) { + int v = entering_vs[j]; + current_vs.insert(v); + int u = unused.back(); + unused.pop_back(); + vertex_to_pos_[v] = u; + pos_to_vertex_[i][u] = v; + } + + if (static_cast(current_vs.size()) > max_frontier_size_) { + max_frontier_size_ = current_vs.size(); + } + + const std::vector& leaving_vs = leaving_vss_[i]; + + frontier_vss_.push_back(std::vector()); + std::vector& vs = frontier_vss_.back(); + remaining_vss_.push_back(std::vector()); + std::vector& rs = remaining_vss_.back(); + for (std::set::const_iterator itor = current_vs.begin(); + itor != current_vs.end(); ++itor) { + vs.push_back(*itor); + + bool found_leaving = false; + for (size_t j = 0; j < leaving_vs.size(); ++j) { + int v = leaving_vs[j]; + if (v == *itor) { + found_leaving = true; + break; + } + } + if (!found_leaving) { + rs.push_back(*itor); + } + } + + for (size_t j = 0; j < leaving_vs.size(); ++j) { + int v = leaving_vs[j]; + current_vs.erase(v); + unused.push_back(vertex_to_pos_[v]); + } + } + } + + public: + FrontierManager(const graphillion::Digraph& graph) : graph_(graph) { construct(); } + + // This function returns the maximum frontier size. + int getMaxFrontierSize() const { return max_frontier_size_; } + + // This function returns the vector that stores the vertex numbers + // that newly enter the frontier when processing the (index)-th edge. + const std::vector& getEnteringVs(int index) const { + return entering_vss_[index]; + } + + // This function returns the vector that stores the vertex numbers + // that leave the frontier after the (index)-th edge is processed. + const std::vector& getLeavingVs(int index) const { + return leaving_vss_[index]; + } + + // This function returns the vector that stores the vertex numbers + // that leave the frontier after the (index)-th edge is processed. + const std::vector& getFrontierVs(int index) const { + return frontier_vss_[index]; + } + + // This function returns the vector that stores ... + // + const std::vector& getRemainingVs(int index) const { + return remaining_vss_[index]; + } + + // This function translates the vertex number to the position + // in the PodArray used by FrontierExampleSpec. + int vertexToPos(int v) const { return vertex_to_pos_[v]; } + + int posToVertex(int index, int pos) const { + return pos_to_vertex_[index][pos]; + } + + int getVerticesEnteringLevel(short v) const { + for (size_t i = 0; i < entering_vss_.size(); ++i) { + for (size_t j = 0; j < entering_vss_[i].size(); ++j) { + if (entering_vss_[i][j] == v) { + return static_cast(i); + } + } + } + return -1; + } + + int getAllVerticesEnteringLevel() const { + int n = static_cast(entering_vss_.size()); + + for (int i = n - 1; i >= 0; --i) { + if (entering_vss_[i].size() > 0) { + return i; + } + } + return -1; + } + + void print() { + for (int i = 0; i < graph_.edgeSize(); ++i) { + std::cout << "["; + for (size_t j = 0; j < entering_vss_[i].size(); ++j) { + std::cout << entering_vss_[i][j] << ", "; + } + std::cout << "]"; + std::cout << "["; + for (size_t j = 0; j < leaving_vss_[i].size(); ++j) { + std::cout << leaving_vss_[i][j] << ", "; + } + std::cout << "]"; + std::cout << "["; + for (size_t j = 0; j < frontier_vss_[i].size(); ++j) { + std::cout << frontier_vss_[i][j] << ", "; + } + std::cout << "]"; + std::cout << "["; + for (size_t j = 0; j < remaining_vss_[i].size(); ++j) { + std::cout << remaining_vss_[i][j] << ", "; + } + std::cout << "]" << std::endl; + } + + for (int v = 1; v <= graph_.vertexSize(); ++v) { + std::cout << vertex_to_pos_[v] << ", "; + } + + std::cout << "max f size = " << max_frontier_size_ << std::endl; + } +}; + +#endif // __FRONTIER_MANAGER_HPP diff --git a/src/graphillion/digraphs/FrontierRootedForest.hpp b/src/graphillion/digraphs/FrontierRootedForest.hpp new file mode 100644 index 0000000..f7d4208 --- /dev/null +++ b/src/graphillion/digraphs/FrontierRootedForest.hpp @@ -0,0 +1,222 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FRONTIER_FOREST_HPP +#define FRONTIER_FOREST_HPP + +#include +#include +#include + +#include "FrontierData.hpp" +#include "FrontierManager.hpp" +#include "subsetting/DdSpec.hpp" +#include "Digraph.hpp" + +using namespace tdzdd; + +typedef unsigned short ushort; + +class FrontierRootedForestSpec + : public tdzdd::PodArrayDdSpec { + private: + // input graph + const graphillion::Digraph& graph_; + // root verteces + const std::set roots; + // spanning forest or not + bool is_spanning; + // number of vertices + const short n_; + // number of edges + const int m_; + + const FrontierManager fm_; + + // This function gets deg of v. + short getIndeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].indeg; + } + + short getOutdeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].outdeg; + } + + // This function sets deg of v to be d. + void setIndeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].indeg = d; + } + + void setOutdeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].outdeg = d; + } + + // This function gets comp of v. + ushort getComp(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].comp; + } + + // This function sets comp of v to be c. + void setComp(DirectedFrontierData* data, short v, ushort c) const { + data[fm_.vertexToPos(v)].comp = c; + } + + void initializeData(DirectedFrontierData* data) const { + for (int i = 0; i < fm_.getMaxFrontierSize(); ++i) { + data[i].indeg = 0; + data[i].outdeg = 0; + data[i].comp = 0; + } + } + + public: + FrontierRootedForestSpec(const graphillion::Digraph& graph, + const std::set& _roots, + bool _is_spanning) + : graph_(graph), + roots(_roots), + is_spanning(_is_spanning), + n_(static_cast(graph_.vertexSize())), + m_(graph_.edgeSize()), + fm_(graph_) { + if (n_ >= (1 << 16)) { + std::cerr << "The number of vertices must be smaller than 2^15." + << std::endl; + exit(1); + } + setArraySize(fm_.getMaxFrontierSize()); + } + + int getRoot(DirectedFrontierData* data) const { + initializeData(data); + return m_; + } + + int getChild(DirectedFrontierData* data, int level, int value) const { + assert(1 <= level && level <= m_); + + // edge index (starting from 0) + const int edge_index = m_ - level; + // edge that we are processing. + // The endpoints of "edge" are edge.v1 and edge.v2. + const graphillion::Digraph::EdgeInfo& edge = graph_.edgeInfo(edge_index); + + // initialize deg and comp of the vertices newly entering the frontier + const std::vector& entering_vs = fm_.getEnteringVs(edge_index); + for (size_t i = 0; i < entering_vs.size(); ++i) { + int v = entering_vs[i]; + // initially the value of deg is 0 + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + // initially the value of comp is the vertex number itself + setComp(data, v, static_cast(v)); + } + + // vertices on the frontier + const std::vector& frontier_vs = fm_.getFrontierVs(edge_index); + + if (value == 1) { // if we take the edge (go to 1-arc) + // increment deg of v1 and v2 (recall that edge = {v1, v2}) + auto outdeg1 = getOutdeg(data, edge.v1); + auto indeg2 = getIndeg(data, edge.v2); + + setIndeg(data, edge.v2, indeg2 + 1); + setOutdeg(data, edge.v1, outdeg1 + 1); + + ushort c1 = getComp(data, edge.v1); + ushort c2 = getComp(data, edge.v2); + + if (c1 == c2) { // Any cycle must not occur. + return 0; + } + + if (c1 != c2) { // connected components c1 and c2 become connected + ushort cmin = std::min(c1, c2); + ushort cmax = std::max(c1, c2); + + // replace component number cmin with cmax + for (size_t i = 0; i < frontier_vs.size(); ++i) { + int v = frontier_vs[i]; + if (getComp(data, v) == cmin) { + setComp(data, v, cmax); + } + } + } + } + + // vertices that are leaving the frontier + const std::vector& leaving_vs = fm_.getLeavingVs(edge_index); + for (size_t i = 0; i < leaving_vs.size(); ++i) { + int v = leaving_vs[i]; + + // vertex in a spanning forest must not be isolated + if (is_spanning) { + if (getIndeg(data, v) == 0 && + getOutdeg(data, v) == 0) { // the degree of v must be at least 1 + return 0; + } + } + + if (roots.size()) { + // roots are specified. + if (roots.count(v)) { + // the in-degree of a root vertex must be 0. + if (getIndeg(data, v) > 0) { + return 0; + } + // the out-degree of a root vertex must be positive. + if (getOutdeg(data, v) == 0) { + return 0; + } + } else { + // the in-degree of a non-root vertex must be 1 or the vertex is + // isolated. + bool ok = (getIndeg(data, v) == 1) || + (getIndeg(data, v) == 0 && getOutdeg(data, v) == 0); + if (!ok) { + return 0; + } + } + } else { + // root are not specified. + // the in-degree of a vertex must be 0 or 1. + if (getIndeg(data, v) > 1) { + return 0; + } + } + + // Since deg and comp of v are never used until the end, + // we erase the values. + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + setComp(data, v, 0); + } + if (level == 1) { + return -1; + } + assert(level - 1 > 0); + return level - 1; + } +}; + +#endif // FRONTIER_FOREST_HPP diff --git a/src/graphillion/digraphs/FrontierRootedTree.hpp b/src/graphillion/digraphs/FrontierRootedTree.hpp new file mode 100644 index 0000000..63b3f91 --- /dev/null +++ b/src/graphillion/digraphs/FrontierRootedTree.hpp @@ -0,0 +1,289 @@ +/** +Copyright (c) 2021 ComputerAlgorithmsGroupAtKyotoU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FRONTIER_TREE_HPP +#define FRONTIER_TREE_HPP + +#include +#include + +#include "FrontierData.hpp" +#include "FrontierManager.hpp" +#include "subsetting/DdSpec.hpp" +#include "Digraph.hpp" + +using namespace tdzdd; + +typedef unsigned short ushort; + +typedef unsigned short FrontierTreeData; + +class FrontierRootedTreeSpec + : public tdzdd::PodArrayDdSpec { + private: + // input graph + const graphillion::Digraph& graph_; + // number of vertices + const short n_; + // number of edges + const int m_; + + // root node + const ushort root_; + + const bool isSpanning_; + + const FrontierManager fm_; + + // This function gets deg of v. + short getIndeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].indeg; + } + + short getOutdeg(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].outdeg; + } + + // This function sets deg of v to be d. + void setIndeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].indeg = d; + } + + void setOutdeg(DirectedFrontierData* data, short v, short d) const { + data[fm_.vertexToPos(v)].outdeg = d; + } + + void resetDeg(DirectedFrontierData* data, short v) const { + setIndeg(data, v, 0); + setOutdeg(data, v, 0); + } + + // This function gets comp of v. + ushort getComp(DirectedFrontierData* data, short v) const { + return data[fm_.vertexToPos(v)].comp; + } + + // This function sets comp of v to be c. + void setComp(DirectedFrontierData* data, short v, ushort c) const { + data[fm_.vertexToPos(v)].comp = c; + } + + void initializeData(DirectedFrontierData* data) const { + for (int i = 0; i < fm_.getMaxFrontierSize(); ++i) { + data[i].indeg = 0; + data[i].outdeg = 0; + data[i].comp = 0; + } + } + + public: + FrontierRootedTreeSpec(const graphillion::Digraph& graph, ushort root, + bool isSpanning) + : graph_(graph), + n_(graph_.vertexSize()), + m_(graph_.edgeSize()), + root_(root), + isSpanning_(isSpanning), + fm_(graph_) { + if (n_ >= (1 << 15)) { + std::cerr << "The number of vertices must be smaller than 2^15." + << std::endl; + exit(1); + } + setArraySize(fm_.getMaxFrontierSize()); + } + + int getRoot(DirectedFrontierData* data) const { + initializeData(data); + return m_; + } + + int getChild(DirectedFrontierData* data, int level, int value) const { + assert(1 <= level && level <= m_); + + // edge index (starting from 0) + const int edge_index = m_ - level; + // edge that we are processing. + // The endpoints of "edge" are edge.v1 and edge.v2. + const graphillion::Digraph::EdgeInfo& edge = graph_.edgeInfo(edge_index); + + // initialize deg and comp of the vertices newly entering the frontier + const std::vector& entering_vs = fm_.getEnteringVs(edge_index); + for (size_t i = 0; i < entering_vs.size(); ++i) { + int v = entering_vs[i]; + // initially the value of comp is the vertex number itself + resetDeg(data, v); + setComp(data, v, static_cast(v)); + } + + // vertices on the frontier + const std::vector& frontier_vs = fm_.getFrontierVs(edge_index); + + if (value == 1) { // if we take the edge (go to 1-arc) + ushort c1 = getComp(data, edge.v1); + ushort c2 = getComp(data, edge.v2); + + if (c1 == c2) { // Any cycle must not occur. + return 0; + } + + // increment deg of v1 and v2 (recall that edge = {v1, v2}) + auto outdeg1 = getOutdeg(data, edge.v1); + auto indeg2 = getIndeg(data, edge.v2); + setIndeg(data, edge.v2, indeg2 + 1); + setOutdeg(data, edge.v1, outdeg1 + 1); + + if (c1 != c2) { // connected components c1 and c2 become connected + ushort cmin = std::min(c1, c2); + ushort cmax = std::max(c1, c2); + + // replace component number cmin with cmax + for (size_t i = 0; i < frontier_vs.size(); ++i) { + int v = frontier_vs[i]; + if (getComp(data, v) == cmin) { + setComp(data, v, cmax); + } + } + } + } + + // vertices that are leaving the frontier + const std::vector& leaving_vs = fm_.getLeavingVs(edge_index); + for (size_t i = 0; i < leaving_vs.size(); ++i) { + int v = leaving_vs[i]; + + if (isSpanning_) { + if (getIndeg(data, v) + getOutdeg(data, v) == + 0) { // the degree of v must be at least 1 + return 0; + } + } + + if (v == root_) { + // the in-degree of root node must be 0. + if (getIndeg(data, v) != 0) { + return 0; + } + // the out-degdee of root node must not be 0. + // if (getOutdeg(data, v) == 0) { + // return 0; + //} + } else { + // if v has no incoming edge, v cant have outgoing edges. + if (getIndeg(data, v) == 0 && getOutdeg(data, v) > 0) { + return 0; + } + // in-degree of non-root node must be 0 or 1. + if (getIndeg(data, v) > 1) { + return 0; + } + } + + // The degree of v must be 0 or 2. + // if (getDeg(data, v) != 0 && getDeg(data, v) != 2) { + // return 0; + //} + bool comp_found = false; + bool deg_found = false; + bool frontier_exists = false; + // Search a vertex that has the component number same as that of v. + // Also check whether a vertex whose degree is at least 1 exists + // on the frontier. + for (size_t j = 0; j < frontier_vs.size(); ++j) { + int w = frontier_vs[j]; + if (w == v) { // skip if w is the leaving vertex + continue; + } + // skip if w is one of the vertices that + // has already leaved the frontier + bool found_leaved = false; + for (size_t k = 0; k < i; ++k) { + if (w == leaving_vs[k]) { + found_leaved = true; + break; + } + } + if (found_leaved) { + continue; + } + frontier_exists = true; + // w has the component number same as that of v + if (getComp(data, w) == getComp(data, v)) { + comp_found = true; + } + // The degree of w is at least 1. + if (getIndeg(data, w) + getOutdeg(data, w)) { + deg_found = true; + } + if (deg_found && comp_found) { + break; + } + } + // There is no vertex that has the component number + // same as that of v. That is, the connected component + // of v becomes determined. + if (!comp_found) { + // Here, deg of v is 0 or 2. If deg of v is 0, + // this means that v is isolated. + // If deg of v is 2, and there is a vertex whose + // deg is at least 1, this means that there is a + // connected component other than that of v. + // That is, the generated subgraph is not connected. + // Then, we return the 0-terminal. + // assert(getDeg(data, v) == 0 || getDeg(data, v) == 2); + if (getIndeg(data, v) + getOutdeg(data, v) > 0 && deg_found) { + return 0; // return the 0-terminal. + } else if (getIndeg(data, v) + + getOutdeg(data, v)) { // If deg of v is 2, + // and there is no vertex whose deg is at least 1 + // a single cycle is completed. + // Then, we return the 1-terminal + + if (isSpanning_) { + if (frontier_exists) { + return 0; + } else { + return -1; // return the 1-terminal + } + } else { + return -1; // return the 1-terminal + } + } + } + + // Since deg and comp of v are never used until the end, + // we erase the values. + resetDeg(data, v); + setComp(data, v, 0); + } + if (level == 1) { + // If we come here, the edge set is empty (taking no edge). + return -1; // return the 1-terminal + } + + assert(level - 1 > 0); + return level - 1; + } +}; + +#endif // FRONTIER_TREE_HPP