{"id":19268401,"url":"https://github.com/permutatriangle/permuta","last_synced_at":"2025-07-06T22:04:10.637Z","repository":{"id":2993047,"uuid":"42050433","full_name":"PermutaTriangle/Permuta","owner":"PermutaTriangle","description":"A native Python library for permutation pattern research","archived":false,"fork":false,"pushed_at":"2025-06-20T19:03:03.000Z","size":5211,"stargazers_count":16,"open_issues_count":3,"forks_count":5,"subscribers_count":13,"default_branch":"develop","last_synced_at":"2025-06-20T19:52:42.845Z","etag":null,"topics":["mesh-pattern","pattern-avoidance","permuta","permutation-class","permutation-pattern","permutation-statistics","permutations"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PermutaTriangle.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-09-07T11:57:22.000Z","updated_at":"2025-06-20T19:03:03.000Z","dependencies_parsed_at":"2024-08-29T13:43:51.231Z","dependency_job_id":"0ff07534-e88b-47eb-a6dd-75cb3fa3ea44","html_url":"https://github.com/PermutaTriangle/Permuta","commit_stats":{"total_commits":821,"total_committers":17,"mean_commits":"48.294117647058826","dds":0.6492082825822167,"last_synced_commit":"031cf63e29e4070694abc53333ee9b4556d58845"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/PermutaTriangle/Permuta","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FPermuta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FPermuta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FPermuta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FPermuta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PermutaTriangle","download_url":"https://codeload.github.com/PermutaTriangle/Permuta/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FPermuta/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263980141,"owners_count":23538919,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mesh-pattern","pattern-avoidance","permuta","permutation-class","permutation-pattern","permutation-statistics","permutations"],"created_at":"2024-11-09T20:16:09.984Z","updated_at":"2025-07-06T22:04:10.621Z","avatar_url":"https://github.com/PermutaTriangle.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"#######\npermuta\n#######\n\n.. image:: https://github.com/PermutaTriangle/Permuta/workflows/tests/badge.svg\n    :alt: Tests\n    :target: https://github.com/PermutaTriangle/Permuta/actions\n.. image:: https://img.shields.io/pypi/v/Permuta.svg\n    :alt: PyPI\n    :target: https://pypi.python.org/pypi/Permuta\n.. image:: https://img.shields.io/pypi/l/Permuta.svg\n    :target: https://pypi.python.org/pypi/Permuta\n.. image:: https://img.shields.io/pypi/pyversions/Permuta.svg\n    :target: https://pypi.python.org/pypi/Permuta\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n    :alt: Code style: black\n    :target: https://github.com/psf/black\n.. image:: http://www.mypy-lang.org/static/mypy_badge.svg\n    :alt: Checked with mypy\n    :target: http://mypy-lang.org/\n.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.4725758.svg\n   :target: https://doi.org/10.5281/zenodo.4725758\n\nPermuta is a Python library for working with perms (short for permutations),\npatterns, and mesh patterns.\n\nIf this code is useful to you in your work, please consider citing it. To generate a\nBibTeX entry (or another format), click the \"DOI\" badge above to go to `Zenodo`_ and locate the \"Export\"\nsection.\n\n.. _Zenodo: https://doi.org/10.5281/zenodo.4725758\n\nIf you need support, you can join us in our `Discord support server`_.\n\n.. _Discord support server: https://discord.gg/ngPZVT5\n\nInstalling\n==========\n\nTo install Permuta on your system, run:\n\n.. code-block:: bash\n\n    pip install permuta\n\nIt is also possible to install Permuta in development mode to work on the\nsource code, in which case you run the following after cloning the repository:\n\n.. code-block:: bash\n\n    pip install -e .\n\nTo run the unit tests:\n\n.. code-block:: bash\n\n    tox\n\nUsing Permuta\n#############\n\nOnce you've installed Permuta, it can be imported by a Python script or an\ninteractive Python session, just like any other Python library:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from permuta import *\n\nImporting ``*`` supplies you with the 'Perm' and 'PermSet'\nclasses along with the 'AvoidanceClass' class (with alias 'Av') for generating\nperms avoiding a set of patterns. It also gives you the 'MeshPatt' class\nand some other submodules which we will not discuss in this readme.\n\nCreating a single perm\n######################\n\nPermutations are zero-based in Permuta and can be created using any iterable.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Perm()  # Empty perm\n    Perm(())\n    \u003e\u003e\u003e Perm([])  # Another empty perm\n    Perm(())\n    \u003e\u003e\u003e Perm((0, 1, 2, 3)) # The zero-based version of 1234\n    Perm((0, 1, 2, 3))\n    \u003e\u003e\u003e Perm((2, 1, 3)) # Warning: it will initialise with any iterable\n    Perm((2, 1, 3))\n\nPermutations can also be created using some specific class methods.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Perm.from_string(\"201\")  # strings\n    Perm((2, 0, 1))\n    \u003e\u003e\u003e Perm.one_based((1, 3, 2, 4)) # one-based iterable of integers\n    Perm((0, 2, 1, 3))\n    \u003e\u003e\u003e Perm.to_standard(\"a2gsv3\") # standardising any iterable using '\u003c'\n    Perm((2, 0, 3, 4, 5, 1))\n    \u003e\u003e\u003e Perm.from_integer(210) # an integer between 0 and 9876543210\n    Perm((2, 1, 0))\n    \u003e\u003e\u003e Perm.from_integer(321) # any integer given is standardised\n    Perm((2, 1, 0))\n    \u003e\u003e\u003e Perm.from_integer(201)\n    Perm((2, 0, 1))\n\nPrinting perms gives zero-based strings.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e print(Perm(()))\n    ε\n    \u003e\u003e\u003e print(Perm((2, 1, 0)))\n    210\n    \u003e\u003e\u003e print(Perm((6, 2, 10, 9, 3, 8, 0, 1, 5, 11, 4, 7)))\n    (6)(2)(10)(9)(3)(8)(0)(1)(5)(11)(4)(7)\n\nTo get an iterator of all permutations of a certain length you can use\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Perms4 = Perm.of_length(4)\n\nYou can run a for-loop over this iterator if you need to do something with all\nthe permutations of this size. If you just want a specific permutation of this\nsize you might be better off using the unrank function.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Perm.unrank(23,4)\n    Perm((3, 2, 1, 0))\n\nThe avoids, contains, and occurrence methods enable working with patterns:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e p = Perm((0,2,1,3))\n    \u003e\u003e\u003e p.contains(Perm((2, 1, 0)))\n    False\n    \u003e\u003e\u003e p.avoids(Perm((0, 1)))\n    False\n    \u003e\u003e\u003e list(p.occurrences_of(Perm((1, 0))))\n    [(1, 2)]\n    \u003e\u003e\u003e list(Perm((0, 1)).occurrences_in(p))\n    [(0, 1), (0, 2), (0, 3), (1, 3), (2, 3)]\n\nThe basic symmetries are implemented:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e [p.reverse(), p.complement(), p.inverse()]\n    [Perm((3, 1, 2, 0)), Perm((3, 1, 2, 0)), Perm((0, 2, 1, 3))]\n\nTo take direct sums and skew sums we use ``+`` and ``-``:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e q = Perm((0, 1, 2, 3, 4))\n    \u003e\u003e\u003e p + q\n    Perm((0, 2, 1, 3, 4, 5, 6, 7, 8))\n    \u003e\u003e\u003e p - q\n    Perm((5, 7, 6, 8, 0, 1, 2, 3, 4))\n\nThere are numerous practical methods available:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e list(p.fixed_points())\n    [0, 3]\n    \u003e\u003e\u003e list(p.ascents())\n    [0, 2]\n    \u003e\u003e\u003e list(p.descents())\n    [1]\n    \u003e\u003e\u003e list(p.inversions())\n    [(1, 2)]\n    \u003e\u003e\u003e p.major_index()\n    2\n\nCreating a perm class\n#####################\n\nPerm classes are created by first specifying a basis and then calling the 'Av' class, to create the set of permutations avoiding the basis:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e basis = Basis(Perm((1, 0, 2)), Perm((1, 2, 0)))\n    \u003e\u003e\u003e basis\n    Basis((Perm((1, 0, 2)), Perm((1, 2, 0))))\n    \u003e\u003e\u003e perm_class = Av(basis)\n    \u003e\u003e\u003e perm_class\n    Av(Basis((Perm((1, 0, 2)), Perm((1, 2, 0)))))\n\nYou can ask whether a perm belongs to the perm class:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Perm((3, 2, 1, 0)) in perm_class\n    True\n    \u003e\u003e\u003e Perm((0, 2, 1, 3)) in perm_class\n    False\n\nYou can get its enumeration up to a fixed length.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e perm_class.enumeration(10)\n    [1, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512]\n    \u003e\u003e\u003e perm_class.count(11)\n    1024\n\nYou can also look to see if some well know enumeration strategies apply to a\ngiven class.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from permuta.enumeration_strategies import find_strategies\n    \u003e\u003e\u003e basis = [Perm((3, 2, 0, 1)), Perm((1, 0, 2, 3))]\n    \u003e\u003e\u003e for strat in find_strategies(basis):\n    ...     print(strat.reference())\n    The insertion encoding of permutations: Corollary 10\n    \u003e\u003e\u003e basis = [Perm((1, 2, 0, 3)), Perm((2, 0, 1, 3)), Perm((0, 1, 2, 3))]\n    \u003e\u003e\u003e for strat in find_strategies(basis):\n    ...     print(strat.reference())\n    Enumeration of Permutation Classes and Weighted Labelled Independent Sets: Corollary 4.3\n    \u003e\u003e\u003e basis = [Perm((1, 3, 0, 2)), Perm((2, 0, 3, 1))]\n    \u003e\u003e\u003e for strat in find_strategies(basis):\n    ...     print(strat.reference())\n    Enumeration of Permutation Classes and Weighted Labelled Independent Sets: Corollary 4.6\n    The class contains only finitely many simple permutations\n\nThe output is the name of a paper, followed by the statement in the paper where the enumeration strategy is discussed or stated.\n\nPermutation statistics\n######################\n\nWith the ``PermutationStatistic`` class we can look for distributions of statistics for\nclasses and look for statistics preservations (or transformation) either for two classes\nor given a bijection. First we need to import it.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from permuta.permutils.statistics import PermutationStatistic\n\nTo see a distribution for a given statistic we grab its instance and provide a length\nand a class (no class will use the set of all permutations).\n\n.. code-block:: python\n\n    \u003e\u003e\u003e PermutationStatistic.show_predefined_statistics() # Show all statistics with id\n    [0] Number of inversions\n    [1] Number of non-inversions\n    [2] Major index\n    [3] Number of descents\n    [4] Number of ascents\n    [5] Number of peaks\n    [6] Number of valleys\n    [7] Number of cycles\n    [8] Number of left-to-right minimas\n    [9] Number of left-to-right maximas\n    [10] Number of right-to-left minimas\n    [11] Number of right-to-left maximas\n    [12] Number of fixed points\n    [13] Order\n    [14] Longest increasing subsequence\n    [15] Longest decreasing subsequence\n    [16] Depth\n    [17] Number of bounces\n    [18] Maximum drop size\n    [19] Number of primes in the column sums\n    [20] Holeyness of a permutation\n    [21] Number of stack-sorts needed\n    [22] Number of pop-stack-sorts needed\n    [23] Number of pinnacles\n    [24] Number of cyclic peaks\n    [25] Number of cyclic valleys\n    [26] Number of double excedance\n    [27] Number of double drops\n    [28] Number of foremaxima\n    [29] Number of afterminima\n    [30] Number of aftermaxima\n    [31] Number of foreminima\n\n    \u003e\u003e\u003e depth = PermutationStatistic.get_by_index(16)\n    \u003e\u003e\u003e depth.distribution_for_length(5)\n    [1, 4, 12, 24, 35, 24, 20]\n    \u003e\u003e\u003e depth.distribution_up_to(4, Av.from_string(\"123\"))\n    [[1], [1], [1, 1], [0, 2, 3], [0, 0, 3, 7, 4]]\n\nGiven a bijection as a dictionary, we can check which statistics are preserved with\n``check_all_preservations`` and which are transformed with ``check_all_transformed``\n\n.. code-block:: python\n\n    \u003e\u003e\u003e bijection = {p: p.reverse() for p in Perm.up_to_length(5)}\n    \u003e\u003e\u003e for stat in PermutationStatistic.check_all_preservations(bijection):\n    ...     print(stat)\n    Number of peaks\n    Number of valleys\n    Holeyness of a permutation\n    Number of pinnacles\n\nWe can find all (predefined) statistics equally distributed over two permutation\nclasses with ``equally_distributed``. We also support checks for joint distribution\nof more than one statistics with ``jointly_equally_distributed`` and transformation\nof jointly distributed stats with ``jointly_transformed_equally_distributed``.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e cls1 = Av.from_string(\"2143,415263\")\n    \u003e\u003e\u003e cls2 = Av.from_string(\"3142\")\n    \u003e\u003e\u003e for stat in PermutationStatistic.equally_distributed(cls1, cls2, 6):\n    ...     print(stat)\n    Major index\n    Number of descents\n    Number of ascents\n    Number of peaks\n    Number of valleys\n    Number of left-to-right minimas\n    Number of right-to-left maximas\n    Longest increasing subsequence\n    Longest decreasing subsequence\n    Number of pinnacles\n\nThe BiSC algorithm\n==================\n\nThe BiSC algorithm can tell you what mesh patterns are avoided by a set of\npermutations. Although the output of the algorithm is only guaranteed to\ndescribe the finite inputted set of permutations, the user usually hopes that\nthe patterns found by the algorithm describe an infinite set of permutatations.\nTo use the algorithm we first need to import it.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from permuta.bisc import *\n\nA classic example of a set of permutations described by pattern avoidance are\nthe permutations sortable in one pass through a stack. We use the function\n``stack_sortable`` which returns ``True`` for permutations that satisfy this\nproperty. The user now has two choices: Run\n``auto_bisc(Perm.stack_sortable)`` and let the algorithm run\nwithout any more user input. It will try to use sensible values, starting by\nlearning small patterns from small permutations, and only considering longer\npatterns when that fails. If the user wants to have more control over what\nhappens that is also possible and we now walk through that: We input the\nproperty into ``bisc`` and ask it to search for patterns of length 3.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e bisc(Perm.stack_sortable, 3)\n    I will use permutations up to length 7\n    {3: {Perm((1, 2, 0)): [set()]}}\n\nWhen this command is run without specifying what length of permutations you\nwant to consider, ``bisc`` will create permutations up to length 7 that satisfy\nthe property of being stack-sortable. The output means: There is a single\nlength 3 pattern found, and its underlying classical pattern is the permutation\n``Perm((1, 2, 0))``. Ignore the ``[set()]`` in the output for now. We can use\n``show_me`` to get a better visualization of the patterns found. In this call\nto the algorithm we also specify that only permutations up to length 5 should\nbe considered.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e SG = bisc(Perm.stack_sortable, 3, 5)\n    \u003e\u003e\u003e show_me(SG)\n    There are 1 underlying classical patterns of length 3\n    There are 1 different shadings on 120\n    The number of sets to monitor at the start of the clean-up phase is 1\n    \u003cBLANKLINE\u003e\n    Now displaying the patterns\n    \u003cBLANKLINE\u003e\n     | | |\n    -+-●-+-\n     | | |\n    -●-+-+-\n     | | |\n    -+-+-●-\n     | | |\n    \u003cBLANKLINE\u003e\n\nWe should ignore the ``The number of sets to monitor at the start of the clean-up phase\nis 1`` message for now.\n\nWe do not really need this algorithm for sets of permutations described by the\navoidance of classical patterns. Its main purpose is to describe sets with mesh\npatterns, such as the West-2-stack-sortable permutations\n\n.. code-block:: python\n\n    \u003e\u003e\u003e SG = bisc(Perm.west_2_stack_sortable, 5, 7)\n    \u003e\u003e\u003e show_me(SG)\n    There are 2 underlying classical patterns of length 4\n    There are 1 different shadings on 1230\n    There are 1 different shadings on 2130\n    The number of sets to monitor at the start of the clean-up phase is 1\n    There are 1 underlying classical patterns of length 5\n    There are 1 different shadings on 42130\n    \u003cBLANKLINE\u003e\n    Now displaying the patterns\n    \u003cBLANKLINE\u003e\n     | | | |\n    -+-+-●-+-\n     | | | |\n    -+-●-+-+-\n     | | | |\n    -●-+-+-+-\n     | | | |\n    -+-+-+-●-\n     | | | |\n    \u003cBLANKLINE\u003e\n     |▒| | |\n    -+-+-●-+-\n     | | | |\n    -●-+-+-+-\n     | | | |\n    -+-●-+-+-\n     | | | |\n    -+-+-+-●-\n     | | | |\n    \u003cBLANKLINE\u003e\n     |▒| | | |\n    -●-+-+-+-+-\n     | |▒| | |\n    -+-+-+-●-+-\n     | | | | |\n    -+-●-+-+-+-\n     | | | | |\n    -+-+-●-+-+-\n     | | | | |\n    -+-+-+-+-●-\n     | | | | |\n    \u003cBLANKLINE\u003e\n\nThis is good news and bad news. Good because we quickly got a description of the\nset we were looking at, that would have taken a long time to find by hand. The bad news\nis that there is actually some redundancy in the output. To understand better what is\ngoing on we will start by putting the permutations under investigation in a dictionary,\nwhich keeps them separated by length.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e A, B = create_bisc_input(7, Perm.west_2_stack_sortable)\n\nThis creates two dictionaries with keys 1, 2, ..., 7 such that ``A[i]`` points\nto the list of permutations of length ``i`` that are West-2-stack-sortable, and\n``B[i]`` points to the complement. We can pass the A dictionary directly into\nBiSC since only the permutations satisfying the property are used to find the\npatterns. We can use the second dictionary to check whether every permutation\nin the complement contains at least one of the patterns we found.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e SG = bisc(A, 5, 7)\n    \u003e\u003e\u003e patterns_suffice_for_bad(SG, 7, B)\n    Starting sanity check with bad perms\n    Now checking permutations of length 0\n    Now checking permutations of length 1\n    Now checking permutations of length 2\n    Now checking permutations of length 3\n    Now checking permutations of length 4\n    Now checking permutations of length 5\n    Now checking permutations of length 6\n    Now checking permutations of length 7\n    Sanity check passes for the bad perms\n    (True, [])\n\nIn this case it is true that every permutation in B, up to length 7, contains\nat least one of the patterns found. Had that not been the case a list of\npermutations would have been outputted (instead of just the empty list).\n\nNow, we claim that there is actually redundancy in the patterns we found, and\nthe length 4 mesh patterns should be enough to describe the set. This can occur\nand it can be tricky to theoretically prove that one mesh pattern is implied\nby another pattern (or a set of others, as is the case here). We use the dictionary\n``B`` again and run\n\n.. code-block:: python\n\n    \u003e\u003e\u003e bases, dict_numbs_to_patts = run_clean_up(SG, B)\n    \u003cBLANKLINE\u003e\n    The bases found have lengths\n    [2]\n\nThere is one basis of mesh patterns found, with 2 patterns\n\n.. code-block:: python\n\n    \u003e\u003e\u003e show_me_basis(bases[0], dict_numbs_to_patts)\n    \u003cBLANKLINE\u003e\n    Displaying the patterns in the basis\n    \u003cBLANKLINE\u003e\n     | | | |\n    -+-+-●-+-\n     | | | |\n    -+-●-+-+-\n     | | | |\n    -●-+-+-+-\n     | | | |\n    -+-+-+-●-\n     | | | |\n    \u003cBLANKLINE\u003e\n     |▒| | |\n    -+-+-●-+-\n     | | | |\n    -●-+-+-+-\n     | | | |\n    -+-●-+-+-\n     | | | |\n    -+-+-+-●-\n     | | | |\n    \u003cBLANKLINE\u003e\n\nThis is the output we were expecting. There are several other properties of\npermutations that can be imported from ``permuta.bisc.perm_properties``, such\nas ``smooth``, ``forest-like``, ``baxter``, ``simsun``, ``quick_sortable``, etc.\n\nBoth ``bisc`` and ``auto_bisc`` can accept input in the form of a property,\nor a list of permutations (satisfying some property).\n\nLicense\n#######\n\nBSD-3: see the `LICENSE \u003chttps://github.com/PermutaTriangle/Permuta/blob/master/LICENSE\u003e`_ file.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutatriangle%2Fpermuta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpermutatriangle%2Fpermuta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutatriangle%2Fpermuta/lists"}