# Copyright 2018-2024 Xanadu Quantum Technologies Inc.# Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at# http://www.apache.org/licenses/LICENSE-2.0# Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License."""This file contains functions to create spin Hamiltonians."""importpennylaneasqmlfrompennylaneimportI,X,Y,Z,mathfrompennylane.fermiimportFermiWordfrom.latticeimportLattice,generate_lattice# pylint: disable=too-many-arguments, too-many-branches
[docs]deftransverse_ising(lattice,n_cells,coupling=1.0,h=1.0,boundary_condition=False,neighbour_order=1):r"""Generates the Hamiltonian for the transverse-field Ising model on a lattice. The Hamiltonian is represented as: .. math:: \hat{H} = -J \sum_{<i,j>} \sigma_i^{z} \sigma_j^{z} - h\sum_{i} \sigma_{i}^{x} where :math:`J` is the coupling parameter defined for the Hamiltonian, :math:`h` is the strength of the transverse magnetic field, :math:`<i,j>` represents the indices for neighbouring sites and :math:`\sigma` is a Pauli operator. Args: lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, ``'rectangle'``, ``'triangle'``, ``'honeycomb'``, ``'kagome'``, ``'lieb'``, ``'cubic'``, ``'bcc'``, ``'fcc'`` or ``'diamond'``. n_cells (List[int]): Number of cells in each direction of the grid. coupling (float or tensor_like[float]): Coupling between spins. It should be a number, an array of length equal to ``neighbour_order`` or a square matrix of shape ``(num_spins, num_spins)``, where ``num_spins`` is the total number of spins. Default value is 1.0. h (float): Value of external magnetic field. Default is 1.0. boundary_condition (bool or list[bool]): Specifies whether or not to enforce periodic boundary conditions for the different lattice axes. Default is ``False`` indicating open boundary condition. neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1, indicating nearest neighbours. Returns: ~ops.op_math.Sum: Hamiltonian for the transverse-field Ising model. **Example** >>> n_cells = [2,2] >>> j = 0.5 >>> h = 0.1 >>> spin_ham = qml.spin.transverse_ising("square", n_cells, coupling=j, h=h) >>> spin_ham ( -0.5 * (Z(0) @ Z(1)) + -0.5 * (Z(0) @ Z(2)) + -0.5 * (Z(1) @ Z(3)) + -0.5 * (Z(2) @ Z(3)) + -0.1 * X(0) + -0.1 * X(1) + -0.1 * X(2) + -0.1 * X(3) ) """lattice=generate_lattice(lattice,n_cells,boundary_condition,neighbour_order)ifisinstance(coupling,(int,float,complex)):coupling=[coupling]coupling=math.asarray(coupling)hamiltonian=0.0*qml.I(0)ifcoupling.shapenotin[(neighbour_order,),(lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The coupling parameter should be a number or an array of shape ({neighbour_order},) or ({lattice.n_sites},{lattice.n_sites})")ifcoupling.shape==(neighbour_order,):foredgeinlattice.edges:i,j,order=edgehamiltonian+=-coupling[order]*(Z(i)@Z(j))else:foredgeinlattice.edges:i,j=edge[0:2]hamiltonian+=-coupling[i][j]*(Z(i)@Z(j))forvertexinrange(lattice.n_sites):hamiltonian+=-h*X(vertex)returnhamiltonian.simplify()
[docs]defheisenberg(lattice,n_cells,coupling=None,boundary_condition=False,neighbour_order=1):r"""Generates the Hamiltonian for the Heisenberg model on a lattice. The Hamiltonian is represented as: .. math:: \hat{H} = J\sum_{<i,j>}(\sigma_i^x\sigma_j^x + \sigma_i^y\sigma_j^y + \sigma_i^z\sigma_j^z) where :math:`J` is the coupling constant defined for the Hamiltonian, :math:`<i,j>` represents the indices for neighbouring sites and :math:`\sigma` is a Pauli operator. Args: lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, ``'rectangle'``, ``'triangle'``, ``'honeycomb'``, ``'kagome'``, ``'lieb'``, ``'cubic'``, ``'bcc'``, ``'fcc'`` or ``'diamond'``. n_cells (List[int]): Number of cells in each direction of the grid. coupling (tensor_like[float]): Coupling between spins. It can be an array of shape ``(neighbour_order, 3)`` or ``(3, num_spins, num_spins)``, where ``num_spins`` is the total number of spins. boundary_condition (bool or list[bool]): Specifies whether or not to enforce periodic boundary conditions for the different lattice axes. Default is ``False`` indicating open boundary condition. neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1, indicating nearest neighbours. Returns: ~ops.op_math.Sum: Hamiltonian for the heisenberg model. **Example** >>> n_cells = [2,2] >>> j = np.array([0.5, 0.5, 0.5]) >>> spin_ham = qml.spin.heisenberg("square", n_cells, coupling=j) >>> spin_ham ( 0.5 * (X(0) @ X(1)) + 0.5 * (Y(0) @ Y(1)) + 0.5 * (Z(0) @ Z(1)) + 0.5 * (X(0) @ X(2)) + 0.5 * (Y(0) @ Y(2)) + 0.5 * (Z(0) @ Z(2)) + 0.5 * (X(1) @ X(3)) + 0.5 * (Y(1) @ Y(3)) + 0.5 * (Z(1) @ Z(3)) + 0.5 * (X(2) @ X(3)) + 0.5 * (Y(2) @ Y(3)) + 0.5 * (Z(2) @ Z(3)) ) """lattice=generate_lattice(lattice,n_cells,boundary_condition,neighbour_order)ifcouplingisNone:coupling=[[1.0,1.0,1.0]]coupling=math.asarray(coupling)ifcoupling.ndim==1:coupling=math.asarray([coupling])ifcoupling.shapenotin[(neighbour_order,3),(3,lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The coupling parameter shape should be equal to ({neighbour_order},3) or (3,{lattice.n_sites},{lattice.n_sites})")hamiltonian=0.0*qml.I(0)ifcoupling.shape==(neighbour_order,3):foredgeinlattice.edges:i,j,order=edgehamiltonian+=(coupling[order][0]*(X(i)@X(j))+coupling[order][1]*(Y(i)@Y(j))+coupling[order][2]*(Z(i)@Z(j)))else:foredgeinlattice.edges:i,j=edge[0:2]hamiltonian+=(coupling[0][i][j]*X(i)@X(j)+coupling[1][i][j]*Y(i)@Y(j)+coupling[2][i][j]*Z(i)@Z(j))returnhamiltonian.simplify()
[docs]deffermi_hubbard(lattice,n_cells,hopping=1.0,coulomb=1.0,boundary_condition=False,neighbour_order=1,mapping="jordan_wigner",):r"""Generates the Hamiltonian for the Fermi-Hubbard model on a lattice. The Hamiltonian is represented as: .. math:: \hat{H} = -t\sum_{<i,j>, \sigma} c_{i\sigma}^{\dagger}c_{j\sigma} + U\sum_{i}n_{i \uparrow} n_{i\downarrow} where :math:`t` is the hopping term representing the kinetic energy of electrons, :math:`U` is the on-site Coulomb interaction representing the repulsion between electrons, :math:`<i,j>` represents the indices of neighbouring spins, :math:`\sigma` is the spin degree of freedom, and :math:`n_{i \uparrow}, n_{i \downarrow}` are number operators for spin-up and spin-down fermions at site :math:`i`. This function assumes two fermions with opposite spins on each lattice site. Args: lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, ``'rectangle'``, ``'triangle'``, ``'honeycomb'``, ``'kagome'``, ``'lieb'``, ``'cubic'``, ``'bcc'``, ``'fcc'`` or ``'diamond'``. n_cells (List[int]): Number of cells in each direction of the grid. hopping (float or tensor_like[float]): Hopping strength between neighbouring sites. It can be a number, an array of length equal to ``neighbour_order`` or a square matrix of shape ``(num_spins, num_spins)``, where ``num_spins`` is the total number of spins. Default value is 1.0. coulomb (float or tensor_like[float]): Coulomb interaction between spins. It can be a constant or an array of length equal to the number of spins. boundary_condition (bool or list[bool]): Specifies whether or not to enforce periodic boundary conditions for the different lattice axes. Default is ``False`` indicating open boundary condition. neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1, indicating nearest neighbours. mapping (str): Specifies the fermion-to-qubit mapping. Input values can be ``'jordan_wigner'``, ``'parity'`` or ``'bravyi_kitaev'``. Returns: ~ops.op_math.Sum: Hamiltonian for the Fermi-Hubbard model. **Example** >>> n_cells = [2] >>> t = 0.5 >>> u = 1.0 >>> spin_ham = qml.spin.fermi_hubbard("chain", n_cells, hopping=t, coulomb=u) >>> spin_ham ( -0.25 * (Y(0) @ Z(1) @ Y(2)) + -0.25 * (X(0) @ Z(1) @ X(2)) + 0.5 * I(0) + -0.25 * (Y(1) @ Z(2) @ Y(3)) + -0.25 * (X(1) @ Z(2) @ X(3)) + -0.25 * Z(1) + -0.25 * Z(0) + 0.25 * (Z(0) @ Z(1)) + -0.25 * Z(3) + -0.25 * Z(2) + 0.25 * (Z(2) @ Z(3)) ) """lattice=generate_lattice(lattice,n_cells,boundary_condition,neighbour_order)ifisinstance(hopping,(int,float,complex)):hopping=[hopping]hopping=math.asarray(hopping)ifhopping.shapenotin[(neighbour_order,),(lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The hopping parameter should be a number or an array of shape ({neighbour_order},) or ({lattice.n_sites},{lattice.n_sites})")spin=2hopping_ham=0.0*FermiWord({})ifhopping.shape==(neighbour_order,):foredgeinlattice.edges:forsinrange(spin):i,j,order=edges1=i*spin+ss2=j*spin+shopping_term=-hopping[order]*(FermiWord({(0,s1):"+",(1,s2):"-"})+FermiWord({(0,s2):"+",(1,s1):"-"}))hopping_ham+=hopping_termelse:foredgeinlattice.edges:forsinrange(spin):i,j=edge[0:2]s1=i*spin+ss2=j*spin+shopping_term=-hopping[i][j]*(FermiWord({(0,s1):"+",(1,s2):"-"})+FermiWord({(0,s2):"+",(1,s1):"-"}))hopping_ham+=hopping_termint_term=0.0*FermiWord({})ifisinstance(coulomb,(int,float,complex)):coulomb=math.ones(lattice.n_sites)*coulombforiinrange(lattice.n_sites):up_spin=i*spindown_spin=i*spin+1int_term+=coulomb[i]*FermiWord({(0,up_spin):"+",(1,up_spin):"-",(2,down_spin):"+",(3,down_spin):"-"})hamiltonian=hopping_ham+int_termmapping=mapping.strip().lower()ifmappingnotin["jordan_wigner","parity","bravyi_kitaev"]:raiseValueError(f"The '{mapping}' transformation is not available."f"Please set mapping to 'jordan_wigner', 'parity', or 'bravyi_kitaev'")qubit_ham=qml.qchem.qubit_observable(hamiltonian,mapping=mapping)returnqubit_ham.simplify()
[docs]defemery(lattice,n_cells,hopping=1.0,coulomb=1.0,intersite_coupling=1.0,boundary_condition=False,neighbour_order=1,mapping="jordan_wigner",):r"""Generates the Hamiltonian for the Emery model on a lattice. The `Hamiltonian <https://arxiv.org/pdf/2309.11786>`_ for the `Emery model <https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.58.2794>`_ is represented as: .. math:: \begin{align*} \hat{H} & = - t \sum_{\langle i,j \rangle, \sigma} c_{i\sigma}^{\dagger}c_{j\sigma} + U \sum_{i} n_{i \uparrow} n_{i\downarrow} + V \sum_{<i,j>} (n_{i \uparrow} + n_{i \downarrow})(n_{j \uparrow} + n_{j \downarrow})\ , \end{align*} where :math:`t` is the hopping term representing the kinetic energy of electrons, :math:`U` is the on-site Coulomb interaction representing the repulsion between electrons, :math:`V` is the intersite coupling, :math:`<i,j>` represents the indices for neighbouring sites, :math:`\sigma` is the spin degree of freedom, and :math:`n_{k \uparrow}`, :math:`n_{k \downarrow}` are number operators for spin-up and spin-down fermions at site :math:`k`. This function assumes two fermions with opposite spins on each lattice site. Args: lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, ``'rectangle'``, ``'triangle'``, ``'honeycomb'``, ``'kagome'``, ``'lieb'``, ``'cubic'``, ``'bcc'``, ``'fcc'`` or ``'diamond'``. n_cells (list[int]): Number of cells in each direction of the grid. hopping (float or tensor_like[float]): Hopping strength between neighbouring sites. It can be a number, an array of length equal to ``neighbour_order`` or a square matrix of shape ``(n_sites, n_sites)``, where ``n_sites`` is the total number of sites. Default value is 1.0. coulomb (float or tensor_like[float]): Coulomb interaction between spins. It can be a number or an array of length equal to the number of spins. Default value is 1.0. intersite_coupling (float or tensor_like[float]): Interaction strength between spins on neighbouring sites. It can be a number, an array with length equal to ``neighbour_order`` or a square matrix of size ``(n_sites, n_sites)``, where ``n_sites`` is the total number of sites. Default value is 1.0. boundary_condition (bool or list[bool]): Specifies whether or not to enforce periodic boundary conditions for the different lattice axes. Default is ``False`` indicating open boundary condition. neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1, indicating nearest neighbours. mapping (str): Specifies the fermion-to-qubit mapping. Input values can be ``'jordan_wigner'``, ``'parity'`` or ``'bravyi_kitaev'``. Raises: ValueError: If ``hopping``, ``coulomb``, or ``intersite_coupling`` doesn't have correct dimensions, or if ``mapping`` is not available. Returns: ~ops.op_math.Sum: Hamiltonian for the Emery model. **Example** >>> n_cells = [2] >>> h = 0.5 >>> u = 1.0 >>> v = 0.2 >>> spin_ham = qml.spin.emery("chain", n_cells, hopping=h, coulomb=u, intersite_coupling=v) >>> spin_ham ( -0.25 * (Y(0) @ Z(1) @ Y(2)) + -0.25 * (X(0) @ Z(1) @ X(2)) + 0.7000000000000002 * I(0) + -0.25 * (Y(1) @ Z(2) @ Y(3)) + -0.25 * (X(1) @ Z(2) @ X(3)) + -0.35 * Z(1) + -0.35 * Z(0) + 0.25 * (Z(0) @ Z(1)) + -0.35 * Z(3) + -0.35 * Z(2) + 0.25 * (Z(2) @ Z(3)) + 0.05 * (Z(0) @ Z(2)) + 0.05 * (Z(0) @ Z(3)) + 0.05 * (Z(1) @ Z(2)) + 0.05 * (Z(1) @ Z(3)) ) """lattice=generate_lattice(lattice,n_cells,boundary_condition,neighbour_order)hopping=(math.asarray([hopping])ifisinstance(hopping,(int,float,complex))elsemath.asarray(hopping))intersite_coupling=(math.asarray([intersite_coupling])ifisinstance(intersite_coupling,(int,float,complex))elsemath.asarray(intersite_coupling))ifhopping.shapenotin[(neighbour_order,),(lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The hopping parameter should be a number or an "f"array of shape ({neighbour_order},) or ({lattice.n_sites},{lattice.n_sites}).")ifintersite_coupling.shapenotin[(neighbour_order,),(lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The intersite_coupling parameter should be a number or "f"an array of shape ({neighbour_order},) or ({lattice.n_sites},{lattice.n_sites}).")spin=2hopping_term=0.0*FermiWord({})intersite_term=0.0*FermiWord({})fori,j,orderinlattice.edges:hop=hopping[order]ifhopping.shape==(neighbour_order,)elsehopping[i][j]forsinrange(spin):s1=i*spin+ss2=j*spin+shopping_term-=hop*(FermiWord({(0,s1):"+",(1,s2):"-"})+FermiWord({(0,s2):"+",(1,s1):"-"}))intersite=(intersite_coupling[order]ifintersite_coupling.shape==(neighbour_order,)elseintersite_coupling[i][j])intersite_term+=(intersite*(FermiWord({(0,i*spin):"+",(1,i*spin):"-"})+FermiWord({(0,i*spin+1):"+",(1,i*spin+1):"-"}))*(FermiWord({(0,j*spin):"+",(1,j*spin):"-"})+FermiWord({(0,j*spin+1):"+",(1,j*spin+1):"-"})))ifisinstance(coulomb,(int,float,complex)):coulomb=math.ones(lattice.n_sites)*coulombcoulomb_term=0.0*FermiWord({})foriinrange(lattice.n_sites):up_spin=i*spindown_spin=i*spin+1coulomb_term+=coulomb[i]*FermiWord({(0,up_spin):"+",(1,up_spin):"-",(2,down_spin):"+",(3,down_spin):"-"})hamiltonian=hopping_term+coulomb_term+intersite_termmapping=mapping.strip().lower()ifmappingnotin["jordan_wigner","parity","bravyi_kitaev"]:raiseValueError(f"The '{mapping}' transformation is not available."f"Please set mapping to 'jordan_wigner', 'parity', or 'bravyi_kitaev'.")qubit_ham=qml.qchem.qubit_observable(hamiltonian,mapping=mapping)returnqubit_ham.simplify()
[docs]defhaldane(lattice,n_cells,hopping=1.0,hopping_next=1.0,phi=1.0,boundary_condition=False,mapping="jordan_wigner",):r"""Generates the Hamiltonian for the Haldane model on a lattice. The `Hamiltonian <https://arxiv.org/pdf/2211.13615>`_ for the `Haldane model <https://journals.aps.org/prl/pdf/10.1103/PhysRevLett.61.2015>`_ is represented as: .. math:: \begin{align*} \hat{H} & = - t^{1} \sum_{\langle i,j \rangle, \sigma} c_{i\sigma}^\dagger c_{j\sigma} - t^{2} \sum_{\langle\langle i,j \rangle\rangle, \sigma} \left( e^{i\phi} c_{i\sigma}^\dagger c_{j\sigma} + e^{-i\phi} c_{j\sigma}^\dagger c_{i\sigma} \right) \end{align*} where :math:`t^{1}` is the hopping amplitude between neighbouring sites :math:`\langle i,j \rangle`, :math:`t^{2}` is the hopping amplitude between next-nearest neighbour sites :math:`\langle \langle i,j \rangle \rangle`, :math:`\phi` is the phase factor that breaks time-reversal symmetry in the system, and :math:`\sigma` is the spin degree of freedom. This function assumes two fermions with opposite spins on each lattice site. Args: lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, ``'rectangle'``, ``'triangle'``, ``'honeycomb'``, ``'kagome'``, ``'lieb'``, ``'cubic'``, ``'bcc'``, ``'fcc'`` or ``'diamond'``. n_cells (list[int]): Number of cells in each direction of the grid. hopping (float or tensor_like[float]): Hopping strength between nearest neighbouring sites. It can be a number, or a square matrix of size ``(n_sites, n_sites)``, where ``n_sites`` is the total number of sites. Default value is 1.0. hopping_next (float or tensor_like[float]): Hopping strength between next-nearest neighbouring sites. It can be a number, or a square matrix of size ``(n_sites, n_sites)``, where ``n_sites`` is the total number of sites. Default value is 1.0. phi (float or tensor_like[float]): Phase factor in the system. It can be a number, or a square matrix of size ``(n_sites, n_sites)``, where ``n_sites`` is the total number of sites. Default value is 1.0. boundary_condition (bool or list[bool]): Specifies whether or not to enforce periodic boundary conditions for the different lattice axes. Default is ``False`` indicating open boundary condition. mapping (str): Specifies the fermion-to-qubit mapping. Input values can be ``'jordan_wigner'``, ``'parity'`` or ``'bravyi_kitaev'``. Raises: ValueError: If ``hopping``, ``hopping_next``, or ``phi`` doesn't have correct dimensions, or if ``mapping`` is not available. Returns: ~ops.op_math.Sum: Hamiltonian for the Haldane model. **Example** >>> n_cells = [2] >>> h1 = 0.5 >>> h2 = 1.0 >>> phi = 0.1 >>> spin_ham = qml.spin.haldane("chain", n_cells, hopping=h1, hopping_next=h2, phi=phi) >>> spin_ham ( -0.25 * (Y(0) @ Z(1) @ Y(2)) + -0.25 * (X(0) @ Z(1) @ X(2)) + -0.25 * (Y(1) @ Z(2) @ Y(3)) + -0.25 * (X(1) @ Z(2) @ X(3)) ) """lattice=generate_lattice(lattice,n_cells,boundary_condition,neighbour_order=2)hopping=(math.asarray([hopping])ifisinstance(hopping,(int,float,complex))elsemath.asarray(hopping))hopping_next=(math.asarray([hopping_next])ifisinstance(hopping_next,(int,float,complex))elsemath.asarray(hopping_next))phi=math.asarray([phi])ifisinstance(phi,(int,float,complex))elsemath.asarray(phi)ifhopping.shapenotin[(1,),(lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The hopping parameter should be a constant or an array of shape ({lattice.n_sites},{lattice.n_sites}).")ifhopping_next.shapenotin[(1,),(lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The hopping_next parameter should be a constant or an array of shape ({lattice.n_sites},{lattice.n_sites}).")ifphi.shapenotin[(1,),(lattice.n_sites,lattice.n_sites)]:raiseValueError(f"The phi parameter should be a constant or an array of shape ({lattice.n_sites},{lattice.n_sites}).")spin=2hamiltonian=0.0*FermiWord({})fori,j,orderinlattice.edges:hop1=hopping[0]ifhopping.shape==(1,)elsehopping[i][j]hop2=hopping_next[0]ifhopping_next.shape==(1,)elsehopping_next[i][j]phi_term=phi[0]ifphi.shape==(1,)elsephi[i][j]forsinrange(spin):s1=i*spin+ss2=j*spin+siforder==0:hamiltonian-=hop1*(FermiWord({(0,s1):"+",(1,s2):"-"})+FermiWord({(0,s2):"+",(1,s1):"-"}))else:hamiltonian-=hop2*(math.exp(1j*phi_term)*FermiWord({(0,s1):"+",(1,s2):"-"}))hamiltonian-=hop2*(math.exp(-1j*phi_term)*FermiWord({(0,s2):"+",(1,s1):"-"}))mapping=mapping.strip().lower()ifmappingnotin["jordan_wigner","parity","bravyi_kitaev"]:raiseValueError(f"The '{mapping}' transformation is not available."f"Please set mapping to 'jordan_wigner', 'parity', or 'bravyi_kitaev'.")qubit_ham=qml.qchem.qubit_observable(hamiltonian,mapping=mapping)returnqubit_ham.simplify()
[docs]defkitaev(n_cells,coupling=None,boundary_condition=False):r"""Generates the Hamiltonian for the Kitaev model on the honeycomb lattice. The `Kitaev <https://arxiv.org/abs/cond-mat/0506438>`_ model Hamiltonian is represented as: .. math:: \begin{align*} \hat{H} = K_X \sum_{\langle i,j \rangle \in X}\sigma_i^x\sigma_j^x + \:\: K_Y \sum_{\langle i,j \rangle \in Y}\sigma_i^y\sigma_j^y + \:\: K_Z \sum_{\langle i,j \rangle \in Z}\sigma_i^z\sigma_j^z \end{align*} where :math:`\sigma` is a Pauli operator and :math:`<i,j>` represents the indices for neighbouring spins. The parameters :math:`K_X`, :math:`K_Y`, :math:`K_Z` are the coupling constants defined for the Hamiltonian, where :math:`X`, :math:`Y`, :math:`Z` represent the set of edges in the Honeycomb lattice between spins :math:`i` and :math:`j` with real-space bond directions :math:`[0, 1], [\frac{\sqrt{3}}{2}, \frac{1}{2}], [\frac{\sqrt{3}}{2}, -\frac{1}{2}]`, respectively. Args: n_cells (list[int]): Number of cells in each direction of the grid. coupling (tensor_like[float]): Coupling between spins. It can be an array of length 3 defining :math:`K_X`, :math:`K_Y`, :math:`K_Z` coupling constants. Default value is 1.0 for each coupling constant. boundary_condition (bool or list[bool]): Specifies whether or not to enforce periodic boundary conditions for the different lattice axes. Default is ``False`` indicating open boundary condition. Raises: ValueError: if ``coupling`` doesn't have correct dimensions. Returns: ~ops.op_math.Sum: Hamiltonian for the Kitaev model. **Example** >>> n_cells = [2, 2] >>> k = np.array([0.5, 0.6, 0.7]) >>> spin_ham = qml.spin.kitaev(n_cells, coupling=k) >>> spin_ham ( 0.5 * (X(0) @ X(1)) + 0.5 * (X(2) @ X(3)) + 0.5 * (X(4) @ X(5)) + 0.5 * (X(6) @ X(7)) + 0.6 * (Y(1) @ Y(2)) + 0.6 * (Y(5) @ Y(6)) + 0.7 * (Z(1) @ Z(4)) + 0.7 * (Z(3) @ Z(6)) ) """ifcouplingisNone:coupling=[1.0,1.0,1.0]iflen(coupling)!=3:raiseValueError("The coupling parameter should be a list of length 3.")vectors=[[1,0],[0.5,0.75**0.5]]positions=[[0,0],[0.5,0.5/3**0.5]]custom_edges=[[(0,1),("XX",coupling[0])],[(1,2),("YY",coupling[1])],[(1,n_cells[1]*2),("ZZ",coupling[2])],]lattice=Lattice(n_cells=n_cells[0:2],vectors=vectors,positions=positions,boundary_condition=boundary_condition,custom_edges=custom_edges,)opmap={"X":X,"Y":Y,"Z":Z}hamiltonian=0.0*qml.I(0)foredgeinlattice.edges:v1,v2=edge[0:2]op1,op2=edge[2][0]coeff=edge[2][1]hamiltonian+=coeff*(opmap[op1](v1)@opmap[op2](v2))returnhamiltonian.simplify()
[docs]defspin_hamiltonian(lattice):r"""Generates a spin Hamiltonian for a custom :class:`~pennylane.spin.Lattice` object. Args: lattice (Lattice): custom lattice defined with ``custom_edges`` Raises: ValueError: if the provided Lattice does not have ``custom_edges`` defined with operators Returns: ~ops.op_math.Sum: Hamiltonian for the lattice **Example** .. code-block:: python >>> lattice = qml.spin.Lattice( ... n_cells=[2, 2], ... vectors=[[1, 0], [0, 1]], ... positions=[[0, 0], [1, 5]], ... boundary_condition=False, ... custom_edges=[[(0, 1), ("XX", 0.5)], [(1, 2), ("YY", 0.6)], [(1, 4), ("ZZ", 0.7)]], ... custom_nodes=[[0, ("X", 0.5)], [1, ("Y", 0.3)]], ... ) >>> qml.spin.spin_hamiltonian(lattice=lattice) ( 0.5 * (X(0) @ X(1)) + 0.5 * (X(2) @ X(3)) + 0.5 * (X(4) @ X(5)) + 0.5 * (X(6) @ X(7)) + 0.6 * (Y(1) @ Y(2)) + 0.6 * (Y(5) @ Y(6)) + 0.7 * (Z(1) @ Z(4)) + 0.7 * (Z(3) @ Z(6)) + 0.5 * X(0) + 0.3 * Y(1) ) """ifnotisinstance(lattice.edges[0][2],tuple):raiseValueError("Custom edges need to be defined and should have an operator defined as a `str`")opmap={"I":I,"X":X,"Y":Y,"Z":Z}hamiltonian=0.0*qml.I(0)foredgeinlattice.edges:v1,v2=edge[0:2]op1,op2=edge[2][0]coeff=edge[2][1]hamiltonian+=coeff*(opmap[op1](v1)@opmap[op2](v2))iflattice.nodesisnotNone:fornodeinlattice.nodes:n=node[0]op=node[1][0]coeff=node[1][1]hamiltonian+=coeff*(opmap[op](n))returnhamiltonian.simplify()