diff --git a/examples/introduction.ipynb b/examples/introduction.ipynb index f0aec13..99a2d8c 100644 --- a/examples/introduction.ipynb +++ b/examples/introduction.ipynb @@ -258,6 +258,14 @@ "error" ] }, + { + "cell_type": "markdown", + "id": "9e7320d9-560e-4cac-bb48-a8fbadc3652b", + "metadata": {}, + "source": [ + "Note that this error can be silenced by setting `albert.ALLOW_NON_EINSTEIN_NOTATION` to `True`. This is experimental and *will* break things, but in simple cases of static expression construction, allows at least basic support for expressions that do not obey Einstein summation notation." + ] + }, { "cell_type": "markdown", "id": "e3eda021-48cc-4e91-bf0f-c4e235cf1d4f", @@ -664,6 +672,14 @@ "rhs.canonicalise().collect()" ] }, + { + "cell_type": "markdown", + "id": "0109407d-582d-48b6-b61d-47e8b28520b8", + "metadata": {}, + "source": [ + "Setting `albert.INFER_ALGEBRA_SYMMETRIES` to `True` will allow `albert` to infer the symmetries of algebraic nodes (`Add` or `Mul`) on construction, from the symmetries provided for the children nodes. This is often very useful for determining the permutational symmetries of output tensors or intermediates, when the symmetry of the inputs is known, but it may impact performance noticeably and is therefore disabled by default." + ] + }, { "cell_type": "markdown", "id": "e9e0dd67-fac8-4f2c-a5fb-7b04034346b8", @@ -684,7 +700,9 @@ "- `delete`, to set the value of a node matching some type filter to zero;\n", "- `apply`, to call a function on a node matching some type filter, and replace the node with the result.\n", "\n", - "The type filters can be a type to match the instance of, a tuple of types, or a function that evaluates to `True` or `False` when called with the node as the argument. Since nodes are immutable objects, each method returns a copy of the original node, which may contain references to existing nodes." + "The type filters can be a type to match the instance of, a tuple of types, or a function that evaluates to `True` or `False` when called with the node as the argument. Since nodes are immutable objects, each method returns a copy of the original node, which may contain references to existing nodes.\n", + "\n", + "Each function also has a `depth` and `order` argument that can be used to control the maximum depth of the search, and whether the search proceeds in post- or pre-order." ] }, { @@ -801,7 +819,49 @@ "id": "b4682871-4ecf-4ebb-a241-e10223f185d1", "metadata": {}, "source": [ - "The `apply` function should be used for the majority of cases where one wishes to algebraically manipulate the expression. Many complex substitutions, morphisms, and other manipulations are possible through this approach." + "The `apply` function should be used for the majority of cases where one wishes to algebraically manipulate the expression. Many complex substitutions, morphisms, and other manipulations are possible through this approach. Another example is symbolically representing tensor factorisations." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "28459f0a-b3ad-46c7-9485-ed38740a04d2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "u(i,x0) * v(k,x0) * u(k,x1) * v(j,x1)" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rhs = from_string(\"a(i,k) * a(k,j)\")\n", + "\n", + "count = 0\n", + "\n", + "def factorise_a(tensor: Tensor) -> Tensor:\n", + " global count\n", + " if tensor.name == \"a\":\n", + " i, j = tensor.indices\n", + " x = Index(f\"x{count}\")\n", + " count += 1\n", + " return Tensor(i, x, name=\"u\") * Tensor(j, x, name=\"v\")\n", + " return tensor\n", + "\n", + "rhs.apply(factorise_a, Tensor)" + ] + }, + { + "cell_type": "markdown", + "id": "963f187c-d682-4618-a81f-60fe50b41ed7", + "metadata": {}, + "source": [ + "Lastly, the `children` property can also be used to directly access the children of a node, allowing the user to implement custom traversals for more complex requirements." ] }, { @@ -822,21 +882,10 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "57ed7b0f-8b06-4624-a96c-bca78bd716cc", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import numpy as np\n", "\n", @@ -864,51 +913,10 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "6e932a2e-6ec7-49a8-9118-a61b50ce8a9f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\"\"\"Code generated by `albert` version 0.0.0.\n", - "\n", - " * date: 2025-10-30T16:40:26.509729\n", - " * python version: 3.10.12 (main, Aug 15 2025, 14:32:43) [GCC 11.4.0]\n", - " * albert version: 0.0.0\n", - " * caller: /home/ollie/git/albert/albert/code/einsum.py\n", - " * node: ollie-desktop\n", - " * system: Linux\n", - " * processor: x86_64\n", - " * release: 6.8.0-86-generic\n", - "\"\"\"\n", - "\n", - "from types import SimpleNamespace\n", - "import numpy as np\n", - "\n", - "\n", - "def my_function(a=None, b=None, c=None, d=None, **kwargs):\n", - " \"\"\"Code generated by `albert` 0.0.0.\n", - "\n", - " Args:\n", - " a: \n", - " b: \n", - " c: \n", - " d: \n", - "\n", - " Returns:\n", - " x: \n", - " \"\"\"\n", - "\n", - " x = np.einsum(a, (0, 1), b, (2, 1), d, (3, 2), (0, 3), optimize=True)\n", - " x += np.einsum(c, (0, 1), d, (2, 1), (0, 2), optimize=True)\n", - "\n", - " return x\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "from albert.code.einsum import EinsumCodeGenerator\n", "import sys\n", @@ -949,21 +957,10 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "a6d62317-c347-4328-a311-8aa4b0a71921", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(True, False)" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "x1 = Tensor(Index(\"i\"), Index(\"j\"), name=\"x\")\n", "x2 = Tensor(Index(\"i\"), Index(\"j\"), name=\"x\")\n", @@ -972,21 +969,10 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "935019aa-5766-4106-88a3-eb5a3ac0b475", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(True, True)" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "x1 = Tensor.factory(Index(\"i\"), Index(\"j\"), name=\"x\")\n", "x2 = Tensor.factory(Index(\"i\"), Index(\"j\"), name=\"x\")\n", @@ -1003,21 +989,10 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "id": "644dd968-7241-477a-933f-651e29f84459", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, a(i,j), a(i,j) + b(i,j), a(i,j) * c(j,k)]" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "nodes = [\n", " from_string(\"a(i,j) * c(j,k)\"),\n", @@ -1038,50 +1013,10 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "fc3dd23e-d80a-417f-8da3-c930e4691a3d", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'_type': 'Mul',\n", - " '_module': 'albert.algebra',\n", - " 'children': ({'_type': 'Tensor',\n", - " '_module': 'albert.tensor',\n", - " 'indices': ({'_type': 'Index',\n", - " '_module': 'albert.index',\n", - " 'name': 'i',\n", - " 'spin': None,\n", - " 'space': None},\n", - " {'_type': 'Index',\n", - " '_module': 'albert.index',\n", - " 'name': 'j',\n", - " 'spin': None,\n", - " 'space': None}),\n", - " 'name': 'a',\n", - " 'symmetry': None},\n", - " {'_type': 'Tensor',\n", - " '_module': 'albert.tensor',\n", - " 'indices': ({'_type': 'Index',\n", - " '_module': 'albert.index',\n", - " 'name': 'j',\n", - " 'spin': None,\n", - " 'space': None},\n", - " {'_type': 'Index',\n", - " '_module': 'albert.index',\n", - " 'name': 'k',\n", - " 'spin': None,\n", - " 'space': None}),\n", - " 'name': 'b',\n", - " 'symmetry': None})}" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "rhs = from_string(\"a(i,j) * b(j,k)\")\n", "rhs.as_json()" @@ -1089,21 +1024,10 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "id": "2fa3a00f-45a9-429b-9c8c-f2e0e0d93085", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "a(i,j) * b(j,k)" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "loaded = Base.from_json(rhs.as_json())\n", "loaded" @@ -1111,21 +1035,10 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "dd805277-8707-4975-8210-d7ef3a6c72f2", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "loaded == rhs" ]