{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "YzCtfSLPk6Ps" }, "source": [ "# PyGlove: A Bird View\n", "\n", "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/pyglove/blob/main/docs/notebooks/intro/birdview.ipynb)\n", "\n", "Welcome to PyGlove! If you are new to PyGlove, this notebook is for you. It typically takes 5-10 minutes to complete, with code examples. By the end of this read, you'll have a brief understanding of what PyGlove is, when it is useful and how to use it, in particular:\n", "\n", "* Symbolic object-oriented programming (SOOP): You'll learn how to develop symbolic classes effectively and explore important concepts like symbolic operations, events, placeholding, and detour.\n", " \n", "* PyGlove's applications: We'll provide an overview of the various applications of PyGlove, with a particular focus on program search (e.g. evolutionary computing, AutoML).\n", "\n", "With these knowledge, you'll be well-prepared to delve deeper into PyGlove and its extensive features, allowing you utilize it effectively in a variety of programming contexts." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "4w3IL7GvI-Qd" }, "outputs": [], "source": [ "!pip install pyglove" ] }, { "cell_type": "markdown", "metadata": { "id": "pFQ7WU0Vq1C9" }, "source": [ "## SOOP: PyGlove's Foundation\n", "\n", "PyGlove introduces symbolic object-oriented programming (SOOP) based on the concept of symbolic objects, which can be utilized both as regular objects and manipulated based on their representations. Symbolic objects are instances of symbolic classes, here we use a simple symbolic `Foo` class to illustrate the basics features of symbolic objects. " ] }, { "cell_type": "markdown", "metadata": { "id": "rxMTl0VEA5lK" }, "source": [ "### Developing Symbolic Classes\n", "First, symbolic classes can be created via the `@pg.symbolize` decorator:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "executionInfo": { "elapsed": 897, "status": "ok", "timestamp": 1683331999042, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "OzoeJKKZqyix" }, "outputs": [], "source": [ "import pyglove as pg\n", "\n", "@pg.symbolize\n", "class Foo:\n", " def __init__(self, x, y):\n", " self.z = x + y\n", " \n", " def hello(self):\n", " print('hello, pyglove!')" ] }, { "cell_type": "markdown", "metadata": { "id": "fSavlZ7ZrhSa" }, "source": [ "Then we can create an instance of `Foo`, which is a symbolic object that can be used as usual." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "executionInfo": { "elapsed": 5, "status": "ok", "timestamp": 1683331999191, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "0Elg_mTnk3mG", "outputId": "46275328-9e67-4715-cbd9-ad58044937d6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello, pyglove!\n" ] }, { "data": { "text/plain": [ "3" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo = Foo(1, 2)\n", "foo.hello()\n", "foo.z" ] }, { "cell_type": "markdown", "metadata": { "id": "j6V9kOchBF9g" }, "source": [ "### Symbolic Operations\n", "\n", "Symbolic operations can be applied on symbolic objects.\n", "First, we can print the human-readable symbolic representation of `foo` object:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "executionInfo": { "elapsed": 4, "status": "ok", "timestamp": 1683331999366, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "KtfzmchSsttp", "outputId": "d605acba-c8cc-4d62-d470-e246d8b4636b" }, "outputs": [ { "data": { "text/plain": [ "Foo(x=1, y=2)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo" ] }, { "cell_type": "markdown", "metadata": { "id": "JikyEdeys9DZ" }, "source": [ "`foo` is also mutable, the internal state `z` is automatically recomputed from the new `x` and the old `y`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "executionInfo": { "elapsed": 4, "status": "ok", "timestamp": 1683331999541, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "UquPziOcs8Xg", "outputId": "90173bda-8043-4686-e01a-ba681c5dc584" }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.rebind(x=2)\n", "foo.z" ] }, { "cell_type": "markdown", "metadata": { "id": "MckJy_4ktRsk" }, "source": [ "But where is `y` stored? From the class definition, it's not assigned to any member of `foo`. The answer is, `y` is stored as a symbolic attribute of `foo`, which can be accessed via the `sym_init_args` property:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "executionInfo": { "elapsed": 4, "status": "ok", "timestamp": 1683331999717, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "hi_4jrJEtRPH", "outputId": "935e2dcb-13a4-4fc1-fae5-2da9e57a74e8" }, "outputs": [ { "data": { "text/plain": [ "{x=2, y=2}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo.sym_init_args" ] }, { "cell_type": "markdown", "metadata": { "id": "ggQnarYK0fVl" }, "source": [ "Moreover, we can query `foo`'s symbolic representation:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "executionInfo": { "elapsed": 3, "status": "ok", "timestamp": 1683331999860, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "rfLjD9Iu0ksB", "outputId": "48f32bc0-161a-462d-cca8-9d267a9d101c" }, "outputs": [ { "data": { "text/plain": [ "{'x': 2, 'y': 2}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pg.query(foo, where=lambda v: isinstance(v, int))" ] }, { "cell_type": "markdown", "metadata": { "id": "pX4eXIvUtnii" }, "source": [ "Another important aspect of symbolic object is *symbolic equality*, which is\n", "the critical for comparing if two concepts are the same. Symbolic comparison is performed based on neither reference, nor their internal states, but solely based on their symbolic representations:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "executionInfo": { "elapsed": 57, "status": "ok", "timestamp": 1683332000076, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "QWdHh1wcuB0D", "outputId": "f5e74d48-53e0-44ee-e47a-c2b7ca29ebce" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "False\n" ] } ], "source": [ "print(pg.eq(foo, Foo(2, 2)))\n", "print(pg.eq(foo, Foo(2, 3)))" ] }, { "cell_type": "markdown", "metadata": { "id": "hqYHq_T7KivQ" }, "source": [ "Similarly, we can *symbolically compare* two symbolic objects:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "executionInfo": { "elapsed": 4, "status": "ok", "timestamp": 1683332000354, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "il39spqjKg2f", "outputId": "b23fa819-901d-4e58-d83b-956173198f4e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "False\n" ] } ], "source": [ "print(pg.lt(foo, Foo(2, 3)))\n", "print(pg.lt(foo, Foo(0, 3)))" ] }, { "cell_type": "markdown", "metadata": { "id": "5ivTBC3vuV0q" }, "source": [ "We can also get the *symbolic hash* from it, which is computed soely from symbolic representations:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "executionInfo": { "elapsed": 4, "status": "ok", "timestamp": 1683332000526, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "CU_y864suaDN", "outputId": "f012d97c-353f-44fe-a514-b24f16fa63fb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1925191923907587291\n", "1925191923907587291\n" ] } ], "source": [ "print(pg.hash(foo))\n", "print(pg.hash(Foo(2, 2)))" ] }, { "cell_type": "markdown", "metadata": { "id": "j6MoKBicumBE" }, "source": [ "Moreover, `foo` can be *symbolically cloned*:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "executionInfo": { "elapsed": 3, "status": "ok", "timestamp": 1683332000678, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "vjlZD08guwK7", "outputId": "c63b86f8-effd-4bea-8652-da2b74b507b0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n" ] } ], "source": [ "foo2 = foo.clone()\n", "print(foo2 != foo)\n", "print(pg.eq(foo2, foo))" ] }, { "cell_type": "markdown", "metadata": { "id": "EDJVlu4Su6lG" }, "source": [ "It can also be *symbolically serialized* and *deserialized*:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "executionInfo": { "elapsed": 3, "status": "ok", "timestamp": 1683332000900, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "fg0HXONyu5_Q", "outputId": "e03bc5b5-bc00-4048-f328-2d84dd23ad69" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\"_type\": \"__main__.Foo\", \"x\": 2, \"y\": 2}\n", "True\n" ] } ], "source": [ "json_str = foo.to_json_str()\n", "print(json_str)\n", "\n", "foo3 = pg.from_json_str(json_str)\n", "print(pg.eq(foo3, foo))" ] }, { "cell_type": "markdown", "metadata": { "id": "Mx-H3I4axUUo" }, "source": [ "### Symbolic Validation\n", "\n", "Then let's take a look at symbolic validation. What if we want to limit the value of `x` and `y` to be positive integers? We can pass a list of symbolic field definitions to `@pg.symbolize`:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "executionInfo": { "elapsed": 2, "status": "ok", "timestamp": 1683332001089, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "1IgtVaYSx1E6" }, "outputs": [], "source": [ "@pg.symbolize([\n", " ('x', pg.typing.Int(min_value=1)),\n", " ('y', pg.typing.Int(min_value=1)),\n", "])\n", "class Foo:\n", " def __init__(self, x, y):\n", " self.z = x + y" ] }, { "cell_type": "markdown", "metadata": { "id": "0iW4Y8VczHiL" }, "source": [ "Then we can see the instantiation on `Foo` will fail since the value of `x` is not positive." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "executionInfo": { "elapsed": 5, "status": "ok", "timestamp": 1683332001266, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "BII1ml7pyDuy", "outputId": "6a33eb09-8438-435e-80f4-57cc8a8e9052" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error Value 0 is out of range (min=1, max=None). (path=x)\n" ] } ], "source": [ "\n", "try:\n", " Foo(0, 1)\n", "except ValueError as e:\n", " print('Error', e)" ] }, { "cell_type": "markdown", "metadata": { "id": "wT84zHylzVII" }, "source": [ "Value specifications of symbolic objects can also be programmed:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "executionInfo": { "elapsed": 76, "status": "ok", "timestamp": 1683332001521, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "707907eDzlqh", "outputId": "6e227158-085a-451d-977c-d7abcc89658a" }, "outputs": [ { "data": { "text/plain": [ "Bar(x= at 0x7fdd31d3fb50>)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def value_or_factory(value_spec):\n", " return pg.typing.Union([\n", " value_spec,\n", " pg.typing.Callable(returns=value_spec)\n", " ])\n", "\n", "@pg.symbolize([\n", " ('x', value_or_factory(pg.typing.Int(min_value=1))),\n", "])\n", "class Bar:\n", " def __init__(self, x):\n", " pass\n", "\n", "# Okay!\n", "Bar(1)\n", "\n", "# Okay too!\n", "Bar(lambda: 1)" ] }, { "cell_type": "markdown", "metadata": { "id": "rZzX-GkJA8cl" }, "source": [ "### Symbolic Events" ] }, { "cell_type": "markdown", "metadata": { "id": "TtNDZzc7wW4B" }, "source": [ "Symbolic objects are also aware of its containing object and the topology:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "executionInfo": { "elapsed": 4, "status": "ok", "timestamp": 1683332001709, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "LlIboBlFwNRf", "outputId": "6b671fa5-3004-4abf-d2da-02f48d7fc0aa" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "None\n", "\n", "{\n", " x = Foo(\n", " x = 2,\n", " y = 2\n", " )\n", "}\n", "x\n", "None\n", "\n" ] } ], "source": [ "foo = pg.from_json_str(json_str)\n", "print(foo.sym_parent)\n", "print(foo.sym_path)\n", "\n", "# When `foo` is assigned to a symbolic dict,\n", "# its `sym_parent` property get updated.\n", "d = pg.Dict(x=foo)\n", "print(foo.sym_parent)\n", "print(foo.sym_path)\n", "\n", "# When the parent dict reassign the key to another\n", "# value, `foo.sym_parent` get updated again.\n", "d.x = 1\n", "print(foo.sym_parent)\n", "print(foo.sym_path)" ] }, { "cell_type": "markdown", "metadata": { "id": "ugp9eMUu2gNs" }, "source": [ "We can also subscribe to such events:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "executionInfo": { "elapsed": 3, "status": "ok", "timestamp": 1683332001925, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "PySl6DYK3eW-", "outputId": "5ab10798-276e-43fa-93ad-c7f65fd6a180" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Path has changed from to x\n", "Parent has changed from None to {}\n", "Parent has changed from {x=Foo(x=1, y=2)} to None\n", "Path has changed from x to \n" ] } ], "source": [ "@pg.symbolize([\n", " ('x', pg.typing.Int(min_value=1)),\n", " ('y', pg.typing.Int(min_value=1)),\n", "])\n", "class Foo:\n", " def __init__(self, x, y):\n", " self.z = x + y\n", "\n", " def _on_parent_change(self, old_parent, new_parent):\n", " super()._on_parent_change(old_parent, new_parent)\n", " print('Parent has changed from %r to %r'\n", " % (old_parent, new_parent))\n", "\n", " def _on_path_change(self, old_path, new_path):\n", " super()._on_path_change(old_path, new_path)\n", " print('Path has changed from %r to %r'\n", " % (old_path, new_path))\n", "\n", "f = Foo(1, 2)\n", "d = pg.Dict(x=f)\n", "d.x = 1" ] }, { "cell_type": "markdown", "metadata": { "id": "-055dv-2-3We" }, "source": [ "### Using Symbolic Functions\n", "Besides symbolic classes, functions can be symbolized too:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "executionInfo": { "elapsed": 2, "status": "ok", "timestamp": 1683332002151, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "joiZp47L-7q6" }, "outputs": [], "source": [ "@pg.symbolize\n", "def foo(x, y):\n", " return x + y" ] }, { "cell_type": "markdown", "metadata": { "id": "5p18yDF9_As3" }, "source": [ "An instance of a symbolic function is also a symbolic object, since the symbolic function is converted into a symbolic class with a `__call__` method:\n" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "executionInfo": { "elapsed": 66, "status": "ok", "timestamp": 1683332002390, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "IKE0wy46_Kzs", "outputId": "839ea72d-e777-44a2-d080-dbf9c68514d9" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo(\n", " x = 1,\n", " y = 2\n", ")\n" ] }, { "data": { "text/plain": [ "3" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# `f` is a symbolic object representing a bound\n", "# symbolic function `foo`.\n", "f = foo(1, 2)\n", "print(f)\n", "\n", "# It can be invoked separately.\n", "f()" ] }, { "cell_type": "markdown", "metadata": { "id": "WMTJZ06e_ffO" }, "source": [ "Besides the capabilities inherited from symbolic classes, symbolic functions are very powerful in its advanced binding capabilities:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "executionInfo": { "elapsed": 6, "status": "ok", "timestamp": 1683332002578, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "Sm65KQb2_ouI", "outputId": "56f0b6d0-b28a-4ce0-95ef-e1820c56313a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo(\n", " x = 1,\n", " y = MISSING_VALUE(Any())\n", ")\n", "2\n", "MISSING_VALUE(Any())\n", "foo(\n", " x = 1,\n", " y = 2\n", ")\n", "foo(\n", " x = 2,\n", " y = 2\n", ")\n", "4\n" ] }, { "data": { "text/plain": [ "5" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Partial binding with x=1:\n", "f = foo(1)\n", "print(f)\n", "\n", "# Call `f` with `y` specified at call time,\n", "# however, `y` is not bound with `f`.\n", "print(f(y=1))\n", "print(f.y)\n", "\n", "# Incrementally bind y=2:\n", "f.y = 2\n", "print(f)\n", "\n", "# Rebind x to 2:\n", "f.rebind(x=2)\n", "print(f)\n", "print(f())\n", "\n", "# Override args at call time:\n", "f(x=3, override_args=True)" ] }, { "cell_type": "markdown", "metadata": { "id": "mbXOy0Fv4ReX" }, "source": [ "### Symbolic Placeholding\n", "\n", "A symbolic object can be placeheld with pure symbolic values, which\n", "represents a high-level concept that is not materialized yet for execution:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "executionInfo": { "elapsed": 3, "status": "ok", "timestamp": 1683332002852, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "5ZVQKVHs4j3Q", "outputId": "0a504116-1548-40f0-f828-be19b7e3976b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Foo(\n", " x = TBD(),\n", " y = TBD()\n", ")\n", "True\n" ] } ], "source": [ "class TBD(pg.symbolic.PureSymbolic, pg.Object):\n", " pass\n", "\n", "foo = Foo(TBD(), TBD())\n", "print(foo)\n", "print(pg.is_abstract(foo))" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "executionInfo": { "elapsed": 7, "status": "ok", "timestamp": 1683332003039, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "2TJCGHXM5Uks", "outputId": "16230cb7-5450-4042-93dd-0f27411b30e3" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error 'Foo' object has no attribute 'z'\n" ] } ], "source": [ "try:\n", " # `foo` is abstract, so `Foo.__init__` is not yet\n", " # called on `foo`.\n", " foo.z\n", "except AttributeError as e:\n", " print('Error', e)" ] }, { "cell_type": "markdown", "metadata": { "id": "e3yEe4_44Mzm" }, "source": [ "### Symbolic Detour\n", "Sometimes, objects we care about are not exposed as function or class arguments:\n", "\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "executionInfo": { "elapsed": 59, "status": "ok", "timestamp": 1683332003308, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "oo9Nkm-gB1W4", "outputId": "2f33eaad-18ff-4bad-edbf-fa63e7dd384b" }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class A:\n", "\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", "\n", " def __call__(self):\n", " return self.x + self.y\n", "\n", "def my_fun():\n", " return A(1, 2)()\n", "\n", "my_fun()" ] }, { "cell_type": "markdown", "metadata": { "id": "P8jsiQmZCHMT" }, "source": [ "Can we modify `my_fun`'s behavior without modifying its source code? Symbolic detour is the tool for that purpose:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "executionInfo": { "elapsed": 3, "status": "ok", "timestamp": 1683332003468, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "nTG0WyAmCWKI", "outputId": "c7922071-3ad9-49f9-d4e4-e8a4441525c6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "3\n" ] } ], "source": [ "class B(A):\n", "\n", " def __call__(self):\n", " return self.x * self.y\n", "\n", "# Within this context manager,\n", "# A.__new__ is redirected to B.__new__.\\\n", "# NOTE: `pg.detour` can detour Python\n", "# classes that are even not symbolized.\n", "# However, it cannot be used to detour\n", "# builtins.\n", "with pg.detour([(A, B)]):\n", " print(my_fun())\n", "\n", "# Out of the context manager,\n", "# A.__new__ remains its original definition.\n", "print(my_fun()) " ] }, { "cell_type": "markdown", "metadata": { "id": "HJV1TQm8LZyd" }, "source": [ "## PyGlove's Applications" ] }, { "cell_type": "markdown", "metadata": { "id": "zxCVLLaU5sFT" }, "source": [ "### Program Search\n", "\n", "Based on symbolic placeholding, PyGlove provides a few pure symbolic classes called *hyper primitives* which are helpful in representing a space of objects:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "executionInfo": { "elapsed": 5, "status": "ok", "timestamp": 1683332003662, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "FL63ST4H56UN", "outputId": "d4ce8dc9-384d-4e0b-b131-0a2a13208388" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Space({\n", " 0 = 'x': Choices(num_choices=1, [\n", " (0): 1\n", " (1): 2\n", " ])\n", "})\n", "2\n" ] } ], "source": [ "# A space of `Foo` with `x` chosen from [1, 2] and a fixed `y`.\n", "foo_space = Foo(pg.oneof([1, 2]), 3)\n", "\n", "# We can inspect the space by looking at its genome shape:\n", "print(pg.dna_spec(foo_space))\n", "\n", "# Or looking at the size of the space:\n", "print(pg.dna_spec(foo_space).space_size)" ] }, { "cell_type": "markdown", "metadata": { "id": "To9oaDJP62dT" }, "source": [ "For `foo_space` to be materialized, we need to provide the\n", "choice for `x`:\n" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "executionInfo": { "elapsed": 2, "status": "ok", "timestamp": 1683332003836, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "ZEcjSWKe68le", "outputId": "ee48655c-9506-433c-f4e4-09c5b31ea089" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Foo(\n", " x = 1,\n", " y = 3\n", ")\n" ] } ], "source": [ "# Providing a genome to the space so it can be converted to\n", "# a sample in the space:\n", "foo = pg.materialize(foo_space, pg.DNA(0))\n", "print(foo)" ] }, { "cell_type": "markdown", "metadata": { "id": "tTWB2cmV7MIC" }, "source": [ "We can also use hyper primitives to generate samples from the space:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "executionInfo": { "elapsed": 64, "status": "ok", "timestamp": 1683332004080, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "UOUD_fkl7Vxw", "outputId": "2318b3c4-f7c0-4808-c0e8-a48a3e352e54" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[Foo(x=1, y=3), Foo(x=1, y=4), Foo(x=2, y=3), Foo(x=2, y=4)]\n", "[Foo(x=1, y=3), Foo(x=2, y=3), Foo(x=2, y=4)]\n" ] } ], "source": [ "# Generate all possible examples from the space.\n", "foo_space = Foo(pg.oneof([1, 2]), pg.oneof([3, 4]))\n", "print(list(pg.iter(foo_space)))\n", "\n", "# Random sample 3 examples:\n", "print([x for x in pg.random_sample(foo_space, num_examples=3, seed=1)])" ] }, { "cell_type": "markdown", "metadata": { "id": "gK81vSeB8OOR" }, "source": [ "Based on the search space, we can evolve a `Foo` to maximize the evaluations:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "executionInfo": { "elapsed": 11028, "status": "ok", "timestamp": 1683332015267, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "fqPgjPii8Nin", "outputId": "8eb244f3-ad72-4808-e5ea-2377f49e9b2e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Space size 10000\n" ] } ], "source": [ "# Create a larger search space.\n", "foo_space = Foo(pg.oneof(range(1, 101)), pg.oneof(range(1, 101)))\n", "print('Space size', pg.dna_spec(foo_space).space_size)\n", "\n", "# Use regularized evolution as search algorithm.\n", "# Please refer to https://arxiv.org/abs/1802.01548\n", "search_algorithm = pg.evolution.regularized_evolution(\n", " pg.evolution.mutators.Uniform(),\n", " population_size=100,\n", " tournament_size=20)\n", "\n", "# Perform the search, using `z` (x + y) as the reward.\n", "rewards = []\n", "for foo, feedback in pg.sample(\n", " foo_space, search_algorithm, num_examples=500):\n", " reward = foo.z\n", " feedback(reward)\n", " rewards.append(reward)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "colab": { "height": 265 }, "executionInfo": { "elapsed": 314, "status": "ok", "timestamp": 1683332015727, "user": { "displayName": "", "userId": "" }, "user_tz": 420 }, "id": "ic48k4kW9vQc", "outputId": "397c1769-385e-40c6-e3cb-4cdc6e6b6725" }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot the reward over time:\n", "import matplotlib.pyplot as plt\n", "plt.plot(range(len(rewards)), rewards)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "Y_IyzlSeOI36" }, "source": [ "### Programs that Need Lots of Flexiblity \n", "\n", "SOOP offer a high degree of flexibility in programming due to the mutability of symbolic objects. This not only enables developers to utilize partial or late binding, but also grants access to the power of rebinding. Program search is just one example of such usage. Additionally, other use cases like flexible function binding and direct manipulation demonstrate the versatility of symbolic objects. For more information on these examples, please refer to [Symbolic Functions](https://pyglove.readthedocs.io/en/latest/notebooks/intro/basics/symbolic_function.html) and [Interactive SVG](https://pyglove.readthedocs.io/en/latest/notebooks/python/interactive_svg.html)." ] }, { "cell_type": "markdown", "metadata": { "id": "2QrQ-gP5L-Ps" }, "source": [ "### Representation-centric Systems\n", "\n", "Symbolic objects inherently serve as representations, simplifying representation-centric systems when used with SOOP. Machine learning systems are a prime example of such systems that typically rely on a separate configuration system, which is no longer required with PyGlove. For further details on PyGlove's application in machine learning, please refer to the [Symbolic Machine Learning](https://pyglove.readthedocs.io/en/latest/guide/ml/index.html) examples.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "-zLVMdkKQGrU" }, "source": [ "### More\n", "\n", "PyGlove offers a wide range of capabilities for you to discover, such as the ability to create [domain-specific languages](https://pyglove.readthedocs.io/en/latest/notebooks/python/sticky_notes.html) using symbolic placeholding and develop [context-aware components](https://pyglove.readthedocs.io/en/latest/notebooks/python/where_is_the_duck.html) that can seamlessly adjust to changes in object hierarchy. We are excited to see the innovative ways in which you utilize SOOP and PyGlove and look forward to learning from you." ] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 0 }