<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://andrey.abel.ai/</id>
  <title>Andrey blog</title>
  <updated>2026-10-24T00:27:09.872106+00:00</updated>
  <link href="https://andrey.abel.ai/"/>
  <link href="https://andrey.abel.ai/blog/atom.xml" rel="self"/>
  <generator uri="https://ablog.readthedocs.io/" version="0.11.13">ABlog</generator>
  <entry>
    <id>https://andrey.abel.ai/blog/20261023-andrey-0-8-0.html</id>
    <title>Nonlinear dependence</title>
    <updated>2026-10-23T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="nonlinear-dependence"&gt;

&lt;p&gt;A constraint-based method learns a graph from independence tests. Its result depends on the test.
The default test, partial correlation, detects only linear dependence. When an effect is nonlinear,
it can miss an edge or add a false one. In 0.8.0, you can use PC, FCI, GFCI, and CD-NOD with four
supported tests that detect nonlinear dependence: KCI, RCoT, CMIknn, and GPDC. Each runs faster than
the package we measured it against at every size we timed, at the same level of accuracy: PC with
RCoT runs more than 5x faster than causal-learn and more than 9x faster than pcalg.&lt;/p&gt;
&lt;section id="tl-dr"&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Four supported nonlinear tests: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;indep_test=&amp;quot;kci&amp;quot;&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;rcot&amp;quot;&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;cmiknn&amp;quot;&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;gpdc&amp;quot;&lt;/span&gt;&lt;/code&gt;, in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pc&lt;/span&gt;&lt;/code&gt;,
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;fci&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;gfci&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cdnod&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Faster than the reference package at every size we timed, at the same level of accuracy: PC with
RCoT runs 5.9x to 36x faster than causal-learn and 9.5x to 29x faster than pcalg.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;KCI and CMIknn also run on a CUDA GPU, with the same graphs as on the CPU.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="what-partial-correlation-misses"&gt;
&lt;h2&gt;What partial correlation misses&lt;/h2&gt;
&lt;p&gt;Partial correlation asks whether &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; rises or falls with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; in a straight line. Many effects do
not: a dose helps up to a point and then harms, a signal repeats, a cost grows with the square of a
size. Each panel below has 400 points with the same noise.&lt;/p&gt;
&lt;figure class="dp"&gt;&lt;table class="dp-table"&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;th scope="col"&gt;Partial correlation&lt;/th&gt;&lt;th scope="col"&gt;KCI&lt;/th&gt;&lt;th scope="col"&gt;RCoT&lt;/th&gt;&lt;th scope="col"&gt;CMIknn&lt;/th&gt;&lt;th scope="col"&gt;GPDC&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th scope="row"&gt;&lt;svg viewBox="0 0 180 110" role="img" aria-label="y = x: 400 points"&gt;&lt;g class="dp-dots"&gt;&lt;circle cx="112.5" cy="45.1" r="1.6"/&gt;&lt;circle cx="52.2" cy="73.5" r="1.6"/&gt;&lt;circle cx="14.7" cy="81.7" r="1.6"/&gt;&lt;circle cx="10.7" cy="86.5" r="1.6"/&gt;&lt;circle cx="141.4" cy="20.1" r="1.6"/&gt;&lt;circle cx="157.7" cy="32.0" r="1.6"/&gt;&lt;circle cx="107.5" cy="43.2" r="1.6"/&gt;&lt;circle cx="127.6" cy="34.1" r="1.6"/&gt;&lt;circle cx="97.2" cy="52.7" r="1.6"/&gt;&lt;circle cx="161.4" cy="18.7" r="1.6"/&gt;&lt;circle cx="141.8" cy="41.2" r="1.6"/&gt;&lt;circle cx="8.4" cy="73.3" r="1.6"/&gt;&lt;circle cx="148.6" cy="37.6" r="1.6"/&gt;&lt;circle cx="13.5" cy="79.5" r="1.6"/&gt;&lt;circle cx="127.7" cy="45.0" r="1.6"/&gt;&lt;circle cx="36.8" cy="67.9" r="1.6"/&gt;&lt;circle cx="149.6" cy="30.5" r="1.6"/&gt;&lt;circle cx="96.8" cy="49.2" r="1.6"/&gt;&lt;circle cx="57.2" cy="66.9" r="1.6"/&gt;&lt;circle cx="77.3" cy="51.0" r="1.6"/&gt;&lt;circle cx="12.6" cy="88.6" r="1.6"/&gt;&lt;circle cx="28.4" cy="100.4" r="1.6"/&gt;&lt;circle cx="118.0" cy="46.6" r="1.6"/&gt;&lt;circle cx="114.1" cy="41.7" r="1.6"/&gt;&lt;circle cx="108.9" cy="48.3" r="1.6"/&gt;&lt;circle cx="70.9" cy="56.0" r="1.6"/&gt;&lt;circle cx="171.5" cy="11.3" r="1.6"/&gt;&lt;circle cx="168.9" cy="20.6" r="1.6"/&gt;&lt;circle cx="120.4" cy="50.7" r="1.6"/&gt;&lt;circle cx="114.7" cy="33.0" r="1.6"/&gt;&lt;circle cx="120.9" cy="32.5" r="1.6"/&gt;&lt;circle cx="71.8" cy="63.1" r="1.6"/&gt;&lt;circle cx="30.2" cy="64.1" r="1.6"/&gt;&lt;circle cx="126.3" cy="40.2" r="1.6"/&gt;&lt;circle cx="94.2" cy="59.5" r="1.6"/&gt;&lt;circle cx="58.9" cy="67.6" r="1.6"/&gt;&lt;circle cx="87.7" cy="46.7" r="1.6"/&gt;&lt;circle cx="153.9" cy="32.5" r="1.6"/&gt;&lt;circle cx="161.2" cy="9.2" r="1.6"/&gt;&lt;circle cx="66.7" cy="69.5" r="1.6"/&gt;&lt;circle cx="101.7" cy="41.6" r="1.6"/&gt;&lt;circle cx="60.8" cy="61.9" r="1.6"/&gt;&lt;circle cx="105.5" cy="47.7" r="1.6"/&gt;&lt;circle cx="63.4" cy="57.0" r="1.6"/&gt;&lt;circle cx="72.2" cy="71.4" r="1.6"/&gt;&lt;circle cx="154.0" cy="16.4" r="1.6"/&gt;&lt;circle cx="45.3" cy="72.8" r="1.6"/&gt;&lt;circle cx="110.2" cy="48.4" r="1.6"/&gt;&lt;circle cx="21.8" cy="77.2" r="1.6"/&gt;&lt;circle cx="144.6" cy="22.5" r="1.6"/&gt;&lt;circle cx="137.1" cy="27.7" r="1.6"/&gt;&lt;circle cx="47.3" cy="57.5" r="1.6"/&gt;&lt;circle cx="151.7" cy="19.3" r="1.6"/&gt;&lt;circle cx="17.6" cy="75.2" r="1.6"/&gt;&lt;circle cx="63.1" cy="68.5" r="1.6"/&gt;&lt;circle cx="32.6" cy="77.0" r="1.6"/&gt;&lt;circle cx="81.9" cy="52.8" r="1.6"/&gt;&lt;circle cx="138.6" cy="32.6" r="1.6"/&gt;&lt;circle cx="45.8" cy="70.0" r="1.6"/&gt;&lt;circle cx="16.5" cy="81.7" r="1.6"/&gt;&lt;circle cx="74.3" cy="54.0" r="1.6"/&gt;&lt;circle cx="40.6" cy="75.1" r="1.6"/&gt;&lt;circle cx="22.9" cy="84.4" r="1.6"/&gt;&lt;circle cx="103.2" cy="53.4" r="1.6"/&gt;&lt;circle cx="57.0" cy="73.6" r="1.6"/&gt;&lt;circle cx="118.2" cy="33.0" r="1.6"/&gt;&lt;circle cx="40.7" cy="74.9" r="1.6"/&gt;&lt;circle cx="162.5" cy="16.8" r="1.6"/&gt;&lt;circle cx="67.9" cy="71.8" r="1.6"/&gt;&lt;circle cx="25.3" cy="94.5" r="1.6"/&gt;&lt;circle cx="111.2" cy="51.6" r="1.6"/&gt;&lt;circle cx="160.1" cy="15.3" r="1.6"/&gt;&lt;circle cx="80.2" cy="50.0" r="1.6"/&gt;&lt;circle cx="164.6" cy="19.1" r="1.6"/&gt;&lt;circle cx="90.0" cy="58.9" r="1.6"/&gt;&lt;circle cx="77.7" cy="59.4" r="1.6"/&gt;&lt;circle cx="109.7" cy="46.9" r="1.6"/&gt;&lt;circle cx="171.2" cy="21.0" r="1.6"/&gt;&lt;circle cx="163.6" cy="39.4" r="1.6"/&gt;&lt;circle cx="83.4" cy="62.1" r="1.6"/&gt;&lt;circle cx="132.3" cy="36.5" r="1.6"/&gt;&lt;circle cx="89.6" cy="42.8" r="1.6"/&gt;&lt;circle cx="94.8" cy="50.1" r="1.6"/&gt;&lt;circle cx="136.9" cy="23.4" r="1.6"/&gt;&lt;circle cx="76.0" cy="62.0" r="1.6"/&gt;&lt;circle cx="128.5" cy="38.6" r="1.6"/&gt;&lt;circle cx="124.6" cy="65.8" r="1.6"/&gt;&lt;circle cx="160.9" cy="19.7" r="1.6"/&gt;&lt;circle cx="26.8" cy="76.4" r="1.6"/&gt;&lt;circle cx="127.6" cy="24.8" r="1.6"/&gt;&lt;circle cx="160.1" cy="26.7" r="1.6"/&gt;&lt;circle cx="166.7" cy="19.8" r="1.6"/&gt;&lt;circle cx="10.4" cy="92.2" r="1.6"/&gt;&lt;circle cx="149.6" cy="36.0" r="1.6"/&gt;&lt;circle cx="168.9" cy="24.5" r="1.6"/&gt;&lt;circle cx="165.0" cy="23.6" r="1.6"/&gt;&lt;circle cx="32.4" cy="68.4" r="1.6"/&gt;&lt;circle cx="167.5" cy="20.1" r="1.6"/&gt;&lt;circle cx="153.9" cy="31.5" r="1.6"/&gt;&lt;circle cx="142.9" cy="29.7" r="1.6"/&gt;&lt;circle cx="86.7" cy="53.1" r="1.6"/&gt;&lt;circle cx="46.1" cy="76.8" r="1.6"/&gt;&lt;circle cx="139.5" cy="24.1" r="1.6"/&gt;&lt;circle cx="159.5" cy="36.8" r="1.6"/&gt;&lt;circle cx="51.6" cy="71.1" r="1.6"/&gt;&lt;circle cx="96.4" cy="45.9" r="1.6"/&gt;&lt;circle cx="80.6" cy="66.7" r="1.6"/&gt;&lt;circle cx="160.7" cy="20.5" r="1.6"/&gt;&lt;circle cx="14.6" cy="76.4" r="1.6"/&gt;&lt;circle cx="128.0" cy="33.8" r="1.6"/&gt;&lt;circle cx="108.8" cy="57.1" r="1.6"/&gt;&lt;circle cx="12.7" cy="91.4" r="1.6"/&gt;&lt;circle cx="126.0" cy="29.3" r="1.6"/&gt;&lt;circle cx="10.6" cy="85.1" r="1.6"/&gt;&lt;circle cx="132.3" cy="35.2" r="1.6"/&gt;&lt;circle cx="92.1" cy="49.3" r="1.6"/&gt;&lt;circle cx="160.4" cy="18.1" r="1.6"/&gt;&lt;circle cx="18.8" cy="88.6" r="1.6"/&gt;&lt;circle cx="146.0" cy="31.4" r="1.6"/&gt;&lt;circle cx="18.9" cy="82.6" r="1.6"/&gt;&lt;circle cx="64.5" cy="68.0" r="1.6"/&gt;&lt;circle cx="78.6" cy="59.1" r="1.6"/&gt;&lt;circle cx="166.4" cy="11.5" r="1.6"/&gt;&lt;circle cx="100.2" cy="55.7" r="1.6"/&gt;&lt;circle cx="50.5" cy="56.7" r="1.6"/&gt;&lt;circle cx="47.6" cy="58.2" r="1.6"/&gt;&lt;circle cx="153.7" cy="38.1" r="1.6"/&gt;&lt;circle cx="45.0" cy="73.5" r="1.6"/&gt;&lt;circle cx="28.4" cy="77.2" r="1.6"/&gt;&lt;circle cx="55.3" cy="73.4" r="1.6"/&gt;&lt;circle cx="104.1" cy="52.4" r="1.6"/&gt;&lt;circle cx="98.9" cy="51.1" r="1.6"/&gt;&lt;circle cx="140.8" cy="26.4" r="1.6"/&gt;&lt;circle cx="99.9" cy="52.6" r="1.6"/&gt;&lt;circle cx="55.3" cy="55.3" r="1.6"/&gt;&lt;circle cx="75.7" cy="57.3" r="1.6"/&gt;&lt;circle cx="142.2" cy="31.7" r="1.6"/&gt;&lt;circle cx="110.7" cy="34.2" r="1.6"/&gt;&lt;circle cx="165.3" cy="16.7" r="1.6"/&gt;&lt;circle cx="68.6" cy="59.8" r="1.6"/&gt;&lt;circle cx="98.6" cy="51.9" r="1.6"/&gt;&lt;circle cx="105.4" cy="34.0" r="1.6"/&gt;&lt;circle cx="147.1" cy="23.2" r="1.6"/&gt;&lt;circle cx="31.9" cy="79.5" r="1.6"/&gt;&lt;circle cx="74.7" cy="70.9" r="1.6"/&gt;&lt;circle cx="157.2" cy="21.9" r="1.6"/&gt;&lt;circle cx="15.1" cy="93.3" r="1.6"/&gt;&lt;circle cx="142.9" cy="42.7" r="1.6"/&gt;&lt;circle cx="76.1" cy="61.1" r="1.6"/&gt;&lt;circle cx="144.1" cy="28.2" r="1.6"/&gt;&lt;circle cx="9.6" cy="78.6" r="1.6"/&gt;&lt;circle cx="67.9" cy="60.7" r="1.6"/&gt;&lt;circle cx="20.9" cy="77.1" r="1.6"/&gt;&lt;circle cx="115.0" cy="51.2" r="1.6"/&gt;&lt;circle cx="52.9" cy="69.3" r="1.6"/&gt;&lt;circle cx="123.2" cy="38.2" r="1.6"/&gt;&lt;circle cx="162.8" cy="16.1" r="1.6"/&gt;&lt;circle cx="28.8" cy="78.6" r="1.6"/&gt;&lt;circle cx="149.8" cy="22.1" r="1.6"/&gt;&lt;circle cx="17.8" cy="87.7" r="1.6"/&gt;&lt;circle cx="70.4" cy="59.1" r="1.6"/&gt;&lt;circle cx="78.5" cy="55.3" r="1.6"/&gt;&lt;circle cx="88.2" cy="62.8" r="1.6"/&gt;&lt;circle cx="168.1" cy="18.6" r="1.6"/&gt;&lt;circle cx="135.2" cy="36.2" r="1.6"/&gt;&lt;circle cx="58.7" cy="80.0" r="1.6"/&gt;&lt;circle cx="52.3" cy="62.7" r="1.6"/&gt;&lt;circle cx="149.6" cy="30.3" r="1.6"/&gt;&lt;circle cx="152.5" cy="32.5" r="1.6"/&gt;&lt;circle cx="91.8" cy="55.2" r="1.6"/&gt;&lt;circle cx="64.5" cy="63.2" r="1.6"/&gt;&lt;circle cx="171.2" cy="8.0" r="1.6"/&gt;&lt;circle cx="59.8" cy="67.3" r="1.6"/&gt;&lt;circle cx="38.0" cy="72.2" r="1.6"/&gt;&lt;circle cx="152.3" cy="17.0" r="1.6"/&gt;&lt;circle cx="141.2" cy="27.6" r="1.6"/&gt;&lt;circle cx="117.5" cy="34.0" r="1.6"/&gt;&lt;circle cx="165.2" cy="24.5" r="1.6"/&gt;&lt;circle cx="159.8" cy="18.0" r="1.6"/&gt;&lt;circle cx="130.7" cy="36.3" r="1.6"/&gt;&lt;circle cx="149.2" cy="20.4" r="1.6"/&gt;&lt;circle cx="48.5" cy="78.0" r="1.6"/&gt;&lt;circle cx="31.2" cy="83.9" r="1.6"/&gt;&lt;circle cx="117.9" cy="32.4" r="1.6"/&gt;&lt;circle cx="125.2" cy="39.6" r="1.6"/&gt;&lt;circle cx="35.4" cy="79.1" r="1.6"/&gt;&lt;circle cx="72.9" cy="60.0" r="1.6"/&gt;&lt;circle cx="157.3" cy="29.3" r="1.6"/&gt;&lt;circle cx="100.1" cy="39.6" r="1.6"/&gt;&lt;circle cx="102.8" cy="56.5" r="1.6"/&gt;&lt;circle cx="39.8" cy="75.8" r="1.6"/&gt;&lt;circle cx="94.3" cy="49.0" r="1.6"/&gt;&lt;circle cx="93.8" cy="52.3" r="1.6"/&gt;&lt;circle cx="22.6" cy="87.7" r="1.6"/&gt;&lt;circle cx="169.0" cy="25.6" r="1.6"/&gt;&lt;circle cx="101.7" cy="45.3" r="1.6"/&gt;&lt;circle cx="9.1" cy="95.1" r="1.6"/&gt;&lt;circle cx="134.7" cy="29.6" r="1.6"/&gt;&lt;circle cx="168.4" cy="30.4" r="1.6"/&gt;&lt;circle cx="104.7" cy="50.8" r="1.6"/&gt;&lt;circle cx="60.4" cy="65.6" r="1.6"/&gt;&lt;circle cx="38.8" cy="83.9" r="1.6"/&gt;&lt;circle cx="118.3" cy="36.6" r="1.6"/&gt;&lt;circle cx="40.0" cy="74.8" r="1.6"/&gt;&lt;circle cx="102.7" cy="55.1" r="1.6"/&gt;&lt;circle cx="106.8" cy="56.7" r="1.6"/&gt;&lt;circle cx="165.8" cy="31.8" r="1.6"/&gt;&lt;circle cx="19.9" cy="82.9" r="1.6"/&gt;&lt;circle cx="90.0" cy="61.4" r="1.6"/&gt;&lt;circle cx="130.0" cy="45.7" r="1.6"/&gt;&lt;circle cx="37.1" cy="77.5" r="1.6"/&gt;&lt;circle cx="71.6" cy="45.1" r="1.6"/&gt;&lt;circle cx="18.3" cy="81.9" r="1.6"/&gt;&lt;circle cx="127.0" cy="32.1" r="1.6"/&gt;&lt;circle cx="22.4" cy="80.7" r="1.6"/&gt;&lt;circle cx="72.8" cy="55.1" r="1.6"/&gt;&lt;circle cx="151.3" cy="36.5" r="1.6"/&gt;&lt;circle cx="85.5" cy="58.2" r="1.6"/&gt;&lt;circle cx="157.7" cy="22.5" r="1.6"/&gt;&lt;circle cx="133.6" cy="34.7" r="1.6"/&gt;&lt;circle cx="158.1" cy="25.5" r="1.6"/&gt;&lt;circle cx="28.9" cy="84.0" r="1.6"/&gt;&lt;circle cx="20.1" cy="85.0" r="1.6"/&gt;&lt;circle cx="19.5" cy="88.8" r="1.6"/&gt;&lt;circle cx="150.5" cy="44.4" r="1.6"/&gt;&lt;circle cx="112.0" cy="52.2" r="1.6"/&gt;&lt;circle cx="89.4" cy="50.2" r="1.6"/&gt;&lt;circle cx="34.8" cy="65.9" r="1.6"/&gt;&lt;circle cx="118.5" cy="28.4" r="1.6"/&gt;&lt;circle cx="60.2" cy="65.3" r="1.6"/&gt;&lt;circle cx="124.6" cy="32.2" r="1.6"/&gt;&lt;circle cx="83.5" cy="49.7" r="1.6"/&gt;&lt;circle cx="91.2" cy="57.6" r="1.6"/&gt;&lt;circle cx="137.5" cy="44.9" r="1.6"/&gt;&lt;circle cx="23.2" cy="81.6" r="1.6"/&gt;&lt;circle cx="102.9" cy="60.1" r="1.6"/&gt;&lt;circle cx="40.3" cy="76.7" r="1.6"/&gt;&lt;circle cx="140.5" cy="27.4" r="1.6"/&gt;&lt;circle cx="88.2" cy="64.0" r="1.6"/&gt;&lt;circle cx="170.1" cy="18.8" r="1.6"/&gt;&lt;circle cx="38.0" cy="66.8" r="1.6"/&gt;&lt;circle cx="165.9" cy="18.3" r="1.6"/&gt;&lt;circle cx="139.4" cy="28.5" r="1.6"/&gt;&lt;circle cx="86.9" cy="48.2" r="1.6"/&gt;&lt;circle cx="141.4" cy="19.1" r="1.6"/&gt;&lt;circle cx="106.9" cy="45.0" r="1.6"/&gt;&lt;circle cx="115.4" cy="33.8" r="1.6"/&gt;&lt;circle cx="157.8" cy="24.7" r="1.6"/&gt;&lt;circle cx="18.7" cy="87.6" r="1.6"/&gt;&lt;circle cx="144.9" cy="27.6" r="1.6"/&gt;&lt;circle cx="70.6" cy="65.8" r="1.6"/&gt;&lt;circle cx="61.4" cy="69.5" r="1.6"/&gt;&lt;circle cx="171.0" cy="22.9" r="1.6"/&gt;&lt;circle cx="136.1" cy="49.6" r="1.6"/&gt;&lt;circle cx="87.6" cy="53.5" r="1.6"/&gt;&lt;circle cx="77.3" cy="67.5" r="1.6"/&gt;&lt;circle cx="151.9" cy="27.5" r="1.6"/&gt;&lt;circle cx="22.2" cy="72.1" r="1.6"/&gt;&lt;circle cx="124.2" cy="42.3" r="1.6"/&gt;&lt;circle cx="137.4" cy="36.8" r="1.6"/&gt;&lt;circle cx="139.1" cy="22.7" r="1.6"/&gt;&lt;circle cx="60.9" cy="61.9" r="1.6"/&gt;&lt;circle cx="138.6" cy="25.6" r="1.6"/&gt;&lt;circle cx="45.0" cy="75.0" r="1.6"/&gt;&lt;circle cx="67.4" cy="57.7" r="1.6"/&gt;&lt;circle cx="76.5" cy="63.9" r="1.6"/&gt;&lt;circle cx="96.8" cy="55.1" r="1.6"/&gt;&lt;circle cx="26.5" cy="76.2" r="1.6"/&gt;&lt;circle cx="74.7" cy="64.0" r="1.6"/&gt;&lt;circle cx="8.0" cy="82.9" r="1.6"/&gt;&lt;circle cx="130.1" cy="19.4" r="1.6"/&gt;&lt;circle cx="147.7" cy="40.4" r="1.6"/&gt;&lt;circle cx="30.8" cy="83.8" r="1.6"/&gt;&lt;circle cx="123.4" cy="31.1" r="1.6"/&gt;&lt;circle cx="142.7" cy="31.8" r="1.6"/&gt;&lt;circle cx="169.0" cy="11.3" r="1.6"/&gt;&lt;circle cx="146.4" cy="36.2" r="1.6"/&gt;&lt;circle cx="77.6" cy="56.3" r="1.6"/&gt;&lt;circle cx="168.7" cy="20.7" r="1.6"/&gt;&lt;circle cx="167.7" cy="19.1" r="1.6"/&gt;&lt;circle cx="90.6" cy="50.7" r="1.6"/&gt;&lt;circle cx="131.6" cy="44.0" r="1.6"/&gt;&lt;circle cx="157.9" cy="31.8" r="1.6"/&gt;&lt;circle cx="86.1" cy="45.1" r="1.6"/&gt;&lt;circle cx="149.7" cy="25.7" r="1.6"/&gt;&lt;circle cx="123.1" cy="38.4" r="1.6"/&gt;&lt;circle cx="56.2" cy="68.0" r="1.6"/&gt;&lt;circle cx="133.9" cy="32.0" r="1.6"/&gt;&lt;circle cx="101.6" cy="56.4" r="1.6"/&gt;&lt;circle cx="23.4" cy="88.7" r="1.6"/&gt;&lt;circle cx="72.2" cy="51.7" r="1.6"/&gt;&lt;circle cx="20.1" cy="82.1" r="1.6"/&gt;&lt;circle cx="86.1" cy="49.0" r="1.6"/&gt;&lt;circle cx="78.3" cy="62.5" r="1.6"/&gt;&lt;circle cx="77.5" cy="49.0" r="1.6"/&gt;&lt;circle cx="104.2" cy="44.8" r="1.6"/&gt;&lt;circle cx="28.1" cy="76.3" r="1.6"/&gt;&lt;circle cx="161.1" cy="19.0" r="1.6"/&gt;&lt;circle cx="120.2" cy="45.9" r="1.6"/&gt;&lt;circle cx="143.1" cy="43.6" r="1.6"/&gt;&lt;circle cx="155.1" cy="33.0" r="1.6"/&gt;&lt;circle cx="103.7" cy="36.0" r="1.6"/&gt;&lt;circle cx="14.6" cy="76.4" r="1.6"/&gt;&lt;circle cx="124.7" cy="40.9" r="1.6"/&gt;&lt;circle cx="101.3" cy="50.5" r="1.6"/&gt;&lt;circle cx="143.5" cy="23.1" r="1.6"/&gt;&lt;circle cx="95.3" cy="52.2" r="1.6"/&gt;&lt;circle cx="141.4" cy="40.4" r="1.6"/&gt;&lt;circle cx="171.5" cy="9.6" r="1.6"/&gt;&lt;circle cx="65.5" cy="60.4" r="1.6"/&gt;&lt;circle cx="36.0" cy="94.0" r="1.6"/&gt;&lt;circle cx="72.2" cy="63.0" r="1.6"/&gt;&lt;circle cx="131.5" cy="34.5" r="1.6"/&gt;&lt;circle cx="80.0" cy="54.1" r="1.6"/&gt;&lt;circle cx="104.5" cy="46.0" r="1.6"/&gt;&lt;circle cx="28.9" cy="83.8" r="1.6"/&gt;&lt;circle cx="127.1" cy="38.2" r="1.6"/&gt;&lt;circle cx="53.9" cy="66.6" r="1.6"/&gt;&lt;circle cx="39.3" cy="76.8" r="1.6"/&gt;&lt;circle cx="149.5" cy="30.4" r="1.6"/&gt;&lt;circle cx="100.6" cy="40.0" r="1.6"/&gt;&lt;circle cx="87.5" cy="61.0" r="1.6"/&gt;&lt;circle cx="155.4" cy="27.8" r="1.6"/&gt;&lt;circle cx="22.1" cy="96.5" r="1.6"/&gt;&lt;circle cx="122.2" cy="35.7" r="1.6"/&gt;&lt;circle cx="61.8" cy="59.5" r="1.6"/&gt;&lt;circle cx="36.8" cy="72.2" r="1.6"/&gt;&lt;circle cx="118.7" cy="34.6" r="1.6"/&gt;&lt;circle cx="67.5" cy="59.8" r="1.6"/&gt;&lt;circle cx="62.1" cy="62.8" r="1.6"/&gt;&lt;circle cx="162.8" cy="18.8" r="1.6"/&gt;&lt;circle cx="40.7" cy="76.2" r="1.6"/&gt;&lt;circle cx="92.0" cy="44.1" r="1.6"/&gt;&lt;circle cx="11.9" cy="89.1" r="1.6"/&gt;&lt;circle cx="34.8" cy="87.1" r="1.6"/&gt;&lt;circle cx="152.9" cy="20.4" r="1.6"/&gt;&lt;circle cx="137.4" cy="20.0" r="1.6"/&gt;&lt;circle cx="99.3" cy="56.0" r="1.6"/&gt;&lt;circle cx="44.5" cy="73.4" r="1.6"/&gt;&lt;circle cx="99.5" cy="54.0" r="1.6"/&gt;&lt;circle cx="10.0" cy="90.1" r="1.6"/&gt;&lt;circle cx="124.9" cy="38.0" r="1.6"/&gt;&lt;circle cx="125.5" cy="46.8" r="1.6"/&gt;&lt;circle cx="114.0" cy="45.0" r="1.6"/&gt;&lt;circle cx="108.3" cy="55.7" r="1.6"/&gt;&lt;circle cx="20.1" cy="87.1" r="1.6"/&gt;&lt;circle cx="48.4" cy="79.3" r="1.6"/&gt;&lt;circle cx="102.2" cy="55.5" r="1.6"/&gt;&lt;circle cx="72.6" cy="72.7" r="1.6"/&gt;&lt;circle cx="170.7" cy="10.2" r="1.6"/&gt;&lt;circle cx="159.5" cy="20.0" r="1.6"/&gt;&lt;circle cx="32.9" cy="91.1" r="1.6"/&gt;&lt;circle cx="104.8" cy="51.1" r="1.6"/&gt;&lt;circle cx="122.2" cy="44.2" r="1.6"/&gt;&lt;circle cx="30.4" cy="83.6" r="1.6"/&gt;&lt;circle cx="59.3" cy="76.5" r="1.6"/&gt;&lt;circle cx="125.4" cy="32.8" r="1.6"/&gt;&lt;circle cx="155.8" cy="27.9" r="1.6"/&gt;&lt;circle cx="64.0" cy="61.1" r="1.6"/&gt;&lt;circle cx="47.2" cy="67.9" r="1.6"/&gt;&lt;circle cx="142.8" cy="21.1" r="1.6"/&gt;&lt;circle cx="103.9" cy="60.0" r="1.6"/&gt;&lt;circle cx="86.2" cy="42.7" r="1.6"/&gt;&lt;circle cx="50.0" cy="61.5" r="1.6"/&gt;&lt;circle cx="19.9" cy="79.2" r="1.6"/&gt;&lt;circle cx="10.9" cy="70.3" r="1.6"/&gt;&lt;circle cx="103.1" cy="46.2" r="1.6"/&gt;&lt;circle cx="39.3" cy="69.1" r="1.6"/&gt;&lt;circle cx="168.0" cy="25.1" r="1.6"/&gt;&lt;circle cx="25.6" cy="72.8" r="1.6"/&gt;&lt;circle cx="82.1" cy="55.4" r="1.6"/&gt;&lt;circle cx="72.7" cy="63.8" r="1.6"/&gt;&lt;circle cx="46.1" cy="57.2" r="1.6"/&gt;&lt;circle cx="130.8" cy="38.0" r="1.6"/&gt;&lt;circle cx="113.6" cy="43.1" r="1.6"/&gt;&lt;circle cx="127.0" cy="38.8" r="1.6"/&gt;&lt;circle cx="21.6" cy="87.8" r="1.6"/&gt;&lt;circle cx="65.8" cy="59.9" r="1.6"/&gt;&lt;circle cx="93.3" cy="46.7" r="1.6"/&gt;&lt;circle cx="78.0" cy="55.9" r="1.6"/&gt;&lt;circle cx="14.7" cy="102.0" r="1.6"/&gt;&lt;circle cx="39.8" cy="67.6" r="1.6"/&gt;&lt;circle cx="163.0" cy="24.5" r="1.6"/&gt;&lt;circle cx="34.7" cy="85.4" r="1.6"/&gt;&lt;circle cx="147.7" cy="24.4" r="1.6"/&gt;&lt;circle cx="142.8" cy="26.7" r="1.6"/&gt;&lt;circle cx="72.2" cy="68.2" r="1.6"/&gt;&lt;circle cx="84.6" cy="38.3" r="1.6"/&gt;&lt;circle cx="143.1" cy="39.0" r="1.6"/&gt;&lt;circle cx="119.6" cy="52.7" r="1.6"/&gt;&lt;circle cx="145.3" cy="37.7" r="1.6"/&gt;&lt;circle cx="132.2" cy="25.2" r="1.6"/&gt;&lt;circle cx="121.4" cy="41.1" r="1.6"/&gt;&lt;circle cx="157.7" cy="26.9" r="1.6"/&gt;&lt;circle cx="142.9" cy="31.1" r="1.6"/&gt;&lt;circle cx="37.4" cy="79.9" r="1.6"/&gt;&lt;circle cx="130.7" cy="40.7" r="1.6"/&gt;&lt;circle cx="22.2" cy="86.7" r="1.6"/&gt;&lt;circle cx="77.8" cy="53.5" r="1.6"/&gt;&lt;circle cx="73.1" cy="53.3" r="1.6"/&gt;&lt;/g&gt;&lt;/svg&gt;&lt;span class="dp-name"&gt;y = x&lt;/span&gt;&lt;/th&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p = 0.002&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;&lt;svg viewBox="0 0 180 110" role="img" aria-label="y = cos(x): 400 points"&gt;&lt;g class="dp-dots"&gt;&lt;circle cx="112.5" cy="38.7" r="1.6"/&gt;&lt;circle cx="52.2" cy="55.6" r="1.6"/&gt;&lt;circle cx="14.7" cy="77.3" r="1.6"/&gt;&lt;circle cx="10.7" cy="82.3" r="1.6"/&gt;&lt;circle cx="141.4" cy="51.1" r="1.6"/&gt;&lt;circle cx="157.7" cy="87.0" r="1.6"/&gt;&lt;circle cx="107.5" cy="30.6" r="1.6"/&gt;&lt;circle cx="127.6" cy="46.9" r="1.6"/&gt;&lt;circle cx="97.2" cy="32.1" r="1.6"/&gt;&lt;circle cx="161.4" cy="75.1" r="1.6"/&gt;&lt;circle cx="141.8" cy="76.9" r="1.6"/&gt;&lt;circle cx="8.4" cy="65.8" r="1.6"/&gt;&lt;circle cx="148.6" cy="82.3" r="1.6"/&gt;&lt;circle cx="13.5" cy="74.5" r="1.6"/&gt;&lt;circle cx="127.7" cy="60.0" r="1.6"/&gt;&lt;circle cx="36.8" cy="56.5" r="1.6"/&gt;&lt;circle cx="149.6" cy="75.1" r="1.6"/&gt;&lt;circle cx="96.8" cy="27.6" r="1.6"/&gt;&lt;circle cx="57.2" cy="45.5" r="1.6"/&gt;&lt;circle cx="77.3" cy="21.9" r="1.6"/&gt;&lt;circle cx="12.6" cy="85.2" r="1.6"/&gt;&lt;circle cx="28.4" cy="98.3" r="1.6"/&gt;&lt;circle cx="118.0" cy="47.8" r="1.6"/&gt;&lt;circle cx="114.1" cy="36.7" r="1.6"/&gt;&lt;circle cx="108.9" cy="38.2" r="1.6"/&gt;&lt;circle cx="70.9" cy="28.2" r="1.6"/&gt;&lt;circle cx="171.5" cy="75.0" r="1.6"/&gt;&lt;circle cx="168.9" cy="84.1" r="1.6"/&gt;&lt;circle cx="120.4" cy="56.1" r="1.6"/&gt;&lt;circle cx="114.7" cy="27.1" r="1.6"/&gt;&lt;circle cx="120.9" cy="35.0" r="1.6"/&gt;&lt;circle cx="71.8" cy="36.6" r="1.6"/&gt;&lt;circle cx="30.2" cy="54.5" r="1.6"/&gt;&lt;circle cx="126.3" cy="52.2" r="1.6"/&gt;&lt;circle cx="94.2" cy="37.9" r="1.6"/&gt;&lt;circle cx="58.9" cy="45.6" r="1.6"/&gt;&lt;circle cx="87.7" cy="19.2" r="1.6"/&gt;&lt;circle cx="153.9" cy="83.1" r="1.6"/&gt;&lt;circle cx="161.2" cy="63.6" r="1.6"/&gt;&lt;circle cx="66.7" cy="45.1" r="1.6"/&gt;&lt;circle cx="101.7" cy="22.7" r="1.6"/&gt;&lt;circle cx="60.8" cy="38.0" r="1.6"/&gt;&lt;circle cx="105.5" cy="33.7" r="1.6"/&gt;&lt;circle cx="63.4" cy="31.2" r="1.6"/&gt;&lt;circle cx="72.2" cy="46.3" r="1.6"/&gt;&lt;circle cx="154.0" cy="64.0" r="1.6"/&gt;&lt;circle cx="45.3" cy="58.3" r="1.6"/&gt;&lt;circle cx="110.2" cy="39.9" r="1.6"/&gt;&lt;circle cx="21.8" cy="71.9" r="1.6"/&gt;&lt;circle cx="144.6" cy="58.6" r="1.6"/&gt;&lt;circle cx="137.1" cy="53.7" r="1.6"/&gt;&lt;circle cx="47.3" cy="39.1" r="1.6"/&gt;&lt;circle cx="151.7" cy="64.6" r="1.6"/&gt;&lt;circle cx="17.6" cy="69.8" r="1.6"/&gt;&lt;circle cx="63.1" cy="45.0" r="1.6"/&gt;&lt;circle cx="32.6" cy="69.1" r="1.6"/&gt;&lt;circle cx="81.9" cy="24.6" r="1.6"/&gt;&lt;circle cx="138.6" cy="61.8" r="1.6"/&gt;&lt;circle cx="45.8" cy="54.7" r="1.6"/&gt;&lt;circle cx="16.5" cy="77.4" r="1.6"/&gt;&lt;circle cx="74.3" cy="25.5" r="1.6"/&gt;&lt;circle cx="40.6" cy="63.4" r="1.6"/&gt;&lt;circle cx="22.9" cy="80.4" r="1.6"/&gt;&lt;circle cx="103.2" cy="38.2" r="1.6"/&gt;&lt;circle cx="57.0" cy="53.5" r="1.6"/&gt;&lt;circle cx="118.2" cy="31.9" r="1.6"/&gt;&lt;circle cx="40.7" cy="63.1" r="1.6"/&gt;&lt;circle cx="162.5" cy="74.0" r="1.6"/&gt;&lt;circle cx="67.9" cy="47.6" r="1.6"/&gt;&lt;circle cx="25.3" cy="92.1" r="1.6"/&gt;&lt;circle cx="111.2" cy="44.8" r="1.6"/&gt;&lt;circle cx="160.1" cy="69.7" r="1.6"/&gt;&lt;circle cx="80.2" cy="20.9" r="1.6"/&gt;&lt;circle cx="164.6" cy="78.7" r="1.6"/&gt;&lt;circle cx="90.0" cy="34.8" r="1.6"/&gt;&lt;circle cx="77.7" cy="32.0" r="1.6"/&gt;&lt;circle cx="109.7" cy="37.5" r="1.6"/&gt;&lt;circle cx="171.2" cy="86.3" r="1.6"/&gt;&lt;circle cx="163.6" cy="102.0" r="1.6"/&gt;&lt;circle cx="83.4" cy="36.1" r="1.6"/&gt;&lt;circle cx="132.3" cy="56.9" r="1.6"/&gt;&lt;circle cx="89.6" cy="15.4" r="1.6"/&gt;&lt;circle cx="94.8" cy="27.2" r="1.6"/&gt;&lt;circle cx="136.9" cy="48.2" r="1.6"/&gt;&lt;circle cx="76.0" cy="35.0" r="1.6"/&gt;&lt;circle cx="128.5" cy="53.5" r="1.6"/&gt;&lt;circle cx="124.6" cy="80.2" r="1.6"/&gt;&lt;circle cx="160.9" cy="75.8" r="1.6"/&gt;&lt;circle cx="26.8" cy="70.2" r="1.6"/&gt;&lt;circle cx="127.6" cy="35.8" r="1.6"/&gt;&lt;circle cx="160.1" cy="83.4" r="1.6"/&gt;&lt;circle cx="166.7" cy="81.5" r="1.6"/&gt;&lt;circle cx="10.4" cy="89.0" r="1.6"/&gt;&lt;circle cx="149.6" cy="81.8" r="1.6"/&gt;&lt;circle cx="168.9" cy="88.9" r="1.6"/&gt;&lt;circle cx="165.0" cy="84.5" r="1.6"/&gt;&lt;circle cx="32.4" cy="58.9" r="1.6"/&gt;&lt;circle cx="167.5" cy="82.5" r="1.6"/&gt;&lt;circle cx="153.9" cy="81.9" r="1.6"/&gt;&lt;circle cx="142.9" cy="64.7" r="1.6"/&gt;&lt;circle cx="86.7" cy="26.5" r="1.6"/&gt;&lt;circle cx="46.1" cy="62.6" r="1.6"/&gt;&lt;circle cx="139.5" cy="53.0" r="1.6"/&gt;&lt;circle cx="159.5" cy="94.7" r="1.6"/&gt;&lt;circle cx="51.6" cy="53.1" r="1.6"/&gt;&lt;circle cx="96.4" cy="23.3" r="1.6"/&gt;&lt;circle cx="80.6" cy="40.9" r="1.6"/&gt;&lt;circle cx="160.7" cy="76.6" r="1.6"/&gt;&lt;circle cx="14.6" cy="71.0" r="1.6"/&gt;&lt;circle cx="128.0" cy="47.2" r="1.6"/&gt;&lt;circle cx="108.8" cy="48.5" r="1.6"/&gt;&lt;circle cx="12.7" cy="88.6" r="1.6"/&gt;&lt;circle cx="126.0" cy="38.6" r="1.6"/&gt;&lt;circle cx="10.6" cy="80.5" r="1.6"/&gt;&lt;circle cx="132.3" cy="55.4" r="1.6"/&gt;&lt;circle cx="92.1" cy="24.5" r="1.6"/&gt;&lt;circle cx="160.4" cy="73.4" r="1.6"/&gt;&lt;circle cx="18.8" cy="85.8" r="1.6"/&gt;&lt;circle cx="146.0" cy="71.2" r="1.6"/&gt;&lt;circle cx="18.9" cy="78.6" r="1.6"/&gt;&lt;circle cx="64.5" cy="43.9" r="1.6"/&gt;&lt;circle cx="78.6" cy="31.6" r="1.6"/&gt;&lt;circle cx="166.4" cy="71.3" r="1.6"/&gt;&lt;circle cx="100.2" cy="38.1" r="1.6"/&gt;&lt;circle cx="50.5" cy="36.4" r="1.6"/&gt;&lt;circle cx="47.6" cy="39.7" r="1.6"/&gt;&lt;circle cx="153.7" cy="89.4" r="1.6"/&gt;&lt;circle cx="45.0" cy="59.2" r="1.6"/&gt;&lt;circle cx="28.4" cy="70.6" r="1.6"/&gt;&lt;circle cx="55.3" cy="54.1" r="1.6"/&gt;&lt;circle cx="104.1" cy="37.9" r="1.6"/&gt;&lt;circle cx="98.9" cy="31.5" r="1.6"/&gt;&lt;circle cx="140.8" cy="57.7" r="1.6"/&gt;&lt;circle cx="99.9" cy="34.2" r="1.6"/&gt;&lt;circle cx="55.3" cy="32.4" r="1.6"/&gt;&lt;circle cx="75.7" cy="29.4" r="1.6"/&gt;&lt;circle cx="142.2" cy="66.0" r="1.6"/&gt;&lt;circle cx="110.7" cy="23.6" r="1.6"/&gt;&lt;circle cx="165.3" cy="76.5" r="1.6"/&gt;&lt;circle cx="68.6" cy="33.1" r="1.6"/&gt;&lt;circle cx="98.6" cy="32.2" r="1.6"/&gt;&lt;circle cx="105.4" cy="17.3" r="1.6"/&gt;&lt;circle cx="147.1" cy="63.0" r="1.6"/&gt;&lt;circle cx="31.9" cy="72.4" r="1.6"/&gt;&lt;circle cx="74.7" cy="45.6" r="1.6"/&gt;&lt;circle cx="157.2" cy="74.5" r="1.6"/&gt;&lt;circle cx="15.1" cy="91.2" r="1.6"/&gt;&lt;circle cx="142.9" cy="80.2" r="1.6"/&gt;&lt;circle cx="76.1" cy="34.0" r="1.6"/&gt;&lt;circle cx="144.1" cy="64.7" r="1.6"/&gt;&lt;circle cx="9.6" cy="72.5" r="1.6"/&gt;&lt;circle cx="67.9" cy="34.4" r="1.6"/&gt;&lt;circle cx="20.9" cy="72.0" r="1.6"/&gt;&lt;circle cx="115.0" cy="49.2" r="1.6"/&gt;&lt;circle cx="52.9" cy="50.2" r="1.6"/&gt;&lt;circle cx="123.2" cy="45.2" r="1.6"/&gt;&lt;circle cx="162.8" cy="73.5" r="1.6"/&gt;&lt;circle cx="28.8" cy="72.2" r="1.6"/&gt;&lt;circle cx="149.8" cy="65.4" r="1.6"/&gt;&lt;circle cx="17.8" cy="84.6" r="1.6"/&gt;&lt;circle cx="70.4" cy="31.9" r="1.6"/&gt;&lt;circle cx="78.5" cy="27.1" r="1.6"/&gt;&lt;circle cx="88.2" cy="38.6" r="1.6"/&gt;&lt;circle cx="168.1" cy="81.2" r="1.6"/&gt;&lt;circle cx="135.2" cy="61.0" r="1.6"/&gt;&lt;circle cx="58.7" cy="60.4" r="1.6"/&gt;&lt;circle cx="52.3" cy="42.7" r="1.6"/&gt;&lt;circle cx="149.6" cy="74.9" r="1.6"/&gt;&lt;circle cx="152.5" cy="81.4" r="1.6"/&gt;&lt;circle cx="91.8" cy="31.3" r="1.6"/&gt;&lt;circle cx="64.5" cy="38.2" r="1.6"/&gt;&lt;circle cx="171.2" cy="70.8" r="1.6"/&gt;&lt;circle cx="59.8" cy="44.8" r="1.6"/&gt;&lt;circle cx="38.0" cy="61.1" r="1.6"/&gt;&lt;circle cx="152.3" cy="62.6" r="1.6"/&gt;&lt;circle cx="141.2" cy="59.8" r="1.6"/&gt;&lt;circle cx="117.5" cy="32.2" r="1.6"/&gt;&lt;circle cx="165.2" cy="85.7" r="1.6"/&gt;&lt;circle cx="159.8" cy="72.7" r="1.6"/&gt;&lt;circle cx="130.7" cy="54.2" r="1.6"/&gt;&lt;circle cx="149.2" cy="62.6" r="1.6"/&gt;&lt;circle cx="48.5" cy="62.8" r="1.6"/&gt;&lt;circle cx="31.2" cy="77.8" r="1.6"/&gt;&lt;circle cx="117.9" cy="30.8" r="1.6"/&gt;&lt;circle cx="125.2" cy="49.8" r="1.6"/&gt;&lt;circle cx="35.4" cy="70.5" r="1.6"/&gt;&lt;circle cx="72.9" cy="32.8" r="1.6"/&gt;&lt;circle cx="157.3" cy="83.4" r="1.6"/&gt;&lt;circle cx="100.1" cy="18.8" r="1.6"/&gt;&lt;circle cx="102.8" cy="41.6" r="1.6"/&gt;&lt;circle cx="39.8" cy="64.5" r="1.6"/&gt;&lt;circle cx="94.3" cy="25.5" r="1.6"/&gt;&lt;circle cx="93.8" cy="29.3" r="1.6"/&gt;&lt;circle cx="22.6" cy="84.4" r="1.6"/&gt;&lt;circle cx="169.0" cy="90.2" r="1.6"/&gt;&lt;circle cx="101.7" cy="27.1" r="1.6"/&gt;&lt;circle cx="9.1" cy="92.0" r="1.6"/&gt;&lt;circle cx="134.7" cy="52.4" r="1.6"/&gt;&lt;circle cx="168.4" cy="95.5" r="1.6"/&gt;&lt;circle cx="104.7" cy="36.7" r="1.6"/&gt;&lt;circle cx="60.4" cy="42.6" r="1.6"/&gt;&lt;circle cx="38.8" cy="74.8" r="1.6"/&gt;&lt;circle cx="118.3" cy="36.3" r="1.6"/&gt;&lt;circle cx="40.0" cy="63.2" r="1.6"/&gt;&lt;circle cx="102.7" cy="39.7" r="1.6"/&gt;&lt;circle cx="106.8" cy="45.9" r="1.6"/&gt;&lt;circle cx="165.8" cy="95.0" r="1.6"/&gt;&lt;circle cx="19.9" cy="78.9" r="1.6"/&gt;&lt;circle cx="90.0" cy="37.8" r="1.6"/&gt;&lt;circle cx="130.0" cy="64.4" r="1.6"/&gt;&lt;circle cx="37.1" cy="67.9" r="1.6"/&gt;&lt;circle cx="71.6" cy="15.1" r="1.6"/&gt;&lt;circle cx="18.3" cy="77.8" r="1.6"/&gt;&lt;circle cx="127.0" cy="43.6" r="1.6"/&gt;&lt;circle cx="22.4" cy="76.0" r="1.6"/&gt;&lt;circle cx="72.8" cy="26.9" r="1.6"/&gt;&lt;circle cx="151.3" cy="84.5" r="1.6"/&gt;&lt;circle cx="85.5" cy="32.1" r="1.6"/&gt;&lt;circle cx="157.7" cy="75.7" r="1.6"/&gt;&lt;circle cx="133.6" cy="56.8" r="1.6"/&gt;&lt;circle cx="158.1" cy="79.7" r="1.6"/&gt;&lt;circle cx="28.9" cy="78.7" r="1.6"/&gt;&lt;circle cx="20.1" cy="81.4" r="1.6"/&gt;&lt;circle cx="19.5" cy="86.0" r="1.6"/&gt;&lt;circle cx="150.5" cy="92.9" r="1.6"/&gt;&lt;circle cx="112.0" cy="46.6" r="1.6"/&gt;&lt;circle cx="89.4" cy="24.1" r="1.6"/&gt;&lt;circle cx="34.8" cy="55.0" r="1.6"/&gt;&lt;circle cx="118.5" cy="26.8" r="1.6"/&gt;&lt;circle cx="60.2" cy="42.3" r="1.6"/&gt;&lt;circle cx="124.6" cy="40.1" r="1.6"/&gt;&lt;circle cx="83.5" cy="21.3" r="1.6"/&gt;&lt;circle cx="91.2" cy="33.9" r="1.6"/&gt;&lt;circle cx="137.5" cy="74.8" r="1.6"/&gt;&lt;circle cx="23.2" cy="77.0" r="1.6"/&gt;&lt;circle cx="102.9" cy="45.9" r="1.6"/&gt;&lt;circle cx="40.3" cy="65.4" r="1.6"/&gt;&lt;circle cx="140.5" cy="58.5" r="1.6"/&gt;&lt;circle cx="88.2" cy="40.0" r="1.6"/&gt;&lt;circle cx="170.1" cy="82.9" r="1.6"/&gt;&lt;circle cx="38.0" cy="54.7" r="1.6"/&gt;&lt;circle cx="165.9" cy="79.0" r="1.6"/&gt;&lt;circle cx="139.4" cy="58.1" r="1.6"/&gt;&lt;circle cx="86.9" cy="20.7" r="1.6"/&gt;&lt;circle cx="141.4" cy="50.0" r="1.6"/&gt;&lt;circle cx="106.9" cy="32.0" r="1.6"/&gt;&lt;circle cx="115.4" cy="29.1" r="1.6"/&gt;&lt;circle cx="157.8" cy="78.5" r="1.6"/&gt;&lt;circle cx="18.7" cy="84.5" r="1.6"/&gt;&lt;circle cx="144.9" cy="65.2" r="1.6"/&gt;&lt;circle cx="70.6" cy="39.9" r="1.6"/&gt;&lt;circle cx="61.4" cy="46.8" r="1.6"/&gt;&lt;circle cx="171.0" cy="88.4" r="1.6"/&gt;&lt;circle cx="136.1" cy="78.4" r="1.6"/&gt;&lt;circle cx="87.6" cy="27.3" r="1.6"/&gt;&lt;circle cx="77.3" cy="41.6" r="1.6"/&gt;&lt;circle cx="151.9" cy="74.7" r="1.6"/&gt;&lt;circle cx="22.2" cy="65.8" r="1.6"/&gt;&lt;circle cx="124.2" cy="51.5" r="1.6"/&gt;&lt;circle cx="137.4" cy="65.0" r="1.6"/&gt;&lt;circle cx="139.1" cy="50.8" r="1.6"/&gt;&lt;circle cx="60.9" cy="37.9" r="1.6"/&gt;&lt;circle cx="138.6" cy="53.6" r="1.6"/&gt;&lt;circle cx="45.0" cy="61.1" r="1.6"/&gt;&lt;circle cx="67.4" cy="30.8" r="1.6"/&gt;&lt;circle cx="76.5" cy="37.2" r="1.6"/&gt;&lt;circle cx="96.8" cy="34.6" r="1.6"/&gt;&lt;circle cx="26.5" cy="70.0" r="1.6"/&gt;&lt;circle cx="74.7" cy="37.3" r="1.6"/&gt;&lt;circle cx="8.0" cy="77.1" r="1.6"/&gt;&lt;circle cx="130.1" cy="33.1" r="1.6"/&gt;&lt;circle cx="147.7" cy="84.4" r="1.6"/&gt;&lt;circle cx="30.8" cy="77.9" r="1.6"/&gt;&lt;circle cx="123.4" cy="37.1" r="1.6"/&gt;&lt;circle cx="142.7" cy="66.9" r="1.6"/&gt;&lt;circle cx="169.0" cy="73.2" r="1.6"/&gt;&lt;circle cx="146.4" cy="77.6" r="1.6"/&gt;&lt;circle cx="77.6" cy="28.2" r="1.6"/&gt;&lt;circle cx="168.7" cy="84.1" r="1.6"/&gt;&lt;circle cx="167.7" cy="81.4" r="1.6"/&gt;&lt;circle cx="90.6" cy="25.3" r="1.6"/&gt;&lt;circle cx="131.6" cy="64.8" r="1.6"/&gt;&lt;circle cx="157.9" cy="87.0" r="1.6"/&gt;&lt;circle cx="86.1" cy="16.7" r="1.6"/&gt;&lt;circle cx="149.7" cy="69.5" r="1.6"/&gt;&lt;circle cx="123.1" cy="45.2" r="1.6"/&gt;&lt;circle cx="56.2" cy="47.2" r="1.6"/&gt;&lt;circle cx="133.9" cy="54.0" r="1.6"/&gt;&lt;circle cx="101.6" cy="40.2" r="1.6"/&gt;&lt;circle cx="23.4" cy="85.5" r="1.6"/&gt;&lt;circle cx="72.2" cy="22.9" r="1.6"/&gt;&lt;circle cx="20.1" cy="77.9" r="1.6"/&gt;&lt;circle cx="86.1" cy="21.3" r="1.6"/&gt;&lt;circle cx="78.3" cy="35.7" r="1.6"/&gt;&lt;circle cx="77.5" cy="19.5" r="1.6"/&gt;&lt;circle cx="104.2" cy="28.9" r="1.6"/&gt;&lt;circle cx="28.1" cy="69.7" r="1.6"/&gt;&lt;circle cx="161.1" cy="75.3" r="1.6"/&gt;&lt;circle cx="120.2" cy="50.1" r="1.6"/&gt;&lt;circle cx="143.1" cy="81.7" r="1.6"/&gt;&lt;circle cx="155.1" cy="85.1" r="1.6"/&gt;&lt;circle cx="103.7" cy="17.9" r="1.6"/&gt;&lt;circle cx="14.6" cy="70.9" r="1.6"/&gt;&lt;circle cx="124.7" cy="50.6" r="1.6"/&gt;&lt;circle cx="101.3" cy="33.0" r="1.6"/&gt;&lt;circle cx="143.5" cy="57.8" r="1.6"/&gt;&lt;circle cx="95.3" cy="30.0" r="1.6"/&gt;&lt;circle cx="141.4" cy="75.3" r="1.6"/&gt;&lt;circle cx="171.5" cy="72.9" r="1.6"/&gt;&lt;circle cx="65.5" cy="34.6" r="1.6"/&gt;&lt;circle cx="36.0" cy="87.9" r="1.6"/&gt;&lt;circle cx="72.2" cy="36.4" r="1.6"/&gt;&lt;circle cx="131.5" cy="53.3" r="1.6"/&gt;&lt;circle cx="80.0" cy="25.9" r="1.6"/&gt;&lt;circle cx="104.5" cy="30.7" r="1.6"/&gt;&lt;circle cx="28.9" cy="78.5" r="1.6"/&gt;&lt;circle cx="127.1" cy="51.0" r="1.6"/&gt;&lt;circle cx="53.9" cy="46.6" r="1.6"/&gt;&lt;circle cx="39.3" cy="66.0" r="1.6"/&gt;&lt;circle cx="149.5" cy="74.9" r="1.6"/&gt;&lt;circle cx="100.6" cy="19.7" r="1.6"/&gt;&lt;circle cx="87.5" cy="36.1" r="1.6"/&gt;&lt;circle cx="155.4" cy="79.3" r="1.6"/&gt;&lt;circle cx="22.1" cy="95.0" r="1.6"/&gt;&lt;circle cx="122.2" cy="40.7" r="1.6"/&gt;&lt;circle cx="61.8" cy="34.7" r="1.6"/&gt;&lt;circle cx="36.8" cy="61.6" r="1.6"/&gt;&lt;circle cx="118.7" cy="34.4" r="1.6"/&gt;&lt;circle cx="67.5" cy="33.3" r="1.6"/&gt;&lt;circle cx="62.1" cy="38.5" r="1.6"/&gt;&lt;circle cx="162.8" cy="76.7" r="1.6"/&gt;&lt;circle cx="40.7" cy="64.6" r="1.6"/&gt;&lt;circle cx="92.0" cy="18.3" r="1.6"/&gt;&lt;circle cx="11.9" cy="85.6" r="1.6"/&gt;&lt;circle cx="34.8" cy="80.3" r="1.6"/&gt;&lt;circle cx="152.9" cy="67.4" r="1.6"/&gt;&lt;circle cx="137.4" cy="45.1" r="1.6"/&gt;&lt;circle cx="99.3" cy="37.7" r="1.6"/&gt;&lt;circle cx="44.5" cy="59.4" r="1.6"/&gt;&lt;circle cx="99.5" cy="35.5" r="1.6"/&gt;&lt;circle cx="10.0" cy="86.4" r="1.6"/&gt;&lt;circle cx="124.9" cy="47.5" r="1.6"/&gt;&lt;circle cx="125.5" cy="58.9" r="1.6"/&gt;&lt;circle cx="114.0" cy="40.5" r="1.6"/&gt;&lt;circle cx="108.3" cy="46.3" r="1.6"/&gt;&lt;circle cx="20.1" cy="83.9" r="1.6"/&gt;&lt;circle cx="48.4" cy="64.5" r="1.6"/&gt;&lt;circle cx="102.2" cy="39.7" r="1.6"/&gt;&lt;circle cx="72.6" cy="47.9" r="1.6"/&gt;&lt;circle cx="170.7" cy="73.1" r="1.6"/&gt;&lt;circle cx="159.5" cy="74.7" r="1.6"/&gt;&lt;circle cx="32.9" cy="85.7" r="1.6"/&gt;&lt;circle cx="104.8" cy="37.1" r="1.6"/&gt;&lt;circle cx="122.2" cy="50.9" r="1.6"/&gt;&lt;circle cx="30.4" cy="77.7" r="1.6"/&gt;&lt;circle cx="59.3" cy="56.0" r="1.6"/&gt;&lt;circle cx="125.4" cy="42.1" r="1.6"/&gt;&lt;circle cx="155.8" cy="79.9" r="1.6"/&gt;&lt;circle cx="64.0" cy="35.8" r="1.6"/&gt;&lt;circle cx="47.2" cy="51.4" r="1.6"/&gt;&lt;circle cx="142.8" cy="54.3" r="1.6"/&gt;&lt;circle cx="103.9" cy="46.8" r="1.6"/&gt;&lt;circle cx="86.2" cy="13.9" r="1.6"/&gt;&lt;circle cx="50.0" cy="42.4" r="1.6"/&gt;&lt;circle cx="19.9" cy="74.5" r="1.6"/&gt;&lt;circle cx="10.9" cy="63.0" r="1.6"/&gt;&lt;circle cx="103.1" cy="29.5" r="1.6"/&gt;&lt;circle cx="39.3" cy="56.9" r="1.6"/&gt;&lt;circle cx="168.0" cy="88.8" r="1.6"/&gt;&lt;circle cx="25.6" cy="66.2" r="1.6"/&gt;&lt;circle cx="82.1" cy="27.8" r="1.6"/&gt;&lt;circle cx="72.7" cy="37.3" r="1.6"/&gt;&lt;circle cx="46.1" cy="39.3" r="1.6"/&gt;&lt;circle cx="130.8" cy="56.3" r="1.6"/&gt;&lt;circle cx="113.6" cy="37.8" r="1.6"/&gt;&lt;circle cx="127.0" cy="51.6" r="1.6"/&gt;&lt;circle cx="21.6" cy="84.6" r="1.6"/&gt;&lt;circle cx="65.8" cy="33.8" r="1.6"/&gt;&lt;circle cx="93.3" cy="22.1" r="1.6"/&gt;&lt;circle cx="78.0" cy="27.8" r="1.6"/&gt;&lt;circle cx="14.7" cy="101.5" r="1.6"/&gt;&lt;circle cx="39.8" cy="54.8" r="1.6"/&gt;&lt;circle cx="163.0" cy="83.7" r="1.6"/&gt;&lt;circle cx="34.7" cy="78.3" r="1.6"/&gt;&lt;circle cx="147.7" cy="65.3" r="1.6"/&gt;&lt;circle cx="142.8" cy="61.1" r="1.6"/&gt;&lt;circle cx="72.2" cy="42.5" r="1.6"/&gt;&lt;circle cx="84.6" cy="8.0" r="1.6"/&gt;&lt;circle cx="143.1" cy="76.2" r="1.6"/&gt;&lt;circle cx="119.6" cy="57.4" r="1.6"/&gt;&lt;circle cx="145.3" cy="77.7" r="1.6"/&gt;&lt;circle cx="132.2" cy="43.4" r="1.6"/&gt;&lt;circle cx="121.4" cy="46.0" r="1.6"/&gt;&lt;circle cx="157.7" cy="81.0" r="1.6"/&gt;&lt;circle cx="142.9" cy="66.4" r="1.6"/&gt;&lt;circle cx="37.4" cy="70.6" r="1.6"/&gt;&lt;circle cx="130.7" cy="59.5" r="1.6"/&gt;&lt;circle cx="22.2" cy="83.3" r="1.6"/&gt;&lt;circle cx="77.8" cy="24.9" r="1.6"/&gt;&lt;circle cx="73.1" cy="24.8" r="1.6"/&gt;&lt;/g&gt;&lt;/svg&gt;&lt;span class="dp-name"&gt;y = cos(x)&lt;/span&gt;&lt;/th&gt;&lt;td class="dp-missed"&gt;&lt;b aria-label="misses it"&gt;✗&lt;/b&gt;&lt;small&gt;p = 0.17&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p = 0.002&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;&lt;svg viewBox="0 0 180 110" role="img" aria-label="y = x²: 400 points"&gt;&lt;g class="dp-dots"&gt;&lt;circle cx="112.5" cy="84.3" r="1.6"/&gt;&lt;circle cx="52.2" cy="77.7" r="1.6"/&gt;&lt;circle cx="14.7" cy="28.3" r="1.6"/&gt;&lt;circle cx="10.7" cy="25.1" r="1.6"/&gt;&lt;circle cx="141.4" cy="51.2" r="1.6"/&gt;&lt;circle cx="157.7" cy="49.9" r="1.6"/&gt;&lt;circle cx="107.5" cy="82.5" r="1.6"/&gt;&lt;circle cx="127.6" cy="70.9" r="1.6"/&gt;&lt;circle cx="97.2" cy="89.8" r="1.6"/&gt;&lt;circle cx="161.4" cy="33.6" r="1.6"/&gt;&lt;circle cx="141.8" cy="70.9" r="1.6"/&gt;&lt;circle cx="8.4" cy="8.0" r="1.6"/&gt;&lt;circle cx="148.6" cy="62.8" r="1.6"/&gt;&lt;circle cx="13.5" cy="23.9" r="1.6"/&gt;&lt;circle cx="127.7" cy="81.3" r="1.6"/&gt;&lt;circle cx="36.8" cy="52.3" r="1.6"/&gt;&lt;circle cx="149.6" cy="55.4" r="1.6"/&gt;&lt;circle cx="96.8" cy="86.4" r="1.6"/&gt;&lt;circle cx="57.2" cy="76.9" r="1.6"/&gt;&lt;circle cx="77.3" cy="79.0" r="1.6"/&gt;&lt;circle cx="12.6" cy="30.8" r="1.6"/&gt;&lt;circle cx="28.4" cy="70.1" r="1.6"/&gt;&lt;circle cx="118.0" cy="85.2" r="1.6"/&gt;&lt;circle cx="114.1" cy="80.9" r="1.6"/&gt;&lt;circle cx="108.9" cy="87.3" r="1.6"/&gt;&lt;circle cx="70.9" cy="79.2" r="1.6"/&gt;&lt;circle cx="171.5" cy="15.4" r="1.6"/&gt;&lt;circle cx="168.9" cy="27.4" r="1.6"/&gt;&lt;circle cx="120.4" cy="88.7" r="1.6"/&gt;&lt;circle cx="114.7" cy="72.7" r="1.6"/&gt;&lt;circle cx="120.9" cy="71.3" r="1.6"/&gt;&lt;circle cx="71.8" cy="86.6" r="1.6"/&gt;&lt;circle cx="30.2" cy="38.4" r="1.6"/&gt;&lt;circle cx="126.3" cy="77.2" r="1.6"/&gt;&lt;circle cx="94.2" cy="95.3" r="1.6"/&gt;&lt;circle cx="58.9" cy="79.4" r="1.6"/&gt;&lt;circle cx="87.7" cy="80.7" r="1.6"/&gt;&lt;circle cx="153.9" cy="53.8" r="1.6"/&gt;&lt;circle cx="161.2" cy="24.8" r="1.6"/&gt;&lt;circle cx="66.7" cy="88.5" r="1.6"/&gt;&lt;circle cx="101.7" cy="80.2" r="1.6"/&gt;&lt;circle cx="60.8" cy="75.8" r="1.6"/&gt;&lt;circle cx="105.5" cy="86.6" r="1.6"/&gt;&lt;circle cx="63.4" cy="73.7" r="1.6"/&gt;&lt;circle cx="72.2" cy="94.8" r="1.6"/&gt;&lt;circle cx="154.0" cy="38.3" r="1.6"/&gt;&lt;circle cx="45.3" cy="68.5" r="1.6"/&gt;&lt;circle cx="110.2" cy="87.5" r="1.6"/&gt;&lt;circle cx="21.8" cy="36.9" r="1.6"/&gt;&lt;circle cx="144.6" cy="51.4" r="1.6"/&gt;&lt;circle cx="137.1" cy="60.9" r="1.6"/&gt;&lt;circle cx="47.3" cy="56.5" r="1.6"/&gt;&lt;circle cx="151.7" cy="43.0" r="1.6"/&gt;&lt;circle cx="17.6" cy="27.5" r="1.6"/&gt;&lt;circle cx="63.1" cy="84.4" r="1.6"/&gt;&lt;circle cx="32.6" cy="54.7" r="1.6"/&gt;&lt;circle cx="81.9" cy="83.5" r="1.6"/&gt;&lt;circle cx="138.6" cy="64.7" r="1.6"/&gt;&lt;circle cx="45.8" cy="66.6" r="1.6"/&gt;&lt;circle cx="16.5" cy="31.7" r="1.6"/&gt;&lt;circle cx="74.3" cy="79.9" r="1.6"/&gt;&lt;circle cx="40.6" cy="64.4" r="1.6"/&gt;&lt;circle cx="22.9" cy="45.7" r="1.6"/&gt;&lt;circle cx="103.2" cy="91.7" r="1.6"/&gt;&lt;circle cx="57.0" cy="83.1" r="1.6"/&gt;&lt;circle cx="118.2" cy="72.2" r="1.6"/&gt;&lt;circle cx="40.7" cy="64.4" r="1.6"/&gt;&lt;circle cx="162.5" cy="30.7" r="1.6"/&gt;&lt;circle cx="67.9" cy="91.7" r="1.6"/&gt;&lt;circle cx="25.3" cy="59.4" r="1.6"/&gt;&lt;circle cx="111.2" cy="90.5" r="1.6"/&gt;&lt;circle cx="160.1" cy="31.7" r="1.6"/&gt;&lt;circle cx="80.2" cy="79.9" r="1.6"/&gt;&lt;circle cx="164.6" cy="30.7" r="1.6"/&gt;&lt;circle cx="90.0" cy="93.3" r="1.6"/&gt;&lt;circle cx="77.7" cy="87.3" r="1.6"/&gt;&lt;circle cx="109.7" cy="86.1" r="1.6"/&gt;&lt;circle cx="171.2" cy="25.0" r="1.6"/&gt;&lt;circle cx="163.6" cy="50.9" r="1.6"/&gt;&lt;circle cx="83.4" cy="93.2" r="1.6"/&gt;&lt;circle cx="132.3" cy="71.5" r="1.6"/&gt;&lt;circle cx="89.6" cy="77.8" r="1.6"/&gt;&lt;circle cx="94.8" cy="86.6" r="1.6"/&gt;&lt;circle cx="136.9" cy="56.9" r="1.6"/&gt;&lt;circle cx="76.0" cy="88.6" r="1.6"/&gt;&lt;circle cx="128.5" cy="75.0" r="1.6"/&gt;&lt;circle cx="124.6" cy="102.0" r="1.6"/&gt;&lt;circle cx="160.9" cy="35.1" r="1.6"/&gt;&lt;circle cx="26.8" cy="44.8" r="1.6"/&gt;&lt;circle cx="127.6" cy="62.2" r="1.6"/&gt;&lt;circle cx="160.1" cy="42.5" r="1.6"/&gt;&lt;circle cx="166.7" cy="29.0" r="1.6"/&gt;&lt;circle cx="10.4" cy="29.9" r="1.6"/&gt;&lt;circle cx="149.6" cy="60.5" r="1.6"/&gt;&lt;circle cx="168.9" cy="31.0" r="1.6"/&gt;&lt;circle cx="165.0" cy="34.6" r="1.6"/&gt;&lt;circle cx="32.4" cy="46.1" r="1.6"/&gt;&lt;circle cx="167.5" cy="28.4" r="1.6"/&gt;&lt;circle cx="153.9" cy="52.7" r="1.6"/&gt;&lt;circle cx="142.9" cy="59.3" r="1.6"/&gt;&lt;circle cx="86.7" cy="86.4" r="1.6"/&gt;&lt;circle cx="46.1" cy="73.4" r="1.6"/&gt;&lt;circle cx="139.5" cy="56.1" r="1.6"/&gt;&lt;circle cx="159.5" cy="52.7" r="1.6"/&gt;&lt;circle cx="51.6" cy="74.8" r="1.6"/&gt;&lt;circle cx="96.4" cy="83.1" r="1.6"/&gt;&lt;circle cx="80.6" cy="96.0" r="1.6"/&gt;&lt;circle cx="160.7" cy="36.1" r="1.6"/&gt;&lt;circle cx="14.6" cy="23.1" r="1.6"/&gt;&lt;circle cx="128.0" cy="70.6" r="1.6"/&gt;&lt;circle cx="108.8" cy="95.7" r="1.6"/&gt;&lt;circle cx="12.7" cy="33.6" r="1.6"/&gt;&lt;circle cx="126.0" cy="66.9" r="1.6"/&gt;&lt;circle cx="10.6" cy="23.6" r="1.6"/&gt;&lt;circle cx="132.3" cy="70.3" r="1.6"/&gt;&lt;circle cx="92.1" cy="84.9" r="1.6"/&gt;&lt;circle cx="160.4" cy="34.1" r="1.6"/&gt;&lt;circle cx="18.8" cy="42.5" r="1.6"/&gt;&lt;circle cx="146.0" cy="58.8" r="1.6"/&gt;&lt;circle cx="18.9" cy="37.0" r="1.6"/&gt;&lt;circle cx="64.5" cy="85.1" r="1.6"/&gt;&lt;circle cx="78.6" cy="87.5" r="1.6"/&gt;&lt;circle cx="166.4" cy="21.5" r="1.6"/&gt;&lt;circle cx="100.2" cy="93.3" r="1.6"/&gt;&lt;circle cx="50.5" cy="59.6" r="1.6"/&gt;&lt;circle cx="47.6" cy="57.6" r="1.6"/&gt;&lt;circle cx="153.7" cy="59.2" r="1.6"/&gt;&lt;circle cx="45.0" cy="68.9" r="1.6"/&gt;&lt;circle cx="28.4" cy="48.1" r="1.6"/&gt;&lt;circle cx="55.3" cy="81.1" r="1.6"/&gt;&lt;circle cx="104.1" cy="90.9" r="1.6"/&gt;&lt;circle cx="98.9" cy="88.7" r="1.6"/&gt;&lt;circle cx="140.8" cy="57.5" r="1.6"/&gt;&lt;circle cx="99.9" cy="90.3" r="1.6"/&gt;&lt;circle cx="55.3" cy="63.8" r="1.6"/&gt;&lt;circle cx="75.7" cy="84.0" r="1.6"/&gt;&lt;circle cx="142.2" cy="61.7" r="1.6"/&gt;&lt;circle cx="110.7" cy="74.0" r="1.6"/&gt;&lt;circle cx="165.3" cy="27.7" r="1.6"/&gt;&lt;circle cx="68.6" cy="80.9" r="1.6"/&gt;&lt;circle cx="98.6" cy="89.4" r="1.6"/&gt;&lt;circle cx="105.4" cy="73.5" r="1.6"/&gt;&lt;circle cx="147.1" cy="50.2" r="1.6"/&gt;&lt;circle cx="31.9" cy="55.8" r="1.6"/&gt;&lt;circle cx="74.7" cy="96.1" r="1.6"/&gt;&lt;circle cx="157.2" cy="40.7" r="1.6"/&gt;&lt;circle cx="15.1" cy="40.0" r="1.6"/&gt;&lt;circle cx="142.9" cy="71.6" r="1.6"/&gt;&lt;circle cx="76.1" cy="87.9" r="1.6"/&gt;&lt;circle cx="144.1" cy="57.1" r="1.6"/&gt;&lt;circle cx="9.6" cy="15.5" r="1.6"/&gt;&lt;circle cx="67.9" cy="81.2" r="1.6"/&gt;&lt;circle cx="20.9" cy="35.3" r="1.6"/&gt;&lt;circle cx="115.0" cy="89.9" r="1.6"/&gt;&lt;circle cx="52.9" cy="74.5" r="1.6"/&gt;&lt;circle cx="123.2" cy="76.2" r="1.6"/&gt;&lt;circle cx="162.8" cy="29.8" r="1.6"/&gt;&lt;circle cx="28.8" cy="50.1" r="1.6"/&gt;&lt;circle cx="149.8" cy="47.1" r="1.6"/&gt;&lt;circle cx="17.8" cy="39.6" r="1.6"/&gt;&lt;circle cx="70.4" cy="81.8" r="1.6"/&gt;&lt;circle cx="78.5" cy="83.8" r="1.6"/&gt;&lt;circle cx="88.2" cy="96.2" r="1.6"/&gt;&lt;circle cx="168.1" cy="26.3" r="1.6"/&gt;&lt;circle cx="135.2" cy="69.8" r="1.6"/&gt;&lt;circle cx="58.7" cy="90.9" r="1.6"/&gt;&lt;circle cx="52.3" cy="67.4" r="1.6"/&gt;&lt;circle cx="149.6" cy="55.2" r="1.6"/&gt;&lt;circle cx="152.5" cy="54.9" r="1.6"/&gt;&lt;circle cx="91.8" cy="90.4" r="1.6"/&gt;&lt;circle cx="64.5" cy="80.5" r="1.6"/&gt;&lt;circle cx="171.2" cy="12.7" r="1.6"/&gt;&lt;circle cx="59.8" cy="80.0" r="1.6"/&gt;&lt;circle cx="38.0" cy="58.0" r="1.6"/&gt;&lt;circle cx="152.3" cy="40.3" r="1.6"/&gt;&lt;circle cx="141.2" cy="58.4" r="1.6"/&gt;&lt;circle cx="117.5" cy="73.3" r="1.6"/&gt;&lt;circle cx="165.2" cy="35.2" r="1.6"/&gt;&lt;circle cx="159.8" cy="34.5" r="1.6"/&gt;&lt;circle cx="130.7" cy="71.9" r="1.6"/&gt;&lt;circle cx="149.2" cy="46.1" r="1.6"/&gt;&lt;circle cx="48.5" cy="77.6" r="1.6"/&gt;&lt;circle cx="31.2" cy="58.8" r="1.6"/&gt;&lt;circle cx="117.9" cy="71.8" r="1.6"/&gt;&lt;circle cx="125.2" cy="77.0" r="1.6"/&gt;&lt;circle cx="35.4" cy="60.8" r="1.6"/&gt;&lt;circle cx="72.9" cy="84.5" r="1.6"/&gt;&lt;circle cx="157.3" cy="47.7" r="1.6"/&gt;&lt;circle cx="100.1" cy="78.1" r="1.6"/&gt;&lt;circle cx="102.8" cy="94.6" r="1.6"/&gt;&lt;circle cx="39.8" cy="64.0" r="1.6"/&gt;&lt;circle cx="94.3" cy="85.4" r="1.6"/&gt;&lt;circle cx="93.8" cy="88.5" r="1.6"/&gt;&lt;circle cx="22.6" cy="48.3" r="1.6"/&gt;&lt;circle cx="169.0" cy="31.9" r="1.6"/&gt;&lt;circle cx="101.7" cy="83.7" r="1.6"/&gt;&lt;circle cx="9.1" cy="29.9" r="1.6"/&gt;&lt;circle cx="134.7" cy="63.8" r="1.6"/&gt;&lt;circle cx="168.4" cy="37.2" r="1.6"/&gt;&lt;circle cx="104.7" cy="89.5" r="1.6"/&gt;&lt;circle cx="60.4" cy="79.0" r="1.6"/&gt;&lt;circle cx="38.8" cy="70.3" r="1.6"/&gt;&lt;circle cx="118.3" cy="75.7" r="1.6"/&gt;&lt;circle cx="40.0" cy="63.3" r="1.6"/&gt;&lt;circle cx="102.7" cy="93.2" r="1.6"/&gt;&lt;circle cx="106.8" cy="95.2" r="1.6"/&gt;&lt;circle cx="165.8" cy="41.4" r="1.6"/&gt;&lt;circle cx="19.9" cy="38.9" r="1.6"/&gt;&lt;circle cx="90.0" cy="95.6" r="1.6"/&gt;&lt;circle cx="130.0" cy="81.1" r="1.6"/&gt;&lt;circle cx="37.1" cy="61.7" r="1.6"/&gt;&lt;circle cx="71.6" cy="69.4" r="1.6"/&gt;&lt;circle cx="18.3" cy="35.2" r="1.6"/&gt;&lt;circle cx="127.0" cy="69.3" r="1.6"/&gt;&lt;circle cx="22.4" cy="41.3" r="1.6"/&gt;&lt;circle cx="72.8" cy="79.8" r="1.6"/&gt;&lt;circle cx="151.3" cy="59.7" r="1.6"/&gt;&lt;circle cx="85.5" cy="90.6" r="1.6"/&gt;&lt;circle cx="157.7" cy="40.9" r="1.6"/&gt;&lt;circle cx="133.6" cy="69.2" r="1.6"/&gt;&lt;circle cx="158.1" cy="43.3" r="1.6"/&gt;&lt;circle cx="28.9" cy="55.4" r="1.6"/&gt;&lt;circle cx="20.1" cy="41.2" r="1.6"/&gt;&lt;circle cx="19.5" cy="43.9" r="1.6"/&gt;&lt;circle cx="150.5" cy="67.8" r="1.6"/&gt;&lt;circle cx="112.0" cy="91.1" r="1.6"/&gt;&lt;circle cx="89.4" cy="84.7" r="1.6"/&gt;&lt;circle cx="34.8" cy="47.4" r="1.6"/&gt;&lt;circle cx="118.5" cy="67.8" r="1.6"/&gt;&lt;circle cx="60.2" cy="78.5" r="1.6"/&gt;&lt;circle cx="124.6" cy="70.1" r="1.6"/&gt;&lt;circle cx="83.5" cy="81.5" r="1.6"/&gt;&lt;circle cx="91.2" cy="92.5" r="1.6"/&gt;&lt;circle cx="137.5" cy="76.9" r="1.6"/&gt;&lt;circle cx="23.2" cy="43.6" r="1.6"/&gt;&lt;circle cx="102.9" cy="98.0" r="1.6"/&gt;&lt;circle cx="40.3" cy="65.6" r="1.6"/&gt;&lt;circle cx="140.5" cy="58.6" r="1.6"/&gt;&lt;circle cx="88.2" cy="97.3" r="1.6"/&gt;&lt;circle cx="170.1" cy="24.1" r="1.6"/&gt;&lt;circle cx="38.0" cy="52.9" r="1.6"/&gt;&lt;circle cx="165.9" cy="28.4" r="1.6"/&gt;&lt;circle cx="139.4" cy="60.3" r="1.6"/&gt;&lt;circle cx="86.9" cy="81.8" r="1.6"/&gt;&lt;circle cx="141.4" cy="50.2" r="1.6"/&gt;&lt;circle cx="106.9" cy="84.1" r="1.6"/&gt;&lt;circle cx="115.4" cy="73.3" r="1.6"/&gt;&lt;circle cx="157.8" cy="42.8" r="1.6"/&gt;&lt;circle cx="18.7" cy="41.2" r="1.6"/&gt;&lt;circle cx="144.9" cy="56.0" r="1.6"/&gt;&lt;circle cx="70.6" cy="88.2" r="1.6"/&gt;&lt;circle cx="61.4" cy="83.6" r="1.6"/&gt;&lt;circle cx="171.0" cy="27.0" r="1.6"/&gt;&lt;circle cx="136.1" cy="82.1" r="1.6"/&gt;&lt;circle cx="87.6" cy="87.2" r="1.6"/&gt;&lt;circle cx="77.3" cy="94.7" r="1.6"/&gt;&lt;circle cx="151.9" cy="50.7" r="1.6"/&gt;&lt;circle cx="22.2" cy="32.8" r="1.6"/&gt;&lt;circle cx="124.2" cy="79.8" r="1.6"/&gt;&lt;circle cx="137.4" cy="69.3" r="1.6"/&gt;&lt;circle cx="139.1" cy="55.0" r="1.6"/&gt;&lt;circle cx="60.9" cy="75.9" r="1.6"/&gt;&lt;circle cx="138.6" cy="58.0" r="1.6"/&gt;&lt;circle cx="45.0" cy="70.2" r="1.6"/&gt;&lt;circle cx="67.4" cy="77.9" r="1.6"/&gt;&lt;circle cx="76.5" cy="90.7" r="1.6"/&gt;&lt;circle cx="96.8" cy="92.0" r="1.6"/&gt;&lt;circle cx="26.5" cy="44.0" r="1.6"/&gt;&lt;circle cx="74.7" cy="89.6" r="1.6"/&gt;&lt;circle cx="8.0" cy="16.3" r="1.6"/&gt;&lt;circle cx="130.1" cy="56.1" r="1.6"/&gt;&lt;circle cx="147.7" cy="66.2" r="1.6"/&gt;&lt;circle cx="30.8" cy="58.2" r="1.6"/&gt;&lt;circle cx="123.4" cy="69.4" r="1.6"/&gt;&lt;circle cx="142.7" cy="61.4" r="1.6"/&gt;&lt;circle cx="169.0" cy="18.4" r="1.6"/&gt;&lt;circle cx="146.4" cy="63.2" r="1.6"/&gt;&lt;circle cx="77.6" cy="84.2" r="1.6"/&gt;&lt;circle cx="168.7" cy="27.7" r="1.6"/&gt;&lt;circle cx="167.7" cy="27.2" r="1.6"/&gt;&lt;circle cx="90.6" cy="85.7" r="1.6"/&gt;&lt;circle cx="131.6" cy="78.9" r="1.6"/&gt;&lt;circle cx="157.9" cy="49.5" r="1.6"/&gt;&lt;circle cx="86.1" cy="78.5" r="1.6"/&gt;&lt;circle cx="149.7" cy="50.7" r="1.6"/&gt;&lt;circle cx="123.1" cy="76.4" r="1.6"/&gt;&lt;circle cx="56.2" cy="76.9" r="1.6"/&gt;&lt;circle cx="133.9" cy="66.5" r="1.6"/&gt;&lt;circle cx="101.6" cy="94.3" r="1.6"/&gt;&lt;circle cx="23.4" cy="50.7" r="1.6"/&gt;&lt;circle cx="72.2" cy="76.1" r="1.6"/&gt;&lt;circle cx="20.1" cy="38.5" r="1.6"/&gt;&lt;circle cx="86.1" cy="82.1" r="1.6"/&gt;&lt;circle cx="78.3" cy="90.6" r="1.6"/&gt;&lt;circle cx="77.5" cy="77.2" r="1.6"/&gt;&lt;circle cx="104.2" cy="83.6" r="1.6"/&gt;&lt;circle cx="28.1" cy="46.8" r="1.6"/&gt;&lt;circle cx="161.1" cy="34.2" r="1.6"/&gt;&lt;circle cx="120.2" cy="84.2" r="1.6"/&gt;&lt;circle cx="143.1" cy="72.4" r="1.6"/&gt;&lt;circle cx="155.1" cy="53.2" r="1.6"/&gt;&lt;circle cx="103.7" cy="75.2" r="1.6"/&gt;&lt;circle cx="14.6" cy="23.0" r="1.6"/&gt;&lt;circle cx="124.7" cy="78.3" r="1.6"/&gt;&lt;circle cx="101.3" cy="88.7" r="1.6"/&gt;&lt;circle cx="143.5" cy="52.7" r="1.6"/&gt;&lt;circle cx="95.3" cy="88.8" r="1.6"/&gt;&lt;circle cx="141.4" cy="70.4" r="1.6"/&gt;&lt;circle cx="171.5" cy="13.7" r="1.6"/&gt;&lt;circle cx="65.5" cy="78.8" r="1.6"/&gt;&lt;circle cx="36.0" cy="75.9" r="1.6"/&gt;&lt;circle cx="72.2" cy="86.9" r="1.6"/&gt;&lt;circle cx="131.5" cy="69.9" r="1.6"/&gt;&lt;circle cx="80.0" cy="83.7" r="1.6"/&gt;&lt;circle cx="104.5" cy="84.8" r="1.6"/&gt;&lt;circle cx="28.9" cy="55.2" r="1.6"/&gt;&lt;circle cx="127.1" cy="75.1" r="1.6"/&gt;&lt;circle cx="53.9" cy="73.1" r="1.6"/&gt;&lt;circle cx="39.3" cy="64.2" r="1.6"/&gt;&lt;circle cx="149.5" cy="55.3" r="1.6"/&gt;&lt;circle cx="100.6" cy="78.5" r="1.6"/&gt;&lt;circle cx="87.5" cy="94.2" r="1.6"/&gt;&lt;circle cx="155.4" cy="47.9" r="1.6"/&gt;&lt;circle cx="22.1" cy="55.8" r="1.6"/&gt;&lt;circle cx="122.2" cy="74.1" r="1.6"/&gt;&lt;circle cx="61.8" cy="74.5" r="1.6"/&gt;&lt;circle cx="36.8" cy="56.2" r="1.6"/&gt;&lt;circle cx="118.7" cy="73.7" r="1.6"/&gt;&lt;circle cx="67.5" cy="80.0" r="1.6"/&gt;&lt;circle cx="62.1" cy="77.9" r="1.6"/&gt;&lt;circle cx="162.8" cy="32.4" r="1.6"/&gt;&lt;circle cx="40.7" cy="65.6" r="1.6"/&gt;&lt;circle cx="92.0" cy="80.0" r="1.6"/&gt;&lt;circle cx="11.9" cy="29.9" r="1.6"/&gt;&lt;circle cx="34.8" cy="67.5" r="1.6"/&gt;&lt;circle cx="152.9" cy="43.1" r="1.6"/&gt;&lt;circle cx="137.4" cy="53.3" r="1.6"/&gt;&lt;circle cx="99.3" cy="93.4" r="1.6"/&gt;&lt;circle cx="44.5" cy="68.1" r="1.6"/&gt;&lt;circle cx="99.5" cy="91.6" r="1.6"/&gt;&lt;circle cx="10.0" cy="27.1" r="1.6"/&gt;&lt;circle cx="124.9" cy="75.5" r="1.6"/&gt;&lt;circle cx="125.5" cy="83.7" r="1.6"/&gt;&lt;circle cx="114.0" cy="84.1" r="1.6"/&gt;&lt;circle cx="108.3" cy="94.4" r="1.6"/&gt;&lt;circle cx="20.1" cy="43.3" r="1.6"/&gt;&lt;circle cx="48.4" cy="78.7" r="1.6"/&gt;&lt;circle cx="102.2" cy="93.5" r="1.6"/&gt;&lt;circle cx="72.6" cy="96.4" r="1.6"/&gt;&lt;circle cx="170.7" cy="15.4" r="1.6"/&gt;&lt;circle cx="159.5" cy="36.7" r="1.6"/&gt;&lt;circle cx="32.9" cy="68.4" r="1.6"/&gt;&lt;circle cx="104.8" cy="89.7" r="1.6"/&gt;&lt;circle cx="122.2" cy="82.1" r="1.6"/&gt;&lt;circle cx="30.4" cy="57.4" r="1.6"/&gt;&lt;circle cx="59.3" cy="88.2" r="1.6"/&gt;&lt;circle cx="125.4" cy="70.5" r="1.6"/&gt;&lt;circle cx="155.8" cy="47.7" r="1.6"/&gt;&lt;circle cx="64.0" cy="78.1" r="1.6"/&gt;&lt;circle cx="47.2" cy="66.3" r="1.6"/&gt;&lt;circle cx="142.8" cy="51.2" r="1.6"/&gt;&lt;circle cx="103.9" cy="98.1" r="1.6"/&gt;&lt;circle cx="86.2" cy="76.2" r="1.6"/&gt;&lt;circle cx="50.0" cy="63.6" r="1.6"/&gt;&lt;circle cx="19.9" cy="35.5" r="1.6"/&gt;&lt;circle cx="10.9" cy="10.2" r="1.6"/&gt;&lt;circle cx="103.1" cy="84.9" r="1.6"/&gt;&lt;circle cx="39.3" cy="57.1" r="1.6"/&gt;&lt;circle cx="168.0" cy="32.6" r="1.6"/&gt;&lt;circle cx="25.6" cy="39.4" r="1.6"/&gt;&lt;circle cx="82.1" cy="86.2" r="1.6"/&gt;&lt;circle cx="72.7" cy="88.0" r="1.6"/&gt;&lt;circle cx="46.1" cy="54.8" r="1.6"/&gt;&lt;circle cx="130.8" cy="73.5" r="1.6"/&gt;&lt;circle cx="113.6" cy="82.4" r="1.6"/&gt;&lt;circle cx="127.0" cy="75.7" r="1.6"/&gt;&lt;circle cx="21.6" cy="46.6" r="1.6"/&gt;&lt;circle cx="65.8" cy="78.6" r="1.6"/&gt;&lt;circle cx="93.3" cy="82.9" r="1.6"/&gt;&lt;circle cx="78.0" cy="84.1" r="1.6"/&gt;&lt;circle cx="14.7" cy="47.5" r="1.6"/&gt;&lt;circle cx="39.8" cy="56.3" r="1.6"/&gt;&lt;circle cx="163.0" cy="37.5" r="1.6"/&gt;&lt;circle cx="34.7" cy="65.7" r="1.6"/&gt;&lt;circle cx="147.7" cy="50.9" r="1.6"/&gt;&lt;circle cx="142.8" cy="56.6" r="1.6"/&gt;&lt;circle cx="72.2" cy="91.7" r="1.6"/&gt;&lt;circle cx="84.6" cy="71.2" r="1.6"/&gt;&lt;circle cx="143.1" cy="68.0" r="1.6"/&gt;&lt;circle cx="119.6" cy="90.8" r="1.6"/&gt;&lt;circle cx="145.3" cy="65.4" r="1.6"/&gt;&lt;circle cx="132.2" cy="60.8" r="1.6"/&gt;&lt;circle cx="121.4" cy="79.3" r="1.6"/&gt;&lt;circle cx="157.7" cy="45.0" r="1.6"/&gt;&lt;circle cx="142.9" cy="60.6" r="1.6"/&gt;&lt;circle cx="37.4" cy="64.5" r="1.6"/&gt;&lt;circle cx="130.7" cy="76.1" r="1.6"/&gt;&lt;circle cx="22.2" cy="46.7" r="1.6"/&gt;&lt;circle cx="77.8" cy="81.7" r="1.6"/&gt;&lt;circle cx="73.1" cy="78.3" r="1.6"/&gt;&lt;/g&gt;&lt;/svg&gt;&lt;span class="dp-name"&gt;y = x²&lt;/span&gt;&lt;/th&gt;&lt;td class="dp-missed"&gt;&lt;b aria-label="misses it"&gt;✗&lt;/b&gt;&lt;small&gt;p = 0.23&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p = 0.002&lt;/small&gt;&lt;/td&gt;&lt;td class="dp-seen"&gt;&lt;b aria-label="finds it"&gt;✓&lt;/b&gt;&lt;small&gt;p &amp;lt; 0.001&lt;/small&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption&gt;Three ways y depends on x, each with the same noise; ✓ finds the dependence at alpha 0.05. Partial correlation measures only a straight-line trend, so it misses the cos and the square; KCI, RCoT, CMIknn, and GPDC find all three.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;All the tests detect the linear dependence. For the cosine and the square, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; depends on &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;,
but the straight-line trend is flat, so partial correlation misses the dependence. KCI uses kernels
to compare the two variables and detects all three relationships.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="an-example"&gt;
&lt;h2&gt;An example&lt;/h2&gt;
&lt;p&gt;We simulate 500 rows of 8 variables, each a weighted sum of the cosines of its causes plus noise.
We fit PC with partial correlation and with each nonlinear test, then count the true edges each
finds.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey.data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sample_scm&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sample_scm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;functional&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;nonmonotone&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;density&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;standardize&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;adjacency&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;true&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fisherz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;kci&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;rcot&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cmiknn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;gpdc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indep_test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;adjacency&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
    &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;7&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; true edges, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; extra&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;fisherz: 3 of 8 true edges, 4 extra
    kci: 8 of 8 true edges, 0 extra
   rcot: 8 of 8 true edges, 0 extra
 cmiknn: 8 of 8 true edges, 0 extra
   gpdc: 7 of 8 true edges, 0 extra
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figure class="gf"&gt;&lt;div class="gf-grid"&gt;&lt;div class="gf-cell"&gt;&lt;p class="gf-name"&gt;True graph&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="121.25" y1="40.80" x2="136.07" y2="46.94" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="112.01,36.97 119.34,45.42 123.16,36.18" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="104.97" y1="44.01" x2="139.28" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="143.11,136.07 143.90,124.92 134.66,128.75" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="138.89" y1="61.11" x2="68.18" y2="131.82" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="61.11,138.89 71.72,135.36 64.64,128.28" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="159.20" y1="121.25" x2="153.06" y2="136.07" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="163.03,112.01 154.58,119.34 163.82,123.16" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="136.07" y1="143.11" x2="53.25" y2="108.80" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="44.01,104.97 51.34,113.42 55.16,104.18" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="138.89" y1="138.89" x2="68.18" y2="68.18" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="61.11,61.11 64.64,71.72 71.72,64.64" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="90.81" y1="158.81" x2="48.26" y2="116.26" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="41.19,109.19 44.73,119.80 51.80,112.73" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="91.20" y1="146.75" x2="56.89" y2="63.93" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="95.03,155.99 95.82,144.84 86.58,148.66" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="100.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="148.08" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="168.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="168.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="148.08" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="100.00" cy="168.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="168.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;circle cx="51.92" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;F&lt;/text&gt;&lt;circle cx="32.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;G&lt;/text&gt;&lt;circle cx="51.92" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;H&lt;/text&gt;&lt;/svg&gt;&lt;p class="gf-count"&gt;8 links&lt;/p&gt;&lt;/div&gt;&lt;div class="gf-cell"&gt;&lt;p class="gf-name"&gt;Partial correlation&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="104.97" y1="44.01" x2="139.28" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="143.11,136.07 143.90,124.92 134.66,128.75" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="95.03" y1="44.01" x2="56.89" y2="136.07" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="163.03" y1="112.01" x2="156.88" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="153.06,136.07 161.50,128.75 152.27,124.92" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="145.00" y1="100.00" x2="45.00" y2="100.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="155.00,100.00 145.00,95.00 145.00,105.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="146.75" y1="91.20" x2="63.93" y2="56.89" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="155.99,95.03 148.66,86.58 144.84,95.82" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="95.03" y1="155.99" x2="60.72" y2="73.17" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="56.89,63.93 56.10,75.08 65.34,71.25" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="36.97" y1="87.99" x2="43.12" y2="73.17" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="46.94,63.93 38.50,71.25 47.73,75.08" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="100.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="148.08" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="168.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="168.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="148.08" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="100.00" cy="168.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="168.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;circle cx="51.92" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;F&lt;/text&gt;&lt;circle cx="32.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;G&lt;/text&gt;&lt;circle cx="51.92" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;H&lt;/text&gt;&lt;/svg&gt;&lt;p class="gf-count"&gt;3 of 8 found, 4 extra&lt;/p&gt;&lt;/div&gt;&lt;div class="gf-cell"&gt;&lt;p class="gf-name"&gt;KCI&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="112.01" y1="36.97" x2="126.83" y2="43.12" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="136.07,46.94 128.75,38.50 124.92,47.73" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="104.97" y1="44.01" x2="139.28" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="143.11,136.07 143.90,124.92 134.66,128.75" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.82" y1="68.18" x2="61.11" y2="138.89" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="138.89,61.11 128.28,64.64 135.36,71.72" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="159.20" y1="121.25" x2="153.06" y2="136.07" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="163.03,112.01 154.58,119.34 163.82,123.16" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="136.07" y1="143.11" x2="53.25" y2="108.80" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="44.01,104.97 51.34,113.42 55.16,104.18" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.82" y1="131.82" x2="61.11" y2="61.11" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="138.89,138.89 135.36,128.28 128.28,135.36" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="90.81" y1="158.81" x2="48.26" y2="116.26" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="41.19,109.19 44.73,119.80 51.80,112.73" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="95.03" y1="155.99" x2="56.89" y2="63.93" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;circle cx="100.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="148.08" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="168.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="168.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="148.08" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="100.00" cy="168.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="168.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;circle cx="51.92" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;F&lt;/text&gt;&lt;circle cx="32.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;G&lt;/text&gt;&lt;circle cx="51.92" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;H&lt;/text&gt;&lt;/svg&gt;&lt;p class="gf-count"&gt;8 of 8 found, 0 extra&lt;/p&gt;&lt;/div&gt;&lt;div class="gf-cell"&gt;&lt;p class="gf-name"&gt;RCoT&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="112.01" y1="36.97" x2="136.07" y2="46.94" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="104.97" y1="44.01" x2="139.28" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="143.11,136.07 143.90,124.92 134.66,128.75" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="138.89" y1="61.11" x2="61.11" y2="138.89" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="163.03" y1="112.01" x2="156.88" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="153.06,136.07 161.50,128.75 152.27,124.92" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="136.07" y1="143.11" x2="53.25" y2="108.80" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="44.01,104.97 51.34,113.42 55.16,104.18" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="138.89" y1="138.89" x2="68.18" y2="68.18" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="61.11,61.11 64.64,71.72 71.72,64.64" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="90.81" y1="158.81" x2="48.26" y2="116.26" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="41.19,109.19 44.73,119.80 51.80,112.73" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="95.03" y1="155.99" x2="60.72" y2="73.17" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="56.89,63.93 56.10,75.08 65.34,71.25" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="100.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="148.08" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="168.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="168.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="148.08" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="100.00" cy="168.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="168.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;circle cx="51.92" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;F&lt;/text&gt;&lt;circle cx="32.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;G&lt;/text&gt;&lt;circle cx="51.92" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;H&lt;/text&gt;&lt;/svg&gt;&lt;p class="gf-count"&gt;8 of 8 found, 0 extra&lt;/p&gt;&lt;/div&gt;&lt;div class="gf-cell"&gt;&lt;p class="gf-name"&gt;CMIknn&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="112.01" y1="36.97" x2="136.07" y2="46.94" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="104.97" y1="44.01" x2="139.28" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="143.11,136.07 143.90,124.92 134.66,128.75" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="138.89" y1="61.11" x2="61.11" y2="138.89" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="163.03" y1="112.01" x2="156.88" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="153.06,136.07 161.50,128.75 152.27,124.92" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="136.07" y1="143.11" x2="53.25" y2="108.80" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="44.01,104.97 51.34,113.42 55.16,104.18" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.82" y1="131.82" x2="61.11" y2="61.11" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="138.89,138.89 135.36,128.28 128.28,135.36" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="90.81" y1="158.81" x2="48.26" y2="116.26" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="41.19,109.19 44.73,119.80 51.80,112.73" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="95.03" y1="155.99" x2="56.89" y2="63.93" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;circle cx="100.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="148.08" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="168.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="168.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="148.08" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="100.00" cy="168.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="168.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;circle cx="51.92" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;F&lt;/text&gt;&lt;circle cx="32.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;G&lt;/text&gt;&lt;circle cx="51.92" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;H&lt;/text&gt;&lt;/svg&gt;&lt;p class="gf-count"&gt;8 of 8 found, 0 extra&lt;/p&gt;&lt;/div&gt;&lt;div class="gf-cell"&gt;&lt;p class="gf-name"&gt;GPDC&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="112.01" y1="36.97" x2="136.07" y2="46.94" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="104.97" y1="44.01" x2="139.28" y2="126.83" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="143.11,136.07 143.90,124.92 134.66,128.75" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="138.89" y1="61.11" x2="61.11" y2="138.89" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="159.20" y1="121.25" x2="153.06" y2="136.07" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="163.03,112.01 154.58,119.34 163.82,123.16" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.82" y1="131.82" x2="61.11" y2="61.11" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="138.89,138.89 135.36,128.28 128.28,135.36" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="90.81" y1="158.81" x2="41.19" y2="109.19" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="95.03" y1="155.99" x2="56.89" y2="63.93" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;circle cx="100.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="148.08" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="168.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="168.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="148.08" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="148.08" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="100.00" cy="168.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="100.00" y="168.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;circle cx="51.92" cy="148.08" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="148.08" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;F&lt;/text&gt;&lt;circle cx="32.00" cy="100.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="100.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;G&lt;/text&gt;&lt;circle cx="51.92" cy="51.92" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="51.92" y="51.92" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;H&lt;/text&gt;&lt;/svg&gt;&lt;p class="gf-count"&gt;7 of 8 found, 0 extra&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption&gt;The example's true graph, and the graph PC finds on its 500 rows with each test.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Partial correlation finds 3 of the 8 edges and adds 4 false edges. KCI, RCoT, and CMIknn find all 8,
and GPDC finds 7. None of the nonlinear tests adds a false edge.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="four-tests"&gt;
&lt;h2&gt;Four tests&lt;/h2&gt;
&lt;p&gt;Each test asks whether &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; are independent given a set of variables &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;z&lt;/span&gt;&lt;/code&gt;. They differ in how
they measure dependence and how much computation they need.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;indep_test&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Test&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;How it decides&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Cost&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;kci&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Kernel conditional independence (&lt;a class="reference external" href="https://arxiv.org/abs/1202.3775"&gt;Zhang et al., 2011&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Compares &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; in a Gaussian-kernel feature space after removing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;z&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Grows with the square of the rows or faster; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;rank=k&lt;/span&gt;&lt;/code&gt; caps each kernel at &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;k&lt;/span&gt;&lt;/code&gt; columns&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;rcot&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Randomized conditional correlation (&lt;a class="reference external" href="https://arxiv.org/abs/1702.03877"&gt;Strobl, Zhang, and Visweswaran, 2019&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Approximates the kernel test with quasi-Monte Carlo random Fourier features, sampled for each conditioning set&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Linear in the rows; warns below 500 rows&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;cmiknn&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Nearest-neighbor conditional mutual information (&lt;a class="reference external" href="https://proceedings.mlr.press/v84/runge18a.html"&gt;Runge, 2018&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Estimates conditional mutual information and builds its null by shuffling &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; among neighbors in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;z&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;500 shuffles per test by default&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;gpdc&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Gaussian-process distance correlation (&lt;a class="reference external" href="https://doi.org/10.1126/sciadv.aau4996"&gt;Runge et al., 2019&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Regresses &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; on &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;z&lt;/span&gt;&lt;/code&gt; with Gaussian processes, then tests the residuals with distance correlation&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Grows with the cube of the rows per regression&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Your choice depends on the size of your data:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RCoT&lt;/strong&gt; takes time linear in the number of rows. Every fit we ran finished, up to 50 variables
and 5,000 rows. It warns below 500 rows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;KCI&lt;/strong&gt; reached 20 variables at 2,000 rows in our runs. For more rows, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;KCI(rank=...)&lt;/span&gt;&lt;/code&gt; caps its
cost, and a GPU runs it faster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CMIknn&lt;/strong&gt; is the slowest of the four: 10 variables at 500 rows takes minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GPDC&lt;/strong&gt; reached 20 variables at 1,000 rows in our runs. Its cost grows with the cube of the rows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="how-fast"&gt;
&lt;h2&gt;How fast&lt;/h2&gt;
&lt;p&gt;We compared PC and FCI with each test against the package that implements it: causal-learn for
KCI and RCoT, pcalg with RCIT for RCoT, and tigramite for CMIknn and GPDC. We ran each on additive
data and on nonmonotone data generated with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cos&lt;/span&gt;&lt;/code&gt;, using 20 datasets per size, and timed 5 of them.&lt;/p&gt;
&lt;figure class="ct"&gt;&lt;p class="ct-title"&gt;Each test against its reference package&lt;/p&gt;&lt;div class="ct-scroll"&gt;&lt;table class="ct-table"&gt;&lt;thead&gt;&lt;tr&gt;&lt;td colspan="2"&gt;&lt;/td&gt;&lt;th scope="col" class="ct-ours"&gt;Faster by&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="5"&gt;&lt;span&gt;PC and FCI&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;KCI&lt;small&gt;vs causal-learn&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours"&gt;2.7-5.6x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;RCoT&lt;small&gt;vs causal-learn&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours"&gt;5.9-36x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;RCoT&lt;small&gt;vs pcalg&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours"&gt;9.5-29x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;CMIknn&lt;small&gt;vs tigramite&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours"&gt;2.3-10x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;GPDC&lt;small&gt;vs tigramite&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours"&gt;1.3-1.9x&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;figcaption&gt;The range over sizes of the reference&amp;#x27;s median fit time over Andrey&amp;#x27;s.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Each test runs at the same level of accuracy as its reference package. The release card gives the
errors at every size. On nonmonotone data, partial correlation makes more errors than the empty
graph from 10 variables onward, while every nonlinear test makes fewer errors than the empty graph.&lt;/p&gt;
&lt;p&gt;KCI and CMIknn also run on a CUDA GPU (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ANDREY_BACKEND=cuda&lt;/span&gt;&lt;/code&gt;) and return the same graph as on the
CPU; the &lt;a class="reference internal" href="docs/benchmarks/0.8.0.html"&gt;&lt;span class="std std-doc"&gt;0.8.0 release card&lt;/span&gt;&lt;/a&gt; lists the speedups and the hardware.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="limits"&gt;
&lt;h2&gt;Limits&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measured sizes.&lt;/strong&gt; PC up to 20 variables at 2,000 rows with KCI, 50 variables at 5,000 rows
with RCoT, 20 variables at 500 rows with CMIknn, and 20 variables at 1,000 rows with GPDC. FCI
with KCI and RCoT at 5 and 10 variables. The release card lists every size and the hardware.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost.&lt;/strong&gt; KCI and GPDC costs grow quickly with the number of rows; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;KCI(rank=...)&lt;/span&gt;&lt;/code&gt; caps KCI’s
cost. CMIknn grows with the number of variables and rows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;0.8.0 also brings background knowledge for every search, score defaults that follow the data, and
faster categorical tests. The &lt;a class="reference internal" href="docs/benchmarks/0.8.0.html"&gt;&lt;span class="std std-doc"&gt;0.8.0 release card&lt;/span&gt;&lt;/a&gt; and the
&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;changelog&lt;/span&gt;&lt;/a&gt; give the details.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="help-us-build-it"&gt;
&lt;h2&gt;Help us build it&lt;/h2&gt;
&lt;p&gt;Andrey is in alpha. The API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need.
We also welcome pull requests. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; explains how
you can set up the development environment.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261023-andrey-0-8-0.html"/>
    <summary>A constraint-based method learns a graph from independence tests. Its result depends on the test.
The default test, partial correlation, detects only linear dependence. When an effect is nonlinear,
it can miss an edge or add a false one. In 0.8.0, you can use PC, FCI, GFCI, and CD-NOD with four
supported tests that detect nonlinear dependence: KCI, RCoT, CMIknn, and GPDC. Each runs faster than
the package we measured it against at every size we timed, at the same level of accuracy: PC with
RCoT runs more than 5x faster than causal-learn and more than 9x faster than pcalg.</summary>
    <category term="Constraint-based" label="Constraint-based"/>
    <category term="Nonlineardata" label="Nonlinear data"/>
    <category term="Release" label="Release"/>
    <published>2026-10-23T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261023-andrey-0-9-0.html</id>
    <title>Acceleration</title>
    <updated>2026-10-23T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="acceleration"&gt;

&lt;p&gt;Andrey was already the fastest causal discovery package. 0.9.0 is our biggest speed breakthrough
yet, the fastest causal discovery we have ever shipped: BOSS runs up to 8.1x faster than before,
exact search 5.2x, and FCI and GFCI 3.1x, and every graph is exactly the same. Every release so far
added methods or data; this one is all speed. This post is the first of two. Here the rocket lifts
off. The next release, 0.10.0, follows it up to the boundary: how far each method now goes before it
stops finishing, or stops being accurate.&lt;/p&gt;
&lt;section id="tl-dr"&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;The fastest Andrey ever. Speedups over 0.8.0, at the best of the sizes we measured:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Permutation-based: BOSS up to 8.1x, GRaSP 3.8x.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Score-based: exact search 5.2x, GES and XGES 1.7x.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Constraint-based: FCI and GFCI 3.1x, PC 1.8x, CD-NOD 1.6x.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LiNGAM, pairwise, and temporal: LPCMCI 2.8x, PNL 2.2x, DirectLiNGAM and
MultiGroupDirectLiNGAM 1.9x.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exactly the same answers: in all 1,216 identity checks, 27 methods in five column orders with
one worker and four, 0.9.0 returns exactly 0.8.0’s output.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PC, FCI, GFCI, and CD-NOD spread their largest skeleton passes over worker processes: with four
workers, CD-NOD runs up to 4.6x faster than 0.8.0 and PC 2.6x. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey&lt;/span&gt; &lt;span class="pre"&gt;calibrate&lt;/span&gt;&lt;/code&gt; measures when
the workers pay off on your machine.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="where-the-time-went"&gt;
&lt;h2&gt;Where the time went&lt;/h2&gt;
&lt;p&gt;A causal discovery method spends its time on three jobs: tests or scores, the bookkeeping of the
graph it searches, and the memory that holds them. The
&lt;a class="reference internal" href="blog/20261008-why-andrey-is-fast.html"&gt;&lt;span class="std std-doc"&gt;Why Andrey is fast&lt;/span&gt;&lt;/a&gt; post shows the ideas we started from. 0.9.0
applies them further, without changing a single result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ask in stacks.&lt;/strong&gt; PC, FCI, GFCI, and CD-NOD test whether two variables are independent given a
set of others. 0.9.0 hands the test all of a pair’s conditioning sets at once, together with
those of the pairs next to it, so one array operation answers what took many. Exact search does
the same for its scores: it solves the equations of up to 4,096 parent sets in one call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use every core.&lt;/strong&gt; With more than one worker, the largest passes of the PC, FCI, GFCI, and CD-NOD
skeleton search run in worker processes. Small passes stay in one process, where starting
workers would cost more than it saves. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey&lt;/span&gt; &lt;span class="pre"&gt;calibrate&lt;/span&gt;&lt;/code&gt; measures that cutoff on your machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copy less.&lt;/strong&gt; A search tries many changes to its graph and keeps few. XGES now copies only the
part of the graph a branch changes. After each move, GES updates only the edges the move can reach
instead of rebuilding the whole graph, and keeps each reach set until a move can change it. BOSS
and GRaSP keep one tuple of candidate parents, and the set of allowed parents, instead of building
them again on every step.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build once.&lt;/strong&gt; FCI computes one reach set per variable for its possible-d-separation step, and
builds its neighbor lists and reach sets once for each version of the graph; GFCI builds each
node’s neighbor list once. LPCMCI keeps each node’s neighbors and paths for each version of the
graph instead of building them for every query. Exact search looks up each variable’s best parent
set in a table built once per fit. DirectLiNGAM and the methods built on it compute each pair’s
residual entropy once per step instead of twice. PNL builds each variable’s kernel once and can run
its independence tests on a CUDA GPU.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hold less.&lt;/strong&gt; The first pass of PC, FCI, GFCI, and CD-NOD builds its queries one stack at a time
instead of listing every pair’s conditioning sets first, which on a complete graph of 1,000
variables held gigabytes. ANM and the KCI test build their kernels in blocks, in one reused matrix.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="an-example"&gt;
&lt;h2&gt;An example&lt;/h2&gt;
&lt;p&gt;The answer does not depend on how many workers you use. We fit GES on 200 variables with one worker
and with four, and compare the graphs.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey.data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sample_scm&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# worker processes re-import this script&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sample_scm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;density&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# The same fit with one worker and with four.&lt;/span&gt;
    &lt;span class="n"&gt;graphs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;graphs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;adjacency&lt;/span&gt;

    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;same graph with 1 and 4 workers:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array_equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graphs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;graphs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;same graph with 1 and 4 workers: True
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="how-much-faster"&gt;
&lt;h2&gt;How much faster&lt;/h2&gt;
&lt;p&gt;We timed each method in 0.9.0 against Andrey 0.8.0 on the same datasets, side by side on the same
machine.&lt;/p&gt;
&lt;figure class="sr"&gt;&lt;p class="sr-title"&gt;How much faster than Andrey 0.8.0&lt;/p&gt;&lt;svg viewBox="0 0 800 546" role="img" aria-label="Each method's speedup over Andrey 0.8.0, from its slowest to its fastest size"&gt;&lt;line class="sr-grid" x1="170.0" x2="170.0" y1="0" y2="524"/&gt;&lt;text class="sr-tick" x="170.0" y="538" text-anchor="middle"&gt;1x&lt;/text&gt;&lt;line class="sr-grid" x1="276.1" x2="276.1" y1="0" y2="524"/&gt;&lt;text class="sr-tick" x="276.1" y="538" text-anchor="middle"&gt;1.5x&lt;/text&gt;&lt;line class="sr-grid" x1="351.4" x2="351.4" y1="0" y2="524"/&gt;&lt;text class="sr-tick" x="351.4" y="538" text-anchor="middle"&gt;2x&lt;/text&gt;&lt;line class="sr-grid" x1="457.4" x2="457.4" y1="0" y2="524"/&gt;&lt;text class="sr-tick" x="457.4" y="538" text-anchor="middle"&gt;3x&lt;/text&gt;&lt;line class="sr-grid" x1="591.1" x2="591.1" y1="0" y2="524"/&gt;&lt;text class="sr-tick" x="591.1" y="538" text-anchor="middle"&gt;5x&lt;/text&gt;&lt;line class="sr-grid" x1="679.1" x2="679.1" y1="0" y2="524"/&gt;&lt;text class="sr-tick" x="679.1" y="538" text-anchor="middle"&gt;7x&lt;/text&gt;&lt;text class="sr-family" x="0" y="20"&gt;Constraint&lt;/text&gt;&lt;text class="sr-method" x="162" y="46" text-anchor="end"&gt;CD-NOD&lt;/text&gt;&lt;rect class="sr-bar" x="260.6" y="37" width="35.3" height="14" rx="6"&gt;&lt;title&gt;CD-NOD: 1.4x to 1.6x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="301.9" y="47"&gt;1.6x&lt;/text&gt;&lt;text class="sr-method" x="162" y="70" text-anchor="end"&gt;FCI&lt;/text&gt;&lt;rect class="sr-bar" x="457.4" y="61" width="12.9" height="14" rx="6"&gt;&lt;title&gt;FCI: 2.9x to 3.1x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="476.2" y="71"&gt;3.1x&lt;/text&gt;&lt;text class="sr-method" x="162" y="94" text-anchor="end"&gt;GFCI&lt;/text&gt;&lt;rect class="sr-bar" x="381.5" y="85" width="91.3" height="14" rx="6"&gt;&lt;title&gt;GFCI: 2.2x to 3.1x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="478.8" y="95"&gt;3.1x&lt;/text&gt;&lt;text class="sr-method" x="162" y="118" text-anchor="end"&gt;PC&lt;/text&gt;&lt;rect class="sr-bar" x="310.2" y="109" width="26.9" height="14" rx="6"&gt;&lt;title&gt;PC: 1.7x to 1.8x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="343.1" y="119"&gt;1.8x&lt;/text&gt;&lt;text class="sr-family" x="0" y="146"&gt;Score&lt;/text&gt;&lt;text class="sr-method" x="162" y="172" text-anchor="end"&gt;Exact search&lt;/text&gt;&lt;rect class="sr-bar" x="595.3" y="163" width="10.4" height="14" rx="6"&gt;&lt;title&gt;Exact search: 5x to 5.2x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="611.7" y="173"&gt;5.2x&lt;/text&gt;&lt;text class="sr-method" x="162" y="196" text-anchor="end"&gt;GES&lt;/text&gt;&lt;rect class="sr-bar" x="248.1" y="187" width="61.8" height="14" rx="6"&gt;&lt;title&gt;GES: 1.3x to 1.7x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="315.9" y="197"&gt;1.7x&lt;/text&gt;&lt;text class="sr-method" x="162" y="220" text-anchor="end"&gt;XGES&lt;/text&gt;&lt;rect class="sr-bar" x="201.3" y="211" width="108.3" height="14" rx="6"&gt;&lt;title&gt;XGES: 1.1x to 1.7x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="315.6" y="221"&gt;1.7x&lt;/text&gt;&lt;text class="sr-family" x="0" y="248"&gt;Permutation&lt;/text&gt;&lt;text class="sr-method" x="162" y="274" text-anchor="end"&gt;BOSS&lt;/text&gt;&lt;rect class="sr-bar" x="455.9" y="265" width="264.5" height="14" rx="6"&gt;&lt;title&gt;BOSS: 2.9x to 8.1x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="726.3" y="275"&gt;8.1x&lt;/text&gt;&lt;text class="sr-method" x="162" y="298" text-anchor="end"&gt;GRaSP&lt;/text&gt;&lt;rect class="sr-bar" x="525.4" y="289" width="4.0" height="14" rx="6"&gt;&lt;title&gt;GRaSP: 3.8x to 3.8x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="531.4" y="299"&gt;3.8x&lt;/text&gt;&lt;text class="sr-family" x="0" y="326"&gt;LiNGAM&lt;/text&gt;&lt;text class="sr-method" x="162" y="352" text-anchor="end"&gt;DirectLiNGAM&lt;/text&gt;&lt;rect class="sr-bar" x="330.3" y="343" width="15.8" height="14" rx="6"&gt;&lt;title&gt;DirectLiNGAM: 1.8x to 1.9x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="352.1" y="353"&gt;1.9x&lt;/text&gt;&lt;text class="sr-method" x="162" y="376" text-anchor="end"&gt;MultiGroupDirectLiNGAM&lt;/text&gt;&lt;rect class="sr-bar" x="318.8" y="367" width="28.2" height="14" rx="6"&gt;&lt;title&gt;MultiGroupDirectLiNGAM: 1.7x to 1.9x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="353.0" y="377"&gt;1.9x&lt;/text&gt;&lt;text class="sr-family" x="0" y="404"&gt;Pairwise&lt;/text&gt;&lt;text class="sr-method" x="162" y="430" text-anchor="end"&gt;PNL&lt;/text&gt;&lt;rect class="sr-bar" x="381.6" y="421" width="4.0" height="14" rx="6"&gt;&lt;title&gt;PNL: 2.2x to 2.2x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="391.2" y="431"&gt;2.2x&lt;/text&gt;&lt;text class="sr-family" x="0" y="458"&gt;Temporal&lt;/text&gt;&lt;text class="sr-method" x="162" y="484" text-anchor="end"&gt;LongitudinalLiNGAM&lt;/text&gt;&lt;rect class="sr-bar" x="226.9" y="475" width="18.9" height="14" rx="6"&gt;&lt;title&gt;LongitudinalLiNGAM: 1.2x to 1.3x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="251.8" y="485"&gt;1.3x&lt;/text&gt;&lt;text class="sr-method" x="162" y="508" text-anchor="end"&gt;LPCMCI&lt;/text&gt;&lt;rect class="sr-bar" x="369.7" y="499" width="76.1" height="14" rx="6"&gt;&lt;title&gt;LPCMCI: 2.1x to 2.8x faster than 0.8.0&lt;/title&gt;&lt;/rect&gt;&lt;text class="sr-value" x="451.8" y="509"&gt;2.8x&lt;/text&gt;&lt;/svg&gt;&lt;figcaption&gt;Each bar runs from a method's smallest to its largest speedup over the measured sizes, with one worker. 1x is as fast as 0.8.0.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The gains grow with the problem: BOSS is 2.9x faster at 200 variables and 8.1x at 400, and GFCI 2.2x
at 200 and 3.1x at 400, because the work we removed, copies and repeated bookkeeping, grows faster
than the tests and scores themselves. Every one of these fits returned exactly 0.8.0’s output.&lt;/p&gt;
&lt;p&gt;GES now also runs 1.6x to 5.4x faster than pcalg’s, whose GES is written in C++, with the same or
fewer errors at every size. The &lt;a class="reference internal" href="docs/benchmarks/0.9.0.html"&gt;&lt;span class="std std-doc"&gt;0.9.0 release card&lt;/span&gt;&lt;/a&gt; gives every size,
the conditions, and the comparison with pcalg.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="limits"&gt;
&lt;h2&gt;Limits&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measured speedups.&lt;/strong&gt; A speedup depends on the data, the settings, and the machine. Methods
without speed work in 0.9.0 run as fast as in 0.8.0; the release card names them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Same answers.&lt;/strong&gt; In the identity checks, the LiNGAM methods return 0.8.0’s output exactly.
Their internal scores can differ in the last bits, which could change a causal order only at an
exact tie between two candidates. With one worker, PNL lets its linear algebra use every core, so
on a machine with several cores its result can differ in the last digits from that with several
workers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workers.&lt;/strong&gt; Worker processes repay their start-up cost only on large passes. On a small
problem, one worker is as fast.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="help-us-build-it"&gt;
&lt;h2&gt;Help us build it&lt;/h2&gt;
&lt;p&gt;Andrey is in alpha. The API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need.
We also welcome pull requests. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; explains how
you can set up the development environment.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/configuration.html"&gt;&lt;span class="std std-doc"&gt;Configuration&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261023-andrey-0-9-0.html"/>
    <summary>Andrey was already the fastest causal discovery package. 0.9.0 is our biggest speed breakthrough
yet, the fastest causal discovery we have ever shipped: BOSS runs up to 8.1x faster than before,
exact search 5.2x, and FCI and GFCI 3.1x, and every graph is exactly the same. Every release so far
added methods or data; this one is all speed. This post is the first of two. Here the rocket lifts
off. The next release, 0.10.0, follows it up to the boundary: how far each method now goes before it
stops finishing, or stops being accurate.</summary>
    <category term="Release" label="Release"/>
    <category term="Speed" label="Speed"/>
    <published>2026-10-23T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261019-andrey-0-7-0.html</id>
    <title>Permutation invariance</title>
    <updated>2026-10-19T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="permutation-invariance"&gt;

&lt;p&gt;Give two people the same table with its columns in different orders. Ask each to learn a causal
graph. They should get the same graph. Column order carries no information: it comes from a database
export, a DataFrame, or whoever wrote the CSV. A method whose graph does not depend on that order is
permutation invariant.&lt;/p&gt;
&lt;p&gt;Many causal-discovery methods are not invariant. When two steps look equally good, they take the
first one they find, and “first” means the leftmost column.
&lt;a class="reference external" href="https://jmlr.org/papers/v15/colombo14a.html"&gt;Colombo and Maathuis (2014)&lt;/a&gt; showed this for PC. It
matters for three reasons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reproducibility.&lt;/strong&gt; Two analysts with the same data get different graphs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trust.&lt;/strong&gt; An edge that flips when you reorder the columns was never supported by the data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Benchmarks.&lt;/strong&gt; Data generators often write columns in an order tied to the graph, so a method
that favors the first column gains or loses accuracy it did not earn.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We plan to make Andrey the first package in which every method is permutation invariant. As a first
step, 0.7.0 makes nine methods invariant: their choices follow the data, not the column order. Our
benchmark also shuffles the columns, so their order cannot skew the results.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Andrey 0.7.0 brings permutation invariance to PC, FCI, GFCI, CD-NOD, hill climbing, tabu search,
BOSS, GRaSP, and GIN: reordering the columns in your dataset no longer changes the discovered
causal graph.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It costs almost no speed: each fix reuses work the method already does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By eliminating sensitivity to arbitrary column ordering, Andrey makes causal discovery more
reproducible, reliable, and trustworthy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="ct"&gt;&lt;p class="ct-title"&gt;Datasets where reordering the columns changed the graph (lower is better)&lt;/p&gt;&lt;div class="ct-scroll"&gt;&lt;table class="ct-table"&gt;&lt;thead&gt;&lt;tr&gt;&lt;td colspan="2"&gt;&lt;/td&gt;&lt;th scope="col" class="ct-ours"&gt;Andrey 0.7.0&lt;/th&gt;&lt;th scope="col"&gt;Andrey 0.6.0&lt;/th&gt;&lt;th scope="col"&gt;causal-learn&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="4"&gt;&lt;span&gt;Score-based&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;Hill climbing, BDeu&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;97%&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Hill climbing, BIC&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;98%&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Tabu search, BDeu&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;75%&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Tabu search, BIC&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;79%&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="2"&gt;&lt;span&gt;Permutation&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;BOSS&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;97%&lt;/td&gt;&lt;td class=""&gt;91%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;GRaSP&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;99%&lt;/td&gt;&lt;td class=""&gt;100%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="1"&gt;&lt;span&gt;Hidden causes&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;GIN&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;73%&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;section id="how-we-found-it"&gt;
&lt;h2&gt;How we found it&lt;/h2&gt;
&lt;p&gt;It started with hill climbing. On the 0.6.0 categorical benchmark, pgmpy’s hill climbing had fewer
errors than Andrey’s on 17 datasets. We went looking for a bug and found a tie: adding &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; and
adding &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; can score exactly the same, and Andrey gave the tie to whichever column came first.
Reverse the columns, and Andrey had no more errors than pgmpy on 14 of the 17.&lt;/p&gt;
&lt;p&gt;Then we checked our own data. Our generators wrote columns in an order tied to the graph: on a
scale-free graph, the biggest hub came first. That order misled us. A better PC rule looked worse
until we shuffled the columns.&lt;/p&gt;
&lt;figure class="cg"&gt;&lt;div class="cg-panels"&gt;&lt;div class="cg-panel"&gt;&lt;p class="co-title"&gt;Before 0.7.0: column 1 is the hub&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 210" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="127.00" y1="32.00" x2="69.41" y2="32.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="137.00,32.00 127.00,27.00 127.00,37.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="163.00" y1="32.00" x2="220.59" y2="32.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="230.59,32.00 220.59,27.00 220.59,37.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.86" y1="46.15" x2="66.66" y2="97.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="139.75,40.00 128.79,42.20 134.94,50.09" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="160.25" y1="40.00" x2="225.45" y2="90.85" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="233.34,97.00 228.53,86.91 222.38,94.80" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="137.59" y1="51.36" x2="63.43" y2="167.06" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="142.98,42.94 133.38,48.66 141.80,54.06" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="157.02" y1="42.94" x2="231.18" y2="158.64" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="236.57,167.06 235.39,155.94 226.97,161.34" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="67.94" y1="38.00" x2="129.59" y2="70.06" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="138.47,74.67 131.90,65.62 127.29,74.49" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="65.42" y1="41.37" x2="134.06" y2="112.75" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="140.99,119.96 137.66,109.29 130.45,116.22" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="63.43" y1="42.94" x2="137.59" y2="158.64" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="142.98,167.06 141.80,155.94 133.38,161.34" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="150.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;1&lt;/text&gt;&lt;circle cx="56.41" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.41" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;2&lt;/text&gt;&lt;circle cx="243.59" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="243.59" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;3&lt;/text&gt;&lt;circle cx="150.00" cy="80.67" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="80.67" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;4&lt;/text&gt;&lt;circle cx="56.41" cy="105.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.41" y="105.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;5&lt;/text&gt;&lt;circle cx="243.59" cy="105.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="243.59" y="105.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;6&lt;/text&gt;&lt;circle cx="150.00" cy="129.33" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="129.33" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;7&lt;/text&gt;&lt;circle cx="56.41" cy="178.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.41" y="178.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;8&lt;/text&gt;&lt;circle cx="243.59" cy="178.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="243.59" y="178.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;9&lt;/text&gt;&lt;circle cx="150.00" cy="178.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="178.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;10&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="cg-panel"&gt;&lt;p class="co-title"&gt;0.7.0: columns shuffled&lt;/p&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 210" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="127.00" y1="32.00" x2="69.41" y2="32.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="137.00,32.00 127.00,27.00 127.00,37.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="163.00" y1="32.00" x2="220.59" y2="32.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="230.59,32.00 220.59,27.00 220.59,37.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.86" y1="46.15" x2="66.66" y2="97.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="139.75,40.00 128.79,42.20 134.94,50.09" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="160.25" y1="40.00" x2="225.45" y2="90.85" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="233.34,97.00 228.53,86.91 222.38,94.80" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="137.59" y1="51.36" x2="63.43" y2="167.06" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="142.98,42.94 133.38,48.66 141.80,54.06" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="157.02" y1="42.94" x2="231.18" y2="158.64" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="236.57,167.06 235.39,155.94 226.97,161.34" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="67.94" y1="38.00" x2="129.59" y2="70.06" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="138.47,74.67 131.90,65.62 127.29,74.49" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="65.42" y1="41.37" x2="134.06" y2="112.75" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="140.99,119.96 137.66,109.29 130.45,116.22" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="63.43" y1="42.94" x2="137.59" y2="158.64" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="142.98,167.06 141.80,155.94 133.38,161.34" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="150.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;9&lt;/text&gt;&lt;circle cx="56.41" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.41" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;1&lt;/text&gt;&lt;circle cx="243.59" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="243.59" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;8&lt;/text&gt;&lt;circle cx="150.00" cy="80.67" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="80.67" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;2&lt;/text&gt;&lt;circle cx="56.41" cy="105.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.41" y="105.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;4&lt;/text&gt;&lt;circle cx="243.59" cy="105.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="243.59" y="105.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;7&lt;/text&gt;&lt;circle cx="150.00" cy="129.33" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="129.33" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;3&lt;/text&gt;&lt;circle cx="56.41" cy="178.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.41" y="178.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;5&lt;/text&gt;&lt;circle cx="243.59" cy="178.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="243.59" y="178.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;6&lt;/text&gt;&lt;circle cx="150.00" cy="178.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="150.00" y="178.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;10&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="cg-panel cg-chart"&gt;&lt;p class="co-title"&gt;p-value rule minus column-order rule, endpoint errors&lt;/p&gt;&lt;div class="cg-row"&gt;&lt;p class="cg-name"&gt;Columns as generated&lt;/p&gt;&lt;div class="cg-pair"&gt;&lt;div class="cg-track"&gt;&lt;span class="cg-bar is-worse" style="--cg-w: 7.7%"&gt;&lt;/span&gt;&lt;b class="cg-v"&gt;+1.0&lt;/b&gt;&lt;/div&gt;&lt;div class="cg-track"&gt;&lt;span class="cg-bar is-worse" style="--cg-w: 36.9%"&gt;&lt;/span&gt;&lt;b class="cg-v"&gt;+4.8&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="cg-row"&gt;&lt;p class="cg-name"&gt;Columns shuffled&lt;/p&gt;&lt;div class="cg-pair"&gt;&lt;div class="cg-track"&gt;&lt;span class="cg-bar is-better" style="--cg-w: 7.7%"&gt;&lt;/span&gt;&lt;b class="cg-v"&gt;-1.0&lt;/b&gt;&lt;/div&gt;&lt;div class="cg-track"&gt;&lt;span class="cg-bar is-better" style="--cg-w: 23.8%"&gt;&lt;/span&gt;&lt;b class="cg-v"&gt;-3.1&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="cg-axis"&gt;&lt;span&gt;fewer errors&lt;/span&gt;&lt;span&gt;more errors&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption&gt;Each node is numbered by its column.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="why-it-happens"&gt;
&lt;h2&gt;Why it happens&lt;/h2&gt;
&lt;p&gt;Column order enters when a method takes the first of two equally good steps. In PC, two colliders
can disagree about one edge, and whichever PC orients first wins.&lt;/p&gt;
&lt;p&gt;Below, A causes B, C, and D, and each of them causes E. A and E look independent given B, with a
p-value of 0.052, just above the significance level of 0.05, so PC also proposes two false
colliders, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;A&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;C&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="pre"&gt;E&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;A&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;D&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="pre"&gt;E&lt;/span&gt;&lt;/code&gt;. The column-order rule visits colliders by the column
they point into, left to right: whenever C or D sits left of E, a false collider wins. The p-value
rule starts with the most clearly separated pair, so the true colliders always win.&lt;/p&gt;
&lt;p&gt;Click a column to move it first, or shuffle them all.&lt;/p&gt;
&lt;figure class="co" data-states="{&amp;quot;ABCDE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,B E D-,C E D-&amp;quot;,&amp;quot;ABCED&amp;quot;:&amp;quot;1|A C E+,B E C-,B E D+,C E D-,A D E-&amp;quot;,&amp;quot;ABDCE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,B E C-,C E D-&amp;quot;,&amp;quot;ABDEC&amp;quot;:&amp;quot;2|A D E+,B E D-,B E C+,C E D-,A C E-&amp;quot;,&amp;quot;ABECD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;ABEDC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;ACBDE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,C E D-,B E D-&amp;quot;,&amp;quot;ACBED&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;ACDBE&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;ACDEB&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;ACEBD&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;ACEDB&amp;quot;:&amp;quot;1|A C E+,C E D-,B E C-,B E D+,A D E-&amp;quot;,&amp;quot;ADBCE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,C E D-,B E C-&amp;quot;,&amp;quot;ADBEC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;ADCBE&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;ADCEB&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;ADEBC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;ADECB&amp;quot;:&amp;quot;2|A D E+,C E D-,B E D-,B E C+,A C E-&amp;quot;,&amp;quot;AEBCD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;AEBDC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;AECBD&amp;quot;:&amp;quot;3|B E C+,C E D+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;AECDB&amp;quot;:&amp;quot;3|C E D+,B E C+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;AEDBC&amp;quot;:&amp;quot;3|B E D+,C E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;AEDCB&amp;quot;:&amp;quot;3|C E D+,B E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;BACDE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,B E D-,C E D-&amp;quot;,&amp;quot;BACED&amp;quot;:&amp;quot;1|A C E+,B E C-,B E D+,C E D-,A D E-&amp;quot;,&amp;quot;BADCE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,B E C-,C E D-&amp;quot;,&amp;quot;BADEC&amp;quot;:&amp;quot;2|A D E+,B E D-,B E C+,C E D-,A C E-&amp;quot;,&amp;quot;BAECD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;BAEDC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;BCADE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,B E D-,C E D-&amp;quot;,&amp;quot;BCAED&amp;quot;:&amp;quot;1|A C E+,B E C-,B E D+,C E D-,A D E-&amp;quot;,&amp;quot;BCDAE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,B E D-,C E D-&amp;quot;,&amp;quot;BCDEA&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,B E D-,C E D-&amp;quot;,&amp;quot;BCEAD&amp;quot;:&amp;quot;1|A C E+,B E C-,B E D+,C E D-,A D E-&amp;quot;,&amp;quot;BCEDA&amp;quot;:&amp;quot;1|A C E+,B E C-,B E D+,C E D-,A D E-&amp;quot;,&amp;quot;BDACE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,B E C-,C E D-&amp;quot;,&amp;quot;BDAEC&amp;quot;:&amp;quot;2|A D E+,B E D-,B E C+,C E D-,A C E-&amp;quot;,&amp;quot;BDCAE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,B E C-,C E D-&amp;quot;,&amp;quot;BDCEA&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,B E C-,C E D-&amp;quot;,&amp;quot;BDEAC&amp;quot;:&amp;quot;2|A D E+,B E D-,B E C+,C E D-,A C E-&amp;quot;,&amp;quot;BDECA&amp;quot;:&amp;quot;2|A D E+,B E D-,B E C+,C E D-,A C E-&amp;quot;,&amp;quot;BEACD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;BEADC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;BECAD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;BECDA&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;BEDAC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;BEDCA&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;CABDE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,C E D-,B E D-&amp;quot;,&amp;quot;CABED&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CADBE&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CADEB&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CAEBD&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CAEDB&amp;quot;:&amp;quot;1|A C E+,C E D-,B E C-,B E D+,A D E-&amp;quot;,&amp;quot;CBADE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,C E D-,B E D-&amp;quot;,&amp;quot;CBAED&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CBDAE&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,C E D-,B E D-&amp;quot;,&amp;quot;CBDEA&amp;quot;:&amp;quot;0|A C E+,A D E+,B E C-,C E D-,B E D-&amp;quot;,&amp;quot;CBEAD&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CBEDA&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CDABE&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CDAEB&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CDBAE&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CDBEA&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CDEAB&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CDEBA&amp;quot;:&amp;quot;0|A C E+,A D E+,C E D-,B E C-,B E D-&amp;quot;,&amp;quot;CEABD&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CEADB&amp;quot;:&amp;quot;1|A C E+,C E D-,B E C-,B E D+,A D E-&amp;quot;,&amp;quot;CEBAD&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CEBDA&amp;quot;:&amp;quot;1|A C E+,B E C-,C E D-,B E D+,A D E-&amp;quot;,&amp;quot;CEDAB&amp;quot;:&amp;quot;1|A C E+,C E D-,B E C-,B E D+,A D E-&amp;quot;,&amp;quot;CEDBA&amp;quot;:&amp;quot;1|A C E+,C E D-,B E C-,B E D+,A D E-&amp;quot;,&amp;quot;DABCE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,C E D-,B E C-&amp;quot;,&amp;quot;DABEC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DACBE&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DACEB&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DAEBC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DAECB&amp;quot;:&amp;quot;2|A D E+,C E D-,B E D-,B E C+,A C E-&amp;quot;,&amp;quot;DBACE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,C E D-,B E C-&amp;quot;,&amp;quot;DBAEC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DBCAE&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,C E D-,B E C-&amp;quot;,&amp;quot;DBCEA&amp;quot;:&amp;quot;0|A D E+,A C E+,B E D-,C E D-,B E C-&amp;quot;,&amp;quot;DBEAC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DBECA&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DCABE&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DCAEB&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DCBAE&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DCBEA&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DCEAB&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DCEBA&amp;quot;:&amp;quot;0|A D E+,A C E+,C E D-,B E D-,B E C-&amp;quot;,&amp;quot;DEABC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DEACB&amp;quot;:&amp;quot;2|A D E+,C E D-,B E D-,B E C+,A C E-&amp;quot;,&amp;quot;DEBAC&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DEBCA&amp;quot;:&amp;quot;2|A D E+,B E D-,C E D-,B E C+,A C E-&amp;quot;,&amp;quot;DECAB&amp;quot;:&amp;quot;2|A D E+,C E D-,B E D-,B E C+,A C E-&amp;quot;,&amp;quot;DECBA&amp;quot;:&amp;quot;2|A D E+,C E D-,B E D-,B E C+,A C E-&amp;quot;,&amp;quot;EABCD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;EABDC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;EACBD&amp;quot;:&amp;quot;3|B E C+,C E D+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;EACDB&amp;quot;:&amp;quot;3|C E D+,B E C+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;EADBC&amp;quot;:&amp;quot;3|B E D+,C E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;EADCB&amp;quot;:&amp;quot;3|C E D+,B E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;EBACD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;EBADC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;EBCAD&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;EBCDA&amp;quot;:&amp;quot;3|B E C+,B E D+,C E D+,A C E-,A D E-&amp;quot;,&amp;quot;EBDAC&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;EBDCA&amp;quot;:&amp;quot;3|B E D+,B E C+,C E D+,A D E-,A C E-&amp;quot;,&amp;quot;ECABD&amp;quot;:&amp;quot;3|B E C+,C E D+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;ECADB&amp;quot;:&amp;quot;3|C E D+,B E C+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;ECBAD&amp;quot;:&amp;quot;3|B E C+,C E D+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;ECBDA&amp;quot;:&amp;quot;3|B E C+,C E D+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;ECDAB&amp;quot;:&amp;quot;3|C E D+,B E C+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;ECDBA&amp;quot;:&amp;quot;3|C E D+,B E C+,B E D+,A C E-,A D E-&amp;quot;,&amp;quot;EDABC&amp;quot;:&amp;quot;3|B E D+,C E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;EDACB&amp;quot;:&amp;quot;3|C E D+,B E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;EDBAC&amp;quot;:&amp;quot;3|B E D+,C E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;EDBCA&amp;quot;:&amp;quot;3|B E D+,C E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;EDCAB&amp;quot;:&amp;quot;3|C E D+,B E D+,B E C+,A D E-,A C E-&amp;quot;,&amp;quot;EDCBA&amp;quot;:&amp;quot;3|C E D+,B E D+,B E C+,A D E-,A C E-&amp;quot;}"&gt;&lt;div class="co-setup"&gt;&lt;div class="co-graph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 130" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="68.07" y1="59.00" x2="99.59" y2="42.61" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="108.47,38.00 97.29,38.18 101.90,47.05" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="69.54" y1="65.00" x2="97.00" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="107.00,65.00 97.00,60.00 97.00,70.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="68.07" y1="71.00" x2="99.59" y2="87.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="108.47,92.00 101.90,82.95 97.29,91.82" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.53" y1="38.00" x2="163.06" y2="54.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,59.00 165.36,49.95 160.75,58.82" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="133.00" y1="65.00" x2="160.46" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="170.46,65.00 160.46,60.00 160.46,70.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.53" y1="92.00" x2="163.06" y2="75.61" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,71.00 160.75,71.18 165.36,80.05" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="56.54" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.54" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="120.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="120.00" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="120.00" cy="98.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="98.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="183.46" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="183.46" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;p class="co-note"&gt;&lt;b&gt;The data.&lt;/b&gt; A causes B, C, and D, and each of them causes E. PC proposes five colliders; two are false.&lt;/p&gt;&lt;/div&gt;&lt;section class="co-lane co-old"&gt;&lt;div class="co-head"&gt;&lt;p class="co-title"&gt;0.6.0: column-order rule&lt;/p&gt;&lt;div class="co-actions"&gt;&lt;button type="button" class="co-shuffle" hidden&gt;Shuffle the columns&lt;/button&gt;&lt;button type="button" class="co-reset" hidden&gt;Original order&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="co-rule"&gt;Visits the colliders &lt;b&gt;column by column&lt;/b&gt;, left to right: each waits under the column it points into. &lt;span class="co-hint" hidden&gt;Click a column to move it first.&lt;/span&gt;&lt;/p&gt;&lt;div class="co-body"&gt;&lt;ol class="co-board" aria-label="Column order"&gt;&lt;li class="co-col" data-column="A"&gt;&lt;button type="button" class="co-chip" aria-label="Move column A first"&gt;A&lt;/button&gt;&lt;ol class="co-cards"&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li class="co-col" data-column="B"&gt;&lt;button type="button" class="co-chip" aria-label="Move column B first"&gt;B&lt;/button&gt;&lt;ol class="co-cards"&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li class="co-col" data-column="C"&gt;&lt;button type="button" class="co-chip" aria-label="Move column C first"&gt;C&lt;/button&gt;&lt;ol class="co-cards"&gt;&lt;li class="co-card is-false" data-collider="A C E"&gt;&lt;span class="co-step"&gt;1&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;A&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;C&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;E&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.052&lt;/span&gt;&lt;span class="co-verdict"&gt;orients&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li class="co-col" data-column="D"&gt;&lt;button type="button" class="co-chip" aria-label="Move column D first"&gt;D&lt;/button&gt;&lt;ol class="co-cards"&gt;&lt;li class="co-card is-false" data-collider="A D E"&gt;&lt;span class="co-step"&gt;2&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;A&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;D&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;E&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.052&lt;/span&gt;&lt;span class="co-verdict"&gt;orients&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li class="co-col" data-column="E"&gt;&lt;button type="button" class="co-chip" aria-label="Move column E first"&gt;E&lt;/button&gt;&lt;ol class="co-cards"&gt;&lt;li class="co-card is-skipped" data-collider="B E C"&gt;&lt;span class="co-step"&gt;3&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;B&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;E&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;C&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.821&lt;/span&gt;&lt;span class="co-verdict"&gt;skipped: &lt;code&gt;C-E&lt;/code&gt; taken&lt;/span&gt;&lt;/li&gt;&lt;li class="co-card is-skipped" data-collider="B E D"&gt;&lt;span class="co-step"&gt;4&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;B&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;E&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;D&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.631&lt;/span&gt;&lt;span class="co-verdict"&gt;skipped: &lt;code&gt;D-E&lt;/code&gt; taken&lt;/span&gt;&lt;/li&gt;&lt;li class="co-card is-skipped" data-collider="C E D"&gt;&lt;span class="co-step"&gt;5&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;C&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;E&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;D&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.273&lt;/span&gt;&lt;span class="co-verdict"&gt;skipped: &lt;code&gt;C-E, D-E&lt;/code&gt; taken&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="co-side"&gt;&lt;div class="co-graph" data-graph="0" data-count="40" data-why="C and D sit left of E, so their false colliders take C-E and D-E first."&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 130" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="68.07" y1="59.00" x2="108.47" y2="38.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line class="highlight" x1="107.00" y1="65.00" x2="88.27" y2="65.00" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="69.54" y1="65.00" x2="97.00" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="107.00" y1="65.00" x2="88.27" y2="65.00" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="107.00,65.00 97.00,60.00 97.00,70.00" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line class="highlight" x1="108.47" y1="92.00" x2="88.27" y2="81.50" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="68.07" y1="71.00" x2="99.59" y2="87.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="108.47" y1="92.00" x2="88.27" y2="81.50" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="108.47,92.00 101.90,82.95 97.29,91.82" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="131.53" y1="38.00" x2="171.93" y2="59.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line class="highlight" x1="133.00" y1="65.00" x2="151.73" y2="65.00" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="143.00" y1="65.00" x2="170.46" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="133.00" y1="65.00" x2="151.73" y2="65.00" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="133.00,65.00 143.00,70.00 143.00,60.00" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line class="highlight" x1="131.53" y1="92.00" x2="151.73" y2="81.50" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="140.41" y1="87.39" x2="171.93" y2="71.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="131.53" y1="92.00" x2="151.73" y2="81.50" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="131.53,92.00 142.71,91.82 138.10,82.95" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;circle cx="56.54" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.54" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="120.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="120.00" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="120.00" cy="98.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="98.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="183.46" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="183.46" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="co-graph" data-graph="1" data-count="20" data-why="C sits left of E, so its false collider takes C-E first. D-E is right." hidden&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 130" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="68.07" y1="59.00" x2="108.47" y2="38.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line class="highlight" x1="107.00" y1="65.00" x2="88.27" y2="65.00" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="69.54" y1="65.00" x2="97.00" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="107.00" y1="65.00" x2="88.27" y2="65.00" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="107.00,65.00 97.00,60.00 97.00,70.00" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="68.07" y1="71.00" x2="108.47" y2="92.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="131.53" y1="38.00" x2="163.06" y2="54.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,59.00 165.36,49.95 160.75,58.82" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line class="highlight" x1="133.00" y1="65.00" x2="151.73" y2="65.00" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="143.00" y1="65.00" x2="170.46" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="133.00" y1="65.00" x2="151.73" y2="65.00" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="133.00,65.00 143.00,70.00 143.00,60.00" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="131.53" y1="92.00" x2="163.06" y2="75.61" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,71.00 160.75,71.18 165.36,80.05" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="56.54" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.54" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="120.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="120.00" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="120.00" cy="98.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="98.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="183.46" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="183.46" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="co-graph" data-graph="2" data-count="20" data-why="D sits left of E, so its false collider takes D-E first. C-E is right." hidden&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 130" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="68.07" y1="59.00" x2="108.47" y2="38.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="69.54" y1="65.00" x2="107.00" y2="65.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line class="highlight" x1="108.47" y1="92.00" x2="88.27" y2="81.50" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="68.07" y1="71.00" x2="99.59" y2="87.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="108.47" y1="92.00" x2="88.27" y2="81.50" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="108.47,92.00 101.90,82.95 97.29,91.82" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="131.53" y1="38.00" x2="163.06" y2="54.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,59.00 165.36,49.95 160.75,58.82" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="133.00" y1="65.00" x2="160.46" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="170.46,65.00 160.46,60.00 160.46,70.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line class="highlight" x1="131.53" y1="92.00" x2="151.73" y2="81.50" stroke="var(--andrey-accent)" stroke-width="8.0" stroke-opacity="0.28" stroke-linecap="round"/&gt;&lt;line x1="140.41" y1="87.39" x2="171.93" y2="71.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="131.53" y1="92.00" x2="151.73" y2="81.50" stroke="var(--andrey-accent)" stroke-width="3.00" stroke-linecap="round"/&gt;&lt;polygon points="131.53,92.00 142.71,91.82 138.10,82.95" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;circle cx="56.54" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.54" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="120.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="120.00" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="120.00" cy="98.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="98.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="183.46" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="183.46" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="co-graph" data-graph="3" data-count="40" data-why="E sits left of C and D, so its true colliders take both edges first." hidden&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 130" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="68.07" y1="59.00" x2="108.47" y2="38.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="69.54" y1="65.00" x2="107.00" y2="65.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="68.07" y1="71.00" x2="108.47" y2="92.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="131.53" y1="38.00" x2="163.06" y2="54.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,59.00 165.36,49.95 160.75,58.82" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="133.00" y1="65.00" x2="160.46" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="170.46,65.00 160.46,60.00 160.46,70.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.53" y1="92.00" x2="163.06" y2="75.61" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,71.00 160.75,71.18 165.36,80.05" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="56.54" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.54" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="120.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="120.00" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="120.00" cy="98.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="98.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="183.46" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="183.46" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;p class="co-note co-why" aria-live="polite"&gt;C and D sit left of E, so their false colliders take C-E and D-E first.&lt;/p&gt;&lt;p class="co-note"&gt;This graph comes from &lt;b class="co-count"&gt;40&lt;/b&gt; of 120 orders. Graphs seen: &lt;b class="co-seen"&gt;1&lt;/b&gt; of 4.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section class="co-lane co-new"&gt;&lt;div class="co-head"&gt;&lt;p class="co-title"&gt;0.7.0: p-value rule&lt;/p&gt;&lt;/div&gt;&lt;p class="co-rule"&gt;Visits the colliders &lt;b&gt;by p-value, highest first&lt;/b&gt;: the most clearly separated pair goes first, wherever its columns sit.&lt;/p&gt;&lt;div class="co-body"&gt;&lt;ol class="co-rank" aria-label="Colliders by p-value"&gt;&lt;li class="co-card" data-collider="B E C"&gt;&lt;span class="co-step"&gt;1&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;B&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;E&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;C&lt;/span&gt;&lt;/span&gt;&lt;span class="co-bar"&gt;&lt;span class="co-track"&gt;&lt;span class="co-fill" style="--w: 82.1%"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.821&lt;/span&gt;&lt;span class="co-verdict"&gt;orients&lt;/span&gt;&lt;/li&gt;&lt;li class="co-card" data-collider="B E D"&gt;&lt;span class="co-step"&gt;2&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;B&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;E&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;D&lt;/span&gt;&lt;/span&gt;&lt;span class="co-bar"&gt;&lt;span class="co-track"&gt;&lt;span class="co-fill" style="--w: 63.1%"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.631&lt;/span&gt;&lt;span class="co-verdict"&gt;orients&lt;/span&gt;&lt;/li&gt;&lt;li class="co-card" data-collider="C E D"&gt;&lt;span class="co-step"&gt;3&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;C&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;E&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;D&lt;/span&gt;&lt;/span&gt;&lt;span class="co-bar"&gt;&lt;span class="co-track"&gt;&lt;span class="co-fill" style="--w: 27.3%"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.273&lt;/span&gt;&lt;span class="co-verdict"&gt;orients&lt;/span&gt;&lt;/li&gt;&lt;li class="co-card is-false is-skipped" data-collider="A C E"&gt;&lt;span class="co-step"&gt;4&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;A&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;C&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;E&lt;/span&gt;&lt;/span&gt;&lt;span class="co-bar"&gt;&lt;span class="co-track"&gt;&lt;span class="co-fill" style="--w: 5.2%"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.052&lt;/span&gt;&lt;span class="co-verdict"&gt;skipped: &lt;code&gt;C-E&lt;/code&gt; taken&lt;/span&gt;&lt;/li&gt;&lt;li class="co-card is-false is-skipped" data-collider="A D E"&gt;&lt;span class="co-step"&gt;5&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;span class="co-node"&gt;A&lt;/span&gt;&lt;span class="co-arrow" aria-label="causes"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="co-node co-middle"&gt;D&lt;/span&gt;&lt;span class="co-arrow" aria-label="is caused by"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="co-node"&gt;E&lt;/span&gt;&lt;/span&gt;&lt;span class="co-bar"&gt;&lt;span class="co-track"&gt;&lt;span class="co-fill" style="--w: 5.2%"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;p = 0.052&lt;/span&gt;&lt;span class="co-verdict"&gt;skipped: &lt;code&gt;D-E&lt;/code&gt; taken&lt;/span&gt;&lt;/li&gt;&lt;li class="co-card co-axis-row" aria-hidden="true"&gt;&lt;span class="co-step"&gt;&lt;/span&gt;&lt;span class="co-triple"&gt;&lt;/span&gt;&lt;span class="co-axis"&gt;&lt;span class="at-alpha"&gt;0.05&lt;/span&gt;&lt;span class="at-one"&gt;p = 1&lt;/span&gt;&lt;/span&gt;&lt;span class="co-p"&gt;&lt;/span&gt;&lt;span class="co-verdict"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="co-side"&gt;&lt;div class="co-graph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 130" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="68.07" y1="59.00" x2="108.47" y2="38.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="69.54" y1="65.00" x2="107.00" y2="65.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="68.07" y1="71.00" x2="108.47" y2="92.00" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="131.53" y1="38.00" x2="163.06" y2="54.39" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,59.00 165.36,49.95 160.75,58.82" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="133.00" y1="65.00" x2="160.46" y2="65.00" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="170.46,65.00 160.46,60.00 160.46,70.00" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="131.53" y1="92.00" x2="163.06" y2="75.61" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="171.93,71.00 160.75,71.18 165.36,80.05" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="56.54" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="56.54" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;A&lt;/text&gt;&lt;circle cx="120.00" cy="32.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="32.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;B&lt;/text&gt;&lt;circle cx="120.00" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;C&lt;/text&gt;&lt;circle cx="120.00" cy="98.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="120.00" y="98.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;D&lt;/text&gt;&lt;circle cx="183.46" cy="65.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="183.46" y="65.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;E&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;p class="co-note"&gt;The true graph, from all 120 orders.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;figcaption&gt;PC on the same 300 rows. A dashed card is a collider the true graph does not have.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The same thing happened in hill climbing and tabu search (score ties), BOSS and GRaSP (a starting
order taken from the columns), GIN (a test built from each cluster’s first column), and FCI and GFCI
(rules and a search step that visit the columns in order).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-we-fixed-it"&gt;
&lt;h2&gt;How we fixed it&lt;/h2&gt;
&lt;p&gt;Each fix replaces a column’s position with a value computed from its data, which moves with the
column.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PC.&lt;/strong&gt; Conflicting colliders are oriented by the p-value that separated each pair, highest first,
adapting &lt;a class="reference external" href="https://arxiv.org/abs/1610.00378"&gt;Ramsey (2016)&lt;/a&gt;. Meek’s rules then apply in rounds, so
an edge they disagree about stays undirected. In 16 settings, no reordering changed PC’s graph,
and PC made fewer errors in every one, for at most about 4% more time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hill climbing and tabu search.&lt;/strong&gt; Exact score ties go to a rank computed from each column’s data.
Accuracy is unchanged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BOSS and GRaSP.&lt;/strong&gt; Both search the columns sorted by that rank. Their accuracy matches
causal-learn’s.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GIN.&lt;/strong&gt; The test that orders the hidden causes uses every member of a cluster, not the first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FCI and GFCI.&lt;/strong&gt; Their rules apply in rounds, and the possible-d-separation step visits the
columns by rank. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;possible_dsep=&amp;quot;fast&amp;quot;&lt;/span&gt;&lt;/code&gt; keeps the column-order step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Benchmark data.&lt;/strong&gt; Our data generators and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.data.load_dataset&lt;/span&gt;&lt;/code&gt; shuffle the columns along
with the true graph.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Simulated data can understate the problem, so we also ran FCI on six real tables, each under 200
random column orders (20 for the full superconductivity table). With the default, no order changed
the result.&lt;/p&gt;
&lt;figure class="ct"&gt;&lt;p class="ct-title"&gt;Column orders that changed FCI&amp;#x27;s result (lower is better)&lt;/p&gt;&lt;div class="ct-scroll"&gt;&lt;table class="ct-table"&gt;&lt;thead&gt;&lt;tr&gt;&lt;td colspan="2"&gt;&lt;/td&gt;&lt;th scope="col" class="ct-ours"&gt;Default&lt;/th&gt;&lt;th scope="col"&gt;possible_dsep=&amp;quot;fast&amp;quot;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="6"&gt;&lt;span&gt;Real tables&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;Sachs protein signaling&lt;small&gt;11 variables, 853 rows&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours"&gt;0%&lt;/td&gt;&lt;td class=""&gt;0%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Wine&lt;small&gt;13 variables, 178 rows&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours"&gt;0%&lt;/td&gt;&lt;td class=""&gt;0%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Diabetes&lt;small&gt;10 variables, 442 rows&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;75%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Breast cancer&lt;small&gt;30 variables, 569 rows&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;78%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Superconductivity, 1,000-row sample&lt;small&gt;81 variables, 1,000 rows&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;64%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Superconductivity&lt;small&gt;81 variables, 21,263 rows&lt;/small&gt;&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;0%&lt;/td&gt;&lt;td class=""&gt;100%&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="what-it-costs"&gt;
&lt;h2&gt;What it costs&lt;/h2&gt;
&lt;p&gt;Almost nothing. A fix that runs more tests or more score evaluations would trade speed for order
independence, so each fix reuses work the method already does:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PC&lt;/strong&gt; orients by the p-values of the tests that removed each edge, so it runs no new test.
With Meek’s rules in rounds, a fit takes at most about 4% longer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hill climbing and tabu search&lt;/strong&gt; rank each column once per fit, from its data, and add no
score evaluations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BOSS and GRaSP&lt;/strong&gt; sort the columns once by that rank and run the same search, in 1.03x and
0.91x the time of 0.6.0.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GIN&lt;/strong&gt; runs its root test once per member of a cluster, but builds each column’s kernel matrix
once per fit instead of once per test, so it runs about 4x to 7x faster than before.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FCI and GFCI&lt;/strong&gt; put the columns in rank order once and run the same search, in 0.99x to 1.05x
the time at 20 to 200 variables.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One trade remains. On some graphs with 400 or more variables, FCI’s and GFCI’s order-free step can
take longer: on one graph, 406 to 417 s against 120 to 336 s for the column-order step.
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;possible_dsep=&amp;quot;fast&amp;quot;&lt;/span&gt;&lt;/code&gt; keeps that step.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-andrey-compares"&gt;
&lt;h2&gt;How Andrey compares&lt;/h2&gt;
&lt;p&gt;Permutation invariance is a yes-or-no property, so a few small examples answer it. We fit each
package’s implementation at its defaults on three small datasets per method, each in nine column
orders, and checked whether any reordering changed the graph.&lt;/p&gt;
&lt;figure class="ct"&gt;&lt;p class="ct-title"&gt;Does reordering the columns change the graph?&lt;/p&gt;&lt;div class="ct-scroll"&gt;&lt;table class="ct-table"&gt;&lt;thead&gt;&lt;tr&gt;&lt;td colspan="2"&gt;&lt;/td&gt;&lt;th scope="col" class="ct-ours"&gt;Andrey 0.7.0&lt;/th&gt;&lt;th scope="col"&gt;causal-learn 0.1.4.8&lt;/th&gt;&lt;th scope="col"&gt;pcalg 2.7-12&lt;/th&gt;&lt;th scope="col"&gt;bnlearn 5.2.1&lt;/th&gt;&lt;th scope="col"&gt;pgmpy 1.1.2&lt;/th&gt;&lt;th scope="col"&gt;Tetrad 7.6.10&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="2"&gt;&lt;span&gt;Constraint&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;PC&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;Same&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class=""&gt;By name&lt;/td&gt;&lt;td class=""&gt;By name&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;FCI&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;Same&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class=""&gt;By name&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="2"&gt;&lt;span&gt;Score-based&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;Hill climbing&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;Same&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class=""&gt;By run&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;Tabu search&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;Same&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="2"&gt;&lt;span&gt;Permutation&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;BOSS&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;Same&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th scope="row"&gt;GRaSP&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;Same&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th class="ct-group" rowspan="1"&gt;&lt;span&gt;Hidden causes&lt;/span&gt;&lt;/th&gt;&lt;th scope="row"&gt;GIN&lt;/th&gt;&lt;td class="ct-ours ct-best"&gt;Same&lt;/td&gt;&lt;td class=""&gt;Changed*&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class="ct-na"&gt;-&lt;/td&gt;&lt;td class=""&gt;Changed&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;figcaption&gt;Each package at its defaults, on three small datasets per method, each in nine column orders; causal-learn&amp;#x27;s and Tetrad&amp;#x27;s BOSS and GRaSP ran without their own randomness. By name: the graph follows the variables&amp;#x27; names, so it can change when the names follow the positions, as a bare array&amp;#x27;s do. By run: pgmpy&amp;#x27;s hill climbing breaks ties in Python&amp;#x27;s hash order, which changes between runs. * causal-learn&amp;#x27;s GIN also tests columns by position, a known bug (py-why/causal-learn#271).&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;For BOSS, GRaSP, GIN, and tabu search, every other implementation we checked changed its graph under
some reordering. pcalg and causal-learn can make PC’s colliders order-independent, but only with
options that are off by default; Andrey does it by default, with no extra tests.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Fit PC on 10 random orders of the same columns, with the old rule and with 0.7.0’s default:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey.data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sample_scm&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sample_scm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;density&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;standardize&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;column&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;p_value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;graphs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;fit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collider_order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;back&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# put the columns back in their first order&lt;/span&gt;
        &lt;span class="n"&gt;graphs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;adjacency&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ix_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tobytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graphs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;collider_order=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; graph&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from 10 column orders&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;Output&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;collider_order=&amp;#39;column&amp;#39;: 10 graphs from 10 column orders
collider_order=&amp;#39;p_value&amp;#39;: 1 graph from 10 column orders
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/benchmarks/0.7.0.html"&gt;&lt;span class="std std-doc"&gt;0.7.0 release card&lt;/span&gt;&lt;/a&gt; reports every measurement in this release, with
each method’s limits, and the &lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;changelog&lt;/span&gt;&lt;/a&gt; lists every change.&lt;/p&gt;
&lt;p&gt;Andrey is in alpha. The API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need. We
also welcome pull requests. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; helps you get
set up.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261019-andrey-0-7-0.html"/>
    <summary>Give two people the same table with its columns in different orders. Ask each to learn a causal
graph. They should get the same graph. Column order carries no information: it comes from a database
export, a DataFrame, or whoever wrote the CSV. A method whose graph does not depend on that order is
permutation invariant.</summary>
    <category term="Permutationinvariance" label="Permutation invariance"/>
    <category term="Release" label="Release"/>
    <published>2026-10-19T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261017-andrey-0-6-0.html</id>
    <title>Mixed and categorical data</title>
    <updated>2026-10-17T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="mixed-and-categorical-data"&gt;

&lt;p&gt;Your tables may contain more than numbers. A customer record holds a plan and a region alongside
hours used and the amount billed. A patient record holds a diagnosis alongside blood pressure. With
Andrey 0.6.0, you can learn causal graphs from categorical data and from tables that mix categorical
and continuous columns.&lt;/p&gt;
&lt;section id="tl-dr"&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;GES on categorical data: more than 50x faster than causal-learn at every size, with the same
graph on every dataset.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hill climbing on categorical data: more than 20x faster than pgmpy at every size.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PC on mixed data: more than 10x faster than Tetrad at 10 and 20 variables.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We explain how Andrey reads each kind of column, run an example, and report benchmark results.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="tests-and-scores"&gt;
&lt;h2&gt;Tests and scores&lt;/h2&gt;
&lt;p&gt;A constraint-based method tests whether two variables are independent given others. A score-based
method compares graphs by how well they explain the data. Each needs a test or score that fits your
data. In Andrey 0.6.0 you can use both with categorical and mixed data.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Data&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Tests&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Scores&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;Categorical&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;chisq&amp;quot;&lt;/span&gt;&lt;/code&gt; (chi-square), &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;gsq&amp;quot;&lt;/span&gt;&lt;/code&gt; (G-square)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;local_score_BDeu&amp;quot;&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;local_score_BIC_categorical&amp;quot;&lt;/span&gt;&lt;/code&gt; (multinomial BIC)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;Mixed&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;degen_gauss&amp;quot;&lt;/span&gt;&lt;/code&gt; (degenerate Gaussian), &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;cond_gauss&amp;quot;&lt;/span&gt;&lt;/code&gt; (conditional Gaussian)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;local_score_degen_gauss&amp;quot;&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;local_score_cond_gauss&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;PC, FCI, GFCI, and CD-NOD take every test. GES, GFCI, hill climbing, and exact search take every
score; Tabu search takes the categorical scores, and BOSS and GRaSP the mixed ones. GFCI, CD-NOD,
exact search, and the mixed scores in BOSS and GRaSP are experimental.&lt;/p&gt;
&lt;p&gt;The two mixed-data forms make different assumptions:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Degenerate Gaussian&lt;/strong&gt; replaces a categorical column of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;k&lt;/span&gt;&lt;/code&gt; categories with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;k&lt;/span&gt; &lt;span class="pre"&gt;-&lt;/span&gt; &lt;span class="pre"&gt;1&lt;/span&gt;&lt;/code&gt; indicator
columns and treats every column as Gaussian. It suits effects that add up across categories. It is
the faster of the two, though only slightly in PC.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conditional Gaussian&lt;/strong&gt; fits a Gaussian with its own mean and covariance to the continuous
variables within each combination of categories. A category can change the spread as well as the
average. It needs several rows in each combination.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You do not need to encode a DataFrame’s text, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bool&lt;/span&gt;&lt;/code&gt;, or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;category&lt;/span&gt;&lt;/code&gt; columns; Andrey reads them as
categorical. With a mixed-data test or score, Andrey also reads a column with at most 10 distinct
whole numbers as categorical and issues a warning. To be sure, name the categorical columns with
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;categorical=[...]&lt;/span&gt;&lt;/code&gt;, or change the limit with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;max_categories&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="example"&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;We simulate 2,000 customers. Their plan and region each change how many hours they use the service.
The hours set the monthly bill. Plan and region are text; hours and bill are numbers.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;

&lt;span class="c1"&gt;# Customers of a service. Their plan and their region each change how many hours they use it,&lt;/span&gt;
&lt;span class="c1"&gt;# and the hours set the monthly bill. Plan and region are text; hours and bill are numbers.&lt;/span&gt;
&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;basic&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pro&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;north&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;south&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;west&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;shift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;north&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;south&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;west&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;hours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pro&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vectorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shift&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;plan&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;region&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;hours&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bill&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bill&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# The conditional Gaussian test and score handle both kinds of column.&lt;/span&gt;
&lt;span class="n"&gt;categorical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;plan&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;region&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indep_test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cond_gauss&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categorical&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;categorical&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;local_score_cond_gauss&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categorical&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;categorical&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;PC with the conditional Gaussian test and GES with the conditional Gaussian score both find and
orient all three links in the model. Plan and region are independent, but both change the hours.
This lets the data show which way those two links run, then orient the link from hours to the bill.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PC  cpdag  |  4 nodes  |  3 edges (3 directed)

  plan -&amp;gt; hours
  region -&amp;gt; hours
  hours -&amp;gt; bill

  categorical: [&amp;#39;plan&amp;#39;, &amp;#39;region&amp;#39;]

GES  cpdag  |  4 nodes  |  3 edges (3 directed)

  plan -&amp;gt; hours
  region -&amp;gt; hours
  hours -&amp;gt; bill

  score: 2.943e+04
  categorical: [&amp;#39;plan&amp;#39;, &amp;#39;region&amp;#39;]
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="benchmarks"&gt;
&lt;h2&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;We compare methods on simulated categorical and conditional linear Gaussian networks with 10, 20,
and 40 variables and 2,000 rows. We use 5 categorical datasets and 10 mixed datasets at each size.
We compare each method with packages that implement it: causal-learn and pgmpy in Python, bnlearn in
R, and Tetrad in Java.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Data&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Compared with&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Andrey’s speedup&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Graph&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;GES, BDeu score&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Categorical&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;causal-learn&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 50x at every size&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same as causal-learn’s on every dataset&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Hill climbing, BDeu and BIC scores&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Categorical&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;pgmpy&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 20x at every size&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same scores; the graphs differ on 28 of 30 datasets, with fewer errors on 9 and more on 17; on those 17 the two searches part at a tie between two equal moves, which they break differently&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;PC, conditional Gaussian test&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Mixed&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Tetrad&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 10x at 10 and 20 variables&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;fewer errors on 12 of the 18 datasets Tetrad finished, more on 4&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;GES, conditional Gaussian score&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Mixed&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Tetrad (FGES)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 9x at every size&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same on 11 of 30 datasets; of the other 19, fewer errors on 11 and more on 7&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;PC, chi-square and G-square tests&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Categorical&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;bnlearn&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 3x at 20 and 40 variables&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;fewer errors on 24 of 30 datasets, more on 2&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;PC, chi-square and G-square tests&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Categorical&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;causal-learn&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 2x at 20 and 40 variables&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same as causal-learn’s on every dataset&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Each speedup holds at every size it names. At 10 variables, PC on categorical data takes under 0.1 s
in every package, too fast to time fairly. Its speedups therefore start at 20. Tetrad’s PC with the
conditional Gaussian test stopped with an error on 12 of its 30 fits. The table counts only the
datasets both packages finished.&lt;/p&gt;
&lt;p&gt;You can find every measurement in the &lt;a class="reference internal" href="docs/benchmarks/0.6.0.html"&gt;&lt;span class="std std-doc"&gt;0.6.0 release card&lt;/span&gt;&lt;/a&gt;, including
comparisons with bnlearn’s hill climbing and the degenerate Gaussian forms. It also shows where
Andrey is not faster and gives the machine, settings, and known limits.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="more-features"&gt;
&lt;h2&gt;More features&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simulated networks.&lt;/strong&gt; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.data.sample_discrete&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sample_mixed&lt;/span&gt;&lt;/code&gt; sample a categorical or a
conditional linear Gaussian network along with its true graph.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Published networks as categorical data.&lt;/strong&gt; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;load_dataset(&amp;quot;alarm&amp;quot;,&lt;/span&gt; &lt;span class="pre"&gt;discrete=True)&lt;/span&gt;&lt;/code&gt; samples ASIA,
ALARM, HEPAR2, or ANDES from the network’s own probability tables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hill climbing respects the model.&lt;/strong&gt; With a mixed-data score, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hc&lt;/span&gt;&lt;/code&gt; never adds an edge from a
continuous variable to a categorical one. A conditional linear Gaussian network cannot express
that edge.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The categorical tests and scores build on work by Rui Zhu
(&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/pull/94"&gt;#94&lt;/a&gt;). Thank you, Rui.&lt;/p&gt;
&lt;p&gt;For details, read the &lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;changelog&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Pass a DataFrame with text or category columns to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.pc(data,&lt;/span&gt; &lt;span class="pre"&gt;indep_test=&amp;quot;cond_gauss&amp;quot;)&lt;/span&gt;&lt;/code&gt; or
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.ges(data,&lt;/span&gt; &lt;span class="pre"&gt;score_func=&amp;quot;local_score_cond_gauss&amp;quot;)&lt;/span&gt;&lt;/code&gt;, or use &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;chisq&amp;quot;&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;quot;local_score_BDeu&amp;quot;&lt;/span&gt;&lt;/code&gt;
when every column is categorical.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="contribute"&gt;
&lt;h2&gt;Contribute&lt;/h2&gt;
&lt;p&gt;Andrey is in alpha. The API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need. We
also welcome pull requests. You can find setup instructions in the
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261017-andrey-0-6-0.html"/>
    <summary>Your tables may contain more than numbers. A customer record holds a plan and a region alongside
hours used and the amount billed. A patient record holds a diagnosis alongside blood pressure. With
Andrey 0.6.0, you can learn causal graphs from categorical data and from tables that mix categorical
and continuous columns.</summary>
    <category term="Mixedandcategoricaldata" label="Mixed and categorical data"/>
    <category term="Release" label="Release"/>
    <published>2026-10-17T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261016-andrey-0-5-0.html</id>
    <title>Temporal causal discovery</title>
    <updated>2026-10-16T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="temporal-causal-discovery"&gt;

&lt;p&gt;Causes take time. Rain today slows today’s traffic, and today’s traffic shapes tomorrow’s sales.
When you measure the same variables day after day, the order of the days tells you which way a
link can run: the future cannot cause the past. As of 0.5.0, we support six methods that use this
order: PCMCI, PCMCI+, LPCMCI, lasso Granger causality, VARMA-LiNGAM, and LongitudinalLiNGAM.&lt;/p&gt;
&lt;p&gt;We explain what a time-series method finds, run two on a small example, and report benchmark
results.&lt;/p&gt;
&lt;section id="links-across-time"&gt;
&lt;h2&gt;Links across time&lt;/h2&gt;
&lt;p&gt;A time-series method returns one graph per lag. The lag-1 graph holds the links from yesterday to
today. The lag-0 graph holds the links within one day. Most variables also depend on their own
past, so each graph can hold a variable’s link to itself.&lt;/p&gt;
&lt;p&gt;The methods look for different links and make different assumptions:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PCMCI&lt;/strong&gt; uses conditional independence tests for each lagged link. It first selects each
variable’s likely causes from the past, then tests every link given the causes of both ends.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PCMCI+&lt;/strong&gt; adds the links within a time step and orients them where the data allow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LPCMCI&lt;/strong&gt; allows for series you did not measure. It returns a time-series PAG, whose circles
mark the ends the data cannot orient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Granger&lt;/strong&gt; regresses each variable on the past of every variable with a cross-validated lasso
and keeps the lagged links with nonzero coefficients.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VARMA-LiNGAM&lt;/strong&gt; uses non-Gaussian noise to order the variables within a time step and estimates
the weight of every link, lagged and within a step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LongitudinalLiNGAM&lt;/strong&gt; takes a panel instead of one long series: many units, each measured at a
few time points.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="supported-methods"&gt;
&lt;h2&gt;Supported methods&lt;/h2&gt;
&lt;p&gt;Before each release, we benchmark each supported method against another package and check its
results on known data. PCMCI, PCMCI+, LPCMCI, and Granger are new in this release. VARMA-LiNGAM
and LongitudinalLiNGAM were experimental until now.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Finds&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Returns&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.pcmci.html"&gt;&lt;span class="std std-doc"&gt;PCMCI&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Lagged links&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;One directed graph per lag&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.pcmciplus.html"&gt;&lt;span class="std std-doc"&gt;PCMCI+&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Lagged links and links within a step&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A CPDAG within a step and a directed graph per lag&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.lpcmci.html"&gt;&lt;span class="std std-doc"&gt;LPCMCI&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The same, with unmeasured series&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A time-series PAG&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.granger.html"&gt;&lt;span class="std std-doc"&gt;Granger&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Lagged links&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;One directed graph per lag, with the coefficients as weights&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.varma_lingam.html"&gt;&lt;span class="std std-doc"&gt;VARMA-LiNGAM&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Weighted links within a step and from the step before&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Weighted graphs for each lag and the moving average&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.longitudinal_lingam.html"&gt;&lt;span class="std std-doc"&gt;LongitudinalLiNGAM&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Weighted links in a panel&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Weighted graphs at every time point but the first&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Choose a method for your data:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PCMCI+&lt;/strong&gt; when links can act within one time step, as they do when you measure once a day.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PCMCI&lt;/strong&gt; or &lt;strong&gt;Granger&lt;/strong&gt; when every effect takes at least one step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LPCMCI&lt;/strong&gt; when a variable you did not measure may drive several that you did.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VARMA-LiNGAM&lt;/strong&gt; when you need the size of each effect and the noise is non-Gaussian.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LongitudinalLiNGAM&lt;/strong&gt; when you follow many units over a few time points.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="an-example"&gt;
&lt;h2&gt;An example&lt;/h2&gt;
&lt;p&gt;We simulate three daily series. Rain slows traffic the same day, traffic drives the next day’s
sales, and each series carries over from the day before.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;

&lt;span class="c1"&gt;# Three daily series. Rain slows traffic the same day; traffic drives the next day&amp;#39;s sales; each&lt;/span&gt;
&lt;span class="c1"&gt;# series also carries over from the day before.&lt;/span&gt;
&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;traffic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rain&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;traffic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rain&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;traffic&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;sales&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Links from the past only, then links within a day as well.&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pcmci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tau_max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pcmciplus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tau_max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;.post-graph { container-type: inline-size; margin: 2rem 0; padding: clamp(1rem, 2.5vw, 1.5rem); border: 1px solid var(--andrey-line); border-radius: 12px; background: var(--andrey-surface); }.post-graph .pg-title { display: block; margin: 0 0 1rem; font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.02em; }.post-graph .pg-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; }@container (max-width: 34rem) { .post-graph .pg-grid { grid-template-columns: 1fr; } }.post-graph figure { margin: 0; }.post-graph figcaption { margin: 0 0 0.5rem; font-size: 0.85rem; line-height: 1.4; }.post-graph figcaption strong { display: block; color: var(--andrey-ink); font-weight: 600; }.post-graph figcaption span { color: var(--andrey-muted); font-size: 0.8rem; }.post-graph svg { display: block; width: 100%; height: auto; }.post-graph .pg-node { fill: var(--andrey-surface-2); stroke: var(--andrey-line); stroke-width: 1.5; }.post-graph .pg-label { fill: var(--andrey-ink); font: 600 8px var(--font-sans); }.post-graph .pg-day { fill: var(--andrey-muted); font: 500 9px var(--font-sans); }.post-graph .pg-edge { fill: none; stroke-width: 1.6; }.post-graph .pg-edge.is-true { stroke: var(--andrey-edge-directed); }.post-graph .pg-edge.is-false { stroke: var(--andrey-muted); stroke-dasharray: 4 3; }.post-graph .pg-edge.is-sameday { stroke: var(--andrey-ground-amber); stroke-width: 2.2; }.post-graph .pg-head.is-true { fill: var(--andrey-edge-directed); }.post-graph .pg-head.is-false { fill: var(--andrey-muted); }.post-graph .pg-head.is-sameday { fill: var(--andrey-ground-amber); }.post-graph .pg-key { display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; margin: 1rem 0 0; padding: 0; list-style: none; color: var(--andrey-muted); font-size: 0.8rem; }.post-graph .pg-key i { display: inline-block; width: 1.5rem; height: 0; margin-right: 0.4rem; vertical-align: middle; border-top: 2px solid var(--andrey-edge-directed); }.post-graph .pg-key .is-false i { border-top: 2px dashed var(--andrey-muted); }.post-graph .pg-key .is-sameday i { border-top-color: var(--andrey-ground-amber); }&lt;/style&gt;&lt;div class="post-graph"&gt;&lt;span class="pg-title"&gt;The links each method finds in the example&lt;/span&gt;&lt;div class="pg-grid"&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;strong&gt;PCMCI&lt;/strong&gt;&lt;span&gt;Links from the past only; 2 links not in the model&lt;/span&gt;&lt;/figcaption&gt;&lt;svg viewBox="0 0 300 178" role="img" aria-label="PCMCI: rain yesterday -&amp;gt; rain today, rain yesterday -&amp;gt; traffic today, traffic yesterday -&amp;gt; traffic today, traffic yesterday -&amp;gt; sales today, sales yesterday -&amp;gt; traffic today, sales yesterday -&amp;gt; sales today"&gt;&lt;defs&gt;&lt;marker id="pg-head-is-true" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"&gt;&lt;path class="pg-head is-true" d="M0 0 L10 5 L0 10z"/&gt;&lt;/marker&gt;&lt;marker id="pg-head-is-false" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"&gt;&lt;path class="pg-head is-false" d="M0 0 L10 5 L0 10z"/&gt;&lt;/marker&gt;&lt;marker id="pg-head-is-sameday" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"&gt;&lt;path class="pg-head is-sameday" d="M0 0 L10 5 L0 10z"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;text class="pg-day" x="70" y="12" text-anchor="middle"&gt;yesterday&lt;/text&gt;&lt;text class="pg-day" x="230" y="12" text-anchor="middle"&gt;today&lt;/text&gt;&lt;path class="pg-edge is-true" d="M 85.0 34.0 L 211.0 34.0" marker-end="url(#pg-head-is-true)"/&gt;&lt;path class="pg-edge is-false" d="M 84.0 39.4 L 212.3 89.1" marker-end="url(#pg-head-is-false)"/&gt;&lt;path class="pg-edge is-true" d="M 85.0 96.0 L 211.0 96.0" marker-end="url(#pg-head-is-true)"/&gt;&lt;path class="pg-edge is-true" d="M 84.0 101.4 L 212.3 151.1" marker-end="url(#pg-head-is-true)"/&gt;&lt;path class="pg-edge is-false" d="M 84.0 152.6 L 212.3 102.9" marker-end="url(#pg-head-is-false)"/&gt;&lt;path class="pg-edge is-true" d="M 85.0 158.0 L 211.0 158.0" marker-end="url(#pg-head-is-true)"/&gt;&lt;circle class="pg-node" cx="70" cy="34" r="15"/&gt;&lt;text class="pg-label" x="70" y="38" text-anchor="middle"&gt;rain&lt;/text&gt;&lt;circle class="pg-node" cx="230" cy="34" r="15"/&gt;&lt;text class="pg-label" x="230" y="38" text-anchor="middle"&gt;rain&lt;/text&gt;&lt;circle class="pg-node" cx="70" cy="96" r="15"/&gt;&lt;text class="pg-label" x="70" y="100" text-anchor="middle"&gt;traffic&lt;/text&gt;&lt;circle class="pg-node" cx="230" cy="96" r="15"/&gt;&lt;text class="pg-label" x="230" y="100" text-anchor="middle"&gt;traffic&lt;/text&gt;&lt;circle class="pg-node" cx="70" cy="158" r="15"/&gt;&lt;text class="pg-label" x="70" y="162" text-anchor="middle"&gt;sales&lt;/text&gt;&lt;circle class="pg-node" cx="230" cy="158" r="15"/&gt;&lt;text class="pg-label" x="230" y="162" text-anchor="middle"&gt;sales&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;strong&gt;PCMCI+&lt;/strong&gt;&lt;span&gt;Links within a day as well; the model's links exactly&lt;/span&gt;&lt;/figcaption&gt;&lt;svg viewBox="0 0 300 178" role="img" aria-label="PCMCI+: rain today -&amp;gt; traffic today, rain yesterday -&amp;gt; rain today, traffic yesterday -&amp;gt; traffic today, traffic yesterday -&amp;gt; sales today, sales yesterday -&amp;gt; sales today"&gt;&lt;defs&gt;&lt;marker id="pg-head-is-true" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"&gt;&lt;path class="pg-head is-true" d="M0 0 L10 5 L0 10z"/&gt;&lt;/marker&gt;&lt;marker id="pg-head-is-false" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"&gt;&lt;path class="pg-head is-false" d="M0 0 L10 5 L0 10z"/&gt;&lt;/marker&gt;&lt;marker id="pg-head-is-sameday" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"&gt;&lt;path class="pg-head is-sameday" d="M0 0 L10 5 L0 10z"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;text class="pg-day" x="70" y="12" text-anchor="middle"&gt;yesterday&lt;/text&gt;&lt;text class="pg-day" x="230" y="12" text-anchor="middle"&gt;today&lt;/text&gt;&lt;path class="pg-edge is-true is-sameday" d="M 230.0 49.0 Q 258.0 63.0 230.0 77.0" marker-end="url(#pg-head-is-sameday)"/&gt;&lt;path class="pg-edge is-true" d="M 85.0 34.0 L 211.0 34.0" marker-end="url(#pg-head-is-true)"/&gt;&lt;path class="pg-edge is-true" d="M 85.0 96.0 L 211.0 96.0" marker-end="url(#pg-head-is-true)"/&gt;&lt;path class="pg-edge is-true" d="M 84.0 101.4 L 212.3 151.1" marker-end="url(#pg-head-is-true)"/&gt;&lt;path class="pg-edge is-true" d="M 85.0 158.0 L 211.0 158.0" marker-end="url(#pg-head-is-true)"/&gt;&lt;circle class="pg-node" cx="70" cy="34" r="15"/&gt;&lt;text class="pg-label" x="70" y="38" text-anchor="middle"&gt;rain&lt;/text&gt;&lt;circle class="pg-node" cx="230" cy="34" r="15"/&gt;&lt;text class="pg-label" x="230" y="38" text-anchor="middle"&gt;rain&lt;/text&gt;&lt;circle class="pg-node" cx="70" cy="96" r="15"/&gt;&lt;text class="pg-label" x="70" y="100" text-anchor="middle"&gt;traffic&lt;/text&gt;&lt;circle class="pg-node" cx="230" cy="96" r="15"/&gt;&lt;text class="pg-label" x="230" y="100" text-anchor="middle"&gt;traffic&lt;/text&gt;&lt;circle class="pg-node" cx="70" cy="158" r="15"/&gt;&lt;text class="pg-label" x="70" y="162" text-anchor="middle"&gt;sales&lt;/text&gt;&lt;circle class="pg-node" cx="230" cy="158" r="15"/&gt;&lt;text class="pg-label" x="230" y="162" text-anchor="middle"&gt;sales&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ul class="pg-key"&gt;&lt;li class="is-true"&gt;&lt;i&gt;&lt;/i&gt;in the model, from yesterday&lt;/li&gt;&lt;li class="is-sameday"&gt;&lt;i&gt;&lt;/i&gt;in the model, within the day&lt;/li&gt;&lt;li class="is-false"&gt;&lt;i&gt;&lt;/i&gt;not in the model&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;p&gt;PCMCI+ finds exactly the five links in the model, including the same-day link from rain to
traffic.&lt;/p&gt;
&lt;p&gt;PCMCI tests only lagged links, so it cannot find the same-day effect. It also finds two links
absent from the model. Yesterday’s rain predicts today’s traffic because rain persists and today’s
rain slows today’s traffic. Yesterday’s sales also predict today’s traffic. When your measurements
are coarser than the effects, use a method that tests links within a step.&lt;/p&gt;
&lt;p&gt;The script prints both results:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pcmci  temporal  |  3 nodes  |  1 lags

  lag 1  |  6 edges (6 directed)
    rain -&amp;gt; rain
    rain -&amp;gt; traffic
    traffic -&amp;gt; traffic
    traffic -&amp;gt; sales
    sales -&amp;gt; traffic
    sales -&amp;gt; sales

  p_values: a list of 1, too long to print; read metadata[&amp;#39;p_values&amp;#39;]
  statistics: a list of 1, too long to print; read metadata[&amp;#39;statistics&amp;#39;]
  parents: [[[0, 1]], [[1, 1], [0, 1]], [[1, 1], [2, 1], [0, 1]]]

pcmciplus  temporal  |  3 nodes  |  2 lags

  lag 0  |  1 edges (1 directed)
    rain -&amp;gt; traffic

  lag 1  |  4 edges (4 directed)
    rain -&amp;gt; rain
    traffic -&amp;gt; traffic
    traffic -&amp;gt; sales
    sales -&amp;gt; sales

  p_values: a list of 2, too long to print; read metadata[&amp;#39;p_values&amp;#39;]
  statistics: a list of 2, too long to print; read metadata[&amp;#39;statistics&amp;#39;]
  parents: [[[0, 1]], [[1, 1], [0, 1]], [[1, 1], [2, 1]]]
  conflicts: []
  ambiguous_triples: []
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="benchmark-results"&gt;
&lt;h2&gt;Benchmark results&lt;/h2&gt;
&lt;p&gt;We compared each method with a package that implements it, using the simulations in the method’s
paper: PCMCI, PCMCI+, and LPCMCI with tigramite, and the two LiNGAM methods with lingam.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Compared with&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Andrey’s speedup&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Graph&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;PCMCI+&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;tigramite&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 50x at every size; more than 100x at 80 variables and 2,000 steps&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same as tigramite’s on every dataset&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;PCMCI&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;tigramite&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 20x at 10 variables&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same as tigramite’s on every dataset&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;LPCMCI&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;tigramite&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 8x at every size&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same on 73 of 90 datasets; of the other 17, fewer errors on 12 and more on 5&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;LongitudinalLiNGAM&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;lingam&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;more than 1.3x at every timed size&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same on 231 of 240 datasets; of the other 9, fewer errors on 8 and more on 1&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Each speedup holds at every size it names, where Andrey’s graphs have fewer errors than an empty
graph.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/benchmarks/0.5.0.html"&gt;&lt;span class="std std-doc"&gt;0.5.0 release card&lt;/span&gt;&lt;/a&gt; reports every measurement for all six
methods, including Granger and VARMA-LiNGAM. It also gives the sizes, machine, settings, and known
limits.&lt;/p&gt;
&lt;style&gt;@supports selector(:has(a)) { .race050 .race-panel { display: none; } .race050:has(#race050-m-pcmci:checked) #race050-panel-pcmci, .race050:has(#race050-m-pcmci-plus:checked) #race050-panel-pcmci-plus, .race050:has(#race050-m-lpcmci:checked) #race050-panel-lpcmci, .race050:has(#race050-m-granger:checked) #race050-panel-granger, .race050:has(#race050-m-varma-lingam:checked) #race050-panel-varma-lingam, .race050:has(#race050-m-longitudinallingam:checked) #race050-panel-longitudinallingam { display: block; } } @supports not selector(:has(a)) { .race050 .race-panel::before { content: attr(aria-label); display: block; margin-top: 1rem; font-size: 0.85rem; font-weight: 600; } }&lt;/style&gt;&lt;figure class="race race--speedup race050" aria-labelledby="race050-title"&gt;&lt;figcaption class="race-head"&gt;&lt;span class="race-title" id="race050-title"&gt;Speedup over each method's other package&lt;/span&gt;&lt;/figcaption&gt;&lt;fieldset class="race-sizes"&gt;&lt;legend class="visually-hidden"&gt;Method&lt;/legend&gt;&lt;input class="race-choice" type="radio" name="race050-method" id="race050-m-pcmci" checked&gt;&lt;label for="race050-m-pcmci"&gt;PCMCI&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race050-method" id="race050-m-pcmci-plus"&gt;&lt;label for="race050-m-pcmci-plus"&gt;PCMCI+&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race050-method" id="race050-m-lpcmci"&gt;&lt;label for="race050-m-lpcmci"&gt;LPCMCI&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race050-method" id="race050-m-granger"&gt;&lt;label for="race050-m-granger"&gt;Granger&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race050-method" id="race050-m-varma-lingam"&gt;&lt;label for="race050-m-varma-lingam"&gt;VARMA-LiNGAM&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race050-method" id="race050-m-longitudinallingam"&gt;&lt;label for="race050-m-longitudinallingam"&gt;LongitudinalLiNGAM&lt;/label&gt;&lt;/fieldset&gt;&lt;div class="race-panels"&gt;&lt;div class="race-panel" id="race050-panel-pcmci" role="group" aria-label="PCMCI: Andrey&amp;#x27;s speedup over tigramite: d=10, n=500: 23.2x; d=10, n=1,000: 24.0x; d=10, n=2,000: 27.4x; d=20, n=500: 51.4x; d=20, n=1,000: 58.8x; d=20, n=2,000: 69.5x; d=40, n=500: 86.6x; d=40, n=1,000: 104x; d=40, n=2,000: 125x; d=80, n=500: 88.6x; d=80, n=1,000: 132x; d=80, n=2,000: 166x; d=160, n=1,000: 91.6x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;d=10, n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.139759"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;23.2x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;d=10, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.144578"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;24.0x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;d=10, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.165060"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;27.4x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;d=20, n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.309639"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;51.4x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;d=20, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.354217"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;58.8x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;d=20, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.418675"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;69.5x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 6"&gt;&lt;span class="race-name"&gt;d=40, n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.521687"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;86.6x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 7"&gt;&lt;span class="race-name"&gt;d=40, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.626506"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;104x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 8"&gt;&lt;span class="race-name"&gt;d=40, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.753012"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;125x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 9"&gt;&lt;span class="race-name"&gt;d=80, n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.533735"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;88.6x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 10"&gt;&lt;span class="race-name"&gt;d=80, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.795181"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;132x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 11"&gt;&lt;span class="race-name"&gt;d=80, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;166x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 12"&gt;&lt;span class="race-name"&gt;d=160, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.551807"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;91.6x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race050-panel-pcmci-plus" role="group" aria-label="PCMCI+: Andrey&amp;#x27;s speedup over tigramite: d=10, n=500: 52.0x; d=10, n=1,000: 54.8x; d=10, n=2,000: 67.8x; d=20, n=500: 72.9x; d=20, n=1,000: 82.9x; d=20, n=2,000: 96.6x; d=40, n=500: 64.7x; d=40, n=1,000: 69.4x; d=40, n=2,000: 84.3x; d=80, n=1,000: 96.4x; d=80, n=2,000: 116x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;d=10, n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.448276"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;52.0x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;d=10, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.472414"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;54.8x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;d=10, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.584483"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;67.8x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;d=20, n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.628448"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;72.9x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;d=20, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.714655"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;82.9x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;d=20, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.832759"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;96.6x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 6"&gt;&lt;span class="race-name"&gt;d=40, n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.557759"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;64.7x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 7"&gt;&lt;span class="race-name"&gt;d=40, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.598276"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;69.4x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 8"&gt;&lt;span class="race-name"&gt;d=40, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.726724"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;84.3x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 9"&gt;&lt;span class="race-name"&gt;d=80, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.831034"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;96.4x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 10"&gt;&lt;span class="race-name"&gt;d=80, n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;116x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race050-panel-lpcmci" role="group" aria-label="LPCMCI: Andrey&amp;#x27;s speedup over tigramite: d=5 (4 observed), n=500: 8.56x; d=5 (4 observed), n=1,000: 12.0x; d=5 (4 observed), n=2,000: 15.9x; d=10 (7 observed), n=500: 10.4x; d=10 (7 observed), n=1,000: 12.6x; d=10 (7 observed), n=2,000: 18.7x; d=20 (14 observed), n=500: 10.8x; d=20 (14 observed), n=1,000: 12.2x; d=20 (14 observed), n=2,000: 16.5x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;d=5 (4 observed), n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.457754"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;8.56x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;d=5 (4 observed), n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.641711"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;12.0x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;d=5 (4 observed), n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.850267"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;15.9x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;d=10 (7 observed), n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.556150"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;10.4x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;d=10 (7 observed), n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.673797"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;12.6x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;d=10 (7 observed), n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;18.7x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 6"&gt;&lt;span class="race-name"&gt;d=20 (14 observed), n=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.577540"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;10.8x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 7"&gt;&lt;span class="race-name"&gt;d=20 (14 observed), n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.652406"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;12.2x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 8"&gt;&lt;span class="race-name"&gt;d=20 (14 observed), n=2,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.882353"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;16.5x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race050-panel-granger" role="group" aria-label="Granger: Andrey&amp;#x27;s speedup over causal-learn: d=10, n=1,000: 3.34x; d=20, n=1,000: 3.09x; d=40, n=1,000: 2.83x; d=80, n=1,000: 2.15x; d=160, n=1,000: 1.59x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;d=10, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;3.34x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;d=20, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.925150"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;3.09x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;d=40, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.847305"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.83x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;d=80, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.643713"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.15x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;d=160, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.476048"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.59x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race050-panel-varma-lingam" role="group" aria-label="VARMA-LiNGAM: Andrey&amp;#x27;s speedup over lingam: d=3, n=100: 1.05x; d=3, n=300: 1.00x; d=3, n=1,000: 1.02x; d=5, n=100: 1.06x; d=5, n=300: 0.962x; d=5, n=1,000: 1.03x; d=10, n=100: 1.03x; d=10, n=300: 1.01x; d=10, n=1,000: 1.04x; d=20, n=100: 1.11x; d=20, n=300: 1.13x; d=20, n=1,000: 1.12x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;d=3, n=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.929204"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.05x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;d=3, n=300&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.884956"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.00x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;d=3, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.902655"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.02x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;d=5, n=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.938053"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.06x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;d=5, n=300&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.851327"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;0.962x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;d=5, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.911504"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.03x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 6"&gt;&lt;span class="race-name"&gt;d=10, n=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.911504"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.03x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 7"&gt;&lt;span class="race-name"&gt;d=10, n=300&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.893805"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.01x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 8"&gt;&lt;span class="race-name"&gt;d=10, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.920354"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.04x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 9"&gt;&lt;span class="race-name"&gt;d=20, n=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.982301"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.11x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 10"&gt;&lt;span class="race-name"&gt;d=20, n=300&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.13x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 11"&gt;&lt;span class="race-name"&gt;d=20, n=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.991150"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.12x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race050-panel-longitudinallingam" role="group" aria-label="LongitudinalLiNGAM: Andrey&amp;#x27;s speedup over lingam: d=2, n=20, M=1,000: 1.37x; d=2, n=50, M=100: 1.60x; d=2, n=50, M=200: 1.42x; d=2, n=50, M=500: 1.34x; d=2, n=50, M=1,000: 1.33x; d=3, n=20, M=100: 1.55x; d=3, n=20, M=200: 1.47x; d=3, n=20, M=500: 1.52x; d=3, n=20, M=1,000: 1.66x; d=3, n=50, M=100: 1.52x; d=3, n=50, M=200: 1.54x; d=3, n=50, M=500: 1.59x; d=3, n=50, M=1,000: 1.55x; d=5, n=20, M=100: 1.90x; d=5, n=20, M=200: 2.02x; d=5, n=20, M=500: 2.24x; d=5, n=20, M=1,000: 1.77x; d=5, n=50, M=100: 1.87x; d=5, n=50, M=200: 1.95x; d=5, n=50, M=500: 1.96x; d=5, n=50, M=1,000: 1.84x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;d=2, n=20, M=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.611607"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.37x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;d=2, n=50, M=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.714286"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.60x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;d=2, n=50, M=200&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.633929"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.42x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;d=2, n=50, M=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.598214"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.34x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;d=2, n=50, M=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.593750"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.33x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;d=3, n=20, M=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.691964"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.55x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 6"&gt;&lt;span class="race-name"&gt;d=3, n=20, M=200&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.656250"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.47x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 7"&gt;&lt;span class="race-name"&gt;d=3, n=20, M=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.678571"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.52x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 8"&gt;&lt;span class="race-name"&gt;d=3, n=20, M=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.741071"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.66x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 9"&gt;&lt;span class="race-name"&gt;d=3, n=50, M=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.678571"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.52x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 10"&gt;&lt;span class="race-name"&gt;d=3, n=50, M=200&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.687500"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.54x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 11"&gt;&lt;span class="race-name"&gt;d=3, n=50, M=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.709821"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.59x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 12"&gt;&lt;span class="race-name"&gt;d=3, n=50, M=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.691964"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.55x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 13"&gt;&lt;span class="race-name"&gt;d=5, n=20, M=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.848214"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.90x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 14"&gt;&lt;span class="race-name"&gt;d=5, n=20, M=200&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.901786"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.02x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 15"&gt;&lt;span class="race-name"&gt;d=5, n=20, M=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.24x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 16"&gt;&lt;span class="race-name"&gt;d=5, n=20, M=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.790179"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.77x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 17"&gt;&lt;span class="race-name"&gt;d=5, n=50, M=100&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.834821"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.87x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 18"&gt;&lt;span class="race-name"&gt;d=5, n=50, M=200&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.870536"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.95x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 19"&gt;&lt;span class="race-name"&gt;d=5, n=50, M=500&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.875000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.96x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 20"&gt;&lt;span class="race-name"&gt;d=5, n=50, M=1,000&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.821429"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.84x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="race-note"&gt;Each bar is the median, over the datasets of that size, of the other package&amp;#x27;s time divided by Andrey&amp;#x27;s on the same dataset. Each method&amp;#x27;s bars share its own scale.&lt;/p&gt;&lt;/figure&gt;
&lt;p&gt;Speed is not accuracy. On the larger simulated graphs, PCMCI’s graphs have more errors than an
empty graph in both packages, and so do Granger’s. A fast answer still needs enough data for the
question; the release card lists where these methods fall short.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="other-highlights"&gt;
&lt;h2&gt;Other highlights&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Student-t test for PC and FCI.&lt;/strong&gt; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pc&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;fci&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;gfci&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cdnod&lt;/span&gt;&lt;/code&gt; take
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;indep_test=&amp;quot;student_t&amp;quot;&lt;/span&gt;&lt;/code&gt;, the partial-correlation test that the time-series methods use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VARMA-LiNGAM weights.&lt;/strong&gt; With &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;prune=True&lt;/span&gt;&lt;/code&gt;, VARMA-LiNGAM refits the weights it keeps by least
squares. These weights are no longer shrunk toward zero. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;max_iter&lt;/span&gt;&lt;/code&gt; sets the iteration limit of
its likelihood fit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Granger in any units.&lt;/strong&gt; Granger standardizes each series, so its answer does not depend on the
units of any variable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For details, read the &lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;changelog&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/examples/time_series.html"&gt;&lt;span class="doc std std-doc"&gt;time-series notebook&lt;/span&gt;&lt;/a&gt; runs all six methods on a simulated series.
You can open it in Colab, then pass your own table, one row per time step, to any of them.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="help-us-build-it"&gt;
&lt;h2&gt;Help us build it&lt;/h2&gt;
&lt;p&gt;Andrey is in alpha. The API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need.
We also welcome pull requests. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; explains how
you can set up.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261016-andrey-0-5-0.html"/>
    <summary>Causes take time. Rain today slows today’s traffic, and today’s traffic shapes tomorrow’s sales.
When you measure the same variables day after day, the order of the days tells you which way a
link can run: the future cannot cause the past. As of 0.5.0, we support six methods that use this
order: PCMCI, PCMCI+, LPCMCI, lasso Granger causality, VARMA-LiNGAM, and LongitudinalLiNGAM.</summary>
    <category term="Release" label="Release"/>
    <category term="Temporalcausaldiscovery" label="Temporal causal discovery"/>
    <published>2026-10-16T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261015-andrey-0-4-0.html</id>
    <title>Pairwise direction</title>
    <updated>2026-10-15T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="pairwise-direction"&gt;

&lt;p&gt;Does &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; cause &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;, or does &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; cause &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;? With only two variables, independence tests cannot tell:
both directions fit the data equally well. ANM and PNL instead use the shape of the relationship
to infer direction. This release adds ANM. As of 0.4.0, we support both methods.&lt;/p&gt;
&lt;p&gt;We explain how two variables can reveal their direction, run both tests on two pairs, and report
benchmark results.&lt;/p&gt;
&lt;section id="direction-from-two-variables"&gt;
&lt;h2&gt;Direction from two variables&lt;/h2&gt;
&lt;p&gt;PC and GES can orient an edge only through a third variable, such as a common effect. A pair has
no third variable, so &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; explain the data equally well. These methods leave the
edge undirected.&lt;/p&gt;
&lt;p&gt;A model of the mechanism can break the tie. Suppose &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;f(x)&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;e&lt;/span&gt;&lt;/code&gt;, where the noise &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;e&lt;/span&gt;&lt;/code&gt; is
independent of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;. Regress &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; on &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;, and the residual looks independent of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;. Regress &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; on
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;, and in general no residual is independent of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;, unless &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;f&lt;/span&gt;&lt;/code&gt; is linear and the noise is
Gaussian.&lt;/p&gt;
&lt;p&gt;Each test fits both directions and checks whether the residual is independent of the proposed
cause. It uses HSIC, a kernel test that detects nonlinear dependence as well as correlation.
The direction whose residual looks independent is the causal one.&lt;/p&gt;
&lt;p&gt;The methods fit different models:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ANM&lt;/strong&gt; (additive noise model) fits &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;f(x)&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;e&lt;/span&gt;&lt;/code&gt; by Gaussian-process regression. The direction
is generally identifiable when &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;f&lt;/span&gt;&lt;/code&gt; is nonlinear or the noise is non-Gaussian.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PNL&lt;/strong&gt; (post-nonlinear model) fits &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;f2(f1(x)&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;e)&lt;/span&gt;&lt;/code&gt;, where &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;f2&lt;/span&gt;&lt;/code&gt; is invertible. This is an
additive noise model observed through a distortion, such as a sensor that saturates. ANM is
the case where &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;f2&lt;/span&gt;&lt;/code&gt; changes nothing. PNL fits each transform as a sum of smooth basis functions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="supported-methods"&gt;
&lt;h2&gt;Supported methods&lt;/h2&gt;
&lt;p&gt;Before each release, we benchmark each supported method against another package and check its
results on known data. ANM is new in this release. PNL was experimental until now.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Model&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Returns&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.anm.html"&gt;&lt;span class="std std-doc"&gt;ANM&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;f(x)&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;e&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A two-node DAG, both p-values, and both test statistics&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.pnl.html"&gt;&lt;span class="std std-doc"&gt;PNL&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;f2(f1(x)&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;e)&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A two-node DAG, both p-values, and both test statistics&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Choose a method for your data:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ANM&lt;/strong&gt; when the effect is a smooth function of the cause plus independent noise. Its regression
time grows with the cube of the number of rows, so it suits up to a few thousand rows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PNL&lt;/strong&gt; when the effect may pass through a distortion or you have more rows. In our benchmarks,
its median fit took under 2 s at 5,000 rows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both assume that no hidden common cause links the pair. When one is plausible, use the methods in
the &lt;a class="reference internal" href="blog/20261012-andrey-0-2-0.html"&gt;&lt;span class="std std-doc"&gt;0.2.0 post&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="reading-the-result"&gt;
&lt;h2&gt;Reading the result&lt;/h2&gt;
&lt;p&gt;Each test returns a two-node DAG and four values in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;metadata&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pval_forward&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pval_backward&lt;/span&gt;&lt;/code&gt;: the p-values of the independence tests for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; and
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;. A large p-value means the residual looks independent of the proposed cause, so that
direction fits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;stat_forward&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;stat_backward&lt;/span&gt;&lt;/code&gt;: the two HSIC statistics, each divided by its mean under
independence. Smaller values mean weaker dependence.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By default, the edge points in the direction with the larger p-value. On a large sample, both
p-values can round to zero. The smaller statistic then decides. This rule always selects a
direction, even for unrelated variables.&lt;/p&gt;
&lt;p&gt;With a significance level such as &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;alpha=0.05&lt;/span&gt;&lt;/code&gt;, a direction fits when its p-value exceeds the
level. The test draws an edge only when exactly one direction fits.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="example-pairs"&gt;
&lt;h2&gt;Example pairs&lt;/h2&gt;
&lt;p&gt;In both pairs, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; causes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;e&lt;/span&gt;&lt;/code&gt; is Laplace noise:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Additive&lt;/strong&gt;: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;0.3&lt;/span&gt; &lt;span class="pre"&gt;*&lt;/span&gt; &lt;span class="pre"&gt;x**3&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;0.5&lt;/span&gt; &lt;span class="pre"&gt;*&lt;/span&gt; &lt;span class="pre"&gt;e&lt;/span&gt;&lt;/code&gt;. The noise adds to a function of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;, so both
models hold.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Saturating&lt;/strong&gt;: a sensor reads &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;=&lt;/span&gt; &lt;span class="pre"&gt;tanh(x&lt;/span&gt; &lt;span class="pre"&gt;+&lt;/span&gt; &lt;span class="pre"&gt;0.5&lt;/span&gt; &lt;span class="pre"&gt;*&lt;/span&gt; &lt;span class="pre"&gt;e)&lt;/span&gt;&lt;/code&gt;. The noise passes through the
saturation, so only the post-nonlinear model holds.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;

&lt;span class="c1"&gt;# x causes y in both pairs; e is noise.&lt;/span&gt;
&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;laplace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;laplace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The additive pair: y = x + 0.3 * x**3 + 0.5 * e.&lt;/span&gt;
&lt;span class="n"&gt;additive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;# The saturating pair, which a sensor reads: y = tanh(x + 0.5 * e).&lt;/span&gt;
&lt;span class="n"&gt;saturating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tanh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Additive pair&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;additive&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pnl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;additive&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Saturating pair&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;saturating&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pnl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;saturating&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# For comparison: ANM on the saturating pair with a significance level.&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;saturating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;.post-pair { container-type: inline-size; margin: 2rem 0; padding: clamp(1rem, 2.5vw, 1.5rem); border: 1px solid var(--andrey-line); border-radius: 12px; background: var(--andrey-surface); }.post-pair .pp-title { display: block; margin: 0 0 1rem; font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.02em; }.post-pair .pp-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; align-items: start; }.post-pair .pp-grid + .pp-grid { margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--andrey-line); }.post-pair svg { display: block; width: 100%; height: auto; overflow: visible; }.post-pair svg text { fill: var(--andrey-muted); font: 500 9px var(--font-sans); }.post-pair .pp-dots circle { fill: var(--andrey-muted); fill-opacity: 0.5; }.post-pair .pp-panel { margin: 0; padding: 0; }.post-pair .pp-panel figcaption, .post-pair .pp-name { margin: 0 0 0.5rem; padding: 0; font-size: 0.85rem; line-height: 1.4; text-align: start; }.post-pair .pp-panel strong, .post-pair .pp-name strong { display: block; color: var(--andrey-ink); font-weight: 600; }.post-pair .pp-panel figcaption span, .post-pair .pp-name span { color: var(--andrey-muted); font-size: 0.8rem; }.post-pair .pp-test + .pp-test { margin-top: 1rem; }.post-pair .pp-bar { fill: var(--andrey-muted); fill-opacity: 0.45; }.post-pair .pp-bar.is-chosen { fill: var(--andrey-accent); fill-opacity: 1; }.post-pair svg .pp-value { fill: var(--andrey-ink); font-weight: 600; }.post-pair .pp-cut { stroke: var(--andrey-muted); stroke-dasharray: 3 3; }.post-pair .pp-note { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--andrey-line); color: var(--andrey-muted); font-size: 0.8rem; line-height: 1.65; text-align: start; }@container (max-width: 34rem) { .post-pair .pp-grid { grid-template-columns: minmax(0, 1fr); } }&lt;/style&gt;&lt;figure class="post-pair" aria-labelledby="pp-title"&gt;&lt;p class="pp-title" id="pp-title"&gt;Two pairs, two models&lt;/p&gt;&lt;div class="pp-grid"&gt;&lt;figure class="pp-panel"&gt;&lt;figcaption&gt;&lt;strong&gt;The additive pair&lt;/strong&gt;&lt;span&gt;500 rows of y = x + 0.3 * x**3 + 0.5 * e&lt;/span&gt;&lt;/figcaption&gt;&lt;svg viewBox="0 0 300 230" role="img" aria-label="Scatter plot of the additive pair&amp;#x27;s 500 rows: y follows a cubic curve in x, with noise added to it."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="34" x2="290" y1="196.0" y2="196.0" class="tc-grid"/&gt;&lt;text x="28" y="199.0" text-anchor="end"&gt;-200&lt;/text&gt;&lt;line x1="34" x2="290" y1="134.7" y2="134.7" class="tc-grid"/&gt;&lt;text x="28" y="137.7" text-anchor="end"&gt;-100&lt;/text&gt;&lt;line x1="34" x2="290" y1="73.3" y2="73.3" class="tc-grid"/&gt;&lt;text x="28" y="76.3" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="34" x2="290" y1="12.0" y2="12.0" class="tc-grid"/&gt;&lt;text x="28" y="15.0" text-anchor="end"&gt;100&lt;/text&gt;&lt;text x="34.0" y="210" text-anchor="middle"&gt;-8&lt;/text&gt;&lt;text x="70.6" y="210" text-anchor="middle"&gt;-6&lt;/text&gt;&lt;text x="107.1" y="210" text-anchor="middle"&gt;-4&lt;/text&gt;&lt;text x="143.7" y="210" text-anchor="middle"&gt;-2&lt;/text&gt;&lt;text x="180.3" y="210" text-anchor="middle"&gt;0&lt;/text&gt;&lt;text x="216.9" y="210" text-anchor="middle"&gt;2&lt;/text&gt;&lt;text x="253.4" y="210" text-anchor="middle"&gt;4&lt;/text&gt;&lt;text x="290.0" y="210" text-anchor="middle"&gt;6&lt;/text&gt;&lt;/g&gt;&lt;g class="pp-dots"&gt;&lt;circle cx="186.1" cy="73.7" r="1.7"/&gt;&lt;circle cx="169.0" cy="73.3" r="1.7"/&gt;&lt;circle cx="134.5" cy="77.1" r="1.7"/&gt;&lt;circle cx="117.9" cy="82.9" r="1.7"/&gt;&lt;circle cx="198.3" cy="73.7" r="1.7"/&gt;&lt;circle cx="212.2" cy="71.3" r="1.7"/&gt;&lt;circle cx="184.7" cy="73.5" r="1.7"/&gt;&lt;circle cx="191.5" cy="72.0" r="1.7"/&gt;&lt;circle cx="182.0" cy="72.8" r="1.7"/&gt;&lt;circle cx="217.6" cy="69.7" r="1.7"/&gt;&lt;circle cx="198.6" cy="72.5" r="1.7"/&gt;&lt;circle cx="85.1" cy="102.5" r="1.7"/&gt;&lt;circle cx="203.2" cy="71.9" r="1.7"/&gt;&lt;circle cx="130.9" cy="78.5" r="1.7"/&gt;&lt;circle cx="191.5" cy="73.1" r="1.7"/&gt;&lt;circle cx="161.2" cy="73.6" r="1.7"/&gt;&lt;circle cx="204.0" cy="72.2" r="1.7"/&gt;&lt;circle cx="181.9" cy="73.0" r="1.7"/&gt;&lt;circle cx="170.9" cy="73.7" r="1.7"/&gt;&lt;circle cx="177.2" cy="73.7" r="1.7"/&gt;&lt;circle cx="127.8" cy="79.6" r="1.7"/&gt;&lt;circle cx="154.8" cy="74.6" r="1.7"/&gt;&lt;circle cx="187.9" cy="73.4" r="1.7"/&gt;&lt;circle cx="186.7" cy="74.2" r="1.7"/&gt;&lt;circle cx="185.1" cy="73.2" r="1.7"/&gt;&lt;circle cx="175.4" cy="73.3" r="1.7"/&gt;&lt;circle cx="275.2" cy="44.4" r="1.7"/&gt;&lt;circle cx="239.9" cy="65.1" r="1.7"/&gt;&lt;circle cx="188.8" cy="72.9" r="1.7"/&gt;&lt;circle cx="186.8" cy="73.1" r="1.7"/&gt;&lt;circle cx="188.9" cy="70.9" r="1.7"/&gt;&lt;circle cx="175.7" cy="73.2" r="1.7"/&gt;&lt;circle cx="156.4" cy="74.2" r="1.7"/&gt;&lt;circle cx="191.0" cy="73.1" r="1.7"/&gt;&lt;circle cx="181.2" cy="73.7" r="1.7"/&gt;&lt;circle cx="171.6" cy="73.9" r="1.7"/&gt;&lt;circle cx="179.8" cy="73.4" r="1.7"/&gt;&lt;circle cx="207.9" cy="71.8" r="1.7"/&gt;&lt;circle cx="217.3" cy="70.9" r="1.7"/&gt;&lt;circle cx="174.2" cy="73.3" r="1.7"/&gt;&lt;circle cx="183.1" cy="72.8" r="1.7"/&gt;&lt;circle cx="172.2" cy="73.8" r="1.7"/&gt;&lt;circle cx="184.1" cy="73.2" r="1.7"/&gt;&lt;circle cx="173.1" cy="73.5" r="1.7"/&gt;&lt;circle cx="175.8" cy="73.3" r="1.7"/&gt;&lt;circle cx="208.0" cy="72.1" r="1.7"/&gt;&lt;circle cx="165.9" cy="74.1" r="1.7"/&gt;&lt;circle cx="185.5" cy="73.0" r="1.7"/&gt;&lt;circle cx="147.7" cy="75.4" r="1.7"/&gt;&lt;circle cx="200.3" cy="71.9" r="1.7"/&gt;&lt;circle cx="195.9" cy="72.7" r="1.7"/&gt;&lt;circle cx="166.8" cy="73.9" r="1.7"/&gt;&lt;circle cx="205.9" cy="72.1" r="1.7"/&gt;&lt;circle cx="141.1" cy="76.7" r="1.7"/&gt;&lt;circle cx="173.0" cy="73.5" r="1.7"/&gt;&lt;circle cx="158.3" cy="74.6" r="1.7"/&gt;&lt;circle cx="178.4" cy="73.0" r="1.7"/&gt;&lt;circle cx="196.7" cy="72.8" r="1.7"/&gt;&lt;circle cx="166.1" cy="73.8" r="1.7"/&gt;&lt;circle cx="138.9" cy="76.8" r="1.7"/&gt;&lt;circle cx="176.4" cy="73.4" r="1.7"/&gt;&lt;circle cx="163.4" cy="73.6" r="1.7"/&gt;&lt;circle cx="149.1" cy="75.6" r="1.7"/&gt;&lt;circle cx="183.5" cy="73.6" r="1.7"/&gt;&lt;circle cx="170.9" cy="74.1" r="1.7"/&gt;&lt;circle cx="188.0" cy="73.6" r="1.7"/&gt;&lt;circle cx="163.5" cy="74.0" r="1.7"/&gt;&lt;circle cx="219.7" cy="70.5" r="1.7"/&gt;&lt;circle cx="174.5" cy="73.2" r="1.7"/&gt;&lt;circle cx="151.8" cy="75.9" r="1.7"/&gt;&lt;circle cx="185.7" cy="73.2" r="1.7"/&gt;&lt;circle cx="215.5" cy="70.9" r="1.7"/&gt;&lt;circle cx="178.0" cy="73.7" r="1.7"/&gt;&lt;circle cx="224.2" cy="69.4" r="1.7"/&gt;&lt;circle cx="180.3" cy="73.6" r="1.7"/&gt;&lt;circle cx="177.3" cy="73.6" r="1.7"/&gt;&lt;circle cx="185.3" cy="72.6" r="1.7"/&gt;&lt;circle cx="264.9" cy="52.4" r="1.7"/&gt;&lt;circle cx="222.0" cy="69.8" r="1.7"/&gt;&lt;circle cx="178.8" cy="73.3" r="1.7"/&gt;&lt;circle cx="193.5" cy="72.7" r="1.7"/&gt;&lt;circle cx="180.2" cy="73.2" r="1.7"/&gt;&lt;circle cx="181.4" cy="73.8" r="1.7"/&gt;&lt;circle cx="195.8" cy="72.6" r="1.7"/&gt;&lt;circle cx="176.9" cy="73.3" r="1.7"/&gt;&lt;circle cx="191.9" cy="72.6" r="1.7"/&gt;&lt;circle cx="190.3" cy="72.9" r="1.7"/&gt;&lt;circle cx="216.8" cy="71.1" r="1.7"/&gt;&lt;circle cx="153.4" cy="75.4" r="1.7"/&gt;&lt;circle cx="191.5" cy="73.0" r="1.7"/&gt;&lt;circle cx="215.6" cy="70.2" r="1.7"/&gt;&lt;circle cx="230.5" cy="67.9" r="1.7"/&gt;&lt;circle cx="115.8" cy="83.1" r="1.7"/&gt;&lt;circle cx="204.0" cy="72.2" r="1.7"/&gt;&lt;circle cx="240.3" cy="64.6" r="1.7"/&gt;&lt;circle cx="225.2" cy="69.1" r="1.7"/&gt;&lt;circle cx="158.1" cy="74.2" r="1.7"/&gt;&lt;circle cx="233.4" cy="67.2" r="1.7"/&gt;&lt;circle cx="208.0" cy="71.3" r="1.7"/&gt;&lt;circle cx="199.2" cy="72.7" r="1.7"/&gt;&lt;circle cx="179.5" cy="74.7" r="1.7"/&gt;&lt;circle cx="166.3" cy="73.7" r="1.7"/&gt;&lt;circle cx="197.2" cy="72.5" r="1.7"/&gt;&lt;circle cx="214.6" cy="70.8" r="1.7"/&gt;&lt;circle cx="168.8" cy="73.6" r="1.7"/&gt;&lt;circle cx="181.8" cy="73.2" r="1.7"/&gt;&lt;circle cx="178.1" cy="73.9" r="1.7"/&gt;&lt;circle cx="216.5" cy="70.6" r="1.7"/&gt;&lt;circle cx="134.3" cy="79.1" r="1.7"/&gt;&lt;circle cx="191.7" cy="73.2" r="1.7"/&gt;&lt;circle cx="185.0" cy="73.2" r="1.7"/&gt;&lt;circle cx="127.8" cy="79.5" r="1.7"/&gt;&lt;circle cx="190.8" cy="73.4" r="1.7"/&gt;&lt;circle cx="117.3" cy="83.0" r="1.7"/&gt;&lt;circle cx="193.6" cy="72.7" r="1.7"/&gt;&lt;circle cx="180.8" cy="73.4" r="1.7"/&gt;&lt;circle cx="216.0" cy="70.6" r="1.7"/&gt;&lt;circle cx="143.3" cy="76.3" r="1.7"/&gt;&lt;circle cx="201.3" cy="72.7" r="1.7"/&gt;&lt;circle cx="143.4" cy="75.9" r="1.7"/&gt;&lt;circle cx="173.5" cy="73.4" r="1.7"/&gt;&lt;circle cx="177.5" cy="73.5" r="1.7"/&gt;&lt;circle cx="229.5" cy="68.5" r="1.7"/&gt;&lt;circle cx="182.7" cy="73.1" r="1.7"/&gt;&lt;circle cx="168.2" cy="73.6" r="1.7"/&gt;&lt;circle cx="167.0" cy="74.2" r="1.7"/&gt;&lt;circle cx="207.7" cy="71.7" r="1.7"/&gt;&lt;circle cx="165.8" cy="74.0" r="1.7"/&gt;&lt;circle cx="154.9" cy="74.1" r="1.7"/&gt;&lt;circle cx="170.2" cy="73.5" r="1.7"/&gt;&lt;circle cx="183.7" cy="73.4" r="1.7"/&gt;&lt;circle cx="182.4" cy="72.6" r="1.7"/&gt;&lt;circle cx="198.0" cy="73.5" r="1.7"/&gt;&lt;circle cx="182.6" cy="73.6" r="1.7"/&gt;&lt;circle cx="170.2" cy="73.9" r="1.7"/&gt;&lt;circle cx="176.8" cy="73.3" r="1.7"/&gt;&lt;circle cx="198.8" cy="72.5" r="1.7"/&gt;&lt;circle cx="185.6" cy="73.2" r="1.7"/&gt;&lt;circle cx="226.1" cy="69.2" r="1.7"/&gt;&lt;circle cx="174.8" cy="73.3" r="1.7"/&gt;&lt;circle cx="182.3" cy="73.7" r="1.7"/&gt;&lt;circle cx="184.1" cy="73.4" r="1.7"/&gt;&lt;circle cx="202.1" cy="72.0" r="1.7"/&gt;&lt;circle cx="157.7" cy="74.5" r="1.7"/&gt;&lt;circle cx="176.5" cy="73.0" r="1.7"/&gt;&lt;circle cx="211.6" cy="71.3" r="1.7"/&gt;&lt;circle cx="135.5" cy="77.3" r="1.7"/&gt;&lt;circle cx="199.2" cy="72.8" r="1.7"/&gt;&lt;circle cx="176.9" cy="73.6" r="1.7"/&gt;&lt;circle cx="200.0" cy="72.5" r="1.7"/&gt;&lt;circle cx="108.7" cy="86.8" r="1.7"/&gt;&lt;circle cx="174.5" cy="73.5" r="1.7"/&gt;&lt;circle cx="146.5" cy="75.3" r="1.7"/&gt;&lt;circle cx="186.9" cy="73.4" r="1.7"/&gt;&lt;circle cx="169.3" cy="73.4" r="1.7"/&gt;&lt;circle cx="189.8" cy="72.5" r="1.7"/&gt;&lt;circle cx="220.3" cy="70.7" r="1.7"/&gt;&lt;circle cx="155.2" cy="75.9" r="1.7"/&gt;&lt;circle cx="204.2" cy="72.3" r="1.7"/&gt;&lt;circle cx="141.4" cy="76.5" r="1.7"/&gt;&lt;circle cx="175.3" cy="72.6" r="1.7"/&gt;&lt;circle cx="177.5" cy="74.0" r="1.7"/&gt;&lt;circle cx="179.9" cy="73.1" r="1.7"/&gt;&lt;circle cx="236.2" cy="66.2" r="1.7"/&gt;&lt;circle cx="194.9" cy="73.2" r="1.7"/&gt;&lt;circle cx="171.5" cy="73.7" r="1.7"/&gt;&lt;circle cx="169.0" cy="73.8" r="1.7"/&gt;&lt;circle cx="204.0" cy="72.4" r="1.7"/&gt;&lt;circle cx="206.6" cy="72.1" r="1.7"/&gt;&lt;circle cx="180.7" cy="73.4" r="1.7"/&gt;&lt;circle cx="173.5" cy="72.8" r="1.7"/&gt;&lt;circle cx="264.2" cy="52.8" r="1.7"/&gt;&lt;circle cx="171.9" cy="72.9" r="1.7"/&gt;&lt;circle cx="161.9" cy="75.0" r="1.7"/&gt;&lt;circle cx="206.4" cy="72.5" r="1.7"/&gt;&lt;circle cx="198.2" cy="73.4" r="1.7"/&gt;&lt;circle cx="187.8" cy="72.9" r="1.7"/&gt;&lt;circle cx="225.8" cy="69.2" r="1.7"/&gt;&lt;circle cx="215.2" cy="70.8" r="1.7"/&gt;&lt;circle cx="192.8" cy="72.6" r="1.7"/&gt;&lt;circle cx="203.7" cy="72.3" r="1.7"/&gt;&lt;circle cx="167.4" cy="74.0" r="1.7"/&gt;&lt;circle cx="157.2" cy="74.3" r="1.7"/&gt;&lt;circle cx="187.9" cy="73.1" r="1.7"/&gt;&lt;circle cx="190.5" cy="73.3" r="1.7"/&gt;&lt;circle cx="160.2" cy="74.8" r="1.7"/&gt;&lt;circle cx="176.0" cy="73.3" r="1.7"/&gt;&lt;circle cx="211.7" cy="71.0" r="1.7"/&gt;&lt;circle cx="182.7" cy="72.8" r="1.7"/&gt;&lt;circle cx="183.4" cy="73.2" r="1.7"/&gt;&lt;circle cx="163.0" cy="74.4" r="1.7"/&gt;&lt;circle cx="181.3" cy="73.5" r="1.7"/&gt;&lt;circle cx="181.2" cy="73.3" r="1.7"/&gt;&lt;circle cx="148.7" cy="75.0" r="1.7"/&gt;&lt;circle cx="241.0" cy="64.4" r="1.7"/&gt;&lt;circle cx="183.1" cy="73.4" r="1.7"/&gt;&lt;circle cx="100.6" cy="91.0" r="1.7"/&gt;&lt;circle cx="194.7" cy="72.8" r="1.7"/&gt;&lt;circle cx="237.6" cy="64.7" r="1.7"/&gt;&lt;circle cx="183.9" cy="73.3" r="1.7"/&gt;&lt;circle cx="172.1" cy="73.3" r="1.7"/&gt;&lt;circle cx="162.4" cy="75.2" r="1.7"/&gt;&lt;circle cx="188.0" cy="72.9" r="1.7"/&gt;&lt;circle cx="163.1" cy="74.1" r="1.7"/&gt;&lt;circle cx="183.4" cy="73.2" r="1.7"/&gt;&lt;circle cx="184.5" cy="73.5" r="1.7"/&gt;&lt;circle cx="227.6" cy="68.0" r="1.7"/&gt;&lt;circle cx="144.9" cy="76.1" r="1.7"/&gt;&lt;circle cx="180.3" cy="73.3" r="1.7"/&gt;&lt;circle cx="192.5" cy="72.8" r="1.7"/&gt;&lt;circle cx="161.3" cy="73.8" r="1.7"/&gt;&lt;circle cx="175.7" cy="73.5" r="1.7"/&gt;&lt;circle cx="142.4" cy="75.9" r="1.7"/&gt;&lt;circle cx="191.3" cy="72.9" r="1.7"/&gt;&lt;circle cx="148.5" cy="74.6" r="1.7"/&gt;&lt;circle cx="176.0" cy="73.3" r="1.7"/&gt;&lt;circle cx="205.4" cy="71.5" r="1.7"/&gt;&lt;circle cx="179.2" cy="72.7" r="1.7"/&gt;&lt;circle cx="212.2" cy="71.0" r="1.7"/&gt;&lt;circle cx="194.2" cy="73.2" r="1.7"/&gt;&lt;circle cx="212.8" cy="71.6" r="1.7"/&gt;&lt;circle cx="155.3" cy="74.6" r="1.7"/&gt;&lt;circle cx="145.2" cy="76.0" r="1.7"/&gt;&lt;circle cx="144.4" cy="76.0" r="1.7"/&gt;&lt;circle cx="204.8" cy="72.4" r="1.7"/&gt;&lt;circle cx="186.0" cy="72.9" r="1.7"/&gt;&lt;circle cx="180.2" cy="73.0" r="1.7"/&gt;&lt;circle cx="159.9" cy="74.7" r="1.7"/&gt;&lt;circle cx="188.1" cy="73.0" r="1.7"/&gt;&lt;circle cx="172.0" cy="73.7" r="1.7"/&gt;&lt;circle cx="190.3" cy="72.7" r="1.7"/&gt;&lt;circle cx="178.8" cy="73.4" r="1.7"/&gt;&lt;circle cx="180.6" cy="73.1" r="1.7"/&gt;&lt;circle cx="196.1" cy="72.6" r="1.7"/&gt;&lt;circle cx="149.5" cy="74.3" r="1.7"/&gt;&lt;circle cx="183.4" cy="73.3" r="1.7"/&gt;&lt;circle cx="163.3" cy="72.9" r="1.7"/&gt;&lt;circle cx="197.8" cy="72.6" r="1.7"/&gt;&lt;circle cx="179.9" cy="73.7" r="1.7"/&gt;&lt;circle cx="249.6" cy="60.7" r="1.7"/&gt;&lt;circle cx="161.9" cy="74.3" r="1.7"/&gt;&lt;circle cx="227.9" cy="68.4" r="1.7"/&gt;&lt;circle cx="197.1" cy="72.5" r="1.7"/&gt;&lt;circle cx="179.6" cy="73.6" r="1.7"/&gt;&lt;circle cx="198.3" cy="73.4" r="1.7"/&gt;&lt;circle cx="184.5" cy="72.1" r="1.7"/&gt;&lt;circle cx="187.1" cy="72.8" r="1.7"/&gt;&lt;circle cx="212.4" cy="71.7" r="1.7"/&gt;&lt;circle cx="143.1" cy="75.8" r="1.7"/&gt;&lt;circle cx="200.6" cy="72.6" r="1.7"/&gt;&lt;circle cx="175.4" cy="73.7" r="1.7"/&gt;&lt;circle cx="172.4" cy="73.4" r="1.7"/&gt;&lt;circle cx="261.2" cy="54.4" r="1.7"/&gt;&lt;circle cx="195.4" cy="72.4" r="1.7"/&gt;&lt;circle cx="179.7" cy="73.7" r="1.7"/&gt;&lt;circle cx="177.2" cy="73.2" r="1.7"/&gt;&lt;circle cx="206.0" cy="72.0" r="1.7"/&gt;&lt;circle cx="148.3" cy="75.5" r="1.7"/&gt;&lt;circle cx="190.1" cy="72.8" r="1.7"/&gt;&lt;circle cx="196.1" cy="72.3" r="1.7"/&gt;&lt;circle cx="197.0" cy="72.8" r="1.7"/&gt;&lt;circle cx="172.3" cy="74.0" r="1.7"/&gt;&lt;circle cx="196.7" cy="71.4" r="1.7"/&gt;&lt;circle cx="165.7" cy="73.4" r="1.7"/&gt;&lt;circle cx="174.4" cy="73.7" r="1.7"/&gt;&lt;circle cx="177.0" cy="73.1" r="1.7"/&gt;&lt;circle cx="181.9" cy="73.8" r="1.7"/&gt;&lt;circle cx="153.0" cy="74.3" r="1.7"/&gt;&lt;circle cx="176.5" cy="73.2" r="1.7"/&gt;&lt;circle cx="44.7" cy="153.2" r="1.7"/&gt;&lt;circle cx="192.6" cy="73.0" r="1.7"/&gt;&lt;circle cx="202.5" cy="72.2" r="1.7"/&gt;&lt;circle cx="156.9" cy="74.6" r="1.7"/&gt;&lt;circle cx="189.9" cy="72.8" r="1.7"/&gt;&lt;circle cx="199.1" cy="72.4" r="1.7"/&gt;&lt;circle cx="240.9" cy="64.9" r="1.7"/&gt;&lt;circle cx="201.6" cy="72.3" r="1.7"/&gt;&lt;circle cx="177.3" cy="74.5" r="1.7"/&gt;&lt;circle cx="238.9" cy="65.8" r="1.7"/&gt;&lt;circle cx="234.3" cy="67.1" r="1.7"/&gt;&lt;circle cx="180.4" cy="73.4" r="1.7"/&gt;&lt;circle cx="193.2" cy="74.0" r="1.7"/&gt;&lt;circle cx="212.4" cy="70.7" r="1.7"/&gt;&lt;circle cx="179.4" cy="73.6" r="1.7"/&gt;&lt;circle cx="204.1" cy="72.3" r="1.7"/&gt;&lt;circle cx="189.7" cy="73.1" r="1.7"/&gt;&lt;circle cx="170.6" cy="73.0" r="1.7"/&gt;&lt;circle cx="194.3" cy="72.9" r="1.7"/&gt;&lt;circle cx="183.1" cy="73.3" r="1.7"/&gt;&lt;circle cx="149.7" cy="75.4" r="1.7"/&gt;&lt;circle cx="175.8" cy="72.6" r="1.7"/&gt;&lt;circle cx="145.3" cy="75.9" r="1.7"/&gt;&lt;circle cx="179.4" cy="73.9" r="1.7"/&gt;&lt;circle cx="177.5" cy="73.3" r="1.7"/&gt;&lt;circle cx="177.3" cy="73.3" r="1.7"/&gt;&lt;circle cx="183.8" cy="73.3" r="1.7"/&gt;&lt;circle cx="154.6" cy="75.0" r="1.7"/&gt;&lt;circle cx="217.2" cy="70.6" r="1.7"/&gt;&lt;circle cx="188.7" cy="73.1" r="1.7"/&gt;&lt;circle cx="199.4" cy="71.1" r="1.7"/&gt;&lt;circle cx="209.1" cy="71.3" r="1.7"/&gt;&lt;circle cx="183.6" cy="73.1" r="1.7"/&gt;&lt;circle cx="134.2" cy="78.1" r="1.7"/&gt;&lt;circle cx="190.3" cy="72.8" r="1.7"/&gt;&lt;circle cx="183.0" cy="73.0" r="1.7"/&gt;&lt;circle cx="199.6" cy="73.1" r="1.7"/&gt;&lt;circle cx="181.5" cy="73.4" r="1.7"/&gt;&lt;circle cx="198.3" cy="72.7" r="1.7"/&gt;&lt;circle cx="273.9" cy="45.5" r="1.7"/&gt;&lt;circle cx="173.8" cy="73.4" r="1.7"/&gt;&lt;circle cx="160.7" cy="74.5" r="1.7"/&gt;&lt;circle cx="175.8" cy="73.5" r="1.7"/&gt;&lt;circle cx="193.2" cy="71.6" r="1.7"/&gt;&lt;circle cx="177.9" cy="74.5" r="1.7"/&gt;&lt;circle cx="183.8" cy="73.3" r="1.7"/&gt;&lt;circle cx="155.3" cy="74.8" r="1.7"/&gt;&lt;circle cx="191.3" cy="73.0" r="1.7"/&gt;&lt;circle cx="169.7" cy="73.3" r="1.7"/&gt;&lt;circle cx="162.7" cy="74.1" r="1.7"/&gt;&lt;circle cx="204.0" cy="71.7" r="1.7"/&gt;&lt;circle cx="182.8" cy="73.2" r="1.7"/&gt;&lt;circle cx="179.7" cy="73.4" r="1.7"/&gt;&lt;circle cx="209.5" cy="71.8" r="1.7"/&gt;&lt;circle cx="148.1" cy="75.1" r="1.7"/&gt;&lt;circle cx="189.4" cy="72.9" r="1.7"/&gt;&lt;circle cx="172.6" cy="73.9" r="1.7"/&gt;&lt;circle cx="161.1" cy="74.6" r="1.7"/&gt;&lt;circle cx="188.2" cy="73.5" r="1.7"/&gt;&lt;circle cx="174.4" cy="73.0" r="1.7"/&gt;&lt;circle cx="172.7" cy="73.7" r="1.7"/&gt;&lt;circle cx="220.2" cy="69.8" r="1.7"/&gt;&lt;circle cx="163.5" cy="73.6" r="1.7"/&gt;&lt;circle cx="180.7" cy="73.6" r="1.7"/&gt;&lt;circle cx="124.8" cy="81.2" r="1.7"/&gt;&lt;circle cx="159.8" cy="74.6" r="1.7"/&gt;&lt;circle cx="206.9" cy="71.6" r="1.7"/&gt;&lt;circle cx="196.1" cy="73.8" r="1.7"/&gt;&lt;circle cx="182.5" cy="73.2" r="1.7"/&gt;&lt;circle cx="165.5" cy="74.2" r="1.7"/&gt;&lt;circle cx="182.5" cy="73.0" r="1.7"/&gt;&lt;circle cx="112.3" cy="85.1" r="1.7"/&gt;&lt;circle cx="190.4" cy="72.9" r="1.7"/&gt;&lt;circle cx="190.7" cy="73.1" r="1.7"/&gt;&lt;circle cx="186.6" cy="73.1" r="1.7"/&gt;&lt;circle cx="184.9" cy="73.9" r="1.7"/&gt;&lt;circle cx="145.3" cy="75.5" r="1.7"/&gt;&lt;circle cx="167.3" cy="73.3" r="1.7"/&gt;&lt;circle cx="183.2" cy="73.0" r="1.7"/&gt;&lt;circle cx="175.9" cy="73.5" r="1.7"/&gt;&lt;circle cx="256.0" cy="57.4" r="1.7"/&gt;&lt;circle cx="214.7" cy="72.5" r="1.7"/&gt;&lt;circle cx="158.5" cy="74.3" r="1.7"/&gt;&lt;circle cx="183.9" cy="73.0" r="1.7"/&gt;&lt;circle cx="189.4" cy="73.1" r="1.7"/&gt;&lt;circle cx="156.6" cy="74.1" r="1.7"/&gt;&lt;circle cx="171.7" cy="76.1" r="1.7"/&gt;&lt;circle cx="190.6" cy="72.9" r="1.7"/&gt;&lt;circle cx="209.9" cy="71.7" r="1.7"/&gt;&lt;circle cx="173.3" cy="73.5" r="1.7"/&gt;&lt;circle cx="166.8" cy="74.1" r="1.7"/&gt;&lt;circle cx="199.2" cy="72.8" r="1.7"/&gt;&lt;circle cx="183.7" cy="73.1" r="1.7"/&gt;&lt;circle cx="179.4" cy="73.4" r="1.7"/&gt;&lt;circle cx="168.1" cy="74.1" r="1.7"/&gt;&lt;circle cx="145.0" cy="75.4" r="1.7"/&gt;&lt;circle cx="119.4" cy="81.7" r="1.7"/&gt;&lt;circle cx="183.5" cy="73.2" r="1.7"/&gt;&lt;circle cx="162.7" cy="73.9" r="1.7"/&gt;&lt;circle cx="235.5" cy="66.5" r="1.7"/&gt;&lt;circle cx="152.2" cy="74.7" r="1.7"/&gt;&lt;circle cx="178.4" cy="73.1" r="1.7"/&gt;&lt;circle cx="176.0" cy="73.3" r="1.7"/&gt;&lt;circle cx="166.3" cy="74.1" r="1.7"/&gt;&lt;circle cx="192.9" cy="73.8" r="1.7"/&gt;&lt;circle cx="186.5" cy="73.0" r="1.7"/&gt;&lt;circle cx="191.3" cy="73.0" r="1.7"/&gt;&lt;circle cx="147.4" cy="75.0" r="1.7"/&gt;&lt;circle cx="173.9" cy="73.2" r="1.7"/&gt;&lt;circle cx="181.0" cy="73.3" r="1.7"/&gt;&lt;circle cx="177.4" cy="73.5" r="1.7"/&gt;&lt;circle cx="134.4" cy="77.6" r="1.7"/&gt;&lt;circle cx="163.0" cy="73.9" r="1.7"/&gt;&lt;circle cx="220.7" cy="69.9" r="1.7"/&gt;&lt;circle cx="159.7" cy="73.9" r="1.7"/&gt;&lt;circle cx="202.6" cy="72.2" r="1.7"/&gt;&lt;circle cx="199.2" cy="72.5" r="1.7"/&gt;&lt;circle cx="175.8" cy="73.5" r="1.7"/&gt;&lt;circle cx="179.0" cy="72.9" r="1.7"/&gt;&lt;circle cx="199.4" cy="72.6" r="1.7"/&gt;&lt;circle cx="188.5" cy="72.7" r="1.7"/&gt;&lt;circle cx="200.8" cy="72.4" r="1.7"/&gt;&lt;circle cx="193.5" cy="73.4" r="1.7"/&gt;&lt;circle cx="189.1" cy="73.0" r="1.7"/&gt;&lt;circle cx="212.3" cy="71.4" r="1.7"/&gt;&lt;circle cx="199.3" cy="72.5" r="1.7"/&gt;&lt;circle cx="161.5" cy="73.8" r="1.7"/&gt;&lt;circle cx="192.8" cy="73.2" r="1.7"/&gt;&lt;circle cx="148.2" cy="75.4" r="1.7"/&gt;&lt;circle cx="177.4" cy="73.4" r="1.7"/&gt;&lt;circle cx="176.1" cy="73.2" r="1.7"/&gt;&lt;circle cx="163.7" cy="73.4" r="1.7"/&gt;&lt;circle cx="218.4" cy="70.4" r="1.7"/&gt;&lt;circle cx="149.9" cy="74.6" r="1.7"/&gt;&lt;circle cx="95.7" cy="94.5" r="1.7"/&gt;&lt;circle cx="172.3" cy="73.4" r="1.7"/&gt;&lt;circle cx="253.2" cy="59.0" r="1.7"/&gt;&lt;circle cx="168.7" cy="73.7" r="1.7"/&gt;&lt;circle cx="200.1" cy="72.8" r="1.7"/&gt;&lt;circle cx="160.9" cy="74.3" r="1.7"/&gt;&lt;circle cx="183.8" cy="73.4" r="1.7"/&gt;&lt;circle cx="225.8" cy="68.1" r="1.7"/&gt;&lt;circle cx="190.7" cy="72.8" r="1.7"/&gt;&lt;circle cx="239.6" cy="64.5" r="1.7"/&gt;&lt;circle cx="183.2" cy="73.4" r="1.7"/&gt;&lt;circle cx="242.5" cy="64.1" r="1.7"/&gt;&lt;circle cx="200.8" cy="72.3" r="1.7"/&gt;&lt;circle cx="195.2" cy="72.8" r="1.7"/&gt;&lt;circle cx="207.7" cy="71.8" r="1.7"/&gt;&lt;circle cx="185.9" cy="73.1" r="1.7"/&gt;&lt;circle cx="174.1" cy="74.4" r="1.7"/&gt;&lt;circle cx="181.4" cy="73.4" r="1.7"/&gt;&lt;circle cx="165.8" cy="76.3" r="1.7"/&gt;&lt;circle cx="195.1" cy="72.7" r="1.7"/&gt;&lt;circle cx="160.6" cy="74.2" r="1.7"/&gt;&lt;circle cx="183.4" cy="73.7" r="1.7"/&gt;&lt;circle cx="181.6" cy="73.5" r="1.7"/&gt;&lt;circle cx="188.0" cy="72.8" r="1.7"/&gt;&lt;circle cx="193.7" cy="72.5" r="1.7"/&gt;&lt;circle cx="152.6" cy="75.0" r="1.7"/&gt;&lt;circle cx="185.5" cy="72.8" r="1.7"/&gt;&lt;circle cx="176.8" cy="73.6" r="1.7"/&gt;&lt;circle cx="185.0" cy="73.0" r="1.7"/&gt;&lt;circle cx="189.3" cy="73.0" r="1.7"/&gt;&lt;circle cx="183.7" cy="73.3" r="1.7"/&gt;&lt;circle cx="191.7" cy="72.8" r="1.7"/&gt;&lt;circle cx="181.0" cy="74.4" r="1.7"/&gt;&lt;circle cx="178.9" cy="73.7" r="1.7"/&gt;&lt;circle cx="170.1" cy="73.9" r="1.7"/&gt;&lt;circle cx="166.0" cy="73.8" r="1.7"/&gt;&lt;circle cx="189.3" cy="72.8" r="1.7"/&gt;&lt;circle cx="189.4" cy="72.9" r="1.7"/&gt;&lt;circle cx="163.1" cy="73.8" r="1.7"/&gt;&lt;circle cx="232.9" cy="67.8" r="1.7"/&gt;&lt;circle cx="187.9" cy="73.5" r="1.7"/&gt;&lt;circle cx="181.5" cy="72.9" r="1.7"/&gt;&lt;circle cx="201.3" cy="72.5" r="1.7"/&gt;&lt;circle cx="179.8" cy="73.3" r="1.7"/&gt;&lt;circle cx="179.4" cy="73.4" r="1.7"/&gt;&lt;circle cx="168.2" cy="73.7" r="1.7"/&gt;&lt;circle cx="159.0" cy="74.9" r="1.7"/&gt;&lt;circle cx="190.3" cy="72.7" r="1.7"/&gt;&lt;circle cx="201.6" cy="72.8" r="1.7"/&gt;&lt;circle cx="188.3" cy="72.9" r="1.7"/&gt;&lt;circle cx="174.7" cy="73.6" r="1.7"/&gt;&lt;circle cx="183.3" cy="73.2" r="1.7"/&gt;&lt;circle cx="182.8" cy="73.1" r="1.7"/&gt;&lt;circle cx="218.0" cy="70.5" r="1.7"/&gt;&lt;circle cx="175.6" cy="74.2" r="1.7"/&gt;&lt;circle cx="160.0" cy="73.5" r="1.7"/&gt;&lt;circle cx="205.9" cy="72.0" r="1.7"/&gt;&lt;circle cx="208.8" cy="71.5" r="1.7"/&gt;&lt;circle cx="137.5" cy="77.1" r="1.7"/&gt;&lt;circle cx="163.4" cy="73.7" r="1.7"/&gt;&lt;circle cx="186.1" cy="73.1" r="1.7"/&gt;&lt;circle cx="196.0" cy="73.1" r="1.7"/&gt;&lt;circle cx="184.7" cy="73.1" r="1.7"/&gt;&lt;circle cx="162.7" cy="74.4" r="1.7"/&gt;&lt;circle cx="153.8" cy="74.5" r="1.7"/&gt;&lt;circle cx="180.5" cy="73.2" r="1.7"/&gt;&lt;circle cx="198.5" cy="71.9" r="1.7"/&gt;&lt;circle cx="165.0" cy="73.9" r="1.7"/&gt;&lt;circle cx="145.6" cy="76.0" r="1.7"/&gt;&lt;circle cx="182.3" cy="73.2" r="1.7"/&gt;&lt;circle cx="162.8" cy="73.8" r="1.7"/&gt;&lt;circle cx="143.6" cy="75.9" r="1.7"/&gt;&lt;circle cx="194.7" cy="72.9" r="1.7"/&gt;&lt;circle cx="199.1" cy="72.4" r="1.7"/&gt;&lt;circle cx="176.1" cy="72.8" r="1.7"/&gt;&lt;circle cx="170.6" cy="73.5" r="1.7"/&gt;&lt;circle cx="169.5" cy="73.8" r="1.7"/&gt;&lt;circle cx="174.3" cy="73.0" r="1.7"/&gt;&lt;circle cx="183.3" cy="72.9" r="1.7"/&gt;&lt;circle cx="181.3" cy="72.9" r="1.7"/&gt;&lt;circle cx="174.0" cy="74.0" r="1.7"/&gt;&lt;circle cx="186.2" cy="73.3" r="1.7"/&gt;&lt;circle cx="188.2" cy="72.8" r="1.7"/&gt;&lt;circle cx="182.6" cy="73.4" r="1.7"/&gt;&lt;circle cx="175.6" cy="74.1" r="1.7"/&gt;&lt;circle cx="185.5" cy="73.0" r="1.7"/&gt;&lt;circle cx="184.0" cy="72.9" r="1.7"/&gt;&lt;circle cx="173.3" cy="73.5" r="1.7"/&gt;&lt;circle cx="171.1" cy="73.8" r="1.7"/&gt;&lt;circle cx="182.0" cy="73.2" r="1.7"/&gt;&lt;circle cx="184.9" cy="74.1" r="1.7"/&gt;&lt;circle cx="184.9" cy="73.1" r="1.7"/&gt;&lt;circle cx="175.4" cy="73.1" r="1.7"/&gt;&lt;circle cx="182.9" cy="73.8" r="1.7"/&gt;&lt;circle cx="245.4" cy="62.9" r="1.7"/&gt;&lt;circle cx="177.4" cy="73.8" r="1.7"/&gt;&lt;circle cx="201.5" cy="72.4" r="1.7"/&gt;&lt;/g&gt;&lt;text class="tc-title" x="290" y="226" text-anchor="end"&gt;x&lt;/text&gt;&lt;text class="tc-title" x="8" y="107.0" text-anchor="middle"&gt;y&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;div class="pp-tests"&gt;&lt;div class="pp-test"&gt;&lt;p class="pp-name"&gt;&lt;strong&gt;ANM&lt;/strong&gt;&lt;span&gt;names x -&amp;gt; y, the true direction&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 300 76" role="img" aria-label="x -&amp;gt; y: p-value 0.029; y -&amp;gt; x: p-value 0"&gt;&lt;text x="44" y="22" text-anchor="end"&gt;x -&amp;gt; y&lt;/text&gt;&lt;rect class="pp-bar is-chosen" x="52" y="12" width="5.8" height="14" rx="2"/&gt;&lt;text class="pp-value" x="67.9" y="22"&gt;0.029&lt;/text&gt;&lt;text x="44" y="48" text-anchor="end"&gt;y -&amp;gt; x&lt;/text&gt;&lt;rect class="pp-bar" x="52" y="38" width="1.5" height="14" rx="2"/&gt;&lt;text class="pp-value" x="67.9" y="48"&gt;0&lt;/text&gt;&lt;line class="pp-cut" x1="61.9" x2="61.9" y1="4" y2="60"/&gt;&lt;text x="61.9" y="72" text-anchor="middle"&gt;0.05&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="pp-test"&gt;&lt;p class="pp-name"&gt;&lt;strong&gt;PNL&lt;/strong&gt;&lt;span&gt;names x -&amp;gt; y, the true direction&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 300 76" role="img" aria-label="x -&amp;gt; y: p-value 0.072; y -&amp;gt; x: p-value 2.4e-09"&gt;&lt;text x="44" y="22" text-anchor="end"&gt;x -&amp;gt; y&lt;/text&gt;&lt;rect class="pp-bar is-chosen" x="52" y="12" width="14.3" height="14" rx="2"/&gt;&lt;text class="pp-value" x="72.3" y="22"&gt;0.072&lt;/text&gt;&lt;text x="44" y="48" text-anchor="end"&gt;y -&amp;gt; x&lt;/text&gt;&lt;rect class="pp-bar" x="52" y="38" width="1.5" height="14" rx="2"/&gt;&lt;text class="pp-value" x="67.9" y="48"&gt;2.4e-09&lt;/text&gt;&lt;line class="pp-cut" x1="61.9" x2="61.9" y1="4" y2="60"/&gt;&lt;text x="61.9" y="72" text-anchor="middle"&gt;0.05&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="pp-grid"&gt;&lt;figure class="pp-panel"&gt;&lt;figcaption&gt;&lt;strong&gt;The saturating pair&lt;/strong&gt;&lt;span&gt;500 rows of y = tanh(x + 0.5 * e)&lt;/span&gt;&lt;/figcaption&gt;&lt;svg viewBox="0 0 300 230" role="img" aria-label="Scatter plot of the saturating pair&amp;#x27;s 500 rows: y rises with x and flattens toward -1 and 1 as the sensor saturates."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="34" x2="290" y1="196.0" y2="196.0" class="tc-grid"/&gt;&lt;text x="28" y="199.0" text-anchor="end"&gt;-1&lt;/text&gt;&lt;line x1="34" x2="290" y1="104.0" y2="104.0" class="tc-grid"/&gt;&lt;text x="28" y="107.0" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="34" x2="290" y1="12.0" y2="12.0" class="tc-grid"/&gt;&lt;text x="28" y="15.0" text-anchor="end"&gt;1&lt;/text&gt;&lt;text x="34.0" y="210" text-anchor="middle"&gt;-8&lt;/text&gt;&lt;text x="70.6" y="210" text-anchor="middle"&gt;-6&lt;/text&gt;&lt;text x="107.1" y="210" text-anchor="middle"&gt;-4&lt;/text&gt;&lt;text x="143.7" y="210" text-anchor="middle"&gt;-2&lt;/text&gt;&lt;text x="180.3" y="210" text-anchor="middle"&gt;0&lt;/text&gt;&lt;text x="216.9" y="210" text-anchor="middle"&gt;2&lt;/text&gt;&lt;text x="253.4" y="210" text-anchor="middle"&gt;4&lt;/text&gt;&lt;text x="290.0" y="210" text-anchor="middle"&gt;6&lt;/text&gt;&lt;/g&gt;&lt;g class="pp-dots"&gt;&lt;circle cx="186.1" cy="152.6" r="1.7"/&gt;&lt;circle cx="169.0" cy="96.9" r="1.7"/&gt;&lt;circle cx="134.5" cy="186.0" r="1.7"/&gt;&lt;circle cx="117.9" cy="195.9" r="1.7"/&gt;&lt;circle cx="198.3" cy="168.8" r="1.7"/&gt;&lt;circle cx="212.2" cy="17.6" r="1.7"/&gt;&lt;circle cx="184.7" cy="127.7" r="1.7"/&gt;&lt;circle cx="191.5" cy="15.0" r="1.7"/&gt;&lt;circle cx="182.0" cy="38.8" r="1.7"/&gt;&lt;circle cx="217.6" cy="12.2" r="1.7"/&gt;&lt;circle cx="198.6" cy="29.9" r="1.7"/&gt;&lt;circle cx="85.1" cy="196.0" r="1.7"/&gt;&lt;circle cx="203.2" cy="16.9" r="1.7"/&gt;&lt;circle cx="130.9" cy="194.8" r="1.7"/&gt;&lt;circle cx="191.5" cy="80.1" r="1.7"/&gt;&lt;circle cx="161.2" cy="106.9" r="1.7"/&gt;&lt;circle cx="204.0" cy="26.4" r="1.7"/&gt;&lt;circle cx="181.9" cy="62.8" r="1.7"/&gt;&lt;circle cx="170.9" cy="147.3" r="1.7"/&gt;&lt;circle cx="177.2" cy="157.8" r="1.7"/&gt;&lt;circle cx="127.8" cy="195.7" r="1.7"/&gt;&lt;circle cx="154.8" cy="183.5" r="1.7"/&gt;&lt;circle cx="187.9" cy="122.9" r="1.7"/&gt;&lt;circle cx="186.7" cy="186.4" r="1.7"/&gt;&lt;circle cx="185.1" cy="86.6" r="1.7"/&gt;&lt;circle cx="175.4" cy="94.2" r="1.7"/&gt;&lt;circle cx="275.2" cy="12.0" r="1.7"/&gt;&lt;circle cx="239.9" cy="12.4" r="1.7"/&gt;&lt;circle cx="188.8" cy="45.6" r="1.7"/&gt;&lt;circle cx="186.8" cy="73.7" r="1.7"/&gt;&lt;circle cx="188.9" cy="12.1" r="1.7"/&gt;&lt;circle cx="175.7" cy="90.3" r="1.7"/&gt;&lt;circle cx="156.4" cy="163.4" r="1.7"/&gt;&lt;circle cx="191.0" cy="80.8" r="1.7"/&gt;&lt;circle cx="181.2" cy="149.5" r="1.7"/&gt;&lt;circle cx="171.6" cy="171.5" r="1.7"/&gt;&lt;circle cx="179.8" cy="113.3" r="1.7"/&gt;&lt;circle cx="207.9" cy="20.4" r="1.7"/&gt;&lt;circle cx="217.3" cy="19.9" r="1.7"/&gt;&lt;circle cx="174.2" cy="97.0" r="1.7"/&gt;&lt;circle cx="183.1" cy="41.8" r="1.7"/&gt;&lt;circle cx="172.2" cy="157.8" r="1.7"/&gt;&lt;circle cx="184.1" cy="84.3" r="1.7"/&gt;&lt;circle cx="173.1" cy="129.7" r="1.7"/&gt;&lt;circle cx="175.8" cy="99.4" r="1.7"/&gt;&lt;circle cx="208.0" cy="37.8" r="1.7"/&gt;&lt;circle cx="165.9" cy="176.9" r="1.7"/&gt;&lt;circle cx="185.5" cy="55.1" r="1.7"/&gt;&lt;circle cx="147.7" cy="190.2" r="1.7"/&gt;&lt;circle cx="200.3" cy="16.0" r="1.7"/&gt;&lt;circle cx="195.9" cy="43.0" r="1.7"/&gt;&lt;circle cx="166.8" cy="167.1" r="1.7"/&gt;&lt;circle cx="205.9" cy="28.7" r="1.7"/&gt;&lt;circle cx="141.1" cy="194.8" r="1.7"/&gt;&lt;circle cx="173.0" cy="123.7" r="1.7"/&gt;&lt;circle cx="158.3" cy="187.6" r="1.7"/&gt;&lt;circle cx="178.4" cy="58.6" r="1.7"/&gt;&lt;circle cx="196.7" cy="55.2" r="1.7"/&gt;&lt;circle cx="166.1" cy="150.8" r="1.7"/&gt;&lt;circle cx="138.9" cy="193.7" r="1.7"/&gt;&lt;circle cx="176.4" cy="109.8" r="1.7"/&gt;&lt;circle cx="163.4" cy="116.9" r="1.7"/&gt;&lt;circle cx="149.1" cy="194.0" r="1.7"/&gt;&lt;circle cx="183.5" cy="141.1" r="1.7"/&gt;&lt;circle cx="170.9" cy="181.3" r="1.7"/&gt;&lt;circle cx="188.0" cy="147.8" r="1.7"/&gt;&lt;circle cx="163.5" cy="169.1" r="1.7"/&gt;&lt;circle cx="219.7" cy="19.2" r="1.7"/&gt;&lt;circle cx="174.5" cy="89.2" r="1.7"/&gt;&lt;circle cx="151.8" cy="195.6" r="1.7"/&gt;&lt;circle cx="185.7" cy="89.9" r="1.7"/&gt;&lt;circle cx="215.5" cy="16.0" r="1.7"/&gt;&lt;circle cx="178.0" cy="149.7" r="1.7"/&gt;&lt;circle cx="224.2" cy="14.1" r="1.7"/&gt;&lt;circle cx="180.3" cy="139.3" r="1.7"/&gt;&lt;circle cx="177.3" cy="142.7" r="1.7"/&gt;&lt;circle cx="185.3" cy="26.3" r="1.7"/&gt;&lt;circle cx="264.9" cy="12.0" r="1.7"/&gt;&lt;circle cx="222.0" cy="14.5" r="1.7"/&gt;&lt;circle cx="178.8" cy="100.1" r="1.7"/&gt;&lt;circle cx="193.5" cy="37.1" r="1.7"/&gt;&lt;circle cx="180.2" cy="86.9" r="1.7"/&gt;&lt;circle cx="181.4" cy="166.5" r="1.7"/&gt;&lt;circle cx="195.8" cy="36.5" r="1.7"/&gt;&lt;circle cx="176.9" cy="91.9" r="1.7"/&gt;&lt;circle cx="191.9" cy="31.0" r="1.7"/&gt;&lt;circle cx="190.3" cy="51.6" r="1.7"/&gt;&lt;circle cx="216.8" cy="24.2" r="1.7"/&gt;&lt;circle cx="153.4" cy="194.4" r="1.7"/&gt;&lt;circle cx="191.5" cy="69.5" r="1.7"/&gt;&lt;circle cx="215.6" cy="12.6" r="1.7"/&gt;&lt;circle cx="230.5" cy="12.8" r="1.7"/&gt;&lt;circle cx="115.8" cy="195.2" r="1.7"/&gt;&lt;circle cx="204.0" cy="25.8" r="1.7"/&gt;&lt;circle cx="240.3" cy="12.1" r="1.7"/&gt;&lt;circle cx="225.2" cy="13.4" r="1.7"/&gt;&lt;circle cx="158.1" cy="171.7" r="1.7"/&gt;&lt;circle cx="233.4" cy="12.9" r="1.7"/&gt;&lt;circle cx="208.0" cy="13.9" r="1.7"/&gt;&lt;circle cx="199.2" cy="47.3" r="1.7"/&gt;&lt;circle cx="179.5" cy="193.9" r="1.7"/&gt;&lt;circle cx="166.3" cy="144.6" r="1.7"/&gt;&lt;circle cx="197.2" cy="29.0" r="1.7"/&gt;&lt;circle cx="214.6" cy="14.9" r="1.7"/&gt;&lt;circle cx="168.8" cy="138.6" r="1.7"/&gt;&lt;circle cx="181.8" cy="88.0" r="1.7"/&gt;&lt;circle cx="178.1" cy="167.8" r="1.7"/&gt;&lt;circle cx="216.5" cy="14.3" r="1.7"/&gt;&lt;circle cx="134.3" cy="196.0" r="1.7"/&gt;&lt;circle cx="191.7" cy="93.0" r="1.7"/&gt;&lt;circle cx="185.0" cy="88.2" r="1.7"/&gt;&lt;circle cx="127.8" cy="195.6" r="1.7"/&gt;&lt;circle cx="190.8" cy="116.9" r="1.7"/&gt;&lt;circle cx="117.3" cy="195.8" r="1.7"/&gt;&lt;circle cx="193.6" cy="39.6" r="1.7"/&gt;&lt;circle cx="180.8" cy="114.5" r="1.7"/&gt;&lt;circle cx="216.0" cy="14.1" r="1.7"/&gt;&lt;circle cx="143.3" cy="194.5" r="1.7"/&gt;&lt;circle cx="201.3" cy="59.7" r="1.7"/&gt;&lt;circle cx="143.4" cy="189.7" r="1.7"/&gt;&lt;circle cx="173.5" cy="119.2" r="1.7"/&gt;&lt;circle cx="177.5" cy="124.5" r="1.7"/&gt;&lt;circle cx="229.5" cy="15.0" r="1.7"/&gt;&lt;circle cx="182.7" cy="74.6" r="1.7"/&gt;&lt;circle cx="168.2" cy="131.8" r="1.7"/&gt;&lt;circle cx="167.0" cy="182.9" r="1.7"/&gt;&lt;circle cx="207.7" cy="17.6" r="1.7"/&gt;&lt;circle cx="165.8" cy="172.7" r="1.7"/&gt;&lt;circle cx="154.9" cy="146.7" r="1.7"/&gt;&lt;circle cx="170.2" cy="122.2" r="1.7"/&gt;&lt;circle cx="183.7" cy="114.3" r="1.7"/&gt;&lt;circle cx="182.4" cy="28.5" r="1.7"/&gt;&lt;circle cx="198.0" cy="148.5" r="1.7"/&gt;&lt;circle cx="182.6" cy="136.5" r="1.7"/&gt;&lt;circle cx="170.2" cy="170.5" r="1.7"/&gt;&lt;circle cx="176.8" cy="93.0" r="1.7"/&gt;&lt;circle cx="198.8" cy="32.5" r="1.7"/&gt;&lt;circle cx="185.6" cy="81.1" r="1.7"/&gt;&lt;circle cx="226.1" cy="14.7" r="1.7"/&gt;&lt;circle cx="174.8" cy="98.8" r="1.7"/&gt;&lt;circle cx="182.3" cy="154.4" r="1.7"/&gt;&lt;circle cx="184.1" cy="117.1" r="1.7"/&gt;&lt;circle cx="202.1" cy="18.3" r="1.7"/&gt;&lt;circle cx="157.7" cy="182.9" r="1.7"/&gt;&lt;circle cx="176.5" cy="62.0" r="1.7"/&gt;&lt;circle cx="211.6" cy="16.6" r="1.7"/&gt;&lt;circle cx="135.5" cy="192.8" r="1.7"/&gt;&lt;circle cx="199.2" cy="58.2" r="1.7"/&gt;&lt;circle cx="176.9" cy="140.1" r="1.7"/&gt;&lt;circle cx="200.0" cy="36.5" r="1.7"/&gt;&lt;circle cx="108.7" cy="195.9" r="1.7"/&gt;&lt;circle cx="174.5" cy="134.4" r="1.7"/&gt;&lt;circle cx="146.5" cy="184.0" r="1.7"/&gt;&lt;circle cx="186.9" cy="119.1" r="1.7"/&gt;&lt;circle cx="169.3" cy="103.5" r="1.7"/&gt;&lt;circle cx="189.8" cy="25.4" r="1.7"/&gt;&lt;circle cx="220.3" cy="26.2" r="1.7"/&gt;&lt;circle cx="155.2" cy="195.8" r="1.7"/&gt;&lt;circle cx="204.2" cy="32.4" r="1.7"/&gt;&lt;circle cx="141.4" cy="193.9" r="1.7"/&gt;&lt;circle cx="175.3" cy="29.0" r="1.7"/&gt;&lt;circle cx="177.5" cy="178.4" r="1.7"/&gt;&lt;circle cx="179.9" cy="69.8" r="1.7"/&gt;&lt;circle cx="236.2" cy="12.4" r="1.7"/&gt;&lt;circle cx="194.9" cy="92.3" r="1.7"/&gt;&lt;circle cx="171.5" cy="155.8" r="1.7"/&gt;&lt;circle cx="169.0" cy="162.6" r="1.7"/&gt;&lt;circle cx="204.0" cy="36.5" r="1.7"/&gt;&lt;circle cx="206.6" cy="28.1" r="1.7"/&gt;&lt;circle cx="180.7" cy="110.0" r="1.7"/&gt;&lt;circle cx="173.5" cy="37.3" r="1.7"/&gt;&lt;circle cx="264.2" cy="12.0" r="1.7"/&gt;&lt;circle cx="171.9" cy="48.8" r="1.7"/&gt;&lt;circle cx="161.9" cy="194.5" r="1.7"/&gt;&lt;circle cx="206.4" cy="67.8" r="1.7"/&gt;&lt;circle cx="198.2" cy="143.9" r="1.7"/&gt;&lt;circle cx="187.8" cy="53.9" r="1.7"/&gt;&lt;circle cx="225.8" cy="14.8" r="1.7"/&gt;&lt;circle cx="215.2" cy="15.4" r="1.7"/&gt;&lt;circle cx="192.8" cy="29.3" r="1.7"/&gt;&lt;circle cx="203.7" cy="31.3" r="1.7"/&gt;&lt;circle cx="167.4" cy="176.3" r="1.7"/&gt;&lt;circle cx="157.2" cy="172.7" r="1.7"/&gt;&lt;circle cx="187.9" cy="69.8" r="1.7"/&gt;&lt;circle cx="190.5" cy="108.0" r="1.7"/&gt;&lt;circle cx="160.2" cy="192.5" r="1.7"/&gt;&lt;circle cx="176.0" cy="96.0" r="1.7"/&gt;&lt;circle cx="211.7" cy="13.6" r="1.7"/&gt;&lt;circle cx="182.7" cy="38.6" r="1.7"/&gt;&lt;circle cx="183.4" cy="87.6" r="1.7"/&gt;&lt;circle cx="163.0" cy="187.9" r="1.7"/&gt;&lt;circle cx="181.3" cy="134.5" r="1.7"/&gt;&lt;circle cx="181.2" cy="104.1" r="1.7"/&gt;&lt;circle cx="148.7" cy="178.2" r="1.7"/&gt;&lt;circle cx="241.0" cy="12.2" r="1.7"/&gt;&lt;circle cx="183.1" cy="117.4" r="1.7"/&gt;&lt;circle cx="100.6" cy="195.9" r="1.7"/&gt;&lt;circle cx="194.7" cy="47.3" r="1.7"/&gt;&lt;circle cx="237.6" cy="12.0" r="1.7"/&gt;&lt;circle cx="183.9" cy="92.9" r="1.7"/&gt;&lt;circle cx="172.1" cy="93.6" r="1.7"/&gt;&lt;circle cx="162.4" cy="195.3" r="1.7"/&gt;&lt;circle cx="188.0" cy="47.8" r="1.7"/&gt;&lt;circle cx="163.1" cy="176.1" r="1.7"/&gt;&lt;circle cx="183.4" cy="88.7" r="1.7"/&gt;&lt;circle cx="184.5" cy="125.0" r="1.7"/&gt;&lt;circle cx="227.6" cy="12.1" r="1.7"/&gt;&lt;circle cx="144.9" cy="194.5" r="1.7"/&gt;&lt;circle cx="180.3" cy="98.0" r="1.7"/&gt;&lt;circle cx="192.5" cy="44.1" r="1.7"/&gt;&lt;circle cx="161.3" cy="139.4" r="1.7"/&gt;&lt;circle cx="175.7" cy="129.8" r="1.7"/&gt;&lt;circle cx="142.4" cy="187.8" r="1.7"/&gt;&lt;circle cx="191.3" cy="52.9" r="1.7"/&gt;&lt;circle cx="148.5" cy="148.0" r="1.7"/&gt;&lt;circle cx="176.0" cy="99.6" r="1.7"/&gt;&lt;circle cx="205.4" cy="14.0" r="1.7"/&gt;&lt;circle cx="179.2" cy="33.1" r="1.7"/&gt;&lt;circle cx="212.2" cy="14.2" r="1.7"/&gt;&lt;circle cx="194.2" cy="98.9" r="1.7"/&gt;&lt;circle cx="212.8" cy="31.0" r="1.7"/&gt;&lt;circle cx="155.3" cy="181.6" r="1.7"/&gt;&lt;circle cx="145.2" cy="193.9" r="1.7"/&gt;&lt;circle cx="144.4" cy="193.2" r="1.7"/&gt;&lt;circle cx="204.8" cy="42.4" r="1.7"/&gt;&lt;circle cx="186.0" cy="49.4" r="1.7"/&gt;&lt;circle cx="180.2" cy="53.9" r="1.7"/&gt;&lt;circle cx="159.9" cy="190.9" r="1.7"/&gt;&lt;circle cx="188.1" cy="65.6" r="1.7"/&gt;&lt;circle cx="172.0" cy="151.4" r="1.7"/&gt;&lt;circle cx="190.3" cy="34.7" r="1.7"/&gt;&lt;circle cx="178.8" cy="114.9" r="1.7"/&gt;&lt;circle cx="180.6" cy="75.1" r="1.7"/&gt;&lt;circle cx="196.1" cy="36.5" r="1.7"/&gt;&lt;circle cx="149.5" cy="114.7" r="1.7"/&gt;&lt;circle cx="183.4" cy="96.3" r="1.7"/&gt;&lt;circle cx="163.3" cy="37.2" r="1.7"/&gt;&lt;circle cx="197.8" cy="39.7" r="1.7"/&gt;&lt;circle cx="179.9" cy="148.4" r="1.7"/&gt;&lt;circle cx="249.6" cy="12.0" r="1.7"/&gt;&lt;circle cx="161.9" cy="183.5" r="1.7"/&gt;&lt;circle cx="227.9" cy="12.9" r="1.7"/&gt;&lt;circle cx="197.1" cy="30.6" r="1.7"/&gt;&lt;circle cx="179.6" cy="146.3" r="1.7"/&gt;&lt;circle cx="198.3" cy="135.1" r="1.7"/&gt;&lt;circle cx="184.5" cy="15.0" r="1.7"/&gt;&lt;circle cx="187.1" cy="39.2" r="1.7"/&gt;&lt;circle cx="212.4" cy="31.8" r="1.7"/&gt;&lt;circle cx="143.1" cy="186.2" r="1.7"/&gt;&lt;circle cx="200.6" cy="44.1" r="1.7"/&gt;&lt;circle cx="175.4" cy="154.8" r="1.7"/&gt;&lt;circle cx="172.4" cy="107.2" r="1.7"/&gt;&lt;circle cx="261.2" cy="12.0" r="1.7"/&gt;&lt;circle cx="195.4" cy="22.3" r="1.7"/&gt;&lt;circle cx="179.7" cy="158.3" r="1.7"/&gt;&lt;circle cx="177.2" cy="88.2" r="1.7"/&gt;&lt;circle cx="206.0" cy="23.0" r="1.7"/&gt;&lt;circle cx="148.3" cy="192.5" r="1.7"/&gt;&lt;circle cx="190.1" cy="40.2" r="1.7"/&gt;&lt;circle cx="196.1" cy="21.6" r="1.7"/&gt;&lt;circle cx="197.0" cy="48.8" r="1.7"/&gt;&lt;circle cx="172.3" cy="175.0" r="1.7"/&gt;&lt;circle cx="196.7" cy="12.5" r="1.7"/&gt;&lt;circle cx="165.7" cy="93.5" r="1.7"/&gt;&lt;circle cx="174.4" cy="153.1" r="1.7"/&gt;&lt;circle cx="177.0" cy="75.9" r="1.7"/&gt;&lt;circle cx="181.9" cy="163.7" r="1.7"/&gt;&lt;circle cx="153.0" cy="154.6" r="1.7"/&gt;&lt;circle cx="176.5" cy="86.5" r="1.7"/&gt;&lt;circle cx="44.7" cy="196.0" r="1.7"/&gt;&lt;circle cx="192.6" cy="68.4" r="1.7"/&gt;&lt;circle cx="202.5" cy="24.2" r="1.7"/&gt;&lt;circle cx="156.9" cy="186.4" r="1.7"/&gt;&lt;circle cx="189.9" cy="40.7" r="1.7"/&gt;&lt;circle cx="199.1" cy="29.4" r="1.7"/&gt;&lt;circle cx="240.9" cy="12.6" r="1.7"/&gt;&lt;circle cx="201.6" cy="25.0" r="1.7"/&gt;&lt;circle cx="177.3" cy="192.4" r="1.7"/&gt;&lt;circle cx="238.9" cy="13.3" r="1.7"/&gt;&lt;circle cx="234.3" cy="13.5" r="1.7"/&gt;&lt;circle cx="180.4" cy="119.1" r="1.7"/&gt;&lt;circle cx="193.2" cy="179.6" r="1.7"/&gt;&lt;circle cx="212.4" cy="12.8" r="1.7"/&gt;&lt;circle cx="179.4" cy="140.3" r="1.7"/&gt;&lt;circle cx="204.1" cy="34.2" r="1.7"/&gt;&lt;circle cx="189.7" cy="71.2" r="1.7"/&gt;&lt;circle cx="170.6" cy="59.0" r="1.7"/&gt;&lt;circle cx="194.3" cy="55.2" r="1.7"/&gt;&lt;circle cx="183.1" cy="96.8" r="1.7"/&gt;&lt;circle cx="149.7" cy="192.2" r="1.7"/&gt;&lt;circle cx="175.8" cy="25.2" r="1.7"/&gt;&lt;circle cx="145.3" cy="193.1" r="1.7"/&gt;&lt;circle cx="179.4" cy="171.8" r="1.7"/&gt;&lt;circle cx="177.5" cy="100.7" r="1.7"/&gt;&lt;circle cx="177.3" cy="93.2" r="1.7"/&gt;&lt;circle cx="183.8" cy="100.1" r="1.7"/&gt;&lt;circle cx="154.6" cy="191.4" r="1.7"/&gt;&lt;circle cx="217.2" cy="15.9" r="1.7"/&gt;&lt;circle cx="188.7" cy="69.5" r="1.7"/&gt;&lt;circle cx="199.4" cy="12.2" r="1.7"/&gt;&lt;circle cx="209.1" cy="14.2" r="1.7"/&gt;&lt;circle cx="183.6" cy="75.5" r="1.7"/&gt;&lt;circle cx="134.2" cy="195.5" r="1.7"/&gt;&lt;circle cx="190.3" cy="43.7" r="1.7"/&gt;&lt;circle cx="183.0" cy="60.5" r="1.7"/&gt;&lt;circle cx="199.6" cy="94.0" r="1.7"/&gt;&lt;circle cx="181.5" cy="110.6" r="1.7"/&gt;&lt;circle cx="198.3" cy="44.4" r="1.7"/&gt;&lt;circle cx="273.9" cy="12.0" r="1.7"/&gt;&lt;circle cx="173.8" cy="119.7" r="1.7"/&gt;&lt;circle cx="160.7" cy="188.5" r="1.7"/&gt;&lt;circle cx="175.8" cy="128.7" r="1.7"/&gt;&lt;circle cx="193.2" cy="12.7" r="1.7"/&gt;&lt;circle cx="177.9" cy="191.6" r="1.7"/&gt;&lt;circle cx="183.8" cy="99.8" r="1.7"/&gt;&lt;circle cx="155.3" cy="188.3" r="1.7"/&gt;&lt;circle cx="191.3" cy="61.7" r="1.7"/&gt;&lt;circle cx="169.7" cy="95.7" r="1.7"/&gt;&lt;circle cx="162.7" cy="175.2" r="1.7"/&gt;&lt;circle cx="204.0" cy="15.2" r="1.7"/&gt;&lt;circle cx="182.8" cy="84.1" r="1.7"/&gt;&lt;circle cx="179.7" cy="114.4" r="1.7"/&gt;&lt;circle cx="209.5" cy="25.8" r="1.7"/&gt;&lt;circle cx="148.1" cy="181.8" r="1.7"/&gt;&lt;circle cx="189.4" cy="50.3" r="1.7"/&gt;&lt;circle cx="172.6" cy="167.6" r="1.7"/&gt;&lt;circle cx="161.1" cy="190.3" r="1.7"/&gt;&lt;circle cx="188.2" cy="127.5" r="1.7"/&gt;&lt;circle cx="174.4" cy="59.2" r="1.7"/&gt;&lt;circle cx="172.7" cy="148.2" r="1.7"/&gt;&lt;circle cx="220.2" cy="12.8" r="1.7"/&gt;&lt;circle cx="163.5" cy="116.6" r="1.7"/&gt;&lt;circle cx="180.7" cy="136.7" r="1.7"/&gt;&lt;circle cx="124.8" cy="196.0" r="1.7"/&gt;&lt;circle cx="159.8" cy="189.2" r="1.7"/&gt;&lt;circle cx="206.9" cy="16.4" r="1.7"/&gt;&lt;circle cx="196.1" cy="168.8" r="1.7"/&gt;&lt;circle cx="182.5" cy="86.8" r="1.7"/&gt;&lt;circle cx="165.5" cy="183.0" r="1.7"/&gt;&lt;circle cx="182.5" cy="61.2" r="1.7"/&gt;&lt;circle cx="112.3" cy="195.9" r="1.7"/&gt;&lt;circle cx="190.4" cy="54.2" r="1.7"/&gt;&lt;circle cx="190.7" cy="77.1" r="1.7"/&gt;&lt;circle cx="186.6" cy="77.2" r="1.7"/&gt;&lt;circle cx="184.9" cy="171.8" r="1.7"/&gt;&lt;circle cx="145.3" cy="186.1" r="1.7"/&gt;&lt;circle cx="167.3" cy="91.6" r="1.7"/&gt;&lt;circle cx="183.2" cy="60.6" r="1.7"/&gt;&lt;circle cx="175.9" cy="125.9" r="1.7"/&gt;&lt;circle cx="256.0" cy="12.0" r="1.7"/&gt;&lt;circle cx="214.7" cy="150.5" r="1.7"/&gt;&lt;circle cx="158.5" cy="173.6" r="1.7"/&gt;&lt;circle cx="183.9" cy="55.9" r="1.7"/&gt;&lt;circle cx="189.4" cy="78.4" r="1.7"/&gt;&lt;circle cx="156.6" cy="158.0" r="1.7"/&gt;&lt;circle cx="171.7" cy="196.0" r="1.7"/&gt;&lt;circle cx="190.6" cy="47.3" r="1.7"/&gt;&lt;circle cx="209.9" cy="23.2" r="1.7"/&gt;&lt;circle cx="173.3" cy="125.0" r="1.7"/&gt;&lt;circle cx="166.8" cy="177.1" r="1.7"/&gt;&lt;circle cx="199.2" cy="54.8" r="1.7"/&gt;&lt;circle cx="183.7" cy="74.6" r="1.7"/&gt;&lt;circle cx="179.4" cy="108.7" r="1.7"/&gt;&lt;circle cx="168.1" cy="179.5" r="1.7"/&gt;&lt;circle cx="145.0" cy="180.4" r="1.7"/&gt;&lt;circle cx="119.4" cy="195.0" r="1.7"/&gt;&lt;circle cx="183.5" cy="83.5" r="1.7"/&gt;&lt;circle cx="162.7" cy="159.4" r="1.7"/&gt;&lt;circle cx="235.5" cy="12.5" r="1.7"/&gt;&lt;circle cx="152.2" cy="176.8" r="1.7"/&gt;&lt;circle cx="178.4" cy="65.2" r="1.7"/&gt;&lt;circle cx="176.0" cy="91.3" r="1.7"/&gt;&lt;circle cx="166.3" cy="178.5" r="1.7"/&gt;&lt;circle cx="192.9" cy="166.1" r="1.7"/&gt;&lt;circle cx="186.5" cy="59.4" r="1.7"/&gt;&lt;circle cx="191.3" cy="61.3" r="1.7"/&gt;&lt;circle cx="147.4" cy="174.9" r="1.7"/&gt;&lt;circle cx="173.9" cy="78.3" r="1.7"/&gt;&lt;circle cx="181.0" cy="97.1" r="1.7"/&gt;&lt;circle cx="177.4" cy="130.7" r="1.7"/&gt;&lt;circle cx="134.4" cy="193.9" r="1.7"/&gt;&lt;circle cx="163.0" cy="158.1" r="1.7"/&gt;&lt;circle cx="220.7" cy="13.4" r="1.7"/&gt;&lt;circle cx="159.7" cy="149.8" r="1.7"/&gt;&lt;circle cx="202.6" cy="24.7" r="1.7"/&gt;&lt;circle cx="199.2" cy="32.1" r="1.7"/&gt;&lt;circle cx="175.8" cy="124.6" r="1.7"/&gt;&lt;circle cx="179.0" cy="49.4" r="1.7"/&gt;&lt;circle cx="199.4" cy="38.6" r="1.7"/&gt;&lt;circle cx="188.5" cy="33.0" r="1.7"/&gt;&lt;circle cx="200.8" cy="29.5" r="1.7"/&gt;&lt;circle cx="193.5" cy="118.6" r="1.7"/&gt;&lt;circle cx="189.1" cy="66.8" r="1.7"/&gt;&lt;circle cx="212.3" cy="21.1" r="1.7"/&gt;&lt;circle cx="199.3" cy="31.5" r="1.7"/&gt;&lt;circle cx="161.5" cy="140.0" r="1.7"/&gt;&lt;circle cx="192.8" cy="88.2" r="1.7"/&gt;&lt;circle cx="148.2" cy="190.9" r="1.7"/&gt;&lt;circle cx="177.4" cy="110.5" r="1.7"/&gt;&lt;circle cx="176.1" cy="89.7" r="1.7"/&gt;&lt;circle cx="163.7" cy="89.2" r="1.7"/&gt;&lt;circle cx="218.4" cy="14.5" r="1.7"/&gt;&lt;circle cx="149.9" cy="161.7" r="1.7"/&gt;&lt;circle cx="95.7" cy="196.0" r="1.7"/&gt;&lt;circle cx="172.3" cy="109.6" r="1.7"/&gt;&lt;circle cx="253.2" cy="12.0" r="1.7"/&gt;&lt;circle cx="168.7" cy="152.2" r="1.7"/&gt;&lt;circle cx="200.1" cy="57.6" r="1.7"/&gt;&lt;circle cx="160.9" cy="180.4" r="1.7"/&gt;&lt;circle cx="183.8" cy="111.5" r="1.7"/&gt;&lt;circle cx="225.8" cy="12.1" r="1.7"/&gt;&lt;circle cx="190.7" cy="46.2" r="1.7"/&gt;&lt;circle cx="239.6" cy="12.1" r="1.7"/&gt;&lt;circle cx="183.2" cy="113.2" r="1.7"/&gt;&lt;circle cx="242.5" cy="12.2" r="1.7"/&gt;&lt;circle cx="200.8" cy="27.5" r="1.7"/&gt;&lt;circle cx="195.2" cy="51.9" r="1.7"/&gt;&lt;circle cx="207.7" cy="21.5" r="1.7"/&gt;&lt;circle cx="185.9" cy="67.8" r="1.7"/&gt;&lt;circle cx="174.1" cy="190.9" r="1.7"/&gt;&lt;circle cx="181.4" cy="116.3" r="1.7"/&gt;&lt;circle cx="165.8" cy="196.0" r="1.7"/&gt;&lt;circle cx="195.1" cy="43.3" r="1.7"/&gt;&lt;circle cx="160.6" cy="172.3" r="1.7"/&gt;&lt;circle cx="183.4" cy="158.0" r="1.7"/&gt;&lt;circle cx="181.6" cy="129.8" r="1.7"/&gt;&lt;circle cx="188.0" cy="38.6" r="1.7"/&gt;&lt;circle cx="193.7" cy="24.6" r="1.7"/&gt;&lt;circle cx="152.6" cy="189.4" r="1.7"/&gt;&lt;circle cx="185.5" cy="43.8" r="1.7"/&gt;&lt;circle cx="176.8" cy="145.3" r="1.7"/&gt;&lt;circle cx="185.0" cy="59.7" r="1.7"/&gt;&lt;circle cx="189.3" cy="58.7" r="1.7"/&gt;&lt;circle cx="183.7" cy="92.7" r="1.7"/&gt;&lt;circle cx="191.7" cy="42.2" r="1.7"/&gt;&lt;circle cx="181.0" cy="190.8" r="1.7"/&gt;&lt;circle cx="178.9" cy="155.9" r="1.7"/&gt;&lt;circle cx="170.1" cy="166.2" r="1.7"/&gt;&lt;circle cx="166.0" cy="150.5" r="1.7"/&gt;&lt;circle cx="189.3" cy="45.0" r="1.7"/&gt;&lt;circle cx="189.4" cy="47.2" r="1.7"/&gt;&lt;circle cx="163.1" cy="150.5" r="1.7"/&gt;&lt;circle cx="232.9" cy="15.6" r="1.7"/&gt;&lt;circle cx="187.9" cy="135.5" r="1.7"/&gt;&lt;circle cx="181.5" cy="49.3" r="1.7"/&gt;&lt;circle cx="201.3" cy="34.8" r="1.7"/&gt;&lt;circle cx="179.8" cy="99.8" r="1.7"/&gt;&lt;circle cx="179.4" cy="108.2" r="1.7"/&gt;&lt;circle cx="168.2" cy="147.6" r="1.7"/&gt;&lt;circle cx="159.0" cy="193.3" r="1.7"/&gt;&lt;circle cx="190.3" cy="36.4" r="1.7"/&gt;&lt;circle cx="201.6" cy="64.0" r="1.7"/&gt;&lt;circle cx="188.3" cy="50.5" r="1.7"/&gt;&lt;circle cx="174.7" cy="140.0" r="1.7"/&gt;&lt;circle cx="183.3" cy="89.7" r="1.7"/&gt;&lt;circle cx="182.8" cy="73.4" r="1.7"/&gt;&lt;circle cx="218.0" cy="15.9" r="1.7"/&gt;&lt;circle cx="175.6" cy="186.3" r="1.7"/&gt;&lt;circle cx="160.0" cy="85.1" r="1.7"/&gt;&lt;circle cx="205.9" cy="22.1" r="1.7"/&gt;&lt;circle cx="208.8" cy="16.1" r="1.7"/&gt;&lt;circle cx="137.5" cy="194.2" r="1.7"/&gt;&lt;circle cx="163.4" cy="140.1" r="1.7"/&gt;&lt;circle cx="186.1" cy="70.0" r="1.7"/&gt;&lt;circle cx="196.0" cy="89.4" r="1.7"/&gt;&lt;circle cx="184.7" cy="68.5" r="1.7"/&gt;&lt;circle cx="162.7" cy="187.1" r="1.7"/&gt;&lt;circle cx="153.8" cy="171.0" r="1.7"/&gt;&lt;circle cx="180.5" cy="82.6" r="1.7"/&gt;&lt;circle cx="198.5" cy="15.0" r="1.7"/&gt;&lt;circle cx="165.0" cy="158.7" r="1.7"/&gt;&lt;circle cx="145.6" cy="194.1" r="1.7"/&gt;&lt;circle cx="182.3" cy="88.7" r="1.7"/&gt;&lt;circle cx="162.8" cy="151.1" r="1.7"/&gt;&lt;circle cx="143.6" cy="190.4" r="1.7"/&gt;&lt;circle cx="194.7" cy="54.5" r="1.7"/&gt;&lt;circle cx="199.1" cy="26.9" r="1.7"/&gt;&lt;circle cx="176.1" cy="43.2" r="1.7"/&gt;&lt;circle cx="170.6" cy="130.8" r="1.7"/&gt;&lt;circle cx="169.5" cy="162.1" r="1.7"/&gt;&lt;circle cx="174.3" cy="58.9" r="1.7"/&gt;&lt;circle cx="183.3" cy="48.4" r="1.7"/&gt;&lt;circle cx="181.3" cy="49.8" r="1.7"/&gt;&lt;circle cx="174.0" cy="178.1" r="1.7"/&gt;&lt;circle cx="186.2" cy="99.4" r="1.7"/&gt;&lt;circle cx="188.2" cy="39.6" r="1.7"/&gt;&lt;circle cx="182.6" cy="120.0" r="1.7"/&gt;&lt;circle cx="175.6" cy="181.0" r="1.7"/&gt;&lt;circle cx="185.5" cy="60.5" r="1.7"/&gt;&lt;circle cx="184.0" cy="50.8" r="1.7"/&gt;&lt;circle cx="173.3" cy="123.7" r="1.7"/&gt;&lt;circle cx="171.1" cy="158.8" r="1.7"/&gt;&lt;circle cx="182.0" cy="89.4" r="1.7"/&gt;&lt;circle cx="184.9" cy="183.7" r="1.7"/&gt;&lt;circle cx="184.9" cy="75.7" r="1.7"/&gt;&lt;circle cx="175.4" cy="72.4" r="1.7"/&gt;&lt;circle cx="182.9" cy="164.4" r="1.7"/&gt;&lt;circle cx="245.4" cy="12.2" r="1.7"/&gt;&lt;circle cx="177.4" cy="160.2" r="1.7"/&gt;&lt;circle cx="201.5" cy="33.1" r="1.7"/&gt;&lt;/g&gt;&lt;text class="tc-title" x="290" y="226" text-anchor="end"&gt;x&lt;/text&gt;&lt;text class="tc-title" x="8" y="107.0" text-anchor="middle"&gt;y&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;div class="pp-tests"&gt;&lt;div class="pp-test"&gt;&lt;p class="pp-name"&gt;&lt;strong&gt;ANM&lt;/strong&gt;&lt;span&gt;names y -&amp;gt; x, reversed&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 300 76" role="img" aria-label="x -&amp;gt; y: p-value 0; y -&amp;gt; x: p-value 1.7e-07"&gt;&lt;text x="44" y="22" text-anchor="end"&gt;x -&amp;gt; y&lt;/text&gt;&lt;rect class="pp-bar" x="52" y="12" width="1.5" height="14" rx="2"/&gt;&lt;text class="pp-value" x="67.9" y="22"&gt;0&lt;/text&gt;&lt;text x="44" y="48" text-anchor="end"&gt;y -&amp;gt; x&lt;/text&gt;&lt;rect class="pp-bar is-chosen" x="52" y="38" width="1.5" height="14" rx="2"/&gt;&lt;text class="pp-value" x="67.9" y="48"&gt;1.7e-07&lt;/text&gt;&lt;line class="pp-cut" x1="61.9" x2="61.9" y1="4" y2="60"/&gt;&lt;text x="61.9" y="72" text-anchor="middle"&gt;0.05&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="pp-test"&gt;&lt;p class="pp-name"&gt;&lt;strong&gt;PNL&lt;/strong&gt;&lt;span&gt;names x -&amp;gt; y, the true direction&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 300 76" role="img" aria-label="x -&amp;gt; y: p-value 0.586; y -&amp;gt; x: p-value 2.1e-06"&gt;&lt;text x="44" y="22" text-anchor="end"&gt;x -&amp;gt; y&lt;/text&gt;&lt;rect class="pp-bar is-chosen" x="52" y="12" width="116.1" height="14" rx="2"/&gt;&lt;text class="pp-value" x="174.1" y="22"&gt;0.586&lt;/text&gt;&lt;text x="44" y="48" text-anchor="end"&gt;y -&amp;gt; x&lt;/text&gt;&lt;rect class="pp-bar" x="52" y="38" width="1.5" height="14" rx="2"/&gt;&lt;text class="pp-value" x="67.9" y="48"&gt;2.1e-06&lt;/text&gt;&lt;line class="pp-cut" x1="61.9" x2="61.9" y1="4" y2="60"/&gt;&lt;text x="61.9" y="72" text-anchor="middle"&gt;0.05&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption class="pp-note"&gt;Each bar is one direction&amp;#x27;s p-value, on a scale from 0 to 1; the cobalt bar is the direction the test names, and the dashed line marks 0.05. On the additive pair, both tests name x -&amp;gt; y and reject y -&amp;gt; x. On the saturating pair, ANM rejects both directions, since the saturation is not additive noise, and names the one with the larger p-value; PNL keeps only x -&amp;gt; y.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;On the additive pair, ANM and PNL both select the true direction, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;, and reject &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On the saturating pair, the distortion breaks ANM, and PNL models it:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;ANM rejects both directions. Both p-values are near zero because the saturation is not additive
noise. Its default rule still selects a direction, here the reversed one, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;. With
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;alpha=0.05&lt;/span&gt;&lt;/code&gt;, ANM draws no edge (the last result below).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PNL models the saturation as its outer transform. Its p-value for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; is large.
Its p-value for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; is near zero, so it selects the true direction.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The script prints each result in full:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Additive pair

ANM  dag  |  2 nodes  |  1 edges (1 directed)

  x -&amp;gt; y

  pval_forward: 0.02928
  pval_backward: 0
  stat_forward: 1.725
  stat_backward: 11.33

PNL  dag  |  2 nodes  |  1 edges (1 directed)

  x -&amp;gt; y

  pval_forward: 0.07243
  pval_backward: 2.379e-09
  stat_forward: 1.525
  stat_backward: 4.287

Saturating pair

ANM  dag  |  2 nodes  |  1 edges (1 directed)

  y -&amp;gt; x

  pval_forward: 0
  pval_backward: 1.695e-07
  stat_forward: 21.66
  stat_backward: 4.322

PNL  dag  |  2 nodes  |  1 edges (1 directed)

  x -&amp;gt; y

  pval_forward: 0.5863
  pval_backward: 2.091e-06
  stat_forward: 0.8921
  stat_backward: 3.344

ANM  dag  |  2 nodes  |  0 edges (no edges)

  pval_forward: 0
  pval_backward: 1.695e-07
  stat_forward: 21.66
  stat_backward: 4.322
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="benchmark-results"&gt;
&lt;h2&gt;Benchmark results&lt;/h2&gt;
&lt;p&gt;We compared both methods with causal-learn, a Python package that implements them, on simulated
cause-effect pairs. We tested ANM on additive-noise pairs and PNL on post-nonlinear pairs.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Compared with&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Andrey’s speedup&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Direction&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;ANM&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;causal-learn&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;about 2x&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;the same as causal-learn’s on every pair&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;PNL&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;causal-learn&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;up to 1,470x&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;true on more pairs than causal-learn’s&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/benchmarks/0.4.0.html"&gt;&lt;span class="std std-doc"&gt;0.4.0 release card&lt;/span&gt;&lt;/a&gt; reports every measurement, including the
sizes, machine, and settings.&lt;/p&gt;
&lt;style&gt;@supports selector(:has(a)) { .race040p .race-panels { display: grid; } .race040p .race-panel { grid-area: 1 / 1; visibility: hidden; } .race040p:has(#race040p-m-anm:checked) #race040p-panel-anm, .race040p:has(#race040p-m-pnl:checked) #race040p-panel-pnl { visibility: visible; } } @supports not selector(:has(a)) { .race040p .race-panel::before { content: attr(aria-label); display: block; margin-top: 1rem; font-size: 0.85rem; font-weight: 600; } }&lt;/style&gt;&lt;figure class="race race--speedup race040p" aria-labelledby="race040p-title"&gt;&lt;figcaption class="race-head"&gt;&lt;span class="race-title" id="race040p-title"&gt;Speedup over causal-learn&lt;/span&gt;&lt;a class="race-link" href="../docs/benchmarks/0.4.0.html"&gt;Release card&lt;/a&gt;&lt;/figcaption&gt;&lt;fieldset class="race-sizes"&gt;&lt;legend class="visually-hidden"&gt;Method&lt;/legend&gt;&lt;input class="race-choice" type="radio" name="race040p-method" id="race040p-m-anm" checked&gt;&lt;label for="race040p-m-anm"&gt;ANM&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race040p-method" id="race040p-m-pnl"&gt;&lt;label for="race040p-m-pnl"&gt;PNL&lt;/label&gt;&lt;/fieldset&gt;&lt;div class="race-panels"&gt;&lt;div class="race-panel" id="race040p-panel-anm" role="group" aria-label="ANM: Andrey&amp;#x27;s speedup over causal-learn at 200, 500, 1,000, 2,000, 4,000, and 8,000 rows: 2.08x, 1.88x, 2.05x, 2.38x, 2.47x, 2.39x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;200 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.809946"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.08x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;500 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.698141"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.88x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;1,000 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.793879"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.05x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;2,000 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.958951"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.38x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;4,000 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.47x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;8,000 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.963588"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.39x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race040p-panel-pnl" role="group" aria-label="PNL: Andrey&amp;#x27;s speedup over causal-learn at 200, 500, 1,000, 2,000, and 5,000 rows: 1,470x, 1,310x, 993x, 696x, 275x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;200 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1,470x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;500 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.984199"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1,310x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;1,000 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.946211"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;993x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;2,000 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.897482"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;696x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;5,000 rows&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.770157"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;275x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="race-note"&gt;Each bar is causal-learn&amp;#x27;s time divided by Andrey&amp;#x27;s on the same pair, the median over the pairs of that size, on 4 cores per fit. The bars are on a log scale, so equal steps are equal factors; each method&amp;#x27;s bars share its own scale.&lt;/p&gt;&lt;/figure&gt;
&lt;p&gt;Our ANM implementation fits the same Gaussian process and uses the same independence test as
causal-learn, so both return the same graph on every pair.&lt;/p&gt;
&lt;p&gt;The PNL implementations differ more in how they fit and test each direction. Ours selects the true
direction on 94% of pairs; causal-learn’s selects it on 45%.&lt;/p&gt;
&lt;style&gt;.post-accuracy { margin: 2rem 0; padding: clamp(1rem, 2.5vw, 1.5rem); border: 1px solid var(--andrey-line); border-radius: 12px; background: var(--andrey-surface); container-type: inline-size; }.post-accuracy .pa-title { display: block; margin: 0 0 1rem; font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.02em; }.post-accuracy .tc-legend { margin: 0.4rem 0 0.25rem; }.post-accuracy .pa-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; }.post-accuracy .tchart { padding: 0; border: 0; border-radius: 0; background: none; }.post-accuracy .tchart-name span { display: block; }.post-accuracy .tc-key.pa-solid i { border-top-style: solid; }.post-accuracy .pa-note { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--andrey-line); color: var(--andrey-muted); font-size: 0.8rem; line-height: 1.65; text-align: start; }@container (max-width: 34rem) { .post-accuracy .pa-grid { grid-template-columns: minmax(0, 1fr); } }&lt;/style&gt;&lt;figure class="post-accuracy" aria-labelledby="pa-title"&gt;&lt;p class="pa-title" id="pa-title"&gt;Pairs with the true direction&lt;/p&gt;&lt;div class="pa-grid"&gt;&lt;figure class="tchart"&gt;&lt;figcaption&gt;&lt;span class="tchart-name"&gt;&lt;strong&gt;ANM&lt;/strong&gt;&lt;span&gt;The same direction as causal-learn on every pair&lt;/span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;p class="tc-legend"&gt;&lt;span class="tc-key tc-andrey"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Andrey and causal-learn&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 320 214" role="img" aria-label="ANM: share of pairs with the true direction by rows, Andrey and causal-learn alike. 200: 0.75; 500: 0.8; 1,000: 0.95; 2,000: 0.95; 4,000: 1; 8,000: 1."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="34" x2="290" y1="184.0" y2="184.0" class="tc-grid"/&gt;&lt;text x="28" y="187.0" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="34" x2="290" y1="142.0" y2="142.0" class="tc-grid"/&gt;&lt;text x="28" y="145.0" text-anchor="end"&gt;0.25&lt;/text&gt;&lt;line x1="34" x2="290" y1="100.0" y2="100.0" class="tc-grid"/&gt;&lt;text x="28" y="103.0" text-anchor="end"&gt;0.5&lt;/text&gt;&lt;line x1="34" x2="290" y1="58.0" y2="58.0" class="tc-grid"/&gt;&lt;text x="28" y="61.0" text-anchor="end"&gt;0.75&lt;/text&gt;&lt;line x1="34" x2="290" y1="16.0" y2="16.0" class="tc-grid"/&gt;&lt;text x="28" y="19.0" text-anchor="end"&gt;1&lt;/text&gt;&lt;text x="34.0" y="198" text-anchor="middle"&gt;200&lt;/text&gt;&lt;text x="85.2" y="198" text-anchor="middle"&gt;500&lt;/text&gt;&lt;text x="136.4" y="198" text-anchor="middle"&gt;1,000&lt;/text&gt;&lt;text x="187.6" y="198" text-anchor="middle"&gt;2,000&lt;/text&gt;&lt;text x="238.8" y="198" text-anchor="middle"&gt;4,000&lt;/text&gt;&lt;text x="290.0" y="198" text-anchor="middle"&gt;8,000&lt;/text&gt;&lt;/g&gt;&lt;text class="tc-title" x="34" y="213"&gt;rows&lt;/text&gt;&lt;polyline class="tc-line tc-andrey" points="34.0,58.0 85.2,49.6 136.4,24.4 187.6,24.4 238.8,16.0 290.0,16.0"/&gt;&lt;circle class="tc-point tc-andrey" cx="34.0" cy="58.0" r="3.5"&gt;&lt;title&gt;Andrey and causal-learn, 200 rows: 0.75&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="85.2" cy="49.6" r="3.5"&gt;&lt;title&gt;Andrey and causal-learn, 500 rows: 0.8&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="136.4" cy="24.4" r="3.5"&gt;&lt;title&gt;Andrey and causal-learn, 1,000 rows: 0.95&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="187.6" cy="24.4" r="3.5"&gt;&lt;title&gt;Andrey and causal-learn, 2,000 rows: 0.95&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="238.8" cy="16.0" r="3.5"&gt;&lt;title&gt;Andrey and causal-learn, 4,000 rows: 1&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="290.0" cy="16.0" r="3.5"&gt;&lt;title&gt;Andrey and causal-learn, 8,000 rows: 1&lt;/title&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;figure class="tchart"&gt;&lt;figcaption&gt;&lt;span class="tchart-name"&gt;&lt;strong&gt;PNL&lt;/strong&gt;&lt;span&gt;Andrey: every pair from 1,000 rows&lt;/span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;p class="tc-legend"&gt;&lt;span class="tc-key tc-andrey"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Andrey&lt;/span&gt;&lt;span class="tc-key tc-other pa-solid"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;causal-learn&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 320 214" role="img" aria-label="PNL: share of pairs with the true direction by rows, Andrey and causal-learn. 200: 0.75 and 0.5; 500: 0.95 and 0.5; 1,000: 1 and 0.5; 2,000: 1 and 0.5; 5,000: 1 and 0.25."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="34" x2="290" y1="184.0" y2="184.0" class="tc-grid"/&gt;&lt;text x="28" y="187.0" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="34" x2="290" y1="142.0" y2="142.0" class="tc-grid"/&gt;&lt;text x="28" y="145.0" text-anchor="end"&gt;0.25&lt;/text&gt;&lt;line x1="34" x2="290" y1="100.0" y2="100.0" class="tc-grid"/&gt;&lt;text x="28" y="103.0" text-anchor="end"&gt;0.5&lt;/text&gt;&lt;line x1="34" x2="290" y1="58.0" y2="58.0" class="tc-grid"/&gt;&lt;text x="28" y="61.0" text-anchor="end"&gt;0.75&lt;/text&gt;&lt;line x1="34" x2="290" y1="16.0" y2="16.0" class="tc-grid"/&gt;&lt;text x="28" y="19.0" text-anchor="end"&gt;1&lt;/text&gt;&lt;text x="34.0" y="198" text-anchor="middle"&gt;200&lt;/text&gt;&lt;text x="98.0" y="198" text-anchor="middle"&gt;500&lt;/text&gt;&lt;text x="162.0" y="198" text-anchor="middle"&gt;1,000&lt;/text&gt;&lt;text x="226.0" y="198" text-anchor="middle"&gt;2,000&lt;/text&gt;&lt;text x="290.0" y="198" text-anchor="middle"&gt;5,000&lt;/text&gt;&lt;/g&gt;&lt;text class="tc-title" x="34" y="213"&gt;rows&lt;/text&gt;&lt;polyline class="tc-line tc-other" points="34.0,100.0 98.0,100.0 162.0,100.0 226.0,100.0 290.0,142.0"/&gt;&lt;circle class="tc-point tc-other" cx="34.0" cy="100.0" r="3.5"&gt;&lt;title&gt;causal-learn, 200 rows: 0.5&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other" cx="98.0" cy="100.0" r="3.5"&gt;&lt;title&gt;causal-learn, 500 rows: 0.5&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other" cx="162.0" cy="100.0" r="3.5"&gt;&lt;title&gt;causal-learn, 1,000 rows: 0.5&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other" cx="226.0" cy="100.0" r="3.5"&gt;&lt;title&gt;causal-learn, 2,000 rows: 0.5&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other" cx="290.0" cy="142.0" r="3.5"&gt;&lt;title&gt;causal-learn, 5,000 rows: 0.25&lt;/title&gt;&lt;/circle&gt;&lt;polyline class="tc-line tc-andrey" points="34.0,58.0 98.0,24.4 162.0,16.0 226.0,16.0 290.0,16.0"/&gt;&lt;circle class="tc-point tc-andrey" cx="34.0" cy="58.0" r="3.5"&gt;&lt;title&gt;Andrey, 200 rows: 0.75&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="98.0" cy="24.4" r="3.5"&gt;&lt;title&gt;Andrey, 500 rows: 0.95&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="162.0" cy="16.0" r="3.5"&gt;&lt;title&gt;Andrey, 1,000 rows: 1&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="226.0" cy="16.0" r="3.5"&gt;&lt;title&gt;Andrey, 2,000 rows: 1&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="290.0" cy="16.0" r="3.5"&gt;&lt;title&gt;Andrey, 5,000 rows: 1&lt;/title&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;/div&gt;&lt;figcaption class="pa-note"&gt;The share of pairs, per size, on which each package names the true direction; higher is better. Each size has 20 pairs, except 10 at 4,000 rows and 5 at 8,000 rows for ANM. The empty graph names no direction, so its share is 0 at every size.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;At 200 rows, both methods select the reversed direction on a quarter of the pairs. A direction
test selects a direction on every pair, right or wrong. Check both p-values before you trust it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="other-highlights"&gt;
&lt;h2&gt;Other highlights&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Statistics break ties.&lt;/strong&gt; On a large sample, both p-values can round to zero. PNL now selects
the direction with the smaller HSIC statistic, relative to its mean under independence, instead
of drawing no edge. ANM uses the same rule. Both statistics are in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;metadata&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ANM in any units.&lt;/strong&gt; ANM standardizes each column first, so its answer does not depend on either
variable’s units.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For details, read the &lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;changelog&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/examples/pairwise.html"&gt;&lt;span class="doc std std-doc"&gt;pairwise notebook&lt;/span&gt;&lt;/a&gt; runs both tests on simulated pairs. You can open
it in Colab, then pass your own two-column table to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.anm&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.pnl&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="help-us-build-it"&gt;
&lt;h2&gt;Help us build it&lt;/h2&gt;
&lt;p&gt;Andrey is in alpha. The API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need.
We also welcome pull requests. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; explains how
you can set up.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261015-andrey-0-4-0.html"/>
    <summary>Does x cause y, or does y cause x? With only two variables, independence tests cannot tell:
both directions fit the data equally well. ANM and PNL instead use the shape of the relationship
to infer direction. This release adds ANM. As of 0.4.0, we support both methods.</summary>
    <category term="Pairwisedirection" label="Pairwise direction"/>
    <category term="Release" label="Release"/>
    <published>2026-10-15T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261012-andrey-0-3-0.html</id>
    <title>Score-based search</title>
    <updated>2026-10-12T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="score-based-search"&gt;

&lt;p&gt;A score-based method searches for the graph that best explains the data according to a score.
This release adds Tabu search and XGES. From 0.3.0, both are supported, as is hill climbing.
Hill climbing also returns the same graphs in less time.&lt;/p&gt;
&lt;p&gt;Below, we explain why a search can stop at the wrong graph. We run GES, hill climbing, Tabu search,
and XGES on one small dataset, then report benchmark results.&lt;/p&gt;
&lt;section id="searching-by-a-score"&gt;
&lt;h2&gt;Searching by a score&lt;/h2&gt;
&lt;p&gt;Andrey’s score-based methods rate a graph by its BIC (Bayesian information criterion). For each
variable, the BIC measures how much variance its parents in the graph leave unexplained.
It adds a penalty for each parent. A lower score is better. This BIC assumes linear relations
with Gaussian noise. The methods also assume no hidden common causes.&lt;/p&gt;
&lt;p&gt;The number of graphs grows faster than exponentially with the number of variables, so a search
cannot score them all. A greedy search starts from the empty graph. It takes the move that
improves the score most, such as adding, removing, or reversing one edge. It stops when no move
improves the score. That graph is a local optimum: no single move improves it. A better graph
can lie a few moves away, past graphs that score worse. A search that stops there can miss edges
or point them the wrong way.&lt;/p&gt;
&lt;p&gt;The four methods handle local optima differently:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hill climbing&lt;/strong&gt; searches over DAGs (directed acyclic graphs) and stops at the first local
optimum.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tabu search&lt;/strong&gt; takes the same moves. At a local optimum, it takes the best move even when the
score gets worse, and keeps going. It never returns to a recently visited graph, so it cannot
step straight back. It returns the best graph it visited, so with the same settings its score is
never worse than hill climbing’s.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GES&lt;/strong&gt; (greedy equivalence search) searches over equivalence classes: sets of graphs that fit
any data equally well. It adds edges while the score improves, then removes edges while the
score improves.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;XGES&lt;/strong&gt; (extremely greedy equivalence search) searches the same classes with GES’s moves and
edge reversals, in a different order. Each step deletes an edge if that improves the score.
Otherwise, it tries reversing an edge, then adding one. Its extended search then deletes each
edge of the result in turn, forbids adding it back, and searches again. It keeps any result
with a better score, which lets it leave a local optimum.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="methods"&gt;
&lt;h2&gt;Methods&lt;/h2&gt;
&lt;p&gt;Before each release, we benchmark each supported method against another package and check its
results on known graphs. Tabu search and XGES are new in this release.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Status&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Searches over&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Returns&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.ges.html"&gt;&lt;span class="std std-doc"&gt;GES&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Supported&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Equivalence classes&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A CPDAG and its BIC score&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.xges.html"&gt;&lt;span class="std std-doc"&gt;XGES&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Supported&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Equivalence classes&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A CPDAG and its BIC score&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.hc.html"&gt;&lt;span class="std std-doc"&gt;Hill climbing&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Supported&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;DAGs&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The CPDAG of the final DAG, and its score&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.tabu.html"&gt;&lt;span class="std std-doc"&gt;Tabu search&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Supported&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;DAGs&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;The CPDAG of the best DAG visited, and its score&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.gies.html"&gt;&lt;span class="std std-doc"&gt;GIES&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Experimental&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Equivalence classes&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A CPDAG and its BIC score; without interventions, the same search as GES&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.exact_search.html"&gt;&lt;span class="std std-doc"&gt;Exact search&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Experimental&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Every DAG, so only a few variables&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;One DAG with the best BIC score&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.calm.html"&gt;&lt;span class="std std-doc"&gt;CALM&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;Experimental&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A penalized likelihood, by continuous optimization&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;A weighted DAG, without a score&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Choose a method for your data:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;XGES&lt;/strong&gt; for continuous data that roughly fit the BIC’s assumptions. In our benchmarks, its
graphs scored better and had fewer errors than GES’s (see &lt;a class="reference internal" href="#benchmarks"&gt;Benchmarks&lt;/a&gt;). Set
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;extended_search=False&lt;/span&gt;&lt;/code&gt; for a faster fit that can stop at a local optimum.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GES&lt;/strong&gt; for the same data, as a well-studied reference.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hill climbing&lt;/strong&gt; as a fast baseline over DAGs, and &lt;strong&gt;Tabu search&lt;/strong&gt; when hill climbing may stop
early. Both take &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;max_iter&lt;/span&gt;&lt;/code&gt;, a move limit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exact search&lt;/strong&gt; to find the best-scoring graph on a few variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CALM&lt;/strong&gt; to try continuous optimization that returns edge weights. You need the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;torch&lt;/span&gt;&lt;/code&gt; extra.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;BOSS and GRaSP also minimize the BIC but search over orders of the variables. These are Andrey’s
permutation-based methods. When hidden common causes are plausible, use the methods in the
&lt;a class="reference internal" href="blog/20261012-andrey-0-2-0.html"&gt;&lt;span class="std std-doc"&gt;0.2.0 post&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="reading-a-cpdag"&gt;
&lt;h2&gt;Reading a CPDAG&lt;/h2&gt;
&lt;p&gt;GES, XGES, hill climbing, Tabu search, and GIES all return a CPDAG (completed partially directed
acyclic graph). You can find its BIC score in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;metadata[&amp;quot;score&amp;quot;]&lt;/span&gt;&lt;/code&gt;.
A CPDAG represents an equivalence class:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;: every graph in the class has &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; causing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;--&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;: the class has graphs with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; and graphs with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;. These fit the data
equally well, so the data cannot orient this edge.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a class="reference internal" href="docs/guides/graph-types.html#cpdag-an-equivalence-class"&gt;&lt;span class="std std-ref"&gt;Graph types&lt;/span&gt;&lt;/a&gt; for details.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="example"&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Here &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt; causes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x2&lt;/span&gt;&lt;/code&gt; cause &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x3&lt;/span&gt;&lt;/code&gt;. The relations are linear.
The noise is Gaussian. We chose this model because GES and hill climbing stop early on it.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;

&lt;span class="c1"&gt;# x0 causes x1, and x0, x1, and x2 cause x3.&lt;/span&gt;
&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;standard_normal&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;x0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e2&lt;/span&gt;
&lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e1&lt;/span&gt;
&lt;span class="n"&gt;x3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e3&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x3&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tabu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# For comparison: XGES without its extended search.&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extended_search&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;.post-graphs { container-type: inline-size; margin: 2rem 0; padding: clamp(1rem, 2.5vw, 1.5rem); border: 1px solid var(--andrey-line); border-radius: 12px; background: var(--andrey-surface); }
.post-graphs .pg-title { display: block; margin: 0 0 1.1rem; font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.02em; }
.post-graphs .pg-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: end; gap: 1.5rem 1.25rem; }
.post-graphs .pg-panel { margin: 0; padding: 0; }
.post-graphs .pg-graph svg { display: block; width: 100%; max-width: 17rem; height: auto; margin: 0 auto; }
.post-graphs .pg-panel figcaption { margin: 0.55rem 0 0; padding: 0; text-align: center; font-size: 0.85rem; line-height: 1.4; }
.post-graphs .pg-panel strong { display: block; color: var(--andrey-ink); font-weight: 600; }
.post-graphs .pg-panel span { color: var(--andrey-muted); font-size: 0.8rem; }
.post-graphs .pg-note { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--andrey-line); color: var(--andrey-muted); font-size: 0.8rem; line-height: 1.65; text-align: start; }
@container (max-width: 40rem) { .post-graphs .pg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container (max-width: 26rem) { .post-graphs .pg-grid { grid-template-columns: minmax(0, 1fr); } }&lt;/style&gt;&lt;figure class="post-graphs" aria-labelledby="pg-title"&gt;&lt;p class="pg-title" id="pg-title"&gt;What each search returns&lt;/p&gt;&lt;div class="pg-grid"&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg role="img" aria-label="True graph: x0 -&amp;gt; x1, x0 -&amp;gt; x3, x1 -&amp;gt; x3, x2 -&amp;gt; x3." xmlns="http://www.w3.org/2000/svg" viewBox="61 35 238 170" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="92.31" y1="83.57" x2="158.22" y2="61.25" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="167.69,58.04 156.61,56.51 159.82,65.99" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="92.83" y1="89.81" x2="257.29" y2="116.34" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="267.17,117.93 258.09,111.40 256.50,121.27" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="61.04" x2="260.82" y2="107.31" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,112.83 263.57,103.14 258.06,111.48" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="178.96" x2="260.82" y2="132.69" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,127.17 258.06,128.52 263.57,136.86" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="80.00" cy="87.74" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="80.00" y="87.74" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="180.00" cy="53.87" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="53.87" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="180.00" cy="186.13" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="186.13" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="280.00" cy="120.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="280.00" y="120.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;True graph&lt;/strong&gt;&lt;span&gt;the model behind the data&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg role="img" aria-label="GES: x2 -&amp;gt; x0, x3 -&amp;gt; x0, x1 -&amp;gt; x3, x2 -&amp;gt; x3; BIC score 18.22." xmlns="http://www.w3.org/2000/svg" viewBox="61 35 238 170" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="96.40" y1="103.87" x2="170.73" y2="177.01" stroke="var(--andrey-accent)" stroke-width="2.0" stroke-linecap="round" class="wrong"/&gt;&lt;polygon points="89.27,96.86 92.89,107.44 99.90,100.31" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="102.71" y1="91.40" x2="267.17" y2="117.93" stroke="var(--andrey-accent)" stroke-width="2.0" stroke-linecap="round" class="wrong"/&gt;&lt;polygon points="92.83,89.81 101.91,96.34 103.50,86.47" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="61.04" x2="260.82" y2="107.31" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,112.83 263.57,103.14 258.06,111.48" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="178.96" x2="260.82" y2="132.69" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,127.17 258.06,128.52 263.57,136.86" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="80.00" cy="87.74" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="80.00" y="87.74" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="180.00" cy="53.87" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="53.87" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="180.00" cy="186.13" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="186.13" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="280.00" cy="120.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="280.00" y="120.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;GES&lt;/strong&gt;&lt;span&gt;score 18.22, a local optimum&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg role="img" aria-label="Hill climbing: x2 -&amp;gt; x0, x3 -&amp;gt; x0, x1 -&amp;gt; x3, x2 -&amp;gt; x3; BIC score 18.22." xmlns="http://www.w3.org/2000/svg" viewBox="61 35 238 170" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="96.40" y1="103.87" x2="170.73" y2="177.01" stroke="var(--andrey-accent)" stroke-width="2.0" stroke-linecap="round" class="wrong"/&gt;&lt;polygon points="89.27,96.86 92.89,107.44 99.90,100.31" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="102.71" y1="91.40" x2="267.17" y2="117.93" stroke="var(--andrey-accent)" stroke-width="2.0" stroke-linecap="round" class="wrong"/&gt;&lt;polygon points="92.83,89.81 101.91,96.34 103.50,86.47" fill="var(--andrey-accent)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="61.04" x2="260.82" y2="107.31" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,112.83 263.57,103.14 258.06,111.48" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="178.96" x2="260.82" y2="132.69" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,127.17 258.06,128.52 263.57,136.86" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="80.00" cy="87.74" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="80.00" y="87.74" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="180.00" cy="53.87" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="53.87" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="180.00" cy="186.13" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="186.13" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="280.00" cy="120.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="280.00" y="120.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;Hill climbing&lt;/strong&gt;&lt;span&gt;score 18.22, a local optimum&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg role="img" aria-label="Tabu search: x0 -- x1, x0 -&amp;gt; x3, x1 -&amp;gt; x3, x2 -&amp;gt; x3; BIC score 15.11." xmlns="http://www.w3.org/2000/svg" viewBox="61 35 238 170" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="92.31" y1="83.57" x2="167.69" y2="58.04" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="92.83" y1="89.81" x2="257.29" y2="116.34" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="267.17,117.93 258.09,111.40 256.50,121.27" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="61.04" x2="260.82" y2="107.31" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,112.83 263.57,103.14 258.06,111.48" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="178.96" x2="260.82" y2="132.69" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,127.17 258.06,128.52 263.57,136.86" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="80.00" cy="87.74" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="80.00" y="87.74" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="180.00" cy="53.87" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="53.87" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="180.00" cy="186.13" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="186.13" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="280.00" cy="120.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="280.00" y="120.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;Tabu search&lt;/strong&gt;&lt;span&gt;score 15.11, the true graph&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg role="img" aria-label="XGES: x0 -- x1, x0 -&amp;gt; x3, x1 -&amp;gt; x3, x2 -&amp;gt; x3; BIC score 15.11." xmlns="http://www.w3.org/2000/svg" viewBox="61 35 238 170" width="100%" font-family="Inter, system-ui, sans-serif"&gt;&lt;line x1="92.31" y1="83.57" x2="167.69" y2="58.04" stroke="var(--andrey-edge-undirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;line x1="92.83" y1="89.81" x2="257.29" y2="116.34" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="267.17,117.93 258.09,111.40 256.50,121.27" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="61.04" x2="260.82" y2="107.31" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,112.83 263.57,103.14 258.06,111.48" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="190.84" y1="178.96" x2="260.82" y2="132.69" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="269.16,127.17 258.06,128.52 263.57,136.86" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="80.00" cy="87.74" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="80.00" y="87.74" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="180.00" cy="53.87" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="53.87" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="180.00" cy="186.13" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="180.00" y="186.13" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="280.00" cy="120.00" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="280.00" y="120.00" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;XGES&lt;/strong&gt;&lt;span&gt;score 15.11, the true graph&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&lt;figcaption class="pg-note"&gt;Every node in the same place; lower scores are better. GES and hill climbing stop at the same graph: x2 -&amp;gt; x0 (cobalt) is not in the true graph, x3 -&amp;gt; x0 (cobalt) points the wrong way, and x0 -- x1 is missing. Tabu search and XGES return the true graph's equivalence class, which leaves x0 -- x1 (amber) undirected.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;GES and hill climbing stop at the same graph, with a score of 18.22. It has &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x2&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt;, an edge
absent from the true graph. It also has &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x3&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt;, which points the wrong way.
It misses &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt; &lt;span class="pre"&gt;--&lt;/span&gt; &lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tabu search and XGES return the true graph’s equivalence class, with a lower score of 15.11.
The edge &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt; &lt;span class="pre"&gt;--&lt;/span&gt; &lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt; stays undirected. The true graph and the same graph with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x1&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt; are
equivalent, so they fit any data equally well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;XGES without its extended search returns GES’s graph (the last result below). Here, the
extended search finds the better graph.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The script prints each result in full:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GES  cpdag  |  4 nodes  |  4 edges (4 directed)

  x2 -&amp;gt; x0
  x3 -&amp;gt; x0
  x1 -&amp;gt; x3
  x2 -&amp;gt; x3

  score: 18.22

HC  cpdag  |  4 nodes  |  4 edges (4 directed)

  x2 -&amp;gt; x0
  x3 -&amp;gt; x0
  x1 -&amp;gt; x3
  x2 -&amp;gt; x3

  score: 18.22

Tabu  cpdag  |  4 nodes  |  4 edges (1 undirected, 3 directed)

  x0 -- x1
  x0 -&amp;gt; x3
  x1 -&amp;gt; x3
  x2 -&amp;gt; x3

  score: 15.11

XGES  cpdag  |  4 nodes  |  4 edges (1 undirected, 3 directed)

  x0 -- x1
  x0 -&amp;gt; x3
  x1 -&amp;gt; x3
  x2 -&amp;gt; x3

  score: 15.11

XGES  cpdag  |  4 nodes  |  4 edges (4 directed)

  x2 -&amp;gt; x0
  x3 -&amp;gt; x0
  x1 -&amp;gt; x3
  x2 -&amp;gt; x3

  score: 18.22
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="benchmarks"&gt;
&lt;h2&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;We compared XGES with xges, a Python package that implements it, and Tabu search and hill
climbing with bnlearn, an R package. The data are linear-Gaussian, simulated from random graphs
with ten rows per variable.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Compared with&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Andrey’s speedup&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Same graph&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;XGES&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;xges&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;up to 140x&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;on every dataset&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;Tabu search&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;bnlearn&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;up to 561x&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;on most datasets&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;Hill climbing&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;bnlearn&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;up to 906x&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;on most datasets&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/benchmarks/0.3.0.html"&gt;&lt;span class="std std-doc"&gt;0.3.0 release card&lt;/span&gt;&lt;/a&gt; reports every measurement, with the sizes,
machines, and settings. Hill climbing ran to a local optimum and Tabu search until it stopped on
its own, both with no move limit. Without the extended search, XGES still returns the same graph
as xges on every dataset, and runs faster at every size.&lt;/p&gt;
&lt;style&gt;@supports selector(:has(a)) { .race030 .race-panels { display: grid; } .race030 .race-panel { grid-area: 1 / 1; visibility: hidden; } .race030:has(#race030-m-xges:checked) #race030-panel-xges, .race030:has(#race030-m-tabu:checked) #race030-panel-tabu, .race030:has(#race030-m-hc:checked) #race030-panel-hc { visibility: visible; } } @supports not selector(:has(a)) { .race030 .race-panel::before { content: attr(aria-label); display: block; margin-top: 1rem; font-size: 0.85rem; font-weight: 600; } }&lt;/style&gt;&lt;figure class="race race--speedup race030" aria-labelledby="race030-title"&gt;&lt;figcaption class="race-head"&gt;&lt;span class="race-title" id="race030-title"&gt;Speedup over xges and bnlearn&lt;/span&gt;&lt;a class="race-link" href="../docs/benchmarks/0.3.0.html"&gt;Release card&lt;/a&gt;&lt;/figcaption&gt;&lt;fieldset class="race-sizes"&gt;&lt;legend class="visually-hidden"&gt;Method&lt;/legend&gt;&lt;input class="race-choice" type="radio" name="race030-method" id="race030-m-xges" checked&gt;&lt;label for="race030-m-xges"&gt;XGES&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race030-method" id="race030-m-tabu"&gt;&lt;label for="race030-m-tabu"&gt;Tabu search&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race030-method" id="race030-m-hc"&gt;&lt;label for="race030-m-hc"&gt;Hill climbing&lt;/label&gt;&lt;/fieldset&gt;&lt;div class="race-panels"&gt;&lt;div class="race-panel" id="race030-panel-xges" role="group" aria-label="XGES: Andrey&amp;#x27;s speedup over xges at 25, 50, 100, and 200 variables: 3.95x, 10.9x, 43.2x, 140x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;25 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.277988"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;3.95x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;50 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.483395"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;10.9x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;100 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.762063"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;43.2x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;200 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;140x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race030-panel-tabu" role="group" aria-label="Tabu search: Andrey&amp;#x27;s speedup over bnlearn at 10, 20, 50, 100, 200, 400, and 800 variables: 2.54x, 2.38x, 5.63x, 16.6x, 55.3x, 176x, 561x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;10 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.147268"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.54x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;20 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.136989"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.38x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;50 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.273015"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;5.63x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;100 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.443843"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;16.6x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;200 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.633957"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;55.3x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;400 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.816858"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;176x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 6"&gt;&lt;span class="race-name"&gt;800 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;561x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race030-panel-hc" role="group" aria-label="Hill climbing: Andrey&amp;#x27;s speedup over bnlearn at 10, 20, 50, 100, 200, 400, and 800 variables: 5.27x, 3.60x, 6.13x, 19.3x, 70.6x, 231x, 906x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;10 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.244092"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;5.27x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;20 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.188123"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;3.60x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;50 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.266292"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;6.13x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;100 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.434732"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;19.3x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;200 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.625203"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;70.6x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 5"&gt;&lt;span class="race-name"&gt;400 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.799293"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;231x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 6"&gt;&lt;span class="race-name"&gt;800 variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;906x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="race-note"&gt;Each bar is the other package&amp;#x27;s time divided by Andrey&amp;#x27;s on the same dataset, the median over five datasets: xges for XGES, on 1 core per fit, and bnlearn for Tabu search and hill climbing, on 4 cores. Every graph has a mean degree of 2. The bars are on a log scale, so equal steps are equal factors; each method&amp;#x27;s bars share its own scale.&lt;/p&gt;&lt;/figure&gt;
&lt;p&gt;On these data, XGES’s graphs scored better than GES’s and had fewer errors, at every size. On
the denser graphs, the extended search cut XGES’s errors at every size, nearly by half at 50
variables.&lt;/p&gt;
&lt;p&gt;Agreement between packages does not establish accuracy. From 50 variables on, Tabu search’s and
hill climbing’s graphs have more errors than the empty graph, in both packages. Tabu search finds
better-scoring graphs than hill climbing at every size, yet at 400 variables its graphs have more
errors. A better score is not always a better graph.&lt;/p&gt;
&lt;p&gt;On the sparser graphs, XGES’s graphs score better than the true graph at every size. With ten
rows per variable, the best-scoring graph is not the true one.
A larger &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;lambda_value&lt;/span&gt;&lt;/code&gt; penalizes each parent more and favors sparser graphs.&lt;/p&gt;
&lt;style&gt;.post-errors { margin: 2rem 0; padding: clamp(1rem, 2.5vw, 1.5rem); border: 1px solid var(--andrey-line); border-radius: 12px; background: var(--andrey-surface); container-type: inline-size; }.post-errors .pe-title { display: block; margin: 0 0 1rem; font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.02em; }.post-errors .tc-legend { margin: 0.4rem 0 0.25rem; }.post-errors .pe-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; }.post-errors .tchart { padding: 0; border: 0; border-radius: 0; background: none; }.post-errors .tchart-name span { display: block; }.post-errors .tc-key.pe-solid i { border-top-style: solid; }.post-errors .tc-key.s2 i { border-top-color: var(--andrey-categorical-2); }.post-errors .tc-line.s2 { stroke: var(--andrey-categorical-2); }.post-errors .tc-point.s2 { fill: var(--andrey-categorical-2); }.post-errors .pe-key-band i { height: 0.7rem; border: 0; background: color-mix(in srgb, var(--andrey-muted) 22%, transparent); }.post-errors .pe-band { fill: color-mix(in srgb, var(--andrey-muted) 22%, transparent); }.post-errors svg .pe-end { fill: var(--andrey-ink); font-weight: 600; }.post-errors .pe-note { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--andrey-line); color: var(--andrey-muted); font-size: 0.8rem; line-height: 1.65; text-align: start; }@container (max-width: 34rem) { .post-errors .pe-grid { grid-template-columns: minmax(0, 1fr); } }&lt;/style&gt;&lt;figure class="post-errors" aria-labelledby="pe-title"&gt;&lt;p class="pe-title" id="pe-title"&gt;Errors against the true graph&lt;/p&gt;&lt;div class="pe-grid"&gt;&lt;figure class="tchart"&gt;&lt;figcaption&gt;&lt;span class="tchart-name"&gt;&lt;strong&gt;XGES&lt;/strong&gt;&lt;span&gt;Mean degree 4: the fewest errors at every size&lt;/span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;p class="tc-legend"&gt;&lt;span class="tc-key tc-andrey"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;XGES&lt;/span&gt;&lt;span class="tc-key s2"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Without extended search&lt;/span&gt;&lt;span class="tc-key tc-other pe-solid"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;GES&lt;/span&gt;&lt;span class="tc-key tc-other-1"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Empty graph&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 320 214" role="img" aria-label="XGES at mean degree 4: median errors by variables, XGES, XGES without its extended search, GES, and the empty graph. 25: 38, 44, 55, against 50; 50: 45, 88, 109, against 100; 100: 63, 113, 179, against 200; 200: 151, 236, 266, against 400."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="30" x2="290" y1="184.0" y2="184.0" class="tc-grid"/&gt;&lt;text x="24" y="187.0" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="30" x2="290" y1="142.0" y2="142.0" class="tc-grid"/&gt;&lt;text x="24" y="145.0" text-anchor="end"&gt;100&lt;/text&gt;&lt;line x1="30" x2="290" y1="100.0" y2="100.0" class="tc-grid"/&gt;&lt;text x="24" y="103.0" text-anchor="end"&gt;200&lt;/text&gt;&lt;line x1="30" x2="290" y1="58.0" y2="58.0" class="tc-grid"/&gt;&lt;text x="24" y="61.0" text-anchor="end"&gt;300&lt;/text&gt;&lt;line x1="30" x2="290" y1="16.0" y2="16.0" class="tc-grid"/&gt;&lt;text x="24" y="19.0" text-anchor="end"&gt;400&lt;/text&gt;&lt;text x="30.0" y="198" text-anchor="middle"&gt;25&lt;/text&gt;&lt;text x="116.7" y="198" text-anchor="middle"&gt;50&lt;/text&gt;&lt;text x="203.3" y="198" text-anchor="middle"&gt;100&lt;/text&gt;&lt;text x="290.0" y="198" text-anchor="middle"&gt;200&lt;/text&gt;&lt;/g&gt;&lt;text class="tc-title" x="30" y="213"&gt;variables&lt;/text&gt;&lt;polyline class="tc-line tc-other tc-other-1" points="30.0,163.0 116.7,142.0 203.3,100.0 290.0,16.0"/&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="30.0" cy="163.0" r="3.5"&gt;&lt;title&gt;Empty graph, 25 variables: 50 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="116.7" cy="142.0" r="3.5"&gt;&lt;title&gt;Empty graph, 50 variables: 100 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="203.3" cy="100.0" r="3.5"&gt;&lt;title&gt;Empty graph, 100 variables: 200 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="290.0" cy="16.0" r="3.5"&gt;&lt;title&gt;Empty graph, 200 variables: 400 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="19.0"&gt;400&lt;/text&gt;&lt;polyline class="tc-line tc-other" points="30.0,160.9 116.7,138.2 203.3,108.8 290.0,72.3"/&gt;&lt;circle class="tc-point tc-other" cx="30.0" cy="160.9" r="3.5"&gt;&lt;title&gt;GES, 25 variables: 55 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other" cx="116.7" cy="138.2" r="3.5"&gt;&lt;title&gt;GES, 50 variables: 109 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other" cx="203.3" cy="108.8" r="3.5"&gt;&lt;title&gt;GES, 100 variables: 179 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other" cx="290.0" cy="72.3" r="3.5"&gt;&lt;title&gt;GES, 200 variables: 266 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="75.3"&gt;266&lt;/text&gt;&lt;polyline class="tc-line s2" points="30.0,165.5 116.7,147.0 203.3,136.5 290.0,84.9"/&gt;&lt;circle class="tc-point s2" cx="30.0" cy="165.5" r="3.5"&gt;&lt;title&gt;Without extended search, 25 variables: 44 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="116.7" cy="147.0" r="3.5"&gt;&lt;title&gt;Without extended search, 50 variables: 88 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="203.3" cy="136.5" r="3.5"&gt;&lt;title&gt;Without extended search, 100 variables: 113 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="290.0" cy="84.9" r="3.5"&gt;&lt;title&gt;Without extended search, 200 variables: 236 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="87.9"&gt;236&lt;/text&gt;&lt;polyline class="tc-line tc-andrey" points="30.0,168.0 116.7,165.1 203.3,157.5 290.0,120.6"/&gt;&lt;circle class="tc-point tc-andrey" cx="30.0" cy="168.0" r="3.5"&gt;&lt;title&gt;XGES, 25 variables: 38 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="116.7" cy="165.1" r="3.5"&gt;&lt;title&gt;XGES, 50 variables: 45 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="203.3" cy="157.5" r="3.5"&gt;&lt;title&gt;XGES, 100 variables: 63 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="290.0" cy="120.6" r="3.5"&gt;&lt;title&gt;XGES, 200 variables: 151 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="123.6"&gt;151&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;figure class="tchart"&gt;&lt;figcaption&gt;&lt;span class="tchart-name"&gt;&lt;strong&gt;Tabu search and hill climbing&lt;/strong&gt;&lt;span&gt;Mean degree 2: more errors than the empty graph from 50 variables&lt;/span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;p class="tc-legend"&gt;&lt;span class="tc-key tc-andrey"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Tabu search&lt;/span&gt;&lt;span class="tc-key s2"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Hill climbing&lt;/span&gt;&lt;span class="tc-key tc-other-1"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Empty graph&lt;/span&gt;&lt;span class="tc-key pe-key-band"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;More errors than the empty graph&lt;/span&gt;&lt;/p&gt;&lt;svg viewBox="0 0 320 214" role="img" aria-label="Tabu search and hill climbing at mean degree 2: median errors by variables, against the empty graph. 10: 10 and 10, against 10; 20: 14 and 12, against 20; 50: 54 and 55, against 50; 100: 117 and 120, against 100; 200: 267 and 273, against 200; 400: 615 and 605, against 400; 800: 1,495 and 1,500, against 800."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="30" x2="290" y1="184.0" y2="184.0" class="tc-grid"/&gt;&lt;text x="24" y="187.0" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="30" x2="290" y1="142.0" y2="142.0" class="tc-grid"/&gt;&lt;text x="24" y="145.0" text-anchor="end"&gt;400&lt;/text&gt;&lt;line x1="30" x2="290" y1="100.0" y2="100.0" class="tc-grid"/&gt;&lt;text x="24" y="103.0" text-anchor="end"&gt;800&lt;/text&gt;&lt;line x1="30" x2="290" y1="58.0" y2="58.0" class="tc-grid"/&gt;&lt;text x="24" y="61.0" text-anchor="end"&gt;1,200&lt;/text&gt;&lt;line x1="30" x2="290" y1="16.0" y2="16.0" class="tc-grid"/&gt;&lt;text x="24" y="19.0" text-anchor="end"&gt;1,600&lt;/text&gt;&lt;text x="30.0" y="198" text-anchor="middle"&gt;10&lt;/text&gt;&lt;text x="73.3" y="198" text-anchor="middle"&gt;20&lt;/text&gt;&lt;text x="116.7" y="198" text-anchor="middle"&gt;50&lt;/text&gt;&lt;text x="160.0" y="198" text-anchor="middle"&gt;100&lt;/text&gt;&lt;text x="203.3" y="198" text-anchor="middle"&gt;200&lt;/text&gt;&lt;text x="246.7" y="198" text-anchor="middle"&gt;400&lt;/text&gt;&lt;text x="290.0" y="198" text-anchor="middle"&gt;800&lt;/text&gt;&lt;/g&gt;&lt;text class="tc-title" x="30" y="213"&gt;variables&lt;/text&gt;&lt;polygon class="pe-band" points="99.3,180.0 116.7,178.3 116.7,178.8 99.3,180.0"/&gt;&lt;polygon class="pe-band" points="116.7,178.3 160.0,171.7 160.0,173.5 116.7,178.8"/&gt;&lt;polygon class="pe-band" points="160.0,171.7 203.3,156.0 203.3,163.0 160.0,173.5"/&gt;&lt;polygon class="pe-band" points="203.3,156.0 246.7,119.4 246.7,142.0 203.3,163.0"/&gt;&lt;polygon class="pe-band" points="246.7,119.4 290.0,27.0 290.0,100.0 246.7,142.0"/&gt;&lt;polyline class="tc-line tc-other tc-other-1" points="30.0,182.9 73.3,181.9 116.7,178.8 160.0,173.5 203.3,163.0 246.7,142.0 290.0,100.0"/&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="30.0" cy="182.9" r="3.5"&gt;&lt;title&gt;Empty graph, 10 variables: 10 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="73.3" cy="181.9" r="3.5"&gt;&lt;title&gt;Empty graph, 20 variables: 20 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="116.7" cy="178.8" r="3.5"&gt;&lt;title&gt;Empty graph, 50 variables: 50 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="160.0" cy="173.5" r="3.5"&gt;&lt;title&gt;Empty graph, 100 variables: 100 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="203.3" cy="163.0" r="3.5"&gt;&lt;title&gt;Empty graph, 200 variables: 200 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="246.7" cy="142.0" r="3.5"&gt;&lt;title&gt;Empty graph, 400 variables: 400 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="290.0" cy="100.0" r="3.5"&gt;&lt;title&gt;Empty graph, 800 variables: 800 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="103.0"&gt;800&lt;/text&gt;&lt;polyline class="tc-line s2" points="30.0,182.9 73.3,182.7 116.7,178.2 160.0,171.4 203.3,155.3 246.7,120.5 290.0,26.5"/&gt;&lt;circle class="tc-point s2" cx="30.0" cy="182.9" r="3.5"&gt;&lt;title&gt;Hill climbing, 10 variables: 10 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="73.3" cy="182.7" r="3.5"&gt;&lt;title&gt;Hill climbing, 20 variables: 12 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="116.7" cy="178.2" r="3.5"&gt;&lt;title&gt;Hill climbing, 50 variables: 55 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="160.0" cy="171.4" r="3.5"&gt;&lt;title&gt;Hill climbing, 100 variables: 120 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="203.3" cy="155.3" r="3.5"&gt;&lt;title&gt;Hill climbing, 200 variables: 273 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="246.7" cy="120.5" r="3.5"&gt;&lt;title&gt;Hill climbing, 400 variables: 605 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point s2" cx="290.0" cy="26.5" r="3.5"&gt;&lt;title&gt;Hill climbing, 800 variables: 1,500 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="20.5"&gt;1,500&lt;/text&gt;&lt;polyline class="tc-line tc-andrey" points="30.0,182.9 73.3,182.5 116.7,178.3 160.0,171.7 203.3,156.0 246.7,119.4 290.0,27.0"/&gt;&lt;circle class="tc-point tc-andrey" cx="30.0" cy="182.9" r="3.5"&gt;&lt;title&gt;Tabu search, 10 variables: 10 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="73.3" cy="182.5" r="3.5"&gt;&lt;title&gt;Tabu search, 20 variables: 14 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="116.7" cy="178.3" r="3.5"&gt;&lt;title&gt;Tabu search, 50 variables: 54 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="160.0" cy="171.7" r="3.5"&gt;&lt;title&gt;Tabu search, 100 variables: 117 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="203.3" cy="156.0" r="3.5"&gt;&lt;title&gt;Tabu search, 200 variables: 267 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="246.7" cy="119.4" r="3.5"&gt;&lt;title&gt;Tabu search, 400 variables: 615 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="290.0" cy="27.0" r="3.5"&gt;&lt;title&gt;Tabu search, 800 variables: 1,495 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="33.0"&gt;1,495&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;/div&gt;&lt;figcaption class="pe-note"&gt;The median over five datasets per size of the structural Hamming distance (SHD) to the true graph&amp;#x27;s equivalence class: the edges added, missed, or misoriented. Lower is better. XGES returns the same graphs as xges, with and without the extended search, so each shares one line. Tabu search and hill climbing are Andrey&amp;#x27;s; bnlearn&amp;#x27;s medians are within one error of them at every size.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Andrey alone also ran XGES at up to 1,000 variables, and Tabu search and hill climbing at up to
3,200. On the sparser graphs, their graphs at these sizes have more errors than the empty graph.&lt;/p&gt;
&lt;p&gt;Speed and accuracy depend on your data, settings, and hardware.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="other-highlights"&gt;
&lt;h2&gt;Other highlights&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster hill climbing.&lt;/strong&gt; Hill climbing caches the score change for each candidate move
between steps. It recomputes only the moves a step changes.
It returns the same graphs as before in less time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hill climbing runs in one process.&lt;/strong&gt; Its worker processes were slower at every measured size.
We removed them, along with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ANDREY_HC_PARALLEL_MIN_WORK&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey&lt;/span&gt; &lt;span class="pre"&gt;calibrate&lt;/span&gt; &lt;span class="pre"&gt;--method&lt;/span&gt; &lt;span class="pre"&gt;hc&lt;/span&gt;&lt;/code&gt;.
This change breaks scripts that set either one.
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ANDREY_NUM_WORKERS&lt;/span&gt;&lt;/code&gt; still sets the workers for GES, GIES, and GFCI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;XGES without the extended search.&lt;/strong&gt; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.xges(data,&lt;/span&gt; &lt;span class="pre"&gt;extended_search=False)&lt;/span&gt;&lt;/code&gt; returns the
graph where the first search stops. On the sparser graphs at 200 variables, it took a tenth of
the time of the full search.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shorter summaries.&lt;/strong&gt; Since 0.2.1, a result’s summary shows the length of a long metadata list
instead of printing it in full.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details, read the &lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;changelog&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/examples/ges_quickstart.html"&gt;&lt;span class="doc std std-doc"&gt;GES quickstart&lt;/span&gt;&lt;/a&gt; notebook fits GES on a small chain.
You can open it in Colab and replace &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.ges&lt;/span&gt;&lt;/code&gt; with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.xges&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.tabu&lt;/span&gt;&lt;/code&gt;, or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;andrey.hc&lt;/span&gt;&lt;/code&gt;.
Then try the methods on your own data.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="help-us-build-it"&gt;
&lt;h2&gt;Help us build it&lt;/h2&gt;
&lt;p&gt;Andrey is in alpha, and the API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need.
We also welcome pull requests. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; explains how
you can set up.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261012-andrey-0-3-0.html"/>
    <summary>A score-based method searches for the graph that best explains the data according to a score.
This release adds Tabu search and XGES. From 0.3.0, both are supported, as is hill climbing.
Hill climbing also returns the same graphs in less time.</summary>
    <category term="Release" label="Release"/>
    <category term="Score-based" label="Score-based"/>
    <published>2026-10-12T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261012-andrey-0-2-0.html</id>
    <title>Hidden common causes</title>
    <updated>2026-10-12T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="hidden-common-causes"&gt;

&lt;p&gt;A hidden common cause, or latent confounder, is an unmeasured variable that affects two measured
variables. This release adds RCD and BottomUpParceLiNGAM as supported methods. With FCI, they
cover data with hidden common causes. Below, we run all three on one small dataset and report
benchmark results. Other updates make small fits several times faster.&lt;/p&gt;
&lt;section id="why-hidden-causes-matter"&gt;
&lt;h2&gt;Why hidden causes matter&lt;/h2&gt;
&lt;p&gt;Many causal discovery methods, including PC, GES, and DirectLiNGAM, assume causal sufficiency:
every common cause of two measured variables is measured too. Real data often violate this
assumption. A hidden common cause can make two variables dependent even without an edge between
them. A method that assumes causal sufficiency can then draw an edge that is not there. Methods
that allow hidden common causes can mark the pair or leave the edge’s ends unresolved.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="methods"&gt;
&lt;h2&gt;Methods&lt;/h2&gt;
&lt;p&gt;We benchmark each supported method against another package and check its results on known graphs
before each release. RCD and BottomUpParceLiNGAM are new in this release.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Assumes&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.fci.html"&gt;&lt;span class="std std-doc"&gt;FCI&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Linear relations with roughly Gaussian noise, through its Fisher-z test&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.rcd.html"&gt;&lt;span class="std std-doc"&gt;RCD&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Linear relations with non-Gaussian noise&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/api/andrey.bottom_up_parce_lingam.html"&gt;&lt;span class="std std-doc"&gt;BottomUpParceLiNGAM&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;Linear relations with non-Gaussian noise&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="reading-a-pag"&gt;
&lt;h2&gt;Reading a PAG&lt;/h2&gt;
&lt;p&gt;All three return a partial ancestral graph (PAG) over the variables you measured. Each end of
an edge carries a tail, an arrowhead, or a circle. A circle means that end is unresolved.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; causes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;: a hidden variable links &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;, such as a common cause.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;o-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; does not cause &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt;; whether &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; causes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt; is unresolved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;o-o&lt;/span&gt; &lt;span class="pre"&gt;y&lt;/span&gt;&lt;/code&gt;: both ends are unresolved.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;FCI marks an end with a tail or an arrowhead only when every graph consistent with the
data’s conditional independences agrees on that mark. RCD and BottomUpParceLiNGAM use
the same marks for their findings: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt;&lt;/code&gt; for a direct cause and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;-&amp;gt;&lt;/span&gt;&lt;/code&gt; for a pair linked by a hidden
variable. BottomUpParceLiNGAM also uses &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;o-o&lt;/span&gt;&lt;/code&gt; between variables it cannot order.
See &lt;a class="reference internal" href="docs/guides/graph-types.html#pag-uncertainty-with-hidden-variables"&gt;&lt;span class="std std-ref"&gt;Graph types&lt;/span&gt;&lt;/a&gt; for details.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="example"&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Here &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;h&lt;/span&gt;&lt;/code&gt; causes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt; but is not in the data. Also, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt; causes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x3&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt; causes
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x2&lt;/span&gt;&lt;/code&gt;. The noise is uniform, so it is not Gaussian.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;

&lt;span class="c1"&gt;# h is a common cause of x0 and x1, and it is not in the data.&lt;/span&gt;
&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;x0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e1&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e3&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fci&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rcd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bottom_up_parce_lingam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# For comparison: DirectLiNGAM assumes every common cause is in the data.&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direct_lingam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;.post-graphs { container-type: inline-size; margin: 2rem 0; padding: clamp(1rem, 2.5vw, 1.5rem); border: 1px solid var(--andrey-line); border-radius: 12px; background: var(--andrey-surface); }.post-graphs .pg-title { display: block; margin: 0 0 1.1rem; font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.02em; }.post-graphs .pg-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: end; gap: 1.5rem 1.25rem; }.post-graphs .pg-panel { margin: 0; padding: 0; }.post-graphs .pg-graph svg { display: block; width: 100%; max-width: 17rem; height: auto; margin: 0 auto; }.post-graphs .pg-panel figcaption { margin: 0.55rem 0 0; padding: 0; text-align: center; font-size: 0.85rem; line-height: 1.4; }.post-graphs .pg-panel strong { display: block; color: var(--andrey-ink); font-weight: 600; }.post-graphs .pg-panel span { color: var(--andrey-muted); font-size: 0.8rem; }.post-graphs .pg-note { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--andrey-line); color: var(--andrey-muted); font-size: 0.8rem; line-height: 1.65; text-align: start; }@container (max-width: 40rem) { .post-graphs .pg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }@container (max-width: 26rem) { .post-graphs .pg-grid { grid-template-columns: minmax(0, 1fr); } }&lt;/style&gt;&lt;figure class="post-graphs" aria-labelledby="pg-title"&gt;&lt;p class="pg-title" id="pg-title"&gt;What each method returns&lt;/p&gt;&lt;div class="pg-grid"&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="14 156 256 89" width="100%" font-family="Inter, system-ui, sans-serif" role="img" aria-label="True graph: a hidden variable h causes x0 and x1; x0 causes x3; x1 causes x2."&gt;&lt;line x1="85.00" y1="226.40" x2="55.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="45.00,226.40 55.00,231.40 55.00,221.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="112.72" y1="208.73" x2="133.68" y2="183.59" stroke="var(--andrey-muted)" stroke-width="2.0" stroke-linecap="round" stroke-dasharray="4 3"/&gt;&lt;polygon points="106.32,216.41 116.57,211.93 108.88,205.53" fill="var(--andrey-muted)" stroke-linejoin="round"/&gt;&lt;line x1="199.00" y1="226.40" x2="229.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="239.00,226.40 229.00,221.40 229.00,231.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="171.28" y1="208.73" x2="150.32" y2="183.59" stroke="var(--andrey-muted)" stroke-width="2.0" stroke-linecap="round" stroke-dasharray="4 3"/&gt;&lt;polygon points="177.68,216.41 175.12,205.53 167.43,211.93" fill="var(--andrey-muted)" stroke-linejoin="round"/&gt;&lt;circle cx="98.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="98.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="186.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="186.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="252.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="252.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="32.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;circle cx="142.00" cy="173.60" r="13.00" fill="var(--andrey-paper)" stroke="var(--andrey-muted)" stroke-width="1.8" stroke-dasharray="3 3"/&gt;&lt;text x="142.00" y="173.60" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-muted)" dominant-baseline="central"&gt;h&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;True graph&lt;/strong&gt;&lt;span&gt;h is hidden, not in the data&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="14 208 256 36" width="100%" font-family="Inter, system-ui, sans-serif" role="img" aria-label="FCI: x0 o-o x1, x0 o-o x3, x1 o-o x2."&gt;&lt;line x1="119.40" y1="226.40" x2="164.60" y2="226.40" stroke="var(--andrey-edge-partial)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;circle cx="115.20" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;circle cx="168.80" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;line x1="76.60" y1="226.40" x2="53.40" y2="226.40" stroke="var(--andrey-edge-partial)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;circle cx="80.80" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;circle cx="49.20" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;line x1="207.40" y1="226.40" x2="230.60" y2="226.40" stroke="var(--andrey-edge-partial)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;circle cx="203.20" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;circle cx="234.80" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;circle cx="98.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="98.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="186.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="186.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="252.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="252.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="32.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;FCI&lt;/strong&gt;&lt;span&gt;every end unresolved&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="14 208 256 36" width="100%" font-family="Inter, system-ui, sans-serif" role="img" aria-label="RCD: x0 &amp;lt;-&amp;gt; x1, x0 -&amp;gt; x3, x1 -&amp;gt; x2."&gt;&lt;line x1="121.00" y1="226.40" x2="163.00" y2="226.40" stroke="var(--andrey-edge-bidirected)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="111.00,226.40 121.00,231.40 121.00,221.40" fill="var(--andrey-edge-bidirected)" stroke-linejoin="round"/&gt;&lt;polygon points="173.00,226.40 163.00,221.40 163.00,231.40" fill="var(--andrey-edge-bidirected)" stroke-linejoin="round"/&gt;&lt;line x1="85.00" y1="226.40" x2="55.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="45.00,226.40 55.00,231.40 55.00,221.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="199.00" y1="226.40" x2="229.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="239.00,226.40 229.00,221.40 229.00,231.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="98.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="98.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="186.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="186.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="252.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="252.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="32.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;RCD&lt;/strong&gt;&lt;span&gt;matches the true graph&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="14 208 256 36" width="100%" font-family="Inter, system-ui, sans-serif" role="img" aria-label="BottomUpParceLiNGAM: x0 o-o x1, x0 -&amp;gt; x3, x1 -&amp;gt; x2."&gt;&lt;line x1="119.40" y1="226.40" x2="164.60" y2="226.40" stroke="var(--andrey-edge-partial)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;circle cx="115.20" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;circle cx="168.80" cy="226.40" r="4.20" fill="var(--andrey-surface)" stroke="var(--andrey-edge-partial)" stroke-width="2.0"/&gt;&lt;line x1="85.00" y1="226.40" x2="55.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="45.00,226.40 55.00,231.40 55.00,221.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="199.00" y1="226.40" x2="229.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="239.00,226.40 229.00,221.40 229.00,231.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="98.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="98.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="186.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="186.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="252.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="252.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="32.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;BottomUpParceLiNGAM&lt;/strong&gt;&lt;span&gt;x0 and x1 left unordered&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure class="pg-panel"&gt;&lt;div class="pg-graph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="14 208 256 36" width="100%" font-family="Inter, system-ui, sans-serif" role="img" aria-label="DirectLiNGAM: x0 -&amp;gt; x1, x0 -&amp;gt; x3, x1 -&amp;gt; x2."&gt;&lt;line x1="111.00" y1="226.40" x2="163.00" y2="226.40" stroke="var(--andrey-categorical-2)" stroke-width="2.0" stroke-linecap="round" class="wrong"/&gt;&lt;polygon points="173.00,226.40 163.00,221.40 163.00,231.40" fill="var(--andrey-categorical-2)" stroke-linejoin="round"/&gt;&lt;circle class="highlight" cx="168.00" cy="226.40" r="9.00" fill="none" stroke="var(--andrey-categorical-2)" stroke-width="2.0"/&gt;&lt;line x1="85.00" y1="226.40" x2="55.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="45.00,226.40 55.00,231.40 55.00,221.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;line x1="199.00" y1="226.40" x2="229.00" y2="226.40" stroke="var(--andrey-edge-directed)" stroke-width="2.0" stroke-linecap="round"/&gt;&lt;polygon points="239.00,226.40 229.00,221.40 229.00,231.40" fill="var(--andrey-edge-directed)" stroke-linejoin="round"/&gt;&lt;circle cx="98.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="98.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x0&lt;/text&gt;&lt;circle cx="186.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="186.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x1&lt;/text&gt;&lt;circle cx="252.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="252.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x2&lt;/text&gt;&lt;circle cx="32.00" cy="226.40" r="13.00" fill="var(--andrey-surface)" stroke="var(--andrey-ink)" stroke-width="1.8"/&gt;&lt;text x="32.00" y="226.40" text-anchor="middle" font-size="11" font-weight="600" fill="var(--andrey-ink)" dominant-baseline="central"&gt;x3&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;&lt;strong&gt;DirectLiNGAM&lt;/strong&gt;&lt;span&gt;a wrong edge, x0 -&amp;gt; x1&lt;/span&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&lt;figcaption class="pg-note"&gt;Every node in the same place: h is hidden (dashed), and DirectLiNGAM's x0 -&amp;gt; x1 (amber) is not in the true graph.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;RCD returns the true graph: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt; for the pair linked by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;h&lt;/span&gt;&lt;/code&gt;, and both direct
edges.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;BottomUpParceLiNGAM finds both direct edges. It cannot order &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt;, which share &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;h&lt;/span&gt;&lt;/code&gt;,
so it groups them in one block joined by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;o-o&lt;/span&gt;&lt;/code&gt;. In &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;causal_order&lt;/span&gt;&lt;/code&gt;, this block appears as the
nested list &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;[0,&lt;/span&gt; &lt;span class="pre"&gt;1]&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FCI finds the three edges but leaves every end a circle. Its orientations rely on
conditional independences. These four variables have the same independences as a graph with
no hidden cause and no collider (a variable that two arrows point into), such as
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x3&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="pre"&gt;x0&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x1&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x2&lt;/span&gt;&lt;/code&gt;, so the independences cannot settle any mark. Given an observed cause
of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt;&lt;/code&gt; and another of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt;, FCI can mark the pair &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;RCD and BottomUpParceLiNGAM also test whether regression residuals are independent.
With non-Gaussian noise, this test settles the directions in this example. RCD and
BottomUpParceLiNGAM also estimate the edge weights: 0.598 and 0.61, compared with the true 0.6.
DirectLiNGAM assumes causal sufficiency. On the same data, it returns &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x0&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;x1&lt;/span&gt;&lt;/code&gt; (the last graph
above), an edge absent from the true graph.&lt;/p&gt;
&lt;p&gt;The script prints each result in full:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;FCI  pag  |  4 nodes  |  3 edges (3 circle)

  x0 o-o x1
  x0 o-o x3
  x1 o-o x2

RCD  pag  |  4 nodes  |  3 edges (1 bidirected, 2 directed)

  x0 &amp;lt;-&amp;gt; x1
  x0 -&amp;gt; x3  (+0.598)
  x1 -&amp;gt; x2  (+0.61)

  confounded_pairs: [[0, 1]]

BottomUpParceLiNGAM  pag  |  4 nodes  |  3 edges (1 circle, 2 directed)

  x0 o-o x1
  x0 -&amp;gt; x3  (+0.598)
  x1 -&amp;gt; x2  (+0.61)

  causal_order: [[0, 1], 3, 2]

DirectLiNGAM  dag  |  4 nodes  |  3 edges (3 directed)

  x0 -&amp;gt; x1  (+0.686)
  x0 -&amp;gt; x3  (+0.598)
  x1 -&amp;gt; x2  (+0.61)

  order: x0 -&amp;gt; x1 -&amp;gt; x2 -&amp;gt; x3
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="benchmarks"&gt;
&lt;h2&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;We compared the three supported methods with packages that implement them.&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head nowrap"&gt;&lt;p&gt;Method&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Compared with&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Variables&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Andrey’s speedup&lt;/p&gt;&lt;/th&gt;
&lt;th class="head nowrap"&gt;&lt;p&gt;Same graph&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;RCD&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;lingam&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;4-24 observed&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;1.7-2.9x&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;25 of 25 datasets&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;BottomUpParceLiNGAM&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;lingam&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;4-24 observed&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;2.5-10.8x&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;25 of 25 datasets&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="nowrap"&gt;&lt;p&gt;FCI&lt;/p&gt;&lt;/td&gt;
&lt;td class="nowrap"&gt;&lt;p&gt;causal-learn&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;20-800, hidden ones included&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;3.4-16.5x at 50 to 800; 0.62x at 20&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;13 of 18 datasets&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a class="reference internal" href="docs/benchmarks/0.2.0.html"&gt;&lt;span class="std std-doc"&gt;0.2.0 release card&lt;/span&gt;&lt;/a&gt; reports measurements for RCD and
BottomUpParceLiNGAM on linear data with uniform noise, one node in five hidden, and 2,000 rows,
on 2 cores.&lt;/p&gt;
&lt;style&gt;@supports selector(:has(a)) { .race020 .race-panels { display: grid; } .race020 .race-panel { grid-area: 1 / 1; visibility: hidden; } .race020:has(#race020-m-rcd:checked) #race020-panel-rcd, .race020:has(#race020-m-bottomupparcelingam:checked) #race020-panel-bottomupparcelingam { visibility: visible; } } @supports not selector(:has(a)) { .race020 .race-panel::before { content: attr(aria-label); display: block; margin-top: 1rem; font-size: 0.85rem; font-weight: 600; } }&lt;/style&gt;&lt;figure class="race race--speedup race020" aria-labelledby="race020-title"&gt;&lt;figcaption class="race-head"&gt;&lt;span class="race-title" id="race020-title"&gt;Speedup over lingam&lt;/span&gt;&lt;a class="race-link" href="../docs/benchmarks/0.2.0.html"&gt;Release card&lt;/a&gt;&lt;/figcaption&gt;&lt;fieldset class="race-sizes"&gt;&lt;legend class="visually-hidden"&gt;Method&lt;/legend&gt;&lt;input class="race-choice" type="radio" name="race020-method" id="race020-m-rcd" checked&gt;&lt;label for="race020-m-rcd"&gt;RCD&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race020-method" id="race020-m-bottomupparcelingam"&gt;&lt;label for="race020-m-bottomupparcelingam"&gt;BottomUpParceLiNGAM&lt;/label&gt;&lt;/fieldset&gt;&lt;div class="race-panels"&gt;&lt;div class="race-panel" id="race020-panel-rcd" role="group" aria-label="RCD: Andrey&amp;#x27;s speedup over lingam at 4, 8, 12, 16, and 24 observed variables: 1.66x, 2.35x, 2.17x, 2.88x, 2.55x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;4 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.576389"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.66x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;8 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.815972"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.35x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;12 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.753472"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.17x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;16 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.88x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;24 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.885417"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.55x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race020-panel-bottomupparcelingam" role="group" aria-label="BottomUpParceLiNGAM: Andrey&amp;#x27;s speedup over lingam at 4, 8, 12, 16, and 24 observed variables: 2.48x, 5.08x, 5.68x, 6.61x, 10.8x"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;4 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.229630"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;2.48x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 1"&gt;&lt;span class="race-name"&gt;8 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.470370"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;5.08x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 2"&gt;&lt;span class="race-name"&gt;12 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.525926"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;5.68x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 3"&gt;&lt;span class="race-name"&gt;16 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.612037"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;6.61x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row race-row--andrey" style="--i: 4"&gt;&lt;span class="race-name"&gt;24 observed variables&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;10.8x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="race-note"&gt;Each bar is lingam's time divided by Andrey's on the same dataset, the median over five datasets, on 2 cores. Each method's bars share its own scale.&lt;/p&gt;&lt;/figure&gt;
&lt;p&gt;Agreement between packages does not establish accuracy: from 8 observed variables,
BottomUpParceLiNGAM’s directed edges have more errors than the empty graph’s, in both packages.&lt;/p&gt;
&lt;style&gt;.post-errors { margin: 2rem 0; padding: clamp(1rem, 2.5vw, 1.5rem); border: 1px solid var(--andrey-line); border-radius: 12px; background: var(--andrey-surface); container-type: inline-size; }.post-errors .pe-title { display: block; margin: 0; font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.02em; }.post-errors .tc-legend { margin: 0.5rem 0 1rem; }.post-errors .pe-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; }.post-errors .tchart { padding: 0; border: 0; border-radius: 0; background: none; }.post-errors .tchart-name span { display: block; }.post-errors .pe-key-band i { height: 0.7rem; border: 0; background: color-mix(in srgb, var(--andrey-categorical-2) 32%, transparent); }.post-errors .pe-band { fill: color-mix(in srgb, var(--andrey-categorical-2) 32%, transparent); }.post-errors svg .pe-end { fill: var(--andrey-ink); font-weight: 600; }.post-errors .pe-note { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--andrey-line); color: var(--andrey-muted); font-size: 0.8rem; line-height: 1.65; text-align: start; }@container (max-width: 34rem) { .post-errors .pe-grid { grid-template-columns: minmax(0, 1fr); } }&lt;/style&gt;&lt;figure class="post-errors" aria-labelledby="pe-title"&gt;&lt;p class="pe-title" id="pe-title"&gt;Directed-edge errors&lt;/p&gt;&lt;p class="tc-legend"&gt;&lt;span class="tc-key tc-andrey"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Andrey and lingam&lt;/span&gt;&lt;span class="tc-key tc-other-1"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;Empty graph&lt;/span&gt;&lt;span class="tc-key pe-key-band"&gt;&lt;i aria-hidden="true"&gt;&lt;/i&gt;More errors than the empty graph&lt;/span&gt;&lt;/p&gt;&lt;div class="pe-grid"&gt;&lt;figure class="tchart"&gt;&lt;figcaption&gt;&lt;span class="tchart-name"&gt;&lt;strong&gt;RCD&lt;/strong&gt;&lt;span&gt;At or below the empty graph&amp;#x27;s errors at every size&lt;/span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;svg viewBox="0 0 320 214" role="img" aria-label="RCD: median directed-edge errors by observed variables, Andrey and lingam against the empty graph. 4: 0 against 3; 8: 4 against 4; 12: 6 against 8; 16: 8 against 11; 24: 11 against 19."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="30" x2="290" y1="184.0" y2="184.0" class="tc-grid"/&gt;&lt;text x="24" y="187.0" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="30" x2="290" y1="142.0" y2="142.0" class="tc-grid"/&gt;&lt;text x="24" y="145.0" text-anchor="end"&gt;10&lt;/text&gt;&lt;line x1="30" x2="290" y1="100.0" y2="100.0" class="tc-grid"/&gt;&lt;text x="24" y="103.0" text-anchor="end"&gt;20&lt;/text&gt;&lt;line x1="30" x2="290" y1="58.0" y2="58.0" class="tc-grid"/&gt;&lt;text x="24" y="61.0" text-anchor="end"&gt;30&lt;/text&gt;&lt;line x1="30" x2="290" y1="16.0" y2="16.0" class="tc-grid"/&gt;&lt;text x="24" y="19.0" text-anchor="end"&gt;40&lt;/text&gt;&lt;text x="30.0" y="198" text-anchor="middle"&gt;4&lt;/text&gt;&lt;text x="82.0" y="198" text-anchor="middle"&gt;8&lt;/text&gt;&lt;text x="134.0" y="198" text-anchor="middle"&gt;12&lt;/text&gt;&lt;text x="186.0" y="198" text-anchor="middle"&gt;16&lt;/text&gt;&lt;text x="290.0" y="198" text-anchor="middle"&gt;24&lt;/text&gt;&lt;/g&gt;&lt;text class="tc-title" x="30" y="213"&gt;observed variables&lt;/text&gt;&lt;polyline class="tc-line tc-other tc-other-1" points="30.0,171.4 82.0,167.2 134.0,150.4 186.0,137.8 290.0,104.2"/&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="30.0" cy="171.4" r="3.5"&gt;&lt;title&gt;Empty graph, 4 observed variables: 3 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="82.0" cy="167.2" r="3.5"&gt;&lt;title&gt;Empty graph, 8 observed variables: 4 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="134.0" cy="150.4" r="3.5"&gt;&lt;title&gt;Empty graph, 12 observed variables: 8 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="186.0" cy="137.8" r="3.5"&gt;&lt;title&gt;Empty graph, 16 observed variables: 11 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="290.0" cy="104.2" r="3.5"&gt;&lt;title&gt;Empty graph, 24 observed variables: 19 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="107.2"&gt;19&lt;/text&gt;&lt;polyline class="tc-line tc-andrey" points="30.0,184.0 82.0,167.2 134.0,158.8 186.0,150.4 290.0,137.8"/&gt;&lt;circle class="tc-point tc-andrey" cx="30.0" cy="184.0" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 4 observed variables: 0 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="82.0" cy="167.2" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 8 observed variables: 4 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="134.0" cy="158.8" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 12 observed variables: 6 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="186.0" cy="150.4" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 16 observed variables: 8 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="290.0" cy="137.8" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 24 observed variables: 11 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="140.8"&gt;11&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;figure class="tchart"&gt;&lt;figcaption&gt;&lt;span class="tchart-name"&gt;&lt;strong&gt;BottomUpParceLiNGAM&lt;/strong&gt;&lt;span&gt;More errors than the empty graph from 8 observed variables&lt;/span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;svg viewBox="0 0 320 214" role="img" aria-label="BottomUpParceLiNGAM: median directed-edge errors by observed variables, Andrey and lingam against the empty graph. 4: 1 against 3; 8: 11 against 4; 12: 17 against 8; 16: 22 against 11; 24: 40 against 19."&gt;&lt;g class="tc-axis"&gt;&lt;line x1="30" x2="290" y1="184.0" y2="184.0" class="tc-grid"/&gt;&lt;text x="24" y="187.0" text-anchor="end"&gt;0&lt;/text&gt;&lt;line x1="30" x2="290" y1="142.0" y2="142.0" class="tc-grid"/&gt;&lt;text x="24" y="145.0" text-anchor="end"&gt;10&lt;/text&gt;&lt;line x1="30" x2="290" y1="100.0" y2="100.0" class="tc-grid"/&gt;&lt;text x="24" y="103.0" text-anchor="end"&gt;20&lt;/text&gt;&lt;line x1="30" x2="290" y1="58.0" y2="58.0" class="tc-grid"/&gt;&lt;text x="24" y="61.0" text-anchor="end"&gt;30&lt;/text&gt;&lt;line x1="30" x2="290" y1="16.0" y2="16.0" class="tc-grid"/&gt;&lt;text x="24" y="19.0" text-anchor="end"&gt;40&lt;/text&gt;&lt;text x="30.0" y="198" text-anchor="middle"&gt;4&lt;/text&gt;&lt;text x="82.0" y="198" text-anchor="middle"&gt;8&lt;/text&gt;&lt;text x="134.0" y="198" text-anchor="middle"&gt;12&lt;/text&gt;&lt;text x="186.0" y="198" text-anchor="middle"&gt;16&lt;/text&gt;&lt;text x="290.0" y="198" text-anchor="middle"&gt;24&lt;/text&gt;&lt;/g&gt;&lt;polygon class="pe-band" points="41.6,170.5 82.0,137.8 82.0,167.2 41.6,170.5"/&gt;&lt;polygon class="pe-band" points="82.0,137.8 134.0,112.6 134.0,150.4 82.0,167.2"/&gt;&lt;polygon class="pe-band" points="134.0,112.6 186.0,91.6 186.0,137.8 134.0,150.4"/&gt;&lt;polygon class="pe-band" points="186.0,91.6 290.0,16.0 290.0,104.2 186.0,137.8"/&gt;&lt;text class="tc-title" x="30" y="213"&gt;observed variables&lt;/text&gt;&lt;polyline class="tc-line tc-other tc-other-1" points="30.0,171.4 82.0,167.2 134.0,150.4 186.0,137.8 290.0,104.2"/&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="30.0" cy="171.4" r="3.5"&gt;&lt;title&gt;Empty graph, 4 observed variables: 3 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="82.0" cy="167.2" r="3.5"&gt;&lt;title&gt;Empty graph, 8 observed variables: 4 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="134.0" cy="150.4" r="3.5"&gt;&lt;title&gt;Empty graph, 12 observed variables: 8 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="186.0" cy="137.8" r="3.5"&gt;&lt;title&gt;Empty graph, 16 observed variables: 11 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-other tc-other-1" cx="290.0" cy="104.2" r="3.5"&gt;&lt;title&gt;Empty graph, 24 observed variables: 19 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="107.2"&gt;19&lt;/text&gt;&lt;polyline class="tc-line tc-andrey" points="30.0,179.8 82.0,137.8 134.0,112.6 186.0,91.6 290.0,16.0"/&gt;&lt;circle class="tc-point tc-andrey" cx="30.0" cy="179.8" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 4 observed variables: 1 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="82.0" cy="137.8" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 8 observed variables: 11 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="134.0" cy="112.6" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 12 observed variables: 17 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="186.0" cy="91.6" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 16 observed variables: 22 errors&lt;/title&gt;&lt;/circle&gt;&lt;circle class="tc-point tc-andrey" cx="290.0" cy="16.0" r="3.5"&gt;&lt;title&gt;Andrey and lingam, 24 observed variables: 40 errors&lt;/title&gt;&lt;/circle&gt;&lt;text class="pe-end" x="297" y="19.0"&gt;40&lt;/text&gt;&lt;/svg&gt;&lt;/figure&gt;&lt;/div&gt;&lt;figcaption class="pe-note"&gt;The median over five datasets per size; Andrey and lingam return the same graphs, so they share one line. Lower is better.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;FCI’s numbers come from the &lt;a class="reference internal" href="docs/benchmarks/0.1.0.html"&gt;&lt;span class="std std-doc"&gt;0.1.0 release card&lt;/span&gt;&lt;/a&gt;: linear-Gaussian
data with one variable in ten hidden and ten rows per variable, on 16 cores. At 20 variables,
Andrey is slower, but both packages take under 0.1 s. Their median endpoint errors are within
two of each other. At 800 variables, both have more endpoint errors than the empty graph.&lt;/p&gt;
&lt;p&gt;Speed and accuracy depend on your data, settings, and hardware.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="other-highlights"&gt;
&lt;h2&gt;Other highlights&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster small fits.&lt;/strong&gt; PC, FCI, GES, and hill climbing now find the Basic Linear Algebra
Subprograms (BLAS) libraries once per process instead of on every fit. At 10 variables, PC
and FCI take about a twelfth of the time they took before, hill climbing an eighth, and GES
two fifths. Larger fits are no slower, and the graphs are unchanged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Release cards.&lt;/strong&gt; The &lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt; page has one card per release:
its benchmarks, the methods it adds or promotes, their known limits, and how to rerun the
benchmarks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limitations.&lt;/strong&gt; Every method’s docstring has a Limitations paragraph stating where the method
was measured to work or that it is not benchmarked yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Checks before the fit.&lt;/strong&gt; Every method checks your data and settings before fitting. Data
with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NaN&lt;/span&gt;&lt;/code&gt; or infinity, a constant column, or fewer than 2 rows raises a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ValueError&lt;/span&gt;&lt;/code&gt; that
names the argument. An out-of-range setting raises the same error.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details, read the &lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;changelog&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-it"&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Two new notebooks fit &lt;a class="reference internal" href="docs/examples/rcd.html"&gt;&lt;span class="doc std std-doc"&gt;RCD&lt;/span&gt;&lt;/a&gt; and
&lt;a class="reference internal" href="docs/examples/bottom_up_parce_lingam.html"&gt;&lt;span class="doc std std-doc"&gt;BottomUpParceLiNGAM&lt;/span&gt;&lt;/a&gt; on the example’s model, read the
edge weights, and check the result over ten datasets. You can open each notebook in Colab, then
try the methods on your own data.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="help-us-build-it"&gt;
&lt;h2&gt;Help us build it&lt;/h2&gt;
&lt;p&gt;Andrey is in alpha, and the API may change between releases. When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need.
We also welcome pull requests. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; explains how
to set up.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Release cards&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/changelog.html"&gt;&lt;span class="std std-doc"&gt;Changelog&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261012-andrey-0-2-0.html"/>
    <summary>A hidden common cause, or latent confounder, is an unmeasured variable that affects two measured
variables. This release adds RCD and BottomUpParceLiNGAM as supported methods. With FCI, they
cover data with hidden common causes. Below, we run all three on one small dataset and report
benchmark results. Other updates make small fits several times faster.</summary>
    <category term="Latentvariables" label="Latent variables"/>
    <category term="Release" label="Release"/>
    <published>2026-10-12T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261008-why-andrey-is-fast.html</id>
    <title>Why Andrey is fast</title>
    <updated>2026-10-08T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="why-andrey-is-fast"&gt;

&lt;p&gt;Most of the time in causal discovery goes to two jobs: testing whether variables are independent
(PC and FCI) and scoring candidate graphs (GES and BOSS). We speed up both with four ideas,
easiest first. Each animation runs the same work in two lanes: Andrey’s, and one labeled Others
that does it one step at a time. They are illustrations of the ideas, not measurements; the
&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;benchmarks&lt;/span&gt;&lt;/a&gt; have the measurements.&lt;/p&gt;
&lt;section id="do-many-things-at-once"&gt;
&lt;h2&gt;1. Do many things at once&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Batch and parallelize.&lt;/strong&gt; Within one round of PC, each independence test stands alone, and within
one pass of GES, so does each candidate move. Andrey runs them as one batched array operation,
thousands at a time, with the same results as one at a time.&lt;/p&gt;
&lt;figure class="wf wf-scene" data-scene="parallel" data-play="view" data-n="8" data-unit="call calls"&gt;&lt;div class="wf-stage"&gt;&lt;div class="wf-lanes" aria-hidden="true"&gt;&lt;div class="wf-lane andrey"&gt;&lt;p class="wf-who"&gt;Andrey&lt;small&gt;one call for the batch&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;1&lt;/b&gt;&lt;small&gt;call&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="wf-lane others"&gt;&lt;p class="wf-who"&gt;Others&lt;small&gt;one call per test&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;8&lt;/b&gt;&lt;small&gt;calls&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="wf-sr"&gt;Andrey: 1 call, one call for the batch. Others: 8 calls, one call per test.&lt;/p&gt;&lt;div class="wf-after"&gt;&lt;button class="wf-replay" type="button" aria-label="Replay" hidden&gt;&lt;svg class="i-replay" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M3.2 8a4.8 4.8 0 1 0 1.5-3.5" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;path d="M4.4 1.9v2.9h2.9" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="never-compute-twice"&gt;
&lt;h2&gt;2. Never compute twice&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Cache and update incrementally.&lt;/strong&gt; Andrey computes the correlation matrix once, up front, and
caches it. Each test reads the few entries it needs instead of recomputing them, and GES memoizes
its local scores the same way.&lt;/p&gt;
&lt;figure class="wf wf-scene" data-scene="reuse" data-play="view" data-n="6" data-unit="computation computations"&gt;&lt;div class="wf-stage"&gt;&lt;div class="wf-lanes" aria-hidden="true"&gt;&lt;div class="wf-lane andrey"&gt;&lt;p class="wf-who"&gt;Andrey&lt;small&gt;computed once, then read&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;1&lt;/b&gt;&lt;small&gt;computation&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="wf-lane others"&gt;&lt;p class="wf-who"&gt;Others&lt;small&gt;recomputed for each test&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;6&lt;/b&gt;&lt;small&gt;computations&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="wf-sr"&gt;Andrey: 1 computation, computed once, then read. Others: 6 computations, recomputed for each test.&lt;/p&gt;&lt;div class="wf-after"&gt;&lt;button class="wf-replay" type="button" aria-label="Replay" hidden&gt;&lt;svg class="i-replay" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M3.2 8a4.8 4.8 0 1 0 1.5-3.5" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;path d="M4.4 1.9v2.9h2.9" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="skip-what-can-t-matter"&gt;
&lt;h2&gt;3. Skip what can’t matter&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prune the search early.&lt;/strong&gt; Tests given zero or one other variable have a closed form, so a quick
pass settles most edges before any matrix is inverted. GES rules out invalid candidates with bit
masks before scoring them.&lt;/p&gt;
&lt;figure class="wf wf-scene" data-scene="skip" data-play="view" data-n="10" data-costly="2 6 9" data-unit="inversion inversions"&gt;&lt;div class="wf-stage"&gt;&lt;div class="wf-lanes" aria-hidden="true"&gt;&lt;div class="wf-lane andrey"&gt;&lt;p class="wf-who"&gt;Andrey&lt;small&gt;a quick check first&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;3&lt;/b&gt;&lt;small&gt;inversions&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="wf-lane others"&gt;&lt;p class="wf-who"&gt;Others&lt;small&gt;every test inverts a matrix&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;10&lt;/b&gt;&lt;small&gt;inversions&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="wf-sr"&gt;Andrey: 3 inversions, a quick check first. Others: 10 inversions, every test inverts a matrix.&lt;/p&gt;&lt;div class="wf-after"&gt;&lt;button class="wf-replay" type="button" aria-label="Replay" hidden&gt;&lt;svg class="i-replay" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M3.2 8a4.8 4.8 0 1 0 1.5-3.5" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;path d="M4.4 1.9v2.9h2.9" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="use-the-hardware-you-have"&gt;
&lt;h2&gt;4. Use the hardware you have&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Hardware-aware acceleration.&lt;/strong&gt; With the optional extras, Numba JIT-compiles GES’s path checks, and
a CUDA GPU computes large entropy tables many cells at a time, once they are large enough to repay
the transfer. Correlation matrices move to the GPU too when you set &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ANDREY_BACKEND=cuda&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;figure class="wf wf-scene" data-scene="hardware" data-play="view" data-rows="4" data-cols="12" data-waves="3" data-unit="step steps"&gt;&lt;div class="wf-stage"&gt;&lt;div class="wf-lanes" aria-hidden="true"&gt;&lt;div class="wf-lane andrey"&gt;&lt;p class="wf-who"&gt;Andrey&lt;small&gt;a GPU, many cells per step&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;3&lt;/b&gt;&lt;small&gt;steps&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="wf-lane others"&gt;&lt;p class="wf-who"&gt;Others&lt;small&gt;one core, one cell per step&lt;/small&gt;&lt;/p&gt;&lt;div class="wf-track"&gt;&lt;/div&gt;&lt;p class="wf-count"&gt;&lt;span class="wf-done"&gt;&lt;svg class="i-check" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M4 8.4l2.6 2.6L12 5.4" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;b&gt;48&lt;/b&gt;&lt;small&gt;steps&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class="wf-sr"&gt;Andrey: 3 steps, a GPU, many cells per step. Others: 48 steps, one core, one cell per step.&lt;/p&gt;&lt;div class="wf-after"&gt;&lt;button class="wf-replay" type="button" aria-label="Replay" hidden&gt;&lt;svg class="i-replay" viewBox="0 0 16 16" aria-hidden="true"&gt;&lt;path d="M3.2 8a4.8 4.8 0 1 0 1.5-3.5" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;path d="M4.4 1.9v2.9h2.9" stroke-width="1.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/&gt;&lt;/svg&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261008-why-andrey-is-fast.html"/>
    <summary>Most of the time in causal discovery goes to two jobs: testing whether variables are independent
(PC and FCI) and scoring candidate graphs (GES and BOSS). We speed up both with four ideas,
easiest first. Each animation runs the same work in two lanes: Andrey’s, and one labeled Others
that does it one step at a time. They are illustrations of the ideas, not measurements; the
benchmarks have the measurements.</summary>
    <category term="Engineering" label="Engineering"/>
    <category term="Speed" label="Speed"/>
    <published>2026-10-08T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://andrey.abel.ai/blog/20261008-introducing-andrey.html</id>
    <title>Introducing Andrey</title>
    <updated>2026-10-08T00:00:00+00:00</updated>
    <author>
      <name>Shu Wan</name>
    </author>
    <content type="html">&lt;section class="tex2jax_ignore mathjax_ignore" id="introducing-andrey"&gt;

&lt;p&gt;We’re happy to introduce Andrey, a very fast causal discovery package for Python.&lt;/p&gt;
&lt;p&gt;Andrey is named after Andrey Markov and Andrey Kolmogorov, whose ideas causal discovery still runs
on (&lt;a class="reference internal" href="docs/faq.html#where-does-the-name-andrey-come-from"&gt;&lt;span class="std std-ref"&gt;where the name comes from&lt;/span&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Most data analysis tells you which variables move together. Causal discovery learns from data which
ones drive which, and draws them as a graph of causes. As AI systems take on more analysis and make
decisions of their own, they need to know what causes what.&lt;/p&gt;
&lt;p&gt;For a long time, scale has held causal discovery back. The methods are well understood, but popular
Python packages run them slowly. PC on 400 variables took 617 s in causal-learn. At that speed,
trying a new idea takes time, and data with thousands of variables is out of reach.&lt;/p&gt;
&lt;p&gt;We built Andrey to make causal discovery fast and easy to use. It runs the same methods faster
(over 100x on PC).&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fast&lt;/strong&gt;: Over 100x faster on PC, and faster on most other supported methods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Just as accurate&lt;/strong&gt;: the same errors or fewer in 38 of 42 comparisons with other packages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One API&lt;/strong&gt;: 18 methods, each one function call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent-ready&lt;/strong&gt;: a command line that answers in JSON, and a skill for coding agents.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;style&gt;@supports selector(:has(a)) { .race-panels { display: grid; } .race-panel { grid-area: 1 / 1; visibility: hidden; } .race-size { display: none; } .race:has(#race-m-pc:checked) :is(#race-panel-pc, #race-size-pc), .race:has(#race-m-fci:checked) :is(#race-panel-fci, #race-size-fci), .race:has(#race-m-ges:checked) :is(#race-panel-ges, #race-size-ges), .race:has(#race-m-boss:checked) :is(#race-panel-boss, #race-size-boss), .race:has(#race-m-grasp:checked) :is(#race-panel-grasp, #race-size-grasp), .race:has(#race-m-directlingam:checked) :is(#race-panel-directlingam, #race-size-directlingam) { visibility: visible; display: revert; } } @supports not selector(:has(a)) { .race--speedup .race-sizes-now { display: none; } .race--speedup .race-panel::before { content: attr(aria-label); display: block; margin-top: 1rem; font-size: 0.85rem; font-weight: 600; } }&lt;/style&gt;&lt;figure class="race race--speedup" aria-labelledby="race-title"&gt;&lt;figcaption class="race-head"&gt;&lt;span class="race-title" id="race-title"&gt;Speedup&lt;span class="race-sizes-now"&gt; &lt;span aria-hidden="true"&gt;&amp;middot;&lt;/span&gt; &lt;span class="race-size" id="race-size-pc"&gt;400 variables&lt;/span&gt;&lt;span class="race-size" id="race-size-fci"&gt;800 variables&lt;/span&gt;&lt;span class="race-size" id="race-size-ges"&gt;150 variables&lt;/span&gt;&lt;span class="race-size" id="race-size-boss"&gt;800 variables&lt;/span&gt;&lt;span class="race-size" id="race-size-grasp"&gt;400 variables&lt;/span&gt;&lt;span class="race-size" id="race-size-directlingam"&gt;200 variables&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;a class="race-link" href="../docs/benchmarks.html"&gt;Benchmark report&lt;/a&gt;&lt;/figcaption&gt;&lt;fieldset class="race-sizes"&gt;&lt;legend class="visually-hidden"&gt;Method&lt;/legend&gt;&lt;input class="race-choice" type="radio" name="race-method" id="race-m-pc" checked&gt;&lt;label for="race-m-pc"&gt;PC&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race-method" id="race-m-fci"&gt;&lt;label for="race-m-fci"&gt;FCI&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race-method" id="race-m-ges"&gt;&lt;label for="race-m-ges"&gt;GES&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race-method" id="race-m-boss"&gt;&lt;label for="race-m-boss"&gt;BOSS&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race-method" id="race-m-grasp"&gt;&lt;label for="race-m-grasp"&gt;GRaSP&lt;/label&gt;&lt;input class="race-choice" type="radio" name="race-method" id="race-m-directlingam"&gt;&lt;label for="race-m-directlingam"&gt;DirectLiNGAM&lt;/label&gt;&lt;/fieldset&gt;&lt;div class="race-panels"&gt;&lt;div class="race-panel" id="race-panel-pc" role="group" aria-label="PC, 400 variables: speed relative to the slowest package"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;Andrey&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;941x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row" style="--i: 1"&gt;&lt;span class="race-name"&gt;gCastle&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.003944"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;3.7x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row" style="--i: 2"&gt;&lt;span class="race-name"&gt;causal-learn&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.001063"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race-panel-fci" role="group" aria-label="FCI, 800 variables: speed relative to the slowest package"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;Andrey&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;8.7x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row" style="--i: 1"&gt;&lt;span class="race-name"&gt;causal-learn&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.115607"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race-panel-ges" role="group" aria-label="GES, 150 variables: speed relative to the slowest package"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;Andrey&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;4.3x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row" style="--i: 1"&gt;&lt;span class="race-name"&gt;causal-learn&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.233645"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race-panel-boss" role="group" aria-label="BOSS, 800 variables: speed relative to the slowest package"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;Andrey&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;3.4x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row" style="--i: 1"&gt;&lt;span class="race-name"&gt;causal-learn&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.292398"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race-panel-grasp" role="group" aria-label="GRaSP, 400 variables: speed relative to the slowest package"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;Andrey&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1.4x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row" style="--i: 1"&gt;&lt;span class="race-name"&gt;causal-learn&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.704225"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="race-panel" id="race-panel-directlingam" role="group" aria-label="DirectLiNGAM, 200 variables: speed relative to the slowest package"&gt;&lt;div class="race-row race-row--andrey" style="--i: 0"&gt;&lt;span class="race-name"&gt;Andrey&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 1.000000"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;3.3x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="race-row" style="--i: 1"&gt;&lt;span class="race-name"&gt;causal-learn&lt;/span&gt;&lt;span class="race-track"&gt;&lt;span class="race-bar" style="--f: 0.303030"&gt;&lt;span class="race-fill"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="race-time"&gt;1x&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a class="reference internal" href="blog/20261008-why-andrey-is-fast.html"&gt;&lt;span class="std std-doc"&gt;Why Andrey is fast&lt;/span&gt;&lt;/a&gt; explains the four ideas behind the speed. Andrey
is free and open source, and so are its &lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;benchmarks&lt;/span&gt;&lt;/a&gt;, which you can rerun
yourself.&lt;/p&gt;
&lt;section id="what-s-next"&gt;
&lt;h2&gt;What’s next&lt;/h2&gt;
&lt;p&gt;We plan to add more methods, including more for time series. We also plan to add local causal
discovery, which finds the structure around one variable, such as its direct causes and effects or
its Markov blanket, without learning the whole graph. And we want to use sparsity to scale causal
discovery to millions of variables.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="try-andrey"&gt;
&lt;h2&gt;Try Andrey&lt;/h2&gt;
&lt;div class="window highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;andrey-core
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then run PC on a real dataset:&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;andrey.data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sachs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;andrey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PC  cpdag  |  11 nodes  |  8 edges (6 undirected, 2 directed)

  raf -- mek
  plc -- pip3
  pip2 -- pip3
  erk -- akt
  erk -- pka
  akt -- pka
  p38 -&amp;gt; pkc
  jnk -&amp;gt; pkc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="help-us-build-it"&gt;
&lt;h2&gt;Help us build it&lt;/h2&gt;
&lt;p&gt;Andrey 0.1.0 is in alpha, the API may change between releases.&lt;/p&gt;
&lt;p&gt;When you try it on your own data,
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/issues/new"&gt;tell us what breaks&lt;/a&gt; or which method you need.
We’d welcome a pull request too. The
&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md"&gt;contributing guide&lt;/a&gt; explains how
to set up.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/guides/getting-started.html"&gt;&lt;span class="std std-doc"&gt;Getting started&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/examples/index.html"&gt;&lt;span class="std std-doc"&gt;Examples&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/demos/index.html"&gt;&lt;span class="std std-doc"&gt;Demos&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/benchmarks.html"&gt;&lt;span class="std std-doc"&gt;Benchmarks&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="docs/faq.html"&gt;&lt;span class="std std-doc"&gt;FAQ&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/Abel-ai-lab/andrey"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://andrey.abel.ai/blog/20261008-introducing-andrey.html"/>
    <summary>We’re happy to introduce Andrey, a very fast causal discovery package for Python.</summary>
    <category term="Release" label="Release"/>
    <category term="Speed" label="Speed"/>
    <published>2026-10-08T00:00:00+00:00</published>
  </entry>
</feed>
