Advertisement
Guest User

Untitled

a guest
Nov 13th, 2019
198
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 81.79 KB | None | 0 0
  1. from collections.abc import Mapping, Set
  2. from collections import defaultdict
  3.  
  4.  
  5. # NodeViews
  6. class NodeView(Mapping, Set):
  7.     __slots__ = '_nodes',
  8.  
  9.     def __getstate__(self):
  10.         return {'_nodes': self._nodes}
  11.  
  12.     def __setstate__(self, state):
  13.         self._nodes = state['_nodes']
  14.  
  15.     def __init__(self, graph):
  16.         self._nodes = graph._node
  17.  
  18.     # Mapping methods
  19.     def __len__(self):
  20.         return len(self._nodes)
  21.  
  22.     def __iter__(self):
  23.         return iter(self._nodes)
  24.  
  25.     def __getitem__(self, n):
  26.         return self._nodes[n]
  27.  
  28.     # Set methods
  29.     def __contains__(self, n):
  30.         return n in self._nodes
  31.  
  32.     @classmethod
  33.     def _from_iterable(cls, it):
  34.         return set(it)
  35.  
  36.     # DataView method
  37.     def __call__(self, data=False, default=None):
  38.         if data is False:
  39.             return self
  40.         return NodeDataView(self._nodes, data, default)
  41.  
  42.     def data(self, data=True, default=None):
  43.         if data is False:
  44.             return self
  45.         return NodeDataView(self._nodes, data, default)
  46.  
  47.     def __str__(self):
  48.         return str(list(self))
  49.  
  50.     def __repr__(self):
  51.         return '%s(%r)' % (self.__class__.__name__, tuple(self))
  52.  
  53.  
  54. class NodeDataView(Set):
  55.     __slots__ = ('_nodes', '_data', '_default')
  56.  
  57.     def __getstate__(self):
  58.         return {'_nodes': self._nodes,
  59.                 '_data': self._data,
  60.                 '_default': self._default}
  61.  
  62.     def __setstate__(self, state):
  63.         self._nodes = state['_nodes']
  64.         self._data = state['_data']
  65.         self._default = state['_default']
  66.  
  67.     def __init__(self, nodedict, data=False, default=None):
  68.         self._nodes = nodedict
  69.         self._data = data
  70.         self._default = default
  71.  
  72.     @classmethod
  73.     def _from_iterable(cls, it):
  74.         try:
  75.             return set(it)
  76.         except TypeError as err:
  77.             if "unhashable" in str(err):
  78.                 msg = " : Could be b/c data=True or your values are unhashable"
  79.                 raise TypeError(str(err) + msg)
  80.             raise
  81.  
  82.     def __len__(self):
  83.         return len(self._nodes)
  84.  
  85.     def __iter__(self):
  86.         data = self._data
  87.         if data is False:
  88.             return iter(self._nodes)
  89.         if data is True:
  90.             return iter(self._nodes.items())
  91.         return ((n, dd[data] if data in dd else self._default)
  92.                 for n, dd in self._nodes.items())
  93.  
  94.     def __contains__(self, n):
  95.         try:
  96.             node_in = n in self._nodes
  97.         except TypeError:
  98.             n, d = n
  99.             return n in self._nodes and self[n] == d
  100.         if node_in is True:
  101.             return node_in
  102.         try:
  103.             n, d = n
  104.         except (TypeError, ValueError):
  105.             return False
  106.         return n in self._nodes and self[n] == d
  107.  
  108.     def __getitem__(self, n):
  109.         ddict = self._nodes[n]
  110.         data = self._data
  111.         if data is False or data is True:
  112.             return ddict
  113.         return ddict[data] if data in ddict else self._default
  114.  
  115.     def __str__(self):
  116.         return str(list(self))
  117.  
  118.     def __repr__(self):
  119.         if self._data is False:
  120.             return '%s(%r)' % (self.__class__.__name__, tuple(self))
  121.         if self._data is True:
  122.             return '%s(%r)' % (self.__class__.__name__, dict(self))
  123.         return '%s(%r, data=%r)' % \
  124.                (self.__class__.__name__, dict(self), self._data)
  125.  
  126.  
  127. # DegreeViews
  128. class DiDegreeView(object):
  129.     def __init__(self, G, nbunch=None, weight=None):
  130.         self._graph = G
  131.         self._succ = G._succ if hasattr(G, "_succ") else G._adj
  132.         self._pred = G._pred if hasattr(G, "_pred") else G._adj
  133.         self._nodes = self._succ if nbunch is None \
  134.             else list(G.nbunch_iter(nbunch))
  135.         self._weight = weight
  136.  
  137.     def __call__(self, nbunch=None, weight=None):
  138.         if nbunch is None:
  139.             if weight == self._weight:
  140.                 return self
  141.             return self.__class__(self._graph, None, weight)
  142.         try:
  143.             if nbunch in self._nodes:
  144.                 if weight == self._weight:
  145.                     return self[nbunch]
  146.                 return self.__class__(self._graph, None, weight)[nbunch]
  147.         except TypeError:
  148.             pass
  149.         return self.__class__(self._graph, nbunch, weight)
  150.  
  151.     def __getitem__(self, n):
  152.         weight = self._weight
  153.         succs = self._succ[n]
  154.         preds = self._pred[n]
  155.         if weight is None:
  156.             return len(succs) + len(preds)
  157.         return sum(dd.get(weight, 1) for dd in succs.values()) + \
  158.                sum(dd.get(weight, 1) for dd in preds.values())
  159.  
  160.     def __iter__(self):
  161.         weight = self._weight
  162.         if weight is None:
  163.             for n in self._nodes:
  164.                 succs = self._succ[n]
  165.                 preds = self._pred[n]
  166.                 yield (n, len(succs) + len(preds))
  167.         else:
  168.             for n in self._nodes:
  169.                 succs = self._succ[n]
  170.                 preds = self._pred[n]
  171.                 deg = sum(dd.get(weight, 1) for dd in succs.values()) \
  172.                       + sum(dd.get(weight, 1) for dd in preds.values())
  173.                 yield (n, deg)
  174.  
  175.     def __len__(self):
  176.         return len(self._nodes)
  177.  
  178.     def __str__(self):
  179.         return str(list(self))
  180.  
  181.     def __repr__(self):
  182.         return '%s(%r)' % (self.__class__.__name__, dict(self))
  183.  
  184.  
  185. class DegreeView(DiDegreeView):
  186.     def __getitem__(self, n):
  187.         weight = self._weight
  188.         nbrs = self._succ[n]
  189.         if weight is None:
  190.             return len(nbrs) + (n in nbrs)
  191.         return sum(dd.get(weight, 1) for dd in nbrs.values()) + \
  192.                (n in nbrs and nbrs[n].get(weight, 1))
  193.  
  194.     def __iter__(self):
  195.         weight = self._weight
  196.         if weight is None:
  197.             for n in self._nodes:
  198.                 nbrs = self._succ[n]
  199.                 yield (n, len(nbrs) + (n in nbrs))
  200.         else:
  201.             for n in self._nodes:
  202.                 nbrs = self._succ[n]
  203.                 deg = sum(dd.get(weight, 1) for dd in nbrs.values()) + \
  204.                       (n in nbrs and nbrs[n].get(weight, 1))
  205.                 yield (n, deg)
  206.  
  207.  
  208. class OutDegreeView(DiDegreeView):
  209.     """A DegreeView class to report out_degree for a DiGraph; See DegreeView"""
  210.  
  211.     def __getitem__(self, n):
  212.         weight = self._weight
  213.         nbrs = self._succ[n]
  214.         if self._weight is None:
  215.             return len(nbrs)
  216.         return sum(dd.get(self._weight, 1) for dd in nbrs.values())
  217.  
  218.     def __iter__(self):
  219.         weight = self._weight
  220.         if weight is None:
  221.             for n in self._nodes:
  222.                 succs = self._succ[n]
  223.                 yield (n, len(succs))
  224.         else:
  225.             for n in self._nodes:
  226.                 succs = self._succ[n]
  227.                 deg = sum(dd.get(weight, 1) for dd in succs.values())
  228.                 yield (n, deg)
  229.  
  230.  
  231. class InDegreeView(DiDegreeView):
  232.     """A DegreeView class to report in_degree for a DiGraph; See DegreeView"""
  233.  
  234.     def __getitem__(self, n):
  235.         weight = self._weight
  236.         nbrs = self._pred[n]
  237.         if weight is None:
  238.             return len(nbrs)
  239.         return sum(dd.get(weight, 1) for dd in nbrs.values())
  240.  
  241.     def __iter__(self):
  242.         weight = self._weight
  243.         if weight is None:
  244.             for n in self._nodes:
  245.                 preds = self._pred[n]
  246.                 yield (n, len(preds))
  247.         else:
  248.             for n in self._nodes:
  249.                 preds = self._pred[n]
  250.                 deg = sum(dd.get(weight, 1) for dd in preds.values())
  251.                 yield (n, deg)
  252.  
  253.  
  254. class MultiDegreeView(DiDegreeView):
  255.     """A DegreeView class for undirected multigraphs; See DegreeView"""
  256.  
  257.     def __getitem__(self, n):
  258.         weight = self._weight
  259.         nbrs = self._succ[n]
  260.         if weight is None:
  261.             return sum(len(keys) for keys in nbrs.values()) + \
  262.                    (n in nbrs and len(nbrs[n]))
  263.         # edge weighted graph - degree is sum of nbr edge weights
  264.         deg = sum(d.get(weight, 1) for key_dict in nbrs.values()
  265.                   for d in key_dict.values())
  266.         if n in nbrs:
  267.             deg += sum(d.get(weight, 1) for d in nbrs[n].values())
  268.         return deg
  269.  
  270.     def __iter__(self):
  271.         weight = self._weight
  272.         if weight is None:
  273.             for n in self._nodes:
  274.                 nbrs = self._succ[n]
  275.                 deg = sum(len(keys) for keys in nbrs.values()) + \
  276.                       (n in nbrs and len(nbrs[n]))
  277.                 yield (n, deg)
  278.         else:
  279.             for n in self._nodes:
  280.                 nbrs = self._succ[n]
  281.                 deg = sum(d.get(weight, 1) for key_dict in nbrs.values()
  282.                           for d in key_dict.values())
  283.                 if n in nbrs:
  284.                     deg += sum(d.get(weight, 1) for d in nbrs[n].values())
  285.                 yield (n, deg)
  286.  
  287.  
  288. class DiMultiDegreeView(DiDegreeView):
  289.     """A DegreeView class for MultiDiGraph; See DegreeView"""
  290.  
  291.     def __getitem__(self, n):
  292.         weight = self._weight
  293.         succs = self._succ[n]
  294.         preds = self._pred[n]
  295.         if weight is None:
  296.             return sum(len(keys) for keys in succs.values()) + \
  297.                    sum(len(keys) for keys in preds.values())
  298.         # edge weighted graph - degree is sum of nbr edge weights
  299.         deg = sum(d.get(weight, 1) for key_dict in succs.values()
  300.                   for d in key_dict.values()) + \
  301.               sum(d.get(weight, 1) for key_dict in preds.values()
  302.                   for d in key_dict.values())
  303.         return deg
  304.  
  305.     def __iter__(self):
  306.         weight = self._weight
  307.         if weight is None:
  308.             for n in self._nodes:
  309.                 succs = self._succ[n]
  310.                 preds = self._pred[n]
  311.                 deg = sum(len(keys) for keys in succs.values()) + \
  312.                       sum(len(keys) for keys in preds.values())
  313.                 yield (n, deg)
  314.         else:
  315.             for n in self._nodes:
  316.                 succs = self._succ[n]
  317.                 preds = self._pred[n]
  318.                 deg = sum(d.get(weight, 1) for key_dict in succs.values()
  319.                           for d in key_dict.values()) + \
  320.                       sum(d.get(weight, 1) for key_dict in preds.values()
  321.                           for d in key_dict.values())
  322.                 yield (n, deg)
  323.  
  324.  
  325. class InMultiDegreeView(DiDegreeView):
  326.     """A DegreeView class for inward degree of MultiDiGraph; See DegreeView"""
  327.  
  328.     def __getitem__(self, n):
  329.         weight = self._weight
  330.         nbrs = self._pred[n]
  331.         if weight is None:
  332.             return sum(len(data) for data in nbrs.values())
  333.         # edge weighted graph - degree is sum of nbr edge weights
  334.         return sum(d.get(weight, 1) for key_dict in nbrs.values()
  335.                    for d in key_dict.values())
  336.  
  337.     def __iter__(self):
  338.         weight = self._weight
  339.         if weight is None:
  340.             for n in self._nodes:
  341.                 nbrs = self._pred[n]
  342.                 deg = sum(len(data) for data in nbrs.values())
  343.                 yield (n, deg)
  344.         else:
  345.             for n in self._nodes:
  346.                 nbrs = self._pred[n]
  347.                 deg = sum(d.get(weight, 1) for key_dict in nbrs.values()
  348.                           for d in key_dict.values())
  349.                 yield (n, deg)
  350.  
  351.  
  352. class OutMultiDegreeView(DiDegreeView):
  353.     """A DegreeView class for outward degree of MultiDiGraph; See DegreeView"""
  354.  
  355.     def __getitem__(self, n):
  356.         weight = self._weight
  357.         nbrs = self._succ[n]
  358.         if weight is None:
  359.             return sum(len(data) for data in nbrs.values())
  360.         # edge weighted graph - degree is sum of nbr edge weights
  361.         return sum(d.get(weight, 1) for key_dict in nbrs.values()
  362.                    for d in key_dict.values())
  363.  
  364.     def __iter__(self):
  365.         weight = self._weight
  366.         if weight is None:
  367.             for n in self._nodes:
  368.                 nbrs = self._succ[n]
  369.                 deg = sum(len(data) for data in nbrs.values())
  370.                 yield (n, deg)
  371.         else:
  372.             for n in self._nodes:
  373.                 nbrs = self._succ[n]
  374.                 deg = sum(d.get(weight, 1) for key_dict in nbrs.values()
  375.                           for d in key_dict.values())
  376.                 yield (n, deg)
  377.  
  378.  
  379. # EdgeDataViews
  380. class OutEdgeDataView(object):
  381.     """EdgeDataView for outward edges of DiGraph; See EdgeDataView"""
  382.     __slots__ = ('_viewer', '_nbunch', '_data', '_default',
  383.                  '_adjdict', '_nodes_nbrs', '_report')
  384.  
  385.     def __getstate__(self):
  386.         return {'viewer': self._viewer,
  387.                 'nbunch': self._nbunch,
  388.                 'data': self._data,
  389.                 'default': self._default}
  390.  
  391.     def __setstate__(self, state):
  392.         self.__init__(**state)
  393.  
  394.     def __init__(self, viewer, nbunch=None, data=False, default=None):
  395.         self._viewer = viewer
  396.         adjdict = self._adjdict = viewer._adjdict
  397.         if nbunch is None:
  398.             self._nodes_nbrs = adjdict.items
  399.         else:
  400.             nbunch = list(viewer._graph.nbunch_iter(nbunch))
  401.             self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
  402.         self._nbunch = nbunch
  403.         self._data = data
  404.         self._default = default
  405.         # Set _report based on data and default
  406.         if data is True:
  407.             self._report = lambda n, nbr, dd: (n, nbr, dd)
  408.         elif data is False:
  409.             self._report = lambda n, nbr, dd: (n, nbr)
  410.         else:  # data is attribute name
  411.             self._report = lambda n, nbr, dd: \
  412.                 (n, nbr, dd[data]) if data in dd else (n, nbr, default)
  413.  
  414.     def __len__(self):
  415.         return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
  416.  
  417.     def __iter__(self):
  418.         return (self._report(n, nbr, dd) for n, nbrs in self._nodes_nbrs()
  419.                 for nbr, dd in nbrs.items())
  420.  
  421.     def __contains__(self, e):
  422.         try:
  423.             u, v = e[:2]
  424.             ddict = self._adjdict[u][v]
  425.         except KeyError:
  426.             return False
  427.         return e == self._report(u, v, ddict)
  428.  
  429.     def __str__(self):
  430.         return str(list(self))
  431.  
  432.     def __repr__(self):
  433.         return '%s(%r)' % (self.__class__.__name__, list(self))
  434.  
  435.  
  436. class EdgeDataView(OutEdgeDataView):
  437.  
  438.     __slots__ = ()
  439.  
  440.     def __len__(self):
  441.         return sum(1 for e in self)
  442.  
  443.     def __iter__(self):
  444.         seen = {}
  445.         for n, nbrs in self._nodes_nbrs():
  446.             for nbr, dd in nbrs.items():
  447.                 if nbr not in seen:
  448.                     yield self._report(n, nbr, dd)
  449.             seen[n] = 1
  450.         del seen
  451.  
  452.     def __contains__(self, e):
  453.         try:
  454.             u, v = e[:2]
  455.             ddict = self._adjdict[u][v]
  456.         except KeyError:
  457.             try:
  458.                 ddict = self._adjdict[v][u]
  459.             except KeyError:
  460.                 return False
  461.         return e == self._report(u, v, ddict)
  462.  
  463.  
  464. class InEdgeDataView(OutEdgeDataView):
  465.     """An EdgeDataView class for outward edges of DiGraph; See EdgeDataView"""
  466.     __slots__ = ()
  467.  
  468.     def __iter__(self):
  469.         return (self._report(nbr, n, dd) for n, nbrs in self._nodes_nbrs()
  470.                 for nbr, dd in nbrs.items())
  471.  
  472.     def __contains__(self, e):
  473.         try:
  474.             u, v = e[:2]
  475.             ddict = self._adjdict[v][u]
  476.         except KeyError:
  477.             return False
  478.         return e == self._report(u, v, ddict)
  479.  
  480.  
  481. class OutMultiEdgeDataView(OutEdgeDataView):
  482.     """An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView"""
  483.     __slots__ = ('keys',)
  484.  
  485.     def __getstate__(self):
  486.         return {'viewer': self._viewer,
  487.                 'nbunch': self._nbunch,
  488.                 'keys': self.keys,
  489.                 'data': self._data,
  490.                 'default': self._default}
  491.  
  492.     def __setstate__(self, state):
  493.         self.__init__(**state)
  494.  
  495.     def __init__(self, viewer, nbunch=None,
  496.                  data=False, keys=False, default=None):
  497.         self._viewer = viewer
  498.         adjdict = self._adjdict = viewer._adjdict
  499.         self.keys = keys
  500.         if nbunch is None:
  501.             self._nodes_nbrs = adjdict.items
  502.         else:
  503.             nbunch = list(viewer._graph.nbunch_iter(nbunch))
  504.             self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
  505.         self._nbunch = nbunch
  506.         self._data = data
  507.         self._default = default
  508.         # Set _report based on data and default
  509.         if data is True:
  510.             if keys is True:
  511.                 self._report = lambda n, nbr, k, dd: (n, nbr, k, dd)
  512.             else:
  513.                 self._report = lambda n, nbr, k, dd: (n, nbr, dd)
  514.         elif data is False:
  515.             if keys is True:
  516.                 self._report = lambda n, nbr, k, dd: (n, nbr, k)
  517.             else:
  518.                 self._report = lambda n, nbr, k, dd: (n, nbr)
  519.         else:  # data is attribute name
  520.             if keys is True:
  521.                 self._report = lambda n, nbr, k, dd: (n, nbr, k, dd[data]) \
  522.                     if data in dd else (n, nbr, k, default)
  523.             else:
  524.                 self._report = lambda n, nbr, k, dd: (n, nbr, dd[data]) \
  525.                     if data in dd else (n, nbr, default)
  526.  
  527.     def __len__(self):
  528.         return sum(1 for e in self)
  529.  
  530.     def __iter__(self):
  531.         return (self._report(n, nbr, k, dd) for n, nbrs in self._nodes_nbrs()
  532.                 for nbr, kd in nbrs.items() for k, dd in kd.items())
  533.  
  534.     def __contains__(self, e):
  535.         u, v = e[:2]
  536.         try:
  537.             kdict = self._adjdict[u][v]
  538.         except KeyError:
  539.             return False
  540.         if self.keys is True:
  541.             k = e[2]
  542.             try:
  543.                 dd = kdict[k]
  544.             except KeyError:
  545.                 return False
  546.             return e == self._report(u, v, k, dd)
  547.         for k, dd in kdict.items():
  548.             if e == self._report(u, v, k, dd):
  549.                 return True
  550.         return False
  551.  
  552.  
  553. class MultiEdgeDataView(OutMultiEdgeDataView):
  554.     """An EdgeDataView class for edges of MultiGraph; See EdgeDataView"""
  555.     __slots__ = ()
  556.  
  557.     def __iter__(self):
  558.         seen = {}
  559.         for n, nbrs in self._nodes_nbrs():
  560.             for nbr, kd in nbrs.items():
  561.                 if nbr not in seen:
  562.                     for k, dd in kd.items():
  563.                         yield self._report(n, nbr, k, dd)
  564.             seen[n] = 1
  565.         del seen
  566.  
  567.     def __contains__(self, e):
  568.         u, v = e[:2]
  569.         try:
  570.             kdict = self._adjdict[u][v]
  571.         except KeyError:
  572.             try:
  573.                 kdict = self._adjdict[v][u]
  574.             except KeyError:
  575.                 return False
  576.         if self.keys is True:
  577.             k = e[2]
  578.             try:
  579.                 dd = kdict[k]
  580.             except KeyError:
  581.                 return False
  582.             return e == self._report(u, v, k, dd)
  583.         for k, dd in kdict.items():
  584.             if e == self._report(u, v, k, dd):
  585.                 return True
  586.         return False
  587.  
  588.  
  589. class InMultiEdgeDataView(OutMultiEdgeDataView):
  590.     """An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView"""
  591.     __slots__ = ()
  592.  
  593.     def __iter__(self):
  594.         return (self._report(nbr, n, k, dd) for n, nbrs in self._nodes_nbrs()
  595.                 for nbr, kd in nbrs.items() for k, dd in kd.items())
  596.  
  597.     def __contains__(self, e):
  598.         u, v = e[:2]
  599.         try:
  600.             kdict = self._adjdict[v][u]
  601.         except KeyError:
  602.             return False
  603.         if self.keys is True:
  604.             k = e[2]
  605.             dd = kdict[k]
  606.             return e == self._report(u, v, k, dd)
  607.         for k, dd in kdict.items():
  608.             if e == self._report(u, v, k, dd):
  609.                 return True
  610.         return False
  611.  
  612.  
  613. # EdgeViews    have set operations and no data reported
  614. class OutEdgeView(Set, Mapping):
  615.     """A EdgeView class for outward edges of a DiGraph"""
  616.     __slots__ = ('_adjdict', '_graph', '_nodes_nbrs')
  617.  
  618.     def __getstate__(self):
  619.         return {'_graph': self._graph}
  620.  
  621.     def __setstate__(self, state):
  622.         self._graph = G = state['_graph']
  623.         self._adjdict = G._succ if hasattr(G, "succ") else G._adj
  624.         self._nodes_nbrs = self._adjdict.items
  625.  
  626.     @classmethod
  627.     def _from_iterable(cls, it):
  628.         return set(it)
  629.  
  630.     dataview = OutEdgeDataView
  631.  
  632.     def __init__(self, G):
  633.         self._graph = G
  634.         self._adjdict = G._succ if hasattr(G, "succ") else G._adj
  635.         self._nodes_nbrs = self._adjdict.items
  636.  
  637.     # Set methods
  638.     def __len__(self):
  639.         return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
  640.  
  641.     def __iter__(self):
  642.         for n, nbrs in self._nodes_nbrs():
  643.             for nbr in nbrs:
  644.                 yield (n, nbr)
  645.  
  646.     def __contains__(self, e):
  647.         try:
  648.             u, v = e
  649.             return v in self._adjdict[u]
  650.         except KeyError:
  651.             return False
  652.  
  653.     # Mapping Methods
  654.     def __getitem__(self, e):
  655.         u, v = e
  656.         return self._adjdict[u][v]
  657.  
  658.     # EdgeDataView methods
  659.     def __call__(self, nbunch=None, data=False, default=None):
  660.         if nbunch is None and data is False:
  661.             return self
  662.         return self.dataview(self, nbunch, data, default)
  663.  
  664.     def data(self, data=True, default=None, nbunch=None):
  665.         if nbunch is None and data is False:
  666.             return self
  667.         return self.dataview(self, nbunch, data, default)
  668.  
  669.     # String Methods
  670.     def __str__(self):
  671.         return str(list(self))
  672.  
  673.     def __repr__(self):
  674.         return "{0.__class__.__name__}({1!r})".format(self, list(self))
  675.  
  676.  
  677. class EdgeView(OutEdgeView):
  678.     __slots__ = ()
  679.  
  680.     dataview = EdgeDataView
  681.  
  682.     def __len__(self):
  683.         num_nbrs = (len(nbrs) + (n in nbrs) for n, nbrs in self._nodes_nbrs())
  684.         return sum(num_nbrs) // 2
  685.  
  686.     def __iter__(self):
  687.         seen = {}
  688.         for n, nbrs in self._nodes_nbrs():
  689.             for nbr in list(nbrs):
  690.                 if nbr not in seen:
  691.                     yield (n, nbr)
  692.             seen[n] = 1
  693.         del seen
  694.  
  695.     def __contains__(self, e):
  696.         try:
  697.             u, v = e[:2]
  698.             return v in self._adjdict[u] or u in self._adjdict[v]
  699.         except (KeyError, ValueError):
  700.             return False
  701.  
  702.  
  703. class InEdgeView(OutEdgeView):
  704.     """A EdgeView class for inward edges of a DiGraph"""
  705.     __slots__ = ()
  706.  
  707.     def __setstate__(self, state):
  708.         self._graph = G = state['_graph']
  709.         self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  710.         self._nodes_nbrs = self._adjdict.items
  711.  
  712.     dataview = InEdgeDataView
  713.  
  714.     def __init__(self, G):
  715.         self._graph = G
  716.         self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  717.         self._nodes_nbrs = self._adjdict.items
  718.  
  719.     def __iter__(self):
  720.         for n, nbrs in self._nodes_nbrs():
  721.             for nbr in nbrs:
  722.                 yield (nbr, n)
  723.  
  724.     def __contains__(self, e):
  725.         try:
  726.             u, v = e
  727.             return u in self._adjdict[v]
  728.         except KeyError:
  729.             return False
  730.  
  731.     def __getitem__(self, e):
  732.         u, v = e
  733.         return self._adjdict[v][u]
  734.  
  735.  
  736. class OutMultiEdgeView(OutEdgeView):
  737.     """A EdgeView class for outward edges of a MultiDiGraph"""
  738.     __slots__ = ()
  739.  
  740.     dataview = OutMultiEdgeDataView
  741.  
  742.     def __len__(self):
  743.         return sum(len(kdict) for n, nbrs in self._nodes_nbrs()
  744.                    for nbr, kdict in nbrs.items())
  745.  
  746.     def __iter__(self):
  747.         for n, nbrs in self._nodes_nbrs():
  748.             for nbr, kdict in nbrs.items():
  749.                 for key in kdict:
  750.                     yield (n, nbr, key)
  751.  
  752.     def __contains__(self, e):
  753.         N = len(e)
  754.         if N == 3:
  755.             u, v, k = e
  756.         elif N == 2:
  757.             u, v = e
  758.             k = 0
  759.         else:
  760.             raise ValueError("MultiEdge must have length 2 or 3")
  761.         try:
  762.             return k in self._adjdict[u][v]
  763.         except KeyError:
  764.             return False
  765.  
  766.     def __getitem__(self, e):
  767.         u, v, k = e
  768.         return self._adjdict[u][v][k]
  769.  
  770.     def __call__(self, nbunch=None, data=False, keys=False, default=None):
  771.         if nbunch is None and data is False and keys is True:
  772.             return self
  773.         return self.dataview(self, nbunch, data, keys, default)
  774.  
  775.     def data(self, data=True, keys=False, default=None, nbunch=None):
  776.         if nbunch is None and data is False and keys is True:
  777.             return self
  778.         return self.dataview(self, nbunch, data, keys, default)
  779.  
  780.  
  781. class MultiEdgeView(OutMultiEdgeView):
  782.     """A EdgeView class for edges of a MultiGraph"""
  783.     __slots__ = ()
  784.  
  785.     dataview = MultiEdgeDataView
  786.  
  787.     def __len__(self):
  788.         return sum(1 for e in self)
  789.  
  790.     def __iter__(self):
  791.         seen = {}
  792.         for n, nbrs in self._nodes_nbrs():
  793.             for nbr, kd in nbrs.items():
  794.                 if nbr not in seen:
  795.                     for k, dd in kd.items():
  796.                         yield (n, nbr, k)
  797.             seen[n] = 1
  798.         del seen
  799.  
  800.  
  801. class InMultiEdgeView(OutMultiEdgeView):
  802.     """A EdgeView class for inward edges of a MultiDiGraph"""
  803.     __slots__ = ()
  804.  
  805.     def __setstate__(self, state):
  806.         self._graph = G = state['_graph']
  807.         self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  808.         self._nodes_nbrs = self._adjdict.items
  809.  
  810.     dataview = InMultiEdgeDataView
  811.  
  812.     def __init__(self, G):
  813.         self._graph = G
  814.         self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  815.         self._nodes_nbrs = self._adjdict.items
  816.  
  817.     def __iter__(self):
  818.         for n, nbrs in self._nodes_nbrs():
  819.             for nbr, kdict in nbrs.items():
  820.                 for key in kdict:
  821.                     yield (nbr, n, key)
  822.  
  823.     def __contains__(self, e):
  824.         N = len(e)
  825.         if N == 3:
  826.             u, v, k = e
  827.         elif N == 2:
  828.             u, v = e
  829.             k = 0
  830.         else:
  831.             raise ValueError("MultiEdge must have length 2 or 3")
  832.         try:
  833.             return k in self._adjdict[v][u]
  834.         except KeyError:
  835.             return False
  836.  
  837.     def __getitem__(self, e):
  838.         u, v, k = e
  839.         return self._adjdict[v][u][k]
  840.  
  841.  
  842. # __all__ = ['AtlasView', 'AdjacencyView', 'MultiAdjacencyView',
  843. #            'UnionAtlas', 'UnionAdjacency',
  844. #            'UnionMultiInner', 'UnionMultiAdjacency',
  845. #            'FilterAtlas', 'FilterAdjacency',
  846. #            'FilterMultiInner', 'FilterMultiAdjacency',
  847. # ]
  848.  
  849.  
  850. class AtlasView(Mapping):
  851.     __slots__ = ('_atlas',)
  852.  
  853.     def __getstate__(self):
  854.         return {'_atlas': self._atlas}
  855.  
  856.     def __setstate__(self, state):
  857.         self._atlas = state['_atlas']
  858.  
  859.     def __init__(self, d):
  860.         self._atlas = d
  861.  
  862.     def __len__(self):
  863.         return len(self._atlas)
  864.  
  865.     def __iter__(self):
  866.         return iter(self._atlas)
  867.  
  868.     def __getitem__(self, key):
  869.         return self._atlas[key]
  870.  
  871.     def copy(self):
  872.         return {n: self[n].copy() for n in self._atlas}
  873.  
  874.     def __str__(self):
  875.         return str(self._atlas)  # {nbr: self[nbr] for nbr in self})
  876.  
  877.     def __repr__(self):
  878.         return '%s(%r)' % (self.__class__.__name__, self._atlas)
  879.  
  880.  
  881. class AdjacencyView(AtlasView):
  882.     __slots__ = ()  # Still uses AtlasView slots names _atlas
  883.  
  884.     def __getitem__(self, name):
  885.         return AtlasView(self._atlas[name])
  886.  
  887.     def copy(self):
  888.         return {n: self[n].copy() for n in self._atlas}
  889.  
  890.  
  891. class MultiAdjacencyView(AdjacencyView):
  892.     __slots__ = ()  # Still uses AtlasView slots names _atlas
  893.  
  894.     def __getitem__(self, name):
  895.         return AdjacencyView(self._atlas[name])
  896.  
  897.     def copy(self):
  898.         return {n: self[n].copy() for n in self._atlas}
  899.  
  900.  
  901. class UnionAtlas(Mapping):
  902.     __slots__ = ('_succ', '_pred')
  903.  
  904.     def __getstate__(self):
  905.         return {'_succ': self._succ, '_pred': self._pred}
  906.  
  907.     def __setstate__(self, state):
  908.         self._succ = state['_succ']
  909.         self._pred = state['_pred']
  910.  
  911.     def __init__(self, succ, pred):
  912.         self._succ = succ
  913.         self._pred = pred
  914.  
  915.     def __len__(self):
  916.         return len(self._succ) + len(self._pred)
  917.  
  918.     def __iter__(self):
  919.         return iter(set(self._succ.keys()) | set(self._pred.keys()))
  920.  
  921.     def __getitem__(self, key):
  922.         try:
  923.             return self._succ[key]
  924.         except KeyError:
  925.             return self._pred[key]
  926.  
  927.     def copy(self):
  928.         result = {nbr: dd.copy() for nbr, dd in self._succ.items()}
  929.         for nbr, dd in self._pred.items():
  930.             if nbr in result:
  931.                 result[nbr].update(dd)
  932.             else:
  933.                 result[nbr] = dd.copy()
  934.         return result
  935.  
  936.     def __str__(self):
  937.         return str({nbr: self[nbr] for nbr in self})
  938.  
  939.     def __repr__(self):
  940.         return '%s(%r, %r)' % (self.__class__.__name__, self._succ, self._pred)
  941.  
  942.  
  943. class UnionAdjacency(Mapping):
  944.     __slots__ = ('_succ', '_pred')
  945.  
  946.     def __getstate__(self):
  947.         return {'_succ': self._succ, '_pred': self._pred}
  948.  
  949.     def __setstate__(self, state):
  950.         self._succ = state['_succ']
  951.         self._pred = state['_pred']
  952.  
  953.     def __init__(self, succ, pred):
  954.         # keys must be the same for two input dicts
  955.         assert (len(set(succ.keys()) ^ set(pred.keys())) == 0)
  956.         self._succ = succ
  957.         self._pred = pred
  958.  
  959.     def __len__(self):
  960.         return len(self._succ)  # length of each dict should be the same
  961.  
  962.     def __iter__(self):
  963.         return iter(self._succ)
  964.  
  965.     def __getitem__(self, nbr):
  966.         return UnionAtlas(self._succ[nbr], self._pred[nbr])
  967.  
  968.     def copy(self):
  969.         return {n: self[n].copy() for n in self._succ}
  970.  
  971.     def __str__(self):
  972.         return str({nbr: self[nbr] for nbr in self})
  973.  
  974.     def __repr__(self):
  975.         return '%s(%r, %r)' % (self.__class__.__name__, self._succ, self._pred)
  976.  
  977.  
  978. class UnionMultiInner(UnionAtlas):
  979.     __slots__ = ()  # Still uses UnionAtlas slots names _succ, _pred
  980.  
  981.     def __getitem__(self, node):
  982.         in_succ = node in self._succ
  983.         in_pred = node in self._pred
  984.         if in_succ:
  985.             if in_pred:
  986.                 return UnionAtlas(self._succ[node], self._pred[node])
  987.             return UnionAtlas(self._succ[node], {})
  988.         return UnionAtlas({}, self._pred[node])
  989.  
  990.     def copy(self):
  991.         nodes = set(self._succ.keys()) | set(self._pred.keys())
  992.         return {n: self[n].copy() for n in nodes}
  993.  
  994.  
  995. class UnionMultiAdjacency(UnionAdjacency):
  996.     __slots__ = ()  # Still uses UnionAdjacency slots names _succ, _pred
  997.  
  998.     def __getitem__(self, node):
  999.         return UnionMultiInner(self._succ[node], self._pred[node])
  1000.  
  1001.  
  1002. class FilterAtlas(Mapping):  # nodedict, nbrdict, keydict
  1003.     def __init__(self, d, NODE_OK):
  1004.         self._atlas = d
  1005.         self.NODE_OK = NODE_OK
  1006.  
  1007.     def __len__(self):
  1008.         return sum(1 for n in self)
  1009.  
  1010.     def __iter__(self):
  1011.         try:  # check that NODE_OK has attr 'nodes'
  1012.             node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
  1013.         except AttributeError:
  1014.             node_ok_shorter = False
  1015.         if node_ok_shorter:
  1016.             return (n for n in self.NODE_OK.nodes if n in self._atlas)
  1017.         return (n for n in self._atlas if self.NODE_OK(n))
  1018.  
  1019.     def __getitem__(self, key):
  1020.         if key in self._atlas and self.NODE_OK(key):
  1021.             return self._atlas[key]
  1022.         raise KeyError("Key {} not found".format(key))
  1023.  
  1024.     def copy(self):
  1025.         try:  # check that NODE_OK has attr 'nodes'
  1026.             node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
  1027.         except AttributeError:
  1028.             node_ok_shorter = False
  1029.         if node_ok_shorter:
  1030.             return {u: self._atlas[u] for u in self.NODE_OK.nodes
  1031.                     if u in self._atlas}
  1032.         return {u: d for u, d in self._atlas.items()
  1033.                 if self.NODE_OK(u)}
  1034.  
  1035.     def __str__(self):
  1036.         return str({nbr: self[nbr] for nbr in self})
  1037.  
  1038.     def __repr__(self):
  1039.         return '%s(%r, %r)' % (self.__class__.__name__, self._atlas,
  1040.                                self.NODE_OK)
  1041.  
  1042.  
  1043. class FilterAdjacency(Mapping):  # edgedict
  1044.     def __init__(self, d, NODE_OK, EDGE_OK):
  1045.         self._atlas = d
  1046.         self.NODE_OK = NODE_OK
  1047.         self.EDGE_OK = EDGE_OK
  1048.  
  1049.     def __len__(self):
  1050.         return sum(1 for n in self)
  1051.  
  1052.     def __iter__(self):
  1053.         try:  # check that NODE_OK has attr 'nodes'
  1054.             node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
  1055.         except AttributeError:
  1056.             node_ok_shorter = False
  1057.         if node_ok_shorter:
  1058.             return (n for n in self.NODE_OK.nodes if n in self._atlas)
  1059.         return (n for n in self._atlas if self.NODE_OK(n))
  1060.  
  1061.     def __getitem__(self, node):
  1062.         if node in self._atlas and self.NODE_OK(node):
  1063.             def new_node_ok(nbr):
  1064.                 return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr)
  1065.  
  1066.             return FilterAtlas(self._atlas[node], new_node_ok)
  1067.         raise KeyError("Key {} not found".format(node))
  1068.  
  1069.     def copy(self):
  1070.         try:  # check that NODE_OK has attr 'nodes'
  1071.             node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
  1072.         except AttributeError:
  1073.             node_ok_shorter = False
  1074.         if node_ok_shorter:
  1075.             return {u: {v: d for v, d in self._atlas[u].items()
  1076.                         if self.NODE_OK(v) if self.EDGE_OK(u, v)}
  1077.                     for u in self.NODE_OK.nodes if u in self._atlas}
  1078.         return {u: {v: d for v, d in nbrs.items() if self.NODE_OK(v)
  1079.                     if self.EDGE_OK(u, v)}
  1080.                 for u, nbrs in self._atlas.items()
  1081.                 if self.NODE_OK(u)}
  1082.  
  1083.     def __str__(self):
  1084.         return str({nbr: self[nbr] for nbr in self})
  1085.  
  1086.     def __repr__(self):
  1087.         return '%s(%r, %r, %r)' % (self.__class__.__name__, self._atlas,
  1088.                                    self.NODE_OK, self.EDGE_OK)
  1089.  
  1090.  
  1091. class FilterMultiInner(FilterAdjacency):  # muliedge_seconddict
  1092.     def __iter__(self):
  1093.         try:  # check that NODE_OK has attr 'nodes'
  1094.             node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
  1095.         except AttributeError:
  1096.             node_ok_shorter = False
  1097.         if node_ok_shorter:
  1098.             my_nodes = (n for n in self.NODE_OK.nodes if n in self._atlas)
  1099.         else:
  1100.             my_nodes = (n for n in self._atlas if self.NODE_OK(n))
  1101.         for n in my_nodes:
  1102.             some_keys_ok = False
  1103.             for key in self._atlas[n]:
  1104.                 if self.EDGE_OK(n, key):
  1105.                     some_keys_ok = True
  1106.                     break
  1107.             if some_keys_ok is True:
  1108.                 yield n
  1109.  
  1110.     def __getitem__(self, nbr):
  1111.         if nbr in self._atlas and self.NODE_OK(nbr):
  1112.             def new_node_ok(key):
  1113.                 return self.EDGE_OK(nbr, key)
  1114.  
  1115.             return FilterAtlas(self._atlas[nbr], new_node_ok)
  1116.         raise KeyError("Key {} not found".format(nbr))
  1117.  
  1118.     def copy(self):
  1119.         try:  # check that NODE_OK has attr 'nodes'
  1120.             node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
  1121.         except AttributeError:
  1122.             node_ok_shorter = False
  1123.         if node_ok_shorter:
  1124.             return {v: {k: d for k, d in self._atlas[v].items()
  1125.                         if self.EDGE_OK(v, k)}
  1126.                     for v in self.NODE_OK.nodes if v in self._atlas}
  1127.         return {v: {k: d for k, d in nbrs.items() if self.EDGE_OK(v, k)}
  1128.                 for v, nbrs in self._atlas.items() if self.NODE_OK(v)}
  1129.  
  1130.  
  1131. class FilterMultiAdjacency(FilterAdjacency):  # multiedgedict
  1132.     def __getitem__(self, node):
  1133.         if node in self._atlas and self.NODE_OK(node):
  1134.             def edge_ok(nbr, key):
  1135.                 return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr, key)
  1136.  
  1137.             return FilterMultiInner(self._atlas[node], self.NODE_OK, edge_ok)
  1138.         raise KeyError("Key {} not found".format(node))
  1139.  
  1140.     def copy(self):
  1141.         try:  # check that NODE_OK has attr 'nodes'
  1142.             node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
  1143.         except AttributeError:
  1144.             node_ok_shorter = False
  1145.         if node_ok_shorter:
  1146.             my_nodes = self.NODE_OK.nodes
  1147.             return {u: {v: {k: d for k, d in kd.items()
  1148.                             if self.EDGE_OK(u, v, k)}
  1149.                         for v, kd in self._atlas[u].items() if v in my_nodes}
  1150.                     for u in my_nodes if u in self._atlas}
  1151.         return {u: {v: {k: d for k, d in kd.items()
  1152.                         if self.EDGE_OK(u, v, k)}
  1153.                     for v, kd in nbrs.items() if self.NODE_OK(v)}
  1154.                 for u, nbrs in self._atlas.items() if self.NODE_OK(u)}
  1155.  
  1156.  
  1157. class Graph(object):
  1158.     node_dict_factory = dict
  1159.     node_attr_dict_factory = dict
  1160.     adjlist_outer_dict_factory = dict
  1161.     adjlist_inner_dict_factory = dict
  1162.     edge_attr_dict_factory = dict
  1163.     graph_attr_dict_factory = dict
  1164.  
  1165.     def to_directed_class(self):
  1166.         return nx.DiGraph
  1167.  
  1168.     def to_undirected_class(self):
  1169.         return Graph
  1170.  
  1171.     def __init__(self, incoming_graph_data=None, **attr):
  1172.         self.graph_attr_dict_factory = self.graph_attr_dict_factory
  1173.         self.node_dict_factory = self.node_dict_factory
  1174.         self.node_attr_dict_factory = self.node_attr_dict_factory
  1175.         self.adjlist_outer_dict_factory = self.adjlist_outer_dict_factory
  1176.         self.adjlist_inner_dict_factory = self.adjlist_inner_dict_factory
  1177.         self.edge_attr_dict_factory = self.edge_attr_dict_factory
  1178.  
  1179.         self.graph = self.graph_attr_dict_factory()  # dictionary for graph attributes
  1180.         self._node = self.node_dict_factory()  # empty node attribute dict
  1181.         self._adj = self.adjlist_outer_dict_factory()  # empty adjacency dict
  1182.         # attempt to load graph with data
  1183.         if incoming_graph_data is not None:
  1184.             convert.to_networkx_graph(incoming_graph_data, create_using=self)
  1185.         # load graph attributes (must be after convert)
  1186.         self.graph.update(attr)
  1187.  
  1188.     @property
  1189.     def adj(self):
  1190.         return AdjacencyView(self._adj)
  1191.  
  1192.     @property
  1193.     def name(self):
  1194.         return self.graph.get('name', '')
  1195.  
  1196.     @name.setter
  1197.     def name(self, s):
  1198.         self.graph['name'] = s
  1199.  
  1200.     def __str__(self):
  1201.         return self.name
  1202.  
  1203.     def __iter__(self):
  1204.         return iter(self._node)
  1205.  
  1206.     def __contains__(self, n):
  1207.         try:
  1208.             return n in self._node
  1209.         except TypeError:
  1210.             return False
  1211.  
  1212.     def __len__(self):
  1213.         return len(self._node)
  1214.  
  1215.     def __getitem__(self, n):
  1216.         return self.adj[n]
  1217.  
  1218.     def add_node(self, node_for_adding, **attr):
  1219.         if node_for_adding not in self._node:
  1220.             self._adj[node_for_adding] = self.adjlist_inner_dict_factory()
  1221.             attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory()
  1222.             attr_dict.update(attr)
  1223.         else:  # update attr even if node already exists
  1224.             self._node[node_for_adding].update(attr)
  1225.  
  1226.     def add_nodes_from(self, nodes_for_adding, **attr):
  1227.         for n in nodes_for_adding:
  1228.             # keep all this inside try/except because
  1229.             # CPython throws TypeError on n not in self._node,
  1230.             # while pre-2.7.5 ironpython throws on self._adj[n]
  1231.             try:
  1232.                 if n not in self._node:
  1233.                     self._adj[n] = self.adjlist_inner_dict_factory()
  1234.                     attr_dict = self._node[n] = self.node_attr_dict_factory()
  1235.                     attr_dict.update(attr)
  1236.                 else:
  1237.                     self._node[n].update(attr)
  1238.             except TypeError:
  1239.                 nn, ndict = n
  1240.                 if nn not in self._node:
  1241.                     self._adj[nn] = self.adjlist_inner_dict_factory()
  1242.                     newdict = attr.copy()
  1243.                     newdict.update(ndict)
  1244.                     attr_dict = self._node[nn] = self.node_attr_dict_factory()
  1245.                     attr_dict.update(newdict)
  1246.                 else:
  1247.                     olddict = self._node[nn]
  1248.                     olddict.update(attr)
  1249.                     olddict.update(ndict)
  1250.  
  1251.     def remove_node(self, n):
  1252.         adj = self._adj
  1253.         try:
  1254.             nbrs = list(adj[n])  # list handles self-loops (allows mutation)
  1255.             del self._node[n]
  1256.         except KeyError:  # NetworkXError if n not in self
  1257.             raise NetworkXError("The node %s is not in the graph." % (n,))
  1258.         for u in nbrs:
  1259.             del adj[u][n]  # remove all edges n-u in graph
  1260.         del adj[n]  # now remove node
  1261.  
  1262.     def remove_nodes_from(self, nodes):
  1263.         adj = self._adj
  1264.         for n in nodes:
  1265.             try:
  1266.                 del self._node[n]
  1267.                 for u in list(adj[n]):  # list handles self-loops
  1268.                     del adj[u][n]  # (allows mutation of dict in loop)
  1269.                 del adj[n]
  1270.             except KeyError:
  1271.                 pass
  1272.  
  1273.     @property
  1274.     def nodes(self):
  1275.         nodes = NodeView(self)
  1276.         # Lazy View creation: overload the (class) property on the instance
  1277.         # Then future G.nodes use the existing View
  1278.         # setattr doesn't work because attribute already exists
  1279.         self.__dict__['nodes'] = nodes
  1280.         return nodes
  1281.  
  1282.     def number_of_nodes(self):
  1283.         return len(self._node)
  1284.  
  1285.     def order(self):
  1286.         return len(self._node)
  1287.  
  1288.     def has_node(self, n):
  1289.         try:
  1290.             return n in self._node
  1291.         except TypeError:
  1292.             return False
  1293.  
  1294.     def add_edge(self, u_of_edge, v_of_edge, **attr):
  1295.         u, v = u_of_edge, v_of_edge
  1296.         # add nodes
  1297.         if u not in self._node:
  1298.             self._adj[u] = self.adjlist_inner_dict_factory()
  1299.             self._node[u] = self.node_attr_dict_factory()
  1300.         if v not in self._node:
  1301.             self._adj[v] = self.adjlist_inner_dict_factory()
  1302.             self._node[v] = self.node_attr_dict_factory()
  1303.         # add the edge
  1304.         datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
  1305.         datadict.update(attr)
  1306.         self._adj[u][v] = datadict
  1307.         self._adj[v][u] = datadict
  1308.  
  1309.     def add_edges_from(self, ebunch_to_add, **attr):
  1310.         for e in ebunch_to_add:
  1311.             ne = len(e)
  1312.             if ne == 3:
  1313.                 u, v, dd = e
  1314.             elif ne == 2:
  1315.                 u, v = e
  1316.                 dd = {}  # doesn't need edge_attr_dict_factory
  1317.             else:
  1318.                 raise NetworkXError(
  1319.                     "Edge tuple %s must be a 2-tuple or 3-tuple." % (e,))
  1320.             if u not in self._node:
  1321.                 self._adj[u] = self.adjlist_inner_dict_factory()
  1322.                 self._node[u] = self.node_attr_dict_factory()
  1323.             if v not in self._node:
  1324.                 self._adj[v] = self.adjlist_inner_dict_factory()
  1325.                 self._node[v] = self.node_attr_dict_factory()
  1326.             datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
  1327.             datadict.update(attr)
  1328.             datadict.update(dd)
  1329.             self._adj[u][v] = datadict
  1330.             self._adj[v][u] = datadict
  1331.  
  1332.     def add_weighted_edges_from(self, ebunch_to_add, weight='weight', **attr):
  1333.         self.add_edges_from(((u, v, {weight: d}) for u, v, d in ebunch_to_add),
  1334.                             **attr)
  1335.  
  1336.     def remove_edge(self, u, v):
  1337.         try:
  1338.             del self._adj[u][v]
  1339.             if u != v:  # self-loop needs only one entry removed
  1340.                 del self._adj[v][u]
  1341.         except KeyError:
  1342.             raise NetworkXError("The edge %s-%s is not in the graph" % (u, v))
  1343.  
  1344.     def remove_edges_from(self, ebunch):
  1345.         adj = self._adj
  1346.         for e in ebunch:
  1347.             u, v = e[:2]  # ignore edge data if present
  1348.             if u in adj and v in adj[u]:
  1349.                 del adj[u][v]
  1350.                 if u != v:  # self loop needs only one entry removed
  1351.                     del adj[v][u]
  1352.  
  1353.     def update(self, edges=None, nodes=None):
  1354.         if edges is not None:
  1355.             if nodes is not None:
  1356.                 self.add_nodes_from(nodes)
  1357.                 self.add_edges_from(edges)
  1358.             else:
  1359.                 # check if edges is a Graph object
  1360.                 try:
  1361.                     graph_nodes = edges.nodes
  1362.                     graph_edges = edges.edges
  1363.                 except AttributeError:
  1364.                     # edge not Graph-like
  1365.                     self.add_edges_from(edges)
  1366.                 else:  # edges is Graph-like
  1367.                     self.add_nodes_from(graph_nodes.data())
  1368.                     self.add_edges_from(graph_edges.data())
  1369.                     self.graph.update(edges.graph)
  1370.         elif nodes is not None:
  1371.             self.add_nodes_from(nodes)
  1372.         else:
  1373.             raise NetworkXError("update needs nodes or edges input")
  1374.  
  1375.     def has_edge(self, u, v):
  1376.         try:
  1377.             return v in self._adj[u]
  1378.         except KeyError:
  1379.             return False
  1380.  
  1381.     def neighbors(self, n):
  1382.         try:
  1383.             return iter(self._adj[n])
  1384.         except KeyError:
  1385.             raise NetworkXError("The node %s is not in the graph." % (n,))
  1386.  
  1387.     @property
  1388.     def edges(self):
  1389.         return EdgeView(self)
  1390.  
  1391.     def get_edge_data(self, u, v, default=None):
  1392.         try:
  1393.             return self._adj[u][v]
  1394.         except KeyError:
  1395.             return default
  1396.  
  1397.     def adjacency(self):
  1398.         return iter(self._adj.items())
  1399.  
  1400.     @property
  1401.     def degree(self):
  1402.         return DegreeView(self)
  1403.  
  1404.     def clear(self):
  1405.         self._adj.clear()
  1406.         self._node.clear()
  1407.         self.graph.clear()
  1408.  
  1409.     def is_multigraph(self):
  1410.         """Returns True if graph is a multigraph, False otherwise."""
  1411.         return False
  1412.  
  1413.     def is_directed(self):
  1414.         """Returns True if graph is directed, False otherwise."""
  1415.         return False
  1416.  
  1417.     def copy(self, as_view=False):
  1418.    
  1419.         if as_view is True:
  1420.             return nx.graphviews.generic_graph_view(self)
  1421.         G = self.__class__()
  1422.         G.graph.update(self.graph)
  1423.         G.add_nodes_from((n, d.copy()) for n, d in self._node.items())
  1424.         G.add_edges_from((u, v, datadict.copy())
  1425.                          for u, nbrs in self._adj.items()
  1426.                          for v, datadict in nbrs.items())
  1427.         return G
  1428.  
  1429.     def to_directed(self, as_view=False):
  1430.      
  1431.         graph_class = self.to_directed_class()
  1432.         if as_view is True:
  1433.             return nx.graphviews.generic_graph_view(self, graph_class)
  1434.         # deepcopy when not a view
  1435.         G = graph_class()
  1436.         G.graph.update(deepcopy(self.graph))
  1437.         G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
  1438.         G.add_edges_from((u, v, deepcopy(data))
  1439.                          for u, nbrs in self._adj.items()
  1440.                          for v, data in nbrs.items())
  1441.         return G
  1442.  
  1443.     def to_undirected(self, as_view=False):
  1444.  
  1445.         graph_class = self.to_undirected_class()
  1446.         if as_view is True:
  1447.             return nx.graphviews.generic_graph_view(self, graph_class)
  1448.         # deepcopy when not a view
  1449.         G = graph_class()
  1450.         G.graph.update(deepcopy(self.graph))
  1451.         G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
  1452.         G.add_edges_from((u, v, deepcopy(d))
  1453.                          for u, nbrs in self._adj.items()
  1454.                          for v, d in nbrs.items())
  1455.         return G
  1456.  
  1457.     def subgraph(self, nodes):
  1458.    
  1459.         induced_nodes = nx.filters.show_nodes(self.nbunch_iter(nodes))
  1460.         # if already a subgraph, don't make a chain
  1461.         subgraph = nx.graphviews.subgraph_view
  1462.         if hasattr(self, '_NODE_OK'):
  1463.             return subgraph(self._graph, induced_nodes, self._EDGE_OK)
  1464.         return subgraph(self, induced_nodes)
  1465.  
  1466.     def edge_subgraph(self, edges):
  1467.        
  1468.         return nx.edge_subgraph(self, edges)
  1469.  
  1470.     def size(self, weight=None):
  1471.  
  1472.         s = sum(d for v, d in self.degree(weight=weight))
  1473.         # If `weight` is None, the sum of the degrees is guaranteed to be
  1474.         # even, so we can perform integer division and hence return an
  1475.         # integer. Otherwise, the sum of the weighted degrees is not
  1476.         # guaranteed to be an integer, so we perform "real" division.
  1477.         return s // 2 if weight is None else s / 2
  1478.  
  1479.     def number_of_edges(self, u=None, v=None):
  1480.    
  1481.         if u is None:
  1482.             return int(self.size())
  1483.         if v in self._adj[u]:
  1484.             return 1
  1485.         return 0
  1486.  
  1487.     def nbunch_iter(self, nbunch=None):
  1488.  
  1489.         if nbunch is None:  # include all nodes via iterator
  1490.             bunch = iter(self._adj)
  1491.         elif nbunch in self:  # if nbunch is a single node
  1492.             bunch = iter([nbunch])
  1493.         else:  # if nbunch is a sequence of nodes
  1494.             def bunch_iter(nlist, adj):
  1495.                 try:
  1496.                     for n in nlist:
  1497.                         if n in adj:
  1498.                             yield n
  1499.                 except TypeError as e:
  1500.                     message = e.args[0]
  1501.                     # capture error for non-sequence/iterator nbunch.
  1502.                     if 'iter' in message:
  1503.                         msg = "nbunch is not a node or a sequence of nodes."
  1504.                         raise NetworkXError(msg)
  1505.                     # capture error for unhashable node.
  1506.                     elif 'hashable' in message:
  1507.                         msg = "Node {} in sequence nbunch is not a valid node."
  1508.                         raise NetworkXError(msg.format(n))
  1509.                     else:
  1510.                         raise
  1511.  
  1512.             bunch = bunch_iter(nbunch, self._adj)
  1513.         return bunch
  1514.  
  1515.  
  1516. class DiGraph(Graph):
  1517.  
  1518.  
  1519.     def __init__(self, incoming_graph_data=None, **attr):
  1520.  
  1521.         self.graph_attr_dict_factory = self.graph_attr_dict_factory
  1522.         self.node_dict_factory = self.node_dict_factory
  1523.         self.node_attr_dict_factory = self.node_attr_dict_factory
  1524.         self.adjlist_outer_dict_factory = self.adjlist_outer_dict_factory
  1525.         self.adjlist_inner_dict_factory = self.adjlist_inner_dict_factory
  1526.         self.edge_attr_dict_factory = self.edge_attr_dict_factory
  1527.  
  1528.         self.graph = self.graph_attr_dict_factory()  # dictionary for graph attributes
  1529.         self._node = self.node_dict_factory()  # dictionary for node attr
  1530.         # We store two adjacency lists:
  1531.         # the  predecessors of node n are stored in the dict self._pred
  1532.         # the successors of node n are stored in the dict self._succ=self._adj
  1533.         self._adj = self.adjlist_outer_dict_factory()  # empty adjacency dict
  1534.         self._pred = self.adjlist_outer_dict_factory()  # predecessor
  1535.         self._succ = self._adj  # successor
  1536.  
  1537.         # attempt to load graph with data
  1538.         if incoming_graph_data is not None:
  1539.             convert.to_networkx_graph(incoming_graph_data, create_using=self)
  1540.         # load graph attributes (must be after convert)
  1541.         self.graph.update(attr)
  1542.  
  1543.     @property
  1544.     def adj(self):
  1545.    
  1546.         return AdjacencyView(self._succ)
  1547.  
  1548.     @property
  1549.     def succ(self):
  1550.  
  1551.         return AdjacencyView(self._succ)
  1552.  
  1553.     @property
  1554.     def pred(self):
  1555.    
  1556.         return AdjacencyView(self._pred)
  1557.  
  1558.     def add_node(self, node_for_adding, **attr):
  1559.  
  1560.         if node_for_adding not in self._succ:
  1561.             self._succ[node_for_adding] = self.adjlist_inner_dict_factory()
  1562.             self._pred[node_for_adding] = self.adjlist_inner_dict_factory()
  1563.             attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory()
  1564.             attr_dict.update(attr)
  1565.         else:  # update attr even if node already exists
  1566.             self._node[node_for_adding].update(attr)
  1567.  
  1568.     def add_nodes_from(self, nodes_for_adding, **attr):
  1569.  
  1570.         for n in nodes_for_adding:
  1571.             # keep all this inside try/except because
  1572.             # CPython throws TypeError on n not in self._succ,
  1573.             # while pre-2.7.5 ironpython throws on self._succ[n]
  1574.             try:
  1575.                 if n not in self._succ:
  1576.                     self._succ[n] = self.adjlist_inner_dict_factory()
  1577.                     self._pred[n] = self.adjlist_inner_dict_factory()
  1578.                     attr_dict = self._node[n] = self.node_attr_dict_factory()
  1579.                     attr_dict.update(attr)
  1580.                 else:
  1581.                     self._node[n].update(attr)
  1582.             except TypeError:
  1583.                 nn, ndict = n
  1584.                 if nn not in self._succ:
  1585.                     self._succ[nn] = self.adjlist_inner_dict_factory()
  1586.                     self._pred[nn] = self.adjlist_inner_dict_factory()
  1587.                     newdict = attr.copy()
  1588.                     newdict.update(ndict)
  1589.                     attr_dict = self._node[nn] = self.node_attr_dict_factory()
  1590.                     attr_dict.update(newdict)
  1591.                 else:
  1592.                     olddict = self._node[nn]
  1593.                     olddict.update(attr)
  1594.                     olddict.update(ndict)
  1595.  
  1596.     def remove_node(self, n):
  1597.         try:
  1598.             nbrs = self._succ[n]
  1599.             del self._node[n]
  1600.         except KeyError:  # NetworkXError if n not in self
  1601.             raise NetworkXError("The node %s is not in the digraph." % (n,))
  1602.         for u in nbrs:
  1603.             del self._pred[u][n]  # remove all edges n-u in digraph
  1604.         del self._succ[n]  # remove node from succ
  1605.         for u in self._pred[n]:
  1606.             del self._succ[u][n]  # remove all edges n-u in digraph
  1607.         del self._pred[n]  # remove node from pred
  1608.  
  1609.     def remove_nodes_from(self, nodes):
  1610.         for n in nodes:
  1611.             try:
  1612.                 succs = self._succ[n]
  1613.                 del self._node[n]
  1614.                 for u in succs:
  1615.                     del self._pred[u][n]  # remove all edges n-u in digraph
  1616.                 del self._succ[n]  # now remove node
  1617.                 for u in self._pred[n]:
  1618.                     del self._succ[u][n]  # remove all edges n-u in digraph
  1619.                 del self._pred[n]  # now remove node
  1620.             except KeyError:
  1621.                 pass  # silent failure on remove
  1622.  
  1623.     def add_edge(self, u_of_edge, v_of_edge, **attr):
  1624.         u, v = u_of_edge, v_of_edge
  1625.         # add nodes
  1626.         if u not in self._succ:
  1627.             self._succ[u] = self.adjlist_inner_dict_factory()
  1628.             self._pred[u] = self.adjlist_inner_dict_factory()
  1629.             self._node[u] = self.node_attr_dict_factory()
  1630.         if v not in self._succ:
  1631.             self._succ[v] = self.adjlist_inner_dict_factory()
  1632.             self._pred[v] = self.adjlist_inner_dict_factory()
  1633.             self._node[v] = self.node_attr_dict_factory()
  1634.         # add the edge
  1635.         datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
  1636.         datadict.update(attr)
  1637.         self._succ[u][v] = datadict
  1638.         self._pred[v][u] = datadict
  1639.  
  1640.     def add_edges_from(self, ebunch_to_add, **attr):
  1641.         for e in ebunch_to_add:
  1642.             ne = len(e)
  1643.             if ne == 3:
  1644.                 u, v, dd = e
  1645.             elif ne == 2:
  1646.                 u, v = e
  1647.                 dd = {}
  1648.             else:
  1649.                 raise NetworkXError(
  1650.                     "Edge tuple %s must be a 2-tuple or 3-tuple." % (e,))
  1651.             if u not in self._succ:
  1652.                 self._succ[u] = self.adjlist_inner_dict_factory()
  1653.                 self._pred[u] = self.adjlist_inner_dict_factory()
  1654.                 self._node[u] = self.node_attr_dict_factory()
  1655.             if v not in self._succ:
  1656.                 self._succ[v] = self.adjlist_inner_dict_factory()
  1657.                 self._pred[v] = self.adjlist_inner_dict_factory()
  1658.                 self._node[v] = self.node_attr_dict_factory()
  1659.             datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
  1660.             datadict.update(attr)
  1661.             datadict.update(dd)
  1662.             self._succ[u][v] = datadict
  1663.             self._pred[v][u] = datadict
  1664.  
  1665.     def remove_edge(self, u, v):
  1666.         try:
  1667.             del self._succ[u][v]
  1668.             del self._pred[v][u]
  1669.         except KeyError:
  1670.             raise NetworkXError("The edge %s-%s not in graph." % (u, v))
  1671.  
  1672.     def remove_edges_from(self, ebunch):
  1673.         for e in ebunch:
  1674.             u, v = e[:2]  # ignore edge data
  1675.             if u in self._succ and v in self._succ[u]:
  1676.                 del self._succ[u][v]
  1677.                 del self._pred[v][u]
  1678.  
  1679.     def has_successor(self, u, v):
  1680.         """Returns True if node u has successor v.
  1681.        This is true if graph has the edge u->v.
  1682.        """
  1683.         return (u in self._succ and v in self._succ[u])
  1684.  
  1685.     def has_predecessor(self, u, v):
  1686.         """Returns True if node u has predecessor v.
  1687.        This is true if graph has the edge u<-v.
  1688.        """
  1689.         return (u in self._pred and v in self._pred[u])
  1690.  
  1691.     def successors(self, n):
  1692.         try:
  1693.             return iter(self._succ[n])
  1694.         except KeyError:
  1695.             raise NetworkXError("The node %s is not in the digraph." % (n,))
  1696.  
  1697.     # digraph definitions
  1698.     neighbors = successors
  1699.  
  1700.     def predecessors(self, n):
  1701.         try:
  1702.             return iter(self._pred[n])
  1703.         except KeyError:
  1704.             raise NetworkXError("The node %s is not in the digraph." % (n,))
  1705.  
  1706.     @property
  1707.     def edges(self):
  1708.         return OutEdgeView(self)
  1709.  
  1710.     # alias out_edges to edges
  1711.     out_edges = edges
  1712.  
  1713.     @property
  1714.     def in_edges(self):
  1715.         return InEdgeView(self)
  1716.  
  1717.     @property
  1718.     def degree(self):
  1719.         return DiDegreeView(self)
  1720.  
  1721.     @property
  1722.     def in_degree(self):
  1723.         return InDegreeView(self)
  1724.  
  1725.     @property
  1726.     def out_degree(self):
  1727.         self._succ.clear()
  1728.         self._pred.clear()
  1729.         self._node.clear()
  1730.         self.graph.clear()
  1731.  
  1732.     def is_multigraph(self):
  1733.         """Returns True if graph is a multigraph, False otherwise."""
  1734.         return False
  1735.  
  1736.     def is_directed(self):
  1737.         """Returns True if graph is directed, False otherwise."""
  1738.         return True
  1739.  
  1740.     def to_undirected(self, reciprocal=False, as_view=False):
  1741.         graph_class = self.to_undirected_class()
  1742.         if as_view is True:
  1743.             return nx.graphviews.generic_graph_view(self, Graph)
  1744.         # deepcopy when not a view
  1745.         G = Graph()
  1746.         G.graph.update(deepcopy(self.graph))
  1747.         G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
  1748.         if reciprocal is True:
  1749.             G.add_edges_from((u, v, deepcopy(d))
  1750.                              for u, nbrs in self._adj.items()
  1751.                              for v, d in nbrs.items()
  1752.                              if v in self._pred[u])
  1753.         else:
  1754.             G.add_edges_from((u, v, deepcopy(d))
  1755.                              for u, nbrs in self._adj.items()
  1756.                              for v, d in nbrs.items())
  1757.         return G
  1758.  
  1759.     def reverse(self, copy=True):
  1760.         """Returns the reverse of the graph.
  1761.        The reverse is a graph with the same nodes and edges
  1762.        but with the directions of the edges reversed.
  1763.        Parameters
  1764.        ----------
  1765.        copy : bool optional (default=True)
  1766.            If True, return a new DiGraph holding the reversed edges.
  1767.            If False, the reverse graph is created using a view of
  1768.            the original graph.
  1769.        """
  1770.         if copy:
  1771.             H = self.__class__()
  1772.             H.graph.update(deepcopy(self.graph))
  1773.             H.add_nodes_from((n, deepcopy(d)) for n, d in self.nodes.items())
  1774.             H.add_edges_from((v, u, deepcopy(d)) for u, v, d
  1775.                              in self.edges(data=True))
  1776.             return H
  1777.         return nx.graphviews.reverse_view(self)
  1778.  
  1779.  
  1780. def check_planarity(G, counterexample=False):
  1781.     planarity_state = LRPlanarity(G)
  1782.     embedding = planarity_state.lr_planarity()
  1783.     if embedding is None:
  1784.         # graph is not planar
  1785.         if counterexample:
  1786.             return False, get_counterexample(G)
  1787.         else:
  1788.             return False, None
  1789.     else:
  1790.         # graph is planar
  1791.         return True, embedding
  1792.  
  1793.  
  1794. def get_counterexample(G):
  1795.     # copy graph
  1796.     G = nx.Graph(G)
  1797.  
  1798.     if check_planarity(G)[0]:
  1799.         raise nx.NetworkXException("G is planar - no counter example.")
  1800.  
  1801.     # find Kuratowski subgraph
  1802.     subgraph = nx.Graph()
  1803.     for u in G:
  1804.         nbrs = list(G[u])
  1805.         for v in nbrs:
  1806.             G.remove_edge(u, v)
  1807.             if check_planarity(G)[0]:
  1808.                 G.add_edge(u, v)
  1809.                 subgraph.add_edge(u, v)
  1810.  
  1811.     return subgraph
  1812.  
  1813.  
  1814. class Interval(object):
  1815.  
  1816.     def __init__(self, low=None, high=None):
  1817.         self.low = low
  1818.         self.high = high
  1819.  
  1820.     def empty(self):
  1821.         """Check if the interval is empty"""
  1822.         return self.low is None and self.high is None
  1823.  
  1824.     def copy(self):
  1825.         """Returns a copy of this interval"""
  1826.         return Interval(self.low, self.high)
  1827.  
  1828.     def conflicting(self, b, planarity_state):
  1829.         """Returns True if interval I conflicts with edge b"""
  1830.         return (not self.empty() and
  1831.                 planarity_state.lowpt[self.high] > planarity_state.lowpt[b])
  1832.  
  1833.  
  1834. class ConflictPair(object):
  1835.     """Represents a different constraint between two intervals.
  1836.  
  1837.    The edges in the left interval must have a different orientation than
  1838.    the one in the right interval.
  1839.    """
  1840.  
  1841.     def __init__(self, left=Interval(), right=Interval()):
  1842.         self.left = left
  1843.         self.right = right
  1844.  
  1845.     def swap(self):
  1846.         """Swap left and right intervals"""
  1847.         temp = self.left
  1848.         self.left = self.right
  1849.         self.right = temp
  1850.  
  1851.     def lowest(self, planarity_state):
  1852.         """Returns the lowest lowpoint of a conflict pair"""
  1853.         if self.left.empty():
  1854.             return planarity_state.lowpt[self.right.low]
  1855.         if self.right.empty():
  1856.             return planarity_state.lowpt[self.left.low]
  1857.         return min(planarity_state.lowpt[self.left.low],
  1858.                    planarity_state.lowpt[self.right.low])
  1859.  
  1860.  
  1861. def top_of_stack(l):
  1862.     """Returns the element on top of the stack."""
  1863.     if not l:
  1864.         return None
  1865.     return l[-1]
  1866.  
  1867.  
  1868. class LRPlanarity(object):
  1869.     """A class to maintain the state during planarity check."""
  1870.     __slots__ = [
  1871.         'G', 'roots', 'height', 'lowpt', 'lowpt2', 'nesting_depth',
  1872.         'parent_edge', 'DG', 'adjs', 'ordered_adjs', 'ref', 'side', 'S',
  1873.         'stack_bottom', 'lowpt_edge', 'left_ref', 'right_ref', 'embedding'
  1874.     ]
  1875.  
  1876.     def __init__(self, G):
  1877.         # copy G without adding self-loops
  1878.         self.G = Graph()
  1879.         self.G.add_nodes_from(G.nodes)
  1880.         for e in G.edges:
  1881.             if e[0] != e[1]:
  1882.                 self.G.add_edge(e[0], e[1])
  1883.  
  1884.         self.roots = []
  1885.  
  1886.         # distance from tree root
  1887.         self.height = defaultdict(lambda: None)
  1888.  
  1889.         self.lowpt = {}  # height of lowest return point of an edge
  1890.         self.lowpt2 = {}  # height of second lowest return point
  1891.         self.nesting_depth = {}  # for nesting order
  1892.  
  1893.         # None -> missing edge
  1894.         self.parent_edge = defaultdict(lambda: None)
  1895.  
  1896.         # oriented DFS graph
  1897.         self.DG = DiGraph()
  1898.         self.DG.add_nodes_from(G.nodes)
  1899.  
  1900.         self.adjs = {}
  1901.         self.ordered_adjs = {}
  1902.  
  1903.         self.ref = defaultdict(lambda: None)
  1904.         self.side = defaultdict(lambda: 1)
  1905.  
  1906.         # stack of conflict pairs
  1907.         self.S = []
  1908.         self.stack_bottom = {}
  1909.         self.lowpt_edge = {}
  1910.  
  1911.         self.left_ref = {}
  1912.         self.right_ref = {}
  1913.  
  1914.         self.embedding = PlanarEmbedding()
  1915.  
  1916.     def lr_planarity(self):
  1917.         """Execute the LR planarity test.
  1918.  
  1919.        Returns
  1920.        -------
  1921.        embedding : dict
  1922.            If the graph is planar an embedding is returned. Otherwise None.
  1923.        """
  1924.         if self.G.order() > 2 and self.G.size() > 3 * self.G.order() - 6:
  1925.             # graph is not planar
  1926.             return None
  1927.  
  1928.         # make adjacency lists for dfs
  1929.         for v in self.G:
  1930.             self.adjs[v] = list(self.G[v])
  1931.  
  1932.         # orientation of the graph by depth first search traversal
  1933.         for v in self.G:
  1934.             if self.height[v] is None:
  1935.                 self.height[v] = 0
  1936.                 self.roots.append(v)
  1937.                 self.dfs_orientation(v)
  1938.  
  1939.         # Free no longer used variables
  1940.         self.G = None
  1941.         self.lowpt2 = None
  1942.         self.adjs = None
  1943.  
  1944.         # testing
  1945.         for v in self.DG:  # sort the adjacency lists by nesting depth
  1946.             # note: this sorting leads to non linear time
  1947.             self.ordered_adjs[v] = sorted(
  1948.                 self.DG[v], key=lambda x: self.nesting_depth[(v, x)])
  1949.         for v in self.roots:
  1950.             if not self.dfs_testing(v):
  1951.                 return None
  1952.  
  1953.         # Free no longer used variables
  1954.         self.height = None
  1955.         self.lowpt = None
  1956.         self.S = None
  1957.         self.stack_bottom = None
  1958.         self.lowpt_edge = None
  1959.  
  1960.         for e in self.DG.edges:
  1961.             self.nesting_depth[e] = self.sign(e) * self.nesting_depth[e]
  1962.  
  1963.         self.embedding.add_nodes_from(self.DG.nodes)
  1964.         for v in self.DG:
  1965.             # sort the adjacency lists again
  1966.             self.ordered_adjs[v] = sorted(
  1967.                 self.DG[v], key=lambda x: self.nesting_depth[(v, x)])
  1968.             # initialize the embedding
  1969.             previous_node = None
  1970.             for w in self.ordered_adjs[v]:
  1971.                 self.embedding.add_half_edge_cw(v, w, previous_node)
  1972.                 previous_node = w
  1973.  
  1974.         # Free no longer used variables
  1975.         self.DG = None
  1976.         self.nesting_depth = None
  1977.         self.ref = None
  1978.  
  1979.         # compute the complete embedding
  1980.         for v in self.roots:
  1981.             self.dfs_embedding(v)
  1982.  
  1983.         # Free no longer used variables
  1984.         self.roots = None
  1985.         self.parent_edge = None
  1986.         self.ordered_adjs = None
  1987.         self.left_ref = None
  1988.         self.right_ref = None
  1989.         self.side = None
  1990.  
  1991.         return self.embedding
  1992.  
  1993.     def dfs_orientation(self, v):
  1994.         """Orient the graph by DFS, compute lowpoints and nesting order.
  1995.        """
  1996.         # the recursion stack
  1997.         dfs_stack = [v]
  1998.         # index of next edge to handle in adjacency list of each node
  1999.         ind = defaultdict(lambda: 0)
  2000.         # boolean to indicate whether to skip the initial work for an edge
  2001.         skip_init = defaultdict(lambda: False)
  2002.  
  2003.         while dfs_stack:
  2004.             v = dfs_stack.pop()
  2005.             e = self.parent_edge[v]
  2006.  
  2007.             for w in self.adjs[v][ind[v]:]:
  2008.                 vw = (v, w)
  2009.  
  2010.                 if not skip_init[vw]:
  2011.                     if (v, w) in self.DG.edges or (w, v) in self.DG.edges:
  2012.                         ind[v] += 1
  2013.                         continue  # the edge was already oriented
  2014.  
  2015.                     self.DG.add_edge(v, w)  # orient the edge
  2016.  
  2017.                     self.lowpt[vw] = self.height[v]
  2018.                     self.lowpt2[vw] = self.height[v]
  2019.                     if self.height[w] is None:  # (v, w) is a tree edge
  2020.                         self.parent_edge[w] = vw
  2021.                         self.height[w] = self.height[v] + 1
  2022.  
  2023.                         dfs_stack.append(v)  # revisit v after finishing w
  2024.                         dfs_stack.append(w)  # visit w next
  2025.                         skip_init[vw] = True  # don't redo this block
  2026.                         break  # handle next node in dfs_stack (i.e. w)
  2027.                     else:  # (v, w) is a back edge
  2028.                         self.lowpt[vw] = self.height[w]
  2029.  
  2030.                 # determine nesting graph
  2031.                 self.nesting_depth[vw] = 2 * self.lowpt[vw]
  2032.                 if self.lowpt2[vw] < self.height[v]:  # chordal
  2033.                     self.nesting_depth[vw] += 1
  2034.  
  2035.                 # update lowpoints of parent edge e
  2036.                 if e is not None:
  2037.                     if self.lowpt[vw] < self.lowpt[e]:
  2038.                         self.lowpt2[e] = min(self.lowpt[e], self.lowpt2[vw])
  2039.                         self.lowpt[e] = self.lowpt[vw]
  2040.                     elif self.lowpt[vw] > self.lowpt[e]:
  2041.                         self.lowpt2[e] = min(self.lowpt2[e], self.lowpt[vw])
  2042.                     else:
  2043.                         self.lowpt2[e] = min(self.lowpt2[e], self.lowpt2[vw])
  2044.  
  2045.                 ind[v] += 1
  2046.  
  2047.     def dfs_testing(self, v):
  2048.         """Test for LR partition."""
  2049.         # the recursion stack
  2050.         dfs_stack = [v]
  2051.         # index of next edge to handle in adjacency list of each node
  2052.         ind = defaultdict(lambda: 0)
  2053.         # boolean to indicate whether to skip the initial work for an edge
  2054.         skip_init = defaultdict(lambda: False)
  2055.  
  2056.         while dfs_stack:
  2057.             v = dfs_stack.pop()
  2058.             e = self.parent_edge[v]
  2059.             # to indicate whether to skip the final block after the for loop
  2060.             skip_final = False
  2061.  
  2062.             for w in self.ordered_adjs[v][ind[v]:]:
  2063.                 ei = (v, w)
  2064.  
  2065.                 if not skip_init[ei]:
  2066.                     self.stack_bottom[ei] = top_of_stack(self.S)
  2067.  
  2068.                     if ei == self.parent_edge[w]:  # tree edge
  2069.                         dfs_stack.append(v)  # revisit v after finishing w
  2070.                         dfs_stack.append(w)  # visit w next
  2071.                         skip_init[ei] = True  # don't redo this block
  2072.                         skip_final = True  # skip final work after breaking
  2073.                         break  # handle next node in dfs_stack (i.e. w)
  2074.                     else:  # back edge
  2075.                         self.lowpt_edge[ei] = ei
  2076.                         self.S.append(ConflictPair(right=Interval(ei, ei)))
  2077.  
  2078.                 # integrate new return edges
  2079.                 if self.lowpt[ei] < self.height[v]:
  2080.                     if w == self.ordered_adjs[v][0]:  # e_i has return edge
  2081.                         self.lowpt_edge[e] = self.lowpt_edge[ei]
  2082.                     else:  # add constraints of e_i
  2083.                         if not self.add_constraints(ei, e):
  2084.                             # graph is not planar
  2085.                             return False
  2086.  
  2087.                 ind[v] += 1
  2088.  
  2089.             if not skip_final:
  2090.                 # remove back edges returning to parent
  2091.                 if e is not None:  # v isn't root
  2092.                     self.remove_back_edges(e)
  2093.  
  2094.         return True
  2095.  
  2096.     def add_constraints(self, ei, e):
  2097.         P = ConflictPair()
  2098.         # merge return edges of e_i into P.right
  2099.         while True:
  2100.             Q = self.S.pop()
  2101.             if not Q.left.empty():
  2102.                 Q.swap()
  2103.             if not Q.left.empty():  # not planar
  2104.                 return False
  2105.             if self.lowpt[Q.right.low] > self.lowpt[e]:
  2106.                 # merge intervals
  2107.                 if P.right.empty():  # topmost interval
  2108.                     P.right = Q.right.copy()
  2109.                 else:
  2110.                     self.ref[P.right.low] = Q.right.high
  2111.                 P.right.low = Q.right.low
  2112.             else:  # align
  2113.                 self.ref[Q.right.low] = self.lowpt_edge[e]
  2114.             if top_of_stack(self.S) == self.stack_bottom[ei]:
  2115.                 break
  2116.         # merge conflicting return edges of e_1,...,e_i-1 into P.L
  2117.         while (top_of_stack(self.S).left.conflicting(ei, self) or
  2118.                top_of_stack(self.S).right.conflicting(ei, self)):
  2119.             Q = self.S.pop()
  2120.             if Q.right.conflicting(ei, self):
  2121.                 Q.swap()
  2122.             if Q.right.conflicting(ei, self):  # not planar
  2123.                 return False
  2124.             # merge interval below lowpt(e_i) into P.R
  2125.             self.ref[P.right.low] = Q.right.high
  2126.             if Q.right.low is not None:
  2127.                 P.right.low = Q.right.low
  2128.  
  2129.             if P.left.empty():  # topmost interval
  2130.                 P.left = Q.left.copy()
  2131.             else:
  2132.                 self.ref[P.left.low] = Q.left.high
  2133.             P.left.low = Q.left.low
  2134.  
  2135.         if not (P.left.empty() and P.right.empty()):
  2136.             self.S.append(P)
  2137.         return True
  2138.  
  2139.     def remove_back_edges(self, e):
  2140.         u = e[0]
  2141.         # trim back edges ending at parent u
  2142.         # drop entire conflict pairs
  2143.         while self.S and top_of_stack(self.S).lowest(self) == self.height[u]:
  2144.             P = self.S.pop()
  2145.             if P.left.low is not None:
  2146.                 self.side[P.left.low] = -1
  2147.  
  2148.         if self.S:  # one more conflict pair to consider
  2149.             P = self.S.pop()
  2150.             # trim left interval
  2151.             while P.left.high is not None and P.left.high[1] == u:
  2152.                 P.left.high = self.ref[P.left.high]
  2153.             if P.left.high is None and P.left.low is not None:
  2154.                 # just emptied
  2155.                 self.ref[P.left.low] = P.right.low
  2156.                 self.side[P.left.low] = -1
  2157.                 P.left.low = None
  2158.             # trim right interval
  2159.             while P.right.high is not None and P.right.high[1] == u:
  2160.                 P.right.high = self.ref[P.right.high]
  2161.             if P.right.high is None and P.right.low is not None:
  2162.                 # just emptied
  2163.                 self.ref[P.right.low] = P.left.low
  2164.                 self.side[P.right.low] = -1
  2165.                 P.right.low = None
  2166.             self.S.append(P)
  2167.  
  2168.         # side of e is side of a highest return edge
  2169.         if self.lowpt[e] < self.height[u]:  # e has return edge
  2170.             hl = top_of_stack(self.S).left.high
  2171.             hr = top_of_stack(self.S).right.high
  2172.  
  2173.             if hl is not None and (
  2174.                 hr is None or self.lowpt[hl] > self.lowpt[hr]):
  2175.                 self.ref[e] = hl
  2176.             else:
  2177.                 self.ref[e] = hr
  2178.  
  2179.     def dfs_embedding(self, v):
  2180.         """Completes the embedding."""
  2181.         # the recursion stack
  2182.         dfs_stack = [v]
  2183.         # index of next edge to handle in adjacency list of each node
  2184.         ind = defaultdict(lambda: 0)
  2185.  
  2186.         while dfs_stack:
  2187.             v = dfs_stack.pop()
  2188.  
  2189.             for w in self.ordered_adjs[v][ind[v]:]:
  2190.                 ind[v] += 1
  2191.                 ei = (v, w)
  2192.  
  2193.                 if ei == self.parent_edge[w]:  # tree edge
  2194.                     self.embedding.add_half_edge_first(w, v)
  2195.                     self.left_ref[v] = w
  2196.                     self.right_ref[v] = w
  2197.  
  2198.                     dfs_stack.append(v)  # revisit v after finishing w
  2199.                     dfs_stack.append(w)  # visit w next
  2200.                     break  # handle next node in dfs_stack (i.e. w)
  2201.                 else:  # back edge
  2202.                     if self.side[ei] == 1:
  2203.                         self.embedding.add_half_edge_cw(w, v,
  2204.                                                         self.right_ref[w])
  2205.                     else:
  2206.                         self.embedding.add_half_edge_ccw(w, v,
  2207.                                                          self.left_ref[w])
  2208.                         self.left_ref[w] = v
  2209.  
  2210.     def sign(self, e):
  2211.         """Resolve the relative side of an edge to the absolute side."""
  2212.         # the recursion stack
  2213.         dfs_stack = [e]
  2214.         # dict to remember reference edges
  2215.         old_ref = defaultdict(lambda: None)
  2216.  
  2217.         while dfs_stack:
  2218.             e = dfs_stack.pop()
  2219.  
  2220.             if self.ref[e] is not None:
  2221.                 dfs_stack.append(e)  # revisit e after finishing self.ref[e]
  2222.                 dfs_stack.append(self.ref[e])  # visit self.ref[e] next
  2223.                 old_ref[e] = self.ref[e]  # remember value of self.ref[e]
  2224.                 self.ref[e] = None
  2225.             else:
  2226.                 self.side[e] *= self.side[old_ref[e]]
  2227.  
  2228.         return self.side[e]
  2229.  
  2230.  
  2231. class PlanarEmbedding(DiGraph):
  2232.  
  2233.     def get_data(self):
  2234.         embedding = dict()
  2235.         for v in self:
  2236.             embedding[v] = list(self.neighbors_cw_order(v))
  2237.         return embedding
  2238.  
  2239.     def set_data(self, data):
  2240.         for v in data:
  2241.             for w in reversed(data[v]):
  2242.                 self.add_half_edge_first(v, w)
  2243.  
  2244.     def neighbors_cw_order(self, v):
  2245.         if len(self[v]) == 0:
  2246.             # v has no neighbors
  2247.             return
  2248.         start_node = self.nodes[v]['first_nbr']
  2249.         yield start_node
  2250.         current_node = self[v][start_node]['cw']
  2251.         while start_node != current_node:
  2252.             yield current_node
  2253.             current_node = self[v][current_node]['cw']
  2254.  
  2255.     def check_structure(self):
  2256.         for v in self:
  2257.             try:
  2258.                 sorted_nbrs = set(self.neighbors_cw_order(v))
  2259.             except KeyError:
  2260.                 msg = "Bad embedding. " \
  2261.                       "Missing orientation for a neighbor of {}".format(v)
  2262.                 raise nx.NetworkXException(msg)
  2263.  
  2264.             unsorted_nbrs = set(self[v])
  2265.             if sorted_nbrs != unsorted_nbrs:
  2266.                 msg = "Bad embedding. Edge orientations not set correctly."
  2267.                 raise nx.NetworkXException(msg)
  2268.             for w in self[v]:
  2269.                 # Check if opposite half-edge exists
  2270.                 if not self.has_edge(w, v):
  2271.                     msg = "Bad embedding. Opposite half-edge is missing."
  2272.                     raise nx.NetworkXException(msg)
  2273.  
  2274.         # Check planarity
  2275.         counted_half_edges = set()
  2276.         for component in nx.connected_components(self):
  2277.             if len(component) == 1:
  2278.                 # Don't need to check single node component
  2279.                 continue
  2280.             num_nodes = len(component)
  2281.             num_half_edges = 0
  2282.             num_faces = 0
  2283.             for v in component:
  2284.                 for w in self.neighbors_cw_order(v):
  2285.                     num_half_edges += 1
  2286.                     if (v, w) not in counted_half_edges:
  2287.                         # We encountered a new face
  2288.                         num_faces += 1
  2289.                         # Mark all half-edges belonging to this face
  2290.                         self.traverse_face(v, w, counted_half_edges)
  2291.             num_edges = num_half_edges // 2  # num_half_edges is even
  2292.             if num_nodes - num_edges + num_faces != 2:
  2293.                 # The result does not match Euler's formula
  2294.                 msg = "Bad embedding. The graph does not match Euler's formula"
  2295.                 raise nx.NetworkXException(msg)
  2296.  
  2297.     def add_half_edge_ccw(self, start_node, end_node, reference_neighbor):
  2298.         if reference_neighbor is None:
  2299.             # The start node has no neighbors
  2300.             self.add_edge(start_node, end_node)  # Add edge to graph
  2301.             self[start_node][end_node]['cw'] = end_node
  2302.             self[start_node][end_node]['ccw'] = end_node
  2303.             self.nodes[start_node]['first_nbr'] = end_node
  2304.         else:
  2305.             ccw_reference = self[start_node][reference_neighbor]['ccw']
  2306.             self.add_half_edge_cw(start_node, end_node, ccw_reference)
  2307.  
  2308.             if reference_neighbor == self.nodes[start_node].get('first_nbr',
  2309.                                                                 None):
  2310.                 # Update first neighbor
  2311.                 self.nodes[start_node]['first_nbr'] = end_node
  2312.  
  2313.     def add_half_edge_cw(self, start_node, end_node, reference_neighbor):
  2314.         self.add_edge(start_node, end_node)  # Add edge to graph
  2315.  
  2316.         if reference_neighbor is None:
  2317.             # The start node has no neighbors
  2318.             self[start_node][end_node]['cw'] = end_node
  2319.             self[start_node][end_node]['ccw'] = end_node
  2320.             self.nodes[start_node]['first_nbr'] = end_node
  2321.             return
  2322.  
  2323.         if reference_neighbor not in self[start_node]:
  2324.             raise nx.NetworkXException(
  2325.                 "Cannot add edge. Reference neighbor does not exist")
  2326.  
  2327.         # Get half-edge at the other side
  2328.         cw_reference = self[start_node][reference_neighbor]['cw']
  2329.         # Alter half-edge data structures
  2330.         self[start_node][reference_neighbor]['cw'] = end_node
  2331.         self[start_node][end_node]['cw'] = cw_reference
  2332.         self[start_node][cw_reference]['ccw'] = end_node
  2333.         self[start_node][end_node]['ccw'] = reference_neighbor
  2334.  
  2335.     def connect_components(self, v, w):
  2336.         self.add_half_edge_first(v, w)
  2337.         self.add_half_edge_first(w, v)
  2338.  
  2339.     def add_half_edge_first(self, start_node, end_node):
  2340.         if start_node in self and 'first_nbr' in self.nodes[start_node]:
  2341.             reference = self.nodes[start_node]['first_nbr']
  2342.         else:
  2343.             reference = None
  2344.         self.add_half_edge_ccw(start_node, end_node, reference)
  2345.  
  2346.     def next_face_half_edge(self, v, w):
  2347.         new_node = self[w][v]['ccw']
  2348.         return w, new_node
  2349.  
  2350.     def traverse_face(self, v, w, mark_half_edges=None):
  2351.  
  2352.         if mark_half_edges is None:
  2353.             mark_half_edges = set()
  2354.  
  2355.         face_nodes = [v]
  2356.         mark_half_edges.add((v, w))
  2357.         prev_node = v
  2358.         cur_node = w
  2359.         # Last half-edge is (incoming_node, v)
  2360.         incoming_node = self[v][w]['cw']
  2361.  
  2362.         while cur_node != v or prev_node != incoming_node:
  2363.             face_nodes.append(cur_node)
  2364.             prev_node, cur_node = self.next_face_half_edge(prev_node, cur_node)
  2365.             if (prev_node, cur_node) in mark_half_edges:
  2366.                 raise nx.NetworkXException(
  2367.                     "Bad planar embedding. Impossible face.")
  2368.             mark_half_edges.add((prev_node, cur_node))
  2369.  
  2370.         return face_nodes
  2371.  
  2372.     def is_directed(self):
  2373.         return False
  2374.  
  2375.  
  2376. def solve(left_vertex, right_vertex, len):
  2377.     if len == 0:
  2378.         return True
  2379.     graph = Graph()
  2380.     for i in range(5 + len):
  2381.         graph.add_node(i)
  2382.     for i in range(4):
  2383.         graph.add_edge(0, i + 1)
  2384.         graph.add_edge(i + 1, (i + 1) % 4 + 1)
  2385.     for i in range(len):
  2386.         u = left_vertex[i]
  2387.         v = right_vertex[i]
  2388.         graph.add_edge(i + 5, u)
  2389.         graph.add_edge(i + 5, v)
  2390.     return check_planarity(graph)
  2391.  
  2392.  
  2393. def main():
  2394.     input_file = open("input.txt", "r")
  2395.     test = int(input_file.readline().strip())
  2396.     while test:
  2397.         n = int(input_file.readline().strip())
  2398.         left_vertex = []
  2399.         right_vertex = []
  2400.         for i in range(n):
  2401.             tokens = input_file.readline().strip().split()
  2402.             left_vertex.append(int(tokens[0]))
  2403.             right_vertex.append(int(tokens[1]))
  2404.         left = 0
  2405.         right = n
  2406.         # result, emb = solve(left_vertex, right_vertex, n)
  2407.         # print (result)
  2408.         # exit(0)
  2409.         while right - left > 1:
  2410.             mid = (left + right) // 2
  2411.             if solve(left_vertex, right_vertex, mid)[0]:
  2412.                 left = mid
  2413.             else:
  2414.                 right = mid
  2415.         answer = left
  2416.         if solve(left_vertex, right_vertex, right)[0]:
  2417.             answer = right
  2418.         print(answer)
  2419.         test -= 1
  2420.  
  2421.  
  2422. if __name__ == "__main__":
  2423.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement