Skip to content

Commit

Permalink
Merge branch 'master' into sc-73314-add-benchmarks-webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
rashidnhm authored Sep 23, 2024
2 parents 9a6aee9 + ce43f0e commit 019c0f9
Show file tree
Hide file tree
Showing 14 changed files with 50,714 additions and 58,104 deletions.
59,925 changes: 28,978 additions & 30,947 deletions .github/workflows/core_tests_durations.json

Large diffs are not rendered by default.

37,267 changes: 16,414 additions & 20,853 deletions .github/workflows/jax_tests_durations.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
&& !contains(github.event.pull_request.labels.*.name, 'ci:run-full-test-suite')
|| false
}}
pytest_store_durations: false
pytest_store_durations: true

upload-stable-deps:
needs: tests
Expand Down
11,335 changes: 5,055 additions & 6,280 deletions .github/workflows/tf_tests_durations.json

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions doc/development/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ This page contains the release notes for PennyLane.

.. mdinclude:: ../releases/changelog-dev.md

.. mdinclude:: ../releases/changelog-0.38.1.md

.. mdinclude:: ../releases/changelog-0.38.0.md

.. mdinclude:: ../releases/changelog-0.37.0.md
Expand Down
14 changes: 0 additions & 14 deletions doc/releases/changelog-0.38.1.md

This file was deleted.

8 changes: 8 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -163,6 +166,10 @@

<h3>Bug fixes 🐛</h3>

* Fix float-to-complex casting in various places across PennyLane.
[(#6260)](https://github.com/PennyLaneAI/pennylane/pull/6260)
[(#6268)](https://github.com/PennyLaneAI/pennylane/pull/6268)

* Fix a bug where zero-valued JVPs were calculated wrongly in the presence of shot vectors.
[(#6219)](https://github.com/PennyLaneAI/pennylane/pull/6219)

Expand Down Expand Up @@ -199,6 +206,7 @@ Diksha Dhawan,
Lillian M. A. Frederiksen,
Pietropaolo Frisoni,
Emiliano Godinez,
Austin Huang,
Christina Lee,
William Maxwell,
Lee J. O'Riordan,
Expand Down
2 changes: 1 addition & 1 deletion pennylane/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "0.39.0-dev19"
__version__ = "0.39.0-dev20"
17 changes: 15 additions & 2 deletions pennylane/devices/qubit/apply_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,13 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched:
Returns:
array[complex]: output_state
"""
mat = qml.math.cast_like(op.matrix(), 1j)
# We use this implicit casting strategy as autograd raises ComplexWarnings
# when backpropagating if casting explicitly. Some type of casting is needed
# to prevent ComplexWarnings with backpropagation with other interfaces
if qml.math.get_interface(state) == "tensorflow":
mat = qml.math.cast_like(op.matrix(), state)
else:
mat = op.matrix() + 0j

total_indices = len(state.shape) - is_state_batched
num_indices = len(op.wires)
Expand Down Expand Up @@ -114,7 +120,14 @@ def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batche
Returns:
array[complex]: output_state
"""
mat = qml.math.cast_like(op.matrix(), 1j)
# We use this implicit casting strategy as autograd raises ComplexWarnings
# when backpropagating if casting explicitly. Some type of casting is needed
# to prevent ComplexWarnings with backpropagation with other interfaces
if qml.math.get_interface(state) == "tensorflow":
mat = qml.math.cast_like(op.matrix(), state)
else:
mat = op.matrix() + 0j

total_indices = len(state.shape) - is_state_batched
num_indices = len(op.wires)

Expand Down
10 changes: 9 additions & 1 deletion pennylane/spin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,12 @@
"""

from .lattice import Lattice
from .spin_hamiltonian import emery, fermi_hubbard, haldane, heisenberg, kitaev, transverse_ising
from .spin_hamiltonian import (
emery,
fermi_hubbard,
haldane,
heisenberg,
kitaev,
spin_hamiltonian,
transverse_ising,
)
21 changes: 20 additions & 1 deletion pennylane/spin/lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +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:
Expand All @@ -32,7 +33,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,
default is ``False`` indicating open boundary condition.
Expand All @@ -44,6 +45,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 and
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.
distance_tol (float): Distance below which spatial points are considered equal for the
purpose of identifying nearest neighbours. Default value is 1e-5.
Expand All @@ -54,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
Expand All @@ -78,6 +85,7 @@ def __init__(
boundary_condition=False,
neighbour_order=1,
custom_edges=None,
custom_nodes=None,
distance_tol=1e-5,
):

Expand Down Expand Up @@ -134,6 +142,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(
"The custom node has an index larger than the number of sites."
)
if node[0] < 0:
raise ValueError("The custom node has an index smaller 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
Expand Down
65 changes: 64 additions & 1 deletion pennylane/spin/spin_hamiltonian.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -689,3 +689,66 @@ def kitaev(n_cells, coupling=None, boundary_condition=False):
hamiltonian += coeff * (opmap[op1](v1) @ opmap[op2](v2))

return hamiltonian.simplify()


def spin_hamiltonian(lattice):
r"""Generates a spin Hamiltonian for a custom lattice.
Args:
lattice (Lattice): custom lattice defined with custom_edges
Raises:
ValueError: if the provided Lattice does not have ``custom_edges`` defined with operators
Returns:
~ops.op_math.Sum: Hamiltonian for the lattice
**Example**
.. code-block:: python
>>> lattice = qml.spin.Lattice(
... n_cells=[2, 2],
... vectors=[[1, 0], [0, 1]],
... positions=[[0, 0], [1, 5]],
... boundary_condition=False,
... custom_edges=[[(0, 1), ("XX", 0.5)], [(1, 2), ("YY", 0.6)], [(1, 4), ("ZZ", 0.7)]],
... custom_nodes=[[0, ("X", 0.5)], [1, ("Y", 0.3)]],
... )
>>> qml.spin.spin_hamiltonian(lattice=lattice)
(
0.5 * (X(0) @ X(1))
+ 0.5 * (X(2) @ X(3))
+ 0.5 * (X(4) @ X(5))
+ 0.5 * (X(6) @ X(7))
+ 0.6 * (Y(1) @ Y(2))
+ 0.6 * (Y(5) @ Y(6))
+ 0.7 * (Z(1) @ Z(4))
+ 0.7 * (Z(3) @ Z(6))
+ 0.5 * X(0)
+ 0.3 * Y(1)
)
"""
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`"
)

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]
op1, op2 = edge[2][0]
coeff = edge[2][1]

hamiltonian += coeff * (opmap[op1](v1) @ opmap[op2](v2))

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()
53 changes: 53 additions & 0 deletions tests/spin/test_lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,59 @@ 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"),
[
(
[[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 nodes are added as per custom_nodes 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 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)


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]
Expand Down
Loading

0 comments on commit 019c0f9

Please sign in to comment.