about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/domagi-overlap.dbk20
-rw-r--r--doc/domagi.dbk1
-rw-r--r--domagi/domagi.py18
-rw-r--r--domagi/overlap.sql20
-rw-r--r--meson.build1
-rw-r--r--test-data/expected-output/test1-overlap3
-rw-r--r--test-data/expected-output/test2-overlap3
-rw-r--r--test-data/expected-output/test3-overlap3
-rw-r--r--tests/test_domagi.py32
9 files changed, 101 insertions, 0 deletions
diff --git a/doc/domagi-overlap.dbk b/doc/domagi-overlap.dbk
new file mode 100644
index 0000000..e8e40e7
--- /dev/null
+++ b/doc/domagi-overlap.dbk
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<refentry xmlns="http://docbook.org/ns/docbook"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+  <xi:include href="gen-refentry-overlap.xml" />
+  <refsection>
+    <title>Description</title>
+    <para>Find the paths touched by the specified paths. The output is in a four-column tab-delimited format with the following columns—the name of the specified path, its start coordinate, its end coordinate, and the name of the path that touches it. The start and end coordinates are always 0 and the length of the specified path.</para>
+    <variablelist>
+      <title>Options</title>
+      <xi:include href="input-db-argument.dbk" />
+      <varlistentry>
+        <term><option>-r <replaceable>PATH</replaceable></option></term>
+        <term><option>--path=<replaceable>PATH</replaceable></option></term>
+        <listitem><para>Find paths touched by <replaceable>PATH</replaceable>. This argument may be specified more than once to find paths touched by more than one path.</para></listitem>
+      </varlistentry>
+      <xi:include href="threads-argument.dbk" />
+      <xi:include href="help-option.dbk" />
+    </variablelist>
+  </refsection>
+</refentry>
diff --git a/doc/domagi.dbk b/doc/domagi.dbk
index 0079fa2..53f25e2 100644
--- a/doc/domagi.dbk
+++ b/doc/domagi.dbk
@@ -12,6 +12,7 @@
   <xi:include href="domagi-depth.dbk" />
   <xi:include href="domagi-extract.dbk" />
   <xi:include href="domagi-matrix.dbk" />
+  <xi:include href="domagi-overlap.dbk" />
   <xi:include href="domagi-paths.dbk" />
   <xi:include href="domagi-stats.dbk" />
   <xi:include href="domagi-view.dbk" />
diff --git a/domagi/domagi.py b/domagi/domagi.py
index 4cc5137..faa7429 100644
--- a/domagi/domagi.py
+++ b/domagi/domagi.py
@@ -256,6 +256,24 @@ def matrix(con, threads):
     print(segment_count, segment_count, df.shape[0])
     df.to_csv(sys.stdout, sep=" ", header=False, index=False)
 
+@main.command(short_help="Find paths touched by given input paths")
+@click.option("-i", "--db", "--idx", "con",
+              type=DuckDBParamType(),
+              required=True,
+              help="pangenome duckdb database")
+@click.option("-r", "--path", "paths",
+              # We deviate a little from odgi and allow -r to be specified
+              # several times.
+              multiple=True,
+              metavar="PATH",
+              help="find paths touched by PATH")
+@common_options
+def overlap(con, paths, threads):
+    set_duckdb_threads(con, threads)
+    (con.execute(read_sql("overlap.sql"), [paths])
+     .fetchdf()
+     .to_csv(sys.stdout, sep="\t", index=False))
+
 @main.command(short_help="Interrogate paths")
 @click.option("-i", "--db", "--idx", "con",
               type=DuckDBParamType(),
diff --git a/domagi/overlap.sql b/domagi/overlap.sql
new file mode 100644
index 0000000..2ffb35b
--- /dev/null
+++ b/domagi/overlap.sql
@@ -0,0 +1,20 @@
+WITH path_overlap AS (
+       SELECT path1.id AS path1_id, path1.name AS path1_name, path2.name AS path2_name
+       FROM path path1
+       INNER JOIN path path2
+             ON -- Avoid matching paths to themselves.
+                path1.id != path2.id
+                -- Check if there is any segment in common between the
+                -- two paths.
+                AND EXISTS(SELECT segment_id
+                           FROM path_segment path_segment1
+                           WHERE path_segment1.path_id=path1.id
+                           INTERSECT
+                           SELECT segment_id
+                           FROM path_segment path_segment2
+                           WHERE path_segment2.path_id=path2.id)
+       WHERE path1.name IN (SELECT UNNEST(?)))
+  -- Add path coordinates.
+  SELECT path1_name AS "#path", 0 AS start, length AS "end", path2_name AS "path.touched"
+  FROM path_overlap
+  INNER JOIN path_length ON path_length.path_id=path1_id
diff --git a/meson.build b/meson.build
index 7d85388..7b86e87 100644
--- a/meson.build
+++ b/meson.build
@@ -12,6 +12,7 @@ py.install_sources('domagi/domagi.py',
 install_data('domagi/matrix.sql',
              'domagi/schema.sql',
              'domagi/bed-depth.sql',
+             'domagi/overlap.sql',
              'domagi/path-depth.sql',
              'domagi/pre-import.sql',
 	     'domagi/post-import.sql',
diff --git a/test-data/expected-output/test1-overlap b/test-data/expected-output/test1-overlap
new file mode 100644
index 0000000..ee0297f
--- /dev/null
+++ b/test-data/expected-output/test1-overlap
@@ -0,0 +1,3 @@
+#path	start	end	path.touched
+x	0	50	y
+y	0	50	x
diff --git a/test-data/expected-output/test2-overlap b/test-data/expected-output/test2-overlap
new file mode 100644
index 0000000..81dfa10
--- /dev/null
+++ b/test-data/expected-output/test2-overlap
@@ -0,0 +1,3 @@
+#path	start	end	path.touched
+5+	0	13	5-
+5-	0	13	5+
diff --git a/test-data/expected-output/test3-overlap b/test-data/expected-output/test3-overlap
new file mode 100644
index 0000000..bc22807
--- /dev/null
+++ b/test-data/expected-output/test3-overlap
@@ -0,0 +1,3 @@
+#path	start	end	path.touched
+5+	0	20	5-
+5-	0	13	5+
diff --git a/tests/test_domagi.py b/tests/test_domagi.py
index c65b87e..1ce5fc0 100644
--- a/tests/test_domagi.py
+++ b/tests/test_domagi.py
@@ -19,6 +19,7 @@
 import io
 
 from click.testing import CliRunner
+import duckdb
 import pandas as pd
 from pandas.testing import assert_frame_equal
 from pathlib import Path
@@ -185,6 +186,37 @@ def test_domagi_matrix(tmp_path, test_data_file, expected_output):
 
 @pytest.mark.parametrize("test_data_file, expected_output",
                          [(Path("test-data/test1.gfa"),
+                           Path("test-data/expected-output/test1-overlap")),
+                          (Path("test-data/test2.gfa"),
+                           Path("test-data/expected-output/test2-overlap")),
+                          (Path("test-data/test3.gfa"),
+                           Path("test-data/expected-output/test3-overlap"))])
+def test_domagi_overlap(tmp_path, test_data_file, expected_output):
+    duckdb_path = tmp_path / f"{test_data_file.stem}.db"
+    runner = CliRunner()
+    result = runner.invoke(main, ["build",
+                                  "--gfa", test_data_file,
+                                  "--out", duckdb_path])
+    assert result.exit_code == 0
+    paths = [path for path, in (duckdb.connect(duckdb_path, True)
+                                .execute("SELECT name FROM path")
+                                .fetchall())]
+    result = runner.invoke(main, ["overlap",
+                                  "--db", duckdb_path,
+                                  *sum([["--path", path] for path in paths],
+                                       [])])
+    assert result.exit_code == 0
+    assert_frame_equal(pd.read_csv(expected_output, sep="\t")
+                       .sort_values(by=["#path", "path.touched"],
+                                    ignore_index=True),
+                       pd.read_csv(io.StringIO(result.stdout),
+                                   sep="\t")
+                       .sort_values(by=["#path", "path.touched"],
+                                    ignore_index=True),
+                       check_dtype=False)
+
+@pytest.mark.parametrize("test_data_file, expected_output",
+                         [(Path("test-data/test1.gfa"),
                            Path("test-data/expected-output/test1-paths")),
                           (Path("test-data/test2.gfa"),
                            Path("test-data/expected-output/test2-paths")),