# Brief intro to Graphs slides: https://hackmd.io/@multiscan/st-luc --- take home message ## Do not reinvent the wheel Problems that looks very **different** become very **similar** once represented with the same _language_. Many problems can be represented as graph solved using standard algorithms! Find the graph library for your language (e.g. python [networkx](https://networkx.org/), ruby [rgl](https://github.com/monora/rgl), javascript [cytoscape](https://js.cytoscape.org/)...). --- ## Outline * Definitions * Usefull algorithms * Examples / Hands on --- ## What is a <span class="red">graph</span> ? * A way to represent **relationships** between data like - **membership** (_belongs to_, _derives from_), - **causalities** (dependencies), - **connections** (networks); * Nothing to do with <strong class="red">graph</strong>ics (although graphical representation helps thinking); --- ## Taxonomy :-1: quite a lot of nomenclature * Vertices and Edges * Directed vs Undirected graphs * Vertex degree, sources and sinks * Complete, complement, transpose graph * Connectivity, k-connectivity --- So what is a graph ? --- ### Vertices / Nodes <div class='cont'> <div class='col'> ```graphviz graph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=circle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato uno [label = 1, pos="1,2!"] due [label = 2, pos="0,1!"] tre [label = 3, pos="1,0!"] qua [label = 4, pos="1,1!"] cin [label = 5, pos="0,0!"] } ``` &nbsp; </div> <div class='col'> ```graphviz digraph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=circle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato uno [label = 1, pos="1,2!"] due [label = 2, pos="0,1!"] tre [label = 3, pos="1,0!"] qua [label = 4, pos="1,1!"] cin [label = 5, pos="0,0!"] } ``` &nbsp; </div> </div> --- ### Add edges to make a graph <div class='cont'> <div class='col'> ```graphviz graph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=circle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato uno [label = 1, pos="1,2!"] due [label = 2, pos="0,1!"] tre [label = 3, pos="1,0!"] qua [label = 4, pos="1,1!"] cin [label = 5, pos="0,0!"] uno -- due uno -- qua tre -- qua qua -- tre tre -- due } ``` Undirected </div> <div class='col'> ```graphviz digraph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=circle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato uno [label = 1, pos="1,2!"] due [label = 2, pos="0,1!"] tre [label = 3, pos="1,0!"] qua [label = 4, pos="1,1!"] cin [label = 5, pos="0,0!"] uno -> due uno -> qua tre -> qua qua -> tre tre -> due } ``` Directed </div> </div> --- ### Edge Weights ```graphviz neato graph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" margin="0.001,0.001" fixedsize=false fontsize=10 shape=rectangle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=orange color=white fontsize=12] scale=0.7 layout=neato ve [label="Venezia" pos="11,4!"] vi [label="Vicenza" pos="7,6!"] vr [label="Verona" pos="5,5!"] pd [label="Padova" pos="9,5!"] tv [label="Treviso" pos="10,7!"] bo [label="Bologna" pos="7,3!"] mi [label="Milano" pos="2,5!"] fi [label="Firenze" pos="4,0!"] pi [label="Pisa" pos="2,0!"] ge [label="Genova" pos="1,3!"] tv -- ve [weight=25,label="25"] tv -- vi [weight=60,label="60"] ve -- pd [weight=37,label="37"] pd -- vi [weight=30,label="30"] vi -- vr [weight=52,label="52"] mi -- vr [weight=148,label="148"] ge -- mi [weight=154,label="154"] pd -- bo [weight=123,label="123"] bo -- fi [weight=97,label="97"] pi -- ge [weight=162,label="162"] fi -- pi [weight=88,label="88"] mi -- bo [weight=216,label="216"] } ``` --- ### Connectivity (undirected) ```graphviz neato graph G { fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontsize=10 style=filled shape=circle] edge [fontname="Helvetica,Arial,sans-serif" color=white fontsize=10 penwidth=3.0] bgcolor=black layout=neato a [fillcolor="#98FB98"] b [fillcolor="#98FB98"] c [fillcolor="#FFA500"] d [fillcolor="#98FB98"] e [fillcolor="#98FB98"] f [fillcolor="#FFA500"] g [fillcolor="#98FB98"] h [fillcolor=cyan] i [fillcolor="#FFA500"] j [fillcolor="#98FB98"] k [fillcolor="#FFA500"] l [fillcolor="#98FB98"] m [fillcolor="#98FB98"] a -- b a -- c b -- c b -- d c -- d c -- f [color=red] f -- e f -- g f -- i f -- j f -- h [color=red] e -- g i -- j i -- k [color=red] k -- l k -- m l -- m } ``` * <span style="color: #FFA500">Articulation points</span> * <span style="color: red">Bridges</span> * <span style="color: cyan">Exterior vertex</span> * Biconnected components --- ### Connectivity (directed) ```graphviz neato digraph G { fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontsize=10 style=filled shape=circle] edge [fontname="Helvetica,Arial,sans-serif" color=white fontsize=10 penwidth=2.0] bgcolor=black layout=neato a [fillcolor=cyan] b [fillcolor=cyan] c [fillcolor=cyan] d [fillcolor=cyan] e [fillcolor=orange] f [fillcolor=orange] g [fillcolor=pink] h [fillcolor=pink] i [fillcolor=pink] abcd[fillcolor=cyan shape=rectangle] ef[fillcolor=orange shape=rectangle] ghi[fillcolor=pink shape=rectangle] a -> b a -> c b -> c d -> a c -> d c -> e e -> f f -> e d -> g g -> h h -> i i -> g abcd -> ef ef -> ghi abcd -> ghi } ``` Strongly connected components --- ### Vertex degree ```graphviz! digraph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato cin [style=invis, pos="1,0!"] uno [label="vertex", pos="1,1!"] due [style=invis, pos="0,2!"] tre [style=invis, pos="1,2!"] qua [style=invis, pos="2,2!"] uno -> cin due -> uno tre -> uno qua -> uno } ``` ```graphviz! graph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato cin [style=invis, pos="1,0!"] uno [label="vertex", pos="1,1!"] due [style=invis, pos="0,2!"] tre [style=invis, pos="1,2!"] qua [style=invis, pos="2,2!"] uno -- cin due -- uno tre -- uno qua -- uno } ``` directed: in edges - out edges (3-1=2) undirected: edges count (4) --- ### Short <span class="history">historical</span> digression --- #### The seven bridges of <span class="history">Königsberg</span> (Euler, 1730) <div class=cont> <div class="col"> ![Konigsberg bridges](https://i.imgur.com/sMZeIJI.jpg) </div> <div class="col"> &nbsp; ```graphviz! graph G { fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontsize=9 style=filled shape=circle] edge [fontname="Helvetica,Arial,sans-serif" color=white fontsize=10] bgcolor=black layout=neato R [label="R / 5" fillcolor=red fontcolor=white pos="0,1!"] Y [label="Y / 3" fillcolor=yellow fontcolor=black pos="1,2!"] B [label="B / 3" fillcolor=blue fontcolor=white pos="2,1!"] G [label="G / 3" fillcolor=green fontcolor=black pos="1,0!"] Y -- R Y -- R Y -- B R -- B R -- G R -- G B -- G } ``` </div> </div> --- #### Euler path traverse all the edges only once &nbsp; &nbsp; #### Hamilton path visit all the vertex only once --- <div class="cont"> <div class="col"> ```graphviz! digraph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato cin [style=invis, pos="1,0!"] uno [label="vertex", pos="1,1!"] due [style=invis, pos="0,2!"] tre [style=invis, pos="1,2!"] qua [style=invis, pos="2,2!"] uno -> due uno -> tre uno -> qua uno -> cin } ``` source: only out edges </div> <div class="col"> ```graphviz! digraph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato cin [style=invis, pos="1,0!"] uno [label="vertex", pos="1,1!"] due [style=invis, pos="0,2!"] tre [style=invis, pos="1,2!"] qua [style=invis, pos="2,2!"] due -> uno tre -> uno qua -> uno cin -> uno } ``` sink: only in edges </div> </div> --- ```graphviz graph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=circle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato label="original" fontcolor=orange tre [label = 3 pos="1,0!"] qua [label = 4 pos="0,1!"] due [label = 2, pos="2,1!"] uno [label = 1, pos="1,2!"] uno -- due uno -- qua tre -- qua } ``` ```graphviz graph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=circle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato label="complete" fontcolor=orange tre [label = 3 pos="1,0!"] qua [label = 4 pos="0,1!"] due [label = 2, pos="2,1!"] uno [label = 1, pos="1,2!"] uno -- due uno -- tre uno -- qua due -- tre due -- qua tre -- qua } ``` ```graphviz graph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=circle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato label="complement" fontcolor=orange tre [label = 3 pos="1,0!"] qua [label = 4 pos="0,1!"] due [label = 2, pos="2,1!"] uno [label = 1, pos="1,2!"] uno -- tre due -- tre due -- qua } ``` $G + \overline{G} = C_G$ density: $\rho_G \doteq \frac{|E_G|}{|E_C|}$ --- ## In the computer * Adjacency list (sparse graphs, $E_G \lesssim \frac{1}{4}E_C$) * Adjacency matrix (dense graphs, $E_G \gtrsim \frac{3}{4}E_C$) * Parent list (trees only) * Array (binary trees/heaps) --- ## Common problems and algorithms * Traversal: _depth-first_ (recursive) and _breadth-first_ (iterative) order * Topological sort of DAG (arrange tasks with dependencies) * Finding strongly or bi-connected components * Minimum spanning tree (Kruskal, Prim) * Shortest path (Dijkstra) * Flow networks * TSP (<a href="https://people.epfl.ch/ola.svensson" style="color: pink">Svensson</a>) --- ## Hands on AoC DFS: [2020/10](https://adventofcode.com/2020/day/7) All simple paths: [2020/10](https://adventofcode.com/2020/day/10) Dijkstra: [2021/15](https://adventofcode.com/2021/day/15) --- ### AoC 2021 / 15 <div class="cont"> <div class="col"> <code> <em>1</em></tt>163</br> <em>1</em>381</br> <em>2136</em></br> 369<em>4</em></br> </code> </div> <div class="col"> ```graphviz! digraph G { bgcolor="#191919" fontname="Helvetica,Arial,sans-serif" node [fontname="Helvetica,Arial,sans-serif" fontcolor=white color="#cc9900" fontsize=16 shape=rectangle] edge [fontname="Helvetica,Arial,sans-serif" fontcolor=white color=white fontsize=10] layout=neato 11 [pos="1,-1!"] 12 [pos="3,-1!"] 13 [pos="5,-1!"] 14 [pos="7,-1!"] 21 [pos="1,-2!"] 22 [pos="3,-2!"] 23 [pos="5,-2!"] 24 [pos="7,-2!"] 31 [pos="1,-3!"] 32 [pos="3,-3!"] 33 [pos="5,-3!"] 34 [pos="7,-3!"] 41 [pos="1,-4!"] 42 [pos="3,-4!"] 43 [pos="5,-4!"] 44 [pos="7,-4!"] 11 -> 12 [label=1] 12 -> 13 [label=6] 13 -> 14 [label=3] 14 -> 13 [label=6] 13 -> 12 [label=1] 12 -> 11 [label=1] 21 -> 22 [label=3] 22 -> 23 [label=8] 23 -> 24 [label=1] 24 -> 23 [label=8] 23 -> 22 [label=3] 22 -> 21 [label=1] 31 -> 32 [label=1] 32 -> 33 [label=3] 33 -> 34 [label=6] 34 -> 33 [label=3] 33 -> 32 [label=1] 32 -> 31 [label=2] 41 -> 42 [label=6] 42 -> 43 [label=9] 43 -> 44 [label=4] 44 -> 43 [label=9] 43 -> 42 [label=6] 42 -> 41 [label=3] 11 -> 21 [label=1] 21 -> 31 [label=2] 31 -> 41 [label=3] 41 -> 31 [label=2] 31 -> 21 [label=1] 21 -> 11 [label=1] 12 -> 22 [label=3] 22 -> 32 [label=1] 32 -> 42 [label=6] 42 -> 32 [label=1] 32 -> 22 [label=3] 22 -> 12 [label=1] 13 -> 23 [label=8] 23 -> 33 [label=3] 33 -> 43 [label=9] 43 -> 33 [label=3] 33 -> 23 [label=8] 23 -> 13 [label=6] 14 -> 24 [label=1] 24 -> 34 [label=6] 34 -> 44 [label=4] 44 -> 34 [label=6] 34 -> 24 [label=1] 24 -> 14 [label=3] } ``` </div></div> * values are costs for **arriving** in a _cavern room_ from an adjacent _room_. * formulate as directed graph * least dangerous path &rarr; Dijkstra --- ```ruby! #!/usr/bin/env ruby # https://github.com/monora/rgl require 'rgl/adjacency' require 'rgl/dijkstra' class Quiz def initialize(data) @risk=[] data.lines.each_with_index do |l,i| @risk[i] = l.chomp.split("").map{|c| c.to_i} end @si = @risk.count @sj = @risk[0].count setup_graph() end def nodename(i,j) # sprintf("%03d_%03d", i, j) i * @sj + j end def risk_between(i,j) @risk[i][j] end def setup_graph() @g = RGL::DirectedAdjacencyGraph.new @w = {} 0.upto(@si - 1) do |i| 0.upto(@sj - 1) do |j| n = nodename(i,j) wn = risk_between(i,j) if j < (@sj - 1) m = nodename(i,j+1) wm = risk_between(i,j+1) @g.add_edge(n,m) @w[[n,m]] = wm @g.add_edge(m,n) @w[[m,n]] = wn end if i < (@si - 1) m = nodename(i+1,j) wm = risk_between(i+1,j) @g.add_edge(n,m) @w[[n,m]] = wm @g.add_edge(m,n) @w[[m,n]] = wn end end end end def optimal_path_cost(si=0,sj=0,ti=@si-1,tj=@sj-1) p=@g.dijkstra_shortest_path(@w, nodename(si,sj), nodename(ti,tj)) p[..-2].zip(p[1..]).sum{|e| risk_at(e)} end end q = Quiz.new(File.read("t1.txt")) c = q.optimal_path_cost c == 40 or raise "Total cost for test data should be 40 but we get #{c}." q = Quiz.new(File.read("data.txt")) c = q.optimal_path_cost puts "Quiz 1 solution: #{c}" ``` --- ### AoC 2021 / 15 part 2 * Maze is multiplied by 5 in both directions. * Brute force inpracticable * Dijkstra scales $\mathcal{O}(n \log n)$. Good! <div class="cont"> <div class="col"> <pre> nr time 1 0.073 2 0.313 3 0.727 4 1.334 5 2.139 6 3.331 7 4.913 8 6.429 9 8.308 </pre> </div> <div class="col"> <img src="https://i.imgur.com/HMQ27Af.png" /> </div> </div> --- ```ruby! #!/usr/bin/env ruby # https://github.com/monora/rgl require 'rgl/implicit' require 'rgl/dijkstra' class Quiz def initialize(data, rep=1) @risk=[] data.lines.each_with_index do |l,i| @risk[i] = l.chomp.split("").map{|c| c.to_i} end @si = @risk.count @sj = @risk[0].count @rsi = rep * @si @rsj = rep * @sj @g = RGL::ImplicitGraph.new do |g| g.vertex_iterator do |callback| 0.upto(@rsi*@rsj-1) {|n| callback.call(n)} end g.adjacent_iterator do |n1, callback| i,j = name_to_ij(n1) nn = [] nn << ij_to_name(i-1,j) if i > 0 nn << ij_to_name(i+1,j) if i + 1 < @rsi nn << ij_to_name(i,j-1) if j > 0 nn << ij_to_name(i,j+1) if j + 1 < @rsj nn.each{|n2| callback.call(n2)} end g.directed = true end @edge_weights_lambda = lambda {|edge| self.risk_at(edge)} end def ij_to_name(i,j) return i * @rsj + j end def name_to_ij(n) i = n / @rsj j = n % @rsj return i, j end def risk_at(edge) n1,n2 = edge i, j = name_to_ij(n2) mi = i / @si mj = j / @sj ii = i % @si jj = j % @sj # each time the tile repeats to the right or downward, all of its risk # levels are 1 higher than the tile immediately up or left of it. # However, risk levels above 9 wrap back around to 1. # Numbers 1-9 and wrap is like numbers 0-8 % 9 + 1 (@risk[ii][jj] + mi + mj - 1)%9 + 1 end def optimal_path_cost(si=0,sj=0,ti=@rsi-1,tj=@rsj-1) p=@g.dijkstra_shortest_path(@edge_weights_lambda, ij_to_name(si,sj), ij_to_name(ti,tj)) p[..-2].zip(p[1..]).sum{|e| risk_at(e)} end end q = Quiz.new(File.read("t1.txt"), 1) c = q.optimal_path_cost c == 40 or raise "Total cost for test data should be 40 but we get #{c}." q = Quiz.new(File.read("data.txt"), 1) c = q.optimal_path_cost c == 720 or raise "Total cost for question 1 should be 720 but we get #{c}." q = Quiz.new(File.read("t1.txt"), 5) c = q.optimal_path_cost c == 315 or raise "Total cost for test data should be 315 but we get #{c}." q = Quiz.new(File.read("data.txt"), 5) c = q.optimal_path_cost puts "Quiz 2 solution: #{c}" ``` --- ### AoC 2020 / 10 part 2 Solution stolen [here](https://dev.to/meseta/advent-of-code-day-10-using-more-python-vectorized-calculations-in-numpy-2phe) ```python import numpy as np import networkx as nx data = np.loadtxt("demo.txt") joltages = np.sort(np.append(data, (0, np.max(data)+3))) graph = nx.DiGraph() for idx, adapter in enumerate(joltages[:-1]): others = joltages[idx+1:] for other in others[others <= adapter+3]: graph.add_edge(adapter, other, diff=other-adapter) count = sum(1 for _ in nx.all_simple_paths(graph, supply, device)) print("paths", count) ``` --- ### Sources - K. Orwant, J. Hietaniemi, J. Macdonald: _Mastering Algorithms with Perl_, O'Reilly - Jay Wengrow: _A Common-Sense Guide to Data Structures and Algorithms, Second Edition_, The Pragmatic Programmer - https://www.rubyguides.com/2017/05/graph-theory-in-ruby/ - https://en.m.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg - https://python.plainenglish.io/solve-sudoku-using-depth-first-search-algorithm-dfs-in-python-2be3caa08ccd --- ### Thank you! <style> .reveal .cont{ display: flex; } .reveal .col{ flex: 1; } .reveal .red{ color: #F55C5C; } .reveal .green{ color: #5CF55C; } .reveal .blue{ color: #5C5CF5; } .reveal .history { color: #B8860B; } .reveal em { color: #FFE4B5; } .reveal strong { color: #40E0D0; } /* .reveal { background-color: #e6e6e6; background-image: url('https://epfl-idevelop.github.io/elements/svg/epfl-logo.svg'); background-repeat: no-repeat; background-position: 5px 5px; } .reveal { color: #707070; } .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { color: #212121; } .reveal a { color: #f009; } .reveal a:hover { color: #f00; } .reveal .more { color: #339; } .reveal code { padding-top: 0.2em; padding-bottom: 0.2em; margin: 0; font-size: 85%; background-color: rgba(255, 255, 255, 0.46); border-radius: 3px; } [data-contrast="on"] > div { background-color: #ffffff94; } */ /* https://stackoverflow.com/a/39614958/960623 */ img[alt$=">"] { float: right; } img[alt$="<"] { float: left; } img[alt$="><"] { display: block; max-width: 100%; height: auto; margin: auto; float: none!important; } </style>
{"metaMigratedAt":"2023-06-17T19:04:43.591Z","metaMigratedFrom":"YAML","title":"St. Luc – Graphs","breaks":true,"description":"View the slide with \"Slide Mode\".","contributors":"[{\"id\":\"d4f95d06-3c38-4029-838a-43388a793679\",\"add\":33034,\"del\":14058}]"}
    563 views