{
	"cell_id": 7900896013554414286,
	"cells": [
		{
			"cell_id": 3008518946232599177,
			"cell_origin": "client",
			"cell_type": "latex",
			"cells": [
				{
					"cell_id": 798500217262993049,
					"cell_origin": "client",
					"cell_type": "latex_view",
					"source": "\\package{cdb.utils.develop}{Helper functions to aid development, debugging and testing}\n\nThis package contains some standardised functionality to aid in development of library code and Python algorithms"
				}
			],
			"hidden": true,
			"source": "\\package{cdb.utils.develop}{Helper functions to aid development, debugging and testing}\n\nThis package contains some standardised functionality to aid in development of library code and Python algorithms"
		},
		{
			"cell_id": 13348113113731925581,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "import inspect"
		},
		{
			"cell_id": 13041134632829120942,
			"cell_origin": "client",
			"cell_type": "latex",
			"cells": [
				{
					"cell_id": 17870137200133729598,
					"cell_origin": "client",
					"cell_type": "latex_view",
					"source": "\\algorithm{class Algorithm(ex: Ex)}{Base class for user defined tree-traversal algorithms}"
				}
			],
			"hidden": true,
			"source": "\\algorithm{class Algorithm(ex: Ex)}{Base class for user defined tree-traversal algorithms}"
		},
		{
			"cell_id": 1319220678577630750,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "from cdb.utils._algorithm import Algorithm"
		},
		{
			"cell_id": 13334862458884777602,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "from cdb.utils._algorithm import apply_algo_base as _apply_algo_base\n\ndef _cast_algo(apply, can_apply = lambda node: True):\n\tif len(inspect.getfullargspec(apply).args) != 1:\n\t\traise TypeError(\"Failed to convert function to algorithm: function must only take one 'node' parameter\")\n\tif len(inspect.getfullargspec(can_apply).args) != 1:\n\t\traise TypeError(\"Failed to convert function to algorithm: predicate must only take one 'node' parameter\")\n\treturn type(apply.__name__, (Algorithm, ), { \n\t\t\"can_apply\": lambda self, node: can_apply(node), \n\t\t\"apply\": lambda self, node: apply(node),\n\t\t\"__doc__\": apply.__doc__\n\t})\n\ndef _create_algo(cls, pre_order):\n\tif Algorithm not in cls.__bases__:\n\t\traise TypeError(\"Algorithm must derive from Algorithm type\")\n\tdef wrapper(ex, *args, deep=True, repeat=False, depth=0, **kwargs):\n\t\tx = cls(ex, *args, **kwargs)\n\t\treturn _apply_algo_base(x, ex, deep, repeat, depth, pre_order)\n\n\t# Generate correct meta-information\n\twrapper.__name__ = cls.__name__\n\twrapper.__doc__ = cls.__doc__\n\t# Get argument list of Algorithm constructor\n\ttry:\n\t\tparameters = list(inspect.signature(cls\t.__init__).parameters.values())[1:]\n\texcept:\n\t\tparameters = [ inspect.Parameter(\"ex\", inspect.Parameter.POSITIONAL_OR_KEYWORD) ]\n\t# Parameters of apply_algo_base\n\talgo_parameters = [\n\t\tinspect.Parameter(\"deep\", inspect.Parameter.KEYWORD_ONLY, default=True),\n\t\tinspect.Parameter(\"repeat\", inspect.Parameter.KEYWORD_ONLY, default=False),\n\t\tinspect.Parameter(\"depth\", inspect.Parameter.KEYWORD_ONLY, default=0) ]\n\t# If parameters has a **kwargs at the end, algo_parameters must be inserted before this\n\tif parameters[-1].kind == inspect.Parameter.VAR_KEYWORD:\n\t\twrapper.__signature__ = inspect.Signature(parameters[:-1] + algo_parameters + parameters[-1:])\n\telse:\n\t\twrapper.__signature__ = inspect.Signature(parameters + algo_parameters)\n\treturn wrapper"
		},
		{
			"cell_id": 2421359616022599147,
			"cell_origin": "client",
			"cell_type": "latex",
			"cells": [
				{
					"cell_id": 16353153281984723823,
					"cell_origin": "client",
					"cell_type": "latex_view",
					"source": "\\algorithm{algo(pre_order: bool = False, pred: function = (node) -> True) -> function}{Decorator for creating tree-traversal algorithms}\n\nThis decorator takes a function or class derived from \\verb|cdb.utils.develop.Algorithm| and creates a tree-traversal algorithm which\niterates through an expression applying the function or \\verb|apply| method of a class to each node in turn. It optionally takes the\narguments \\verb|pre_order| which determines the order of traversal of the tree (\\verb|False| reverts to post-order iteration) and\n\\verb|pred| which, if decorating a function, will be called at each node in the tree and the function only applied if the predicate\nreturns true. If an \\verb|Algorithm| class is instead decorated, the behaviour of \\verb|pred| is implemented by the \\verb|can_apply|\nmethod which must be overloaded"
				}
			],
			"hidden": true,
			"source": "\\algorithm{algo(pre_order: bool = False, pred: function = (node) -> True) -> function}{Decorator for creating tree-traversal algorithms}\n\nThis decorator takes a function or class derived from \\verb|cdb.utils.develop.Algorithm| and creates a tree-traversal algorithm which\niterates through an expression applying the function or \\verb|apply| method of a class to each node in turn. It optionally takes the\narguments \\verb|pre_order| which determines the order of traversal of the tree (\\verb|False| reverts to post-order iteration) and\n\\verb|pred| which, if decorating a function, will be called at each node in the tree and the function only applied if the predicate\nreturns true. If an \\verb|Algorithm| class is instead decorated, the behaviour of \\verb|pred| is implemented by the \\verb|can_apply|\nmethod which must be overloaded"
		},
		{
			"cell_id": 8704625228430657301,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "def algo(*args, **kwargs):\n\t# Detect if we are called without arguments and therefore\n\t# get a single class or function instance. In this instance\n\t# default to post order and a predicate always returning true\n\tif len(args) == 1 and len(kwargs) == 0:\n\t\tif inspect.isfunction(args[0]):\n\t\t\tcls = _cast_algo(args[0])\n\t\telif inspect.isclass(args[0]):\n\t\t\tcls = args[0]\n\t\telse:\n\t\t\traise ValueError(\"algo does not accept positional arguments\")\n\t\treturn _create_algo(cls, pre_order=False)\n\n\t# Otherwise we expect an empty *args and collect the keyword arguments\n\tif len(args) != 0:\n\t\traise ValueError(\"algo does not accept positional arguments\")\n\tpre_order = kwargs.get('pre_order', False)\n\tpred = kwargs.get('pred', lambda node: True)\n\n\t# Then we return a decorator with no arguments which will get applied\n\tdef unwrapped(arg):\n\t\tif inspect.isfunction(arg):\n\t\t\tcls = _cast_algo(arg, pred)\n\t\telse:\n\t\t\tif 'pred' in kwargs:\n\t\t\t\traise ValueError(\"pred supplied to algo decorator, but this does not apply to class being decorated\")\n\t\t\tcls = arg\n\t\treturn _create_algo(cls, pre_order=pre_order)\n\treturn unwrapped"
		},
		{
			"cell_id": 2405738556624578142,
			"cell_origin": "client",
			"cell_type": "input",
			"cells": [
				{
					"cell_id": 15761994739137138802,
					"cell_origin": "server",
					"cell_type": "latex_view",
					"cells": [
						{
							"cell_id": 17415156100168742565,
							"cell_origin": "server",
							"cell_type": "input_form",
							"source": "A^{\\mu} + B^{\\mu}"
						}
					],
					"source": "\\begin{dmath*}{}A^{\\mu}+B^{\\mu}\\end{dmath*}"
				},
				{
					"cell_id": 15144194947899395553,
					"cell_origin": "server",
					"cell_type": "output",
					"source": "\\begin{verbatim}Help on function switch_indices in module __main__:\n\nswitch_indices(ex, *, deep=True, repeat=False, depth=0)\n    Switch positions on all indices\n\n\\end{verbatim}"
				}
			],
			"ignore_on_import": true,
			"source": "@algo\ndef switch_indices(node):\n\t\"Switch positions on all indices\"\n\tif node.parent_rel == parent_rel_t.sub:\n\t\tnode.parent_rel = parent_rel_t.super\n\t\treturn result_t.changed\n\tif node.parent_rel == parent_rel_t.super:\n\t\tnode.parent_rel = parent_rel_t.sub\n\t\treturn result_t.changed\n\treturn result_t.unchanged\n\n# also takes optional 'deep', 'repeat' and 'depth' arguments\nswitch_indices($A_{\\mu} + B_{\\mu}$);\nhelp(switch_indices)"
		},
		{
			"cell_id": 9340011096170931285,
			"cell_origin": "client",
			"cell_type": "input",
			"cells": [
				{
					"cell_id": 5850074756591759657,
					"cell_origin": "server",
					"cell_type": "latex_view",
					"cells": [
						{
							"cell_id": 9620504524171758578,
							"cell_origin": "server",
							"cell_type": "input_form",
							"source": "2y"
						}
					],
					"source": "\\begin{dmath*}{}2y\\end{dmath*}"
				},
				{
					"cell_id": 17850582158135921593,
					"cell_origin": "server",
					"cell_type": "output",
					"source": "\\begin{verbatim}Help on function x_to_y in module __main__:\n\nx_to_y(ex, *, deep=True, repeat=False, depth=0)\n\n\\end{verbatim}"
				}
			],
			"ignore_on_import": true,
			"source": "@algo(pre_order=True, pred=lambda node: node.name == 'x')\ndef x_to_y(node):\n\tnode.name = 'y'\n\treturn result_t.changed\n\nx_to_y($x + y$);\nhelp(x_to_y)"
		},
		{
			"cell_id": 15634397355979861807,
			"cell_origin": "client",
			"cell_type": "input",
			"cells": [
				{
					"cell_id": 14537821031780487327,
					"cell_origin": "server",
					"cell_type": "latex_view",
					"cells": [
						{
							"cell_id": 3417601803473011010,
							"cell_origin": "server",
							"cell_type": "input_form",
							"source": "2t_{\\mu}"
						}
					],
					"source": "\\begin{dmath*}{}2t_{\\mu}\\end{dmath*}"
				},
				{
					"cell_id": 17754394564063656935,
					"cell_origin": "server",
					"cell_type": "output",
					"source": "\\begin{verbatim}Help on function a_to_b in module __main__:\n\na_to_b(ex, a, b, *args, deep=True, repeat=False, depth=0, **kwargs)\n\n\\end{verbatim}"
				}
			],
			"ignore_on_import": true,
			"source": "@algo\nclass a_to_b(Algorithm):\n\tdef __init__(self, ex, a, b, *args, **kwargs):\n\t\tAlgorithm.__init__(self, ex)\n\t\tself.a, self.b = a, b\n\t\n\tdef can_apply(self, node):\n\t\treturn node.name == self.a.head()\n\n\tdef apply(self, node):\n\t\tnode.name = self.b.head()\n\t\treturn result_t.changed\n\na_to_b($s_{\\mu} + t_{\\mu}$, $s_{\\mu}$, $t_{\\mu}$);\nhelp(a_to_b)"
		},
		{
			"cell_id": 10135200674124616797,
			"cell_origin": "client",
			"cell_type": "input",
			"ignore_on_import": true,
			"source": "try:\n\t@algo\n\tdef function_with_too_many_arguments(a,b,c):\n\t\tpass\n\traise AssertionError(\"TypeError not raised\")\nexcept TypeError:\n\tpass\n\ntry:\n\t@algo\n\tclass does_not_inherit_from_Algorithm:\n\t\tpass\n\traise AssertionError(\"TypeError not raised\")\nexcept TypeError:\n\tpass\n\ntry:\n\t@algo(pred=lambda n: n.name == 'x')\n\tclass does_not_need_pred(Algorithm):\n\t\tdef can_apply(self,node): return n.name =='x'\n\t\tdef apply(self,node): return result_t.unchanged\n\traise AssertionError(\"ValueError not raised\")\nexcept ValueError:\n\tpass"
		},
		{
			"cell_id": 15845148038835594810,
			"cell_origin": "client",
			"cell_type": "latex",
			"cells": [
				{
					"cell_id": 8739236297038682978,
					"cell_origin": "client",
					"cell_type": "latex_view",
					"source": "\\algorithm{time_algo(algo: function, ex: Ex, *args: <mixed>, iterations: int = 100) -> float}{Simple function to time the execution of an algorithm with given inputs.}\n\nThe arguments in *args are passed directly, but ex is copied before each\ninvocation and so remains unmodified."
				}
			],
			"hidden": true,
			"source": "\\algorithm{time_algo(algo: function, ex: Ex, *args: <mixed>, iterations: int = 100) -> float}{Simple function to time the execution of an algorithm with given inputs.}\n\nThe arguments in *args are passed directly, but ex is copied before each\ninvocation and so remains unmodified."
		},
		{
			"cell_id": 5937432672654218849,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "from datetime import timedelta\n\ndef time_algo(algo, ex, *args, iterations=100):\n\ts = Stopwatch()\n\tfor i in range(iterations):\n\t\ttmp := @(ex);\n\t\ts.start()\n\t\talgo(tmp, *args)\n\t\ts.stop()\n\treturn timedelta(seconds=s.seconds()/iterations, microseconds=s.useconds()/iterations)"
		},
		{
			"cell_id": 17198486848786764579,
			"cell_origin": "client",
			"cell_type": "input",
			"cells": [
				{
					"cell_id": 6295148871247203489,
					"cell_origin": "server",
					"cell_type": "verbatim",
					"source": "\\begin{verbatim}0:00:00.000875\\end{verbatim}"
				}
			],
			"ignore_on_import": true,
			"source": "time_algo(substitute, $a + b$, $a -> b$);"
		},
		{
			"cell_id": 12791856048123301106,
			"cell_origin": "client",
			"cell_type": "input",
			"cells": [
				{
					"cell_id": 13532413926824958590,
					"cell_origin": "server",
					"cell_type": "verbatim",
					"source": "\\begin{verbatim}0:00:00.000493\\end{verbatim}"
				}
			],
			"ignore_on_import": true,
			"source": "def times_2(ex):\n\treturn ex * $2$\n\ntime_algo(times_2, $a$);"
		},
		{
			"cell_id": 15332315745938100730,
			"cell_origin": "client",
			"cell_type": "latex",
			"cells": [
				{
					"cell_id": 8935506242999741244,
					"cell_origin": "client",
					"cell_type": "latex_view",
					"source": "\\algorithm{CadabraTestError}{Exception derived from AssertionError raised by testing functions when an assertion fails}"
				}
			],
			"hidden": true,
			"source": "\\algorithm{CadabraTestError}{Exception derived from AssertionError raised by testing functions when an assertion fails}"
		},
		{
			"cell_id": 11180569447504398263,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "class CadabraTestError(AssertionError):\n\tpass"
		},
		{
			"cell_id": 3479765664248597757,
			"cell_origin": "client",
			"cell_type": "latex",
			"cells": [
				{
					"cell_id": 11588087231457837709,
					"cell_origin": "client",
					"cell_type": "latex_view",
					"source": "\\algorithm{test_algo(expected: Ex, throw_on_fail: bool)}{Decorator to aid defining unit tests for algorithms.}\n\nThis\tadds the boilerplate code and adds an assert for the test."
				}
			],
			"hidden": true,
			"source": "\\algorithm{test_algo(expected: Ex, throw_on_fail: bool)}{Decorator to aid defining unit tests for algorithms.}\n\nThis\tadds the boilerplate code and adds an assert for the test."
		},
		{
			"cell_id": 15123798302514262900,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "def test_algo(expected, throw_on_fail=False):\n\tdef decorator(func):\n\t\tdef wrapper(*args, **kwargs):\n\t\t\tres = func(*args, **kwargs)\n\t\t\tif res == expected:\n\t\t\t\tprint(func.__name__ + \" passed\")\n\t\t\telse:\n\t\t\t\tprint(func.__name__ + \" FAILED\")\n\t\t\t\tprint(\"  Expected: \" + str(expected))\n\t\t\t\tprint(\"  Produced: \" + str(res))\n\t\t\t\tif throw_on_fail: raise CadabraTestError\n\t\t\treturn res\n\t\treturn wrapper\n\treturn decorator"
		},
		{
			"cell_id": 2554539507442301090,
			"cell_origin": "client",
			"cell_type": "input",
			"cells": [
				{
					"cell_id": 4855635716783607432,
					"cell_origin": "server",
					"cell_type": "output",
					"source": "\\begin{verbatim}sort_sum_test01 passed\nsort_sum_test02 FAILED\n  Expected: a + b\n  Produced: a + c\nsort_sum_test03 FAILED\n  Expected: a + b\n  Produced: a + c\nRaised CadabraTestError\n\\end{verbatim}"
				}
			],
			"ignore_on_import": true,
			"source": "@test_algo($a + b + c$)\ndef sort_sum_test01():\n\tex := b + a + c.\n\treturn sort_sum(ex)\nsort_sum_test01()\n\n@test_algo($a + b$)\ndef sort_sum_test02():\n\tex := c + a.\n\treturn sort_sum(ex)\nsort_sum_test02()\n\n@test_algo($a + b$, throw_on_fail=True)\ndef sort_sum_test03():\n\tex := c + a.\n\treturn sort_sum(ex)\n\ntry:\n\tsort_sum_test03()\n\traise AssertionError(\"CadabraTestError not raised!\")\nexcept CadabraTestError:\n\tprint(\"Raised CadabraTestError\")"
		},
		{
			"cell_id": 17571440201905887785,
			"cell_origin": "client",
			"cell_type": "latex",
			"cells": [
				{
					"cell_id": 12853042454041156620,
					"cell_origin": "client",
					"cell_type": "latex_view",
					"source": "\\algorithm{inherit_kernel() -> Kernel}{Find a kernel in the stack.}\n\nMove up stack frames until one which defines the \\verb|__cdbkernel__| variable is located and return \nit. If no Kernel object is found then None is returned"
				}
			],
			"hidden": true,
			"source": "\\algorithm{inherit_kernel() -> Kernel}{Find a kernel in the stack.}\n\nMove up stack frames until one which defines the \\verb|__cdbkernel__| variable is located and return \nit. If no Kernel object is found then None is returned"
		},
		{
			"cell_id": 460414117077303111,
			"cell_origin": "client",
			"cell_type": "input",
			"source": "def inherit_kernel():\n\tframe = inspect.stack()[1][0]\n\twhile \"__cdbkernel__\" not in frame.f_locals:\n\t\tframe = frame.f_back\n\t\tif frame is None:\n\t\t\treturn None\n\treturn frame.f_locals[\"__cdbkernel__\"]"
		},
		{
			"cell_id": 13850153861758424314,
			"cell_origin": "client",
			"cell_type": "input",
			"ignore_on_import": true,
			"source": ""
		}
	],
	"description": "Cadabra JSON notebook format",
	"version": 1.0
}
