{"id":301,"date":"2013-11-11T18:38:42","date_gmt":"2013-11-11T23:38:42","guid":{"rendered":"http:\/\/matroidunion.org\/?p=301"},"modified":"2014-05-24T09:30:52","modified_gmt":"2014-05-24T13:30:52","slug":"matroid-computation-with-sage-extensions","status":"publish","type":"post","link":"https:\/\/matroidunion.org\/?p=301","title":{"rendered":"Matroid Computation with Sage: Extensions"},"content":{"rendered":"<p>In this post I want to continue exploring the new-found capabilities of Sage with respect to matroid computation. It follows up on <a href=\"https:\/\/matroidunion.org\/?p=286\">this post<\/a>; Gordon Royle started a different sequence of posts on sage-matroids <a href=\"http:\/\/symomega.wordpress.com\/2013\/10\/16\/getting-started-with-sage_matroids-i\/\">here<\/a>. Following a comment by Jason Grout, I decided to experiment with the Sage Cell Server. The result? All computations in this post can be carried out live, by clicking the &#8220;Evaluate&#8221; button, and <strong>you can edit them to play with them<\/strong>! How awesome is that? A word of warning: the cells in each section of this post are linked together, so if you didn&#8217;t evaluate a previous cell, the ones below it might not work. For technical reasons, we use # signs on otherwise empty lines. These symbols (and anything following) are comments, and don&#8217;t do anything.<\/p>\n<p>The topic for today is matroid extensions.<!--more--><\/p>\n<h1>Generating extensions<\/h1>\n<p>It is very straightforward to generate all extensions of a fixed matroid. In this example, we specified the name of the new element to be &#8216;x&#8217;. If we hadn&#8217;t done that, the code would have chosen an arbitrary, previously unused, name for the new element.<\/p>\n<div class=\"sage1\"><script type=\"text\/x-sage\">\nM = matroids.named_matroids.Fano()\nS = M.extensions(element='x')\nprint S\n<\/script><\/div>\n<p><strong>Aside: <\/strong>The output looks strange; this is because we are provided with an <em>iterator<\/em>. This is a very nice feature of the Python language on which Sage was built: it is possible for code to construct the next item in a list of items only when that item is requested. This can have serious memory benefits. Suppose there are two billion gizmos, but you&#8217;re only interested in the 753 green ones. If you first generate the list of all gizmos, and then filter out the ones you need, you&#8217;ll have two billion of them stored in your computer&#8217;s memory halfway through the computation. If, instead, you generate them one by one and only keep the ones you want, you&#8217;ll never have more than 754 of them in memory.<\/p>\n<p>In this case, the list of extensions is quite manageable, so let&#8217;s keep them all. I&#8217;ll recycle the variable name <code>S<\/code>.<\/p>\n<div class=\"sage1\"><script type=\"text\/x-sage\">\nS = list(S)\nprint len(S)\n<\/script><\/div>\n<p>Let&#8217;s filter the list. Suppose we are only interested in the extensions that are simple:<\/p>\n<div class=\"sage1\"><script type=\"text\/x-sage\">\nS2 = [N for N in S if N.is_simple()]\nprint len(S2)\n<\/script><\/div>\n<p>Next, suppose we are only interested in the pairwise nonisomorphic matroids in this set. This is, in fact, such a common request that Sage has a built-in function for it. To get it, we import the advanced functionality:<\/p>\n<div class=\"sage1\"><script type=\"text\/x-sage\">\nfrom sage.matroids.advanced import *\nS3 = get_nonisomorphic_matroids(S2)\nprint len(S3)\n<\/script><\/div>\n<p>This answer makes sense: we can place the new element &#8216;x&#8217; either freely in the span of $F_7$, or freely on one of the lines of $F_7$. Note that each of these has a 5-point line minor:<\/p>\n<div class=\"sage1\"><script type=\"text\/x-sage\">\nfor N in S3:\n    if N.has_minor(matroids.Uniform(2,5)):\n        print \"True\"\n<\/script><\/div>\n<p>In view of <a href=\"https:\/\/matroidunion.org\/?p=250\">Peter&#8217;s posts<\/a> on growth rates, it makes sense to investigate matroids with no long line minors. As it happens, Sage has an option for that:<\/p>\n<div class=\"sage1\"><script type=\"text\/x-sage\">\nM = matroids.named_matroids.Fano()\nT = M.extensions(element='x', line_length=4)\nT2 = [N for N in T if N.is_simple()]\nprint len(T2)\n<\/script><\/div>\n<h1>Generating all matroids<\/h1>\n<p>A basic algorithm to generate all matroids of rank $r$ and corank at most $c$ is the following:<\/p>\n<div class=\"sage2\"><script type=\"text\/x-sage\">\nfrom sage.matroids.advanced import *\n#\ndef all_matroids_slow(r, c):\n    # Create the free matroid of rank r with groundset {0, ..., r-1}\n    M0 = Matroid(groundset=range(r), bases=[range(r)])\n    # Organize the output in a dictionary, with matroids grouped by (r,c)\n    D = {}\n    D[(r,0)] = [M0]  # A list containing only one matroid\n    for i in range(1, c + 1):\n        e = r + i  # new element label\n        L = []\n        for M in D[(r, i - 1)]:\n            L.extend(M.extensions(element=e))\n            # The first \"extend\" is a Python function\n            # to grow the list L by another list!\n        D[(r, i)] = get_nonisomorphic_matroids(L)\n    return D\n#\nMM = all_matroids_slow(3, 3)\nfor (r, c) in MM:\n    print \"Number of matroids of rank \", r, \" and corank \", c, \" is \", len(MM[(r,c)])\n<\/script><\/div>\n<p>Let&#8217;s see how much time it costs to generate all matroids of up to 7 elements:<\/p>\n<div class=\"sage2\"><script type=\"text\/x-sage\">\ndef all_matroids(n):\n    D = {}\n    for r in range(n + 1):\n        c = n - r\n        D.update(all_matroids_slow(r, c))\n    return D\n#\ntimeit('MM = all_matroids(7)', number=1, repeat=1)\n<\/script><\/div>\n<p>If we generate all matroids up to 8 elements in this way, it will take about 25 seconds, and you&#8217;d better not attempt 9 elements. The real time-killer here is the <code>get_nonisomorphic_matroids<\/code> function, which does a number of isomorphism tests that is quadratic in the length of its input. In the generation of combinatorial objects, we often try to cut down on this by way of a <strong>canonical labeling<\/strong>. For each matroid extension, we compute a matroid invariant in which some elements are distinguished. Suppose $M$ is a single-element extension of $N$. If the new element $e$ of $M$ is not distinguished, then we know that there exists a different matroid, $N&#8217;$, such that an extension of $N&#8217;$ is isomorphic to $M$, and has been (or will be) generated in another stage of the algorithm. In that case we need not even add $M$ to the temporary list!<\/p>\n<p>There is a slight complication: if $M$ contains coloops, then the only distinguished elements are the coloops! To get all matroids, then, we must change the order of generation. The matroids of rank $r$ and corank $c$ are created by adding a coloop to each matroid of rank $r-1$ and corank $c$, and by extending the matroids of rank $r$ and corank $c-1$.<\/p>\n<div class=\"sage2\"><script type=\"text\/x-sage\">\ndef all_matroids_faster(maxsize):\n    D = {}\n    D[(0,0)] = [Matroid(groundset=[], bases=[{}])]\n    for n in range(1, maxsize + 1):\n        e = n\n        D[(0,n)] = []\n        for r in range(n):\n            # extend:\n            for M in D[(r, n - 1 - r)]:\n                S = [N for N in M.extensions(element=e) \n                     if N.is_distinguished(e)]\n                for N in S:\n                    if not any(N.is_isomorphic(NN) for NN in D[(r, n - r)]):\n                        D[(r, n - r)].append(N)\n            # coextend by coloop:\n            D[(r + 1, n - 1 - r)] = [M._with_coloop(e) for M in D[(r, n - 1 - r)]]\n    return D\n#\ntimeit('MM = all_matroids_faster(7)', number=1, repeat=1)\n<\/script><\/div>\n<p>In fact, this code is fast enough to generate all matroids up to 8 elements. On the Sage cell server, this should take under 7 seconds; the entry in position $(r,c)$ of the table is the number of nonisomorphic matroids of rank $r$ and corank $c$.<\/p>\n<div class=\"sage2\"><script type=\"text\/x-sage\">\nMM = all_matroids_faster(8)\nfor r in range(9):\n    s = ''\n    for c in range(0, 9-r):\n        s = s + ' ' + str(len(MM[(r,c)]))\n    print s\n<\/script><\/div>\n<p>If you want 9 elements, be prepared to wait for much, much longer (2 days should do it). I advise you to run it from your own computer, and then save the result to disk through the command <code>save(MM, \"\/path\/to\/filename.sobj\")<\/code>. Next time you need them, just load them back through <code>MM = load(\"\/path\/to\/filename.sobj\")<\/code>.<\/p>\n<p>That&#8217;s all I want to say about general extensions for now. Sage has more advanced functionality in the <code>sage.matroids.extension<\/code> package; some documentation is <a href=\"http:\/\/www.sagemath.org\/doc\/reference\/matroids\/sage\/matroids\/extension.html\">here<\/a>. A more optimized algorithm to find all matroids can be found in <a href=\"https:\/\/bitbucket.org\/matroid\/sage_matroids\/src\/fc452a7f57523467bc098d558df420890e1b9c13\/speedtests.sage?at=default\">this file<\/a> (time to build all matroids up to 9 elements: about half an hour).<\/p>\n<h1>More control: linear subclasses<\/h1>\n<p>Suppose, next, that you don&#8217;t want just any extension, but you have more information. This information could be of the form &#8220;the new element needs to be in the span of the following subsets&#8221;. If so, you&#8217;re in luck: Sage has support for that. In the next example, since $a,b$ are in the closure of both specified subsets (as can be checked), every extension, such that $i$ is in the closure of each specified set, must place $i$ on the line through $a$ and $b$.<\/p>\n<div class=\"sage3\"><script type=\"text\/x-sage\">\nfrom sage.matroids.advanced import *\nM = matroids.named_matroids.P8()\nS = M.extensions(element='i', subsets=['bce', 'adf'])\nT = [N for N in S if N.is_simple()]\nfor N in T:\n    print N.bases_count()\n    setprint([C for C in N.nonspanning_circuits() if 'i' in C])\n<\/script><\/div>\n<p>How does this work? Well, extensions are defined by modular cuts. The set of hyperplanes of a modular cut is called a <em>linear subclass<\/em>, and internally Sage works with linear subclasses. When you provide a collection of subsets, Sage will find all modular cuts containing the closures of these sets, and work from there. You can compute the smallest modular cut generated by these sets as follows:<\/p>\n<div class=\"sage3\"><script type=\"text\/x-sage\">\nMC = M.modular_cut(['bce', 'adf'])\nsetprint(MC)\n<\/script><\/div>\n<p>Let me conclude this blog post with some homework. I think I&#8217;ve presented pretty much all necessary ingredients above.<\/p>\n<p><strong>Exercise: <\/strong> Write a function to generate all frame matroids of specified rank and maximum size.<\/p>\n<p>Next time, I plan to look at linear extensions and partial fields.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post I want to continue exploring the new-found capabilities of Sage with respect to matroid computation. It follows up on this post; Gordon Royle started a different sequence of posts on sage-matroids here. Following a comment by Jason &hellip; <a href=\"https:\/\/matroidunion.org\/?p=301\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,7],"tags":[],"class_list":["post-301","post","type-post","status-publish","format-standard","hentry","category-matroids","category-sage"],"_links":{"self":[{"href":"https:\/\/matroidunion.org\/index.php?rest_route=\/wp\/v2\/posts\/301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/matroidunion.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/matroidunion.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/matroidunion.org\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/matroidunion.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=301"}],"version-history":[{"count":47,"href":"https:\/\/matroidunion.org\/index.php?rest_route=\/wp\/v2\/posts\/301\/revisions"}],"predecessor-version":[{"id":418,"href":"https:\/\/matroidunion.org\/index.php?rest_route=\/wp\/v2\/posts\/301\/revisions\/418"}],"wp:attachment":[{"href":"https:\/\/matroidunion.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matroidunion.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matroidunion.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}