Skip to content

[BUG] default.clifford(tableau=False) returns a probability vector for qml.expval(qml.Projector(...)) #9670

Description

@YujinSong-hep

Expected behavior

qml.expval(qml.Projector(state, wires=...)) should return the scalar probability of projecting
the current quantum state onto the specified target state.

The Projector documentation states:

The expectation of this observable returns the value
:math:|\langle\psi|\phi\rangle|^2, corresponding to the probability that
:math:|\psi\rangle is projected onto :math:|\phi\rangle.

For the Bell state

|psi> = (|00> + |11>) / sqrt(2)

the expectation value of Projector([0, 0], wires=[0, 1]) should therefore be the scalar
probability 0.5.

The tableau option of default.clifford should not change this measurement's semantics. Its
documentation says that tableau only determines what is returned when the state is requested
with qml.state():

  • tableau=True: qml.state() returns the evolved Clifford tableau.
  • tableau=False: qml.state() returns the evolved state vector.

Therefore, qml.expval(qml.Projector(...)) should return the same scalar expectation value under
both settings.

Actual behavior

With tableau=True, default.clifford correctly returns the scalar 0.5.

With tableau=False, the same Projector expectation returns the complete computational-basis
probability vector:

[0.49999998 0.         0.         0.49999998]

The measurement result consequently has shape (4,), even though an expectation measurement
declares scalar shape ().

default.qubit:
  result: 0.4999999999999999
  shape:  ()

default.clifford(tableau=True):
  result: 0.5
  shape:  ()

default.clifford(tableau=False):
  result: [0.49999998 0.         0.         0.49999998]
  shape:  (4,)

Additional information

The issue appears to be in the analytical BasisStateProjector expectation path in
DefaultClifford._measure_expectation.

The method correctly identifies that the expectation value of a basis-state Projector can be
computed as the probability of its target basis state:

if isinstance(meas_obs, BasisStateProjector):
    kwargs["prob_states"] = math.array([meas_obs.data[0]])
    return self._measure_probability(
        ProbabilityMP(wires=meas_obs.wires), tableau_simulator, **kwargs
    ).squeeze()

However, when tableau=False, the state-vector branch of _measure_probability returns the full
probability distribution rather than selecting the requested prob_states. The resulting
probability vector is then returned unchanged by _measure_expectation.

This does not appear to be intended tableau=False behavior:

  • The tableau docstring only describes changing the output representation of qml.state().
  • Other expectation values, such as qml.expval(qml.Z(0)), remain scalar with tableau=False.
  • The default.clifford documentation explicitly recommends
    qml.expval(qml.Projector(...)) for obtaining the probability of a single target basis state.
  • ExpectationMP.shape() returns ().

Source code

import numpy as np
import pennylane as qml


def run(device_name, tableau=None):
    kwargs = {} if tableau is None else {"tableau": tableau}
    dev = qml.device(device_name, wires=2, **kwargs)

    @qml.qnode(dev)
    def circuit():
        # Prepare (|00> + |11>) / sqrt(2)
        qml.Hadamard(0)
        qml.CNOT(wires=[0, 1])

        # The probability of projecting the Bell state onto |00> is 0.5.
        return qml.expval(qml.Projector([0, 0], wires=[0, 1]))

    result = circuit()
    return result, np.shape(result)


for name, tableau in [
    ("default.qubit", None),
    ("default.clifford", True),
    ("default.clifford", False),
]:
    result, shape = run(name, tableau)
    label = name if tableau is None else f"{name}(tableau={tableau})"

    print(f"{label}:")
    print("  result:", result)
    print("  shape: ", shape)

Tracebacks

default.qubit:
  result: 0.4999999999999999
  shape:  ()
default.clifford(tableau=True):
  result: 0.5
  shape:  ()
default.clifford(tableau=False):
  result: [0.49999998 0.         0.         0.49999998]
  shape:  (4,)

System information

Version: 0.45.0
Platform info: macOS
Python version: 3.13.13

Existing GitHub issues

  • I have searched existing GitHub issues to make sure the issue does not already exist.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🐛Something isn't workingcommunity-botIssue suspected to be found by a bot

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions