{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# How to write a custom PyZeta plugin" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# some standard library imports\n", "from json import load\n", "from os.path import dirname, join\n", "from typing import Callable, Optional, Tuple, Type\n", "\n", "# the core imports for writing your custom logic and the plugin\n", "from pyzeta.core.pyzeta_types.special import tGroupElement\n", "from pyzeta.core.symmetries.symmetry_group import SymmetryGroup\n", "from pyzeta.core.symmetries.trivial_group import TrivialGroup\n", "from pyzeta.framework.plugins.installation_helper import InstallationHelper\n", "from pyzeta.framework.plugins.pyzeta_plugin import PyZetaPlugin" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Implement your custom logic" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "class TestGroup(TrivialGroup):\n", " \"Custom (trivial) symmetry group implementation to supply as a plugin.\"\n", "\n", " def __init__(self, arg1: str, arg2: int) -> None:\n", " \"Initialize our custom symmetry group implementation.\"\n", " super().__init__()\n", " self.arg1 = arg1\n", " self.arg2 = arg2\n", "\n", " def getElements(self) -> Tuple[tGroupElement, ...]:\n", " \"Override this in our custom symmetry group.\"\n", " raise NotImplementedError(\n", " f\"test group[{self.arg1} | {self.arg2}] is not fully implemented!\"\n", " )\n", "\n", " def __str__(self) -> str:\n", " \"Just for printing - generic plugins don't actually need this.\"\n", " return f\"TestGroup({self.arg1}, {self.arg2})\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## How to include custom configuration data" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# here is an example of how to include custom configuration data with your\n", "# plugin (once the data file is installed!):\n", "configFile = InstallationHelper.returnDataPath(\"group_data.json\")\n", "with open(configFile, \"r\", encoding=\"utf-8\") as f:\n", " customData = load(f)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## The actual plugin class" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "the plugin itself: MyTestAlgorithmPlugin @ v22.1.0\n", "the provided service: TestAlgorithm(hi from algorithm_data.json!, 9)...! :)\n" ] } ], "source": [ "class GroupPlugin(PyZetaPlugin[SymmetryGroup]):\n", " \"Test plugin providing a simple custom symmetry group to PyZeta.\"\n", "\n", " _instance: Optional[PyZetaPlugin[SymmetryGroup]] = None\n", "\n", " @staticmethod\n", " def initialize() -> Callable[..., SymmetryGroup]:\n", " \"This is the hook of plugins into `PyZeta`.\"\n", " arg1, arg2 = customData[\"arg1\"], customData[\"arg2\"]\n", " return lambda: TestGroup(arg1=arg1, arg2=arg2)\n", "\n", " @staticmethod\n", " def getInstance() -> PyZetaPlugin[SymmetryGroup]:\n", " \"Plugins should be realized as singletons.\"\n", " if GroupPlugin._instance is None:\n", " GroupPlugin._instance = GroupPlugin()\n", " return GroupPlugin._instance\n", "\n", " @property\n", " def pluginType(self) -> Type[SymmetryGroup]:\n", " \"The type provided by the plugin.\"\n", " return SymmetryGroup\n", "\n", " @property\n", " def pluginName(self) -> str:\n", " \"The name of the plugin.\"\n", " return \"TestGroupPlugin\"\n", "\n", " @property\n", " def pluginVersion(self) -> Tuple[int, int, int]:\n", " \"\"\"\n", " The version of the plugin (the combination of version and name should\n", " be unique). The semantics are (`major`, `minor`, `patch`).\n", " \"\"\"\n", " return (22, 1, 0)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " print(f\"the plugin itself: {GroupPlugin.getInstance()}\")\n", " print(f\"the provided service: {GroupPlugin.initialize()()}\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Installing the plugin" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "With the prerequisits above placed in a single `.py` source file named\n", "`group_plugin.py` you can now install your plugin as follows:\n", "\n", "```\n", "$ pyzeta plugin --install group_plugin.py\n", "$ pyzeta plugin --install group_data.json\n", "```\n", "\n", "Your group can now be used e.g. in conjunction with symmetry reduction! If you would like\n", "to change the configuration data provided via `group_data.json`, simple\n", "adjust the file contents and issue the second command again.\n", "\n", "Note that your plugin overrides the default groups contained in **PyZeta**.\n", "To restore these defaults simply `--uninstall` your plugin. By adapting the\n", "example above it is also quite straightforward to replace a given default\n", "group with your custom plugin and leave the remaining ones untouched. To\n", "achieve this the return value of your plugin's `initialize` must accept a\n", "`groupType` parameter of type\n", "`pyzeta.core.pyzeta_types.group_types.GroupType`. You may then return any\n", "group of your choosing upon a given value of `groupType`." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.3" } }, "nbformat": 4, "nbformat_minor": 4 }