<?xml version="1.0" encoding="utf-8"?> 
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us">
    <generator uri="https://gohugo.io/" version="0.152.2">Hugo</generator><title type="html"><![CDATA[Vf2++ on Blog]]></title>
    
    
    
            <link href="https://blog.scientific-python.org/tags/vf2++/" rel="alternate" type="text/html" title="html" />
            <link href="https://blog.scientific-python.org/tags/vf2++/atom.xml" rel="self" type="application/atom" title="atom" />
    <updated>2026-04-04T04:32:36+00:00</updated>
    
    
    
    
        <id>https://blog.scientific-python.org/tags/vf2&#43;&#43;/</id>
    
        
        <entry>
            <title type="html"><![CDATA[The VF2++ algorithm]]></title>
            <link href="https://blog.scientific-python.org/networkx/vf2pp/graph-iso-vf2pp/?utm_source=atom_feed" rel="alternate" type="text/html" />
            
                <link href="https://blog.scientific-python.org/networkx/vf2pp/iso-feasibility-candidates/?utm_source=atom_feed" rel="related" type="text/html" title="ISO Feasibility &amp; Candidates" />
                <link href="https://blog.scientific-python.org/networkx/vf2pp/node-ordering-ti-updating/?utm_source=atom_feed" rel="related" type="text/html" title="Updates on VF2&#43;&#43;" />
                <link href="https://blog.scientific-python.org/networkx/vf2pp/gsoc-2022/?utm_source=atom_feed" rel="related" type="text/html" title="GSoC 2022: NetworkX VF2&#43;&#43; Implementation" />
                <link href="https://blog.scientific-python.org/networkx/atsp/my-summer-of-code-2021/?utm_source=atom_feed" rel="related" type="text/html" title="My Summer of Code 2021" />
                <link href="https://blog.scientific-python.org/networkx/atsp/completing-the-asadpour-algorithm/?utm_source=atom_feed" rel="related" type="text/html" title="Completing the Asadpour Algorithm" />
            
                <id>https://blog.scientific-python.org/networkx/vf2pp/graph-iso-vf2pp/</id>
            
            
            <published>2022-08-10T00:00:00+00:00</published>
            <updated>2022-08-10T00:00:00+00:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>Implementing the VF2++ algorithm for the Graph Isomorphism.</blockquote><p>The last and final post discussing the <strong>VF2++ helpers</strong> can be found <a href="../iso-feasibility-candidates">here</a>.
Now that we&rsquo;ve figured out how to solve all the sub-problems that <strong>VF2++</strong> consists of, we are ready to combine our
implemented functionalities to create the final solver for the <strong>Graph Isomorphism</strong> problem.</p>
<h2 id="introduction">Introduction<a class="headerlink" href="#introduction" title="Link to this heading">#</a></h2>
<p>We should quickly review the individual functionalities used in the VF2++ algorithm:</p>
<ul>
<li><strong>Node ordering</strong> which finds the optimal order to access the nodes, such that those that are more likely to match are placed first in the order. This reduces the possibility of infeasible searches taking place first.</li>
<li><strong>Candidate selection</strong> such that, given a node $u$ from $G_1$, we obtain the candidate nodes $v$ from $G_2$.</li>
<li><strong>Feasibility rules</strong> introducing easy-to-check cutting and consistency conditions which, if satisfied by a candidate pair of nodes $u$ from $G_1$ and $v$ from $G_2$, the mapping is extended.</li>
<li><strong>$T_i$ updating</strong> which updates the $T_i$ and $\tilde{T}_i$, $i=1,2$ parameters in case that a new pair is added to the mapping, and restores them when a pair is popped from it.</li>
</ul>
<p>We are going to use all these functionalities to form our <strong>Isomorphism solver</strong>.</p>
<h2 id="vf2">VF2++<a class="headerlink" href="#vf2" title="Link to this heading">#</a></h2>
<p>First of all, let&rsquo;s describe the algorithm in simple terms, before presenting the pseudocode. The algorithm will look something like this:</p>
<ol>
<li>Check if all <strong>preconditions</strong> are satisfied before calling the actual solver. For example there&rsquo;s no point examining two graphs with different number of nodes for isomorphism.</li>
<li>Initialize all the necessary <strong>parameters</strong> ($T_i$, $\tilde{T}_i$, $i=1,2$) and maybe cache some information that is going to be used later.</li>
<li>Take the next unexamined node $u$ from the ordering.</li>
<li>Find its candidates and check if there&rsquo;s a candidate $v$ such that the pair $u-v$ satisfies the <strong>feasibility rules</strong></li>
<li>if there&rsquo;s any, extend the mapping and <strong>go to 3</strong>.</li>
<li>if not, pop the last pair $\hat{u}-\hat{v}$ from the mapping and try a different candidate $\hat{v}$, from the remaining candidates of $\hat{u}$</li>
<li>The two graphs are <strong>isomorphic</strong> if the number of <strong>mapped nodes</strong> equals the number of nodes of the two graphs.</li>
<li>The two graphs are <strong>not isomorphic</strong> if there are no remaining candidates for the first node of the ordering (root).</li>
</ol>
<p>The official code for the <strong>VF2++</strong> is presented below.</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="c1"># Check if there&#39;s a graph with no nodes in it</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">G1</span><span class="o">.</span><span class="n">number_of_nodes</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">G2</span><span class="o">.</span><span class="n">number_of_nodes</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check that both graphs have the same number of nodes and degree sequence</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="ow">not</span> <span class="n">nx</span><span class="o">.</span><span class="n">faster_could_be_isomorphic</span><span class="p">(</span><span class="n">G1</span><span class="p">,</span> <span class="n">G2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Initialize parameters (Ti/Ti_tilde, i=1,2) and cache necessary information about degree and labels</span>
</span></span><span class="line"><span class="cl"><span class="n">graph_params</span><span class="p">,</span> <span class="n">state_params</span> <span class="o">=</span> <span class="n">_initialize_parameters</span><span class="p">(</span><span class="n">G1</span><span class="p">,</span> <span class="n">G2</span><span class="p">,</span> <span class="n">node_labels</span><span class="p">,</span> <span class="n">default_label</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check if G1 and G2 have the same labels, and that number of nodes per label is equal between the two graphs</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="ow">not</span> <span class="n">_precheck_label_properties</span><span class="p">(</span><span class="n">graph_params</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Calculate the optimal node ordering</span>
</span></span><span class="line"><span class="cl"><span class="n">node_order</span> <span class="o">=</span> <span class="n">_matching_order</span><span class="p">(</span><span class="n">graph_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Initialize the stack to contain node-candidates pairs</span>
</span></span><span class="line"><span class="cl"><span class="n">stack</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">candidates</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">_find_candidates</span><span class="p">(</span><span class="n">node_order</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">graph_params</span><span class="p">,</span> <span class="n">state_params</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">stack</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">node_order</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">candidates</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">mapping</span> <span class="o">=</span> <span class="n">state_params</span><span class="o">.</span><span class="n">mapping</span>
</span></span><span class="line"><span class="cl"><span class="n">reverse_mapping</span> <span class="o">=</span> <span class="n">state_params</span><span class="o">.</span><span class="n">reverse_mapping</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Index of the node from the order, currently being examined</span>
</span></span><span class="line"><span class="cl"><span class="n">matching_node</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="n">stack</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">current_node</span><span class="p">,</span> <span class="n">candidate_nodes</span> <span class="o">=</span> <span class="n">stack</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">candidate</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">candidate_nodes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># If no remaining candidates, return to a previous state, and follow another branch</span>
</span></span><span class="line"><span class="cl">        <span class="n">stack</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">matching_node</span> <span class="o">-=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">stack</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Pop the previously added u-v pair, and look for a different candidate _v for u</span>
</span></span><span class="line"><span class="cl">            <span class="n">popped_node1</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">stack</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="n">popped_node2</span> <span class="o">=</span> <span class="n">mapping</span><span class="p">[</span><span class="n">popped_node1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="n">mapping</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">popped_node1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">reverse_mapping</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">popped_node2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">_restore_Tinout</span><span class="p">(</span><span class="n">popped_node1</span><span class="p">,</span> <span class="n">popped_node2</span><span class="p">,</span> <span class="n">graph_params</span><span class="p">,</span> <span class="n">state_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">continue</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">_feasibility</span><span class="p">(</span><span class="n">current_node</span><span class="p">,</span> <span class="n">candidate</span><span class="p">,</span> <span class="n">graph_params</span><span class="p">,</span> <span class="n">state_params</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Terminate if mapping is extended to its full</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">mapping</span><span class="p">)</span> <span class="o">==</span> <span class="n">G2</span><span class="o">.</span><span class="n">number_of_nodes</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">cp_mapping</span> <span class="o">=</span> <span class="n">mapping</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="n">cp_mapping</span><span class="p">[</span><span class="n">current_node</span><span class="p">]</span> <span class="o">=</span> <span class="n">candidate</span>
</span></span><span class="line"><span class="cl">            <span class="k">yield</span> <span class="n">cp_mapping</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Feasibility rules pass, so extend the mapping and update the parameters</span>
</span></span><span class="line"><span class="cl">        <span class="n">mapping</span><span class="p">[</span><span class="n">current_node</span><span class="p">]</span> <span class="o">=</span> <span class="n">candidate</span>
</span></span><span class="line"><span class="cl">        <span class="n">reverse_mapping</span><span class="p">[</span><span class="n">candidate</span><span class="p">]</span> <span class="o">=</span> <span class="n">current_node</span>
</span></span><span class="line"><span class="cl">        <span class="n">_update_Tinout</span><span class="p">(</span><span class="n">current_node</span><span class="p">,</span> <span class="n">candidate</span><span class="p">,</span> <span class="n">graph_params</span><span class="p">,</span> <span class="n">state_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Append the next node and its candidates to the stack</span>
</span></span><span class="line"><span class="cl">        <span class="n">candidates</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">_find_candidates</span><span class="p">(</span><span class="n">node_order</span><span class="p">[</span><span class="n">matching_node</span><span class="p">],</span> <span class="n">graph_params</span><span class="p">,</span> <span class="n">state_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">stack</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">node_order</span><span class="p">[</span><span class="n">matching_node</span><span class="p">],</span> <span class="n">candidates</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">matching_node</span> <span class="o">+=</span> <span class="mi">1</span></span></span></code></pre>
</div>
<h2 id="performance">Performance<a class="headerlink" href="#performance" title="Link to this heading">#</a></h2>
<p>This section is dedicated to the performance comparison between <strong>VF2</strong> and <strong>VF2++</strong>. The comparison was performed in
<strong>random graphs</strong> without labels, for number of nodes anywhere between the range $(100-2000)$. The results are depicted
in the two following diagrams.</p>
<center><img src="times.png" alt="vf2++ and vf2 times"/></center>
<center><img src="speedup.png" alt="speedup"/></center>
<p>We notice that the maximum speedup achieved is <strong>14x</strong>, and continues to increase as the number of nodes increase.
It is also highly prominent that the increase in number of nodes, doesn&rsquo;t seem to affect the performance of <strong>VF2++</strong> to
a significant extent, when compared to the drastic impact on the performance of <strong>VF2</strong>. Our results are almost identical
to those presented in the original <strong><a href="https://www.sciencedirect.com/science/article/pii/S0166218X18300829">VF2++ paper</a></strong>, verifying the theoretical analysis and premises of the literature.</p>
<h2 id="optimizations">Optimizations<a class="headerlink" href="#optimizations" title="Link to this heading">#</a></h2>
<p>The achieved boost is due to some key improvements and optimizations, specifically:</p>
<ul>
<li><strong>Optimal node ordering</strong>, which avoids following unfruitful branches that will result in infeasible states. We make sure that the nodes that have the biggest possibility to match are accessed first.</li>
<li><strong>Implementation in a non-recursive manner</strong>, avoiding Python&rsquo;s maximum recursion limit while also reducing function call overhead.</li>
<li><strong>Caching</strong> of both node degrees and nodes per degree in the beginning, so that we don&rsquo;t have to access those features in every degree check. For example, instead of doing</li>
</ul>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">G2</span><span class="o">.</span><span class="n">nodes</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">G1</span><span class="o">.</span><span class="n">degree</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">==</span> <span class="n">G2</span><span class="o">.</span><span class="n">degree</span><span class="p">[</span><span class="n">node</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># do stuff with res ...</span></span></span></code></pre>
</div>
<p>to get the nodes of same degree as u (which happens a lot of times in the implementation), we just do:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="n">G2_nodes_of_degree</span><span class="p">[</span><span class="n">G1</span><span class="o">.</span><span class="n">degree</span><span class="p">[</span><span class="n">u</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># do stuff with res ...</span></span></span></code></pre>
</div>
<p>where &ldquo;G2_nodes_of_degree&rdquo; stores set of nodes for a given degree. The same is done with node labels.</p>
<ul>
<li><strong>Extra shrinking of the candidate set for each node</strong> by adding more checks in the candidate selection method and removing some from the feasibility checks. In simple terms, instead of checking a lot of conditions on a larger set of candidates, we check fewer conditions but on a more targeted and significantly smaller set of candidates.
For example, in this code:</li>
</ul>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">candidates</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">G2</span><span class="o">.</span><span class="n">nodes</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">candidate</span> <span class="ow">in</span> <span class="n">candidates</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">feasibility</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">candidate</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">do_stuff</span><span class="p">()</span></span></span></code></pre>
</div>
<p>we take a huge set of candidates, which results in poor performance due to maximizing calls of &ldquo;feasibility&rdquo;, thus performing
the feasibility checks in a very large set. Now compare that to the following alternative:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">candidates</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="n">n</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">G2_nodes_of_degree</span><span class="p">[</span><span class="n">G1</span><span class="o">.</span><span class="n">degree</span><span class="p">[</span><span class="n">u</span><span class="p">]]</span><span class="o">.</span><span class="n">intersection</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">G2_nodes_of_label</span><span class="p">[</span><span class="n">G1_labels</span><span class="p">[</span><span class="n">u</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">candidate</span> <span class="ow">in</span> <span class="n">candidates</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">feasibility</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">candidate</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">do_stuff</span><span class="p">()</span></span></span></code></pre>
</div>
<p>Immediately we have drastically reduced the number of checks performed and calls to the function, as now we only apply them to nodes of the same degree and label as $u$. This is a simplification for demonstration purposes. In the actual implementation there are more checks and extra shrinking of the candidate set.</p>
<h2 id="demo">Demo<a class="headerlink" href="#demo" title="Link to this heading">#</a></h2>
<p>Let&rsquo;s demonstrate our <strong>VF2++</strong> solver on a real graph. We are going to use the graph from the Graph Isomorphism wikipedia.</p>
<p float="center">
  <img src="https://upload.wikimedia.org/wikipedia/commons/9/9a/Graph_isomorphism_a.svg" width="200" height="200">
  <img src="https://upload.wikimedia.org/wikipedia/commons/8/84/Graph_isomorphism_b.svg" width="395" height="250">
</p>
<p>Let&rsquo;s start by constructing the graphs from the image above. We&rsquo;ll call
the graph on the left <code>G</code> and the graph on the left <code>H</code>:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">networkx</span> <span class="k">as</span> <span class="nn">nx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">G</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">Graph</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;g&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;h&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;i&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;g&#34;</span><span class="p">,</span> <span class="s2">&#34;b&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;g&#34;</span><span class="p">,</span> <span class="s2">&#34;c&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;b&#34;</span><span class="p">,</span> <span class="s2">&#34;h&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;b&#34;</span><span class="p">,</span> <span class="s2">&#34;j&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;h&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;c&#34;</span><span class="p">,</span> <span class="s2">&#34;i&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;c&#34;</span><span class="p">,</span> <span class="s2">&#34;j&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;i&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;d&#34;</span><span class="p">,</span> <span class="s2">&#34;j&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">H</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">Graph</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">6</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre>
</div>
<h3 id="use-the-vf2-without-taking-labels-into-consideration">use the VF2++ without taking labels into consideration<a class="headerlink" href="#use-the-vf2-without-taking-labels-into-consideration" title="Link to this heading">#</a></h3>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">vf2pp_is_isomorphic</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">node_label</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># res: True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">vf2pp_isomorphism</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">node_label</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># res: {1: &#34;a&#34;, 2: &#34;h&#34;, 3: &#34;d&#34;, 4: &#34;i&#34;, 5: &#34;g&#34;, 6: &#34;b&#34;, 7: &#34;j&#34;, 8: &#34;c&#34;}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">nx</span><span class="o">.</span><span class="n">vf2pp_all_isomorphisms</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">node_label</span><span class="o">=</span><span class="kc">None</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># res: all isomorphic mappings (there might be more than one). This function is a generator.</span></span></span></code></pre>
</div>
<h3 id="use-the-vf2-taking-labels-into-consideration">use the VF2++ taking labels into consideration<a class="headerlink" href="#use-the-vf2-taking-labels-into-consideration" title="Link to this heading">#</a></h3>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="c1"># Assign some label to each node</span>
</span></span><span class="line"><span class="cl"><span class="n">G_node_attributes</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;a&#34;</span><span class="p">:</span> <span class="s2">&#34;blue&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;g&#34;</span><span class="p">:</span> <span class="s2">&#34;green&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;b&#34;</span><span class="p">:</span> <span class="s2">&#34;pink&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;h&#34;</span><span class="p">:</span> <span class="s2">&#34;red&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;c&#34;</span><span class="p">:</span> <span class="s2">&#34;yellow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;i&#34;</span><span class="p">:</span> <span class="s2">&#34;orange&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;d&#34;</span><span class="p">:</span> <span class="s2">&#34;cyan&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;j&#34;</span><span class="p">:</span> <span class="s2">&#34;purple&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">nx</span><span class="o">.</span><span class="n">set_node_attributes</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">G_node_attributes</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&#34;color&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">H_node_attributes</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="mi">1</span><span class="p">:</span> <span class="s2">&#34;blue&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">2</span><span class="p">:</span> <span class="s2">&#34;red&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">3</span><span class="p">:</span> <span class="s2">&#34;cyan&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">4</span><span class="p">:</span> <span class="s2">&#34;orange&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">5</span><span class="p">:</span> <span class="s2">&#34;green&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">6</span><span class="p">:</span> <span class="s2">&#34;pink&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">7</span><span class="p">:</span> <span class="s2">&#34;purple&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="mi">8</span><span class="p">:</span> <span class="s2">&#34;yellow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">nx</span><span class="o">.</span><span class="n">set_node_attributes</span><span class="p">(</span><span class="n">H</span><span class="p">,</span> <span class="n">H_node_attributes</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&#34;color&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">vf2pp_is_isomorphic</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">node_label</span><span class="o">=</span><span class="s2">&#34;color&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># res: True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">vf2pp_isomorphism</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">node_label</span><span class="o">=</span><span class="s2">&#34;color&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># res: {1: &#34;a&#34;, 2: &#34;h&#34;, 3: &#34;d&#34;, 4: &#34;i&#34;, 5: &#34;g&#34;, 6: &#34;b&#34;, 7: &#34;j&#34;, 8: &#34;c&#34;}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">res</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">nx</span><span class="o">.</span><span class="n">vf2pp_all_isomorphisms</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">node_label</span><span class="o">=</span><span class="s2">&#34;color&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># res: {1: &#34;a&#34;, 2: &#34;h&#34;, 3: &#34;d&#34;, 4: &#34;i&#34;, 5: &#34;g&#34;, 6: &#34;b&#34;, 7: &#34;j&#34;, 8: &#34;c&#34;}</span></span></span></code></pre>
</div>
<p>Notice how in the first case, our solver may return a different mapping every time, since the absence of labels results in nodes that can map to more than one others. For example, node 1 can map to both a and h, since the graph is symmetrical.
On the second case though, the existence of a single, unique label per node imposes that there&rsquo;s only one match for each node, so the mapping returned is deterministic. This is easily observed from
output of <code>list(nx.vf2pp_all_isomorphisms)</code> which, in the first case, returns all possible mappings while in the latter, returns a single, unique isomorphic mapping.</p>
]]></content>
            
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="taxonomy:Tags" term="gsoc" label="gsoc" />
                             
                                <category scheme="taxonomy:Tags" term="networkx" label="networkx" />
                             
                                <category scheme="taxonomy:Tags" term="vf2&#43;&#43;" label="vf2&#43;&#43;" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ISO Feasibility & Candidates]]></title>
            <link href="https://blog.scientific-python.org/networkx/vf2pp/iso-feasibility-candidates/?utm_source=atom_feed" rel="alternate" type="text/html" />
            
                <link href="https://blog.scientific-python.org/networkx/vf2pp/node-ordering-ti-updating/?utm_source=atom_feed" rel="related" type="text/html" title="Updates on VF2&#43;&#43;" />
                <link href="https://blog.scientific-python.org/networkx/vf2pp/gsoc-2022/?utm_source=atom_feed" rel="related" type="text/html" title="GSoC 2022: NetworkX VF2&#43;&#43; Implementation" />
                <link href="https://blog.scientific-python.org/networkx/atsp/my-summer-of-code-2021/?utm_source=atom_feed" rel="related" type="text/html" title="My Summer of Code 2021" />
                <link href="https://blog.scientific-python.org/networkx/atsp/completing-the-asadpour-algorithm/?utm_source=atom_feed" rel="related" type="text/html" title="Completing the Asadpour Algorithm" />
                <link href="https://blog.scientific-python.org/networkx/atsp/looking-at-the-big-picture/?utm_source=atom_feed" rel="related" type="text/html" title="Looking at the Big Picture" />
            
                <id>https://blog.scientific-python.org/networkx/vf2pp/iso-feasibility-candidates/</id>
            
            
            <published>2022-07-11T00:00:00+00:00</published>
            <updated>2022-07-11T00:00:00+00:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>Information about my progress on two important features of the algorithm.</blockquote><p>The previous post can be found <a href="../node-ordering-ti-updating">here</a>, be sure to check it out so you
can
follow the process step by step. Since then, another two very significant features of the algorithm have been
implemented and tested: <strong>node pair candidate selection</strong> and <strong>feasibility checks</strong>.</p>
<h2 id="introduction">Introduction<a class="headerlink" href="#introduction" title="Link to this heading">#</a></h2>
<p>As previously described, in the ISO problem we are basically trying to create a <strong>mapping</strong> such that, every node
from the first graph is matched to a node from the second graph. This searching for &ldquo;feasible pairs&rdquo; can be visualized
by a tree, where each node is the candidate pair that we should examine. This can become much clearer if we take a look
at the below figure.</p>
<center><img src="dfs.png" alt="DFS VF2++ example$"/></center>
<p>In order to check if the graphs $G_1$, $G_2$ are isomorphic, we check every candidate pair of nodes and if it is
feasible, we extend the mapping and go deeper into the tree of pairs. If it&rsquo;s not feasible, we climb up and follow a
different branch, until every node in $G_1$ is mapped to a node $G_2$. In our example, we start by examining node 0 from G1, with
node 0 of G2. After some checks (details below), we decide that the
nodes 0 and 0 are matching, so we go deeper to map the remaining nodes. The next pair is 1-3, which fails the
feasibility check, so we have to examine a different branch as shown. The new branch is 1-2, which is feasible, so we
continue on using the same logic until all the nodes are mapped.</p>
<h2 id="candidate-pair-selection">Candidate Pair Selection<a class="headerlink" href="#candidate-pair-selection" title="Link to this heading">#</a></h2>
<p>Although in our example we use a random candidate pair of nodes, in the actual implementation we are able to target
specific pairs that are more likely to be matched, hence boost the performance of the algorithm. The idea is that, in
every step of the algorithm, <strong>given a candidate</strong></p>
<p>$$u\in V_1$$</p>
<p><strong>we compute the candidates</strong></p>
<p>$$v\in V_2$$</p>
<p>where $V_1$ and $V_2$ are the nodes of $G_1$ and $G_2$ respectively. Now this is a puzzle that does not require a lot of
specific knowledge on graphs or the algorithm itself. Keep up with me, and you will realize it yourself. First, let $M$
be the mapping so far, which includes all the &ldquo;covered nodes&rdquo; until this point. There are actually <strong>three</strong> different
types of $u$ nodes that we might encounter.</p>
<h3 id="case-1">Case 1<a class="headerlink" href="#case-1" title="Link to this heading">#</a></h3>
<p>Node $u$ has no neighbors (degree of $u$ equals to zero). It would be redundant to test
as candidates for $u$, nodes from $G_2$ that have more than zero neighbors. That said, we eliminate most of the possible
candidates and keep those that have the same degree as $u$ (in this case, zero). Pretty easy right?</p>
<h3 id="case-2">Case 2<a class="headerlink" href="#case-2" title="Link to this heading">#</a></h3>
<p>Node $u$ has neighbors, but none of them belong to the mapping. This situation is illustrated in the following figure.</p>
<center><img src="c2.png" alt="candidates"/></center>
<p>The grey lines indicate that the nodes of $G_1$ (left 1,2) are mapped to the nodes of $G_2$ (right 1,2). They are basically
the mapping. Again, given $u$, we make the observation that candidates $v$ of u, should also have no neighbors in the
mapping, and also have the same degree as $u$ (as in the figure). Notice how if we add a neighbor to $v$, or if we place
one of its neighbors inside the mapping, there is no point examining the pair $u-v$ for matching.</p>
<h3 id="case-3">Case 3<a class="headerlink" href="#case-3" title="Link to this heading">#</a></h3>
<p>Node $u$ has neighbors and some of them belong to the mapping. This scenario is also depicted in the below figure.</p>
<center><img src="c3.png" alt="candidates"/></center>
<p>In this case, to obtain the candidates for $u$, we must look into the neighborhoods of nodes from $G_2$, which map back
to the covered neighbors of $u$. In our example, $u$ has one covered neighbor (1), and 1 from $G_1$ maps to 1 from $G_2$,
which has $v$ as neighbor. Also, for v to be considered as candidate, it should have the same degree as $u$, obviously.
Notice how every node that is not in the neighborhood of 1 (in $G_2$) cannot be matched to $u$ without breaking the
isomorphism.</p>
<h2 id="iso-feasibility-rules">ISO Feasibility Rules<a class="headerlink" href="#iso-feasibility-rules" title="Link to this heading">#</a></h2>
<p>Let&rsquo;s assume that given a node $u$, we obtained its candidate $v$ following the process described in the previous section.
At this point, the <strong>Feasibility Rules</strong> are going to determine whether the mapping should be extended by the pair $u-v$
or if we should try another candidate. The <strong>feasibility</strong> of a pair $u-v$ is examined by <strong>consistency</strong> and
<strong>cutting</strong> checks.</p>
<h3 id="consistency-rules">Consistency rules<a class="headerlink" href="#consistency-rules" title="Link to this heading">#</a></h3>
<p>At, first I am going to present the mathematical expression of the consistency check. It may seem complicated at first,
but it&rsquo;s going to be made simple by using a visual illustration. Using the notation $nbh_i(u)$ for the neighborhood of u
in graph $G_i$, the consistency rule is:</p>
<p>$$\forall\tilde{v}\in nbh_2(v)\cap M:(u, M^{-1}(\tilde{v}))\in E_1) \wedge \forall\tilde{u}\in nbh_1(u)\cap M:(u, M(\tilde{u}))\in E_2)$$</p>
<p>We are going to use the following simple figure to demystify the above equation.</p>
<center><img src="const.png" alt="consistency check scenario"/></center>
<p>The mapping is depicted as grey lines between the nodes that are already mapped, meaning that 1 maps to A and 2 to B.
What is implied by the equation is that, for two nodes $u$ and $v$ to pass the consistency check, the neighbors of $u$
that belong in the mapping, should map to neighbors of $v$ (and backwards). This could be checked by code as simple
as:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="k">for</span> <span class="n">neighbor</span> <span class="ow">in</span> <span class="n">G1</span><span class="p">[</span><span class="n">u</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">neighbor</span> <span class="ow">in</span> <span class="n">mapping</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">mapping</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">G2</span><span class="p">[</span><span class="n">v</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">G1</span><span class="o">.</span><span class="n">number_of_edges</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">neighbor</span><span class="p">)</span> <span class="o">!=</span> <span class="n">G2</span><span class="o">.</span><span class="n">number_of_edges</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">v</span><span class="p">,</span> <span class="n">mapping</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">False</span></span></span></code></pre>
</div>
<p>where the final two lines also check the number of edges between node $u$ and its neighbor $\tilde{u}$, which should be
the same as those between $v$ and its neighbor which $\tilde{u}$ maps to. At a very high level, we could describe this
check as a 1-look-ahead check.</p>
<h3 id="cutting-rules">Cutting rules<a class="headerlink" href="#cutting-rules" title="Link to this heading">#</a></h3>
<p>We have previously discussed what $T_i$ and $\tilde{T_i}$ represent (see previous post). These sets are used in the
cutting checks as follows: the number of neighbors of $u$ that belong to $T_1$, should be equal to the number of
neighbors of $v$ that belong to $T_2$. Take a moment to observe the below figure.</p>
<center><img src="cut.png" alt="cutting check scenario"/></center>
<p>Once again, node 1 maps to A and 2 to B. The red nodes (4,5,6) are basically $T_1$ and the yellow ones (C,D,E) are $T_2$.
Notice that in order for $u-v$ to be feasible, $u$ should have the same number of neighbors, inside $T_1$,
as $v$ in $T_2$. In every other case, the two graphs are not isomorphic, which can be verified visually. For this
example, both nodes have 2 of their neighbors (4,6 and C,E) in $T_1$ and $T_2$ respectively. Careful! If we delete the
$V-E$ edge and connect $V$ to $D$, the cutting condition is still satisfied. However, the feasibility is going to fail,
by the consistency checks of the previous section. A simple code to apply the cutting check would be:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">T1</span><span class="o">.</span><span class="n">intersection</span><span class="p">(</span><span class="n">G1</span><span class="p">[</span><span class="n">u</span><span class="p">]))</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">T2</span><span class="o">.</span><span class="n">intersection</span><span class="p">(</span><span class="n">G2</span><span class="p">[</span><span class="n">v</span><span class="p">]))</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">T1out</span><span class="o">.</span><span class="n">intersection</span><span class="p">(</span><span class="n">G1</span><span class="p">[</span><span class="n">u</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">T2out</span><span class="o">.</span><span class="n">intersection</span><span class="p">(</span><span class="n">G2</span><span class="p">[</span><span class="n">v</span><span class="p">])):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">False</span></span></span></code></pre>
</div>
<p>where <code>T1out</code> and <code>T2out</code> correspond to $\tilde{T_1}$ and $\tilde{T_2}$ respectively. And yes, we have to check for
those as well, however we skipped them in the above explanation for simplicity.</p>
<h2 id="conclusion">Conclusion<a class="headerlink" href="#conclusion" title="Link to this heading">#</a></h2>
<p>At this point, we have successfully implemented and tested all the major components of the algorithm <strong>VF2++</strong>,</p>
<ul>
<li><strong>Node Ordering</strong></li>
<li><strong>$T_i/\tilde{T_i}$ Updating</strong></li>
<li><strong>Feasibility Rules</strong></li>
<li><strong>Candidate Selection</strong></li>
</ul>
<p>This means that, in the next post, hopefully, we are going to discuss our first, full and functional implementation of
<strong>VF2++</strong>.</p>
]]></content>
            
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="taxonomy:Tags" term="gsoc" label="gsoc" />
                             
                                <category scheme="taxonomy:Tags" term="networkx" label="networkx" />
                             
                                <category scheme="taxonomy:Tags" term="vf2&#43;&#43;" label="vf2&#43;&#43;" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Updates on VF2++]]></title>
            <link href="https://blog.scientific-python.org/networkx/vf2pp/node-ordering-ti-updating/?utm_source=atom_feed" rel="alternate" type="text/html" />
            
                <link href="https://blog.scientific-python.org/networkx/vf2pp/gsoc-2022/?utm_source=atom_feed" rel="related" type="text/html" title="GSoC 2022: NetworkX VF2&#43;&#43; Implementation" />
                <link href="https://blog.scientific-python.org/networkx/atsp/my-summer-of-code-2021/?utm_source=atom_feed" rel="related" type="text/html" title="My Summer of Code 2021" />
                <link href="https://blog.scientific-python.org/networkx/atsp/completing-the-asadpour-algorithm/?utm_source=atom_feed" rel="related" type="text/html" title="Completing the Asadpour Algorithm" />
                <link href="https://blog.scientific-python.org/networkx/atsp/looking-at-the-big-picture/?utm_source=atom_feed" rel="related" type="text/html" title="Looking at the Big Picture" />
                <link href="https://blog.scientific-python.org/networkx/atsp/sampling-a-spanning-tree/?utm_source=atom_feed" rel="related" type="text/html" title="Sampling a Spanning Tree" />
            
                <id>https://blog.scientific-python.org/networkx/vf2pp/node-ordering-ti-updating/</id>
            
            
            <published>2022-07-06T00:00:00+00:00</published>
            <updated>2022-07-06T00:00:00+00:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>Summary of the progress on VF2++</blockquote><p>This post includes all the major updates since the <a href="../gsoc-2022">last post</a> about VF2++. Each section
is dedicated to a different sub-problem and presents the progress on it so far. General progress, milestones and related
issues can be <a href="https://github.com/kpetridis24/networkx/milestone/1">found here</a>.</p>
<h2 id="node-ordering">Node ordering<a class="headerlink" href="#node-ordering" title="Link to this heading">#</a></h2>
<p>The node ordering is one major modification that <strong>VF2++</strong> proposes. Basically, the nodes are examined in an order that
makes the matching faster by first examining nodes that are more likely to match. This part of the algorithm has been
implemented, however there is an issue. The existence of detached nodes (not connected to the rest of the graph) causes
the code to crash. Fixing this bug will be a top priority during the next steps. The ordering implementation is described
by the following pseudocode.</p>
<blockquote>
<hr>
<p><strong>Matching Order</strong></p>
<hr>
<ol>
<li><strong>Set</strong> $M = \varnothing$.</li>
<li><strong>Set</strong> $\bar{V1}$ : nodes not in order yet</li>
<li><strong>while</strong> $\bar{V1}$ not empty <strong>do</strong>
<ul>
<li>$rareNodes=[$nodes from $V_1$ with the rarest labels$]$</li>
<li>$maxNode=argmax_{degree}(rareNodes)$</li>
<li>$T=$ BFSTree with $maxNode$ as root</li>
<li><strong>for</strong> every level in $T$ <strong>do</strong>
<ul>
<li>$V_d=[$nodes of the $d^{th}$ level$]$</li>
<li>$\bar{V_1} \setminus V_d$</li>
<li>$ProcessLevel(V_d)$</li>
</ul>
</li>
</ul>
</li>
<li>Output $M$: the matching order of the nodes.</li>
</ol>
</blockquote>
<blockquote>
<hr>
<p><strong>Process Level</strong></p>
<hr>
<ol>
<li><strong>while</strong> $V_d$ not empty <strong>do</strong>
<ul>
<li>$S=[$nodes from $V_d$ with the most neighbors in M$]$</li>
<li>$maxNodes=argmax_{degree}(S)$</li>
<li>$rarestNode=[$node from $maxNodes$ with the rarest label$]$</li>
<li>$V_d \setminus m$</li>
<li>Append m to M</li>
</ul>
</li>
</ol>
</blockquote>
<h2 id="t_i-and-tildet_i">$T_i$ and $\tilde{T_i}$<a class="headerlink" href="#t_i-and-tildet_i" title="Link to this heading">#</a></h2>
<p>According to the VF2++ paper notation:</p>
<p>$$T_1=(u\in V_1 \setminus m: \exists \tilde{u} \in m: (u,\tilde{u}\in E_1))$$</p>
<p>where $V_1$ and $E_1$ contain all the nodes and edges of the first graph respectively, and $m$ is a dictionary, mapping
every node of the first graph to a node of the second graph. Now if we interpret the above equation, we conclude that
$T_1$ contains uncovered neighbors of covered nodes. In simple terms, it includes all the nodes that do not belong in
the mapping $m$ yet, but are neighbors of nodes that are in the mapping. In addition,</p>
<p>$$\tilde{T_1}=(V_1 \setminus m \setminus T_1)$$</p>
<p>The following figure is meant to provide some visual explanation of what exactly $T_i$ is.</p>
<p><img src="/networkx/vf2pp/node-ordering-ti-updating/Ti.png" alt="Illustration of $T_i$."></p>
<p>The blue nodes 1,2,3 are nodes from graph G1 and the green nodes A,B,C belong to the graph G2. The grey lines connecting
those two indicate that in this current state, node 1 is mapped to node A, node 2 is mapped to node B, etc. The yellow
edges are just the neighbors of the covered (mapped) nodes. Here, $T_1$ contains the red nodes (4,5,6) which are
neighbors of the covered nodes 1,2,3, and $T_2$ contains the grey ones (D,E,F). None of the nodes depicted would be
included in $\tilde{T_1}$ or $\tilde{T_2}$. The latter sets would contain all the remaining nodes from the two graphs.</p>
<p>Regarding the computation of these sets, it&rsquo;s not practical to use the brute force method and iterate over all nodes in
every step of the algorithm to find the desired nodes and compute $T_i$ and $\tilde{T_i}$. We use the following
observations to implement an incremental computation of $T_i$ and $\tilde{T_i}$ and make VF2++ more efficient.</p>
<ul>
<li>$T_i$ is empty in the beginning, since there are no mapped nodes ($m=\varnothing$) and therefore no neighbors of
mapped nodes.</li>
<li>$\tilde{T_i}$ initially contains all the nodes from graph $G_i, i=1,2$ which can be realized directly from the
notation if we consider both $m$ and $T_1$ empty sets.</li>
<li>Every step of the algorithm either adds one node $u$ to the mapping or pops one from it.</li>
</ul>
<p>We can conclude that in every step, $T_i$ and $\tilde{T_i}$ can be incrementally updated. This method avoids a ton of
redundant operations and results in significant performance improvement.</p>
<p><img src="/networkx/vf2pp/node-ordering-ti-updating/acceleration.png" alt="Performance comparison between brute force Ti computing and incremental updating."></p>
<p>The above graph shows the difference in performance between using the exhaustive brute force and incrementally updating
$T_i$ and $\tilde{T_i}$. The graph used to obtain these measurements was a regular
<a href="https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model">GNP Graph</a> with a probability for an edge equal to
$0.7$. It can clearly be seen that execution time of the brute force
method increases much more rapidly with the number of nodes/edges than
the incremental update method, as expected.
The brute force method looks like this:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">compute_Ti</span><span class="p">(</span><span class="n">G1</span><span class="p">,</span> <span class="n">G2</span><span class="p">,</span> <span class="n">mapping</span><span class="p">,</span> <span class="n">reverse_mapping</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">T1</span> <span class="o">=</span> <span class="p">{</span><span class="n">nbr</span> <span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">mapping</span> <span class="k">for</span> <span class="n">nbr</span> <span class="ow">in</span> <span class="n">G1</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="k">if</span> <span class="n">nbr</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mapping</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">T2</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">nbr</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">reverse_mapping</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">nbr</span> <span class="ow">in</span> <span class="n">G2</span><span class="p">[</span><span class="n">node</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">nbr</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">reverse_mapping</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">T1_out</span> <span class="o">=</span> <span class="p">{</span><span class="n">n1</span> <span class="k">for</span> <span class="n">n1</span> <span class="ow">in</span> <span class="n">G1</span><span class="o">.</span><span class="n">nodes</span><span class="p">()</span> <span class="k">if</span> <span class="n">n1</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mapping</span> <span class="ow">and</span> <span class="n">n1</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">T1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">T2_out</span> <span class="o">=</span> <span class="p">{</span><span class="n">n2</span> <span class="k">for</span> <span class="n">n2</span> <span class="ow">in</span> <span class="n">G2</span><span class="o">.</span><span class="n">nodes</span><span class="p">()</span> <span class="k">if</span> <span class="n">n2</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">reverse_mapping</span> <span class="ow">and</span> <span class="n">n2</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">T2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">T1</span><span class="p">,</span> <span class="n">T2</span><span class="p">,</span> <span class="n">T1_out</span><span class="p">,</span> <span class="n">T2_out</span></span></span></code></pre>
</div>
<p>If we assume that G1 and G2 have the same number of nodes (N), the average number of nodes in the mapping is $N_m$, and
the average node degree of the graphs is $D$, then the time complexity of this function is:</p>
<p>$$O(2N_mD + 2N) = O(N_mD + N)$$</p>
<p>in which we have excluded the lookup times in $T_i$, $mapping$ and $reverse\_mapping$ as they are all $O(1)$. Our
incremental method works like this:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">update_Tinout</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">G1</span><span class="p">,</span> <span class="n">G2</span><span class="p">,</span> <span class="n">T1</span><span class="p">,</span> <span class="n">T2</span><span class="p">,</span> <span class="n">T1_out</span><span class="p">,</span> <span class="n">T2_out</span><span class="p">,</span> <span class="n">new_node1</span><span class="p">,</span> <span class="n">new_node2</span><span class="p">,</span> <span class="n">mapping</span><span class="p">,</span> <span class="n">reverse_mapping</span>
</span></span><span class="line"><span class="cl"><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># This function should be called right after the feasibility is established and node1 is mapped to node2.</span>
</span></span><span class="line"><span class="cl">    <span class="n">uncovered_neighbors_G1</span> <span class="o">=</span> <span class="p">{</span><span class="n">nbr</span> <span class="k">for</span> <span class="n">nbr</span> <span class="ow">in</span> <span class="n">G1</span><span class="p">[</span><span class="n">new_node1</span><span class="p">]</span> <span class="k">if</span> <span class="n">nbr</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mapping</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">uncovered_neighbors_G2</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">nbr</span> <span class="k">for</span> <span class="n">nbr</span> <span class="ow">in</span> <span class="n">G2</span><span class="p">[</span><span class="n">new_node2</span><span class="p">]</span> <span class="k">if</span> <span class="n">nbr</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">reverse_mapping</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Add the uncovered neighbors of node1 and node2 in T1 and T2 respectively</span>
</span></span><span class="line"><span class="cl">    <span class="n">T1</span><span class="o">.</span><span class="n">discard</span><span class="p">(</span><span class="n">new_node1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">T2</span><span class="o">.</span><span class="n">discard</span><span class="p">(</span><span class="n">new_node2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">T1</span> <span class="o">=</span> <span class="n">T1</span><span class="o">.</span><span class="n">union</span><span class="p">(</span><span class="n">uncovered_neighbors_G1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">T2</span> <span class="o">=</span> <span class="n">T2</span><span class="o">.</span><span class="n">union</span><span class="p">(</span><span class="n">uncovered_neighbors_G2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># todo: maybe check this twice just to make sure</span>
</span></span><span class="line"><span class="cl">    <span class="n">T1_out</span><span class="o">.</span><span class="n">discard</span><span class="p">(</span><span class="n">new_node1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">T2_out</span><span class="o">.</span><span class="n">discard</span><span class="p">(</span><span class="n">new_node2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">T1_out</span> <span class="o">=</span> <span class="n">T1_out</span> <span class="o">-</span> <span class="n">uncovered_neighbors_G1</span>
</span></span><span class="line"><span class="cl">    <span class="n">T2_out</span> <span class="o">=</span> <span class="n">T2_out</span> <span class="o">-</span> <span class="n">uncovered_neighbors_G2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">T1</span><span class="p">,</span> <span class="n">T2</span><span class="p">,</span> <span class="n">T1_out</span><span class="p">,</span> <span class="n">T2_out</span></span></span></code></pre>
</div>
<p>which based on the previous notation, is:</p>
<p>$$O(2D + 2(D + M_{T_1}) + 2D) = O(D + M_{T_1})$$</p>
<p>where $M_{T_1}$ is the expected (average) number of elements in $T_1$.</p>
<p>Certainly, the complexity is much better in this
case, as $D$ and $M_{T_1}$ are significantly smaller than $N_mD$ and $N$.</p>
<p>In this post we investigated how node ordering works at a high level, and also
how we are able to calculate some important parameters so that the space and
time complexity are reduced.
The next post will continue with examining two more significant components of
the VF2++ algorithm: the <strong>candidate node pair selection</strong> and the
<strong>cutting/consistency</strong> rules that decide when the mapping should or shouldn&rsquo;t
be extended.
Stay tuned!</p>
]]></content>
            
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="taxonomy:Tags" term="gsoc" label="gsoc" />
                             
                                <category scheme="taxonomy:Tags" term="networkx" label="networkx" />
                             
                                <category scheme="taxonomy:Tags" term="vf2&#43;&#43;" label="vf2&#43;&#43;" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[GSoC 2022: NetworkX VF2++ Implementation]]></title>
            <link href="https://blog.scientific-python.org/networkx/vf2pp/gsoc-2022/?utm_source=atom_feed" rel="alternate" type="text/html" />
            
                <link href="https://blog.scientific-python.org/networkx/atsp/my-summer-of-code-2021/?utm_source=atom_feed" rel="related" type="text/html" title="My Summer of Code 2021" />
                <link href="https://blog.scientific-python.org/networkx/atsp/completing-the-asadpour-algorithm/?utm_source=atom_feed" rel="related" type="text/html" title="Completing the Asadpour Algorithm" />
                <link href="https://blog.scientific-python.org/networkx/atsp/looking-at-the-big-picture/?utm_source=atom_feed" rel="related" type="text/html" title="Looking at the Big Picture" />
                <link href="https://blog.scientific-python.org/networkx/atsp/sampling-a-spanning-tree/?utm_source=atom_feed" rel="related" type="text/html" title="Sampling a Spanning Tree" />
                <link href="https://blog.scientific-python.org/networkx/atsp/preliminaries-for-sampling-a-spanning-tree/?utm_source=atom_feed" rel="related" type="text/html" title="Preliminaries for Sampling a Spanning Tree" />
            
                <id>https://blog.scientific-python.org/networkx/vf2pp/gsoc-2022/</id>
            
            
            <published>2022-06-09T00:00:00+00:00</published>
            <updated>2022-06-09T00:00:00+00:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>This is the first blog of my GSoC-2022 journey. It includes general information about me, and a superficial description of the project.</blockquote><h2 id="intro">Intro<a class="headerlink" href="#intro" title="Link to this heading">#</a></h2>
<p>I got accepted as a <strong>GSoC</strong> contributor, and I am so excited to spend the summer working on such an incredibly
interesting project. The mentors are very welcoming, communicative, fun to be around, and I really look forward to
collaborating with them. My application for GSoC 2022 can
be found <a href="https://summerofcode.withgoogle.com/programs/2022/projects/V1hY83XG">here</a>.</p>
<h2 id="about-me">About me<a class="headerlink" href="#about-me" title="Link to this heading">#</a></h2>
<p>My name is Konstantinos Petridis, and I am an <strong>Electrical Engineering</strong> student at the Aristotle University of
Thessaloniki. I am currently on my 5th year of studies, with a <strong>Major in Electronics &amp; Computer Science</strong>. Although a
wide range of scientific fields fascinate me, I have a strong passion for <strong>Computer Science</strong>, <strong>Physics</strong> and
<strong>Space</strong>. I love to study, learn new things and don&rsquo;t hesitate to express my curiosity by asking a bunch of questions
to the point of being annoying. You can find me on GitHub <a href="https://github.com/kpetridis24">@kpetridis24</a>.</p>
<h2 id="project">Project<a class="headerlink" href="#project" title="Link to this heading">#</a></h2>
<p>The project I&rsquo;ll be working on, is the implementation of <strong>VF2++</strong>, a state-of-the-art algorithm used for the
<a href="https://en.wikipedia.org/wiki/Graph_isomorphism"><strong>Graph Isomorphism</strong></a> problem, which lies in the
<a href="https://en.wikipedia.org/wiki/Complexity_class">complexity class</a> <a href="https://en.wikipedia.org/wiki/NP_%28complexity%29"><strong>NP</strong></a>.
The functionality of the algorithm is similar to a regular, but
more complex form of a
<a href="https://en.wikipedia.org/wiki/Depth-first_search"><strong>DFS</strong></a>, but performed on the possible solutions rather than the
graph nodes. In order to verify/reject the isomorphism between two graphs, we examine every possible candidate pair of
nodes
(one from the first and one from the second graph) and check whether going deeper into the DFS tree is feasible using
specific rules. In case of feasibility establishment, the DFS tree is expanded, investigating deeper pairs. When one
pair is not feasible, we go up the tree and follow a different branch, just like in a regular <strong>DFS</strong>. More details
about the algorithm can be found <a href="https://doi.org/10.1016/j.dam.2018.02.018">here</a>.</p>
<h2 id="motivation">Motivation<a class="headerlink" href="#motivation" title="Link to this heading">#</a></h2>
<p>The major reasons I chose this project emanate from both my love for <strong>Graph Theory</strong>, and the fascinating nature of
this individual project. The algorithm itself is so recent, that <strong>NetworkX</strong> is possibly going to hold one of the first
implementations of it. This might become a reference that is going to help to further develop and optimize future
implementations of the algorithm by other organisations. Regarding my personal gain, I will become more familiar with
the open source communities and their philosophy, I will collaborate with highly skilled individuals and cultivate a
significant amount of experience on researching, working as a team, getting feedback and help when needed, contributing
to an actual scientific library.</p>
]]></content>
            
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="taxonomy:Tags" term="gsoc" label="gsoc" />
                             
                                <category scheme="taxonomy:Tags" term="networkx" label="networkx" />
                             
                                <category scheme="taxonomy:Tags" term="vf2&#43;&#43;" label="vf2&#43;&#43;" />
                            
                        
                    
                
            
        </entry>
    
</feed>
