{"id":37066150,"url":"https://github.com/danielorlando97/search-space","last_synced_at":"2026-01-14T07:45:44.210Z","repository":{"id":65141355,"uuid":"494183120","full_name":"danielorlando97/search-space","owner":"danielorlando97","description":"Python's DSL to describe the search space of search problems ","archived":false,"fork":false,"pushed_at":"2023-03-24T02:34:10.000Z","size":3179,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-08-28T06:16:59.929Z","etag":null,"topics":["ast","auto-goal","auto-ml","dsl","python","search-problems","search-space","visitor-pattern"],"latest_commit_sha":null,"homepage":"","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielorlando97.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2022-05-19T18:16:16.000Z","updated_at":"2023-02-06T23:15:30.000Z","dependencies_parsed_at":"2023-12-16T21:13:20.406Z","dependency_job_id":"fb3387c9-4f12-494c-99d3-70eb3c3a9328","html_url":"https://github.com/danielorlando97/search-space","commit_stats":{"total_commits":109,"total_committers":2,"mean_commits":54.5,"dds":0.05504587155963303,"last_synced_commit":"2f0fb2f67e356321264428a288d5650a339a5d2d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/danielorlando97/search-space","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielorlando97%2Fsearch-space","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielorlando97%2Fsearch-space/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielorlando97%2Fsearch-space/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielorlando97%2Fsearch-space/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielorlando97","download_url":"https://codeload.github.com/danielorlando97/search-space/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielorlando97%2Fsearch-space/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413489,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["ast","auto-goal","auto-ml","dsl","python","search-problems","search-space","visitor-pattern"],"created_at":"2026-01-14T07:45:43.313Z","updated_at":"2026-01-14T07:45:44.166Z","avatar_url":"https://github.com/danielorlando97.png","language":"Jupyter Notebook","readme":"# 🔭 Python Search Space\n\n[![wakatime](https://wakatime.com/badge/github/danielorlando97/search-space.svg)](https://wakatime.com/badge/github/danielorlando97/search-space)\n[![unit and statistic testing](https://github.com/danielorlando97/search-space/actions/workflows/test.yml/badge.svg)](https://github.com/danielorlando97/search-space/actions/workflows/test.yml)\n\n## 🤔 What's it?\n\nA Python library that defines a DSL to describe search spaces. The DSL defines\ndesign patterns with which the user can decorate their Python classes to describe\ntheir space of interest. The resulting descriptions are declarative, expressive,\ncontext sensitive, extensible and scalable. In addition, the system has a sample\ngenerator system that is consistent and coherent with the selected description.\\\n**Principal Skills**: `Python` `Metaprogramming` `DSL` `AST` `Visitor Pattern`\n\n## 🤓 Motivation\n\nAfter a study of the different systems that try to solve search problems,\nparametric optimization or AutoML, it was detected that there were limitations\nto describe in detail the search spaces. As well as the lack of a tool whose\nmain objective is the description and generation of samples of such spaces.\nExcept for the tools with imperative syntax, none of the tools studied can\nexpress constraints with contextual or conditional dependencies or dynamic\ndimension spaces. The list of tools studied include a:\n[`@autogoal`](https://github.com/autogoal/autogoal/),\n[`@optuna`](https://github.com/optuna/optuna),\n[`@hyperopt`](http://hyperopt.github.io/hyperopt),\n[`@auto-sklearn`](https://github.com/automl/auto-sklearn),\n[`@autogluon`](https://github.com/awslabs/autogluon)\n\n## ⭐ Quickstart\n\n### Factory of Search Space\n\nThe `Domain` space factory is the main public interface to the Python Search Space.\nThis factory is generic to the type of space you want to generate. In addition,\nits constructor can use the following parameters to modify the characteristics of the generated space:\n\n- `min` and `max`: with which the limits of a number space can be defined.\n- `options`: list of options to choose from, this parameter transforms any space to a categorical space.\n- `distribute_like`: string that refers to the random distribution with which the samples of the resulting space will be generated.\n  Its default value is uniform distribution.\n\n```python\n# binary space\nB = Domain[bool]()\n# space of real numbers, (-oo, +oo)\nR = Domain[float]()\n# space of natural numbers, [0, +oo)\nN = Domain[int](min=0)\n# space of negative integers plus zero, [-oo, 0]\n_N = Domain[int](max=0)\n# categorical space of the first 100 powers of 10\nten_powers = [pow(10, i) for i in range(100)]\nP = Domain[int](options = ten_powers)\n# space of colors\nC = Domain[str](options = ['Red', 'Blue', 'Black', 'White'])\n```\n\nAny space generated by the Domain factory define the `get_sample`\nmethod generates random samples according to the respective distribution.\n\n```python\nspace = Domain[int](min=0, max=1)\nvalue, sample_context = sample.get_sample()\n# value is 1 or 0\n```\n\nThe `get_sample` method, in addition to the generated sample,\nreturns the sampling context relative to that sample.\nThis context can be used as a parameter to the `get_sample`\nfunction and ensures that whenever an attempt is made to\ngenerate a sample from the same space with the same context,\nthe same sample will be generated.\n\n```python\nspace = Domain[int](min=0, max=1)\nvalue, sample_context = sample.get_sample()\n# Let value = 1, so\nvalue1, _ = sample.get_sample(context = sample_context)\n# value1 = 1\n```\n\n### Tensorial Spaces\n\nWith the `Domain` factory we can also create tensor spaces.\nTo describe each of the dimensions of the tensors that belong\nto the resulting space, the DSL allows to use ints or random spaces\npreviously defined.\n\n```python\n# space of integer lists with size 10\nL10 = Domain[int][10]()\n# space of all integer lists\nrandom_len = Domain[int]()\nLn = Domain[int][random_len]()\n# Space of all square matrices\nMnxn = Domain[float][random_len][random_len]()\n# Space of all lists of tuple with size 2\nMnx2 = Domain[float][random_len][2]()\n```\n\nIn the case of tensor spaces, the parameters of the constructor of the Domain\nfactory are applied to each of the internal spaces of the domain.\n\n### Constraint Function\n\nThe DSL defines a syntax to be able to describe the details of the search\nspaces we are defining. This syntax is inspired by the definition of\nmathematical domains, where a set is described from the particular\ncharacteristics of any element of the set. To describe these characteristics we use\nthe lambda function syntax of Python. And to use these restriction functions we must\nknow the following characteristics.\n\n- The constraint functions can define as many parameters as we need,\n  but the first one always refers to any element of the space we are\n  describing. The rest of the variables, if they do not have a default\n  value declared, will represent dimensions of the space we are describing.\n\n  ```python\n  # x is any element of A space,\n  # So A = {x in Z, x != 10}\n  A = Domain[int] | (lambda x: x != 10)\n  # B is the space of all strictly increasing lists with size 10\n  B = Domain[int][10] | (lambda x, i: x[i] \u003e x[i+1])\n  # C is the space of all symmetric square matrices with size 10\n  C = Domain[int][10][10] | (lambda x, i, j: x[i][j] == x[j][i])\n  ```\n\n- The constraint functions support all Python arithmetic and\n  comparison operations but some settings are not valid.\n  Check these tests for some of the [valid](https://github.com/danielorlando97/search-space/blob/main/tests/constraint/test_syntaxes_valid.py) and [invalid](https://github.com/danielorlando97/search-space/blob/main/tests/constraint/test_syntaxes_invalid.py) combinations\n\n  ```python\n  valid_space = Domain[int] | (lambda x : x / 10 \u003c 0) # OK\n  invalid_space = Domain[int] | (lambda x : 10 / x \u003c 0) # raise UnSupportOpError\n  ```\n\n- If we want to describe more than one constraint within the same function\n  we must write them all between parentheses and separated by commas.\n\n  ```python\n  # So A = {x in Z, x, in (0, 20) x != 10}\n  A = Domain[int] | (lambda x: (x != 10, x \u003e 0, x \u003c 20)),\n  ```\n\n- The `\u0026`(AND) and `|`(OR) operators have a cutting behavior that allows\n  you to define conditional constraints. If either of these operators\n  can know its result only by evaluating the most left expression then\n  the expression on the right will never be evaluated. In cases where\n  the operands would be constraints then the `\u0026` operator works like\n  commas and the `|` operator defines a segmented domain.\n\n  ```python\n  A = Domain[int] | (lambda x: False \u0026 (x == 0)) # A = Z (integer domain)\n  A = Domain[int] | (lambda x: True \u0026 (x == 0)) # A = { 10 }\n  B = Domain[int] | (lambda x: True | (x == 0)) # B = Z (integer domain)\n  # C is a segmented domain, C = [(-oo, -1), (101, +oo)]\n  # This means that in order to generate samples,\n  # you must first choose between the following segments\n  C = Domain[int] | (lambda x: (x \u003c 0) | (x \u003e 100))\n  ```\n\n### Custom Spaces\n\nAmong the main objectives of the DSL is to define a design pattern\nfor writing a search space using Python's classes. To define a custom\nspace we only have to assign to each of the parameters of the functions\nof the classes a search space as a default value.\n\n```python\nclass Line:\n  \"\"\"\n    We want to find the line that best interpolates our data.\n    And for some reason, we know that the slope is an integer\n    between 50 and 100, but it is not 65. We also know that\n    the intercept of the line is less than 50\n  \"\"\"\n\n  def __init__(\n      self,\n      m: int = Domain[int](min=50, max=100) | (lambda x: x != 65),\n      n: float = Domain[float]() | (lambda x: x \u003c 50)\n  ) -\u003e None:\n      self.m, self.n = m, n\n\n  def get_point(self, x: float = Domain[float](min=0, max=10))\n    return x, self.m * x + self.n\n\n  def contains(self, x, y):\n    return y == self.m * x + self.n\n```\n\nIf we use these decorated classes as a generic type of the Domain\nfactory we can create random spaces where the samples are instances of\nthese classes.\n\n```python\nspace = Domain[Line]()\nline_instance, _ = space.get_sample()\ntype(line_instance) # Line\n\n#If a class method was decorated with the DSL design pattern\n#then its parameters become optional.\n#If no values are specified for those parameters\n#then random ones are generated.\nrandom_point = line_instance.get_point()\nspecific_point = line_instance.get_point(1)\n\nline_instance.contains(0, 0) # OK\nline_instance.contains() # Error\n```\n\n### Contextual Dependencies\n\nTo describe contextual dependencies, a reference to a previously\ndefined search space is needed. This reference will be used as the\ndefault value of one of the parameters of the restriction function\nof the dependent space. In the same way that we describe the\ncharacteristics of a set by means of the particular characteristics\nof any element of it, we will describe the relation between two\nsets by means of the particular relation of any two elements of them.\n\n```python\nclass CenterPoint:\n  \"\"\"\n  We want to find the pointer more centered and with more\n  density around it. For some reason, we know that our data\n  looks like a heavy diagonal 20u thick.\n  All points in the dataset lie between the lines\n  y = x + 10 and y = x - 10.\n  \"\"\"\n\n  Y_Domain = Domain[int]() # previous definition\n\n  def __init__(\n      self,\n      x: float = Domain[float]() | (\n          #Let y be any element of the domain Y_Domain\n          lambda x, y=Y_Domain: (x - 10 \u003c y, y \u003c x + 10)\n      ),\n      y: int = Y_Domain # use the previous definition\n  ) -\u003e None:\n      self.x, self.y = x, y\n```\n\n### Examples of Use Cases\n\n- [Simple Cases, Lines, CenterPoint and LogisticRegression](https://github.com/danielorlando97/search-space/blob/main/tests/examples/basic_class_example_test.py)\n- [Graph Description, Adjacency Matrix and Object Oriented](https://github.com/danielorlando97/search-space/blob/main/tests/examples/graph_examples_test.py) (integration with typing, `Optional` and `Self` spaces)\n- [Transformation of classical problems to search problems, The Bag](https://github.com/danielorlando97/search-space/blob/main/tests/examples/greedy_or_dp_example.py) (hierarchical descriptions, inheritance and reference between classes)\n- [Detailed description of combinatorial spaces, Color Map](https://github.com/danielorlando97/search-space/blob/main/tests/examples/ia_examples_test.py) (Imperative constrains)\n- [AutoML, model selection and parametric description, Semi-supervised Model](https://github.com/danielorlando97/search-space/blob/main/tests/examples/auto_ml_example_test.py)\n\n\u003c!--\n#### Valid Syntaxes\n\n```python\nDomain[int] | (lambda x: x | True)\nDomain[int] | (lambda x: True | x)\nDomain[int] | (lambda x: x \u0026 True)\nDomain[int] | (lambda x: True \u0026 x)\nDomain[str] | (lambda x: x == 'a') # (lambda x: 'a' == x)\nDomain[int] | (lambda x: x != [1, 2]) # (lambda x: [1, 2] != x)\nDomain[int] | (lambda x: x \u003c 10) # (lambda x: 10 \u003c x)\nDomain[int] | (lambda x: x \u003e 10) # (lambda x: 10 \u003e x)\nDomain[int] | (lambda x: x \u003c= 10) # (lambda x: 10 \u003c= x)\nDomain[int] | (lambda x: x \u003e= 10) # (lambda x: 10 \u003e= x)\nDomain[int] | (lambda x: x + 10) # (lambda x: 10 + x)\nDomain[int] | (lambda x: x - 10) # (lambda x: 10 - x)\nDomain[int] | (lambda x: x * 10) # (lambda x: 10 * x)\nDomain[int] | (lambda x: x / 10) # (lambda x: 10 / x)\nDomain[int] | (lambda x: x % 10)\nDomain[int] | (lambda x: x % 10 % 10)\nDomain[int] | (lambda x: x % 10 + 10)\nDomain[int] | (lambda x: x % 10 - 10)\nDomain[int] | (lambda x: x % 10 * 10)\nDomain[int] | (lambda x: x % 10 / 10)\nDomain[int] | (lambda x: x % 10 == 10)\nDomain[int] | (lambda x: x % 10 != 10)\nDomain[int] | (lambda x: x % 10 \u003c 10)\nDomain[int] | (lambda x: x % 10 \u003c= 10)\nDomain[int] | (lambda x: x % 10 \u003e 10)\nDomain[int] | (lambda x: x % 10 \u003e= 10)\n```\n\n#### Invalid Syntaxes\n\n```python\nDomain[int] | (lambda x: 10 % x)\nDomain[int] | (lambda x: False | x \u003c 3)\nDomain[int] | (lambda x: x \u003e 5 | x \u003c 3)\nDomain[int] | (lambda x: True \u0026 x \u003c 3)\nDomain[int] | (lambda x: x \u003e 5 \u0026 x \u003c 3)\nDomain[int] | (lambda x: (x == x) \u003c 3)\nDomain[int] | (lambda x: (x == x) + 3)\nDomain[int] | (lambda x: (x != x) \u003e 3)\nDomain[int] | (lambda x: (x != x) - 3)\nDomain[int] | (lambda x: (x \u003c x) == 3)\nDomain[int] | (lambda x: (x \u003c x) * 3)\nDomain[int] | (lambda x: (x \u003e x) \u003c= 3)\nDomain[int] | (lambda x: (x \u003e x) % 3)\nDomain[int] | (lambda x: (x + 3)[3])\nDomain[int] | (lambda x: (x + 3).member)\nDomain[int] | (lambda x: (x % 3 == 1) + 5)\nDomain[int] | (lambda x: (x % 3 == 1) \u003c 5)\nDomain[int][6][6][6]() | (lambda x, i, j: x[i][j] == x[j][i])\n\n```\n--\u003e\n\u003c!--\n#### Imperative Restrictions\n\n## ⚙️ Installation\n\n## 📚 Documentation\n--\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielorlando97%2Fsearch-space","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielorlando97%2Fsearch-space","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielorlando97%2Fsearch-space/lists"}