diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index ef3746a76..dae170a70 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -326,3 +326,4 @@ Yogish Sekhar Zubin Jain Muzaffer Kal Yilin Li +Shashvat Prabhu diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad2178203..b4f4be9aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -183,7 +183,6 @@ set(HEADERS V3String.h V3Subst.h V3SymTable.h - V3TSP.h V3Table.h V3Task.h V3ThreadPool.h @@ -358,7 +357,6 @@ set(COMMON_SOURCES V3StatsReport.cpp V3String.cpp V3Subst.cpp - V3TSP.cpp V3Table.cpp V3Task.cpp V3ThreadPool.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 08c8899fc..dcd78a431 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -337,7 +337,6 @@ RAW_OBJS_PCH_ASTNOMT = \ V3SplitVar.o \ V3StackCount.o \ V3Subst.o \ - V3TSP.o \ V3Table.o \ V3Task.o \ V3Timing.o \ diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 8eb82b31f..11f2ac14a 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -18,8 +18,6 @@ #include "V3EmitCFunc.h" -#include "V3TSP.h" - #include #include diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp deleted file mode 100644 index 4244a5676..000000000 --- a/src/V3TSP.cpp +++ /dev/null @@ -1,701 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Implementation of Christofides algorithm to -// approximate the solution to the traveling salesman problem. -// -// ISSUES: This isn't exactly Christofides algorithm; see the TODO -// in perfectMatching(). True minimum-weight perfect matching -// would produce a better result. How much better is TBD. -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of either the GNU Lesser General Public License Version 3 -// or the Perl Artistic License Version 2.0. -// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder -// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -// -//************************************************************************* - -#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT - -#include "V3TSP.h" - -#include "V3File.h" -#include "V3Graph.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -VL_DEFINE_DEBUG_FUNCTIONS; - -//###################################################################### -// Support classes - -namespace V3TSP { -static uint32_t s_edgeIdNext = 0; - -static void selfTestStates(); -static void selfTestString(); -} // namespace V3TSP - -// Vertex that tracks a per-vertex key -template -class TspVertexTmpl final : public V3GraphVertex { - VL_RTTI_IMPL(TspVertexTmpl, V3GraphVertex) -private: - const T_Key m_key; - -public: - TspVertexTmpl(V3Graph* graphp, const T_Key& k) - : V3GraphVertex{graphp} - , m_key{k} {} - ~TspVertexTmpl() override = default; - const T_Key& key() const { return m_key; } - -private: - VL_UNCOPYABLE(TspVertexTmpl); -}; - -// TspGraphTmpl represents a complete graph, templatized to work with -// different T_Key types. -template -class TspGraphTmpl final : public V3Graph { -public: - // TYPES - using Vertex = TspVertexTmpl; - - enum VertexState : uint32_t { CLEAR = 0, MST_VISITED = 1, UNMATCHED_ODD = 2 }; - - // MEMBERS - std::unordered_map m_vertices; // T_Key to Vertex lookup map - - // CONSTRUCTORS - TspGraphTmpl() - : V3Graph{} {} - ~TspGraphTmpl() override = default; - - // METHODS - void addVertex(const T_Key& key) { - const bool newEntry = m_vertices.emplace(key, new Vertex{this, key}).second; - UASSERT(newEntry, "Vertex already exists with same key"); - } - - // For purposes of TSP, we are using non-directional graphs. - // Map that onto the normally-directional V3Graph by creating - // a matched pairs of opposite-directional edges to represent - // each non-directional edge: - void addEdge(const T_Key& from, const T_Key& to, int cost) { -#if VL_DEBUG // Hot, so only in debug - UASSERT(from != to, "Adding edge would form a loop"); - UASSERT(cost >= 0, "Negative weight edge"); -#endif - Vertex* const fp = findVertex(from); - Vertex* const tp = findVertex(to); - - // No need to dedup edges. - // The only time we may create duplicate edges is when - // combining the MST with the perfect-matched pairs, - // and in that case, we want to permit duplicate edges. - const uint32_t edgeId = ++V3TSP::s_edgeIdNext; - - // We want to be able to compare edges quickly for a total - // ordering, so pre-compute a sorting key and store it in - // the edge user field. We also want easy access to the 'id' - // which uniquely identifies a single bidir edge. Luckily we - // can do both efficiently. - // cppcheck-suppress badBitmaskCheck - const uint64_t userValue = (static_cast(cost) << 32) | edgeId; - (new V3GraphEdge{this, fp, tp, cost})->user(userValue); - (new V3GraphEdge{this, tp, fp, cost})->user(userValue); - } - - static uint32_t getEdgeId(const V3GraphEdge* edgep) { - return static_cast(edgep->user()); - } - - // cppcheck-suppress duplInheritedMember - bool empty() const { return m_vertices.empty(); } - - const std::list keysToVertexList(const std::vector& odds) { - std::list vertices; - for (unsigned i = 0; i < odds.size(); ++i) vertices.push_back(findVertex(odds.at(i))); - return vertices; - } - -private: - // We will keep sorted lists of edges as vectors - using EdgeList = std::vector; - - static bool edgeCmp(const V3GraphEdge* ap, const V3GraphEdge* bp) { - // We pre-computed these when adding the edge to sort first by cost, then by identity - return ap->user() > bp->user(); - } - - struct EdgeListCmp final { - bool operator()(const EdgeList* ap, const EdgeList* bp) const { - // Compare heads - return edgeCmp(bp->back(), ap->back()); - } - }; - - static Vertex* castVertexp(V3GraphVertex* vxp) { return static_cast(vxp); } - static const Vertex* castVertexp(const V3GraphVertex* vxp) { - return static_cast(vxp); - } - -public: - // From *this, populate *mstp with the minimum spanning tree. - // *mstp must be initially empty. - void makeMinSpanningTree(TspGraphTmpl* mstp) { - UASSERT(mstp->empty(), "Output graph must start empty"); - - // Use Prim's algorithm to efficiently construct the MST. - - uint32_t vertCount = 0; - for (V3GraphVertex& vtx : vertices()) { - mstp->addVertex(castVertexp(&vtx)->key()); - vertCount++; - } - - // Allocate storage for per vertex edge lists up front. - std::vector allocatedEdgeLists{vertCount}; - - // Index of vertex in visitation order (used for indexing allocatedEdgeLists) - uint32_t vertIdx = 0; - - // We keep pending edges as a sorted set of sorted vectors. This allows us to find the - // lowest cost edge quickly, while also reducing the cost of inserting batches of new - // edges, which is what we need in this algorithm. - std::set pendingEdgeListps; - - const auto visit = [&](V3GraphVertex* vtxp) { -#ifdef VL_DEBUG // Very hot, so only in debug - UASSERT(vtxp->user() == VertexState::CLEAR, "Vertex visited twice"); -#endif - // Mark vertex as visited - vtxp->user(VertexState::MST_VISITED); - // Allocate new edge list - EdgeList* const newEdgesp = &allocatedEdgeLists[vertIdx++]; - // Gather out edges of this vertex - for (V3GraphEdge& edge : vtxp->outEdges()) { - // Don't add edges leading to vertices we already visited. This is a highly - // connected graph, so this greatly reduces the cost of maintaining the pending - // set. - if (edge.top()->user() == VertexState::MST_VISITED) continue; - newEdgesp->push_back(&edge); - } - // If no relevant out edges, then we are done - if (newEdgesp->empty()) return; - // Sort new edge list - std::sort(newEdgesp->begin(), newEdgesp->end(), edgeCmp); - // Add edge list to pending set - pendingEdgeListps.insert(newEdgesp); - }; - - // To start, choose an arbitrary vertex and visit it. - visit(vertices().frontp()); - - // Repeatedly find the least costly edge in the pending set. - // If it connects to an unvisited node, visit that node and update - // the pending edge set. If it connects to an already visited node, - // discard it and repeat again. - while (!pendingEdgeListps.empty()) { - // Grab lowest cost edge list - auto it = pendingEdgeListps.begin(); - - // Grab lowest cost edge - EdgeList* const bestEdgeListp = *it; - const V3GraphEdge* const bestEdgep = bestEdgeListp->back(); - - // Remove the lowest cost edge list. We will remove its lowest cost element, and either - // we are done with (if it had a single element) it in which case it will be discarded, - // or the cost of the new head element might be different, so we will need to re-insert - // it in the right place. In either case, it needs to be removed. - pendingEdgeListps.erase(it); - - // If the lowest cost edge list is not a singleton list, then pop the lowest cost - // edge and re-insert the remaining edge list into the pending set. - if (bestEdgeListp->size() > 1) { - bestEdgeListp->pop_back(); - pendingEdgeListps.insert(bestEdgeListp); - } - - // Grab the target vertex - Vertex* const neighborp = castVertexp(bestEdgep->top()); - - // If the neighbour is not yet visited - if (neighborp->user() == VertexState::CLEAR) { - // Visit it - visit(neighborp); - - // Create the edge in our output MST graph - Vertex* const from_vertexp = castVertexp(bestEdgep->fromp()); - mstp->addEdge(from_vertexp->key(), neighborp->key(), bestEdgep->weight()); - -#if VL_DEBUG // Very hot loop, so only in debug - UASSERT(from_vertexp->user() == MST_VISITED, - "bestEdgep->fromp() should be already seen"); -#endif - } - } - - UASSERT(vertIdx == vertCount, "Should have visited all vertices"); - } - - // Populate *outp with a minimal perfect matching of *this. - // *outp must be initially empty. - void perfectMatching(const std::vector& oddKeys, TspGraphTmpl* outp) { - UASSERT(outp->empty(), "Output graph must start empty"); - - const std::list& odds = keysToVertexList(oddKeys); - UASSERT(odds.size() % 2 == 0, "number of odd-order nodes should be even"); - - for (Vertex* const vtxp : odds) { - outp->addVertex(vtxp->key()); - vtxp->user(VertexState::UNMATCHED_ODD); - } - - // TODO: The true Christofides algorithm calls for minimum-weight - // perfect matching. Instead, we have a simple greedy algorithm - // which might get close to the minimum, maybe, with luck? - // - // TODO: Revisit this. It's possible to compute the true minimum in - // N*N*log(N) time using variants of the Blossom algorithm. - // Implementing Blossom looks hard, maybe we can use an existing - // open source implementation -- for example the "LEMON" library - // which has a TSP solver. - - // ----- - - // Gather and sort all edges. We use a vector then sort, because this is faster than a - // sorted set. Reuse the comparator from Prim's routine (note it a 'greater', not a - // 'lesser' comparator). The logic is the same here. - // - // Note that there are two V3GraphEdge's representing a single bidir edge. While we could - // just add both to the pending list and get the same result, we will only add one (based - // on fast pointer comparison - this still yields deterministic results), in order to - // reduce the size of the working set. - std::vector pendingEdges; - - for (Vertex* const fromp : odds) { - for (V3GraphEdge& edge : fromp->outEdges()) { - Vertex* const top = castVertexp(edge.top()); - // There are two edges (in both directions) between these two vertices. Keep one. - if (fromp > top) continue; - // We only care about edges between the odd-order vertices - if (top->user() != VertexState::UNMATCHED_ODD) continue; - // Add to candidate list - pendingEdges.push_back(&edge); - } - } - - // Sort reverse iterators. This yields ascending order with a 'greater' comparator. - std::sort(pendingEdges.rbegin(), pendingEdges.rend(), edgeCmp); - - // Iterate over all edges, in order from low to high cost. - // For any edge whose ends are both odd-order vertices which - // haven't been matched yet, match them. - for (V3GraphEdge* const edgep : pendingEdges) { - Vertex* const fromp = castVertexp(edgep->fromp()); - Vertex* const top = castVertexp(edgep->top()); - if (fromp->user() == VertexState::UNMATCHED_ODD - && top->user() == VertexState::UNMATCHED_ODD) { - outp->addEdge(fromp->key(), top->key(), edgep->weight()); - fromp->user(VertexState::CLEAR); - top->user(VertexState::CLEAR); - } - } - } - - void combineGraph(const TspGraphTmpl& g) { - std::unordered_set edges_done; - for (const V3GraphVertex& vtx : g.vertices()) { - const Vertex* const fromp = castVertexp(&vtx); - for (const V3GraphEdge& edge : fromp->outEdges()) { - const Vertex* const top = castVertexp(edge.top()); - if (edges_done.insert(getEdgeId(&edge)).second) { - addEdge(fromp->key(), top->key(), edge.weight()); - } - } - } - } - - void findEulerTourRecurse(std::unordered_set* markedEdgesp, Vertex* startp, - std::vector* sortedOutp) { - Vertex* cur_vertexp = startp; - - // Go on a random tour. Fun! - std::vector tour; - do { - UINFO(6, "Adding " << cur_vertexp->key() << " to tour."); - tour.push_back(cur_vertexp); - - // Look for an arbitrary edge we've not yet marked - for (V3GraphEdge& edge : cur_vertexp->outEdges()) { - const uint32_t edgeId = getEdgeId(&edge); - if (markedEdgesp->end() == markedEdgesp->find(edgeId)) { - // This edge is not yet marked, so follow it. - markedEdgesp->insert(edgeId); - Vertex* const neighborp = castVertexp(edge.top()); - UINFO(6, "following edge " << edgeId << " from " << cur_vertexp->key() - << " to " << neighborp->key()); - cur_vertexp = neighborp; - goto found; - } - } - v3fatalSrc("No unmarked edges found in tour"); - found:; - } while (cur_vertexp != startp); - UINFO(6, "stopped, got back to start of tour @ " << cur_vertexp->key()); - - // Look for nodes on the tour that still have - // un-marked edges. If we find one, recurse. - for (Vertex* vxp : tour) { - bool recursed; - do { - recursed = false; - // Look for an arbitrary edge at vxp we've not yet marked - for (V3GraphEdge& edge : vxp->outEdges()) { - const uint32_t edgeId = getEdgeId(&edge); - if (markedEdgesp->end() == markedEdgesp->find(edgeId)) { - UINFO(6, "Recursing."); - findEulerTourRecurse(markedEdgesp, vxp, sortedOutp); - recursed = true; - goto recursed; - } - } - recursed:; - } while (recursed); - sortedOutp->push_back(vxp->key()); - } - - if (debug() >= 6) { - UINFO(0, "Tour was:"); - for (const Vertex* vxp : tour) std::cout << "- " << vxp->key() << '\n'; - std::cout << "-\n"; - } - } - - void dumpGraph(std::ostream& os, const string& nameComment) const { - // UINFO(0) as controlled by caller - os << "At " << nameComment << ", dumping graph. Keys:\n"; - for (const V3GraphVertex& vtx : vertices()) { - const Vertex* const tspvp = castVertexp(&vtx); - os << " " << tspvp->key() << '\n'; - for (const V3GraphEdge& edge : tspvp->outEdges()) { - const Vertex* const neighborp = castVertexp(edge.top()); - os << " has edge " << getEdgeId(&edge) << " to " << neighborp->key() << '\n'; - } - } - } - void dumpGraphFilePrefixed(const string& nameComment) const { - if (dumpLevel()) { - const string filename = v3Global.debugFilename(nameComment) + ".txt"; - const std::unique_ptr logp{V3File::new_ofstream(filename)}; - if (logp->fail()) v3fatal("Can't write file: " << filename); - dumpGraph(*logp, nameComment); - } - } - - void findEulerTour(std::vector* sortedOutp) { - UASSERT(sortedOutp->empty(), "Output graph must start empty"); - if (::dumpGraphLevel() >= 6) dumpDotFilePrefixed("findEulerTour"); - std::unordered_set markedEdges; - // Pick a start node - Vertex* const start_vertexp = castVertexp(vertices().frontp()); - findEulerTourRecurse(&markedEdges, start_vertexp, sortedOutp); - } - - std::vector getOddDegreeKeys() const { - std::vector result; - for (const V3GraphVertex& vtx : vertices()) { - const Vertex* const tspvp = castVertexp(&vtx); - const uint32_t degree = vtx.outEdges().size(); - if (degree & 1) result.push_back(tspvp->key()); - } - return result; - } - -private: - Vertex* findVertex(const T_Key& key) const { - const auto it = m_vertices.find(key); - UASSERT(it != m_vertices.end(), "Vertex not found"); - return it->second; - } - VL_UNCOPYABLE(TspGraphTmpl); -}; - -//###################################################################### -// Main algorithm - -void V3TSP::tspSort(const V3TSP::StateVec& states, V3TSP::StateVec* resultp) VL_MT_SAFE { - UASSERT(resultp->empty(), "Output graph must start empty"); - - // Make this TSP implementation work for graphs of size 0 or 1 - // which, unfortunately, is a special case as the following - // code assumes >= 2 nodes. - if (states.empty()) return; - if (states.size() == 1) { - resultp->push_back(*(states.begin())); - return; - } - - // Build the initial graph from the starting state set. - using Graph = TspGraphTmpl; - Graph graph; - for (const auto& state : states) graph.addVertex(state); - for (V3TSP::StateVec::const_iterator it = states.begin(); it != states.end(); ++it) { - for (V3TSP::StateVec::const_iterator jt = it; jt != states.end(); ++jt) { - if (it == jt) continue; - graph.addEdge(*it, *jt, (*it)->cost(*jt)); - } - } - - // Create the minimum spanning tree - Graph minGraph; - graph.makeMinSpanningTree(&minGraph); - if (dumpGraphLevel() >= 6) minGraph.dumpGraphFilePrefixed("minGraph"); - - const std::vector oddDegree = minGraph.getOddDegreeKeys(); - Graph matching; - graph.perfectMatching(oddDegree, &matching); - if (dumpGraphLevel() >= 6) matching.dumpGraphFilePrefixed("matching"); - - // Adds edges to minGraph, the resulting graph will have even number of - // edge counts at every vertex: - minGraph.combineGraph(matching); - - V3TSP::StateVec prelim_result; - minGraph.findEulerTour(&prelim_result); - - UASSERT(prelim_result.size() >= states.size(), "Algorithm size error"); - - // Discard duplicate nodes that the Euler tour might contain. - { - std::unordered_set seen; - for (V3TSP::StateVec::iterator it = prelim_result.begin(); it != prelim_result.end(); - ++it) { - const TspStateBase* const elemp = *it; - const auto itFoundPair = seen.insert(elemp); - if (itFoundPair.second) resultp->push_back(elemp); - } - } - - UASSERT(resultp->size() == states.size(), "Algorithm size error"); - - // Find the most expensive arc and rotate the list so that the most - // expensive arc connects the last and first elements. (Since we're not - // modeling something that actually cycles back, we don't need to pay - // that cost at all.) - { - unsigned max_cost = 0; - unsigned max_cost_idx = 0; - for (unsigned i = 0; i < resultp->size(); ++i) { - const TspStateBase* const ap = (*resultp)[i]; - const TspStateBase* const bp - = (i + 1 == resultp->size()) ? (*resultp)[0] : (*resultp)[i + 1]; - const unsigned cost = ap->cost(bp); - if (cost > max_cost) { - max_cost = cost; - max_cost_idx = i; - } - } - - if (max_cost_idx == resultp->size() - 1) { - // List is already rotated for minimum cost. stop. - return; - } - - V3TSP::StateVec new_result; - unsigned i = max_cost_idx + 1; - UASSERT(i < resultp->size(), "Algorithm size error"); - while (i != max_cost_idx) { - new_result.push_back((*resultp)[i]); - i++; - if (i >= resultp->size()) i = 0; - } - new_result.push_back((*resultp)[i]); - - UASSERT(resultp->size() == new_result.size(), "Algorithm size error"); - *resultp = new_result; - } -} - -//###################################################################### -// Self Tests - -class TspTestState final : public V3TSP::TspStateBase { -public: - TspTestState(unsigned xpos, unsigned ypos) - : m_xpos{xpos} - , m_ypos{ypos} - , m_serial{++s_serialNext} {} - ~TspTestState() override = default; - int cost(const TspStateBase* otherp) const override VL_MT_SAFE { - return cost(dynamic_cast(otherp)); - } - static unsigned diff(unsigned a, unsigned b) VL_PURE { - if (a > b) return a - b; - return b - a; - } - int cost(const TspTestState* otherp) const VL_PURE { - // For test purposes, each TspTestState is merely a point - // on the Cartesian plane; cost is the linear distance - // between two points. - unsigned xabs; - unsigned yabs; - xabs = diff(otherp->m_xpos, m_xpos); - yabs = diff(otherp->m_ypos, m_ypos); - return std::lround(std::sqrt(xabs * xabs + yabs * yabs)); - } - unsigned xpos() const { return m_xpos; } - unsigned ypos() const { return m_ypos; } - - bool operator<(const TspStateBase& other) const override { - return operator<(dynamic_cast(other)); - } - bool operator<(const TspTestState& other) const { return m_serial < other.m_serial; } - -private: - const unsigned m_xpos; - const unsigned m_ypos; - const unsigned m_serial; - static unsigned s_serialNext; -}; - -unsigned TspTestState::s_serialNext = 0; - -void V3TSP::selfTestStates() { - // Linear test -- coords all along the x-axis - { - V3TSP::StateVec states; - const TspTestState s10{10, 0}; - const TspTestState s60{60, 0}; - const TspTestState s20{20, 0}; - const TspTestState s100{100, 0}; - const TspTestState s5{5, 0}; - states.push_back(&s10); - states.push_back(&s60); - states.push_back(&s20); - states.push_back(&s100); - states.push_back(&s5); - - V3TSP::StateVec result; - tspSort(states, &result); - - V3TSP::StateVec expect; - expect.push_back(&s100); - expect.push_back(&s60); - expect.push_back(&s20); - expect.push_back(&s10); - expect.push_back(&s5); - if (VL_UNCOVERABLE(expect != result)) { - for (V3TSP::StateVec::iterator it = result.begin(); it != result.end(); ++it) { - const TspTestState* const statep = dynamic_cast(*it); - cout << statep->xpos() << " "; - } - cout << endl; - v3fatalSrc("TSP linear self-test fail. Result (above) did not match expectation."); - } - } - - // Second test. Coords are distributed in 2D space. - // Test that tspSort() will rotate the list for minimum cost. - { - V3TSP::StateVec states; - const TspTestState a{0, 0}; - const TspTestState b{100, 0}; - const TspTestState c{200, 0}; - const TspTestState d{200, 100}; - const TspTestState e{150, 150}; - const TspTestState f{0, 150}; - const TspTestState g{0, 100}; - - states.push_back(&a); - states.push_back(&b); - states.push_back(&c); - states.push_back(&d); - states.push_back(&e); - states.push_back(&f); - states.push_back(&g); - - V3TSP::StateVec result; - tspSort(states, &result); - - V3TSP::StateVec expect; - expect.push_back(&f); - expect.push_back(&g); - expect.push_back(&a); - expect.push_back(&b); - expect.push_back(&c); - expect.push_back(&d); - expect.push_back(&e); - - if (VL_UNCOVERABLE(expect != result)) { - for (V3TSP::StateVec::iterator it = result.begin(); it != result.end(); ++it) { - const TspTestState* const statep = dynamic_cast(*it); - cout << statep->xpos() << "," << statep->ypos() << " "; - } - cout << endl; - v3fatalSrc( - "TSP 2d cycle=false self-test fail. Result (above) did not match expectation."); - } - } -} - -void V3TSP::selfTestString() { - using Graph = TspGraphTmpl; - Graph graph; - graph.addVertex("0"); - graph.addVertex("1"); - graph.addVertex("2"); - graph.addVertex("3"); - - graph.addEdge("0", "1", 3943); - graph.addEdge("0", "2", 3456); - graph.addEdge("0", "3", 4920); - graph.addEdge("1", "2", 2730); - graph.addEdge("1", "3", 8199); - graph.addEdge("2", "3", 4130); - - Graph minGraph; - graph.makeMinSpanningTree(&minGraph); - if (dumpGraphLevel() >= 6) minGraph.dumpGraphFilePrefixed("minGraph"); - - const std::vector oddDegree = minGraph.getOddDegreeKeys(); - Graph matching; - graph.perfectMatching(oddDegree, &matching); - if (dumpGraphLevel() >= 6) matching.dumpGraphFilePrefixed("matching"); - - minGraph.combineGraph(matching); - - std::vector result; - minGraph.findEulerTour(&result); - - std::vector expect; - expect.emplace_back("0"); - expect.emplace_back("2"); - expect.emplace_back("1"); - expect.emplace_back("2"); - expect.emplace_back("3"); - - if (VL_UNCOVERABLE(expect != result)) { - for (const string& i : result) cout << i << " "; - cout << endl; - v3fatalSrc("TSP string self-test fail. Result (above) did not match expectation."); - } -} - -void V3TSP::selfTest() { - selfTestString(); - selfTestStates(); -} diff --git a/src/V3TSP.h b/src/V3TSP.h deleted file mode 100644 index 840f5fa96..000000000 --- a/src/V3TSP.h +++ /dev/null @@ -1,57 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Implementation of Christofides algorithm to -// approximate the solution to the traveling salesman problem. -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of either the GNU Lesser General Public License Version 3 -// or the Perl Artistic License Version 2.0. -// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder -// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -// -//************************************************************************* - -#ifndef VERILATOR_V3TSP_H_ -#define VERILATOR_V3TSP_H_ - -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Error.h" - -#include - -namespace V3TSP { -// Perform a "Traveling Salesman Problem" optimizing sort -// on any type you like -- so long as inherits from TspStateBase. - -class TspStateBase VL_NOT_FINAL { -public: - // This is the cost function that the TSP sort will minimize. - // All costs in V3TSP are int, chosen to match the type of - // V3GraphEdge::weight() which will reflect each edge's cost. - virtual int cost(const TspStateBase* otherp) const VL_MT_SAFE = 0; - - // This operator< must place a meaningless, arbitrary, but - // stable order on all TspStateBase's. It's used only to - // key maps so that iteration is stable, without relying - // on pointer values that could lead to nondeterminism. - virtual bool operator<(const TspStateBase& otherp) const = 0; - - virtual ~TspStateBase() = default; -}; - -using StateVec = std::vector; - -// Given an unsorted set of TspState's, sort them to minimize -// the transition cost for walking the sorted list. -void tspSort(const StateVec& states, StateVec* resultp) VL_MT_SAFE; - -void selfTest() VL_MT_DISABLED; -} // namespace V3TSP - -#endif // Guard diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 13bb95c6f..f94c8655d 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -103,7 +103,6 @@ #include "V3Stats.h" #include "V3String.h" #include "V3Subst.h" -#include "V3TSP.h" #include "V3Table.h" #include "V3Task.h" #include "V3ThreadPool.h" @@ -734,7 +733,6 @@ static bool verilate(const string& argString) { VHashSha256::selfTest(); VSpellCheck::selfTest(); V3Graph::selfTest(); - V3TSP::selfTest(); V3ScoreboardBase::selfTest(); V3Order::selfTestParallel(); V3ExecGraph::selfTest(); diff --git a/src/cppcheck-suppressions.txt b/src/cppcheck-suppressions.txt index 754ef3c4b..6d9ab321d 100644 --- a/src/cppcheck-suppressions.txt +++ b/src/cppcheck-suppressions.txt @@ -137,8 +137,6 @@ constParameterPointer:src/V3Tristate.cpp constParameterReference:src/V3Tristate.cpp constVariablePointer:src/V3Tristate.cpp constVariableReference:src/V3Tristate.cpp -constVariablePointer:src/V3TSP.cpp -constVariableReference:src/V3TSP.cpp constParameterPointer:src/V3Undriven.cpp constVariablePointer:src/V3Undriven.cpp constParameterPointer:src/V3Unroll.cpp