Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

End-To-End Sparsity Support on default.qubit #6883

Draft
wants to merge 13 commits into
base: `QubitUnitary`-accepts-sparse-matrices-as-inputs
Choose a base branch
from

Conversation

JerryChen97
Copy link
Contributor

@JerryChen97 JerryChen97 commented Jan 24, 2025

Context:
Based on the successful implementations of StatePrep and QubitUnitary, we would like to implement the End-to-End support for the whole pipeline of default.qubit (abbr. DQ) here.

We expect the following code sample to be working properly as naturally expected by a user who knows basic sparse matrix knowledge especially in SciPy,

@qml.qnode(qml.device('default.qubit'))
def circuit(state, mat):
    qml.StatePrep(state, wires=range(8))
    qml.QubitUnitary(mat, wires=range(8))
    return qml.expval(qml.Z(0))

ground_state = scipy.sparse.csr_array((np.array([1]), (np.array([0]), np.array([0]))) shape=(2**8,1))
mat = scipy.sparse.eye(2**8)
circuit(ground_state, mat)

To understand how this would be implemented, let's recall the whole DQ pipeline:
In the default-qubit device, the simulate module organizes the entire execution pipeline for a quantum circuit. Its core function, simulate.py::simulate(), does two main things:

Calls get_final_state to handle state initialization (create_initial_state) and the application of each operator (apply_operation). Any relevant preprocessing or postprocessing (e.g., mid-circuit measurement) is performed here.
Calls measure_final_state to produce measurement results. Depending on the circuit or device configuration, it may branch into analytic or sampling-based measurement.

Description of the Change:
All the following implementations should be within default.qubit

  • initial_state correctly accepts and present the StatePrep
  • apply_operation has a separate single dispatch for csr_matrix and correctly process the op-state multiplication
  • measure
  • simulate
  • At least one integration test

Benefits:
We are able to use at least the simplest samples of sparse matrices in PL circuits.

Possible Drawbacks:
Slightly higher complexity within current pipeline.

Related GitHub Issues:
[sc-83126]

@JerryChen97 JerryChen97 added enhancement ✨ New feature or request WIP 🚧 Work-in-progress labels Jan 24, 2025
@JerryChen97 JerryChen97 self-assigned this Jan 24, 2025
Copy link
Contributor

Hello. You may have forgotten to update the changelog!
Please edit doc/releases/changelog-dev.md with:

  • A one-to-two sentence description of the change. You may include a small working example for new features.
  • A link back to this PR.
  • Your name (or GitHub username) in the contributors section.

@JerryChen97 JerryChen97 changed the base branch from master to `StatePrep`-accepts-sparse-matrices-as-input January 24, 2025 20:41
Base automatically changed from `StatePrep`-accepts-sparse-matrices-as-input to master February 3, 2025 23:10
@JerryChen97 JerryChen97 changed the base branch from master to `QubitUnitary`-accepts-sparse-matrices-as-inputs February 4, 2025 19:53
@JerryChen97 JerryChen97 marked this pull request as ready for review February 5, 2025 23:01
@JerryChen97
Copy link
Contributor Author

Trigger CI for testing apply operation. Still WIP

Copy link

codecov bot commented Feb 5, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 99.59%. Comparing base (53a9de4) to head (84c2440).
Report is 4 commits behind head on QubitUnitary-accepts-sparse-matrices-as-inputs.

Additional details and impacted files
@@                                Coverage Diff                                @@
##           `QubitUnitary`-accepts-sparse-matrices-as-inputs    #6883   +/-   ##
=================================================================================
  Coverage                                             99.59%   99.59%           
=================================================================================
  Files                                                   480      480           
  Lines                                                 45493    45530   +37     
=================================================================================
+ Hits                                                  45308    45345   +37     
  Misses                                                  185      185           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@JerryChen97
Copy link
Contributor Author

It seems that, if we are ok with state being dense, right now it's already good enough...

import pennylane as qml
import numpy as np
from scipy.sparse import csr_matrix, kron
from pennylane.devices.qubit.apply_operation import apply_operation

# 1. Simple sparse state setup
v = csr_matrix([0, 1, 2, 3, 0, 0, 0, 0])
wires = [2, 0, 1]

# 2. Test state preparation
stateprep = qml.StatePrep(v, wires=wires, normalize=True)
state_vec = stateprep.state_vector(wire_order=wires)
state_complex = state_vec.astype(complex)
print("Prepared state:", state_complex)

# 3. Single qubit operation test
op = qml.QubitUnitary(csr_matrix([[0, 1], [1, 0]]), wires=[0])
result = apply_operation(op, state_complex, 0, None)
print("After operation:", result)

# 4. Large system test (N=14 qubits)
N = 20
large_state = csr_matrix(np.random.rand(2**N))

# 5. Device execution
dev = qml.device("default.qubit", wires=N)


@qml.qnode(dev)
def circuit(v):
    qml.StatePrep(v, wires=range(N), normalize=True)
    qml.QubitUnitary(op.matrix(range(N)), wires=range(N))
    return qml.expval(qml.PauliZ(0))


result = circuit(large_state.toarray())
# result = circuit(large_state)
print("Device measurement:", result)

# 6. Larger randome Unitary
I = np.eye(2)
X = np.array([[0, 1], [1, 0]])
Y = np.array([[0, -1j], [1j, 0]])
Z = np.array([[1, 0], [0, -1]])

U_list = [I, X, Y, Z]
U_list = [csr_matrix(op) for op in U_list]
# Pick random indices, then choose unitaries
indices = np.random.choice(len(U_list), size=N)
Us = [U_list[idx] for idx in indices]

U = Us[0]
for i in range(1, N):
    U = kron(U, Us[i], format="csr")
print(U.shape)


@qml.qnode(dev)
def circuit(v):
    qml.StatePrep(v, wires=range(N), normalize=True)
    qml.QubitUnitary(U, wires=range(N))
    return qml.expval(qml.PauliZ(0))


result = circuit(large_state.toarray())
# result = circuit(large_state)
print("Device measurement:", result)

@JerryChen97 JerryChen97 marked this pull request as draft February 6, 2025 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement ✨ New feature or request WIP 🚧 Work-in-progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant