Skip to content

Commit

Permalink
Merge branch 'master' into fix-measure
Browse files Browse the repository at this point in the history
  • Loading branch information
astralcai committed Sep 19, 2024
2 parents b9ed720 + e6fe7ea commit da9e3c1
Show file tree
Hide file tree
Showing 16 changed files with 775 additions and 22 deletions.
4 changes: 4 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
# Release 0.39.0-dev (development release)

<h3>New features since last release</h3>

* 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)

<h3>Improvements 🛠</h3>

Expand Down
7 changes: 4 additions & 3 deletions pennylane/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
state,
var,
vn_entropy,
vn_entanglement_entropy,
purity,
mutual_info,
classical_shadow,
Expand Down Expand Up @@ -183,23 +184,23 @@ def __getattr__(name):
if name == "QubitDevice":
warn(
"QubitDevice will no longer be accessible top level. Please access "
" the class as pennylane.devices.QubitDevice",
"the class as pennylane.devices.QubitDevice",
PennyLaneDeprecationWarning,
)
return pennylane.devices._qubit_device.QubitDevice # pylint:disable=protected-access

if name == "QutritDevice":
warn(
"QutritDevice will no longer be accessible top level. Please access "
" the class as pennylane.devices.QutritDevice",
"the class as pennylane.devices.QutritDevice",
PennyLaneDeprecationWarning,
)
return pennylane.devices._qutrit_device.QutritDevice # pylint:disable=protected-access

if name == "Device":
warn(
"Device will no longer be accessible top level. Please access "
" the class as pennylane.devices.LegacyDevice",
"the class as pennylane.devices.LegacyDevice",
PennyLaneDeprecationWarning,
)
return pennylane.devices._legacy_device.Device # pylint:disable=protected-access
Expand Down
59 changes: 57 additions & 2 deletions pennylane/devices/_qubit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
StateMeasurement,
StateMP,
VarianceMP,
VnEntanglementEntropyMP,
VnEntropyMP,
)
from pennylane.operation import Operation, operation_derivative
Expand Down Expand Up @@ -718,6 +719,29 @@ def statistics(
)
result = self.vn_entropy(wires=obs.wires, log_base=obs.log_base)

elif isinstance(m, VnEntanglementEntropyMP):
if self.wires.labels != tuple(range(self.num_wires)):
raise qml.QuantumFunctionError(
"Returning the Von Neumann entanglement entropy is not supported when using custom wire labels"
)

if self._shot_vector is not None:
raise NotImplementedError(
"Returning the Von Neumann entanglement entropy is not supported with shot vectors."
)

if self.shots is not None:
warnings.warn(
"Requested Von Neumann entanglement entropy with finite shots; the returned "
"state information is analytic and is unaffected by sampling. To silence "
"this warning, set shots=None on the device.",
UserWarning,
)
wires0, wires1 = obs.raw_wires
result = self.vn_entanglement_entropy(
wires0=wires0, wires1=wires1, log_base=obs.log_base
)

elif isinstance(m, MutualInfoMP):
if self.wires.labels != tuple(range(self.num_wires)):
raise qml.QuantumFunctionError(
Expand Down Expand Up @@ -777,6 +801,7 @@ def statistics(
VarianceMP,
ProbabilityMP,
VnEntropyMP,
VnEntanglementEntropyMP,
MutualInfoMP,
ShadowExpvalMP,
),
Expand Down Expand Up @@ -1008,14 +1033,44 @@ def vn_entropy(self, wires, log_base):
"""
try:
state = self.density_matrix(wires=self.wires)
except qml.QuantumFunctionError as e: # pragma: no cover
except (qml.QuantumFunctionError, NotImplementedError) as e: # pragma: no cover
raise NotImplementedError(
f"Cannot compute the Von Neumman entropy with device {self.name} that is not capable of returning the "
f"state. "
) from e
wires = wires.tolist()
return qml.math.vn_entropy(state, indices=wires, c_dtype=self.C_DTYPE, base=log_base)

def vn_entanglement_entropy(self, wires0, wires1, log_base):
r"""Returns the Von Neumann entanglement entropy prior to measurement.
.. math::
S(\rho_A) = -\text{Tr}[\rho_A \log \rho_A] = -\text{Tr}[\rho_B \log \rho_B] = S(\rho_B)
Args:
wires0 (Sequence[int] or int): the wires of the first subsystem
wires1 (Sequence[int] or int): the wires of the second subsystem
log_base (float): Base for the logarithm.
Returns:
float: returns the Von Neumann entropy
"""
try:
state = self.density_matrix(wires=self.wires)
except (qml.QuantumFunctionError, NotImplementedError) as e: # pragma: no cover
raise NotImplementedError(
f"Cannot compute the Von Neumman entropy with device {self.name} that is not capable of returning the "
f"state. "
) from e

wires0 = wires0.tolist()
wires1 = wires1.tolist()

return qml.math.vn_entanglement_entropy(
state, indices0=wires0, indices1=wires1, c_dtype=self.C_DTYPE, base=log_base
)

def mutual_info(self, wires0, wires1, log_base):
r"""Returns the mutual information prior to measurement:
Expand All @@ -1035,7 +1090,7 @@ def mutual_info(self, wires0, wires1, log_base):
"""
try:
state = self.density_matrix(wires=self.wires)
except qml.QuantumFunctionError as e: # pragma: no cover
except (qml.QuantumFunctionError, NotImplementedError) as e: # pragma: no cover
raise NotImplementedError(
f"Cannot compute the mutual information with device {self.name} that is not capable of returning the "
f"state. "
Expand Down
22 changes: 22 additions & 0 deletions pennylane/devices/_qutrit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,28 @@ def vn_entropy(self, wires, log_base):
"Unsupported return type specified for observable Von Neumann entropy"
)

def vn_entanglement_entropy(self, wires0, wires1, log_base):
r"""Returns the Von Neumann entanglement entropy prior to measurement.
.. math::
S(\rho_A) = -\text{Tr}[\rho_A \log \rho_A] = -\text{Tr}[\rho_B \log \rho_B] = S(\rho_B)
Args:
wires0 (Sequence[int] or int): the wires of the first subsystem
wires1 (Sequence[int] or int): the wires of the second subsystem
log_base (float): Base for the logarithm.
Returns:
float: returns the Von Neumann entropy
"""
# TODO: Add support for VnEntanglementEntropy return type. Currently, qml.math is hard coded to calculate this for qubit
# states (see `qml.math.vn_entanglement_entropy()`), so it needs to be updated before VnEntanglementEntropy can be supported for qutrits.
# For now, if a user tries to request this return type, an error will be raised.
raise qml.QuantumFunctionError(
"Unsupported return type specified for observable Von Neumann entanglement entropy"
)

def mutual_info(self, wires0, wires1, log_base):
r"""Returns the mutual information prior to measurement:
Expand Down
10 changes: 7 additions & 3 deletions pennylane/devices/default_mixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
SampleMP,
StateMP,
VarianceMP,
VnEntanglementEntropyMP,
VnEntropyMP,
)
from pennylane.operation import Channel
Expand Down Expand Up @@ -637,6 +638,9 @@ def _snapshot_measurements(self, density_matrix, measurement):
density_matrix, indices=map_wires, c_dtype=self.C_DTYPE, base=base
)

elif isinstance(measurement, VnEntanglementEntropyMP):
snap_result = measurement.process_density_matrix(density_matrix, wire_order=self.wires)

elif isinstance(measurement, MutualInfoMP):
base = measurement.log_base
wires0, wires1 = list(map(self.map_wires, measurement.raw_wires))
Expand Down Expand Up @@ -762,9 +766,9 @@ def execute(self, circuit, **kwargs):
# not specified or all wires specified.
self.measured_wires = self.wires
return super().execute(circuit, **kwargs)
if isinstance(m, (VnEntropyMP, MutualInfoMP)):
# VnEntropy, MutualInfo: Computed for the state
# prior to measurement. So, readout error need not be applied to the
if isinstance(m, (VnEntropyMP, VnEntanglementEntropyMP, MutualInfoMP)):
# VnEntropy, VnEntanglementEntropyMP, MutualInfo: Computed for the state
# prior to measurement. So, readout error need not be applied on the
# corresponding device wires.
continue
wires_list.append(m.wires)
Expand Down
2 changes: 2 additions & 0 deletions pennylane/measurements/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def circuit(x):
StateMeasurement,
Variance,
VnEntropy,
VnEntanglementEntropy,
)
from .mid_measure import MeasurementValue, MidMeasureMP, measure, find_post_processed_mcms
from .mutual_info import MutualInfoMP, mutual_info
Expand All @@ -298,3 +299,4 @@ def circuit(x):
from .state import DensityMatrixMP, StateMP, density_matrix, state
from .var import VarianceMP, var
from .vn_entropy import VnEntropyMP, vn_entropy
from .vn_entanglement_entropy import VnEntanglementEntropyMP, vn_entanglement_entropy
4 changes: 4 additions & 0 deletions pennylane/measurements/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class ObservableReturnTypes(Enum):
State = "state"
MidMeasure = "measure"
VnEntropy = "vnentropy"
VnEntanglementEntropy = "vnentanglemententropy"
MutualInfo = "mutualinfo"
Shadow = "shadow"
ShadowExpval = "shadowexpval"
Expand Down Expand Up @@ -90,6 +91,9 @@ def __repr__(self):
VnEntropy = ObservableReturnTypes.VnEntropy
"""Enum: An enumeration which represents returning Von Neumann entropy before measurements."""

VnEntanglementEntropy = ObservableReturnTypes.VnEntanglementEntropy
"""Enum: An enumeration which represents returning Von Neumann entanglement entropy before measurements."""

MutualInfo = ObservableReturnTypes.MutualInfo
"""Enum: An enumeration which represents returning the mutual information before measurements."""

Expand Down
Loading

0 comments on commit da9e3c1

Please sign in to comment.