From 7f63b0f65202eeceb130b79c1b00690ab29ce2ae Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Wed, 28 Aug 2024 15:04:59 -0400 Subject: [PATCH 01/83] [skip ci] Added Kitaev model Hamiltonian --- pennylane/spin/__init__.py | 3 +- pennylane/spin/lattice.py | 69 +++++++++++++++++++++++++-- pennylane/spin/spin_hamiltonian.py | 66 ++++++++++++++++++++++++- tests/spin/test_lattice.py | 71 +++++++++++++++++++++++++++ tests/spin/test_spin_hamiltonian.py | 74 ++++++++++++++++++++++++++++- 5 files changed, 276 insertions(+), 7 deletions(-) diff --git a/pennylane/spin/__init__.py b/pennylane/spin/__init__.py index 8401e77b35c..3af1c026b95 100644 --- a/pennylane/spin/__init__.py +++ b/pennylane/spin/__init__.py @@ -16,4 +16,5 @@ """ from .lattice import Lattice -from .spin_hamiltonian import fermi_hubbard, heisenberg, transverse_ising +from .spin_hamiltonian import (fermi_hubbard, heisenberg, kitaev, + transverse_ising) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 9c823d8bc73..35560132543 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -69,6 +69,7 @@ def __init__( positions=None, boundary_condition=False, neighbour_order=1, + custom_edges=None, distance_tol=1e-5, ): @@ -109,10 +110,19 @@ def __init__( n_sl = len(self.positions) self.n_sites = math.prod(n_cells) * n_sl self.lattice_points, lattice_map = self._generate_grid(neighbour_order) + if custom_edges is None: + cutoff = ( + neighbour_order * math.max(math.linalg.norm(self.vectors, axis=1)) + distance_tol + ) + edges = self._identify_neighbours(cutoff) + self.edges = Lattice._generate_true_edges(edges, lattice_map, neighbour_order) + else: + if neighbour_order > 1: + raise ValueError( + "custom_edges and neighbour_order cannot be specified at the same time" + ) + self.edges = self.get_custom_edges(custom_edges, lattice_map) - cutoff = neighbour_order * math.max(math.linalg.norm(self.vectors, axis=1)) + distance_tol - edges = self._identify_neighbours(cutoff) - self.edges = Lattice._generate_true_edges(edges, lattice_map, neighbour_order) self.edges_indices = [(v1, v2) for (v1, v2, color) in self.edges] def _identify_neighbours(self, cutoff): @@ -187,7 +197,58 @@ def _generate_grid(self, neighbour_order): lattice_points.append(point) lattice_map.append(node_index) - return math.array(lattice_points), math.array(lattice_map) + return math.array(lattice_points), lattice_map + + def get_custom_edges(self, custom_edges, lattice_map): + """Generates the edges described in `custom_edges` for all unit cells.""" + + if not all([len(edge) in (1, 2) for edge in custom_edges]): + raise TypeError( + """ + custom_edges must be a list of tuples of length 1 or 2. + Every tuple must contain two lattice indices to represent the edge + and can optionally include a list to represent the operation and coefficient for that edge. + """ + ) + + edges = [] + n_sl = len(self.positions) + nsites_axis = math.cumprod([n_sl, *self.n_cells[:0:-1]])[::-1] + + for i, custom_edge in enumerate(custom_edges): + edge = custom_edge[0] + + if edge[0] >= self.n_sites or edge[1] >= self.n_sites: + raise ValueError( + f"The edge {edge} has vertices greater than n_sites, {self.n_sites}" + ) + + edge_operation = custom_edge[1] if len(custom_edge) == 2 else i + map_edge1 = lattice_map.index(edge[0]) + map_edge2 = lattice_map.index(edge[1]) + edge_distance = self.lattice_points[map_edge2] - self.lattice_points[map_edge1] + v1, v2 = math.mod(edge, n_sl) + dist_cell = (edge_distance + self.positions[v1] - self.positions[v2]) @ math.linalg.inv( + self.vectors + ) + dist_cell = math.asarray(math.rint(dist_cell), dtype=int) + edge_ranges = [] + for idx, cell in enumerate(self.n_cells): + if self.boundary_condition[idx]: + edge_ranges.append(range(0, cell)) + else: + edge_ranges.append( + range( + math.maximum(0, -dist_cell[idx]), cell - math.maximum(0, dist_cell[idx]) + ) + ) + + for cell in itertools.product(*edge_ranges): + node1_idx = math.dot(math.mod(cell, self.n_cells), nsites_axis) + v1 + node2_idx = math.dot(math.mod(cell + dist_cell, self.n_cells), nsites_axis) + v2 + edges.append((node1_idx, node2_idx, edge_operation)) + + return edges def add_edge(self, edge_indices): r"""Adds a specific edge based on the site index without translating it. diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index fb48f51e3d5..c4ee9dfab52 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -19,7 +19,7 @@ from pennylane import X, Y, Z, math from pennylane.fermi import FermiWord -from .lattice import _generate_lattice +from .lattice import Lattice, _generate_lattice # pylint: disable=too-many-arguments @@ -298,3 +298,67 @@ def fermi_hubbard( qubit_ham = qml.qchem.qubit_observable(hamiltonian, mapping=mapping) return qubit_ham.simplify() + + +def kitaev(n_cells, coupling=None, boundary_condition=False): + r"""Generates the Kitaev Hamiltonian on the Honeycomb lattice. + + The Hamiltonian is represented as: + + .. math:: + + \hat{H} = K_x.\sum_{}\sigma_i^x\sigma_j^x + K_y.\sum_{}\sigma_i^y\sigma_j^y + K_z.\sum_{}\sigma_i^z\sigma_j^z + + where :math:`K_x`, :math:`K_y`, :math:`K_z` are the coupling constants defined for the Hamiltonian, and ``i,j`` represent + the indices for neighbouring spins. + + Args: + n_cells (List[int]): Number of cells in each direction of the grid. + coupling (List[math.array[float]]): Coupling between spins, it is a list of length 3. + Default value is [1.0, 1.0, 1.0]. + boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes, + default is ``False`` indicating open boundary condition. + + Returns: + pennylane.LinearCombination: Hamiltonian for the Kitaev model. + + **Example** + + >>> n_cells = [2,2] + >>> k = [0.5, 0.6, 0.7] + >>> spin_ham = qml.spin.kitaev(n_cells, coupling=k) + >>> spin_ham + + """ + + if coupling is None: + coupling = [1.0, 1.0, 1.0] + + if len(coupling) != 3: + raise ValueError("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, + ) + print(lattice.edges) + opmap = {"X": X, "Y": Y, "Z": Z} + hamiltonian = 0.0 * qml.I(0) + for edge in lattice.edges: + v1, v2 = edge[0:2] + op1, op2 = edge[2][0] + coeff = edge[2][1] + hamiltonian += coeff * (opmap[op1](v1) @ opmap[op2](v2)) + + return hamiltonian.simplify() diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index 6576a396f3d..1b0eefc0bd5 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -14,6 +14,8 @@ """ Unit tests for functions and classes needed for construct a lattice. """ +import re + import numpy as np import pytest @@ -539,3 +541,72 @@ def test_shape_error(): lattice = "Octagon" with pytest.raises(ValueError, match="Lattice shape, 'Octagon' is not supported."): _generate_lattice(lattice=lattice, n_cells=n_cells) + + +def test_neighbour_order_error(): + r"""Test that an error is raised if neighbour order is greater than 1 when custom_edges are provided.""" + + vectors = [[0, 1], [1, 0]] + n_cells = [3, 3] + custom_edges = [[(0, 1)], [(0, 5)], [(0, 4)]] + with pytest.raises( + ValueError, match="custom_edges and neighbour_order cannot be specified at the same time" + ): + Lattice(n_cells=n_cells, vectors=vectors, neighbour_order=2, custom_edges=custom_edges) + + +def test_custom_edge_type_error(): + r"""Test that an error is raised if custom_edges are not provided as a list of length 1 or 2.""" + + vectors = [[0, 1], [1, 0]] + n_cells = [3, 3] + custom_edges = [[(0, 1), 1, 3], [(0, 5)], [(0, 4)]] + with pytest.raises(TypeError, match="custom_edges must be a list of tuples of length 1 or 2."): + Lattice(n_cells=n_cells, vectors=vectors, custom_edges=custom_edges) + + +def test_custom_edge_value_error(): + r"""Test that an error is raised if the custom_edges contains an edge with site_index greater than number of sites""" + + vectors = [[0, 1], [1, 0]] + n_cells = [3, 3] + custom_edges = [[(0, 1)], [(0, 5)], [(0, 12)]] + with pytest.raises( + ValueError, match=re.escape("The edge (0, 12) has vertices greater than n_sites, 9") + ): + Lattice(n_cells=n_cells, vectors=vectors, custom_edges=custom_edges) + + +@pytest.mark.parametrize( + # expected_edges here were obtained manually + ("vectors", "positions", "n_cells", "custom_edges", "expected_edges"), + [ + ( + [[0, 1], [1, 0]], + [[0, 0]], + [3, 3], + [[(0, 1)], [(0, 5)], [(0, 4)]], + [(0, 1, 0), (0, 5, 1), (0, 4, 2), (1, 2, 0), (3, 4, 0), (0, 4, 2), (1, 5, 2)], + ), + ( + [[0, 1], [1, 0]], + [[0, 0]], + [3, 4], + [[(0, 1)], [(1, 4)], [(1, 5)]], + [(0, 1, 0), (1, 2, 0), (2, 3, 0), (1, 4, 1), (2, 5, 1), (0, 4, 2), (2, 6, 2)], + ), + ( + [[1, 0], [0.5, np.sqrt(3) / 2]], + [[0.5, 0.5 / 3**0.5], [1, 1 / 3**0.5]], + [2, 2], + [[(0, 1)], [(1, 2)], [(1, 5)]], + [(2, 3, 0), (4, 5, 0), (1, 2, 1), (5, 6, 1), (1, 5, 2), (3, 7, 2)], + ), + ], +) +def test_custom_edges(vectors, positions, n_cells, custom_edges, expected_edges): + r"""Test that the edges are added as per custom_edges provided""" + lattice = Lattice( + n_cells=n_cells, vectors=vectors, positions=positions, custom_edges=custom_edges + ) + assert np.all(np.isin(expected_edges, lattice.edges)) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 72ae1243149..8d67da52510 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -21,7 +21,7 @@ import pennylane as qml from pennylane import I, X, Y, Z -from pennylane.spin import fermi_hubbard, heisenberg, transverse_ising +from pennylane.spin import fermi_hubbard, heisenberg, kitaev, transverse_ising def test_coupling_error(): @@ -780,3 +780,75 @@ def test_fermi_hubbard_hamiltonian_matrix(shape, n_cells, t, coulomb, expected_h fermi_hub_ham = fermi_hubbard(lattice=shape, n_cells=n_cells, hopping=t, coulomb=coulomb) qml.assert_equal(fermi_hub_ham, expected_ham) + + +def test_coupling_error_kitaev(): + r"""Test that an error is raised when the provided coupling shape is wrong for + Kitaev Hamiltonian.""" + with pytest.raises( + ValueError, + match=re.escape("The coupling parameter should be a list of length 3"), + ): + kitaev(n_cells=[3, 4], coupling=[1.0, 2.0]) + + +@pytest.mark.parametrize( + # expected_ham here was obtained manually + ("n_cells", "j", "boundary_condition", "expected_ham"), + [ + ( + [2, 2, 1], + None, + False, + 1.0 * (Z(1) @ Z(4)) + + 1.0 * (Z(3) @ Z(6)) + + 1.0 * (X(0) @ X(1)) + + 1.0 * (X(2) @ X(3)) + + 1.0 * (X(4) @ X(5)) + + 1.0 * (X(6) @ X(7)) + + 1.0 * (Y(1) @ Y(2)) + + 1.0 * (Y(5) @ Y(6)), + ), + ( + [2, 2], + [0.5, 0.6, 0.7], + False, + 0.7 * (Z(1) @ Z(4)) + + 0.7 * (Z(3) @ Z(6)) + + 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)), + ), + ( + [2, 3], + [0.1, 0.2, 0.3], + True, + 0.3 * (Z(1) @ Z(6)) + + 0.3 * (Z(3) @ Z(8)) + + 0.3 * (Z(5) @ Z(10)) + + 0.3 * (Z(0) @ Z(7)) + + 0.3 * (Z(2) @ Z(9)) + + 0.3 * (Z(4) @ Z(11)) + + 0.1 * (X(0) @ X(1)) + + 0.1 * (X(2) @ X(3)) + + 0.1 * (X(4) @ X(5)) + + 0.1 * (X(6) @ X(7)) + + 0.1 * (X(8) @ X(9)) + + 0.1 * (X(10) @ X(11)) + + 0.2 * (Y(1) @ Y(2)) + + 0.2 * (Y(3) @ Y(4)) + + 0.2 * (Y(0) @ Y(5)) + + 0.2 * (Y(7) @ Y(8)) + + 0.2 * (Y(9) @ Y(10)) + + 0.2 * (Y(11) @ Y(6)), + ), + ], +) +def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): + r"""Test that the correct Hamiltonian is generated""" + kitaev_ham = kitaev(n_cells=n_cells, coupling=j, boundary_condition=boundary_condition) + + qml.assert_equal(kitaev_ham, expected_ham) From 756dd7696010216a8c73213e3cac7d5efec6687c Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Wed, 28 Aug 2024 15:10:18 -0400 Subject: [PATCH 02/83] Update pennylane/spin/spin_hamiltonian.py --- pennylane/spin/spin_hamiltonian.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index c4ee9dfab52..bacee06854c 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -352,7 +352,6 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): boundary_condition=boundary_condition, custom_edges=custom_edges, ) - print(lattice.edges) opmap = {"X": X, "Y": Y, "Z": Z} hamiltonian = 0.0 * qml.I(0) for edge in lattice.edges: From ed029a525bdfa3263893ad9167263738619c77fa Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Tue, 3 Sep 2024 11:26:39 -0400 Subject: [PATCH 03/83] Cleaned up some --- pennylane/spin/lattice.py | 30 ++++--- pennylane/spin/spin_hamiltonian.py | 127 ++++++++++++++++------------ tests/spin/test_spin_hamiltonian.py | 21 +++++ 3 files changed, 113 insertions(+), 65 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 35560132543..3f19b85acdd 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -33,13 +33,14 @@ class Lattice: n_cells (list[int]): Number of cells in each direction of the grid. vectors (list[list[float]]): Primitive vectors for the lattice. positions (list[list[float]]): Initial positions of spin cites. Default value is - ``[[0.0]*number of dimensions]``. + ``[[0.0]`` :math:`\times` ``number of dimensions]``. + boundary_condition (bool or list[bool]): Defines boundary conditions 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 (nearest neighbour). distance_tol (float): Distance below which spatial points are considered equal for the - purpose of identifying nearest neighbours, default value is 1e-5. + purpose of identifying nearest neighbours. Default value is 1e-5. Raises: TypeError: @@ -53,13 +54,15 @@ class Lattice: Lattice object **Example** + >>> n_cells = [2,2] >>> vectors = [[0, 1], [1, 0]] >>> boundary_condition = [True, False] >>> lattice = qml.spin.Lattice(n_cells, vectors, >>> boundary_condition=boundary_condition) - >>> print(lattice.edges) + >>> lattice.edges [(2, 3, 0), (0, 2, 0), (1, 3, 0), (0, 1, 0)] + """ def __init__( @@ -128,7 +131,7 @@ def __init__( def _identify_neighbours(self, cutoff): r"""Identifies the connections between lattice points and returns the unique connections based on the neighbour_order. This function uses KDTree to identify neighbours, which - follows depth first search traversal.""" + follows depth-first search traversal.""" tree = KDTree(self.lattice_points) indices = tree.query_ball_tree(tree, cutoff) @@ -228,10 +231,10 @@ def get_custom_edges(self, custom_edges, lattice_map): map_edge2 = lattice_map.index(edge[1]) edge_distance = self.lattice_points[map_edge2] - self.lattice_points[map_edge1] v1, v2 = math.mod(edge, n_sl) - dist_cell = (edge_distance + self.positions[v1] - self.positions[v2]) @ math.linalg.inv( - self.vectors - ) - dist_cell = math.asarray(math.rint(dist_cell), dtype=int) + translation_vector = ( + edge_distance + self.positions[v1] - self.positions[v2] + ) @ math.linalg.inv(self.vectors) + translation_vector = math.asarray(math.rint(translation_vector), dtype=int) edge_ranges = [] for idx, cell in enumerate(self.n_cells): if self.boundary_condition[idx]: @@ -239,13 +242,16 @@ def get_custom_edges(self, custom_edges, lattice_map): else: edge_ranges.append( range( - math.maximum(0, -dist_cell[idx]), cell - math.maximum(0, dist_cell[idx]) + math.maximum(0, -translation_vector[idx]), + cell - math.maximum(0, translation_vector[idx]), ) ) for cell in itertools.product(*edge_ranges): node1_idx = math.dot(math.mod(cell, self.n_cells), nsites_axis) + v1 - node2_idx = math.dot(math.mod(cell + dist_cell, self.n_cells), nsites_axis) + v2 + node2_idx = ( + math.dot(math.mod(cell + translation_vector, self.n_cells), nsites_axis) + v2 + ) edges.append((node1_idx, node2_idx, edge_operation)) return edges @@ -377,7 +383,7 @@ def _kagome(n_cells, boundary_condition=False, neighbour_order=1): # TODO Check the efficiency of this function with a dictionary instead. def _generate_lattice(lattice, n_cells, boundary_condition=False, neighbour_order=1): - r"""Generates the lattice object for given shape and n_cells. + r"""Generates the lattice object for a given shape and n_cells. Args: lattice (str): Shape of the lattice. Input Values can be ``'chain'``, ``'square'``, ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. @@ -386,7 +392,7 @@ def _generate_lattice(lattice, n_cells, boundary_condition=False, neighbour_orde neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1 (nearest neighbour). Returns: - lattice object + lattice object. """ lattice_shape = lattice.strip().lower() diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index bacee06854c..f62d8ad6316 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -27,7 +27,7 @@ def transverse_ising( lattice, n_cells, coupling=1.0, h=1.0, boundary_condition=False, neighbour_order=1 ): - r"""Generates the transverse-field Ising model on a lattice. + r"""Generates the Hamiltonian for the transverse-field Ising model on a lattice. The Hamiltonian is represented as: @@ -39,20 +39,21 @@ def transverse_ising( transverse magnetic field and ``i,j`` represent the indices for neighbouring spins. Args: - lattice (str): Shape of the lattice. Input Values can be ``'chain'``, ``'square'``, - ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. - n_cells (List[int]): Number of cells in each direction of the grid. - coupling (float or List[float] or List[math.array[float]]): Coupling between spins, it can be a - number, a list of length equal to ``neighbour_order`` or a square matrix of size - ``(num_spins, num_spins)``. Default value is 1.0. - h (float): Value of external magnetic field. Default is 1.0. - boundary_condition (bool or list[bool]): Defines boundary conditions for 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. + lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, + ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. + n_cells (List[int]): Number of cells in each direction of the grid. + coupling (float or List[float] or List[math.array[float]]): Coupling between spins. It can + be a number, a list 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]): Defines boundary conditions for 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: - pennylane.LinearCombination: Hamiltonian for the transverse-field ising model. + ~ops.op_math.Sum: Hamiltonian for the transverse-field Ising model. **Example** @@ -61,12 +62,16 @@ def transverse_ising( >>> 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) + + -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) @@ -98,7 +103,7 @@ def transverse_ising( def heisenberg(lattice, n_cells, coupling=None, boundary_condition=False, neighbour_order=1): - r"""Generates the Heisenberg model on a lattice. + r"""Generates the Hamiltonian for the Heisenberg model on a lattice. The Hamiltonian is represented as: @@ -106,22 +111,23 @@ def heisenberg(lattice, n_cells, coupling=None, boundary_condition=False, neighb \hat{H} = J\sum_{}(\sigma_i^x\sigma_j^x + \sigma_i^y\sigma_j^y + \sigma_i^z\sigma_j^z) - where ``J`` is the coupling constant defined for the Hamiltonian, and ``i,j`` represent the indices for neighbouring spins. + where ``J`` is the coupling constant defined for the Hamiltonian, and ``i,j`` represent the + indices for neighbouring spins. Args: - lattice (str): Shape of the lattice. Input Values can be ``'chain'``, ``'square'``, ``'rectangle'``, - ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. - n_cells (List[int]): Number of cells in each direction of the grid. - coupling (List[List[float]] or List[math.array[float]]): Coupling between spins, it can be a 2D array - of shape (neighbour_order, 3) or a 3D array of shape 3 * number of spins * number of spins. - Default value is [1.0, 1.0, 1.0]. - boundary_condition (bool or list[bool]): Defines boundary conditions for 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. + lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, + ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. + n_cells (List[int]): Number of cells in each direction of the grid. + coupling (List[List[float]] or List[math.array[float]]): Coupling between spins. It can be a + 2D array of shape ``(neighbour_order, 3)`` or a 3D array of shape + ``(3, num_spins, num_spins)``, where ``num_spins`` is the total number of spins. + boundary_condition (bool or list[bool]): Defines boundary conditions for 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: - pennylane.LinearCombination: Hamiltonian for the heisenberg model. + ~ops.op_math.Sum: Hamiltonian for the heisenberg model. **Example** @@ -129,6 +135,7 @@ def heisenberg(lattice, n_cells, coupling=None, boundary_condition=False, neighb >>> j = [[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)) @@ -141,7 +148,7 @@ def heisenberg(lattice, n_cells, coupling=None, boundary_condition=False, neighb + 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) @@ -187,7 +194,7 @@ def fermi_hubbard( neighbour_order=1, mapping="jordan_wigner", ): - r"""Generates the Fermi-Hubbard model on a lattice. + r"""Generates the Hamiltonian for the Fermi-Hubbard model on a lattice. The Hamiltonian is represented as: @@ -195,37 +202,41 @@ def fermi_hubbard( \hat{H} = -t\sum_{, \sigma}(c_{i\sigma}^{\dagger}c_{j\sigma}) + U\sum_{i}n_{i \uparrow} n_{i\downarrow} - where ``t`` is the hopping term representing the kinetic energy of electrons, ``U`` is the on-site Coulomb interaction, - representing the repulsion between electrons, ``i,j`` represent the indices for neighbouring spins, ``\sigma`` - is the spin degree of freedom, and ``n_{i \uparrow}, n_{i \downarrow}`` are number operators for spin-up and - spin-down fermions at site ``i``. - This function assumes there are two fermions with opposite spins on each lattice site. + where ``t`` is the hopping term representing the kinetic energy of electrons, ``U`` is the + on-site Coulomb interaction, representing the repulsion between electrons, ``i,j`` represent the + indices for 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 ``i``. This function assumes there are two fermions with opposite spins on each lattice + site. Args: - lattice (str): Shape of the lattice. Input Values can be ``'chain'``, ``'square'``, - ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. - n_cells (List[int]): Number of cells in each direction of the grid. - hopping (float or List[float] or List[math.array(float)]): Hopping strength between neighbouring sites, it can be a - number, a list of length equal to ``neighbour_order`` or a square matrix of size - ``(num_spins, num_spins)``. Default value is 1.0. - coulomb (float or List[float]): Coulomb interaction between spins, it can be a constant or a list of length ``num_spins``. - boundary_condition (bool or list[bool]): Defines boundary conditions for 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'``. + lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, + ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. + n_cells (List[int]): Number of cells in each direction of the grid. + hopping (float or List[float] or List[math.array(float)]): Hopping strength between + neighbouring sites, it can be a number, a list of length equal to ``neighbour_order`` or + a square matrix of size ``(num_spins, num_spins)``, where ``num_spins`` is the total + number of spins. Default value is 1.0. + coulomb (float or List[float]): Coulomb interaction between spins. It can be a constant or a + list of length equal to number of spins. + boundary_condition (bool or list[bool]): Defines boundary conditions for 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: - pennylane.operator: Hamiltonian for the Fermi-Hubbard model. + ~ops.op_math.Sum: Hamiltonian for the Fermi-Hubbard model. **Example** >>> n_cells = [2] >>> h = [0.5] - >>> u = [1.0] + >>> u = 1.0 >>> spin_ham = qml.spin.fermi_hubbard("chain", n_cells, hopping=h, coulomb=u) >>> spin_ham + ( -0.25 * (Y(0) @ Z(1) @ Y(2)) + -0.25 * (X(0) @ Z(1) @ X(2)) + 0.5 * I(0) @@ -237,7 +248,7 @@ def fermi_hubbard( + -0.25 * Z(3) + -0.25 * Z(2) + 0.25 * (Z(2) @ Z(3)) - + ) """ lattice = _generate_lattice(lattice, n_cells, boundary_condition, neighbour_order) @@ -301,7 +312,7 @@ def fermi_hubbard( def kitaev(n_cells, coupling=None, boundary_condition=False): - r"""Generates the Kitaev Hamiltonian on the Honeycomb lattice. + r"""Generates the Hamiltonian for the Kitaev model on the Honeycomb lattice. The Hamiltonian is represented as: @@ -320,7 +331,7 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): default is ``False`` indicating open boundary condition. Returns: - pennylane.LinearCombination: Hamiltonian for the Kitaev model. + ~ops.op_math.Sum: Hamiltonian for the Kitaev model. **Example** @@ -328,6 +339,16 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): >>> k = [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)) + ) """ diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 8d67da52510..305c006f8e2 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -845,6 +845,27 @@ def test_coupling_error_kitaev(): + 0.2 * (Y(9) @ Y(10)) + 0.2 * (Y(11) @ Y(6)), ), + ( + [2, 3], + [0.1, 0.2, 0.3], + [True, False], + 0.3 * (Z(1) @ Z(6)) + + 0.3 * (Z(3) @ Z(8)) + + 0.3 * (Z(5) @ Z(10)) + + 0.3 * (Z(0) @ Z(7)) + + 0.3 * (Z(2) @ Z(9)) + + 0.3 * (Z(4) @ Z(11)) + + 0.1 * (X(0) @ X(1)) + + 0.1 * (X(2) @ X(3)) + + 0.1 * (X(4) @ X(5)) + + 0.1 * (X(6) @ X(7)) + + 0.1 * (X(8) @ X(9)) + + 0.1 * (X(10) @ X(11)) + + 0.2 * (Y(1) @ Y(2)) + + 0.2 * (Y(3) @ Y(4)) + + 0.2 * (Y(7) @ Y(8)) + + 0.2 * (Y(9) @ Y(10)), + ), ], ) def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): From 1db7eb59af9631a92387bd84102725fabc415ffa Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Wed, 4 Sep 2024 08:56:08 -0400 Subject: [PATCH 04/83] docstring fix --- pennylane/spin/__init__.py | 3 +-- pennylane/spin/spin_hamiltonian.py | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pennylane/spin/__init__.py b/pennylane/spin/__init__.py index 3af1c026b95..0040dcf958b 100644 --- a/pennylane/spin/__init__.py +++ b/pennylane/spin/__init__.py @@ -16,5 +16,4 @@ """ from .lattice import Lattice -from .spin_hamiltonian import (fermi_hubbard, heisenberg, kitaev, - transverse_ising) +from .spin_hamiltonian import fermi_hubbard, heisenberg, kitaev, transverse_ising diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index f62d8ad6316..25befbb4547 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -317,8 +317,11 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): The Hamiltonian is represented as: .. math:: - - \hat{H} = K_x.\sum_{}\sigma_i^x\sigma_j^x + K_y.\sum_{}\sigma_i^y\sigma_j^y + K_z.\sum_{}\sigma_i^z\sigma_j^z + \begin{align*} + \hat{H} = K_x.\sum_{\langle i,j \rangle \epsilon X}\sigma_i^x\sigma_j^x + + K_y.\sum_{\langle i,j \rangle \epsilon Y}\sigma_i^y\sigma_j^y + + K_z.\sum_{\langle i,j \rangle \epsilon Z}\sigma_i^z\sigma_j^z + \end{align*} where :math:`K_x`, :math:`K_y`, :math:`K_z` are the coupling constants defined for the Hamiltonian, and ``i,j`` represent the indices for neighbouring spins. From c15a6f54232608b47c61e53581320fc54be3d588 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:18:47 -0400 Subject: [PATCH 05/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Utkarsh --- pennylane/spin/spin_hamiltonian.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 25befbb4547..17913ab71f8 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -318,9 +318,9 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): .. math:: \begin{align*} - \hat{H} = K_x.\sum_{\langle i,j \rangle \epsilon X}\sigma_i^x\sigma_j^x + - K_y.\sum_{\langle i,j \rangle \epsilon Y}\sigma_i^y\sigma_j^y + - K_z.\sum_{\langle i,j \rangle \epsilon Z}\sigma_i^z\sigma_j^z + \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:`K_x`, :math:`K_y`, :math:`K_z` are the coupling constants defined for the Hamiltonian, and ``i,j`` represent From 702864f076a93f36d7e43f84086d9479e34765a0 Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Thu, 5 Sep 2024 08:42:48 -0400 Subject: [PATCH 06/83] Added custom_edges example --- pennylane/spin/lattice.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 2bf99c35485..815179b33f2 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -39,6 +39,12 @@ class Lattice: default is ``False`` indicating open boundary condition. neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1 (nearest neighbour). + custom_edges (list(list(tuples))): Specifies the edges to be added in the lattice. + Default value is None, which adds the edges based on neighbour_order. + Each element in the list is for a separate edge, and can contain 1 or 2 tuples. + First tuple contains the index of the starting and ending vertex of the edge. + Second tuple is optional and contains the operator on that edge and coefficient + of that operator. distance_tol (float): Distance below which spatial points are considered equal for the purpose of identifying nearest neighbours. Default value is 1e-5. @@ -203,7 +209,22 @@ def _generate_grid(self, neighbour_order): return math.array(lattice_points), lattice_map def get_custom_edges(self, custom_edges, lattice_map): - """Generates the edges described in `custom_edges` for all unit cells.""" + """Generates the edges described in `custom_edges` for all unit cells. + + **Example** + + Generates a square lattice with a single diagonal and assigns a different operation + to horizontal, vertical, and diagonal edges. + >>> n_cells = [3,3] + >>> vectors = [[1, 0], [0,1]] + >>> custom_edges = [ + [(0, 1), ("XX", 0.1)], + [(0, 3), ("YY", 0.2)], + [(0, 4), ("XY", 0.3)], + ] + >>> lattice = Lattice(n_cells=n_cells, vectors=vectors, custom_edges=custom_edges) + + """ if not all([len(edge) in (1, 2) for edge in custom_edges]): raise TypeError( From 4a4d6bf6c156cab053e64d89cb0b6505255babef Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Thu, 5 Sep 2024 08:48:54 -0400 Subject: [PATCH 07/83] docstring fix --- pennylane/spin/spin_hamiltonian.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 17913ab71f8..b2b0f4e847e 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -323,7 +323,8 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): K_z.\sum_{\langle i,j \rangle \in Z}\sigma_i^z\sigma_j^z \end{align*} - where :math:`K_x`, :math:`K_y`, :math:`K_z` are the coupling constants defined for the Hamiltonian, and ``i,j`` represent + where :math:`K_x`, :math:`K_y`, :math:`K_z` are the coupling constants defined for the Hamiltonian, + ``X``, ``Y``, ``Z``, represent the distinct edges in the Honeycomb lattice, and ``i,j`` represent the indices for neighbouring spins. Args: From e766e2e11f03f60147fe221c4e841282d00e6138 Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Thu, 5 Sep 2024 10:43:35 -0400 Subject: [PATCH 08/83] docstring fix --- pennylane/spin/spin_hamiltonian.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index b2b0f4e847e..885e0204730 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -324,8 +324,9 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): \end{align*} where :math:`K_x`, :math:`K_y`, :math:`K_z` are the coupling constants defined for the Hamiltonian, - ``X``, ``Y``, ``Z``, represent the distinct edges in the Honeycomb lattice, and ``i,j`` represent - the indices for neighbouring spins. + and :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. From 393dc0efbe39854bcd9a1b02d781f5a1e9f3d887 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Thu, 5 Sep 2024 17:10:58 -0400 Subject: [PATCH 09/83] custom spin hamiltonian function --- pennylane/spin/spin_hamiltonian.py | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 885e0204730..c70ccee484a 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -387,3 +387,51 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): hamiltonian += coeff * (opmap[op1](v1) @ opmap[op2](v2)) return hamiltonian.simplify() + + +def custom_hamiltonian_from_lattice(lattice): + r"""Generates the Hamiltonian for a custom lattice. + + Args: + lattice (Lattice): custom lattice defined with custom_edges + + Returns: + ~ops.op_math.Sum: Hamiltonian for the lattice + + **Example** + + lattice = 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_hamiltonian_from_lattice(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)) + ) + """ + if not isinstance(lattice.edges[0][2][0], str): + raise TypeError( + "Custom edges need to be defined and should have an operator defined as a `str`" + ) + + opmap = {"X": X, "Y": Y, "Z": Z} + hamiltonian = 0 + for edge in lattice.edges: + v1, v2 = edge[0:2] + op1, op2 = edge[2][0] + coeff = edge[2][1] + + hamiltonian += coeff * (opmap[op1](v1) @ opmap[op2](v2)) + + return hamiltonian.simplify() From 9f6e9e490851d0f30f55054ed50d83c91aa5e83b Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Fri, 6 Sep 2024 05:10:45 -0400 Subject: [PATCH 10/83] Update pennylane/spin/lattice.py Co-authored-by: Austin Huang <65315367+austingmhuang@users.noreply.github.com> --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 815179b33f2..209683b196c 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -229,7 +229,7 @@ def get_custom_edges(self, custom_edges, lattice_map): if not all([len(edge) in (1, 2) for edge in custom_edges]): raise TypeError( """ - custom_edges must be a list of tuples of length 1 or 2. + The elements of custom_edges should be lists of length 1 or 2. Inside said lists should be a tuple that contains two lattice indices to represent the edge and, optionally, a tuple that represents the operation and coefficient for that edge. Every tuple must contain two lattice indices to represent the edge and can optionally include a list to represent the operation and coefficient for that edge. """ From f47b3ecbe1ce029093f59dfee9a164f7d4c65d77 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Fri, 6 Sep 2024 05:11:16 -0400 Subject: [PATCH 11/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Austin Huang <65315367+austingmhuang@users.noreply.github.com> --- pennylane/spin/spin_hamiltonian.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 885e0204730..28f0b8734d6 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -329,10 +329,10 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): \frac{\sqrt{3}}{2}, -\frac{1}{2}]`, respectively. Args: - n_cells (List[int]): Number of cells in each direction of the grid. - coupling (List[math.array[float]]): Coupling between spins, it is a list of length 3. + n_cells (list[int]): Number of cells in each direction of the grid. + coupling (Optional[list[math.array[float]]]): Coupling between spins, it is a list of length 3. Default value is [1.0, 1.0, 1.0]. - boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes, + boundary_condition (bool | list[bool]): Defines boundary conditions for different lattice axes, default is ``False`` indicating open boundary condition. Returns: From 902be928933bf87743c6369207bee89757939802 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Fri, 6 Sep 2024 05:11:29 -0400 Subject: [PATCH 12/83] Update pennylane/spin/lattice.py Co-authored-by: Austin Huang <65315367+austingmhuang@users.noreply.github.com> --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 209683b196c..81fb98c3df7 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -39,7 +39,7 @@ class Lattice: default is ``False`` indicating open boundary condition. neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1 (nearest neighbour). - custom_edges (list(list(tuples))): Specifies the edges to be added in the lattice. + custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. Default value is None, which adds the edges based on neighbour_order. Each element in the list is for a separate edge, and can contain 1 or 2 tuples. First tuple contains the index of the starting and ending vertex of the edge. From 201fab91d9aba3b603a2bb5c7be58bcc74276356 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Fri, 6 Sep 2024 05:11:39 -0400 Subject: [PATCH 13/83] Update pennylane/spin/lattice.py Co-authored-by: Austin Huang <65315367+austingmhuang@users.noreply.github.com> --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 81fb98c3df7..a16fc4afa6b 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -35,7 +35,7 @@ class Lattice: positions (list[list[float]]): Initial positions of spin cites. Default value is ``[[0.0]`` :math:`\times` ``number of dimensions]``. - boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes, + boundary_condition (bool | list[bool]): Defines boundary conditions for 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 (nearest neighbour). From d3ced1166d945bcc2eae287b9b4222bfd021548d Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Fri, 6 Sep 2024 09:23:37 -0400 Subject: [PATCH 14/83] Addressed comments --- pennylane/spin/spin_hamiltonian.py | 13 +++++++++---- tests/spin/test_spin_hamiltonian.py | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 28f0b8734d6..623c3e9f0ea 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -312,7 +312,8 @@ def fermi_hubbard( def kitaev(n_cells, coupling=None, boundary_condition=False): - r"""Generates the Hamiltonian for the Kitaev model on the Honeycomb lattice. + r"""Generates the Hamiltonian for the `Kitaev model `_ + on the Honeycomb lattice. The Hamiltonian is represented as: @@ -330,11 +331,15 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): Args: n_cells (list[int]): Number of cells in each direction of the grid. - coupling (Optional[list[math.array[float]]]): Coupling between spins, it is a list of length 3. + coupling (Optional[list[float] | tensor_like(float)]): Coupling between spins, it is a list of length 3. Default value is [1.0, 1.0, 1.0]. - boundary_condition (bool | list[bool]): Defines boundary conditions for different lattice axes, + boundary_condition (Optional[bool | list[bool]]): Defines boundary conditions for different lattice axes, default is ``False`` indicating open boundary condition. + Raises: + TypeError: + if ``coupling`` doesn't have correct dimensions. + Returns: ~ops.op_math.Sum: Hamiltonian for the Kitaev model. @@ -361,7 +366,7 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): coupling = [1.0, 1.0, 1.0] if len(coupling) != 3: - raise ValueError("The coupling parameter should be a list of length 3") + raise ValueError("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]] diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index c320d1205ec..959e05acc6a 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -789,7 +789,7 @@ def test_coupling_error_kitaev(): Kitaev Hamiltonian.""" with pytest.raises( ValueError, - match=re.escape("The coupling parameter should be a list of length 3"), + match=re.escape("The coupling parameter should be a list of length 3."), ): kitaev(n_cells=[3, 4], coupling=[1.0, 2.0]) From 54624a5cf06b2fff57d5c458950572b6f8f2b61b Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 6 Sep 2024 14:39:04 -0400 Subject: [PATCH 15/83] draft tests and code --- pennylane/spin/__init__.py | 8 +++++- pennylane/spin/spin_hamiltonian.py | 27 ++++++++++--------- tests/spin/test_spin_hamiltonian.py | 40 ++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/pennylane/spin/__init__.py b/pennylane/spin/__init__.py index 0040dcf958b..b73de4100ef 100644 --- a/pennylane/spin/__init__.py +++ b/pennylane/spin/__init__.py @@ -16,4 +16,10 @@ """ from .lattice import Lattice -from .spin_hamiltonian import fermi_hubbard, heisenberg, kitaev, transverse_ising +from .spin_hamiltonian import ( + fermi_hubbard, + heisenberg, + kitaev, + transverse_ising, + custom_hamiltonian_from_lattice, +) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index c70ccee484a..f72771bdb51 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -395,6 +395,9 @@ def custom_hamiltonian_from_lattice(lattice): Args: lattice (Lattice): custom lattice defined with custom_edges + Raises: + ValueError: if ``custom_edges`` are not defined or not defined with operators + Returns: ~ops.op_math.Sum: Hamiltonian for the lattice @@ -408,20 +411,20 @@ def custom_hamiltonian_from_lattice(lattice): custom_edges=[[(0, 1), ("XX", 0.5)], [(1, 2), ("YY", 0.6)], [(1, 4), ("ZZ", 0.7)]], ) - custom_hamiltonian_from_lattice(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)) - ) + >>> custom_hamiltonian_from_lattice(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)) + ) """ if not isinstance(lattice.edges[0][2][0], str): - raise TypeError( + raise ValueError( "Custom edges need to be defined and should have an operator defined as a `str`" ) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index c320d1205ec..43992c4e02f 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -21,7 +21,14 @@ import pennylane as qml from pennylane import I, X, Y, Z -from pennylane.spin import fermi_hubbard, heisenberg, kitaev, transverse_ising +from pennylane.spin import ( + fermi_hubbard, + heisenberg, + kitaev, + transverse_ising, + custom_hamiltonian_from_lattice, + Lattice, +) pytestmark = pytest.mark.usefixtures("new_opmath_only") @@ -875,3 +882,34 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): kitaev_ham = kitaev(n_cells=n_cells, coupling=j, boundary_condition=boundary_condition) qml.assert_equal(kitaev_ham, expected_ham) + + +@pytest.mark.parametrize( + ("lattice", "expected_ham"), + [ + ( + 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)]], + ), + ( + 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)) + ), + ), + ], +) +def test_custom_hamiltonian(lattice, expected_ham): + r"""Test that the correct Hamiltonian is generated""" + custom_ham = custom_hamiltonian_from_lattice(lattice=lattice) + + qml.assert_equal(custom_ham, expected_ham) From 53868270c88d01ea85e6e4ab28ededb8d3dbbfa4 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Sun, 8 Sep 2024 05:37:04 -0400 Subject: [PATCH 16/83] Update pennylane/spin/lattice.py Co-authored-by: Utkarsh --- pennylane/spin/lattice.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index a16fc4afa6b..c7da4a93091 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -258,15 +258,10 @@ def get_custom_edges(self, custom_edges, lattice_map): translation_vector = math.asarray(math.rint(translation_vector), dtype=int) edge_ranges = [] for idx, cell in enumerate(self.n_cells): - if self.boundary_condition[idx]: - edge_ranges.append(range(0, cell)) - else: - edge_ranges.append( - range( - math.maximum(0, -translation_vector[idx]), - cell - math.maximum(0, translation_vector[idx]), - ) - ) + t_point = 0 if self.boundary_condition[idx] else translation_vector[idx] + edge_ranges.append( + range(math.maximum(0, -t_point), cell - math.maximum(0, t_point)) + ) for cell in itertools.product(*edge_ranges): node1_idx = math.dot(math.mod(cell, self.n_cells), nsites_axis) + v1 From c285ec7e4a990b0e26bba57de07e4d71b05ea0ab Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Sun, 8 Sep 2024 05:37:22 -0400 Subject: [PATCH 17/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Utkarsh --- pennylane/spin/spin_hamiltonian.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 623c3e9f0ea..84ec957474a 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -333,8 +333,8 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): n_cells (list[int]): Number of cells in each direction of the grid. coupling (Optional[list[float] | tensor_like(float)]): Coupling between spins, it is a list of length 3. Default value is [1.0, 1.0, 1.0]. - boundary_condition (Optional[bool | list[bool]]): Defines boundary conditions for different lattice axes, - default is ``False`` indicating open boundary condition. + boundary_condition (Optional[bool | list[bool]]): Defines boundary conditions for different lattice axes. + The default is ``False``, indicating open boundary conditions for all. Raises: TypeError: From 09727358cffc1c86fdb129c2a3298d3852725e7c Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Mon, 9 Sep 2024 06:49:24 -0400 Subject: [PATCH 18/83] fixed docstring for custom_edges --- pennylane/spin/lattice.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index c7da4a93091..8c8c0573146 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -130,7 +130,7 @@ def __init__( raise ValueError( "custom_edges and neighbour_order cannot be specified at the same time" ) - self.edges = self.get_custom_edges(custom_edges, lattice_map) + self.edges = self._get_custom_edges(custom_edges, lattice_map) self.edges_indices = [(v1, v2) for (v1, v2, color) in self.edges] @@ -208,9 +208,18 @@ def _generate_grid(self, neighbour_order): return math.array(lattice_points), lattice_map - def get_custom_edges(self, custom_edges, lattice_map): + def _get_custom_edges(self, custom_edges, lattice_map): """Generates the edges described in `custom_edges` for all unit cells. + Args: + custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. + Default value is None, which adds the edges based on neighbour_order. + Each element in the list is for a separate edge, and can contain 1 or 2 tuples. + First tuple contains the index of the starting and ending vertex of the edge. + Second tuple is optional and contains the operator on that edge and coefficient + of that operator. + lattice_map (list[int]): A list to represent the node number for each lattice_point. + **Example** Generates a square lattice with a single diagonal and assigns a different operation From 60491e23bc8e5981d323954b901ecc95fad33e71 Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Mon, 9 Sep 2024 07:10:41 -0400 Subject: [PATCH 19/83] Fixed tests --- tests/spin/test_lattice.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index 1b0eefc0bd5..b53c3aeea62 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -561,7 +561,9 @@ def test_custom_edge_type_error(): vectors = [[0, 1], [1, 0]] n_cells = [3, 3] custom_edges = [[(0, 1), 1, 3], [(0, 5)], [(0, 4)]] - with pytest.raises(TypeError, match="custom_edges must be a list of tuples of length 1 or 2."): + with pytest.raises( + TypeError, match="The elements of custom_edges should be lists of length 1 or 2." + ): Lattice(n_cells=n_cells, vectors=vectors, custom_edges=custom_edges) From af8c601d0e668e0d6d850ec6a6e9c0a23c7718fb Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Mon, 9 Sep 2024 07:44:29 -0400 Subject: [PATCH 20/83] Updated changelog --- doc/releases/changelog-dev.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 8f116a7d610..ced75b6f822 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -4,6 +4,10 @@

New features since last release

+* Function is added for generating spin Hamiltonian for + [Kitaev](Improvements 🛠 * Improve unit testing for capturing of nested control flows. From cd42fde10d4cda7ef778907313ebdeaad9c0511f Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Mon, 9 Sep 2024 08:28:28 -0400 Subject: [PATCH 21/83] Documentation build fix --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index ced75b6f822..e6c574bdc3b 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -5,7 +5,7 @@

New features since last release

* Function is added for generating spin Hamiltonian for - [Kitaev](Improvements 🛠 From 15b6222dad6036410fee167dc04f1f3ae9ecbf9a Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 9 Sep 2024 14:01:31 -0400 Subject: [PATCH 22/83] test for error --- pennylane/spin/spin_hamiltonian.py | 2 +- tests/spin/test_spin_hamiltonian.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index f72771bdb51..ede7c389a77 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -423,7 +423,7 @@ def custom_hamiltonian_from_lattice(lattice): + 0.7 * (Z(3) @ Z(6)) ) """ - if not isinstance(lattice.edges[0][2][0], str): + if not isinstance(lattice.edges[0][2], tuple): raise ValueError( "Custom edges need to be defined and should have an operator defined as a `str`" ) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 43992c4e02f..99ecb9527b6 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -913,3 +913,12 @@ def test_custom_hamiltonian(lattice, expected_ham): custom_ham = custom_hamiltonian_from_lattice(lattice=lattice) qml.assert_equal(custom_ham, expected_ham) + + +def test_custom_hamiltonian_error(): + r"""Test that the correct Hamiltonian is generated""" + lattice = Lattice(n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 1]]) + with pytest.raises( + ValueError, match="Custom edges need to be defined and should have an operator" + ): + custom_hamiltonian_from_lattice(lattice=lattice) From 05cad7d33fe981a6181697568c6ab76769775e59 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 9 Sep 2024 16:13:39 -0400 Subject: [PATCH 23/83] import errors --- tests/spin/test_spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 99ecb9527b6..697e4bad249 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -22,12 +22,12 @@ import pennylane as qml from pennylane import I, X, Y, Z from pennylane.spin import ( + Lattice, fermi_hubbard, heisenberg, kitaev, transverse_ising, custom_hamiltonian_from_lattice, - Lattice, ) pytestmark = pytest.mark.usefixtures("new_opmath_only") From 82aa678d401e3d611cac3054c1a830b93c0cdca3 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 9 Sep 2024 16:14:56 -0400 Subject: [PATCH 24/83] import errors --- pennylane/spin/spin_hamiltonian.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index ede7c389a77..9e612393983 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -422,6 +422,7 @@ def custom_hamiltonian_from_lattice(lattice): + 0.7 * (Z(1) @ Z(4)) + 0.7 * (Z(3) @ Z(6)) ) + """ if not isinstance(lattice.edges[0][2], tuple): raise ValueError( From 0b96a07d04ffd794485c8c1ed876a06c4e97a85b Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 9 Sep 2024 17:01:02 -0400 Subject: [PATCH 25/83] import errors --- tests/spin/test_spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 697e4bad249..13b316668ff 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -23,11 +23,11 @@ from pennylane import I, X, Y, Z from pennylane.spin import ( Lattice, + custom_hamiltonian_from_lattice, fermi_hubbard, heisenberg, kitaev, transverse_ising, - custom_hamiltonian_from_lattice, ) pytestmark = pytest.mark.usefixtures("new_opmath_only") From 2075d938652f86bbca9335bd8ec4a95580d80e75 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 9 Sep 2024 17:36:51 -0400 Subject: [PATCH 26/83] import errors --- pennylane/spin/spin_hamiltonian.py | 40 ++++++++++++++++-------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 9e612393983..ceb406acc27 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -393,36 +393,38 @@ def custom_hamiltonian_from_lattice(lattice): r"""Generates the Hamiltonian for a custom lattice. Args: - lattice (Lattice): custom lattice defined with custom_edges + lattice (Lattice): custom lattice defined with custom_edges Raises: ValueError: if ``custom_edges`` are not defined or not defined with operators Returns: - ~ops.op_math.Sum: Hamiltonian for the lattice + ~ops.op_math.Sum: Hamiltonian for the lattice **Example** - lattice = 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)]], - ) + .. code-block:: python - >>> custom_hamiltonian_from_lattice(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)) + lattice = 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_hamiltonian_from_lattice(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)) + ) + """ if not isinstance(lattice.edges[0][2], tuple): raise ValueError( From 8dc021b8e93d7d1f81ebb64519e7088c0e13b8fc Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:50:08 -0400 Subject: [PATCH 27/83] Update doc/releases/changelog-dev.md Co-authored-by: Austin Huang <65315367+austingmhuang@users.noreply.github.com> --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index e6c574bdc3b..1828f5dd198 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -4,7 +4,7 @@

New features since last release

-* Function is added for generating spin Hamiltonian for +* Function is added for generating the spin Hamiltonian for the [Kitaev](https://arxiv.org/pdf/2406.06625) model on a lattice. [(#6174)](https://github.com/PennyLaneAI/pennylane/pull/6174) From e35099090abde19748d8846495300fa6c1e4c000 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:14:17 -0400 Subject: [PATCH 28/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Utkarsh --- pennylane/spin/spin_hamiltonian.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 84ec957474a..eeb22c257f6 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -337,8 +337,7 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): The default is ``False``, indicating open boundary conditions for all. Raises: - TypeError: - if ``coupling`` doesn't have correct dimensions. + ValueError: if ``coupling`` doesn't have correct dimensions. Returns: ~ops.op_math.Sum: Hamiltonian for the Kitaev model. From a8288e63e6fafb776e9cfe7239c26f3b1ff5a97c Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:14:34 -0400 Subject: [PATCH 29/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Austin Huang <65315367+austingmhuang@users.noreply.github.com> --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index eeb22c257f6..22c1f2682d4 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -333,7 +333,7 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): n_cells (list[int]): Number of cells in each direction of the grid. coupling (Optional[list[float] | tensor_like(float)]): Coupling between spins, it is a list of length 3. Default value is [1.0, 1.0, 1.0]. - boundary_condition (Optional[bool | list[bool]]): Defines boundary conditions for different lattice axes. + boundary_condition (bool | list[bool]): Defines boundary conditions for different lattice axes. The default is ``False``, indicating open boundary conditions for all. Raises: From d80c4942bb234133513025531589a1b938610f79 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:17:24 -0400 Subject: [PATCH 30/83] Update pennylane/spin/lattice.py Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 8c8c0573146..38e965b8bc9 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -42,7 +42,7 @@ class Lattice: custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. Default value is None, which adds the edges based on neighbour_order. Each element in the list is for a separate edge, and can contain 1 or 2 tuples. - First tuple contains the index of the starting and ending vertex of the edge. + First tuple contains the indices of the starting and ending vertices of the edge. Second tuple is optional and contains the operator on that edge and coefficient of that operator. distance_tol (float): Distance below which spatial points are considered equal for the From 5b940d42f0da443801b08d023334064b62c3cf70 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:17:32 -0400 Subject: [PATCH 31/83] Update pennylane/spin/lattice.py Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 38e965b8bc9..46709633ce9 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -40,7 +40,7 @@ class Lattice: neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1 (nearest neighbour). custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. - Default value is None, which adds the edges based on neighbour_order. + Default value is ``None``, which adds the edges based on ``neighbour_order``. Each element in the list is for a separate edge, and can contain 1 or 2 tuples. First tuple contains the indices of the starting and ending vertices of the edge. Second tuple is optional and contains the operator on that edge and coefficient From acd399de9fd1b552dbcf841b25a3c1a34062df01 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:17:40 -0400 Subject: [PATCH 32/83] Update pennylane/spin/lattice.py Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 46709633ce9..2c3e56e298e 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -128,7 +128,7 @@ def __init__( else: if neighbour_order > 1: raise ValueError( - "custom_edges and neighbour_order cannot be specified at the same time" + "custom_edges and neighbour_order cannot be specified at the same time." ) self.edges = self._get_custom_edges(custom_edges, lattice_map) From 9b4579e0734bafb696fbba40f0f67308476628d5 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:17:51 -0400 Subject: [PATCH 33/83] Update pennylane/spin/lattice.py Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/spin/lattice.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 2c3e56e298e..4f43007bc2f 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -120,9 +120,7 @@ def __init__( self.n_sites = math.prod(n_cells) * n_sl self.lattice_points, lattice_map = self._generate_grid(neighbour_order) if custom_edges is None: - cutoff = ( - neighbour_order * math.max(math.linalg.norm(self.vectors, axis=1)) + distance_tol - ) + cutoff = neighbour_order * math.max(math.linalg.norm(self.vectors, axis=1)) + distance_tol edges = self._identify_neighbours(cutoff) self.edges = Lattice._generate_true_edges(edges, lattice_map, neighbour_order) else: From c4db4ebbb5cbac6d5e2363f63751efc7ca640f40 Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Mon, 16 Sep 2024 12:57:06 -0400 Subject: [PATCH 34/83] Addressed comments --- doc/releases/changelog-dev.md | 2 +- pennylane/spin/lattice.py | 47 +++++++++++++++++++++++++----- pennylane/spin/spin_hamiltonian.py | 21 +++++++------ tests/spin/test_lattice.py | 3 +- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 1828f5dd198..1a0afe3a917 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -5,7 +5,7 @@

New features since last release

* Function is added for generating the spin Hamiltonian for the - [Kitaev](https://arxiv.org/pdf/2406.06625) model on a lattice. + [Kitaev](https://arxiv.org/abs/cond-mat/0506438) model on a lattice. [(#6174)](https://github.com/PennyLaneAI/pennylane/pull/6174)

Improvements 🛠

diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 4f43007bc2f..375e484ee50 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -38,13 +38,13 @@ class Lattice: boundary_condition (bool | list[bool]): Defines boundary conditions for 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 (nearest neighbour). + Default is 1 (nearest neighbour). This cannot be greater than 1 if custom_edges is defined. custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. Default value is ``None``, which adds the edges based on ``neighbour_order``. Each element in the list is for a separate edge, and can contain 1 or 2 tuples. First tuple contains the indices of the starting and ending vertices of the edge. Second tuple is optional and contains the operator on that edge and coefficient - of that operator. + of that operator. Default value is the index of edge in custom_edges list. distance_tol (float): Distance below which spatial points are considered equal for the purpose of identifying nearest neighbours. Default value is 1e-5. @@ -120,13 +120,15 @@ def __init__( self.n_sites = math.prod(n_cells) * n_sl self.lattice_points, lattice_map = self._generate_grid(neighbour_order) if custom_edges is None: - cutoff = neighbour_order * math.max(math.linalg.norm(self.vectors, axis=1)) + distance_tol + cutoff = ( + neighbour_order * math.max(math.linalg.norm(self.vectors, axis=1)) + distance_tol + ) edges = self._identify_neighbours(cutoff) self.edges = Lattice._generate_true_edges(edges, lattice_map, neighbour_order) else: if neighbour_order > 1: raise ValueError( - "custom_edges and neighbour_order cannot be specified at the same time." + "custom_edges cannot be specified if neighbour_order argument is set to greater than 1." ) self.edges = self._get_custom_edges(custom_edges, lattice_map) @@ -218,6 +220,9 @@ def _get_custom_edges(self, custom_edges, lattice_map): of that operator. lattice_map (list[int]): A list to represent the node number for each lattice_point. + Returns: + List of edges. + **Example** Generates a square lattice with a single diagonal and assigns a different operation @@ -228,15 +233,36 @@ def _get_custom_edges(self, custom_edges, lattice_map): [(0, 1), ("XX", 0.1)], [(0, 3), ("YY", 0.2)], [(0, 4), ("XY", 0.3)], - ] - >>> lattice = Lattice(n_cells=n_cells, vectors=vectors, custom_edges=custom_edges) + ] + >>> lattice = qml.spin.Lattice(n_cells=n_cells, vectors=vectors, custom_edges=custom_edges) + >>> lattice.edges + [(0, 1, ('XX', 0.1)), + (1, 2, ('XX', 0.1)), + (3, 4, ('XX', 0.1)), + (4, 5, ('XX', 0.1)), + (6, 7, ('XX', 0.1)), + (7, 8, ('XX', 0.1)), + (0, 3, ('YY', 0.2)), + (1, 4, ('YY', 0.2)), + (2, 5, ('YY', 0.2)), + (3, 6, ('YY', 0.2)), + (4, 7, ('YY', 0.2)), + (5, 8, ('YY', 0.2)), + (0, 4, ('XY', 0.3)), + (1, 5, ('XY', 0.3)), + (3, 7, ('XY', 0.3)), + (4, 8, ('XY', 0.3)) + ] """ if not all([len(edge) in (1, 2) for edge in custom_edges]): raise TypeError( """ - The elements of custom_edges should be lists of length 1 or 2. Inside said lists should be a tuple that contains two lattice indices to represent the edge and, optionally, a tuple that represents the operation and coefficient for that edge. + The elements of custom_edges should be lists of length 1 or 2. + Inside said lists should be a tuple that contains two lattice + indices to represent the edge and, optionally, a tuple that represents + the operation and coefficient for that edge. Every tuple must contain two lattice indices to represent the edge and can optionally include a list to represent the operation and coefficient for that edge. """ @@ -255,14 +281,20 @@ def _get_custom_edges(self, custom_edges, lattice_map): ) edge_operation = custom_edge[1] if len(custom_edge) == 2 else i + + # Finds the coordinates of lattice vertices to be connected map_edge1 = lattice_map.index(edge[0]) map_edge2 = lattice_map.index(edge[1]) edge_distance = self.lattice_points[map_edge2] - self.lattice_points[map_edge1] + + # Calculates the number of unit cells that a given edge spans in each direction v1, v2 = math.mod(edge, n_sl) translation_vector = ( edge_distance + self.positions[v1] - self.positions[v2] ) @ math.linalg.inv(self.vectors) translation_vector = math.asarray(math.rint(translation_vector), dtype=int) + + # Finds the minimum and maximum range for a given edge based on boundary_conditions edge_ranges = [] for idx, cell in enumerate(self.n_cells): t_point = 0 if self.boundary_condition[idx] else translation_vector[idx] @@ -270,6 +302,7 @@ def _get_custom_edges(self, custom_edges, lattice_map): range(math.maximum(0, -t_point), cell - math.maximum(0, t_point)) ) + # Finds the indices for starting and ending vertices of the edge for cell in itertools.product(*edge_ranges): node1_idx = math.dot(math.mod(cell, self.n_cells), nsites_axis) + v1 node2_idx = ( diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 22c1f2682d4..15f6b59671c 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -312,10 +312,9 @@ def fermi_hubbard( def kitaev(n_cells, coupling=None, boundary_condition=False): - r"""Generates the Hamiltonian for the `Kitaev model `_ - on the Honeycomb lattice. + r"""Generates the Hamiltonian for the Kitaev model on the Honeycomb lattice. - The Hamiltonian is represented as: + The `Kitaev `_ model Hamiltonian is represented as: .. math:: \begin{align*} @@ -349,14 +348,14 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): >>> 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)) + 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)) ) """ diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index b53c3aeea62..61838a1be08 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -550,7 +550,8 @@ def test_neighbour_order_error(): n_cells = [3, 3] custom_edges = [[(0, 1)], [(0, 5)], [(0, 4)]] with pytest.raises( - ValueError, match="custom_edges and neighbour_order cannot be specified at the same time" + ValueError, + match="custom_edges cannot be specified if neighbour_order argument is set to greater than 1.", ): Lattice(n_cells=n_cells, vectors=vectors, neighbour_order=2, custom_edges=custom_edges) From f600efb047204e7ee3013a06a464a90ab59453d2 Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Thu, 19 Sep 2024 01:53:12 -0400 Subject: [PATCH 35/83] minor docstring fix --- pennylane/spin/spin_hamiltonian.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 15f6b59671c..b2466ece2b9 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -319,8 +319,8 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): .. 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 + \:\: 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:`K_x`, :math:`K_y`, :math:`K_z` are the coupling constants defined for the Hamiltonian, From 775ba90a4388a8a454f188a6bee9a4ee450b6c3e Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Fri, 20 Sep 2024 07:27:36 -0400 Subject: [PATCH 36/83] Addressed comments --- pennylane/spin/lattice.py | 3 ++- pennylane/spin/spin_hamiltonian.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 375e484ee50..1c7b11d4c88 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -282,7 +282,8 @@ def _get_custom_edges(self, custom_edges, lattice_map): edge_operation = custom_edge[1] if len(custom_edge) == 2 else i - # Finds the coordinates of lattice vertices to be connected + # Finds the coordinates of starting and ending vertices of the edge + # and the vector distance between the coordinates map_edge1 = lattice_map.index(edge[0]) map_edge2 = lattice_map.index(edge[1]) edge_distance = self.lattice_points[map_edge2] - self.lattice_points[map_edge1] diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index b2466ece2b9..b35003a28fe 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -330,9 +330,9 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): Args: n_cells (list[int]): Number of cells in each direction of the grid. - coupling (Optional[list[float] | tensor_like(float)]): Coupling between spins, it is a list of length 3. + coupling (Optional[list[float] or tensor_like(float)]): Coupling between spins, it is a list of length 3. Default value is [1.0, 1.0, 1.0]. - boundary_condition (bool | list[bool]): Defines boundary conditions for different lattice axes. + boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes. The default is ``False``, indicating open boundary conditions for all. Raises: From 81c9766e7bd33f725942502409dd80314b746e3f Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Fri, 20 Sep 2024 08:37:50 -0400 Subject: [PATCH 37/83] used dictionary for lattice mapping --- pennylane/spin/lattice.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 1c7b11d4c88..14a6137f55d 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -130,6 +130,7 @@ def __init__( raise ValueError( "custom_edges cannot be specified if neighbour_order argument is set to greater than 1." ) + lattice_map = dict(zip(lattice_map, self.lattice_points)) self.edges = self._get_custom_edges(custom_edges, lattice_map) self.edges_indices = [(v1, v2) for (v1, v2, color) in self.edges] @@ -284,9 +285,9 @@ def _get_custom_edges(self, custom_edges, lattice_map): # Finds the coordinates of starting and ending vertices of the edge # and the vector distance between the coordinates - map_edge1 = lattice_map.index(edge[0]) - map_edge2 = lattice_map.index(edge[1]) - edge_distance = self.lattice_points[map_edge2] - self.lattice_points[map_edge1] + vertex1 = lattice_map[edge[0]] + vertex2 = lattice_map[edge[1]] + edge_distance = vertex2 - vertex1 # Calculates the number of unit cells that a given edge spans in each direction v1, v2 = math.mod(edge, n_sl) From f12a17349366d98b4e1ddd5df0cf2a366582c1bf Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:40:16 -0400 Subject: [PATCH 38/83] Update doc/releases/changelog-dev.md --- doc/releases/changelog-dev.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 6db86325d6f..db055529904 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -12,7 +12,6 @@ Von Neumann entanglement entropy of a quantum state. [(#5911)](https://github.com/PennyLaneAI/pennylane/pull/5911) -

Improvements 🛠

* PennyLane is now compatible with NumPy 2.0. From 3eadc14c8524dc2735a454600d2986e187581cfd Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:41:17 -0400 Subject: [PATCH 39/83] Update pennylane/spin/lattice.py --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 14a6137f55d..79612e4b9f3 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -35,7 +35,7 @@ class Lattice: positions (list[list[float]]): Initial positions of spin cites. Default value is ``[[0.0]`` :math:`\times` ``number of dimensions]``. - boundary_condition (bool | list[bool]): Defines boundary conditions for different lattice axes, + boundary_condition (bool or list[bool]): Defines boundary conditions for 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 (nearest neighbour). This cannot be greater than 1 if custom_edges is defined. From 5e0f4d7da0e178ece14d6a03c00130e25631615a Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 09:23:28 -0400 Subject: [PATCH 40/83] address comments --- pennylane/spin/spin_hamiltonian.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index ceb406acc27..5af48a8c45f 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -389,8 +389,8 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): return hamiltonian.simplify() -def custom_hamiltonian_from_lattice(lattice): - r"""Generates the Hamiltonian for a custom lattice. +def spin_hamiltonian(lattice): + r"""Generates a spin Hamiltonian for a custom lattice. Args: lattice (Lattice): custom lattice defined with custom_edges @@ -432,7 +432,7 @@ def custom_hamiltonian_from_lattice(lattice): ) opmap = {"X": X, "Y": Y, "Z": Z} - hamiltonian = 0 + hamiltonian = 0.0 * qml.I(0) for edge in lattice.edges: v1, v2 = edge[0:2] op1, op2 = edge[2][0] From b4a0f9ca458c96d8f6a2783460dd951559f83b5b Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 12:41:28 -0400 Subject: [PATCH 41/83] docstring update --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 5af48a8c45f..4f1b28f5a5e 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -413,7 +413,7 @@ def spin_hamiltonian(lattice): custom_edges=[[(0, 1), ("XX", 0.5)], [(1, 2), ("YY", 0.6)], [(1, 4), ("ZZ", 0.7)]], ) - >>> custom_hamiltonian_from_lattice(lattice=lattice) + >>> spin_hamiltonian(lattice=lattice) >>> ( 0.5 * (X(0) @ X(1)) + 0.5 * (X(2) @ X(3)) From 0d49388c30a5e9c8e53685a95819678f4469d15d Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 12:54:27 -0400 Subject: [PATCH 42/83] temp custom nodes --- pennylane/spin/spin_hamiltonian.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 4f1b28f5a5e..29f08f96bb4 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -389,7 +389,7 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): return hamiltonian.simplify() -def spin_hamiltonian(lattice): +def spin_hamiltonian(lattice, custom_nodes=None): r"""Generates a spin Hamiltonian for a custom lattice. Args: @@ -440,4 +440,10 @@ def spin_hamiltonian(lattice): hamiltonian += coeff * (opmap[op1](v1) @ opmap[op2](v2)) + for node in custom_nodes: + n = node[0] + op = node[1] + coeff = node[2] + hamiltonian += coeff * opmap[op](n) + return hamiltonian.simplify() From 3cfbe56af8c2b1425fff63f590fcef1e1243ed05 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 12:58:16 -0400 Subject: [PATCH 43/83] fixes --- pennylane/spin/__init__.py | 2 +- tests/spin/test_spin_hamiltonian.py | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/pennylane/spin/__init__.py b/pennylane/spin/__init__.py index b73de4100ef..a3bf071889e 100644 --- a/pennylane/spin/__init__.py +++ b/pennylane/spin/__init__.py @@ -20,6 +20,6 @@ fermi_hubbard, heisenberg, kitaev, + spin_hamiltonian, transverse_ising, - custom_hamiltonian_from_lattice, ) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 13b316668ff..3b74d4dc10b 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -23,11 +23,11 @@ from pennylane import I, X, Y, Z from pennylane.spin import ( Lattice, - custom_hamiltonian_from_lattice, fermi_hubbard, heisenberg, kitaev, transverse_ising, + spin_hamiltonian ) pytestmark = pytest.mark.usefixtures("new_opmath_only") @@ -907,10 +907,32 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): ), ), ], + ("lattice", "expected_ham"), + [ + ( + 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)]], + ), + ( + 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)) + ), + ), + ], ) def test_custom_hamiltonian(lattice, expected_ham): r"""Test that the correct Hamiltonian is generated""" - custom_ham = custom_hamiltonian_from_lattice(lattice=lattice) + custom_ham = spin_hamiltonian(lattice=lattice) qml.assert_equal(custom_ham, expected_ham) From 804498e62a68ce5ba8644487848a32eccdf7e1d1 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 12:59:18 -0400 Subject: [PATCH 44/83] fixes --- tests/spin/test_spin_hamiltonian.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 3b74d4dc10b..6f956239f2e 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -932,9 +932,9 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): ) def test_custom_hamiltonian(lattice, expected_ham): r"""Test that the correct Hamiltonian is generated""" - custom_ham = spin_hamiltonian(lattice=lattice) + spin_ham = spin_hamiltonian(lattice=lattice) - qml.assert_equal(custom_ham, expected_ham) + qml.assert_equal(spin_ham, expected_ham) def test_custom_hamiltonian_error(): @@ -943,4 +943,4 @@ def test_custom_hamiltonian_error(): with pytest.raises( ValueError, match="Custom edges need to be defined and should have an operator" ): - custom_hamiltonian_from_lattice(lattice=lattice) + spin_hamiltonian(lattice=lattice) From b45dd17670871eb917125c33a310d4c74464cd4a Mon Sep 17 00:00:00 2001 From: ddhawan11 Date: Fri, 20 Sep 2024 13:28:42 -0400 Subject: [PATCH 45/83] resolved conflicts --- doc/releases/changelog-dev.md | 7 +- pennylane/spin/__init__.py | 2 +- pennylane/spin/lattice.py | 33 +- pennylane/spin/spin_hamiltonian.py | 301 +++++++++++++++- tests/spin/test_spin_hamiltonian.py | 521 +++++++++++++++++++++++++++- 5 files changed, 844 insertions(+), 20 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index db055529904..0abe65716b0 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -8,6 +8,11 @@ [Kitaev](https://arxiv.org/abs/cond-mat/0506438) model on a lattice. [(#6174)](https://github.com/PennyLaneAI/pennylane/pull/6174) +* Functions are added for generating spin Hamiltonians for [Emery] + (https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.58.2794) and + [Haldane](https://journals.aps.org/prl/pdf/10.1103/PhysRevLett.61.2015) models on a lattice. + [(#6201)](https://github.com/PennyLaneAI/pennylane/pull/6201/) + * A new `qml.vn_entanglement_entropy` measurement process has been added which measures the Von Neumann entanglement entropy of a quantum state. [(#5911)](https://github.com/PennyLaneAI/pennylane/pull/5911) @@ -184,9 +189,9 @@ This release contains contributions from (in alphabetical order): Guillermo Alonso, Utkarsh Azad, -Diksha Dhawan, Astral Cai, Isaac De Vlugt, +Diksha Dhawan, Lillian M. A. Frederiksen, Pietropaolo Frisoni, Emiliano Godinez, diff --git a/pennylane/spin/__init__.py b/pennylane/spin/__init__.py index 0040dcf958b..0b4b32fa492 100644 --- a/pennylane/spin/__init__.py +++ b/pennylane/spin/__init__.py @@ -16,4 +16,4 @@ """ from .lattice import Lattice -from .spin_hamiltonian import fermi_hubbard, heisenberg, kitaev, transverse_ising +from .spin_hamiltonian import emery, fermi_hubbard, haldane, heisenberg, kitaev, transverse_ising diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 79612e4b9f3..964fb9f08cc 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -257,17 +257,23 @@ def _get_custom_edges(self, custom_edges, lattice_map): """ - if not all([len(edge) in (1, 2) for edge in custom_edges]): - raise TypeError( - """ - The elements of custom_edges should be lists of length 1 or 2. - Inside said lists should be a tuple that contains two lattice - indices to represent the edge and, optionally, a tuple that represents - the operation and coefficient for that edge. - Every tuple must contain two lattice indices to represent the edge - and can optionally include a list to represent the operation and coefficient for that edge. - """ - ) + for edge in custom_edges: + if len(edge) not in (1, 2): + raise TypeError( + """ + The elements of custom_edges should be lists of length 1 or 2. + Inside said lists should be a tuple that contains two lattice + indices to represent the edge and, optionally, a tuple that represents + the operation and coefficient for that edge. + Every tuple must contain two lattice indices to represent the edge + and can optionally include a list to represent the operation and coefficient for that edge. + """ + ) + + if edge[0][0] >= self.n_sites or edge[0][1] >= self.n_sites: + raise ValueError( + f"The edge {edge[0]} has vertices greater than n_sites, {self.n_sites}" + ) edges = [] n_sl = len(self.positions) @@ -276,11 +282,6 @@ def _get_custom_edges(self, custom_edges, lattice_map): for i, custom_edge in enumerate(custom_edges): edge = custom_edge[0] - if edge[0] >= self.n_sites or edge[1] >= self.n_sites: - raise ValueError( - f"The edge {edge} has vertices greater than n_sites, {self.n_sites}" - ) - edge_operation = custom_edge[1] if len(custom_edge) == 2 else i # Finds the coordinates of starting and ending vertices of the edge diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index b35003a28fe..fd518cb7101 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -21,7 +21,7 @@ from .lattice import Lattice, _generate_lattice -# pylint: disable=too-many-arguments +# pylint: disable=too-many-arguments, too-many-branches def transverse_ising( @@ -311,6 +311,305 @@ def fermi_hubbard( return qubit_ham.simplify() +def emery( + 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 `_ for the `Emery model `_ is represented as: + + .. math:: + \begin{align*} + \hat{H} & = -\sum_{\langle i,j \rangle, \sigma} t_{ij}(c_{i\sigma}^{\dagger}c_{j\sigma}) + + \sum_{i}U_{i}n_{i \uparrow} n_{i\downarrow} + \sum_{}V_{ij}(n_{i \uparrow} + + n_{i \downarrow})(n_{j \uparrow} + n_{j \downarrow})\ , + \end{align*} + + where :math:`t_{ij}` is the hopping term representing the kinetic energy of electrons, + :math:`U_{i}` is the on-site Coulomb interaction, representing the repulsion between electrons, + :math:`V_{ij}` is the intersite coupling, :math:`i, j` are the indices for neighbouring spins, + :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 there are two fermions with opposite spins on each lattice site. + + Args: + lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, + ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. + n_cells (list[int]): Number of cells in each direction of the grid. + hopping (float or list[float] or tensor_like[float]): Hopping strength between + neighbouring sites. It can be a number, a list 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 list[float]): Coulomb interaction between spins. It can be a constant or a + list of length equal to number of spins. + intersite_coupling (float or list[float] or tensor_like[float]): Interaction strength between spins on + neighbouring sites. It can be a number, a list 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]): Defines boundary conditions for 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]) + if isinstance(hopping, (int, float, complex)) + else math.asarray(hopping) + ) + intersite_coupling = ( + math.asarray([intersite_coupling]) + if isinstance(intersite_coupling, (int, float, complex)) + else math.asarray(intersite_coupling) + ) + + if hopping.shape not in [(neighbour_order,), (lattice.n_sites, lattice.n_sites)]: + raise ValueError( + f"The hopping parameter should be a number or an " + f"array of shape ({neighbour_order},) or ({lattice.n_sites},{lattice.n_sites})." + ) + + if intersite_coupling.shape not in [(neighbour_order,), (lattice.n_sites, lattice.n_sites)]: + raise ValueError( + 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 = 2 + hopping_term = 0.0 * FermiWord({}) + intersite_term = 0.0 * FermiWord({}) + for i, j, order in lattice.edges: + hop = hopping[order] if hopping.shape == (neighbour_order,) else hopping[i][j] + + for s in range(spin): + s1 = i * spin + s + s2 = j * spin + s + hopping_term -= hop * ( + FermiWord({(0, s1): "+", (1, s2): "-"}) + FermiWord({(0, s2): "+", (1, s1): "-"}) + ) + + intersite = ( + intersite_coupling[order] + if intersite_coupling.shape == (neighbour_order,) + else intersite_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): "-"}) + ) + ) + + if isinstance(coulomb, (int, float, complex)): + coulomb = math.ones(lattice.n_sites) * coulomb + + coulomb_term = 0.0 * FermiWord({}) + for i in range(lattice.n_sites): + up_spin = i * spin + down_spin = i * spin + 1 + coulomb_term += coulomb[i] * FermiWord( + {(0, up_spin): "+", (1, up_spin): "-", (2, down_spin): "+", (3, down_spin): "-"} + ) + + hamiltonian = hopping_term + coulomb_term + intersite_term + + if mapping not in ["jordan_wigner", "parity", "bravyi_kitaev"]: + raise ValueError( + 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) + + return qubit_ham.simplify() + + +def haldane( + 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 `_ for the `Haldane model `_ is represented as: + + .. math:: + + \begin{align*} + \hat{H} & = -\sum_{\langle i,j \rangle}t_{ij}^{1} + (c_{i\sigma}^\dagger c_{j\sigma} + c_{j\sigma}^\dagger c_{i\sigma}) + - \sum_{\langle\langle i,j \rangle\rangle, \sigma} t_{ij}^{2} + \left( e^{i\phi_{ij}} c_{i\sigma}^\dagger c_{j\sigma} + e^{-i\phi_{ij}} c_{j\sigma}^\dagger c_{i\sigma} \right) + \end{align*} + + where :math:`t^{1}_{ij}` is the hopping term representing the hopping amplitude between neighbouring + sites :math:`\langle i,j \rangle`, :math:`t^{2}_{ij}` is the hopping amplitude between next-nearest neighbours :math:`\langle \langle i,j \rangle \rangle`, :math:`\phi_{ij}` is the phase + factor that breaks time-reversal symmetry in the system, and :math:`\sigma` is the spin degree of freedom. + This function assumes there are two fermions with opposite spins on each lattice site. + + Args: + lattice (str): Shape of the lattice. Input values can be ``'chain'``, ``'square'``, + ``'rectangle'``, ``'honeycomb'``, ``'triangle'``, or ``'kagome'``. + 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]): Defines boundary conditions for 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]) + if isinstance(hopping, (int, float, complex)) + else math.asarray(hopping) + ) + hopping_next = ( + math.asarray([hopping_next]) + if isinstance(hopping_next, (int, float, complex)) + else math.asarray(hopping_next) + ) + phi = math.asarray([phi]) if isinstance(phi, (int, float, complex)) else math.asarray(phi) + + if hopping.shape not in [(1,), (lattice.n_sites, lattice.n_sites)]: + raise ValueError( + f"The hopping parameter should be a constant or an array of shape ({lattice.n_sites},{lattice.n_sites})." + ) + + if hopping_next.shape not in [(1,), (lattice.n_sites, lattice.n_sites)]: + raise ValueError( + f"The hopping_next parameter should be a constant or an array of shape ({lattice.n_sites},{lattice.n_sites})." + ) + + if phi.shape not in [(1,), (lattice.n_sites, lattice.n_sites)]: + raise ValueError( + f"The phi parameter should be a constant or an array of shape ({lattice.n_sites},{lattice.n_sites})." + ) + + spin = 2 + hamiltonian = 0.0 * FermiWord({}) + for i, j, order in lattice.edges: + + hop1 = hopping[0] if hopping.shape == (1,) else hopping[i][j] + hop2 = hopping_next[0] if hopping_next.shape == (1,) else hopping_next[i][j] + phi_term = phi[0] if phi.shape == (1,) else phi[i][j] + + for s in range(spin): + s1 = i * spin + s + s2 = j * spin + s + if order == 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): "-"}) + ) + + if mapping not in ["jordan_wigner", "parity", "bravyi_kitaev"]: + raise ValueError( + 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) + + return qubit_ham.simplify() + + def kitaev(n_cells, coupling=None, boundary_condition=False): r"""Generates the Hamiltonian for the Kitaev model on the Honeycomb lattice. diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 959e05acc6a..2bfe98a9332 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -21,8 +21,9 @@ import pennylane as qml from pennylane import I, X, Y, Z -from pennylane.spin import fermi_hubbard, heisenberg, kitaev, transverse_ising +from pennylane.spin import emery, fermi_hubbard, haldane, heisenberg, kitaev, transverse_ising +# pylint: disable=too-many-arguments pytestmark = pytest.mark.usefixtures("new_opmath_only") @@ -784,6 +785,524 @@ def test_fermi_hubbard_hamiltonian_matrix(shape, n_cells, t, coulomb, expected_h qml.assert_equal(fermi_hub_ham, expected_ham) +def test_interaction_parameter_error_emery(): + r"""Test that an error is raised when the provided interaction parameters are of wrong shape for + emery Hamiltonian.""" + n_cells = [4, 4] + lattice = "Square" + with pytest.raises( + ValueError, + match=re.escape( + "The hopping parameter should be a number or an array of shape (1,) or (16,16)" + ), + ): + emery(lattice=lattice, n_cells=n_cells, hopping=[1.0, 2.0], neighbour_order=1) + + with pytest.raises( + ValueError, + match=re.escape( + "The intersite_coupling parameter should be a number or an array of shape (1,) or (16,16)" + ), + ): + emery( + lattice=lattice, + n_cells=n_cells, + hopping=[1.0], + intersite_coupling=[1.0, 2.0], + neighbour_order=1, + ) + + +def test_mapping_error_emery(): + r"""Test that an error is raised when unsupported mapping is provided""" + n_cells = [4, 4] + lattice = "Square" + with pytest.raises(ValueError, match="The 'bk_sf' transformation is not available."): + emery(lattice=lattice, n_cells=n_cells, mapping="bk_sf") + + +@pytest.mark.parametrize( + # expected_ham here was obtained manually + ("shape", "n_cells", "t", "u", "v", "boundary_condition", "expected_ham"), + [ + ( + "square", + [2, 2], + -1.23, + 2.34, + 1.42, + False, + (0.615 + 0j) * (Y(0) @ Z(1) @ Y(2)) + + (0.615 + 0j) * (X(0) @ Z(1) @ X(2)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Y(3)) + + (0.615 + 0j) * (X(1) @ Z(2) @ X(3)) + + (0.615 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + (0.615 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + (0.615 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + (0.615 + 0j) * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (0.615 + 0j) * (Y(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(5) @ Z(6) @ X(7)) + + (8.020000000000005 + 0j) * I(0) + + (-2.005 + 0j) * Z(1) + + (-2.005 + 0j) * Z(0) + + (0.585 + 0j) * (Z(0) @ Z(1)) + + (-2.005 + 0j) * Z(3) + + (-2.005 + 0j) * Z(2) + + (0.585 + 0j) * (Z(2) @ Z(3)) + + (-2.005 + 0j) * Z(5) + + (-2.005 + 0j) * Z(4) + + (0.585 + 0j) * (Z(4) @ Z(5)) + + (-2.005 + 0j) * Z(7) + + (-2.005 + 0j) * Z(6) + + (0.585 + 0j) * (Z(6) @ Z(7)) + + (0.355 + 0j) * (Z(0) @ Z(2)) + + (0.355 + 0j) * (Z(0) @ Z(3)) + + (0.355 + 0j) * (Z(1) @ Z(2)) + + (0.355 + 0j) * (Z(1) @ Z(3)) + + (0.355 + 0j) * (Z(0) @ Z(4)) + + (0.355 + 0j) * (Z(0) @ Z(5)) + + (0.355 + 0j) * (Z(1) @ Z(4)) + + (0.355 + 0j) * (Z(1) @ Z(5)) + + (0.355 + 0j) * (Z(2) @ Z(6)) + + (0.355 + 0j) * (Z(2) @ Z(7)) + + (0.355 + 0j) * (Z(3) @ Z(6)) + + (0.355 + 0j) * (Z(3) @ Z(7)) + + (0.355 + 0j) * (Z(4) @ Z(6)) + + (0.355 + 0j) * (Z(4) @ Z(7)) + + (0.355 + 0j) * (Z(5) @ Z(6)) + + (0.355 + 0j) * (Z(5) @ Z(7)), + ), + ( + "Chain", + [4], + -1.23, + 2.34, + 1.42, + True, + (0.615 + 0j) * (Y(0) @ Z(1) @ Y(2)) + + (0.615 + 0j) * (X(0) @ Z(1) @ X(2)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Y(3)) + + (0.615 + 0j) * (X(1) @ Z(2) @ X(3)) + + (0.615 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (0.615 + 0j) * (Y(2) @ Z(3) @ Y(4)) + + (0.615 + 0j) * (X(2) @ Z(3) @ X(4)) + + (0.615 + 0j) * (Y(3) @ Z(4) @ Y(5)) + + (0.615 + 0j) * (X(3) @ Z(4) @ X(5)) + + (0.615 + 0j) * (Y(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(5) @ Z(6) @ X(7)) + + (8.020000000000005 + 0j) * I(0) + + (-2.005 + 0j) * Z(1) + + (-2.005 + 0j) * Z(0) + + (0.585 + 0j) * (Z(0) @ Z(1)) + + (-2.005 + 0j) * Z(3) + + (-2.005 + 0j) * Z(2) + + (0.585 + 0j) * (Z(2) @ Z(3)) + + (-2.005 + 0j) * Z(5) + + (-2.005 + 0j) * Z(4) + + (0.585 + 0j) * (Z(4) @ Z(5)) + + (-2.005 + 0j) * Z(7) + + (-2.005 + 0j) * Z(6) + + (0.585 + 0j) * (Z(6) @ Z(7)) + + (0.355 + 0j) * (Z(0) @ Z(2)) + + (0.355 + 0j) * (Z(0) @ Z(3)) + + (0.355 + 0j) * (Z(1) @ Z(2)) + + (0.355 + 0j) * (Z(1) @ Z(3)) + + (0.355 + 0j) * (Z(0) @ Z(6)) + + (0.355 + 0j) * (Z(0) @ Z(7)) + + (0.355 + 0j) * (Z(1) @ Z(6)) + + (0.355 + 0j) * (Z(1) @ Z(7)) + + (0.355 + 0j) * (Z(2) @ Z(4)) + + (0.355 + 0j) * (Z(2) @ Z(5)) + + (0.355 + 0j) * (Z(3) @ Z(4)) + + (0.355 + 0j) * (Z(3) @ Z(5)) + + (0.355 + 0j) * (Z(4) @ Z(6)) + + (0.355 + 0j) * (Z(4) @ Z(7)) + + (0.355 + 0j) * (Z(5) @ Z(6)) + + (0.355 + 0j) * (Z(5) @ Z(7)), + ), + ], +) +def test_emery_hamiltonian(shape, n_cells, t, u, v, boundary_condition, expected_ham): + r"""Test that the correct Emery Hamiltonian is generated.""" + + emery_ham = emery( + lattice=shape, + n_cells=n_cells, + hopping=t, + coulomb=u, + intersite_coupling=v, + boundary_condition=boundary_condition, + ) + + qml.assert_equal(emery_ham, expected_ham) + + +@pytest.mark.parametrize( + # expected_ham here was obtained manually. + ("shape", "n_cells", "t", "u", "v", "expected_ham"), + [ + ( + "chain", + [4], + [[0, 1, 0, 0], [1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0]], + 0.1, + 0, + -0.5 * (Y(0) @ Z(1) @ Y(2)) + + -0.5 * (X(0) @ Z(1) @ X(2)) + + 0.1 * I(0) + + -0.5 * (Y(1) @ Z(2) @ Y(3)) + + -0.5 * (X(1) @ Z(2) @ X(3)) + + -0.5 * (Y(2) @ Z(3) @ Y(4)) + + -0.5 * (X(2) @ Z(3) @ X(4)) + + -0.5 * (Y(3) @ Z(4) @ Y(5)) + + -0.5 * (X(3) @ Z(4) @ X(5)) + + -0.5 * (Y(4) @ Z(5) @ Y(6)) + + -0.5 * (X(4) @ Z(5) @ X(6)) + + -0.5 * (Y(5) @ Z(6) @ Y(7)) + + -0.5 * (X(5) @ Z(6) @ X(7)) + + -0.025 * Z(1) + + -0.025 * Z(0) + + 0.025 * (Z(0) @ Z(1)) + + -0.025 * Z(3) + + -0.025 * Z(2) + + 0.025 * (Z(2) @ Z(3)) + + -0.025 * Z(5) + + -0.025 * Z(4) + + 0.025 * (Z(4) @ Z(5)) + + -0.025 * Z(7) + + -0.025 * Z(6) + + 0.025 * (Z(6) @ Z(7)), + ), + ( + "square", + [2, 2], + [[0, 0.5, 0.5, 0], [0.5, 0, 0, 0.5], [0.5, 0, 0, 0.5], [0, 0.5, 0.5, 0]], + [-1.0, 0.0, 1.0, 0], + 0, + -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)) + + -0.25 * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + -0.25 * (X(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + -0.25 * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + -0.25 * (X(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + -0.25 * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + -0.25 * (X(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + -0.25 * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + -0.25 * (X(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + -0.25 * (Y(4) @ Z(5) @ Y(6)) + + -0.25 * (X(4) @ Z(5) @ X(6)) + + -0.25 * (Y(5) @ Z(6) @ Y(7)) + + -0.25 * (X(5) @ Z(6) @ X(7)) + + 0.25 * Z(1) + + 0.25 * Z(0) + + -0.25 * (Z(0) @ Z(1)) + + -0.25 * Z(5) + + -0.25 * Z(4) + + 0.25 * (Z(4) @ Z(5)), + ), + ( + "square", + [2, 2], + 0.1, + [-1.0, 0.0, 1.0, 0], + [[0, 0.5, 0.5, 0], [0.5, 0, 0, 0.5], [0.5, 0, 0, 0.5], [0, 0.5, 0.5, 0]], + -0.05 * (Y(0) @ Z(1) @ Y(2)) + + -0.05 * (X(0) @ Z(1) @ X(2)) + + -0.05 * (Y(1) @ Z(2) @ Y(3)) + + -0.05 * (X(1) @ Z(2) @ X(3)) + + -0.05 * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + -0.05 * (X(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + -0.05 * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + -0.05 * (X(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + -0.05 * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + -0.05 * (X(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + -0.05 * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + -0.05 * (X(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + -0.05 * (Y(4) @ Z(5) @ Y(6)) + + -0.05 * (X(4) @ Z(5) @ X(6)) + + -0.05 * (Y(5) @ Z(6) @ Y(7)) + + -0.05 * (X(5) @ Z(6) @ X(7)) + + 2.0 * I(0) + + -0.25 * Z(1) + + -0.25 * Z(0) + + -0.25 * (Z(0) @ Z(1)) + + -0.5 * Z(3) + + -0.5 * Z(2) + + -0.75 * Z(5) + + -0.75 * Z(4) + + 0.25 * (Z(4) @ Z(5)) + + -0.5 * Z(7) + + -0.5 * Z(6) + + 0.125 * (Z(0) @ Z(2)) + + 0.125 * (Z(0) @ Z(3)) + + 0.125 * (Z(1) @ Z(2)) + + 0.125 * (Z(1) @ Z(3)) + + 0.125 * (Z(0) @ Z(4)) + + 0.125 * (Z(0) @ Z(5)) + + 0.125 * (Z(1) @ Z(4)) + + 0.125 * (Z(1) @ Z(5)) + + 0.125 * (Z(2) @ Z(6)) + + 0.125 * (Z(2) @ Z(7)) + + 0.125 * (Z(3) @ Z(6)) + + 0.125 * (Z(3) @ Z(7)) + + 0.125 * (Z(4) @ Z(6)) + + 0.125 * (Z(4) @ Z(7)) + + 0.125 * (Z(5) @ Z(6)) + + 0.125 * (Z(5) @ Z(7)), + ), + ], +) +def test_emery_hamiltonian_matrix(shape, n_cells, t, u, v, expected_ham): + r"""Test that the correct Emery Hamiltonian is generated when interaction parameters are provided as a matrix""" + + emery_ham = emery(lattice=shape, n_cells=n_cells, hopping=t, coulomb=u, intersite_coupling=v) + + qml.assert_equal(emery_ham, expected_ham) + + +def test_hopping_error_haldane(): + r"""Test that an error is raised when the shape of provided interaction parameters is wrong for + Haldane Hamiltonian.""" + n_cells = [4, 4] + lattice = "Square" + with pytest.raises( + ValueError, + match=re.escape("The hopping parameter should be a constant or an array of shape (16,16)"), + ): + haldane(lattice=lattice, n_cells=n_cells, hopping=[1.0, 2.0]) + + with pytest.raises( + ValueError, + match=re.escape( + "The hopping_next parameter should be a constant or an array of shape (16,16)" + ), + ): + haldane(lattice=lattice, n_cells=n_cells, hopping=1.0, hopping_next=[0.5, 0.6, 0.7]) + + with pytest.raises( + ValueError, + match=re.escape("The phi parameter should be a constant or an array of shape (16,16)"), + ): + haldane(lattice=lattice, n_cells=n_cells, hopping=1.0, hopping_next=0.1, phi=[0.5, 0.6]) + + +def test_mapping_error_haldane(): + r"""Test that an error is raised when unsupported mapping is provided""" + n_cells = [4, 4] + lattice = "Square" + with pytest.raises(ValueError, match="The 'bk_sf' transformation is not available."): + haldane(lattice=lattice, n_cells=n_cells, mapping="bk_sf") + + +@pytest.mark.parametrize( + # expected_ham here was obtained manually. + ("shape", "n_cells", "t1", "t2", "phi", "boundary_condition", "expected_ham"), + [ + ( + "chain", + [4], + -1.23, + 2.34, + 1.0, + True, + (0.615 + 0j) * (Y(0) @ Z(1) @ Y(2)) + + (0.615 + 0j) * (X(0) @ Z(1) @ X(2)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Y(3)) + + (0.615 + 0j) * (X(1) @ Z(2) @ X(3)) + + (0.615 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (0.615 + 0j) * (Y(2) @ Z(3) @ Y(4)) + + (0.615 + 0j) * (X(2) @ Z(3) @ X(4)) + + (0.615 + 0j) * (Y(3) @ Z(4) @ Y(5)) + + (0.615 + 0j) * (X(3) @ Z(4) @ X(5)) + + (0.615 + 0j) * (Y(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(5) @ Z(6) @ X(7)) + + (-0.9845210522252389 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + (-0.6321536978657235 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + (-0.6321536978657235 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + (0.9845210522252389 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + (-0.9845210522252389 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + (-0.6321536978657235 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + (-0.6321536978657235 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + (0.9845210522252389 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + (-0.9845210522252389 + 0j) * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (-0.6321536978657235 + 0j) * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.6321536978657235 + 0j) * (X(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (0.9845210522252389 + 0j) * (X(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.9845210522252389 + 0j) * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (-0.6321536978657235 + 0j) * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.6321536978657235 + 0j) * (X(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (0.9845210522252389 + 0j) * (X(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)), + ), + ( + "square", + [2, 2], + -1.23, + 2.34, + [ + [0.0, 1.0, 1.0, 1.41421356], + [1.0, 0.0, 1.41421356, 1.0], + [1.0, 1.41421356, 0.0, 1.0], + [1.41421356, 1.0, 1.0, 0.0], + ], + False, + (0.615 + 0j) * (Y(0) @ Z(1) @ Y(2)) + + (0.615 + 0j) * (X(0) @ Z(1) @ X(2)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Y(3)) + + (0.615 + 0j) * (X(1) @ Z(2) @ X(3)) + + (0.615 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + (0.615 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + (0.615 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + (0.615 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + (0.615 + 0j) * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (0.615 + 0j) * (Y(4) @ Z(5) @ Y(6)) + + (0.615 + 0j) * (X(4) @ Z(5) @ X(6)) + + (0.615 + 0j) * (Y(5) @ Z(6) @ Y(7)) + + (0.615 + 0j) * (X(5) @ Z(6) @ X(7)) + + (-1.1556861568115007 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (-0.182454122875488 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.182454122875488 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (1.1556861568115007 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-1.1556861568115007 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (-0.182454122875488 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.182454122875488 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (1.1556861568115007 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-1.1556861568115007 + 0j) * (Y(2) @ Z(3) @ X(4)) + + (-0.182454122875488 + 0j) * (Y(2) @ Z(3) @ Y(4)) + + (-0.182454122875488 + 0j) * (X(2) @ Z(3) @ X(4)) + + (1.1556861568115007 + 0j) * (X(2) @ Z(3) @ Y(4)) + + (-1.1556861568115007 + 0j) * (Y(3) @ Z(4) @ X(5)) + + (-0.182454122875488 + 0j) * (Y(3) @ Z(4) @ Y(5)) + + (-0.182454122875488 + 0j) * (X(3) @ Z(4) @ X(5)) + + (1.1556861568115007 + 0j) * (X(3) @ Z(4) @ Y(5)), + ), + ( + "square", + [2, 2], + [[0, 0.5, 0.5, 0], [0.5, 0, 0, 0.5], [0.5, 0, 0, 0.5], [0, 0.5, 0.5, 0]], + 1.0, + [ + [0.0, 1.0, 1.0, 1.41421356], + [1.0, 0.0, 1.41421356, 1.0], + [1.0, 1.41421356, 0.0, 1.0], + [1.41421356, 1.0, 1.0, 0.0], + ], + False, + (-0.25 + 0j) * (Y(0) @ Z(1) @ Y(2)) + + (-0.25 + 0j) * (X(0) @ Z(1) @ X(2)) + + (-0.25 + 0j) * (Y(1) @ Z(2) @ Y(3)) + + (-0.25 + 0j) * (X(1) @ Z(2) @ X(3)) + + (-0.25 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + (-0.25 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + (-0.25 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + (-0.25 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + (-0.25 + 0j) * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.25 + 0j) * (X(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (-0.25 + 0j) * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.25 + 0j) * (X(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (-0.25 + 0j) * (Y(4) @ Z(5) @ Y(6)) + + (-0.25 + 0j) * (X(4) @ Z(5) @ X(6)) + + (-0.25 + 0j) * (Y(5) @ Z(6) @ Y(7)) + + (-0.25 + 0j) * (X(5) @ Z(6) @ X(7)) + + (-0.4938829729963678 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (-0.07797184738268718 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.07797184738268718 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (0.4938829729963678 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.4938829729963678 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (-0.07797184738268718 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.07797184738268718 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (0.4938829729963678 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.4938829729963678 + 0j) * (Y(2) @ Z(3) @ X(4)) + + (-0.07797184738268718 + 0j) * (Y(2) @ Z(3) @ Y(4)) + + (-0.07797184738268718 + 0j) * (X(2) @ Z(3) @ X(4)) + + (0.4938829729963678 + 0j) * (X(2) @ Z(3) @ Y(4)) + + (-0.4938829729963678 + 0j) * (Y(3) @ Z(4) @ X(5)) + + (-0.07797184738268718 + 0j) * (Y(3) @ Z(4) @ Y(5)) + + (-0.07797184738268718 + 0j) * (X(3) @ Z(4) @ X(5)) + + (0.4938829729963678 + 0j) * (X(3) @ Z(4) @ Y(5)), + ), + ( + "square", + [2, 2], + [[0, 0.5, 0.5, 0], [0.5, 0, 0, 0.5], [0.5, 0, 0, 0.5], [0, 0.5, 0.5, 0]], + [[0, 0, 0, 0.5], [0, 0, 0.5, 0], [0, 0.5, 0, 0], [0.5, 0.0, 0.0, 0]], + [ + [0.0, 1.0, 1.0, 1.41421356], + [1.0, 0.0, 1.41421356, 1.0], + [1.0, 1.41421356, 0.0, 1.0], + [1.41421356, 1.0, 1.0, 0.0], + ], + False, + (-0.25 + 0j) * (Y(0) @ Z(1) @ Y(2)) + + (-0.25 + 0j) * (X(0) @ Z(1) @ X(2)) + + (-0.25 + 0j) * (Y(1) @ Z(2) @ Y(3)) + + (-0.25 + 0j) * (X(1) @ Z(2) @ X(3)) + + (-0.25 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Y(4)) + + (-0.25 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ X(4)) + + (-0.25 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Y(5)) + + (-0.25 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ X(5)) + + (-0.25 + 0j) * (Y(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.25 + 0j) * (X(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (-0.25 + 0j) * (Y(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.25 + 0j) * (X(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (-0.25 + 0j) * (Y(4) @ Z(5) @ Y(6)) + + (-0.25 + 0j) * (X(4) @ Z(5) @ X(6)) + + (-0.25 + 0j) * (Y(5) @ Z(6) @ Y(7)) + + (-0.25 + 0j) * (X(5) @ Z(6) @ X(7)) + + (-0.2469414864981839 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (-0.03898592369134359 + 0j) * (Y(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.03898592369134359 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ X(6)) + + (0.2469414864981839 + 0j) * (X(0) @ Z(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Y(6)) + + (-0.2469414864981839 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (-0.03898592369134359 + 0j) * (Y(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.03898592369134359 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ X(7)) + + (0.2469414864981839 + 0j) * (X(1) @ Z(2) @ Z(3) @ Z(4) @ Z(5) @ Z(6) @ Y(7)) + + (-0.2469414864981839 + 0j) * (Y(2) @ Z(3) @ X(4)) + + (-0.03898592369134359 + 0j) * (Y(2) @ Z(3) @ Y(4)) + + (-0.03898592369134359 + 0j) * (X(2) @ Z(3) @ X(4)) + + (0.2469414864981839 + 0j) * (X(2) @ Z(3) @ Y(4)) + + (-0.2469414864981839 + 0j) * (Y(3) @ Z(4) @ X(5)) + + (-0.03898592369134359 + 0j) * (Y(3) @ Z(4) @ Y(5)) + + (-0.03898592369134359 + 0j) * (X(3) @ Z(4) @ X(5)) + + (0.2469414864981839 + 0j) * (X(3) @ Z(4) @ Y(5)), + ), + ], +) +def test_haldane_hamiltonian_matrix(shape, n_cells, t1, t2, phi, boundary_condition, expected_ham): + r"""Test that the correct Haldane Hamiltonian is generated.""" + + haldane_ham = haldane( + lattice=shape, + n_cells=n_cells, + hopping=t1, + hopping_next=t2, + phi=phi, + boundary_condition=boundary_condition, + ) + + qml.assert_equal(haldane_ham, expected_ham) + + def test_coupling_error_kitaev(): r"""Test that an error is raised when the provided coupling shape is wrong for Kitaev Hamiltonian.""" From d9f9144ab861ebce665c27839e56b0fcdf4ac103 Mon Sep 17 00:00:00 2001 From: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:40:54 -0400 Subject: [PATCH 46/83] Update tests/spin/test_lattice.py --- tests/spin/test_lattice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index c6697799a01..ff51c40fa2a 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -862,7 +862,6 @@ def test_custom_edges(vectors, positions, n_cells, custom_edges, expected_edges) n_cells=n_cells, vectors=vectors, positions=positions, custom_edges=custom_edges ) assert np.all(np.isin(expected_edges, lattice.edges)) -======= def test_dimension_error(): r"""Test that an error is raised if wrong dimension is provided for a given lattice shape.""" n_cells = [5, 5, 5] From e4269794a5ce4904e629147c3d05f6e4c63a245f Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 13:43:32 -0400 Subject: [PATCH 47/83] spin hamiltonian lattice --- pennylane/spin/lattice.py | 20 ++++++++++- pennylane/spin/spin_hamiltonian.py | 14 ++++---- tests/spin/test_lattice.py | 53 +++++++++++++++++++++++++++++ tests/spin/test_spin_hamiltonian.py | 12 +++---- 4 files changed, 86 insertions(+), 13 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 815179b33f2..325a1a1f523 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -32,7 +32,7 @@ class Lattice: Args: n_cells (list[int]): Number of cells in each direction of the grid. vectors (list[list[float]]): Primitive vectors for the lattice. - positions (list[list[float]]): Initial positions of spin cites. Default value is + positions (list[list[float]]): Initial positions of spin sites. Default value is ``[[0.0]`` :math:`\times` ``number of dimensions]``. boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes, @@ -45,6 +45,11 @@ class Lattice: First tuple contains the index of the starting and ending vertex of the edge. Second tuple is optional and contains the operator on that edge and coefficient of that operator. + custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials for nodes in + the lattice. Default value is None, which means no on-site potentials. Each element in + the list is for a separate node. For each element, the first value is + the index of the node, and the second element is a tuple which contains the operator and + coefficient. distance_tol (float): Distance below which spatial points are considered equal for the purpose of identifying nearest neighbours. Default value is 1e-5. @@ -55,6 +60,7 @@ class Lattice: if ``positions`` doesn't have a dimension of 2. if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors + if ``custom_nodes`` contains nodes with negative indices or indices greater than number of sites Returns: Lattice object @@ -79,6 +85,7 @@ def __init__( boundary_condition=False, neighbour_order=1, custom_edges=None, + custom_nodes=None, distance_tol=1e-5, ): @@ -134,6 +141,17 @@ def __init__( self.edges_indices = [(v1, v2) for (v1, v2, color) in self.edges] + if custom_nodes is not None: + for node in custom_nodes: + if node[0] > self.n_sites: + raise ValueError( + "Provided a custom node with index larger than number of sites." + ) + if node[0] < 0: + raise ValueError("Provided a custom node with index less than 0") + + self.nodes = custom_nodes + def _identify_neighbours(self, cutoff): r"""Identifies the connections between lattice points and returns the unique connections based on the neighbour_order. This function uses KDTree to identify neighbours, which diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 29f08f96bb4..0ea09dfe37a 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -389,7 +389,7 @@ def kitaev(n_cells, coupling=None, boundary_condition=False): return hamiltonian.simplify() -def spin_hamiltonian(lattice, custom_nodes=None): +def spin_hamiltonian(lattice): r"""Generates a spin Hamiltonian for a custom lattice. Args: @@ -411,6 +411,7 @@ def spin_hamiltonian(lattice, custom_nodes=None): 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)]], ) >>> spin_hamiltonian(lattice=lattice) @@ -440,10 +441,11 @@ def spin_hamiltonian(lattice, custom_nodes=None): hamiltonian += coeff * (opmap[op1](v1) @ opmap[op2](v2)) - for node in custom_nodes: - n = node[0] - op = node[1] - coeff = node[2] - hamiltonian += coeff * opmap[op](n) + if lattice.nodes is not None: + for node in lattice.nodes: + n = node[0] + op = node[1][0] + coeff = node[1][1] + hamiltonian += coeff * (opmap[op](n)) return hamiltonian.simplify() diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index 1b0eefc0bd5..f56236ab638 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -610,3 +610,56 @@ def test_custom_edges(vectors, positions, n_cells, custom_edges, expected_edges) n_cells=n_cells, vectors=vectors, positions=positions, custom_edges=custom_edges ) assert np.all(np.isin(expected_edges, lattice.edges)) + + +@pytest.mark.parametrize( + # expected_nodes here were obtained manually + ("vectors", "positions", "n_cells", "custom_nodes", "expected_nodes"), + [ + ( + [[0, 1], [1, 0]], + [[0, 0]], + [3, 3], + [[0, ("X", 0.3)], [2, ("Y", 0.3)]], + [[0, ("X", 0.3)], [2, ("Y", 0.3)]], + ), + ( + [[1, 0], [0.5, np.sqrt(3) / 2]], + [[0.5, 0.5 / 3**0.5], [1, 1 / 3**0.5]], + [2, 2], + [[0, ("X", 0.3)], [2, ("Y", 0.3)], [1, ("Z", 0.9)]], + [[0, ("X", 0.3)], [2, ("Y", 0.3)], [1, ("Z", 0.9)]], + ), + ], +) +def test_custom_nodes(vectors, positions, n_cells, custom_nodes, expected_nodes): + r"""Test that the edges are added as per custom_edges provided""" + lattice = Lattice( + n_cells=n_cells, vectors=vectors, positions=positions, custom_nodes=custom_nodes + ) + + assert lattice.nodes == expected_nodes + + +@pytest.mark.parametrize( + ("vectors", "positions", "n_cells", "custom_nodes"), + [ + ( + [[0, 1], [1, 0]], + [[0, 0]], + [3, 3], + [[0, ("X", 0.3)], [-202, ("Y", 0.3)]], + ), + ( + [[1, 0], [0.5, np.sqrt(3) / 2]], + [[0.5, 0.5 / 3**0.5], [1, 1 / 3**0.5]], + [2, 2], + [[0, ("X", 0.3)], [204, ("Y", 0.3)], [1, ("Z", 0.9)]], + ), + ], +) +def test_custom_nodes_error(vectors, positions, n_cells, custom_nodes): + r"""Test that the edges are added as per custom_edges provided""" + + with pytest.raises(ValueError, match="Provided a custom"): + Lattice(n_cells=n_cells, vectors=vectors, positions=positions, custom_nodes=custom_nodes) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 6f956239f2e..82aab8537d9 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -27,7 +27,7 @@ heisenberg, kitaev, transverse_ising, - spin_hamiltonian + spin_hamiltonian, ) pytestmark = pytest.mark.usefixtures("new_opmath_only") @@ -906,9 +906,6 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): + 0.7 * (Z(3) @ Z(6)) ), ), - ], - ("lattice", "expected_ham"), - [ ( Lattice( n_cells=[2, 2], @@ -916,6 +913,7 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): 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.3)], [7, ("Y", 0.9)]], ), ( 0.5 * (X(0) @ X(1)) @@ -926,18 +924,20 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): + 0.6 * (Y(5) @ Y(6)) + 0.7 * (Z(1) @ Z(4)) + 0.7 * (Z(3) @ Z(6)) + + 0.3 * X(0) + + 0.9 * Y(7) ), ), ], ) -def test_custom_hamiltonian(lattice, expected_ham): +def test_spin_hamiltonian(lattice, expected_ham): r"""Test that the correct Hamiltonian is generated""" spin_ham = spin_hamiltonian(lattice=lattice) qml.assert_equal(spin_ham, expected_ham) -def test_custom_hamiltonian_error(): +def test_spin_hamiltonian_error(): r"""Test that the correct Hamiltonian is generated""" lattice = Lattice(n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 1]]) with pytest.raises( From 828e7e76eeca08a2bb795a6266494668efb59e4b Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 13:44:33 -0400 Subject: [PATCH 48/83] spin hamiltonian lattice --- tests/spin/test_spin_hamiltonian.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 82aab8537d9..f3597632b55 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -887,6 +887,7 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): @pytest.mark.parametrize( ("lattice", "expected_ham"), [ + # This is the kitaev model ( Lattice( n_cells=[2, 2], From 188ddd63ca5179a354fb14bb913b334e8daace18 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:45:24 -0400 Subject: [PATCH 49/83] Update tests/spin/test_spin_hamiltonian.py Co-authored-by: Diksha Dhawan <40900030+ddhawan11@users.noreply.github.com> --- tests/spin/test_spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index f3597632b55..fe13d86b576 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -942,6 +942,6 @@ def test_spin_hamiltonian_error(): r"""Test that the correct Hamiltonian is generated""" lattice = Lattice(n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 1]]) with pytest.raises( - ValueError, match="Custom edges need to be defined and should have an operator" + ValueError, match="Custom edges need to be defined and each edge should have an operator assigned to it." ): spin_hamiltonian(lattice=lattice) From 9e554f2f6385b5a3dbf90795c05d4fe49efa9425 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 13:50:24 -0400 Subject: [PATCH 50/83] merge conflicts and quick fixes --- pennylane/spin/__init__.py | 10 +++++++++- tests/spin/test_lattice.py | 3 +++ tests/spin/test_spin_hamiltonian.py | 15 +++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/pennylane/spin/__init__.py b/pennylane/spin/__init__.py index 59cf96d681d..8967210caa8 100644 --- a/pennylane/spin/__init__.py +++ b/pennylane/spin/__init__.py @@ -16,4 +16,12 @@ """ from .lattice import Lattice -from .spin_hamiltonian import emery, fermi_hubbard, haldane, heisenberg, kitaev, transverse_ising, spin_hamiltonian +from .spin_hamiltonian import ( + emery, + fermi_hubbard, + haldane, + heisenberg, + kitaev, + transverse_ising, + spin_hamiltonian, +) diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index 3751bf1a61a..e0bca1e2db7 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -792,6 +792,7 @@ def test_shape_error(): with pytest.raises(ValueError, match="Lattice shape, 'Octagon' is not supported."): _generate_lattice(lattice=lattice, n_cells=n_cells) + def test_neighbour_order_error(): r"""Test that an error is raised if neighbour order is greater than 1 when custom_edges are provided.""" @@ -863,6 +864,7 @@ def test_custom_edges(vectors, positions, n_cells, custom_edges, expected_edges) ) assert np.all(np.isin(expected_edges, lattice.edges)) + @pytest.mark.parametrize( # expected_nodes here were obtained manually ("vectors", "positions", "n_cells", "custom_nodes", "expected_nodes"), @@ -915,6 +917,7 @@ def test_custom_nodes_error(vectors, positions, n_cells, custom_nodes): with pytest.raises(ValueError, match="Provided a custom"): Lattice(n_cells=n_cells, vectors=vectors, positions=positions, custom_nodes=custom_nodes) + def test_dimension_error(): r"""Test that an error is raised if wrong dimension is provided for a given lattice shape.""" n_cells = [5, 5, 5] diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 5c2208b4ae7..609cdd702fa 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -21,7 +21,16 @@ import pennylane as qml from pennylane import I, X, Y, Z -from pennylane.spin import emery, fermi_hubbard, haldane, heisenberg, kitaev, transverse_ising, spin_hamiltonian +from pennylane.spin import ( + Lattice, + emery, + fermi_hubbard, + haldane, + heisenberg, + kitaev, + transverse_ising, + spin_hamiltonian, +) # pylint: disable=too-many-arguments pytestmark = pytest.mark.usefixtures("new_opmath_only") @@ -1302,6 +1311,7 @@ def test_haldane_hamiltonian_matrix(shape, n_cells, t1, t2, phi, boundary_condit qml.assert_equal(haldane_ham, expected_ham) + def test_coupling_error_kitaev(): r"""Test that an error is raised when the provided coupling shape is wrong for Kitaev Hamiltonian.""" @@ -1453,6 +1463,7 @@ def test_spin_hamiltonian_error(): r"""Test that the correct Hamiltonian is generated""" lattice = Lattice(n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 1]]) with pytest.raises( - ValueError, match="Custom edges need to be defined and each edge should have an operator assigned to it." + ValueError, + match="Custom edges need to be defined and should have an operator defined as a `str`", ): spin_hamiltonian(lattice=lattice) From 9c68d74d470fbcb7ea621b57bf9a1a949db9387a Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:54:49 -0400 Subject: [PATCH 51/83] Update pennylane/spin/lattice.py Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index d546526fe9f..71434d4fd7e 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -148,7 +148,7 @@ def __init__( "Provided a custom node with index larger than number of sites." ) if node[0] < 0: - raise ValueError("Provided a custom node with index less than 0") + raise ValueError("Provided a custom node with index smaller than 0") self.nodes = custom_nodes From 7c3360e67cea4638f3abd6e213538f60c4b376f7 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:02:00 -0400 Subject: [PATCH 52/83] address most comments --- pennylane/spin/lattice.py | 14 +++++++------- pennylane/spin/spin_hamiltonian.py | 2 ++ tests/spin/test_lattice.py | 2 +- tests/spin/test_spin_hamiltonian.py | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 71434d4fd7e..b8016aa22f9 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -44,11 +44,11 @@ class Lattice: First tuple contains the indices of the starting and ending vertices of the edge. Second tuple is optional and contains the operator on that edge and coefficient of that operator. Default value is the index of edge in custom_edges list. - custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials for nodes in - the lattice. Default value is None, which means no on-site potentials. Each element in - the list is for a separate node. For each element, the first value is - the index of the node, and the second element is a tuple which contains the operator and - coefficient. + custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and + operators for nodes in the lattice. Default value is None, which means no on-site + potentials. Each element in the list is for a separate node. For each element, the first + value is the index of the node, and the second element is a tuple which contains the + operator and coefficient. distance_tol (float): Distance below which spatial points are considered equal for the purpose of identifying nearest neighbours. Default value is 1e-5. @@ -145,10 +145,10 @@ def __init__( for node in custom_nodes: if node[0] > self.n_sites: raise ValueError( - "Provided a custom node with index larger than number of sites." + "The custom node has an index larger than the number of sites." ) if node[0] < 0: - raise ValueError("Provided a custom node with index smaller than 0") + raise ValueError("The custom node has an index smaller than 0.") self.nodes = custom_nodes diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index d9a1295bead..70d4e805ceb 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -726,6 +726,8 @@ def spin_hamiltonian(lattice): + 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) ) """ diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index e0bca1e2db7..8a42c3dd2fa 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -886,7 +886,7 @@ def test_custom_edges(vectors, positions, n_cells, custom_edges, expected_edges) ], ) def test_custom_nodes(vectors, positions, n_cells, custom_nodes, expected_nodes): - r"""Test that the edges are added as per custom_edges provided""" + r"""Test that the nodes are added as per custom_nodes provided""" lattice = Lattice( n_cells=n_cells, vectors=vectors, positions=positions, custom_nodes=custom_nodes ) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 609cdd702fa..488794bff17 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -1408,7 +1408,7 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): @pytest.mark.parametrize( ("lattice", "expected_ham"), [ - # This is the kitaev model + # This is the Hamiltonian for the Kitaev model on the Honeycomb lattice ( Lattice( n_cells=[2, 2], From 7151456ad4a46820cea9e2845be1ef703496cbab Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:11:05 -0400 Subject: [PATCH 53/83] merge confs --- pennylane/spin/lattice.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index b46395ebac5..7ca5b0e20f1 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -44,14 +44,11 @@ class Lattice: First tuple contains the indices of the starting and ending vertices of the edge. Second tuple is optional and contains the operator on that edge and coefficient of that operator. Default value is the index of edge in custom_edges list. - <<<<<<< HEAD custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and operators for nodes in the lattice. Default value is None, which means no on-site potentials. Each element in the list is for a separate node. For each element, the first value is the index of the node, and the second element is a tuple which contains the operator and coefficient. - ======= - >>>>>>> master distance_tol (float): Distance below which spatial points are considered equal for the purpose of identifying nearest neighbours. Default value is 1e-5. From 1fed9ab7f79002974a07805f064bf8ce0dddb80f Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:11:29 -0400 Subject: [PATCH 54/83] merge confs --- pennylane/spin/lattice.py | 82 +++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 7ca5b0e20f1..e365385419e 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -29,50 +29,50 @@ class Lattice: r"""Constructs a Lattice object. - Args: - n_cells (list[int]): Number of cells in each direction of the grid. - vectors (list[list[float]]): Primitive vectors for the lattice. - positions (list[list[float]]): Initial positions of spin sites. Default value is - ``[[0.0]`` :math:`\times` ``number of dimensions]``. - boundary_condition (bool or list[bool]): Defines boundary conditions for 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 neighbour. This cannot be greater than 1 if custom_edges is defined. - custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. - Default value is ``None``, which adds the edges based on ``neighbour_order``. - Each element in the list is for a separate edge, and can contain 1 or 2 tuples. - First tuple contains the indices of the starting and ending vertices of the edge. - Second tuple is optional and contains the operator on that edge and coefficient - of that operator. Default value is the index of edge in custom_edges list. - custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and - operators for nodes in the lattice. Default value is None, which means no on-site - potentials. Each element in the list is for a separate node. For each element, the first - value is the index of the node, and the second element is a tuple which contains the - operator and coefficient. - distance_tol (float): Distance below which spatial points are considered equal for the - purpose of identifying nearest neighbours. Default value is 1e-5. - - Raises: - TypeError: - if ``n_cells`` contains numbers other than positive integers. - ValueError: - if ``positions`` doesn't have a dimension of 2. - if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. - if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors - if ``custom_nodes`` contains nodes with negative indices or indices greater than number of sites + Args: + n_cells (list[int]): Number of cells in each direction of the grid. + vectors (list[list[float]]): Primitive vectors for the lattice. + positions (list[list[float]]): Initial positions of spin sites. Default value is + ``[[0.0]`` :math:`\times` ``number of dimensions]``. + boundary_condition (bool or list[bool]): Defines boundary conditions for 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 neighbour. This cannot be greater than 1 if custom_edges is defined. + custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. + Default value is ``None``, which adds the edges based on ``neighbour_order``. + Each element in the list is for a separate edge, and can contain 1 or 2 tuples. + First tuple contains the indices of the starting and ending vertices of the edge. + Second tuple is optional and contains the operator on that edge and coefficient + of that operator. Default value is the index of edge in custom_edges list. + custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and + operators for nodes in the lattice. Default value is None, which means no on-site + potentials. Each element in the list is for a separate node. For each element, the first + value is the index of the node, and the second element is a tuple which contains the + operator and coefficient. + distance_tol (float): Distance below which spatial points are considered equal for the + purpose of identifying nearest neighbours. Default value is 1e-5. + + Raises: + TypeError: + if ``n_cells`` contains numbers other than positive integers. + ValueError: + if ``positions`` doesn't have a dimension of 2. + if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. + if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors + if ``custom_nodes`` contains nodes with negative indices or indices greater than number of sites - Returns: - Lattice object + Returns: + Lattice object - **Example** + **Example** - >>> n_cells = [2,2] - >>> vectors = [[0, 1], [1, 0]] - >>> boundary_condition = [True, False] - >>> lattice = qml.spin.Lattice(n_cells, vectors, - >>> boundary_condition=boundary_condition) - >>> lattice.edges - [(2, 3, 0), (0, 2, 0), (1, 3, 0), (0, 1, 0)] + >>> n_cells = [2,2] + >>> vectors = [[0, 1], [1, 0]] + >>> boundary_condition = [True, False] + >>> lattice = qml.spin.Lattice(n_cells, vectors, + >>> boundary_condition=boundary_condition) + >>> lattice.edges + [(2, 3, 0), (0, 2, 0), (1, 3, 0), (0, 1, 0)] """ From d5e88dac2e63cfc1c3eab308a41c5d35b26762f1 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:13:04 -0400 Subject: [PATCH 55/83] merge confs --- pennylane/spin/lattice.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index e365385419e..a73571a6841 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -30,26 +30,26 @@ class Lattice: r"""Constructs a Lattice object. Args: - n_cells (list[int]): Number of cells in each direction of the grid. - vectors (list[list[float]]): Primitive vectors for the lattice. - positions (list[list[float]]): Initial positions of spin sites. Default value is + n_cells (list[int]): Number of cells in each direction of the grid. + vectors (list[list[float]]): Primitive vectors for the lattice. + positions (list[list[float]]): Initial positions of spin sites. Default value is ``[[0.0]`` :math:`\times` ``number of dimensions]``. - boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes, + boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes, default is ``False`` indicating open boundary condition. - neighbour_order (int): Specifies the interaction level for neighbors within the lattice. + neighbour_order (int): Specifies the interaction level for neighbors within the lattice. Default is 1, indicating nearest neighbour. This cannot be greater than 1 if custom_edges is defined. - custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. + custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. Default value is ``None``, which adds the edges based on ``neighbour_order``. Each element in the list is for a separate edge, and can contain 1 or 2 tuples. First tuple contains the indices of the starting and ending vertices of the edge. Second tuple is optional and contains the operator on that edge and coefficient of that operator. Default value is the index of edge in custom_edges list. - custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and + custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and operators for nodes in the lattice. Default value is None, which means no on-site potentials. Each element in the list is for a separate node. For each element, the first value is the index of the node, and the second element is a tuple which contains the operator and coefficient. - distance_tol (float): Distance below which spatial points are considered equal for the + distance_tol (float): Distance below which spatial points are considered equal for the purpose of identifying nearest neighbours. Default value is 1e-5. Raises: From 38d3832ee200d1154f441f318fe580db16e03a15 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:13:44 -0400 Subject: [PATCH 56/83] merge confs --- pennylane/spin/lattice.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index a73571a6841..c91ec9841e1 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -33,24 +33,24 @@ class Lattice: n_cells (list[int]): Number of cells in each direction of the grid. vectors (list[list[float]]): Primitive vectors for the lattice. positions (list[list[float]]): Initial positions of spin sites. Default value is - ``[[0.0]`` :math:`\times` ``number of dimensions]``. + ``[[0.0]`` :math:`\times` ``number of dimensions]``. boundary_condition (bool or list[bool]): Defines boundary conditions for different lattice axes, - default is ``False`` indicating open boundary condition. + default is ``False`` indicating open boundary condition. neighbour_order (int): Specifies the interaction level for neighbors within the lattice. - Default is 1, indicating nearest neighbour. This cannot be greater than 1 if custom_edges is defined. + Default is 1, indicating nearest neighbour. This cannot be greater than 1 if custom_edges is defined. custom_edges (Optional[list(list(tuples))]): Specifies the edges to be added in the lattice. - Default value is ``None``, which adds the edges based on ``neighbour_order``. - Each element in the list is for a separate edge, and can contain 1 or 2 tuples. - First tuple contains the indices of the starting and ending vertices of the edge. - Second tuple is optional and contains the operator on that edge and coefficient - of that operator. Default value is the index of edge in custom_edges list. + Default value is ``None``, which adds the edges based on ``neighbour_order``. + Each element in the list is for a separate edge, and can contain 1 or 2 tuples. + First tuple contains the indices of the starting and ending vertices of the edge. + Second tuple is optional and contains the operator on that edge and coefficient + of that operator. Default value is the index of edge in custom_edges list. custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and - operators for nodes in the lattice. Default value is None, which means no on-site - potentials. Each element in the list is for a separate node. For each element, the first - value is the index of the node, and the second element is a tuple which contains the - operator and coefficient. + operators for nodes in the lattice. Default value is None, which means no on-site + potentials. Each element in the list is for a separate node. For each element, the first + value is the index of the node, and the second element is a tuple which contains the + operator and coefficient. distance_tol (float): Distance below which spatial points are considered equal for the - purpose of identifying nearest neighbours. Default value is 1e-5. + purpose of identifying nearest neighbours. Default value is 1e-5. Raises: TypeError: From 8dc63e2a25fb8501b98debb1e0c72fc762b74f1b Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:14:05 -0400 Subject: [PATCH 57/83] merge confs --- pennylane/spin/lattice.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index c91ec9841e1..e76ce807bdd 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -54,15 +54,15 @@ class Lattice: Raises: TypeError: - if ``n_cells`` contains numbers other than positive integers. + if ``n_cells`` contains numbers other than positive integers. ValueError: - if ``positions`` doesn't have a dimension of 2. - if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. - if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors - if ``custom_nodes`` contains nodes with negative indices or indices greater than number of sites + if ``positions`` doesn't have a dimension of 2. + if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. + if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors + if ``custom_nodes`` contains nodes with negative indices or indices greater than number of sites Returns: - Lattice object + Lattice object **Example** From 86934cc05f830636f5477b5e5adac1ed20715835 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:15:08 -0400 Subject: [PATCH 58/83] merge confs --- pennylane/spin/lattice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index e76ce807bdd..2c7e87d53b6 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -53,9 +53,9 @@ class Lattice: purpose of identifying nearest neighbours. Default value is 1e-5. Raises: - TypeError: + TypeError: if ``n_cells`` contains numbers other than positive integers. - ValueError: + ValueError: if ``positions`` doesn't have a dimension of 2. if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors From 880fe666bb0e81d3b8849f8007243cfab63ee108 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:15:40 -0400 Subject: [PATCH 59/83] merge confs --- pennylane/spin/lattice.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 2c7e87d53b6..9b7cc17434d 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -54,12 +54,12 @@ class Lattice: Raises: TypeError: - if ``n_cells`` contains numbers other than positive integers. + if ``n_cells`` contains numbers other than positive integers. ValueError: - if ``positions`` doesn't have a dimension of 2. - if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. - if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors - if ``custom_nodes`` contains nodes with negative indices or indices greater than number of sites + if ``positions`` doesn't have a dimension of 2. + if ``vectors`` doesn't have a dimension of 2 or the length of vectors is not equal to the number of vectors. + if ``boundary_condition`` is not a bool or a list of bools with length equal to the number of vectors + if ``custom_nodes`` contains nodes with negative indices or indices greater than number of sites Returns: Lattice object From 346e97532ead746577ff45f461dd3607a944d40b Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:17:37 -0400 Subject: [PATCH 60/83] merge confs --- pennylane/spin/lattice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 9b7cc17434d..cc5c220fdd4 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -141,6 +141,7 @@ def __init__( self.edges_indices = [(v1, v2) for (v1, v2, color) in self.edges] + # pylint: disable=too-many-branches if custom_nodes is not None: for node in custom_nodes: if node[0] > self.n_sites: From 3bc6469a6e929face3cc07d789dce19b9432e623 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:17:59 -0400 Subject: [PATCH 61/83] merge confs --- pennylane/spin/lattice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index cc5c220fdd4..4fe824c28cf 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -24,7 +24,7 @@ # pylint: disable=too-many-arguments, too-many-instance-attributes # pylint: disable=use-a-generator, too-few-public-methods - +# pylint: disable=too-many-branches class Lattice: r"""Constructs a Lattice object. @@ -141,7 +141,7 @@ def __init__( self.edges_indices = [(v1, v2) for (v1, v2, color) in self.edges] - # pylint: disable=too-many-branches + if custom_nodes is not None: for node in custom_nodes: if node[0] > self.n_sites: From 102099047739209eac6afdb27f97ecd2bbcc093a Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:18:11 -0400 Subject: [PATCH 62/83] pylint --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index 4fe824c28cf..d8c618b7639 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -26,6 +26,7 @@ # pylint: disable=use-a-generator, too-few-public-methods # pylint: disable=too-many-branches + class Lattice: r"""Constructs a Lattice object. @@ -141,7 +142,6 @@ def __init__( self.edges_indices = [(v1, v2) for (v1, v2, color) in self.edges] - if custom_nodes is not None: for node in custom_nodes: if node[0] > self.n_sites: From 8a4866c3c65fd5d434f8078c4d4af9c5ac9a2e1b Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:41:12 -0400 Subject: [PATCH 63/83] opqrst --- tests/spin/test_lattice.py | 2 +- tests/spin/test_spin_hamiltonian.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index 8a42c3dd2fa..7e86acd3f4f 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -914,7 +914,7 @@ def test_custom_nodes(vectors, positions, n_cells, custom_nodes, expected_nodes) def test_custom_nodes_error(vectors, positions, n_cells, custom_nodes): r"""Test that the edges are added as per custom_edges provided""" - with pytest.raises(ValueError, match="Provided a custom"): + with pytest.raises(ValueError, match="The custom node has"): Lattice(n_cells=n_cells, vectors=vectors, positions=positions, custom_nodes=custom_nodes) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 488794bff17..0c70708a365 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -28,8 +28,8 @@ haldane, heisenberg, kitaev, - transverse_ising, spin_hamiltonian, + transverse_ising, ) # pylint: disable=too-many-arguments From cf1e376c1343bacc0d160d59b2ee1c8ea268b741 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:41:34 -0400 Subject: [PATCH 64/83] opqrst --- pennylane/spin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/__init__.py b/pennylane/spin/__init__.py index 8967210caa8..9bf426d3649 100644 --- a/pennylane/spin/__init__.py +++ b/pennylane/spin/__init__.py @@ -22,6 +22,6 @@ haldane, heisenberg, kitaev, - transverse_ising, spin_hamiltonian, + transverse_ising, ) From 8dadcfed178ac1098a3dbc62521378d9aff7f6f1 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:42:54 -0400 Subject: [PATCH 65/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 70d4e805ceb..0e95f3df04a 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -707,7 +707,7 @@ def spin_hamiltonian(lattice): .. code-block:: python - lattice = Lattice( + lattice = qml.spin.Lattice( n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 5]], From be0872193e8c8e27ed230e710529e42e0e7b28d4 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:43:00 -0400 Subject: [PATCH 66/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 0e95f3df04a..e07fa1aa85e 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -716,7 +716,7 @@ def spin_hamiltonian(lattice): custom_nodes=[[0, ("X", 0.5)], [1, ("Y", 0.3)]], ) - >>> spin_hamiltonian(lattice=lattice) + >>> qml.spin.spin_hamiltonian(lattice=lattice) >>> ( 0.5 * (X(0) @ X(1)) + 0.5 * (X(2) @ X(3)) From 97fd0ff23e4b797e08bb2b25a53eb6c9132faca0 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:45:34 -0400 Subject: [PATCH 67/83] Update pennylane/spin/lattice.py 3 spaces Co-authored-by: Utkarsh --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index d8c618b7639..fd4e07e6097 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -49,7 +49,7 @@ class Lattice: operators for nodes in the lattice. Default value is None, which means no on-site potentials. Each element in the list is for a separate node. For each element, the first value is the index of the node, and the second element is a tuple which contains the - operator and coefficient. + operator and coefficient. distance_tol (float): Distance below which spatial points are considered equal for the purpose of identifying nearest neighbours. Default value is 1e-5. From 04fe016eb38d76d2e59d82bb102028420feada75 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:44:07 -0400 Subject: [PATCH 68/83] address comments --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index e07fa1aa85e..fbb329abf35 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -698,7 +698,7 @@ def spin_hamiltonian(lattice): lattice (Lattice): custom lattice defined with custom_edges Raises: - ValueError: if ``custom_edges`` are not defined or not defined with operators + ValueError: if the input lattice does not have ``custom_edges`` defined with operators Returns: ~ops.op_math.Sum: Hamiltonian for the lattice From 4b91a85922d45dd664c04e6870359128b5ff1581 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:46:38 -0400 Subject: [PATCH 69/83] address comments --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index fbb329abf35..2f3284b7bb8 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -698,7 +698,7 @@ def spin_hamiltonian(lattice): lattice (Lattice): custom lattice defined with custom_edges Raises: - ValueError: if the input lattice does not have ``custom_edges`` defined with operators + ValueError: if the provided Lattice does not have ``custom_edges`` defined with operators Returns: ~ops.op_math.Sum: Hamiltonian for the lattice From 76693533a619b4330d5f5a83da13645f31e4b506 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:55:18 -0400 Subject: [PATCH 70/83] Update tests/spin/test_spin_hamiltonian.py Co-authored-by: Utkarsh --- tests/spin/test_spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 0c70708a365..9c4ecc81f98 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -1453,7 +1453,7 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): ], ) def test_spin_hamiltonian(lattice, expected_ham): - r"""Test that the correct Hamiltonian is generated""" + r"""Test that the correct Hamiltonian is generated from a given Lattice""" spin_ham = spin_hamiltonian(lattice=lattice) qml.assert_equal(spin_ham, expected_ham) From bfa9e2f46e9028217d1fad62fa1f4bbdfcc32583 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:56:12 -0400 Subject: [PATCH 71/83] Update tests/spin/test_spin_hamiltonian.py Co-authored-by: Utkarsh --- tests/spin/test_spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 9c4ecc81f98..1637bc7d60c 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -1460,7 +1460,7 @@ def test_spin_hamiltonian(lattice, expected_ham): def test_spin_hamiltonian_error(): - r"""Test that the correct Hamiltonian is generated""" + r"""Test that the correct error is raised Hamiltonian with incompatible Lattice""" lattice = Lattice(n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 1]]) with pytest.raises( ValueError, From eb0670bb6745b62810396e5ee636a1429d34d10f Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 15:57:22 -0400 Subject: [PATCH 72/83] address comments --- pennylane/spin/spin_hamiltonian.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 2f3284b7bb8..8aae04a91a3 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -16,7 +16,7 @@ """ import pennylane as qml -from pennylane import X, Y, Z, math +from pennylane import I, X, Y, Z, math from pennylane.fermi import FermiWord from .lattice import Lattice, _generate_lattice @@ -736,7 +736,7 @@ def spin_hamiltonian(lattice): "Custom edges need to be defined and should have an operator defined as a `str`" ) - opmap = {"X": X, "Y": Y, "Z": Z} + opmap = {"I": I, "X": X, "Y": Y, "Z": Z} hamiltonian = 0.0 * qml.I(0) for edge in lattice.edges: v1, v2 = edge[0:2] From 9af9532be0a56483a6a7f7276a9a995a3082114c Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:06:48 -0400 Subject: [PATCH 73/83] Update tests/spin/test_lattice.py Co-authored-by: Utkarsh --- tests/spin/test_lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spin/test_lattice.py b/tests/spin/test_lattice.py index 7e86acd3f4f..0d95f6a541c 100644 --- a/tests/spin/test_lattice.py +++ b/tests/spin/test_lattice.py @@ -912,7 +912,7 @@ def test_custom_nodes(vectors, positions, n_cells, custom_nodes, expected_nodes) ], ) def test_custom_nodes_error(vectors, positions, n_cells, custom_nodes): - r"""Test that the edges are added as per custom_edges provided""" + r"""Test that the incompatible `custom_nodes` raise correct error""" with pytest.raises(ValueError, match="The custom node has"): Lattice(n_cells=n_cells, vectors=vectors, positions=positions, custom_nodes=custom_nodes) From 94df8e1bd8e6f2472949a80be8ebd7f6dba5a79e Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:31:24 -0400 Subject: [PATCH 74/83] Update pennylane/spin/lattice.py Co-authored-by: Utkarsh --- pennylane/spin/lattice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/lattice.py b/pennylane/spin/lattice.py index fd4e07e6097..9bd749f96f9 100644 --- a/pennylane/spin/lattice.py +++ b/pennylane/spin/lattice.py @@ -46,7 +46,7 @@ class Lattice: Second tuple is optional and contains the operator on that edge and coefficient of that operator. Default value is the index of edge in custom_edges list. custom_nodes (Optional(list(list(int, tuples)))): Specifies the on-site potentials and - operators for nodes in the lattice. Default value is None, which means no on-site + operators for nodes in the lattice. The default value is `None`, which means no on-site potentials. Each element in the list is for a separate node. For each element, the first value is the index of the node, and the second element is a tuple which contains the operator and coefficient. From 66a54887594e0cd585d4f508e20c1c190102ae71 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:31:38 -0400 Subject: [PATCH 75/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Utkarsh --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 8aae04a91a3..f9009dec3f4 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -695,7 +695,7 @@ def spin_hamiltonian(lattice): r"""Generates a spin Hamiltonian for a custom lattice. Args: - lattice (Lattice): custom lattice defined with custom_edges + lattice (Lattice): custom lattice defined with ``custom_edges`` and optional ``custom_nodes`` Raises: ValueError: if the provided Lattice does not have ``custom_edges`` defined with operators From a70d2bcf595a1ba4d8fe04b99c54c86818e88be9 Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:33:14 -0400 Subject: [PATCH 76/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Utkarsh --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index f9009dec3f4..2d4cfaee273 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -707,7 +707,7 @@ def spin_hamiltonian(lattice): .. code-block:: python - lattice = qml.spin.Lattice( + >>> lattice = qml.spin.Lattice( n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 5]], From 132177bb9aadd2d7c5c07ede6f6f2cadde5b4221 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 16:32:49 -0400 Subject: [PATCH 77/83] addres comments --- tests/spin/test_spin_hamiltonian.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/spin/test_spin_hamiltonian.py b/tests/spin/test_spin_hamiltonian.py index 1637bc7d60c..6caccafba21 100644 --- a/tests/spin/test_spin_hamiltonian.py +++ b/tests/spin/test_spin_hamiltonian.py @@ -1450,6 +1450,28 @@ def test_kitaev_hamiltonian(n_cells, j, boundary_condition, expected_ham): + 0.9 * Y(7) ), ), + ( + 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.3)], [7, ("Y", 0.9)], [0, ("X", 0.5)]], + ), + ( + 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.8 * X(0) + + 0.9 * Y(7) + ), + ), ], ) def test_spin_hamiltonian(lattice, expected_ham): From 7fc213732626c21a7918afee19038f3ef608942d Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 16:36:07 -0400 Subject: [PATCH 78/83] i guess this is a bit more accurate? --- pennylane/spin/spin_hamiltonian.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 2d4cfaee273..9c8c41a634e 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -695,7 +695,7 @@ def spin_hamiltonian(lattice): r"""Generates a spin Hamiltonian for a custom lattice. Args: - lattice (Lattice): custom lattice defined with ``custom_edges`` and optional ``custom_nodes`` + lattice (Lattice): custom lattice defined with custom_edges Raises: ValueError: if the provided Lattice does not have ``custom_edges`` defined with operators @@ -707,7 +707,7 @@ def spin_hamiltonian(lattice): .. code-block:: python - >>> lattice = qml.spin.Lattice( + lattice = qml.spin.Lattice( n_cells=[2, 2], vectors=[[1, 0], [0, 1]], positions=[[0, 0], [1, 5]], @@ -719,15 +719,15 @@ def spin_hamiltonian(lattice): >>> 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) + + 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) ) """ From 22e1e10668f593f257db7daeb3f8743e77f5986a Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 16:39:16 -0400 Subject: [PATCH 79/83] like this? --- pennylane/spin/spin_hamiltonian.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 9c8c41a634e..a767d234331 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -716,19 +716,19 @@ def spin_hamiltonian(lattice): 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) - ) + 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) + ) """ if not isinstance(lattice.edges[0][2], tuple): From e01f4f51d7b28f5fc33c4afa5fea8bc22ab8a794 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 16:43:36 -0400 Subject: [PATCH 80/83] like this? --- pennylane/spin/spin_hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index a767d234331..893691af602 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -716,7 +716,7 @@ def spin_hamiltonian(lattice): custom_nodes=[[0, ("X", 0.5)], [1, ("Y", 0.3)]], ) - qml.spin.spin_hamiltonian(lattice=lattice) + >>> qml.spin.spin_hamiltonian(lattice=lattice) ( 0.5 * (X(0) @ X(1)) + 0.5 * (X(2) @ X(3)) From 465fbc45e114715279ee7d4f94ae887e78af51af Mon Sep 17 00:00:00 2001 From: Austin Huang <65315367+austingmhuang@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:04:39 -0400 Subject: [PATCH 81/83] Update pennylane/spin/spin_hamiltonian.py Co-authored-by: Utkarsh --- pennylane/spin/spin_hamiltonian.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 893691af602..04a2646219a 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -707,14 +707,27 @@ def spin_hamiltonian(lattice): .. 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)]], - ) +>>> 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) +) >>> qml.spin.spin_hamiltonian(lattice=lattice) ( From cc514823cf5949cb6f81d4aedfdfc3d0bb30a59c Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 17:06:26 -0400 Subject: [PATCH 82/83] changelog entry --- doc/releases/changelog-dev.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 0a7fb5f2c1a..eb81b692e0f 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -8,6 +8,9 @@ [Kitaev](https://arxiv.org/abs/cond-mat/0506438) model on a lattice. [(#6174)](https://github.com/PennyLaneAI/pennylane/pull/6174) +* Function is added for generating the spin Hamiltonians for custom lattices. + [(#6226)](https://github.com/PennyLaneAI/pennylane/pull/6226) + * Functions are added for generating spin Hamiltonians for [Emery] (https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.58.2794) and [Haldane](https://journals.aps.org/prl/pdf/10.1103/PhysRevLett.61.2015) models on a lattice. @@ -199,6 +202,7 @@ Diksha Dhawan, Lillian M. A. Frederiksen, Pietropaolo Frisoni, Emiliano Godinez, +Austin Huang, Christina Lee, William Maxwell, Lee J. O'Riordan, From 4b39e337a84b0d3498576134698fc863e838bbf6 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 20 Sep 2024 17:12:29 -0400 Subject: [PATCH 83/83] docs --- pennylane/spin/spin_hamiltonian.py | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/pennylane/spin/spin_hamiltonian.py b/pennylane/spin/spin_hamiltonian.py index 04a2646219a..1f01b696812 100644 --- a/pennylane/spin/spin_hamiltonian.py +++ b/pennylane/spin/spin_hamiltonian.py @@ -707,28 +707,14 @@ def spin_hamiltonian(lattice): .. 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) -) - + >>> 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))