{"id":19268383,"url":"https://github.com/permutatriangle/tilings","last_synced_at":"2026-03-12T23:31:25.074Z","repository":{"id":34925519,"uuid":"121506164","full_name":"PermutaTriangle/Tilings","owner":"PermutaTriangle","description":"a Python library for working with gridded permutation and tilings","archived":false,"fork":false,"pushed_at":"2025-07-16T10:55:30.000Z","size":2836,"stargazers_count":10,"open_issues_count":14,"forks_count":4,"subscribers_count":10,"default_branch":"develop","last_synced_at":"2025-09-04T19:06:13.505Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://permutatriangle.github.io/programs/2019-6-17-tilings.html","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":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":".zenodo.json"}},"created_at":"2018-02-14T12:03:05.000Z","updated_at":"2025-07-11T00:49:58.000Z","dependencies_parsed_at":"2025-06-24T02:33:15.929Z","dependency_job_id":"93652467-d24f-4c03-a85c-f9edde17c0a7","html_url":"https://github.com/PermutaTriangle/Tilings","commit_stats":{"total_commits":1160,"total_committers":17,"mean_commits":68.23529411764706,"dds":0.6482758620689655,"last_synced_commit":"3d6f5349d9a7841cdd24a65ce491b55b16a47341"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/PermutaTriangle/Tilings","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FTilings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FTilings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FTilings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FTilings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PermutaTriangle","download_url":"https://codeload.github.com/PermutaTriangle/Tilings/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PermutaTriangle%2FTilings/sbom","scorecard":{"id":109236,"data":{"date":"2025-08-11","repo":{"name":"github.com/PermutaTriangle/Tilings","commit":"4c0fe013790672117123cf91955e66270087106a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Code-Review","score":3,"reason":"Found 8/21 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":1,"reason":"1 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build-and-deploy.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-and-deploy.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/PermutaTriangle/Tilings/build-and-deploy.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-and-deploy.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/PermutaTriangle/Tilings/build-and-deploy.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/PermutaTriangle/Tilings/test.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:55: update your workflow using https://app.stepsecurity.io/secureworkflow/PermutaTriangle/Tilings/test.yml/develop?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/build-and-deploy.yml:18","Warn: pipCommand not pinned by hash: .github/workflows/build-and-deploy.yml:19","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:62","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:63","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:66","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:75","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:76","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   7 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during GetBranch(master): error during branchesHandler.query: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-15T11:42:46.531Z","repository_id":34925519,"created_at":"2025-08-15T11:42:46.531Z","updated_at":"2025-08-15T11:42:46.531Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278722762,"owners_count":26034461,"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","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-09T20:16:03.923Z","updated_at":"2026-03-12T23:31:25.061Z","avatar_url":"https://github.com/PermutaTriangle.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Tilings\n=======\n\n.. image:: https://github.com/PermutaTriangle/Tilings/actions/workflows/test.yml/badge.svg\n    :alt: Tests\n    :target: https://github.com/PermutaTriangle/Tilings/actions/workflows/test.yml\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:: https://img.shields.io/badge/mypy-checked-blue\n    :alt: mypy: checked\n    :target: https://mypy-lang.org/\n.. image:: https://img.shields.io/coveralls/github/PermutaTriangle/Tilings.svg\n    :alt: Coveralls\n    :target: https://coveralls.io/github/PermutaTriangle/Tilings\n.. image:: https://img.shields.io/pypi/v/Tilings.svg\n    :alt: PyPI\n    :target: https://pypi.python.org/pypi/Tilings\n.. image:: https://img.shields.io/pypi/l/Tilings.svg\n    :target: https://pypi.python.org/pypi/Tilings\n.. image:: https://img.shields.io/pypi/pyversions/Tilings.svg\n    :target: https://pypi.python.org/pypi/Tilings\n.. image:: https://zenodo.org/badge/121506164.svg\n   :target: https://zenodo.org/badge/latestdoi/121506164\n\nThe ``tilings`` Python library contains code for working with gridded\npermutations and tilings, and in particular the ``TileScope`` algorithm, which\ncan be used to enumerate permutation classes.\n\nIf you are primarily interested in enumerating permutation classes, then you\nmay wish to skip ahead to the ``TileScope`` section, but note the installation\nwill be the same as for ``tilings``.\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 and locate the \"Cite As\"\nsection.\n\nIf you need support, have a suggestion, or just want to be up to date with the\nlatest developments please join us on our\n`Discord server \u003chttps://discord.gg/ySJD6SV\u003e`__ where we'd be happy to hear\nfrom you! To receive an email notification about major new releases,\nsend an email to `permutatriangle@gmail.com \u003cmailto:permutatriangle@gmail.com\u003e`_\n(but please direct all requests for assistance to the\n`Discord server \u003chttps://discord.gg/ySJD6SV\u003e`__).\n\nInstalling\n----------\n\nTo install ``tilings`` on your system, run:\n\n.. code:: bash\n\n       pip install tilings\n\nIf you would like to edit the source code, you should install ``tilings`` in\ndevelopment mode by cloning the repository, running\n\n.. code:: bash\n\n       pip install -e .\n\nTo verify that your installation is correct, you can try to get a specification for\n`Av(12)` by running in your terminal:\n\n.. code:: bash\n\n        tilescope spec 12 point_placements\n\nYou should then be all set up to use ``tilings`` and the ``TileScope`` algorithm! The\n\"Performance\" section at the end of this document provides some more technical\ninformation.\n\nWhat are gridded permutations and tilings?\n------------------------------------------\n\nWe will be brief in our definitions here, for more details see\n`Christian Bean’s PhD thesis \u003chttps://opinvisindi.is/handle/20.500.11815/1184\u003e`__.\n\nA ``gridded permutation`` is a pair ``(π, P)`` where ``π`` is a\npermutation and ``P`` is a tuple of cells, called the positions, that\ndenote the cells in which the points of ``π`` are drawn on a grid. Let\n``G`` denote the set of all gridded permutations. Containment of gridded\npermutations is defined the same as containment of permutations, except\nincluding the preservation of the cells.\n\nFor example, ``(284376915, ((0, 0), (0, 3), (1, 1), (1, 1), (2, 3), (2, 2), (3, 4), (3, 0), (4, 2))`` is drawn on the grid as follows.\n\n.. code:: python\n\n       +--+--+--+--+-+\n       |  |  |  |● | |\n       +--+--+--+--+-+\n       | ●|  |  |  | |\n       |  |  |● |  | |\n       +--+--+--+--+-+\n       |  |  | ●|  | |\n       |  |  |  |  |●|\n       +--+--+--+--+-+\n       |  |● |  |  | |\n       |  | ●|  |  | |\n       +--+--+--+--+-+\n       |● |  |  |  | |\n       |  |  |  | ●| |\n       +--+--+--+--+-+\n\n\nA ``tiling`` is a triple ``T = ((n, m), O, R)``, where ``n`` and ``m``\nare positive integers, ``O`` is a set of gridded permutations called\n``obstructions``, and ``R`` is a set of sets of gridded permutations\ncalled ``requirements``.\n\nWe say a gridded permutations avoids a set of gridded permutations if it\navoids all of the permutations in the set, otherwise it contains the\nset. To contain a set, therefore, means to contain at least one in the\nset. The set of gridded permutations on a tiling ``Grid(T)`` is the set\nof all gridded permutations in the ``n x m`` grid that avoid ``O`` and\ncontain each set ``r`` in ``R``.\n\nUsing tilings\n-------------\n\nOnce you’ve installed ``tilings``, it can be imported by a Python script\nor an interactive Python session, just like any other Python library:\n\n.. code:: python\n\n       \u003e\u003e\u003e from tilings import *\n\nImporting ``*`` from it supplies you with the ``GriddedPerm`` and ``Tiling``\nclasses.\n\nAs above, a gridded permutation is a pair ``(π, P)`` where ``π`` is a\npermutation and ``P`` is a tuple of cells. The permutation is assumed to\nbe a ``Perm`` from the ``permuta`` Python library. Not every tuple of\ncells is a valid position for a given permutation. This can be checked\nusing the ``contradictory`` method.\n\n.. code:: python\n\n       \u003e\u003e\u003e from permuta import Perm\n       \u003e\u003e\u003e gp = GriddedPerm(Perm((0, 2, 1)), ((0, 0), (0, 0), (1, 0)))\n       \u003e\u003e\u003e gp.contradictory()\n       False\n       \u003e\u003e\u003e gp = GriddedPerm(Perm((0, 1, 2)), ((0, 0), (0, 1), (0, 0)))\n       \u003e\u003e\u003e gp.contradictory()\n       True\n\nA ``Tiling`` is created with an iterable of obstructions and an\niterable of requirements (and each requirement is an iterable of gridded permutations).\nIt is assumed that all cells not mentioned in some obstruction or\nrequirement are empty. You can print the tiling to get an overview of the\ntiling created. In this example, we have a tiling that corresponds to\nnon-empty permutations avoiding\n``123``.\n\n.. code:: python\n\n       \u003e\u003e\u003e obstructions = [GriddedPerm.single_cell(Perm((0, 1)), (1, 1)),\n       ...                 GriddedPerm.single_cell(Perm((1, 0)), (1, 1)),\n       ...                 GriddedPerm.single_cell(Perm((0, 1)), (0, 0)),\n       ...                 GriddedPerm.single_cell(Perm((0, 1, 2)), (2, 0)),\n       ...                 GriddedPerm(Perm((0, 1, 2)), ((0, 0), (2, 0), (2, 0)))]\n       \u003e\u003e\u003e requirements = [[GriddedPerm.single_cell(Perm((0,)), (1, 1))]]\n       \u003e\u003e\u003e tiling = Tiling(obstructions, requirements)\n       \u003e\u003e\u003e print(tiling)\n       +-+-+-+\n       | |●| |\n       +-+-+-+\n       |\\| |1|\n       +-+-+-+\n       1: Av(012)\n       \\: Av(01)\n       ●: point\n       Crossing obstructions:\n       012: (0, 0), (2, 0), (2, 0)\n       Requirement 0:\n       0: (1, 1)\n\nThere are several properties of ``Tiling`` that you can use, e.g.,\n\n.. code:: python\n\n       \u003e\u003e\u003e tiling.dimensions\n       (3, 2)\n       \u003e\u003e\u003e sorted(tiling.active_cells)\n       [(0, 0), (1, 1), (2, 0)]\n       \u003e\u003e\u003e tiling.point_cells\n       frozenset({(1, 1)})\n       \u003e\u003e\u003e sorted(tiling.possibly_empty)\n       [(0, 0), (2, 0)]\n       \u003e\u003e\u003e tiling.positive_cells\n       frozenset({(1, 1)})\n\nThose who have read ahead, or already started using tilings may have noticed\nthat a ``Tiling`` can also be defined with a third argument called ``assumptions``.\nThese can be used to keep track of occurrences of gridded permutations on\ntilings. These are still in development but are essential for certain\nparts of the ``TileScope`` algorithm. For simplicity we will not discuss\nthese again until the `Fusion` section.\n\nThere are a number of methods available on the tiling. You can generate\nthe gridded permutations satisfying the obstructions and requirements\nusing the ``gridded_perms_of_length`` method.\n\n.. code:: python\n\n       \u003e\u003e\u003e for i in range(4):\n       ...     for gp in sorted(tiling.gridded_perms_of_length(i)):\n       ...         print(gp)\n       0: (1, 1)\n       01: (0, 0), (1, 1)\n       10: (1, 1), (2, 0)\n       021: (0, 0), (1, 1), (2, 0)\n       102: (0, 0), (0, 0), (1, 1)\n       120: (0, 0), (1, 1), (2, 0)\n       201: (1, 1), (2, 0), (2, 0)\n       210: (1, 1), (2, 0), (2, 0)\n\nThere are numerous other methods and properties. Many of these are specific\nto the ``TileScope`` algorithm, discussed in `Christian Bean’s PhD\nthesis \u003chttps://opinvisindi.is/handle/20.500.11815/1184\u003e`__. For the remainder\nof this readme we will focus on the ``TileScope`` algorithm.\n\nThe TileScope algorithm\n=======================\n\n\nUsing TileScope\n---------------\n\nIf you've not installed ``tilings`` yet then go ahead and do this first by\npip installing ``tilings``:\n\n.. code:: bash\n\n       pip install tilings\n\nOnce done you can use the ``TileScope`` algorithm in two ways, either directly\nby importing from the ``tilings.tilescope`` module which we will discuss in\ngreater detail shortly, or by using the ``TileScope`` command line tool.\n\nThe command line tool\n---------------------\n\nFirst, check the help commands for more information about its usage.\n\n.. code:: bash\n\n       tilescope -h\n       tilescope spec -h\n\nTo search for a combinatorial specification use the subcommand\n``tilescope spec``, e.g.\n\n.. code:: bash\n\n       tilescope spec 231 point_placements\n\nBy default this command will try to solve for the generating function,\nalthough in some cases you will come across some not-yet-implemented features;\nfor more information please join us on our\n`Discord server \u003chttps://discord.gg/ySJD6SV\u003e`__, where we'd be happy to talk\nabout it!\n\nThe ``point_placements`` argument above is a strategy pack, which we explain in\nmore detail in the ``StrategyPacks`` section.\n\nThe tilescope module\n--------------------\nTileScope can be imported in a interactive Python session from\n``tilings.tilescope``.\n\n.. code:: python\n\n       \u003e\u003e\u003e from tilings.tilescope import *\n\nImporting ``*`` from ``tilings.tilescope`` supplies you with the ``TileScope``\nand ``TileScopePack`` classes. Running the ``TileScope`` is as simple as\nchoosing a class and a strategy pack. We'll go into more detail about the\ndifferent strategies\navailable shortly, but first let's enumerate our first permutation class. The\nexample one always learns first in permutation patterns is enumerating\nAv(231). There are many different packs that will succeed for this class,\nbut to get the most commonly described decomposition we can use\n``point_placements``. The basis can be given to TileScope in several\nformats: an iterable of permuta.Perm, a string where the permutations\nare separated by ``'_'`` (e.g. ``'231_4321'``), or as a ``Tiling``.\n\n.. code:: python\n\n       \u003e\u003e\u003e pack = TileScopePack.point_placements()\n       \u003e\u003e\u003e tilescope = TileScope('231', pack)\n\nOnce we have created our ``TileScope`` we can then use the ``auto_search``\nmethod which will search for a specification using the strategies given.\nIf successful it will return a CombinatorialSpecification.\n``TileScope`` uses ``logzero.logger`` to report information. If you wish to\nsuppress these prints, you can set ``logzero.loglevel``, which we have\ndone here for sake of brevity in this readme!\n\n.. code:: python\n\n       \u003e\u003e\u003e import logzero; import logging; logzero.loglevel(logging.CRITICAL)\n       \u003e\u003e\u003e spec = tilescope.auto_search()\n       \u003e\u003e\u003e print(spec)\n       A combinatorial specification with 4 rules.\n       -----------\n       0 -\u003e (1, 2)\n       insert 0 in cell (0, 0)\n       +-+            +-+     +-+\n       |1|         =  | |  +  |1|\n       +-+            +-+     +-+\n       1: Av(120)             1: Av+(120)\n                              Requirement 0:\n                              0: (0, 0)\n       -------\n       1 -\u003e ()\n       is atom\n       +-+\n       | |\n       +-+\n       \u003cBLANKLINE\u003e\n       -----\n       2 = 3\n       placing the topmost point in cell (0, 0), then row and column separation\n       +-+                +-+-+-+                    +-+-+-+\n       |1|             =  | |●| |                 =  | |●| |\n       +-+                +-+-+-+                    +-+-+-+\n       1: Av+(120)        |1| |1|                    | | |1|\n       Requirement 0:     +-+-+-+                    +-+-+-+\n       0: (0, 0)          1: Av(120)                 |1| | |\n                          ●: point                   +-+-+-+\n                          Crossing obstructions:     1: Av(120)\n                          10: (0, 0), (2, 0)         ●: point\n                          Requirement 0:             Requirement 0:\n                          0: (1, 1)                  0: (1, 2)\n       ---------\n       3 -\u003e (0,)\n       tiling is locally factorable\n       +-+-+-+            +-+\n       | |●| |         ↝  |1|\n       +-+-+-+            +-+\n       | | |1|            1: Av(120)\n       +-+-+-+\n       |1| | |\n       +-+-+-+\n       1: Av(120)\n       ●: point\n       Requirement 0:\n       0: (1, 2)\n\nThe locally factorable tiling in the rule `3 -\u003e (0,)` could be further expanded\ndown to atoms and the root tiling.\nThis can be done using the `expand_verified` method.\n\n.. code:: python\n\n       \u003e\u003e\u003e spec = spec.expand_verified()\n       \u003e\u003e\u003e print(spec)\n       A combinatorial specification with 5 rules.\n       -----------\n       0 -\u003e (1, 2)\n       insert 0 in cell (0, 0)\n       +-+            +-+     +-+\n       |1|         =  | |  +  |1|\n       +-+            +-+     +-+\n       1: Av(120)             1: Av+(120)\n                              Requirement 0:\n                              0: (0, 0)\n       -------\n       1 -\u003e ()\n       is atom\n       +-+\n       | |\n       +-+\n       \u003cBLANKLINE\u003e\n       -----\n       2 = 3\n       placing the topmost point in cell (0, 0), then row and column separation\n       +-+                +-+-+-+                    +-+-+-+\n       |1|             =  | |●| |                 =  | |●| |\n       +-+                +-+-+-+                    +-+-+-+\n       1: Av+(120)        |1| |1|                    | | |1|\n       Requirement 0:     +-+-+-+                    +-+-+-+\n       0: (0, 0)          1: Av(120)                 |1| | |\n                          ●: point                   +-+-+-+\n                          Crossing obstructions:     1: Av(120)\n                          10: (0, 0), (2, 0)         ●: point\n                          Requirement 0:             Requirement 0:\n                          0: (1, 1)                  0: (1, 2)\n       --------------\n       3 -\u003e (0, 4, 0)\n       factor with partition {(0, 0)} / {(1, 2)} / {(2, 1)}\n       +-+-+-+            +-+            +-+                +-+\n       | |●| |         =  |1|         x  |●|             x  |1|\n       +-+-+-+            +-+            +-+                +-+\n       | | |1|            1: Av(120)     ●: point           1: Av(120)\n       +-+-+-+                           Requirement 0:\n       |1| | |                           0: (0, 0)\n       +-+-+-+\n       1: Av(120)\n       ●: point\n       Requirement 0:\n       0: (1, 2)\n       -------\n       4 -\u003e ()\n       is atom\n       +-+\n       |●|\n       +-+\n       ●: point\n       Requirement 0:\n       0: (0, 0)\n\nNow that we have a specification we can do a number of things. For example,\ncounting how many permutations there are in the class. This can be done using\nthe ``count_objects_of_size`` method on the CombinatorialSpecification.\n\n.. code:: python\n\n       \u003e\u003e\u003e [spec.count_objects_of_size(i) for i in range(10)]\n       [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]\n\nOf course we see the Catalan numbers! We can also sample uniformly using the\n``random_sample_object_of_size`` method. This will return a ``GriddedPerm``.\nWe have used the ``ascii_plot`` method for us to visualise it.\nIf you want the underlying ``Perm``, this can be accessed with the ``patt``\nattribute. We also highlighted here the ``permuta.Perm.ascii_plot`` method for\nan alternative visualisation.\n\n.. code:: python\n\n       \u003e\u003e\u003e gp = spec.random_sample_object_of_size(10)\n       \u003e\u003e\u003e print(gp)  # doctest: +SKIP\n       9543102768: (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0)\n       \u003e\u003e\u003e print(gp.ascii_plot())  # doctest: +SKIP\n       +----------+\n       |●         |\n       |         ●|\n       |       ●  |\n       |        ● |\n       | ●        |\n       |  ●       |\n       |   ●      |\n       |      ●   |\n       |    ●     |\n       |     ●    |\n       +----------+\n       \u003e\u003e\u003e perm = gp.patt\n       \u003e\u003e\u003e print(perm)  # doctest: +SKIP\n       9543102768\n       \u003e\u003e\u003e print(perm.ascii_plot())  # doctest: +SKIP\n        | | | | | | | | | |\n       -●-+-+-+-+-+-+-+-+-+-\n        | | | | | | | | | |\n       -+-+-+-+-+-+-+-+-+-●-\n        | | | | | | | | | |\n       -+-+-+-+-+-+-+-●-+-+-\n        | | | | | | | | | |\n       -+-+-+-+-+-+-+-+-●-+-\n        | | | | | | | | | |\n       -+-●-+-+-+-+-+-+-+-+-\n        | | | | | | | | | |\n       -+-+-●-+-+-+-+-+-+-+-\n        | | | | | | | | | |\n       -+-+-+-●-+-+-+-+-+-+-\n        | | | | | | | | | |\n       -+-+-+-+-+-+-●-+-+-+-\n        | | | | | | | | | |\n       -+-+-+-+-●-+-+-+-+-+-\n        | | | | | | | | | |\n       -+-+-+-+-+-●-+-+-+-+-\n        | | | | | | | | | |\n\n\nYou can use the ``get_equations`` method which returns an iterator for the\nsystem of equations implied by the specification.\n\n.. code:: python\n\n       \u003e\u003e\u003e list(spec.get_equations())\n       [Eq(F_0(x), F_1(x) + F_2(x)), Eq(F_1(x), 1), Eq(F_2(x), F_3(x)), Eq(F_3(x), F_0(x)**2*F_4(x)), Eq(F_4(x), x)]\n\nYou can also pass these directly to the ``solve`` method in ``sympy`` by using the\n``get_genf`` method. It will then return the solution which matches the initial\nconditions.\n\n.. code:: python\n\n       \u003e\u003e\u003e spec.get_genf()\n       (1 - sqrt(1 - 4*x))/(2*x)\n\nThe ``sympy.solve`` method can be very slow, particularly on big systems. If\nyou are having troubles, then other softwares such as Mathematica and Maple are\noften better. You can also use the method `get_maple_equations` which will\nreturn a string containing Maple code for the equations.\n\n.. code:: python\n\n       \u003e\u003e\u003e print(spec.get_maple_equations())\n       root_func := F[0, x]:\n       eqs := [\n       F[0, x] = (F[1, x] + F[2, x]),\n       F[1, x] = (1),\n       F[2, x] = F[3, x],\n       F[3, x] = ((F[0, x]**(2)) * F[4, x]),\n       F[4, x] = x\n       ]:\n       count := [1, 1, 2, 5, 14, 42, 132]:\n\nIf you have a system of equations you are unable to solve, then please feel\nfree to send them to our `Discord server \u003chttps://discord.gg/ySJD6SV\u003e`__.\n\nA specification can be saved and loaded later by converting it to\n`JSON \u003chttps://realpython.com/python-json/\u003e`__, a data storage format\nthat can be written to a file or copy-pasted elsewhere for safe keeping.\nThis functionality is built into `TileScope`. We can retrieve the JSON\nrepresentation of a specification and load the specificiation from said\nJSON string by doing the following:\n\n.. code:: python\n\n       \u003e\u003e\u003e import json\n       \u003e\u003e\u003e from comb_spec_searcher import CombinatorialSpecification\n\n       \u003e\u003e\u003e json_string = json.dumps(spec.to_jsonable())\n       \u003e\u003e\u003e reloaded_spec = CombinatorialSpecification.from_dict(json.loads(json_string))\n\n\nStrategyPacks\n=============\n\nWe have implemented a large number of structural decomposition strategies that\nwe will discuss a bit more in the strategies section that follows. One can use\nany subset of these strategies to search for a combinatorial specification.\nThis can be done by creating a ``TileScopePack``.\n\nWe have prepared a number of curated packs of strategies that we find to be\nrather effective. These can accessed as class methods on ``TileScopePack``.\nThey are:\n\n- ``point_placements``: checks if cells are empty or not and places extreme\n  points in cells\n- ``row_and_col_placements``: places the left or rightmost points in columns,\n  or the bottom or topmost points in rows\n- ``regular_insertion_encoding``: this pack includes the strategies required\n  for finding the specification corresponding to a regular insertion encoding\n- ``insertion_row_and_col_placements``: this pack places rows and columns as\n  above, but first ensures every active cell contains a point (this is in the\n  same vein as the \"slots\" of the regular insertion encoding)\n- ``insertion_point_placements``: places extreme points in cells, but first\n  ensures every active cell contains a point\n- ``pattern_placements``: inserts size one requirements into a tiling, and then\n  places points with respect to a pattern, e.g. if your permutation contains 123,\n  then place the leftmost point that acts as a 2 in an occurrence of 123\n- ``requirement_placements``: places points with respect to any requirement,\n  e.g. if your permutation contains {12, 21}, then place the rightmost point\n  that is either an occurrence of 1 in 12 or an occurrence of 2 in 21.\n- ``only_root_placements``: this is the same as ``pattern_placements`` except\n  we only allow inserting into 1x1 tilings, therefore making it a finite pack\n- ``all_the_strategies``: a pack containing (almost) all of the strategies\n\nEach of these packs have different parameters that can be set. You can view\nthis by using the help command e.g.,\n``help(TileScopePack.pattern_placements)``.\nIf you need help picking the right pack to enumerate your class join us on our\n`Discord server \u003chttps://discord.gg/ySJD6SV\u003e`__ where we'd be happy to help.\n\nYou can make any pack use the fusion strategy by using the method\n``make_fusion``; for example, here is how to create the pack\n``row_placements_fusion``.\n\n.. code:: python\n\n       \u003e\u003e\u003e pack = TileScopePack.row_and_col_placements(row_only=True).make_fusion()\n       \u003e\u003e\u003e print(pack)\n       Looking for recursive combinatorial specification with the strategies:\n       Inferral: row and column separation, obstruction transitivity\n       Initial: rearrange assumptions, add assumptions, factor, point corroboration, tracked fusion\n       Verification: verify atoms, insertion encoding verified, one by one verification, locally factorable verification\n       Set 1: row placement\n\nThis particular pack can be used to enumerate ``Av(123)``.\n\n.. code:: python\n\n       \u003e\u003e\u003e tilescope = TileScope('123', pack)\n       \u003e\u003e\u003e spec = tilescope.auto_search(smallest=True)\n       \u003e\u003e\u003e print(spec)  # doctest: +SKIP\n       A combinatorial specification with 10 rules.\n       -----------\n       0 -\u003e (1, 2)\n       insert 0 in cell (0, 0)\n       +-+            +-+     +-+\n       |1|         =  | |  +  |1|\n       +-+            +-+     +-+\n       1: Av(012)             1: Av+(012)\n                              Requirement 0:\n                              0: (0, 0)\n       -------\n       1 -\u003e ()\n       is atom\n       +-+\n       | |\n       +-+\n       -----------\n       3 -\u003e (4, 5)\n       factor with partition {(0, 0), (0, 2)} / {(1, 1)}\n       +-+-+                           +-+                             +-+\n       |1| |                        =  |1|                          x  |●|\n       +-+-+                           +-+                             +-+\n       | |●|                           |\\|                             ●: point\n       +-+-+                           +-+                             Requirement 0:\n       |\\| |                           1: Av(012)                      0: (0, 0)\n       +-+-+                           \\: Av(01)\n       1: Av(012)                      Crossing obstructions:\n       \\: Av(01)                       012: (0, 0), (0, 1), (0, 1)\n       ●: point\n       Crossing obstructions:\n       012: (0, 0), (0, 2), (0, 2)\n       Requirement 0:\n       0: (1, 1)\n       ---------\n       3 -\u003e (5,)\n       adding the assumption 'can count points in cell (0, 0)'\n       +-+-+                           +-+-+\n       |\\|1|                        ↣  |\\|1|\n       +-+-+                           +-+-+\n       1: Av(012)                      1: Av(012)\n       \\: Av(01)                       \\: Av(01)\n       Crossing obstructions:          Crossing obstructions:\n       012: (0, 0), (1, 0), (1, 0)     012: (0, 0), (1, 0), (1, 0)\n                                       Assumption 0:\n                                       can count points in cell (0, 0)\n       --------------\n       5 -\u003e (1, 6, 7)\n       placing the topmost point in row 0\n       +-+-+                               +-+     +-+-+-+                                      +-+-+-+-+\n       |\\|1|                            =  | |  +  |●| | |                                   +  | | |●| |\n       +-+-+                               +-+     +-+-+-+                                      +-+-+-+-+\n       1: Av(012)                                  | |\\|1|                                      |\\|\\| |1|\n       \\: Av(01)                                   +-+-+-+                                      +-+-+-+-+\n       Crossing obstructions:                      1: Av(012)                                   1: Av(012)\n       012: (0, 0), (1, 0), (1, 0)                 \\: Av(01)                                    \\: Av(01)\n       Assumption 0:                               ●: point                                     ●: point\n       can count points in cell (0, 0)             Crossing obstructions:                       Crossing obstructions:\n                                                   012: (1, 0), (2, 0), (2, 0)                  01: (0, 0), (1, 0)\n                                                   Requirement 0:                               012: (0, 0), (3, 0), (3, 0)\n                                                   0: (0, 1)                                    012: (1, 0), (3, 0), (3, 0)\n                                                   Assumption 0:                                Requirement 0:\n                                                   can count points in cells (0, 1), (1, 0)     0: (2, 1)\n                                                                                                Assumption 0:\n                                                                                                can count points in cell (0, 0)\n       -----------\n       6 -\u003e (8, 5)\n       factor with partition {(0, 1)} / {(1, 0), (2, 0)}\n       +-+-+-+                                      +-+                                 +-+-+\n       |●| | |                                   =  |●|                              x  |\\|1|\n       +-+-+-+                                      +-+                                 +-+-+\n       | |\\|1|                                      ●: point                            1: Av(012)\n       +-+-+-+                                      Requirement 0:                      \\: Av(01)\n       1: Av(012)                                   0: (0, 0)                           Crossing obstructions:\n       \\: Av(01)                                    Assumption 0:                       012: (0, 0), (1, 0), (1, 0)\n       ●: point                                     can count points in cell (0, 0)     Assumption 0:\n       Crossing obstructions:                                                           can count points in cell (0, 0)\n       012: (1, 0), (2, 0), (2, 0)\n       Requirement 0:\n       0: (0, 1)\n       Assumption 0:\n       can count points in cells (0, 1), (1, 0)\n       -------\n       8 -\u003e ()\n       is atom\n       +-+\n       |●|\n       +-+\n       ●: point\n       Requirement 0:\n       0: (0, 0)\n       Assumption 0:\n       can count points in cell (0, 0)\n       -----------\n       7 -\u003e (9, 4)\n       factor with partition {(0, 0), (1, 0), (3, 0)} / {(2, 1)}\n       +-+-+-+-+                           +-+-+-+                             +-+\n       | | |●| |                        =  |\\|\\|1|                          x  |●|\n       +-+-+-+-+                           +-+-+-+                             +-+\n       |\\|\\| |1|                           1: Av(012)                          ●: point\n       +-+-+-+-+                           \\: Av(01)                           Requirement 0:\n       1: Av(012)                          Crossing obstructions:              0: (0, 0)\n       \\: Av(01)                           01: (0, 0), (1, 0)\n       ●: point                            012: (0, 0), (2, 0), (2, 0)\n       Crossing obstructions:              012: (1, 0), (2, 0), (2, 0)\n       01: (0, 0), (1, 0)                  Assumption 0:\n       012: (0, 0), (3, 0), (3, 0)         can count points in cell (0, 0)\n       012: (1, 0), (3, 0), (3, 0)\n       Requirement 0:\n       0: (2, 1)\n       Assumption 0:\n       can count points in cell (0, 0)\n       ---------\n       9 -\u003e (5,)\n       fuse columns 0 and 1\n       +-+-+-+                             +-+-+\n       |\\|\\|1|                          ↣  |\\|1|\n       +-+-+-+                             +-+-+\n       1: Av(012)                          1: Av(012)\n       \\: Av(01)                           \\: Av(01)\n       Crossing obstructions:              Crossing obstructions:\n       01: (0, 0), (1, 0)                  012: (0, 0), (1, 0), (1, 0)\n       012: (0, 0), (2, 0), (2, 0)         Assumption 0:\n       012: (1, 0), (2, 0), (2, 0)         can count points in cell (0, 0)\n       Assumption 0:\n       can count points in cell (0, 0)\n       -------\n       4 -\u003e ()\n       is atom\n       +-+\n       |●|\n       +-+\n       ●: point\n       Requirement 0:\n       0: (0, 0)\n       \u003e\u003e\u003e [spec.count_objects_of_size(i) for i in range(10)]\n       [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862]\n\nIt is possible to make your own pack as well, but for that you should first\nlearn more about what the individual strategies do.\n\nThe strategies\n==============\n\nThe ``TileScope`` algorithm has in essence six different strategies that are\napplied in many different ways, resulting in very different universes in which\nto search for a combinatorial specification in. They are:\n\n- ``requirement insertions``: a disjoint union considering whether or not a tiling\n  contains a requirement\n- ``point placements``: places a uniquely defined point onto its own row and/or\n  column\n- ``factor``: when the obstructions and requirements become local to a set of\n  cells, we factor out the local subtiling\n- ``row and column separation``: if all of the points in a cell in a row must\n  appear below all of the other points in the row, then separate this onto its own\n  row.\n- ``obstruction inferral``: add obstructions that the requirements and\n  obstructions of a tiling imply must be avoided\n- ``fusion``: merge two adjacent rows or columns of a tiling, if it can be\n  viewed as a single row or column with a line drawn between\n\n\nRequirement insertions\n----------------------\n\nThe simplest of all the arguments when enumerating permutation classes is to\nsay, either a tiling is empty or contains a point. This can be viewed in\ntilings as either avoiding ``1: (0, 0)`` or containing ``1: (0, 0)``.\n\n.. code:: python\n\n       \u003e\u003e\u003e from tilings.strategies import CellInsertionFactory\n       \u003e\u003e\u003e strategy_generator = CellInsertionFactory()\n       \u003e\u003e\u003e tiling = Tiling.from_string('231')\n       \u003e\u003e\u003e for strategy in strategy_generator(tiling):\n       ...     print(strategy(tiling))\n       insert 0 in cell (0, 0)\n       +-+            +-+     +-+\n       |1|         =  | |  +  |1|\n       +-+            +-+     +-+\n       1: Av(120)             1: Av+(120)\n                              Requirement 0:\n                              0: (0, 0)\n\nThe same underlying principle corresponds to avoiding or containing any set of\ngridded permutations. There are many different variations of this strategy\nused throughout our ``StrategyPacks``.\n\n.. code:: python\n\n       \u003e\u003e\u003e import tilings\n       \u003e\u003e\u003e print(tilings.strategies.requirement_insertion.__all__)\n       ['CellInsertionFactory', 'RootInsertionFactory', 'RequirementExtensionFactory', 'RequirementInsertionFactory', 'FactorInsertionFactory', 'RequirementCorroborationFactory']\n\nPoint placements\n----------------\n\nThe core idea of this strategy is to place a uniquely defined point onto\nits own row and/or column. For example, here is a code snippet that\nshows the rules coming from placing the extreme (rightmost, topmost, leftmost,\nbottommost) points of a non-empty permutation avoiding ``231``.\n\n.. code:: python\n\n       \u003e\u003e\u003e from tilings.strategies import PatternPlacementFactory\n       \u003e\u003e\u003e strategy = PatternPlacementFactory()\n       \u003e\u003e\u003e tiling = Tiling.from_string('231').insert_cell((0,0))\n       \u003e\u003e\u003e for rule in strategy(tiling):\n       ...     print(rule)\n       placing the rightmost point in cell (0, 0)\n       +-+                +-+-+\n       |1|             =  |\\| |\n       +-+                +-+-+\n       1: Av+(120)        | |●|\n       Requirement 0:     +-+-+\n       0: (0, 0)          |1| |\n                          +-+-+\n                          1: Av(120)\n                          \\: Av(01)\n                          ●: point\n                          Crossing obstructions:\n                          120: (0, 0), (0, 2), (0, 0)\n                          Requirement 0:\n                          0: (1, 1)\n       placing the topmost point in cell (0, 0)\n       +-+                +-+-+-+\n       |1|             =  | |●| |\n       +-+                +-+-+-+\n       1: Av+(120)        |1| |1|\n       Requirement 0:     +-+-+-+\n       0: (0, 0)          1: Av(120)\n                          ●: point\n                          Crossing obstructions:\n                          10: (0, 0), (2, 0)\n                          Requirement 0:\n                          0: (1, 1)\n       placing the leftmost point in cell (0, 0)\n       +-+                +-+-+\n       |1|             =  | |1|\n       +-+                +-+-+\n       1: Av+(120)        |●| |\n       Requirement 0:     +-+-+\n       0: (0, 0)          | |1|\n                          +-+-+\n                          1: Av(120)\n                          ●: point\n                          Crossing obstructions:\n                          10: (1, 2), (1, 0)\n                          Requirement 0:\n                          0: (0, 1)\n       placing the bottommost point in cell (0, 0)\n       +-+                +-+-+-+\n       |1|             =  |\\| |1|\n       +-+                +-+-+-+\n       1: Av+(120)        | |●| |\n       Requirement 0:     +-+-+-+\n       0: (0, 0)          1: Av(120)\n                          \\: Av(01)\n                          ●: point\n                          Crossing obstructions:\n                          120: (0, 1), (2, 1), (2, 1)\n                          Requirement 0:\n                          0: (1, 0)\n\n\nOther algorithms used for automatically enumerating permutation classes have\nused variations of point placements. For example, enumeration schemes and the\ninsertion encoding essentially consider placing the bottommost point into the\nrow of a tiling. Here is a code snippet for calling a strategy that places\npoints into a row of a tiling.\n\n.. code:: python\n\n       \u003e\u003e\u003e from permuta.misc import DIR_SOUTH\n       \u003e\u003e\u003e from tilings.strategies import RowAndColumnPlacementFactory\n       \u003e\u003e\u003e strategy = RowAndColumnPlacementFactory(place_row=True, place_col=False)\n       \u003e\u003e\u003e placed_tiling = tiling.place_point_in_cell((0, 0), DIR_SOUTH)\n       \u003e\u003e\u003e for rule in strategy(placed_tiling):\n       ...     print(rule)\n       placing the topmost point in row 1\n       +-+-+-+                         +-+                +-+-+-+-+                       +-+-+-+-+-+\n       |\\| |1|                      =  |●|             +  |●| | | |                    +  | | | |●| |\n       +-+-+-+                         +-+                +-+-+-+-+                       +-+-+-+-+-+\n       | |●| |                         ●: point           | |\\| |1|                       |\\| |1| |1|\n       +-+-+-+                         Requirement 0:     +-+-+-+-+                       +-+-+-+-+-+\n       1: Av(120)                      0: (0, 0)          | | |●| |                       | |●| | | |\n       \\: Av(01)                                          +-+-+-+-+                       +-+-+-+-+-+\n       ●: point                                           1: Av(120)                      1: Av(120)\n       Crossing obstructions:                             \\: Av(01)                       \\: Av(01)\n       120: (0, 1), (2, 1), (2, 1)                        ●: point                        ●: point\n       Requirement 0:                                     Crossing obstructions:          Crossing obstructions:\n       0: (1, 0)                                          120: (1, 1), (3, 1), (3, 1)     10: (0, 1), (4, 1)\n                                                          Requirement 0:                  10: (2, 1), (4, 1)\n                                                          0: (0, 2)                       120: (0, 1), (2, 1), (2, 1)\n                                                          Requirement 1:                  Requirement 0:\n                                                          0: (2, 0)                       0: (1, 0)\n                                                                                          Requirement 1:\n                                                                                          0: (3, 2)\n       placing the bottommost point in row 1\n       +-+-+-+                         +-+                +-+-+-+-+                       +-+-+-+-+-+\n       |\\| |1|                      =  |●|             +  |\\| | |1|                    +  |\\| |\\| |1|\n       +-+-+-+                         +-+                +-+-+-+-+                       +-+-+-+-+-+\n       | |●| |                         ●: point           | |●| | |                       | | | |●| |\n       +-+-+-+                         Requirement 0:     +-+-+-+-+                       +-+-+-+-+-+\n       1: Av(120)                      0: (0, 0)          | | |●| |                       | |●| | | |\n       \\: Av(01)                                          +-+-+-+-+                       +-+-+-+-+-+\n       ●: point                                           1: Av(120)                      1: Av(120)\n       Crossing obstructions:                             \\: Av(01)                       \\: Av(01)\n       120: (0, 1), (2, 1), (2, 1)                        ●: point                        ●: point\n       Requirement 0:                                     Crossing obstructions:          Crossing obstructions:\n       0: (1, 0)                                          120: (0, 2), (3, 2), (3, 2)     01: (0, 2), (2, 2)\n                                                          Requirement 0:                  120: (0, 2), (4, 2), (4, 2)\n                                                          0: (1, 1)                       120: (2, 2), (4, 2), (4, 2)\n                                                          Requirement 1:                  Requirement 0:\n                                                          0: (2, 0)                       0: (1, 0)\n                                                                                          Requirement 1:\n                                                                                          0: (3, 1)\n\n\n\nRow and column separation\n-------------------------\n\nEvery non-empty permutation in ``Av(231)`` can be written in the form αnβ where\n``α``, ``β`` are permutation avoiding ``231``, and all of the values in ``α``\nare below all of the values in ``β``. The tiling representing placing the\ntopmost point in ``Av(231)`` contains a crossing size 2 obstruction\n``10: (0, 0), (2, 0)``. This obstruction precisely says that the points in the\ncell ``(0, 0)`` must appear below the points in the cell ``(2, 0)``. The\n``RowColumnSeparationStrategy`` will try to separate the rows and columns as\nmuch as possible according to the size two crossing obstructions.\n\n.. code:: python\n\n       \u003e\u003e\u003e from permuta.misc import DIR_NORTH\n       \u003e\u003e\u003e from tilings.strategies import RowColumnSeparationStrategy\n       \u003e\u003e\u003e strategy = RowColumnSeparationStrategy()\n       \u003e\u003e\u003e placed_tiling = tiling.place_point_in_cell((0, 0), DIR_NORTH)\n       \u003e\u003e\u003e rule = strategy(placed_tiling)\n       \u003e\u003e\u003e print(rule)\n       row and column separation\n       +-+-+-+                    +-+-+-+\n       | |●| |                 =  | |●| |\n       +-+-+-+                    +-+-+-+\n       |1| |1|                    | | |1|\n       +-+-+-+                    +-+-+-+\n       1: Av(120)                 |1| | |\n       ●: point                   +-+-+-+\n       Crossing obstructions:     1: Av(120)\n       10: (0, 0), (2, 0)         ●: point\n       Requirement 0:             Requirement 0:\n       0: (1, 1)                  0: (1, 2)\n\n\nFactor\n------\n\nIf there are no crossing obstructions between two cells ``a`` and ``b`` on a\ntiling then the choice of points in ``a`` are independent from the choice\nof points in ``b``.\n\n.. code:: python\n\n       \u003e\u003e\u003e separated_tiling = rule.children[0]\n       \u003e\u003e\u003e from tilings.strategies import FactorFactory\n       \u003e\u003e\u003e strategy_generator = FactorFactory()\n       \u003e\u003e\u003e for strategy in strategy_generator(separated_tiling):\n       ...     print(strategy(separated_tiling))\n       factor with partition {(0, 0)} / {(1, 2)} / {(2, 1)}\n       +-+-+-+            +-+            +-+                +-+\n       | |●| |         =  |1|         x  |●|             x  |1|\n       +-+-+-+            +-+            +-+                +-+\n       | | |1|            1: Av(120)     ●: point           1: Av(120)\n       +-+-+-+                           Requirement 0:\n       |1| | |                           0: (0, 0)\n       +-+-+-+\n       1: Av(120)\n       ●: point\n       Requirement 0:\n       0: (1, 2)\n\nThe ``x`` in the printed above rule is used to denote Cartesian product.\nWe do this to signify that there is a size-preserving bijection between the\ngridded permutations on the left-hand side, to the set of 3-tuples coming from\nthe Cartesian product on the right-hand side, where the size of a tuple is the\nsum of the sizes of the parts. In particular, it implies that the enumeration\nof the gridded permutations on the left-hand side can be computed by applying the\nCauchy product to the enumerations of the three sets of gridded permutations on\nthe right-hand side.\n\nTo guarantee that these rules are always counted using the Cauchy product\nwe must also ensure any two cells on the same row or column are also contained\nin the same factor, otherwise when counting the left-hand side we have to\nconsider the possible interleavings going on.\n\n.. code:: python\n\n       \u003e\u003e\u003e tiling = Tiling.from_string('231_132').insert_cell((0,0))\n       \u003e\u003e\u003e placed_tiling = tiling.place_point_in_cell((0, 0), DIR_SOUTH)\n       \u003e\u003e\u003e strategy_generator = FactorFactory()\n       \u003e\u003e\u003e for strategy in strategy_generator(placed_tiling):\n       ...     print(strategy(placed_tiling))\n       factor with partition {(0, 1), (2, 1)} / {(1, 0)}\n       +-+-+-+            +-+-+         +-+\n       |\\| |/|         =  |\\|/|      x  |●|\n       +-+-+-+            +-+-+         +-+\n       | |●| |            /: Av(10)     ●: point\n       +-+-+-+            \\: Av(01)     Requirement 0:\n       /: Av(10)                        0: (0, 0)\n       \\: Av(01)\n       ●: point\n       Requirement 0:\n       0: (1, 0)\n\nUsing the setting ``all`` in ``FactorFactory`` will allow us to factor\naccording to only the obstructions and requirements.\n\n.. code:: python\n\n       \u003e\u003e\u003e strategy_generator = FactorFactory('all')\n       \u003e\u003e\u003e for strategy in strategy_generator(placed_tiling):\n       ...     print(strategy(placed_tiling))\n       interleaving factor with partition {(0, 1)} / {(1, 0)} / {(2, 1)}\n       +-+-+-+            +-+           +-+                +-+\n       |\\| |/|         =  |\\|        *  |●|             *  |/|\n       +-+-+-+            +-+           +-+                +-+\n       | |●| |            \\: Av(01)     ●: point           /: Av(10)\n       +-+-+-+                          Requirement 0:\n       /: Av(10)                        0: (0, 0)\n       \\: Av(01)\n       ●: point\n       Requirement 0:\n       0: (1, 0)\n\nWe instead use the symbol ``*`` to make us aware that this is not counted\nby the Cauchy product, but we must also count the possible interleavings.\n\n\nObstruction inferral\n--------------------\n\nThe presence of requirements alongside the obstructions on a tiling can\nsometimes be used to imply that all of the gridded permutations on a tiling also avoid\nsome additional obstruction. The goal of ``ObstructionInferral`` is to add these to\na tiling.\n\n.. code:: python\n\n       \u003e\u003e\u003e from permuta.misc import DIR_NORTH\n       \u003e\u003e\u003e tiling = Tiling.from_string('1234_1243_1423_4123')\n       \u003e\u003e\u003e placed_tiling = tiling.partial_place_point_in_cell((0, 0), DIR_NORTH)\n       \u003e\u003e\u003e from tilings.strategies import ObstructionInferralFactory\n       \u003e\u003e\u003e strategy_generator = ObstructionInferralFactory(3)\n       \u003e\u003e\u003e for strategy in strategy_generator(placed_tiling):\n       ...     print(strategy(placed_tiling))\n       added the obstructions {012: (0, 0), (0, 0), (0, 0)}\n       +-+                                      +-+\n       |●|                                   =  |●|\n       +-+                                      +-+\n       |1|                                      |1|\n       +-+                                      +-+\n       1: Av(0123, 0132, 0312, 3012)            1: Av(012)\n       ●: point                                 ●: point\n       Crossing obstructions:                   Requirement 0:\n       0123: (0, 0), (0, 0), (0, 0), (0, 1)     0: (0, 1)\n       0132: (0, 0), (0, 0), (0, 1), (0, 0)\n       0312: (0, 0), (0, 1), (0, 0), (0, 0)\n       3012: (0, 1), (0, 0), (0, 0), (0, 0)\n       Requirement 0:\n       0: (0, 1)\n\nIn the above code snippet, we have added the obstruction\n``gp = 012: (0, 0), (0, 0), (0, 0)``. In particular, the 4 crossing\nobstructions, and the 4 localised obstructions, all contained a copy of ``gp``,\nso we simplify the right-hand side by removing these from the tiling.\nThis simplification step happens automatically when creating a ``Tiling``.\n\nFusion\n------\n\nConsider the gridded permutations on the following tiling.\n\n.. code:: python\n\n       \u003e\u003e\u003e tiling = Tiling([GriddedPerm(Perm((0, 1)), ((0, 0), (0, 0))), GriddedPerm(Perm((0, 1)), ((0, 0), (1, 0))), GriddedPerm(Perm((0, 1)), ((1, 0), (1, 0)))])\n       \u003e\u003e\u003e print(tiling)\n       +-+-+\n       |\\|\\|\n       +-+-+\n       \\: Av(01)\n       Crossing obstructions:\n       01: (0, 0), (1, 0)\n       \u003e\u003e\u003e for i in range(4):\n       ...     for gp in sorted(tiling.gridded_perms_of_length(i)):\n       ...         print(gp)\n       ε:\n       0: (0, 0)\n       0: (1, 0)\n       10: (0, 0), (0, 0)\n       10: (0, 0), (1, 0)\n       10: (1, 0), (1, 0)\n       210: (0, 0), (0, 0), (0, 0)\n       210: (0, 0), (0, 0), (1, 0)\n       210: (0, 0), (1, 0), (1, 0)\n       210: (1, 0), (1, 0), (1, 0)\n\nDue to the crossing ``01`` obstruction it is clear that all of the underlying\npermutations will be decreasing. Moreover, the transition between the left cell\nand the right cell can be between any of the points. In particular, this says\nthere are ``n + 1`` gridded permutations of size ``n`` on this tiling. We\ncapture this idea by fusing the two columns into a single column.\n\n.. code:: python\n\n       \u003e\u003e\u003e from tilings.strategies import FusionFactory\n       \u003e\u003e\u003e strategy_generator = FusionFactory()\n       \u003e\u003e\u003e for rule in strategy_generator(tiling):\n       ...     print(rule)\n       fuse columns 0 and 1\n       +-+-+                      +-+\n       |\\|\\|                   ↣  |\\|\n       +-+-+                      +-+\n       \\: Av(01)                  \\: Av(01)\n       Crossing obstructions:     Assumption 0:\n       01: (0, 0), (1, 0)         can count points in cell (0, 0)\n\nWe use the symbol ``↣`` instead of ``=`` to remind us that the counts of the\ntwo sides are definitely not the same.\nNotice, the right-hand side tiling here also now requires that we can count the\nnumber of points in cell ``(0, 0)``. If there are ``k`` points in cell ``(0, 0)``\nin a gridded permutation then there will be ``k + 1`` gridded permutations that\nfuse to this gridded permutation. Of course, here the number of points in cell``(0, 0)``\nis going to be equal to the size of the gridded permutation, but in general,\nthe points that need to be counted might not cover the whole tiling. For\nexample, the following rule was used within specification to enumerate\n``Av(123)``.\n\n.. code:: python\n\n       \u003e\u003e\u003e tiling = Tiling(\n       ...     [\n       ...         GriddedPerm(Perm((0, 1)), ((0, 0), (0, 0))),\n       ...         GriddedPerm(Perm((0, 1)), ((0, 0), (1, 0))),\n       ...         GriddedPerm(Perm((0, 1)), ((1, 0), (1, 0))),\n       ...         GriddedPerm(Perm((0, 1, 2)), ((0, 0), (2, 0), (2, 0))),\n       ...         GriddedPerm(Perm((0, 1, 2)), ((1, 0), (2, 0), (2, 0))),\n       ...         GriddedPerm(Perm((0, 1, 2)), ((2, 0), (2, 0), (2, 0))),\n       ...     ]\n       ... )\n       \u003e\u003e\u003e for rule in strategy_generator(tiling):\n       ...     print(rule)\n       fuse columns 0 and 1\n       +-+-+-+                         +-+-+\n       |\\|\\|1|                      ↣  |\\|1|\n       +-+-+-+                         +-+-+\n       1: Av(012)                      1: Av(012)\n       \\: Av(01)                       \\: Av(01)\n       Crossing obstructions:          Crossing obstructions:\n       01: (0, 0), (1, 0)              012: (0, 0), (1, 0), (1, 0)\n       012: (0, 0), (2, 0), (2, 0)     Assumption 0:\n       012: (1, 0), (2, 0), (2, 0)     can count points in cell (0, 0)\n\nPerformance\n-----------\nThe `TileScope` algorithm can be resource-intensive in both time and memory. This\ncodebase is fully compatible with `PyPy \u003chttps://www.pypy.org/\u003e`__, an alternative\nPython interpreter that usually runs `TileScope` 5x - 7x faster, at the cost of higher\nmemory usage (sometimes as high as 2x). This extra memory usage is largely caused by\nPyPy's approach to incremental garbage collection, and as a result can be partially\nmitigated by setting the environmental variables\n`described here \u003chttps://doc.pypy.org/en/latest/gc_info.html#environment-variables\u003e`__.\nFor example, the configuration\n\n.. code::\n\n    PYPY_GC_MAJOR_COLLECT=1.1\n    PYPY_GC_MAX_DELTA=200MB\n    PYPY_GC_INCREMENT_STEP=10GB\n\ntends to improve memory usage at the cost of 30% - 50% extra time.\n\nIf memory usage, rather than time usage, is a bottleneck, then the default interpreter\n``CPython`` is preferred.\n\n=========\n\nFinally, we'd like to reiterate, if you need support, have a suggestion, or just\nwant to be up to date with the latest developments please join us on our\n`Discord server \u003chttps://discord.gg/ySJD6SV\u003e`__ where we'd be happy to hear\nfrom you!\n\n\nCiting\n######\n\nIf you found this library helpful with your research and would like to cite us,\nyou can use the following `BibTeX`_ or go to `Zenodo`_ for alternative formats.\n\n.. _BibTex: https://zenodo.org/record/4944108/export/hx#.YMcq7y2l30o\n\n.. _Zenodo: https://doi.org/10.5281/zenodo.4944107\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutatriangle%2Ftilings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpermutatriangle%2Ftilings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutatriangle%2Ftilings/lists"}