{ "cells": [ { "cell_type": "markdown", "id": "a845056f-8d44-4d6f-b911-630cbf0022a2", "metadata": {}, "source": [ "# Define Your Model" ] }, { "cell_type": "markdown", "id": "1a9a29c7-3877-4c9c-a72b-8ecb0e9b2f1a", "metadata": {}, "source": [ "## Overview\n", "\n", "In this notebook we will introduce 3 basic components for Renormalizer: `Op`, `BasisSet` and `Model`.\n", "These components are essential for MPS/MPO construction/manipulation for a given physical model." ] }, { "cell_type": "markdown", "id": "ce4ab8f2-26f3-48f1-a93a-6b27e1c46047", "metadata": {}, "source": [ "## `Op`\n", "\n", "`Op` is the abbreviation for \"operators\". It offers a convenient interface to represent operators of your interest.\n", "`Op` is the bridge between symbolic math equations and numerical tensors in MPS/MPO." ] }, { "cell_type": "code", "execution_count": 1, "id": "4bc1aac4-9fec-48f2-a801-abd479d1adc7", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:03.789112Z", "iopub.status.busy": "2026-10-14T01:46:03.788865Z", "iopub.status.idle": "2026-10-14T01:46:04.934643Z", "shell.execute_reply": "2026-10-14T01:46:04.933682Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:04,088[INFO] Use numpy as backend\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:04,088[INFO] numpy random seed is 9012\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:04,089[INFO] backend random seed is 2019\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:04,089[INFO] random seed is 1092\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:04,098[INFO] Git Commit Hash: 3507afde83fd6f408c1c2198aad4dfe455d88312\n" ] } ], "source": [ "from renormalizer import Op" ] }, { "cell_type": "markdown", "id": "b90b8c22-3d60-4503-a21d-c47330d34278", "metadata": {}, "source": [ "### Basics\n", "\n", "There are 3 basic attributes for an operator:\n", "\n", "- The operation or its symbol, such as $\\hat X$, $\\hat a^\\dagger$, $\\hat p^2$\n", "- The DOF at wich the operation is operated on, such as \"spin 0\", \"exiton 1\". In mathematical expression this is usually the subscript for an operator, i.e., $\\hat X_0$ or $\\hat a^\\dagger_1$\n", "- The factor or coefficient for the operator.\n", "\n", "There is an additional useful attributes that is sometimes quite useful: the quantum number. The idea of quantum number will be described in detail in another tutorial.\n", "\n", "`Op` are constructed from the above 4 attributes. The first 2 are necessary and the last 2 are optional." ] }, { "cell_type": "code", "execution_count": 2, "id": "47aa28d6-2bb6-4a19-8244-d80a99fca2f9", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.937133Z", "iopub.status.busy": "2026-10-14T01:46:04.936875Z", "iopub.status.idle": "2026-10-14T01:46:04.943104Z", "shell.execute_reply": "2026-10-14T01:46:04.942236Z" } }, "outputs": [ { "data": { "text/plain": [ "Op('X', [0], 0.5)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Op(\"X\", 0, factor=0.5, qn=0)" ] }, { "cell_type": "markdown", "id": "620740be-ee2d-4c1b-901c-d08f8d346436", "metadata": {}, "source": [ "In renormalizer, you may use anything that can be hashed and compared to denote a DOF.\n", "Common examples include `int`, `str`, and `tuple` of them." ] }, { "cell_type": "code", "execution_count": 3, "id": "303d5892-1992-4e4f-ac56-e3257bf237b4", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.944789Z", "iopub.status.busy": "2026-10-14T01:46:04.944613Z", "iopub.status.idle": "2026-10-14T01:46:04.948435Z", "shell.execute_reply": "2026-10-14T01:46:04.947646Z" } }, "outputs": [ { "data": { "text/plain": [ "Op('X', [('spin 0', 'cool stuff', 2077)], 1.0)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Op(\"X\", (\"spin 0\", \"cool stuff\", 2077))" ] }, { "cell_type": "markdown", "id": "e0009852-e217-46ec-aee3-630e6578267b", "metadata": {}, "source": [ "You may wonder what are the allowed symbols for operators. For that please refer to the `BasisSet` section." ] }, { "cell_type": "markdown", "id": "19c2ad5f-abad-449a-8a3a-2006f2ec8aca", "metadata": {}, "source": [ "Products of operators can be constructed intuitively" ] }, { "cell_type": "code", "execution_count": 4, "id": "8fe6c36d-0179-480e-84ec-f23c32af61d6", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.950220Z", "iopub.status.busy": "2026-10-14T01:46:04.950066Z", "iopub.status.idle": "2026-10-14T01:46:04.953949Z", "shell.execute_reply": "2026-10-14T01:46:04.953152Z" } }, "outputs": [ { "data": { "text/plain": [ "Op('a^\\\\dagger a', [0, 1], 1.0, [[1], [-1]])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Op(r\"a^\\dagger a\", [0, 1])" ] }, { "cell_type": "markdown", "id": "6ee3173b-739d-46c5-bd76-56dbd6c8f704", "metadata": {}, "source": [ "Here, DOFs for the operators are specified through a list, where the first element is the DOF for $a^\\dagger$ and the second element is the DOF for $a$.\n", "\n", "Note that using tuple to specify the DOF has a totally different meaning. Renormalizer will recognize the tuple as a single DOF and set all DOFs in the operator to that DOF. " ] }, { "cell_type": "code", "execution_count": 5, "id": "d900115e-4775-4b84-90fa-0b3339d90cc7", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.955722Z", "iopub.status.busy": "2026-10-14T01:46:04.955550Z", "iopub.status.idle": "2026-10-14T01:46:04.959464Z", "shell.execute_reply": "2026-10-14T01:46:04.958693Z" } }, "outputs": [ { "data": { "text/plain": [ "Op('a^\\\\dagger a', [(0, 1), (0, 1)], 1.0, [[1], [-1]])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Op(r\"a^\\dagger a\", (0, 1))" ] }, { "cell_type": "markdown", "id": "61715410-4731-4e7f-a6cd-73f108bb0836", "metadata": {}, "source": [ "Operator symbols for different DOFs should be separated by a space" ] }, { "cell_type": "code", "execution_count": 6, "id": "81054bdb-48e9-4191-8500-c645deea060a", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.961359Z", "iopub.status.busy": "2026-10-14T01:46:04.961179Z", "iopub.status.idle": "2026-10-14T01:46:04.965099Z", "shell.execute_reply": "2026-10-14T01:46:04.964327Z" } }, "outputs": [ { "data": { "text/plain": [ "Op('X X X', [0, 1, 2], 1.0)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Op(\"X X X\", [0, 1, 2]) # note that Op(\"XXX\", [0, 1, 2]) does not work" ] }, { "cell_type": "markdown", "id": "f34a791e-dbae-4830-aef6-ccd574d1d70d", "metadata": {}, "source": [ "`Op` also has a lot of useful functions and attributes, please refer to the API document for more details." ] }, { "cell_type": "markdown", "id": "dbd21203-9ac8-45b4-bd35-26c844b8241b", "metadata": {}, "source": [ "### Operations and `OpSum`" ] }, { "cell_type": "markdown", "id": "c680bd59-0c99-45aa-8335-f2ba3db1959e", "metadata": {}, "source": [ "Common and simple operations between operators are supported" ] }, { "cell_type": "code", "execution_count": 7, "id": "7e00cea9-1bfc-4c58-bcee-38b885ec7d9d", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.966995Z", "iopub.status.busy": "2026-10-14T01:46:04.966817Z", "iopub.status.idle": "2026-10-14T01:46:04.970882Z", "shell.execute_reply": "2026-10-14T01:46:04.970048Z" } }, "outputs": [ { "data": { "text/plain": [ "Op('X Z', [0, 1], 0.25)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "op1 = Op(\"X\", 0, 0.5)\n", "op2 = Op(\"Z\", 1, 0.5)\n", "op1 * op2" ] }, { "cell_type": "markdown", "id": "4823f97b-6a2d-4fc4-9d1f-dc31eb056f9f", "metadata": {}, "source": [ "Addition between `Op` will result in an `OpSum` instance that is a subclass of list." ] }, { "cell_type": "code", "execution_count": 8, "id": "84b34017-8b9b-4288-8730-e1f12de2806b", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.972563Z", "iopub.status.busy": "2026-10-14T01:46:04.972385Z", "iopub.status.idle": "2026-10-14T01:46:04.976693Z", "shell.execute_reply": "2026-10-14T01:46:04.975691Z" } }, "outputs": [ { "data": { "text/plain": [ "([Op('X', [0], 0.5), Op('Z', [1], 0.5)], renormalizer.model.op.OpSum)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "op1 + op2, type(op1 + op2)" ] }, { "cell_type": "markdown", "id": "cc0683c8-e355-4225-a4f2-a0d23271a3cd", "metadata": {}, "source": [ "`OpSum` supports simple operator algebra such as multiplication/addition, etc." ] }, { "cell_type": "code", "execution_count": 9, "id": "c1cf78f8-ed8a-4bf5-a629-b786f43210b2", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.978275Z", "iopub.status.busy": "2026-10-14T01:46:04.978113Z", "iopub.status.idle": "2026-10-14T01:46:04.981971Z", "shell.execute_reply": "2026-10-14T01:46:04.981114Z" } }, "outputs": [ { "data": { "text/plain": [ "[Op('X X', [0, 0], 0.25), Op('Z X', [1, 0], 0.25)]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# multiplication\n", "opsum = op1 + op2\n", "opsum * op1" ] }, { "cell_type": "code", "execution_count": 10, "id": "04747001-e2fb-442a-a32c-267b8e531aa4", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.983728Z", "iopub.status.busy": "2026-10-14T01:46:04.983575Z", "iopub.status.idle": "2026-10-14T01:46:04.987306Z", "shell.execute_reply": "2026-10-14T01:46:04.986382Z" } }, "outputs": [ { "data": { "text/plain": [ "Op('X', [0], 5.0)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "10 * op1" ] }, { "cell_type": "code", "execution_count": 11, "id": "26cf73a7-ef15-41ab-9f1e-96ce3d9612d8", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.988800Z", "iopub.status.busy": "2026-10-14T01:46:04.988647Z", "iopub.status.idle": "2026-10-14T01:46:04.992569Z", "shell.execute_reply": "2026-10-14T01:46:04.991649Z" } }, "outputs": [ { "data": { "text/plain": [ "[Op('X X', [0, 0], 0.25),\n", " Op('X Z', [0, 1], 0.25),\n", " Op('Z X', [1, 0], 0.25),\n", " Op('Z Z', [1, 1], 0.25)]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opsum * opsum" ] }, { "cell_type": "code", "execution_count": 12, "id": "fa103eaa-78ba-482d-a796-808fe67c5cc0", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.994067Z", "iopub.status.busy": "2026-10-14T01:46:04.993912Z", "iopub.status.idle": "2026-10-14T01:46:04.997639Z", "shell.execute_reply": "2026-10-14T01:46:04.996908Z" } }, "outputs": [ { "data": { "text/plain": [ "[Op('X', [0], 0.5), Op('Z', [1], 0.5), Op('Z', [1], -0.5)]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# addition/subtraction\n", "opsum -= op2\n", "opsum" ] }, { "cell_type": "code", "execution_count": 13, "id": "cb924ed7-6a00-4cb5-8339-a5d468e72d06", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:04.999429Z", "iopub.status.busy": "2026-10-14T01:46:04.999276Z", "iopub.status.idle": "2026-10-14T01:46:05.002862Z", "shell.execute_reply": "2026-10-14T01:46:05.002042Z" } }, "outputs": [ { "data": { "text/plain": [ "[Op('X', [0], 0.5)]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "opsum.simplify()" ] }, { "cell_type": "markdown", "id": "b24fed67-f38e-491e-ad84-709a51bbb5f9", "metadata": {}, "source": [ "However, in general the functionalities are limited and the performance is not optimized.\n", "We recommand using [SymPy](https://www.sympy.org/en/index.html) for advanced symbolic mathematics and convert the final result to Renormalizer `Op`." ] }, { "cell_type": "markdown", "id": "641befa7-4c71-44d7-8763-64bbac2a45c7", "metadata": {}, "source": [ "## `BasisSet`\n", "An essential step for converting symbolic operators into numeric tensors is to specify a set of basis.\n", "Renormalizer includes a zoo of basis set with an emphasis for electron-phonon or vibronic models." ] }, { "cell_type": "code", "execution_count": 14, "id": "24f5feb6-94ec-4f8f-a655-c23782ad3457", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.004506Z", "iopub.status.busy": "2026-10-14T01:46:05.004355Z", "iopub.status.idle": "2026-10-14T01:46:05.008392Z", "shell.execute_reply": "2026-10-14T01:46:05.007536Z" } }, "outputs": [ { "data": { "text/plain": [ "['BasisDummy',\n", " 'BasisHalfSpin',\n", " 'BasisHopsBoson',\n", " 'BasisMultiElectron',\n", " 'BasisMultiElectronVac',\n", " 'BasisSHO',\n", " 'BasisSimpleElectron',\n", " 'BasisSineDVR']" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import renormalizer\n", "[s for s in dir(renormalizer) if s.startswith(\"Basis\")]" ] }, { "cell_type": "markdown", "id": "60704ece-a915-494b-ad2d-c202bbc8195b", "metadata": {}, "source": [ "Each BasisSet is associated with one or more DOFs. For example, we can setup the spin-1/2 basis set for a spin DOF denoted as `(\"spin\", 0)`" ] }, { "cell_type": "code", "execution_count": 15, "id": "66b6d810-e472-426a-a222-b47de5df86d7", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.010293Z", "iopub.status.busy": "2026-10-14T01:46:05.010142Z", "iopub.status.idle": "2026-10-14T01:46:05.014183Z", "shell.execute_reply": "2026-10-14T01:46:05.013293Z" } }, "outputs": [ { "data": { "text/plain": [ "BasisHalfSpin(dof: ('spin', 0), nbas: 2)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from renormalizer import BasisHalfSpin\n", "b = BasisHalfSpin((\"spin\", 0))\n", "b" ] }, { "cell_type": "markdown", "id": "7992808a-0d34-4307-8f24-81aa6fbc6701", "metadata": {}, "source": [ "Each basis set supports a variety of operations. The spin-1/2 basis set naturally supports Pauli operators such as `\"Y\"`, `\"sigma_z\"`." ] }, { "cell_type": "code", "execution_count": 16, "id": "04cbaf61-3d57-4ebb-b270-0882a7675e60", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.015764Z", "iopub.status.busy": "2026-10-14T01:46:05.015613Z", "iopub.status.idle": "2026-10-14T01:46:05.019987Z", "shell.execute_reply": "2026-10-14T01:46:05.019201Z" } }, "outputs": [ { "data": { "text/plain": [ "(array([[0.+0.j, 0.-1.j],\n", " [0.+1.j, 0.+0.j]]),\n", " array([[ 1., 0.],\n", " [ 0., -1.]]))" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b.op_mat(\"Y\"), b.op_mat(\"sigma_z\")" ] }, { "cell_type": "markdown", "id": "a1dedfec-1b1d-4017-9f4a-ce22a3f74c64", "metadata": {}, "source": [ "> In Renormalizer, we use `[1, 0]` for spin up and `[0, 1]` for spin down." ] }, { "cell_type": "markdown", "id": "6f41ff7f-fb28-436f-a4cb-f67730a18939", "metadata": {}, "source": [ "For phonon DOF, it is necessary to specify the quanta truncated in the basis set" ] }, { "cell_type": "code", "execution_count": 17, "id": "f563057c-1ffd-4312-b207-0629e57b5fc8", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.021935Z", "iopub.status.busy": "2026-10-14T01:46:05.021627Z", "iopub.status.idle": "2026-10-14T01:46:05.026892Z", "shell.execute_reply": "2026-10-14T01:46:05.026061Z" } }, "outputs": [ { "data": { "text/plain": [ "(array([[0. , 1. , 0. , 0. ],\n", " [1. , 0. , 1.41421356, 0. ],\n", " [0. , 1.41421356, 0. , 1.73205081],\n", " [0. , 0. , 1.73205081, 0. ]]),\n", " array([[0. , 0.1 , 0. , 0. ],\n", " [0.1 , 0. , 0.14142136, 0. ],\n", " [0. , 0.14142136, 0. , 0.17320508],\n", " [0. , 0. , 0.17320508, 0. ]]))" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# SHO represent simple harmonic oscillator\n", "from renormalizer import BasisSHO\n", "# omega is the vibration frequency, nbas is the number of basis\n", "b = BasisSHO(dof=0, omega=50, nbas=4)\n", "# note the different value because of omega\n", "b.op_mat(r\"b^\\dagger+b\"), b.op_mat(\"x\")" ] }, { "cell_type": "markdown", "id": "c7364e91-6fb0-47f8-bcf6-2430562b712b", "metadata": {}, "source": [ "Note that `BasisSHO` supports both first and second-quantized operators such as\n", "$$\n", "\\hat x = \\sqrt{\\frac{1}{2\\omega}} (b^\\dagger + b)\n", "$$\n", "\n", "here $x$ is the mass-weighted coordinate." ] }, { "cell_type": "markdown", "id": "950dc6cf-b139-424e-b055-9881d4daf858", "metadata": {}, "source": [ "The number of possible basis sets is infinite and highly customized basis set for a particular problem is rairly common.\n", "You may subclass the `BasisSet` parent class in `renormalizer.model.basis.BasisSet` to customize the basis set and the numerical behavior of the operators." ] }, { "cell_type": "markdown", "id": "f9756ed3caa90b1f", "metadata": {}, "source": [ "Renormalizer supports DVR basis (a.k.a. grid basis) where the basis functions are eigenfunctions of the position operator.\n", "Use the `BasisSineDVR` class or set `dvr=True` in BasisSHO for DVR basis set." ] }, { "cell_type": "markdown", "id": "f7e9ae8d481f0ad3", "metadata": {}, "source": [ "## `Model`\n", "\n", "A `Model` is basically made up with a list of `BasisSet` and the Hamiltonian in `OpSum`/`list` form.\n", "A `Model` instance is necessary for any MPS/MPO construction." ] }, { "cell_type": "code", "execution_count": 18, "id": "aaa37708aa385324", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.029118Z", "iopub.status.busy": "2026-10-14T01:46:05.028943Z", "iopub.status.idle": "2026-10-14T01:46:05.032036Z", "shell.execute_reply": "2026-10-14T01:46:05.031195Z" } }, "outputs": [], "source": [ "from renormalizer import Model\n", "model = Model([BasisHalfSpin(0)], [Op(\"X\", 0)])" ] }, { "cell_type": "markdown", "id": "ba12128c9b37c659", "metadata": {}, "source": [ "For example, the corresponding MPO can be constructed from the above model conveniently" ] }, { "cell_type": "code", "execution_count": 19, "id": "3a855d43f514b415", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.033882Z", "iopub.status.busy": "2026-10-14T01:46:05.033711Z", "iopub.status.idle": "2026-10-14T01:46:05.040569Z", "shell.execute_reply": "2026-10-14T01:46:05.039793Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,034[DEBUG] # of operator terms: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,035[DEBUG] Input operator terms: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,036[DEBUG] After combination of the same terms: 1\n" ] }, { "data": { "text/plain": [ " with 1 sites" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from renormalizer import Mpo\n", "mpo = Mpo(model)\n", "mpo" ] }, { "cell_type": "code", "execution_count": 20, "id": "2ef138d923a695f2", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.042369Z", "iopub.status.busy": "2026-10-14T01:46:05.042197Z", "iopub.status.idle": "2026-10-14T01:46:05.046367Z", "shell.execute_reply": "2026-10-14T01:46:05.045407Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[0., 1.],\n", " [1., 0.]])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mpo.todense()" ] }, { "cell_type": "markdown", "id": "dc3188e865d526d", "metadata": {}, "source": [ "You may wonder \n", "\n", "- What are the limitations for the model, in particular the operator terms, to construct MPO? For example, is nearest-neighbour interaction or at most two-body operator enforced?\n", "- If any numerical compression is involved in the construction. If so, how to control the accuracy? \n", "- What are the time cost of constructing MPO for an arbitrary model?\n", "\n", "The answer is: renormalizer offers a unique algorithm for exact and efficient MPO construction of arbitrary operators.\n", "It means\n", "\n", "- There's **NO limitation** for the model. Long-range interaction, three-body operators, anything you like.\n", "- Numerical compression is not involved and the construction is **exact**.\n", "- The construction time cost is neglegible for most models\n", "\n", "Additionally, we also guarantee that the constructed MPO is **optimal** in terms of bond dimension. \n", "\n", "For details of our algorithm, see [A general automatic method for optimal construction of matrix product operators using bipartite graph theory](https://aip.scitation.org/doi/10.1063/5.0018149)." ] }, { "cell_type": "markdown", "id": "128300605664a169", "metadata": {}, "source": [ "Renormalizer also offers a set of built-in models for fast construction of common models. Please refer to the API document for more details." ] }, { "cell_type": "code", "execution_count": 21, "id": "9c0d0e5332078a5d", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.048008Z", "iopub.status.busy": "2026-10-14T01:46:05.047825Z", "iopub.status.idle": "2026-10-14T01:46:05.051882Z", "shell.execute_reply": "2026-10-14T01:46:05.051039Z" } }, "outputs": [ { "data": { "text/plain": [ "['HolsteinModel', 'Model', 'SpinBosonModel', 'TI1DModel']" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import renormalizer\n", "[s for s in dir(renormalizer) if s.endswith(\"Model\")]" ] }, { "cell_type": "markdown", "id": "45ca92c93869529e", "metadata": {}, "source": [ "## Define Custom `BasisSet`\n", "It is quite common to require specific basis set for the problem at hand. The following is an example of defining a custom basis set.\n", "\n", "The key to the implementation is to implement the `op_mat` method, which outputs the matrix representation of a local operator.\n", "You should implement the necessary operators here for your model definition.\n", "The input to the function could be either `str` or `Op`. Use `op.symbol` to access the symbol string." ] }, { "cell_type": "code", "execution_count": 22, "id": "bcf33ab118902659", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.053512Z", "iopub.status.busy": "2026-10-14T01:46:05.053363Z", "iopub.status.idle": "2026-10-14T01:46:05.060794Z", "shell.execute_reply": "2026-10-14T01:46:05.059717Z" } }, "outputs": [], "source": [ "from typing import Union, List\n", "import numpy as np\n", "from renormalizer.model.basis import BasisSet\n", "\n", "class CustomTwoLevelSystem(BasisSet):\n", " \"\"\"\n", " A custom two-level system basis set example.\n", " Implements a simple two-level system with custom operators.\n", " \"\"\"\n", " \n", " def __init__(self, dof, energy_splitting=1.0, sigmaqn: List[int] = None):\n", " # Define the basis states and their quantum numbers\n", " # For a two-level system, we have two states: ground and excited\n", " if sigmaqn is None:\n", " sigmaqn = [0, 1] # Quantum numbers for each state\n", " \n", " # Initialize the BasisSet class with the number of states and quantum numbers\n", " # Must provide `sigma_qn` even if it's all zero.\n", " super().__init__(dof, nbas=2, sigmaqn=sigmaqn)\n", " self.energy_splitting = energy_splitting\n", " \n", " def op_mat(self, op: Union[Op, str]):\n", " \"\"\"\n", " Define operator matrices for supported symbols:\n", " - 'sigma_z': Pauli Z matrix\n", " - 'sigma_x': Pauli X matrix\n", " - 'n': number operator (|1><1|)\n", " - 'H_sys': system Hamiltonian (energy_splitting * sigma_z)\n", " \"\"\"\n", " # Convert symbol to string if necessary\n", " if not isinstance(op, Op):\n", " op = Op(op, None)\n", " \n", " op_symbol, op_factor = op.symbol, op.factor\n", " if op_symbol == \"sigma_z\":\n", " mat = np.array([[1, 0], [0, -1]])\n", " elif op_symbol == \"sigma_x\":\n", " mat = np.array([[0, 1], [1, 0]])\n", " elif op_symbol == \"n\":\n", " mat = np.array([[0, 0], [0, 1]]) # |1><1|\n", " elif op_symbol == \"H_sys\":\n", " # System Hamiltonian: energy_splitting * sigma_z\n", " mat = self.energy_splitting * np.array([[1, 0], [0, -1]])\n", " elif op_symbol == \"I\":\n", " # always define the identity operator\n", " mat = np.eye(self.nbas)\n", " else:\n", " raise ValueError(f\"Unknown operator symbol: {op_symbol}\")\n", " \n", " return mat * op_factor\n", "\n", " def copy(self, new_dof):\n", " \"\"\"Create a copy with a new DOF label\"\"\"\n", " return self.__class__(new_dof, self.energy_splitting, self.sigmaqn)" ] }, { "cell_type": "code", "execution_count": 23, "id": "172e3692822dc131", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.062318Z", "iopub.status.busy": "2026-10-14T01:46:05.062166Z", "iopub.status.idle": "2026-10-14T01:46:05.071352Z", "shell.execute_reply": "2026-10-14T01:46:05.070473Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,063[DEBUG] # of operator terms: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,064[DEBUG] Input operator terms: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,065[DEBUG] After combination of the same terms: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,067[DEBUG] # of operator terms: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,067[DEBUG] Input operator terms: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-10-14 01:46:05,068[DEBUG] After combination of the same terms: 1\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "[[ 1. 0.]\n", " [ 0. -1.]]\n", "[[0. 1.]\n", " [1. 0.]]\n" ] } ], "source": [ "# Create a model with our custom basis\n", "custom_basis = CustomTwoLevelSystem(\"qubit\", energy_splitting=2.0)\n", "model = Model([custom_basis], [Op(\"H_sys\", \"qubit\")])\n", "\n", "# Create MPOs using our custom symbols\n", "sigma_z_mpo = Mpo(model, Op(\"sigma_z\", \"qubit\"))\n", "print(sigma_z_mpo.todense())\n", "sigma_x_mpo = Mpo(model, Op(\"sigma_x\", \"qubit\"))\n", "print(sigma_x_mpo.todense())" ] }, { "cell_type": "markdown", "id": "746e44e4509387e2", "metadata": {}, "source": [ "> To improve computational efficiency, when defining the basis set, float numbers are preferred over complex numbers. Complex operators wil usually be finally converted to real operators, such as $H=YY$ or $H=-iY$." ] }, { "cell_type": "markdown", "id": "e11f301f1000d93c", "metadata": {}, "source": [ "## Units in Renormalizer\n", "All internal numbers in renormalizer are assumed to have the unit of atomic unit (a.u.).\n", "Consequently, all external interfaces such as `factor` in `Op` are assumed to have the unit of atomic unit.\n", "\n", "To facilitate unit conversion, You may use the `Quantity` class in Renormalizer to convert the values in other units to atomic units." ] }, { "cell_type": "code", "execution_count": 24, "id": "cf3184f5e36d069e", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.073018Z", "iopub.status.busy": "2026-10-14T01:46:05.072843Z", "iopub.status.idle": "2026-10-14T01:46:05.075472Z", "shell.execute_reply": "2026-10-14T01:46:05.074652Z" } }, "outputs": [], "source": [ "from renormalizer import Quantity" ] }, { "cell_type": "code", "execution_count": 25, "id": "163dbd3cfbf45d90", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.077220Z", "iopub.status.busy": "2026-10-14T01:46:05.077072Z", "iopub.status.idle": "2026-10-14T01:46:05.080841Z", "shell.execute_reply": "2026-10-14T01:46:05.080081Z" } }, "outputs": [ { "data": { "text/plain": [ "1.0000225550441808" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v = Quantity(27212, \"mev\")\n", "v.as_au()" ] }, { "cell_type": "markdown", "id": "d7a201e76e22e9e8", "metadata": {}, "source": [ "Note that directly using a `Quantity` instance for numerical arguments of model construction will result in errors. \n", "For example, you should write `BasisSHO(dof=0, omega=Quantity(50).as_au(), nbas=4)` instead of `BasisSHO(dof=0, omega=Quantity(50), nbas=4)`.\n", "The design enforces the user to pay special attention to the units as well as the value of the parameters, which helps to prevent unit/parameter typo." ] }, { "cell_type": "markdown", "id": "54ff0251185fef1c", "metadata": {}, "source": [ "To convert the unit from a.u. to other supported units, use `as_unit` to convert to the desired unit, and then access the `value` attribute for the value." ] }, { "cell_type": "code", "execution_count": 26, "id": "7e1a58a8c0824f8d", "metadata": { "execution": { "iopub.execute_input": "2026-10-14T01:46:05.082860Z", "iopub.status.busy": "2026-10-14T01:46:05.082714Z", "iopub.status.idle": "2026-10-14T01:46:05.086545Z", "shell.execute_reply": "2026-10-14T01:46:05.085720Z" } }, "outputs": [ { "data": { "text/plain": [ "27211.386245981" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Quantity(1, \"au\").as_unit(\"mev\").value" ] } ], "metadata": { "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.17" } }, "nbformat": 4, "nbformat_minor": 5 }