File: connectivity.rb

package info (click to toggle)
ruby-rgfa 1.3.1%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 820 kB
  • sloc: ruby: 5,649; makefile: 9
file content (131 lines) | stat: -rw-r--r-- 4,002 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#
# Methods which analyse the connectivity of the graph.
#
module RGFA::Connectivity

  require "set"

  # Computes the connectivity of a segment from its number of links.
  #
  # @param segment [String|RGFA::Line::Segment] segment name or instance
  #
  # @return [Array<conn_symbol,conn_symbol>]
  #  conn. symbols respectively of the :B and :E ends of +segment+.
  #
  # <b>Connectivity symbol:</b> (+conn_symbol+)
  # - Let _n_ be the number of links to an end (+:B+ or +:E+) of a segment.
  #   Then the connectivity symbol is +:M+ if <i>n > 1</i>, otherwise _n_.
  #
  def connectivity(segment)
    connectivity_symbols(links_of([segment, :B]).size,
                         links_of([segment, :E]).size)
  end

  # Does the removal of the link alone divide a component
  # of the graph into two?
  # @return [Boolean]
  # @param link [RGFA::Line::Link] a link
  def cut_link?(link)
    return false if link.circular?
    return true if links_of(link.from_end.invert_end_type).size == 0
    return true if links_of(link.to_end.invert_end_type).size == 0
    c = {}
    [:from, :to].each do |et|
      c[et] = Set.new
      visited = Set.new
      segend = link.send(:"#{et}_end")
      visited << segend.name
      visited << link.other_end(segend).name
      traverse_component(segend, c[et], visited)
    end
    return c[:from] != c[:to]
  end

  # Does the removal of the segment and its links divide a
  # component of the graph into two?
  # @param segment [String, RGFA::Line::Segment] a segment name or instance
  # @return [Boolean]
  def cut_segment?(segment)
    segment_name = segment.kind_of?(RGFA::Line) ? segment.name : segment
    cn = connectivity(segment_name)
    return false if [[0,0],[0,1],[1,0]].include?(cn)
    start_points = []
    [:B, :E].each do |et|
      start_points += links_of([segment_name, et]).map do |l|
        l.other_end([segment_name, et]).invert_end_type
      end
    end
    cc = []
    start_points.uniq.each do |start_point|
      cc << Set.new
      visited = Set.new
      visited << segment_name
      traverse_component(start_point, cc.last, visited)
    end
    return cc.any?{|c|c != cc[0]}
  end

  # Find the connected component of the graph in which a segment is included
  # @return [Array<String>]
  #   array of segment names
  # @param segment [String, RGFA::Line::Segment] a segment name or instance
  # @param visited [Set<String>] a set of segments to ignore during graph
  #   traversal; all segments in the found component will be added to it
  def segment_connected_component(segment, visited = Set.new)
    segment_name = segment.kind_of?(RGFA::Line) ? segment.name : segment
    visited << segment_name
    c = [segment_name]
    traverse_component([segment_name, :B], c, visited)
    traverse_component([segment_name, :E], c, visited)
    return c
  end

  # Find the connected components of the graph
  # @return [Array<Array<String>>]
  #   array of components, each an array of segment names
  def connected_components
    components = []
    visited = Set.new
    segment_names.each do |sn|
      next if visited.include?(sn)
      components << segment_connected_component(sn, visited)
    end
    return components
  end

  # Split connected components of the graph into single-component RGFAs
  # @return [Array<RGFA>]
  def split_connected_components
    retval = []
    ccs = connected_components
    ccs.each do |cc|
      gfa2 = self.clone
      gfa2.rm(gfa2.segment_names - cc)
      retval << gfa2
    end
    return retval
  end

  private

  def traverse_component(segment_end, c, visited)
    links_of(segment_end).each do |l|
      oe = l.other_end(segment_end)
      sn = oe.name
      next if visited.include?(sn)
      visited << sn
      c << sn
      traverse_component([sn, :B], c, visited)
      traverse_component([sn, :E], c, visited)
    end
  end

  def connectivity_symbols(n,m)
    [connectivity_symbol(n), connectivity_symbol(m)]
  end

  def connectivity_symbol(n)
    n > 1 ? :M : n
  end

end