[med-svn] [Git][med-team/libhandlegraph][upstream] New upstream version 20261019.3d8c3c9
Andreas Tille (@tille)
gitlab at salsa.debian.org
Mon Oct 26 18:38:12 GMT 2026
Andreas Tille pushed to branch upstream at Debian Med / libhandlegraph
Commits:
a3e1507c by Andreas Tille at 2026-10-26T19:33:54+01:00
New upstream version 20261019.3d8c3c9
- - - - -
16 changed files:
- CMakeLists.txt
- + 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
=====================================
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/-/commit/a3e1507c751c22807e4a7da8854eecaa6d6257e6
--
View it on GitLab: https://salsa.debian.org/med-team/libhandlegraph/-/commit/a3e1507c751c22807e4a7da8854eecaa6d6257e6
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/fdd4a3c1/attachment-0001.htm>
More information about the debian-med-commit
mailing list