[med-svn] [Git][med-team/libhandlegraph][master] 8 commits: Turns out we need latest upstream commit to have the correct API for vg

Andreas Tille (@tille) gitlab at salsa.debian.org
Mon Oct 26 18:37:51 GMT 2026



Andreas Tille pushed to branch master at Debian Med / libhandlegraph


Commits:
12f45245 by Andreas Tille at 2026-10-26T19:33:26+01:00
Turns out we need latest upstream commit to have the correct API for vg

- - - - -
69b44b29 by Andreas Tille at 2026-10-26T19:33:53+01:00
New upstream version

- - - - -
a3e1507c by Andreas Tille at 2026-10-26T19:33:54+01:00
New upstream version 20261019.3d8c3c9
- - - - -
7385723e by Andreas Tille at 2026-10-26T19:33:55+01:00
Update upstream source from tag 'upstream/20261019.3d8c3c9'

Update to upstream version '20261019.3d8c3c9'
with Debian dir 4e8c677e3fcd41919054d1b5b296a0513ad2e91f
- - - - -
81f58bab by Andreas Tille at 2026-10-26T19:34:00+01:00
Changes made by cme

- - - - -
f8ae61d0 by Andreas Tille at 2026-10-26T19:34:08+01:00
Remove field Section on binary package libhandlegraph1 that duplicates source.

Changes-By: lintian-brush
Fixes: lintian: libhandlegraph1 binary: installable-field-mirrors-source Section [debian/control:14]
See-also: https://lintian.debian.org/tags/installable-field-mirrors-source.html

- - - - -
930e7677 by Andreas Tille at 2026-10-26T19:34:09+01:00
Set upstream metadata fields: Bug-Database, Repository-Browse.

Changes-By: lintian-brush
Fixes: lintian: source: upstream-metadata-file-is-missing
See-also: https://lintian.debian.org/tags/upstream-metadata-file-is-missing.html

- - - - -
a67fedc7 by Andreas Tille at 2026-10-26T19:37:02+01:00
routine-update: Ready to upload to unstable

- - - - -


20 changed files:

- CMakeLists.txt
- debian/changelog
- debian/control
- + debian/upstream/metadata
- debian/watch
- + src/canonical_gfa.cpp
- src/copy_graph.cpp
- src/dagify.cpp
- src/deletable_handle_graph.cpp
- + src/include/handlegraph/algorithms/canonical_gfa.hpp
- src/include/handlegraph/algorithms/dagify.hpp
- + src/include/handlegraph/algorithms/path_length.hpp
- src/include/handlegraph/deletable_handle_graph.hpp
- src/include/handlegraph/mutable_path_deletable_handle_graph.hpp
- src/include/handlegraph/path_metadata.hpp
- src/include/handlegraph/types.hpp
- + src/mutable_path_deletable_handle_graph.cpp
- + src/path_length.cpp
- src/path_metadata.cpp
- src/types.cpp


Changes:

=====================================
CMakeLists.txt
=====================================
@@ -36,6 +36,7 @@ set (CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
 
 add_library(handlegraph_objs OBJECT
   src/deletable_handle_graph.cpp
+  src/mutable_path_deletable_handle_graph.cpp
   src/dfs.cpp
   src/handle_graph.cpp
   src/mutable_handle_graph.cpp
@@ -68,6 +69,8 @@ add_library(handlegraph_objs OBJECT
   src/chop.cpp
   src/weakly_connected_components.cpp
   src/extend.cpp
+  src/canonical_gfa.cpp
+  src/path_length.cpp
   src/include/handlegraph/handle_graph.hpp
   src/include/handlegraph/mutable_handle_graph.hpp
   src/include/handlegraph/deletable_handle_graph.hpp
@@ -80,6 +83,7 @@ add_library(handlegraph_objs OBJECT
   src/include/handlegraph/util.hpp
   src/include/handlegraph/types.hpp
   src/include/handlegraph/iteratee.hpp
+  src/include/handlegraph/algorithms/canonical_gfa.hpp
   src/include/handlegraph/algorithms/copy_graph.hpp
   src/include/handlegraph/algorithms/append_graph.hpp
   src/include/handlegraph/algorithms/are_equivalent.hpp
@@ -99,6 +103,7 @@ add_library(handlegraph_objs OBJECT
   src/include/handlegraph/algorithms/chop.hpp
   src/include/handlegraph/algorithms/weakly_connected_components.hpp
   src/include/handlegraph/algorithms/extend.hpp
+  src/include/handlegraph/algorithms/path_length.hpp
   src/include/handlegraph/algorithms/internal/dfs.hpp
   )
 
@@ -120,6 +125,10 @@ add_library(handlegraph_static STATIC $<TARGET_OBJECTS:handlegraph_objs>)
 set_target_properties(handlegraph_static PROPERTIES OUTPUT_NAME handlegraph)
 target_include_directories(handlegraph_static INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/include> $<INSTALL_INTERFACE:include>)
 
+# Add an alias so that library can be used inside the build tree, e.g. when testing
+add_library(libhandlegraph::handlegraph_shared ALIAS handlegraph_shared)
+add_library(libhandlegraph::handlegraph_static ALIAS handlegraph_static)
+
 # Set up for installability
 # Make sure to put all the targets in an export set
 install(TARGETS handlegraph_shared handlegraph_static EXPORT libhandlegraphTargets 


=====================================
debian/changelog
=====================================
@@ -1,3 +1,15 @@
+libhandlegraph (20261019.3d8c3c9-1) unstable; urgency=medium
+
+  * Turns out we need latest upstream commit to have the correct API for vg
+  * New upstream version
+  * Reflow Uploaders field (cme)
+  * Remove Priority field (cme)
+  * Remove field Section on binary package libhandlegraph1 that duplicates
+    source.
+  * Set upstream metadata fields: Bug-Database, Repository-Browse.
+
+ -- Andreas Tille <tille at debian.org>  Mon, 26 Oct 2026 19:34:17 +0100
+
 libhandlegraph (2024-11-22-1) unstable; urgency=medium
 
   * Initial release (Closes: #1151631).


=====================================
debian/control
=====================================
@@ -1,12 +1,12 @@
 Source: libhandlegraph
-Section: libs
-Priority: optional
+Standards-Version: 4.7.4
 Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>
+Uploaders:
+ Andreas Tille <tille at debian.org>,
+Section: libs
 Build-Depends:
  debhelper-compat (= 14),
  cmake,
-Standards-Version: 4.7.4
 Vcs-Browser: https://salsa.debian.org/med-team/libhandlegraph
 Vcs-Git: https://salsa.debian.org/med-team/libhandlegraph.git
 Homepage: https://github.com/vgteam/libhandlegraph
@@ -14,8 +14,8 @@ Homepage: https://github.com/vgteam/libhandlegraph
 Package: libhandlegraph1
 Architecture: any
 Multi-Arch: same
-Section: libs
-Depends: ${shlibs:Depends}
+Depends:
+ ${shlibs:Depends},
 Description: handle-based abstraction for genome variation graphs
  libhandlegraph defines a set of interfaces for handle graphs: genome
  variation graphs in which all access is mediated by opaque handle objects.
@@ -27,7 +27,8 @@ Package: libhandlegraph-dev
 Architecture: any
 Multi-Arch: same
 Section: libdevel
-Depends: libhandlegraph1 (= ${binary:Version})
+Depends:
+ libhandlegraph1 (= ${binary:Version}),
 Description: development files for libhandlegraph
  libhandlegraph defines a set of interfaces for handle graphs: genome
  variation graphs in which all access is mediated by opaque handle objects.


=====================================
debian/upstream/metadata
=====================================
@@ -0,0 +1,3 @@
+---
+Bug-Database: https://github.com/vgteam/libhandlegraph/issues
+Repository-Browse: https://github.com/vgteam/libhandlegraph


=====================================
debian/watch
=====================================
@@ -1,6 +1,6 @@
 Version: 5
 
-Template: Github
-Dist: https://github.com/vgteam/libhandlegraph
-Custom-Version: [tag]*-?(\d[\d-]+)
-Uversion-Mangle: s/tag-//
+Source: https://github.com/vgteam/libhandlegraph
+Mode: git
+Matching-Pattern: HEAD
+Git-Pretty: %cd.%h


=====================================
src/canonical_gfa.cpp
=====================================
@@ -0,0 +1,122 @@
+/**
+ * \file canonical_gfa.cpp
+ *
+ * Implements an algorithm to writing a HandleGraph to an output stream in a
+ * canonical GFA format. This can be used for computing stable graph names
+ * using an appropriate hashing function (see https://github.com/jltsiren/pggname).
+ */
+
+#include "handlegraph/algorithms/canonical_gfa.hpp"
+
+#include <algorithm>
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace handlegraph {
+namespace algorithms {
+
+//-----------------------------------------------------------------------------
+
+template<typename T>
+T node_id_as(const HandleGraph& graph, const handle_t& handle) = delete;
+
+template<>
+nid_t node_id_as<nid_t>(const HandleGraph& graph, const handle_t& handle) {
+    return graph.get_id(handle);
+}
+
+template<>
+std::string node_id_as<std::string>(const HandleGraph& graph, const handle_t& handle) {
+    nid_t id = graph.get_id(handle);
+    return std::to_string(id);
+}
+
+template<typename T>
+std::vector<std::pair<T, handle_t>> nodes_in_order(const HandleGraph& graph) {
+    std::vector<std::pair<T, handle_t>> id_to_handle;
+    graph.for_each_handle([&](const handle_t& handle) {
+        id_to_handle.emplace_back(node_id_as<T>(graph, handle), handle);
+    });
+    std::sort(id_to_handle.begin(), id_to_handle.end(), [](const std::pair<T, handle_t>& a, const std::pair<T, handle_t>& b) {
+        return a.first < b.first;
+    });
+    return id_to_handle;
+}
+
+// Return value is (to_id, to_is_reverse, from_is_reverse) to make the tuple smaller.
+// We need to sort the forward and reverse edges separately to get them sorted by
+// (from_is_reverse, to_id, to_is_reverse).
+template<typename T>
+std::vector<std::tuple<T, bool, bool>> canonical_edges_in_order(const HandleGraph& graph, const T& from_id, handle_t forward_handle) {
+    std::vector<std::tuple<T, bool, bool>> edges;
+
+    // Canonical edges from the forward orientation.
+    graph.follow_edges(forward_handle, false, [&](const handle_t& to_handle) {
+        T to_id = node_id_as<T>(graph, to_handle);
+        bool to_is_rev = graph.get_is_reverse(to_handle);
+        if (from_id <= to_id) {
+            // Self-loops from forward orientation are always canonical.
+            edges.emplace_back(to_id, to_is_rev, false);
+        }
+    });
+    std::sort(edges.begin(), edges.end());
+    size_t forward_edges = edges.size();
+
+    // Canonical edges from the reverse orientation.
+    handle_t reverse_handle = graph.flip(forward_handle);
+    graph.follow_edges(reverse_handle, false, [&](const handle_t& to_handle) {
+        T to_id = node_id_as<T>(graph, to_handle);
+        bool to_is_rev = graph.get_is_reverse(to_handle);
+        if (from_id < to_id || (from_id == to_id && !to_is_rev)) {
+            // Self-loops from reverse orientation are canonical only if
+            // they go to the forward orientation.
+            edges.emplace_back(to_id, to_is_rev, true);
+        }
+    });
+    std::sort(edges.begin() + forward_edges, edges.end());
+
+    return edges;
+}
+
+void write_gfa_s_line(const std::string& id, const std::string& sequence, std::ostream& out) {
+    out.put('S'); out.put('\t'); out.write(id.data(), id.size());
+    out.put('\t'); out.write(sequence.data(), sequence.size()); out.put('\n');
+}
+
+void write_gfa_l_line(const std::string& from_id, bool from_is_rev, const std::string& to_id, bool to_is_rev, std::ostream& out) {
+    out.put('L'); out.put('\t'); out.write(from_id.data(), from_id.size());
+    out.put('\t'); out.put(from_is_rev ? '-' : '+'); out.put('\t');
+    out.write(to_id.data(), to_id.size());
+    out.put('\t'); out.put(to_is_rev ? '-' : '+'); out.put('\n');
+}
+
+void canonical_gfa(const HandleGraph& graph, std::ostream& out, bool integer_ids) {
+    if (integer_ids) {
+        auto nodes = nodes_in_order<nid_t>(graph);
+        for (const auto& node : nodes) {
+            std::string from_id_str = std::to_string(node.first);
+            std::string node_seq = graph.get_sequence(node.second); // TODO: GBWTGraph could avoid this copy.
+            write_gfa_s_line(from_id_str, node_seq, out);
+            auto edges = canonical_edges_in_order(graph, node.first, node.second);
+            for (const auto& edge : edges) {
+                write_gfa_l_line(from_id_str, std::get<2>(edge), std::to_string(std::get<0>(edge)), std::get<1>(edge), out);
+            }
+        }
+    } else {
+        auto nodes = nodes_in_order<std::string>(graph);
+        for (const auto& node : nodes) {
+            write_gfa_s_line(node.first, graph.get_sequence(node.second), out);
+            auto edges = canonical_edges_in_order(graph, node.first, node.second);
+            for (const auto& edge : edges) {
+                write_gfa_l_line(node.first, std::get<2>(edge), std::get<0>(edge), std::get<1>(edge), out);
+            }
+        }
+    }
+    out.flush();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace algorithms
+} // namespace handlegraph


=====================================
src/copy_graph.cpp
=====================================
@@ -44,13 +44,10 @@ void copy_path_handle_graph(const PathHandleGraph* from, MutablePathMutableHandl
     //            throw runtime_error("error:[copy_handle_graph] cannot copy into a non-empty graph");
     //        }
     
-    // For every sense of path
-    for (auto& sense : {PathSense::REFERENCE, PathSense::GENERIC, PathSense::HAPLOTYPE}) {
-        // copy paths of that sense
-        from->for_each_path_of_sense(sense, [&](const path_handle_t& path_handle) {
-            copy_path(from, path_handle, into);
-        });
-    }
+    // copy paths of every sense of path
+    from->for_each_path_of_sense({PathSense::REFERENCE, PathSense::GENERIC, PathSense::HAPLOTYPE}, [&](const path_handle_t& path_handle) {
+        copy_path(from, path_handle, into);
+    });
 }
 
 void copy_path(const PathHandleGraph* from, const path_handle_t& from_path,


=====================================
src/dagify.cpp
=====================================
@@ -191,8 +191,10 @@ handle_t SubHandleGraph::get_underlying_handle(const handle_t& handle) const {
 
 // TODO: SubHandleGraph port over
 
-unordered_map<nid_t, nid_t> dagify(const HandleGraph* graph, MutableHandleGraph* into,
-                                   size_t min_preserved_path_length) {
+/// Internal implementation that exposes the ID translation back to the original graph and the injection into the dagified graph, with all copies in order. 
+static std::pair<std::unordered_map<nid_t, nid_t>, unordered_map<handle_t, vector<handle_t>>> dagify_internal(const HandleGraph* graph,
+                                                                                                              MutableHandleGraph* into,
+                                                                                                              size_t min_preserved_path_length) {
     
     // initialize the translator from the dagified graph back to the original graph
     unordered_map<nid_t, nid_t> translator;
@@ -460,6 +462,7 @@ unordered_map<nid_t, nid_t> dagify(const HandleGraph* graph, MutableHandleGraph*
                            canonical_edge);
             
             // connect the last copy of the first node to all copies of the second
+            // TODO: Why the asymmetry?
             const handle_t& from = injector[edge.first].back();
             for (const handle_t& to : injector[edge.second]) {
                 into->create_edge(from, to);
@@ -473,19 +476,27 @@ unordered_map<nid_t, nid_t> dagify(const HandleGraph* graph, MutableHandleGraph*
         return true;
     });
     
-    // return the ID translator
-    return translator;
+    // return the ID translator and the handle injector
+    return {translator, injector};
+}
+
+unordered_map<nid_t, nid_t> dagify(const HandleGraph* graph, MutableHandleGraph* into,
+                                   size_t min_preserved_path_length) {
+    
+    return dagify_internal(graph, into, min_preserved_path_length).first;
 }
 
-std::unordered_map<nid_t, nid_t> dagify_from(const HandleGraph* graph,
-                                             std::vector<handle_t> start_handles,
-                                             DeletableHandleGraph* into,
-                                             size_t min_preserved_path_length) {
+std::pair<std::unordered_map<nid_t, nid_t>, std::vector<handle_t>> dagify_from(const HandleGraph* graph,
+                                                                               std::vector<handle_t> start_handles,
+                                                                               DeletableHandleGraph* into,
+                                                                               size_t min_preserved_path_length) {
     
     // Cheating implementation
     
     // Dagify the *entire* graph, creating some nodes not reachable from the starting points.
-    auto new_id_to_old_id = dagify(graph, into, min_preserved_path_length);
+    auto dagification_result = dagify_internal(graph, into, min_preserved_path_length);
+    auto& new_id_to_old_id = dagification_result.first;
+    auto& old_handle_to_new_handles = dagification_result.second;
     
     // Find all the node IDs we have start handles on
     std::unordered_set<nid_t> start_nodes;
@@ -493,22 +504,27 @@ std::unordered_map<nid_t, nid_t> dagify_from(const HandleGraph* graph,
         start_nodes.insert(graph->get_id(h));
     }
     
-    // Invert enough of the ID mapping so we can translate our start handles
-    std::unordered_map<nid_t, std::vector<nid_t>> start_id_to_new_ids;
-    for (auto& kv : new_id_to_old_id) {
-        if (start_nodes.count(kv.second)) {
-            start_id_to_new_ids[kv.second].push_back(kv.first);
-        }
-    }
-    
-    // Translate all our start handles into the into graph.
+    // Translate all our start handles into the into graph to get their copies
+    // that have all paths going right from their starts.
     std::vector<handle_t> into_start_handles;
     for (auto& h : start_handles) {
-        auto id = graph->get_id(h);
-        auto is_rev = graph->get_is_reverse(h);
-        for (auto& new_id : start_id_to_new_ids[id]) {
-            // We know the orientations of the duplicated nodes are the same, so we just pass orientation along.
-            into_start_handles.push_back(into->get_handle(new_id, is_rev));
+        auto found = old_handle_to_new_handles.find(h);
+        if (found == old_handle_to_new_handles.end()) {
+            // We must be holding this handle in the opposite orientation from
+            // its assigned one.
+            
+            // It must exist in the other orientation.
+            auto& copies = old_handle_to_new_handles.at(graph->flip(h));
+
+            // We want all paths to the right, so we need to find the last copy
+            // (so we have all paths to the left) and flip it.
+            into_start_handles.push_back(into->flip(copies.back()));
+        } else {
+            // We are holding this handle in the assigned orientation. We want
+            // all paths going right and that's what we have from the first
+            // copy.
+            auto& copies = found->second;
+            into_start_handles.push_back(copies.front());
         }
     }
     
@@ -527,6 +543,8 @@ std::unordered_map<nid_t, nid_t> dagify_from(const HandleGraph* graph,
         {}
     );
     
+    // Drop all the un-tagged nodes.
+
     // TODO: We're supposed to be able to destroy the current handle as we loop
     // over it, but HashGraph can't handle that.
     std::vector<handle_t> to_remove;
@@ -543,7 +561,7 @@ std::unordered_map<nid_t, nid_t> dagify_from(const HandleGraph* graph,
          into->destroy_handle(h);
     }
     
-    return new_id_to_old_id;
+    return {new_id_to_old_id, into_start_handles};
 }
 
 }


=====================================
src/deletable_handle_graph.cpp
=====================================
@@ -18,6 +18,30 @@ handle_t DeletableHandleGraph::truncate_handle(const handle_t& handle, bool trun
     }
 }
 
+handle_t DeletableHandleGraph::change_sequence(const handle_t& handle, const std::string& sequence) {
+    // new handle with the new sequence
+    handle_t new_handle = create_handle(sequence);
+    if (get_is_reverse(handle)) {
+        new_handle = flip(new_handle);
+    }
+    
+    // copy its edges
+    follow_edges(handle, false, [&](const handle_t& next) {
+        create_edge(new_handle, next);
+    });
+    follow_edges(handle, true, [&](const handle_t& prev) {
+        // ensure that we don't double add a non-reversing self-edge
+        if (get_id(prev) != get_id(handle) || get_is_reverse(prev)) {
+            create_edge(prev, new_handle);
+        }
+    });
+    
+    // clear the original
+    destroy_handle(handle);
+    
+    return new_handle;
+}
+
 }
 
 


=====================================
src/include/handlegraph/algorithms/canonical_gfa.hpp
=====================================
@@ -0,0 +1,29 @@
+#ifndef HANDLEGRAPH_ALGORITHMS_CANONICAL_GFA_INCLUDED
+#define HANDLEGRAPH_ALGORITHMS_CANONICAL_GFA_INCLUDED
+
+#include <iostream>
+
+#include "handlegraph/handle_graph.hpp"
+
+namespace handlegraph {
+namespace algorithms {
+
+//-----------------------------------------------------------------------------
+
+/**
+ * Writes the given HandleGraph to the given output stream in a canonical GFA
+ * format. This format can be used for computing stable graph names using an
+ * appropriate hashing function (see https://github.com/jltsiren/pggname).
+ *
+ * If integer_ids is true, node identifiers are interpreted as integers and
+ * sorted in numerical order. Otherwise the identifiers are interpreted as
+ * strings and sorted in lexicographic order.
+ */
+void canonical_gfa(const HandleGraph& graph, std::ostream& out, bool integer_ids);
+
+ //-----------------------------------------------------------------------------
+
+} // namespace algorithms
+} // namespace handlegraph
+
+#endif // HANDLEGRAPH_ALGORITHMS_CANONICAL_GFA_INCLUDED


=====================================
src/include/handlegraph/algorithms/dagify.hpp
=====================================
@@ -7,8 +7,6 @@
 #include "handlegraph/mutable_handle_graph.hpp"
 #include "handlegraph/deletable_handle_graph.hpp"
 
-#include "handlegraph/algorithms/dagify.hpp"
-
 namespace handlegraph {
 namespace algorithms {
 
@@ -40,11 +38,14 @@ std::unordered_map<nid_t, nid_t> dagify(const HandleGraph* graph,
 /// Input HandleGraph must have a single stranded orientation. Consider
 /// checking this property with has_single_stranded_orientation() before using.
 ///
-/// Returns a mapping from the node IDs of into to the node IDs in graph.
-std::unordered_map<nid_t, nid_t> dagify_from(const HandleGraph* graph,
-                                             std::vector<handle_t> start_handles,
-                                             DeletableHandleGraph* into,
-                                             size_t min_preserved_path_length);
+/// Returns a mapping from the node IDs of into to the node IDs in graph, and
+/// the subgraph versions of the start copies of the start handles (not any
+/// duplicate copies that can be reached from other start nodes). The returned
+/// handles have all paths reading rightwards out of them preserved.
+std::pair<std::unordered_map<nid_t, nid_t>, std::vector<handle_t>> dagify_from(const HandleGraph* graph,
+                                                                               std::vector<handle_t> start_handles,
+                                                                               DeletableHandleGraph* into,
+                                                                               size_t min_preserved_path_length);
 }
 }
 


=====================================
src/include/handlegraph/algorithms/path_length.hpp
=====================================
@@ -0,0 +1,21 @@
+#ifndef HANDLEGRAPH_ALGORITHMS_PATH_LENGTH_HPP_INCLUDED
+#define HANDLEGRAPH_ALGORITHMS_PATH_LENGTH_HPP_INCLUDED
+
+/**
+ * \file path_length.hpp
+ *
+ * Defines an algorithm for measuring the length of a path
+ */
+
+#include "handlegraph/path_handle_graph.hpp"
+
+namespace handlegraph {
+namespace algorithms {
+
+/// Returns the length of a path in bases.
+size_t get_path_length(const PathHandleGraph* graph, const path_handle_t& path);
+
+}
+}
+
+#endif


=====================================
src/include/handlegraph/deletable_handle_graph.hpp
=====================================
@@ -30,6 +30,12 @@ public:
     /// May **NOT** be called during iteration along a path, if it could destroy that path.
     virtual void destroy_handle(const handle_t& handle) = 0;
     
+    /// Change the sequence of handle's forward orientation to a new sequence. Returns a (possibly altered)
+    /// handle to the node with the new sequence. The returned handle has the same orientation as the
+    /// handle provided as an argument. May invalidate existing handles to the node. Updates paths to
+    /// follow the new node if called through a class inheriting a MutablePathHandleGraph interface.
+    virtual handle_t change_sequence(const handle_t& handle, const std::string& sequence);
+    
     /// Remove the edge connecting the given handles in the given order and orientations.
     /// Ignores nonexistent edges.
     /// Does not update any stored paths.


=====================================
src/include/handlegraph/mutable_path_deletable_handle_graph.hpp
=====================================
@@ -21,6 +21,11 @@ public:
     
     // No extra methods. Deleting a node or edge that is contained in a path is undefined behavior.
     // The method clear() is now assumed to delete paths as well as nodes and edges.
+    // The method change_sequence() is now assumed to update paths if the underlying handle changes as a result.
+    
+    // the default implementation is augmented to meet the new semantics
+    virtual handle_t change_sequence(const handle_t& handle, const std::string& sequence);
+
 };
 
 }


=====================================
src/include/handlegraph/path_metadata.hpp
=====================================
@@ -30,7 +30,7 @@ namespace handlegraph {
  * - SENSE_HAPLOTYPE: a haplotype from a particular individual. Has a "sample"
  *   name, a "locus" name, a haplotype number, and a phase block identifier.
  *
- * Paths of all sneses can represent subpaths, with bounds.
+ * Paths of all senses can represent subpaths, with bounds.
  *
  * Depending on sense, a path might have:
  *
@@ -163,11 +163,21 @@ public:
     /// stops if the iteratee returns false.
     template<typename Iteratee>
     bool for_each_path_of_sense(const PathSense& sense, const Iteratee& iteratee) const;
+
+    /// Loop through all the paths with any of the given senses. Returns false and
+    /// stops if the iteratee returns false.
+    template<typename Iteratee>
+    bool for_each_path_of_sense(const std::unordered_set<PathSense>& senses, const Iteratee& iteratee) const;
     
     /// Loop through all the paths with the given sample name.
     /// Returns false and stops if the iteratee returns false.
     template<typename Iteratee>
     bool for_each_path_of_sample(const std::string& sample, const Iteratee& iteratee) const;
+
+    /// Loop through all the paths with any of the given sample names.
+    /// Returns false and stops if the iteratee returns false.
+    template<typename Iteratee>
+    bool for_each_path_of_sample(const std::unordered_set<std::string>& samples, const Iteratee& iteratee) const;
     
     /// Loop through all the paths matching the given query. Query elements
     /// which are null match everything. Returns false and stops if the
@@ -192,6 +202,11 @@ public:
     /// TODO: Take the opportunity to make steps track orientation better?
     template<typename Iteratee>
     bool for_each_step_of_sense(const handle_t& visited, const PathSense& sense, const Iteratee& iteratee) const;
+
+    /// Loop through all steps on the given handle for paths with any of the
+    /// given senses. Returns false and stops if the iteratee returns false.
+    template<typename Iteratee>
+    bool for_each_step_of_sense(const handle_t& visited, const std::unordered_set<PathSense>& senses, const Iteratee& iteratee) const;
     
 protected:
     
@@ -215,6 +230,10 @@ protected:
     /// Loop through all steps on the given handle for paths with the given
     /// sense. Returns false and stops if the iteratee returns false.
     virtual bool for_each_step_of_sense_impl(const handle_t& visited, const PathSense& sense, const std::function<bool(const step_handle_t&)>& iteratee) const;
+
+    /// Loop through all steps on the given handle for paths with any of the
+    /// given senses. Returns false and stops if the iteratee returns false.
+    virtual bool for_each_step_of_sense_impl(const handle_t& visited, const std::unordered_set<PathSense>& senses, const std::function<bool(const step_handle_t&)>& iteratee) const;
     
     ////////////////////////////////////////////////////////////////////////////
     // Backing methods that need to be implemented for default implementation
@@ -230,19 +249,11 @@ protected:
     
     /// Execute a function on each path in the graph. If it returns false, stop
     /// iteration. Returns true if we finished and false if we stopped early.
-    ///
-    /// If the graph contains compressed haplotype paths and properly
-    /// implements for_each_path_of_sense to retrieve them, they should not be
-    /// visible here. Only reference or generic named paths should be visible.
     virtual bool for_each_path_handle_impl(const std::function<bool(const path_handle_t&)>& iteratee) const = 0;
     
     /// Execute a function on each step of a handle in any path. If it
     /// returns false, stop iteration. Returns true if we finished and false if
     /// we stopped early.
-    ///
-    /// If the graph contains compressed haplotype paths and properly
-    /// implements for_each_step_of_sense to find them, they should not be
-    /// visible here. Only reference or generic named paths should be visible.
     virtual bool for_each_step_on_handle_impl(const handle_t& handle,
         const std::function<bool(const step_handle_t&)>& iteratee) const = 0;
     
@@ -275,13 +286,28 @@ private:
 
 template<typename Iteratee>
 bool PathMetadata::for_each_path_of_sense(const PathSense& sense, const Iteratee& iteratee) const {
+    // TODO: If we try and call just on {sense} here, we *don't* successfully
+    // call the other overload but we also don't infinitely recurse at runtime;
+    // the iteratee just never sees anything. What's really going on???
+    //
+    // Anyway, we need to manually make a set to ensure we delegate to the other overload.
     std::unordered_set<PathSense> senses{sense};
+    return for_each_path_of_sense(senses, iteratee);
+}
+
+template<typename Iteratee>
+bool PathMetadata::for_each_path_of_sense(const std::unordered_set<PathSense>& senses, const Iteratee& iteratee) const {
     return for_each_path_matching_impl(&senses, nullptr, nullptr, BoolReturningWrapper<Iteratee>::wrap(iteratee));
 }
 
 template<typename Iteratee>
 bool PathMetadata::for_each_path_of_sample(const std::string& sample, const Iteratee& iteratee) const {
     std::unordered_set<std::string> samples{sample};
+    return for_each_path_of_sample(samples, iteratee);
+}
+
+template<typename Iteratee>
+bool PathMetadata::for_each_path_of_sample(const std::unordered_set<std::string>& samples, const Iteratee& iteratee) const {
     return for_each_path_matching_impl(nullptr, &samples, nullptr, BoolReturningWrapper<Iteratee>::wrap(iteratee));
 }
 
@@ -309,5 +335,10 @@ bool PathMetadata::for_each_step_of_sense(const handle_t& visited, const PathSen
     return for_each_step_of_sense_impl(visited, sense, BoolReturningWrapper<Iteratee>::wrap(iteratee));
 }
 
+template<typename Iteratee>
+bool PathMetadata::for_each_step_of_sense(const handle_t& visited, const std::unordered_set<PathSense>& senses, const Iteratee& iteratee) const {
+    return for_each_step_of_sense_impl(visited, senses, BoolReturningWrapper<Iteratee>::wrap(iteratee));
+}
+
 }
 #endif


=====================================
src/include/handlegraph/types.hpp
=====================================
@@ -70,7 +70,17 @@ struct step_handle_t { char data[2 * sizeof(int64_t)]; };
  *
  * For single nodes, we only have forward and reverse.
  */
-struct net_handle_t { char data[sizeof(nid_t)]; };
+struct net_handle_t { 
+    char data[sizeof(nid_t)]; 
+    
+    // Define equality here so that it can be used in python bindings
+
+    /// Define equality on net handles
+    bool operator==(const net_handle_t& other) const;
+
+    /// Define inequality on net handles
+    bool operator!=(const net_handle_t& other) const;
+};
 
 /// Define equality on handles
 bool operator==(const handle_t& a, const handle_t& b);
@@ -81,8 +91,6 @@ bool operator!=(const handle_t& a, const handle_t& b);
 /// Define equality on path handles
 bool operator==(const path_handle_t& a, const path_handle_t& b);
 
-/// Define equality on net handles
-bool operator==(const net_handle_t& a, const net_handle_t& b);
 
 /// Define inequality on path handles
 bool operator!=(const path_handle_t& a, const path_handle_t& b);
@@ -92,10 +100,6 @@ bool operator==(const step_handle_t& a, const step_handle_t& b);
 
 /// Define inequality on step handles
 bool operator!=(const step_handle_t& a, const step_handle_t& b);
-
-/// Define inequality on net handles
-bool operator!=(const net_handle_t& a, const net_handle_t& b);
-
 }
 
 // Hashes need to be in the std namespace


=====================================
src/mutable_path_deletable_handle_graph.cpp
=====================================
@@ -0,0 +1,50 @@
+#include "handlegraph/mutable_path_deletable_handle_graph.hpp"
+
+/** \file mutable_path_deletable_handle_graph.cpp
+ * Implement methods for MutablePathDeletableHandleGraph.
+ */
+
+namespace handlegraph {
+
+handle_t MutablePathDeletableHandleGraph::change_sequence(const handle_t& handle,
+                                                          const std::string& sequence) {
+    // TODO: repetitive with DeletableHandleGraph, but we can't call it because it could destroy
+    // all the paths containing the handle as a side effect of the destroy_handle call at the end
+    
+    // new handle with the new sequence
+    handle_t new_handle = create_handle(sequence);
+    // copy its edges
+    follow_edges(forward(handle), false, [&](const handle_t& next) {
+        create_edge(new_handle, next);
+    });
+    follow_edges(forward(handle), true, [&](const handle_t& prev) {
+        // ensure that we don't double add a non-reversing self-edge
+        if (get_id(prev) != get_id(handle) || get_is_reverse(prev)) {
+            create_edge(prev, new_handle);
+        }
+    });
+    
+    // collect the steps we need to replace
+    std::vector<step_handle_t> steps;
+    for_each_step_on_handle(handle, [&](const step_handle_t& step) {
+        steps.push_back(step);
+    });
+    
+    // replace them
+    for (const auto& step : steps) {
+        std::vector<handle_t> rewriter(1, get_is_reverse(get_handle_of_step(step)) ? flip(new_handle) : new_handle);
+        rewrite_segment(step, get_next_step(step), rewriter);
+    }
+    if (get_is_reverse(handle)) {
+        new_handle = flip(new_handle);
+    }
+    
+    // clear the original
+    destroy_handle(handle);
+    
+    return new_handle;
+}
+
+}
+
+


=====================================
src/path_length.cpp
=====================================
@@ -0,0 +1,21 @@
+#include "handlegraph/algorithms/path_length.hpp"
+#include "handlegraph/path_position_handle_graph.hpp"
+
+namespace handlegraph {
+namespace algorithms {
+
+size_t get_path_length(const PathHandleGraph* graph, const path_handle_t& path) {
+    // A path-position graph already knows this.
+    if (const PathPositionHandleGraph* positional
+        = dynamic_cast<const PathPositionHandleGraph*>(graph)) {
+        return positional->get_path_length(path);
+    }
+    size_t length = 0;
+    for (handle_t h : graph->scan_path(path)) {
+        length += graph->get_length(h);
+    }
+    return length;
+}
+
+}
+}


=====================================
src/path_metadata.cpp
=====================================
@@ -369,5 +369,16 @@ bool PathMetadata::for_each_step_of_sense_impl(const handle_t& visited, const Pa
     });
 }
 
+bool PathMetadata::for_each_step_of_sense_impl(const handle_t& visited, const std::unordered_set<PathSense>& senses, const std::function<bool(const step_handle_t&)>& iteratee) const {
+    return for_each_step_on_handle_impl(visited, [&](const step_handle_t& handle) {
+        if (!senses.count(get_sense(get_path_handle_of_step(handle)))) {
+            // Skip this non-matching path's step
+            return true;
+        }
+        // And emit any steps on matching paths
+        return iteratee(handle);
+    });
+}
+
 }
 


=====================================
src/types.cpp
=====================================
@@ -39,13 +39,13 @@ bool operator!=(const step_handle_t& a, const step_handle_t& b) {
 }
 
 /// Define equality on net handles
-bool operator==(const net_handle_t& a, const net_handle_t& b) {
-    return as_integer(a) == as_integer(b);
+bool net_handle_t::operator==(const net_handle_t& other) const {
+    return as_integer(*this) == as_integer(other);
 }
 
 /// Define inequality on net handles
-bool operator!=(const net_handle_t& a, const net_handle_t& b) {
-    return !(a == b);
+bool net_handle_t::operator!=(const net_handle_t& other) const {
+    return as_integer(*this) != as_integer(other);
 }
 
 }



View it on GitLab: https://salsa.debian.org/med-team/libhandlegraph/-/compare/0acb9ffe8f15b35d9e7872247e451f1f0321fd49...a67fedc7295162e1f8159dc8a9823f6bc5a7ddc4

-- 
View it on GitLab: https://salsa.debian.org/med-team/libhandlegraph/-/compare/0acb9ffe8f15b35d9e7872247e451f1f0321fd49...a67fedc7295162e1f8159dc8a9823f6bc5a7ddc4
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20261026/bc10e806/attachment-0001.htm>


More information about the debian-med-commit mailing list