From 0a393087c737811ce1ae5b13449248d29d68d1da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radek=20Hu=C5=A1ek?= <husek@iuuk.mff.cuni.cz>
Date: Thu, 8 Jul 2021 21:03:02 +0200
Subject: [PATCH] Add basic voltage graphs support

---
 graph_tools/__main__.py       |   1 +
 graph_tools/all.py            |   1 +
 graph_tools/voltage_graphs.py | 118 ++++++++++++++++++++++++++++++++++
 3 files changed, 120 insertions(+)
 create mode 100644 graph_tools/voltage_graphs.py

diff --git a/graph_tools/__main__.py b/graph_tools/__main__.py
index d2d94eb..15264be 100644
--- a/graph_tools/__main__.py
+++ b/graph_tools/__main__.py
@@ -8,6 +8,7 @@ to_test = [
   "sequences",
   "parameters",
   "misc",
+  "voltage_graphs",
   "tests"
 ]
 
diff --git a/graph_tools/all.py b/graph_tools/all.py
index cc664bb..631ae0c 100644
--- a/graph_tools/all.py
+++ b/graph_tools/all.py
@@ -2,4 +2,5 @@ from .base import *
 from .sequences import *
 from .parameters import *
 from .misc import *
+from .voltage_graphs import *
 
diff --git a/graph_tools/voltage_graphs.py b/graph_tools/voltage_graphs.py
new file mode 100644
index 0000000..d021796
--- /dev/null
+++ b/graph_tools/voltage_graphs.py
@@ -0,0 +1,118 @@
+def _init_():
+  global voltage_graph_zn_to_sequence
+
+  from .base import Gadget, CUBIC_VERTEX, FREE_EDGE, GraphSequence
+  import itertools
+
+  def flatten(x): return list(itertools.chain.from_iterable(x))
+
+  class GadgetBuilder:
+    def __init__(self, subgadgets):
+      self._subgadgets = subgadgets
+      self._degree = { i: g.size() for i, g in enumerate(subgadgets) }
+      self._joins = []
+      self._outs = []
+
+    def _get_out(self, v):
+      ret = self._degree[v]
+      assert ret > 0
+      self._degree[v] = ret - 1
+      return ret
+
+    def extend(self, gadget):
+      i = len(self._subgadgets)
+      self._degree[i] = gadget.size()
+      self._subgadgets.append(gadget)
+      return i
+
+    def join(self, u, v):
+      ui = self._get_out(u)
+      vi = self._get_out(v)
+      self._joins.append(((u+1, ui), (v+1, vi)))
+      return self
+
+    def out(self, u):
+      ui = self._get_out(u)
+      self._outs.append((u+1, ui))
+      return self
+
+    def finish(self):
+      assert all( d == 0 for d in self._degree.values() )
+      return Gadget.join(
+        self._subgadgets,
+        self._joins,
+        self._outs
+      )
+
+
+  def voltage_graph_zn_to_sequence(template, assignment):
+    """Transform a voltage graph template into a graph sequence.
+
+    EXAMPLES:
+    >>> gs = voltage_graph_zn_to_sequence([2, [(0,0), (0,1), (1,1)]], [1,0,2])
+    >>> from sage.all import graphs
+    >>> from .parameters import UnderlyingGraph, EdgeColoring, CircuitDoubleCover
+    >>> gs.graph(5).eval(EdgeColoring(3))
+    0
+    >>> gs.graph(5).eval(CircuitDoubleCover)
+    52
+    >>> P = gs.graph(5).eval(UnderlyingGraph)
+    >>> P.is_isomorphic(graphs.PetersenGraph())
+    True
+
+    >>> gs = voltage_graph_zn_to_sequence([4, [(0,1), (0,2), (0,3), (1,1), (2,2), (3,3)]], [0,0,0, 1,2,3])
+    >>> C = gs.graph(7).eval(UnderlyingGraph)
+    >>> C.is_isomorphic(graphs.CoxeterGraph())
+    True
+
+    >>> gs = voltage_graph_zn_to_sequence([4, [(0,0), (0,1), (1,2), (1,3), (2,3), (2,3)]], [1, 0,0,0, 1,-1])
+    >>> gs.graph(5).eval(EdgeColoring(3))
+    0
+    >>> F = gs.graph(5).eval(UnderlyingGraph)
+    >>> F.is_isomorphic(graphs.FlowerSnark())
+    True
+    """
+
+    n, E = template
+    m = max(map(abs, assignment))
+    s = 2*sum(map(abs, assignment))
+
+    E_zero = [ (u, v) for i, (u, v) in enumerate(E) if assignment[i] == 0 ]
+    E_nonzero = [ (u, v, assignment[i]) for i, (u, v) in enumerate(E) if assignment[i] != 0 ]
+
+    assert len(assignment) == len(E)
+    assert sorted(flatten(E)) == flatten( [i]*3 for i in range(n) )
+
+    gadget = GadgetBuilder([ CUBIC_VERTEX ] * n)
+
+    for u, v in E_zero:
+      gadget.join(u, v)
+
+    for u, v, a in E_nonzero:
+      free_edges = [ gadget.extend(FREE_EDGE) for _ in range(abs(a) - 1) ]
+      if a > 0:
+        for i in range(a):
+          gadget.out(free_edges[i] if i < a - 1 else u)
+          gadget.out(v if i == 0 else free_edges[i-1])
+      else:
+        for i in range(-a):
+          gadget.out(v if i == 0 else free_edges[i-1])
+          gadget.out(free_edges[i] if i < a - 1 else u)
+
+    gadget = gadget.finish()
+
+    @GraphSequence
+    class Sequence:
+      sequence_start = 1
+
+      base_gadget = gadget
+      step_gadget = gadget
+
+      step_join = [ ((1, 2*i-1), (2, 2*i)) for i in range(1, 1 + gadget.size() // 2) ]
+      step_out = [ (1 + (i % 2), i) for i in range(1, 1 + gadget.size()) ]
+      final_join = [ ((1, 2*i-1), (1, 2*i)) for i in range(1, 1 + gadget.size() // 2) ]
+
+    return Sequence
+
+_init_()
+
-- 
GitLab