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

[dyn-alloc-base] Lightning dynamically allocate qubit memory #1043

Merged
merged 88 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
69e6269
lightning qubit can change numqubits
josephleekl Jan 21, 2025
5d6ddd7
Auto update version from '0.41.0-dev5' to '0.41.0-dev6'
ringo-but-quantum Jan 21, 2025
fc6a9e6
LQ support updating numqubit
josephleekl Jan 22, 2025
3cb66a3
LQ dyn
josephleekl Jan 22, 2025
ee62471
remove print statements
josephleekl Jan 22, 2025
4835b1f
add test and format
josephleekl Jan 22, 2025
f34bf83
style fix
josephleekl Jan 22, 2025
2f1d0ce
update docstring
josephleekl Jan 22, 2025
550cbaf
update
josephleekl Jan 23, 2025
d0d5662
restore lightning_base
josephleekl Jan 23, 2025
7c32b82
update
josephleekl Jan 23, 2025
3863001
Auto update version from '0.41.0-dev6' to '0.41.0-dev7'
ringo-but-quantum Jan 23, 2025
3e7864d
revert arg
josephleekl Jan 23, 2025
bc85851
Merge branch 'master' into dyn-alloc
josephleekl Jan 23, 2025
55fa41e
update test
josephleekl Jan 23, 2025
04be8ed
num_qubit property
josephleekl Jan 23, 2025
3f853de
property
josephleekl Jan 23, 2025
9e5225e
update doc
josephleekl Jan 23, 2025
b454039
add tests
josephleekl Jan 24, 2025
6f9e173
amintor comments
josephleekl Jan 24, 2025
43b3c0f
refactor
josephleekl Jan 24, 2025
a8e75d7
format
josephleekl Jan 24, 2025
05fbfa9
update changelog
josephleekl Jan 24, 2025
900c6e5
update doc
josephleekl Jan 24, 2025
97e40fc
Merge branch 'master' into dyn-alloc
josephleekl Jan 24, 2025
544da03
Auto update version from '0.41.0-dev7' to '0.41.0-dev8'
ringo-but-quantum Jan 24, 2025
142e6ec
make abstract
josephleekl Jan 24, 2025
3f3e532
make abstract
josephleekl Jan 24, 2025
bbf2cae
update test
josephleekl Jan 24, 2025
b786c77
remove method
josephleekl Jan 27, 2025
96ea5bf
add vjp test
josephleekl Jan 27, 2025
88a1427
restrict tests to LQ
josephleekl Jan 27, 2025
dd41826
Merge branch 'master' into dyn-alloc
josephleekl Jan 27, 2025
54707e9
Auto update version from '0.41.0-dev9' to '0.41.0-dev10'
ringo-but-quantum Jan 27, 2025
b522966
remove abstract method
josephleekl Jan 28, 2025
ff23e4c
Merge branch 'master' into dyn-alloc
josephleekl Jan 29, 2025
390e1ab
Auto update version from '0.41.0-dev14' to '0.41.0-dev15'
ringo-but-quantum Jan 29, 2025
9d1197d
no longer update numqubit
josephleekl Jan 30, 2025
5edbbba
update
josephleekl Jan 30, 2025
fff47ed
remove updatenumqubit test
josephleekl Jan 30, 2025
d51f3ac
add abstract class
josephleekl Jan 31, 2025
c70eb9e
format
josephleekl Jan 31, 2025
23b9f63
update docstring
josephleekl Jan 31, 2025
aaaa8f9
update docstring
josephleekl Jan 31, 2025
28e8230
update method name
josephleekl Jan 31, 2025
d6fc59c
update method name
josephleekl Jan 31, 2025
65ea81c
update method name
josephleekl Jan 31, 2025
59b2e73
update doc
josephleekl Jan 31, 2025
52baf08
update github workflow CI get LQ coverage
josephleekl Jan 31, 2025
d3fcf02
trigger CI
josephleekl Jan 31, 2025
d27cf53
Merge branch 'master' into dyn-alloc
josephleekl Feb 4, 2025
b8cdbc8
revert workflow
josephleekl Feb 4, 2025
e871faa
Auto update version from '0.41.0-dev16' to '0.41.0-dev17'
ringo-but-quantum Feb 4, 2025
b58d75a
apply comments
josephleekl Feb 4, 2025
d53fd8e
split lines
josephleekl Feb 4, 2025
9049fc7
add test
josephleekl Feb 4, 2025
f5cf369
update tests
josephleekl Feb 5, 2025
8e1f3b6
update tests
josephleekl Feb 5, 2025
7e0ec90
update test condition
josephleekl Feb 5, 2025
9024218
Merge branch 'master' into dyn-alloc
josephleekl Feb 5, 2025
f50d3e2
Auto update version from '0.41.0-dev17' to '0.41.0-dev18'
ringo-but-quantum Feb 5, 2025
4f54e54
Merge branch 'master' into dyn-alloc
josephleekl Feb 6, 2025
a7122a4
Ali comments
josephleekl Feb 6, 2025
17d9a5c
last minute allocation
josephleekl Feb 6, 2025
030b2cc
Auto update version from '0.41.0-dev18' to '0.41.0-dev19'
ringo-but-quantum Feb 6, 2025
e217dc3
update docstring
josephleekl Feb 6, 2025
3f58f4f
[dyn-alloc-lk] Lightning Kokkos support dynamic qubit allocation (#1049)
josephleekl Feb 6, 2025
9e24bdf
Auto update version from '0.41.0-dev19' to '0.41.0-dev20'
ringo-but-quantum Feb 6, 2025
7721f53
update docstring
josephleekl Feb 6, 2025
d40c697
update docstring
josephleekl Feb 6, 2025
ede0dd2
fix LG
josephleekl Feb 6, 2025
5fbe59b
Merge branch 'master' into dyn-alloc
josephleekl Feb 6, 2025
3911618
update base
josephleekl Feb 6, 2025
7ff2bfe
fix tests
josephleekl Feb 7, 2025
aec3682
simplify
josephleekl Feb 7, 2025
1595fc9
gpu tests fail updates
josephleekl Feb 7, 2025
d74af9b
LK config later
josephleekl Feb 7, 2025
c7343c4
Update mpitests/test_device.py
josephleekl Feb 7, 2025
e431617
use self.dev_mpi in mpi test_apply
josephleekl Feb 7, 2025
44d0e87
format
josephleekl Feb 7, 2025
78baa7b
remove _mpi and _mpi_buf_size
josephleekl Feb 7, 2025
b1c5ad9
undo self.dev_mpi
josephleekl Feb 7, 2025
2fac162
format
josephleekl Feb 7, 2025
cfa5dac
recreate mpi_handler none
josephleekl Feb 7, 2025
4da92dd
pylint
josephleekl Feb 7, 2025
8677890
ali comments
josephleekl Feb 10, 2025
ea427d8
mpitest self
josephleekl Feb 10, 2025
5586c12
remove duplicated _statevector
josephleekl Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

### Improvements

* Lightning devices support dynamically allocated wires (e.g. `qml.device("lightning.qubit")`)
[(#1043)](https://github.com/PennyLaneAI/pennylane-lightning/pull/1043)

* Remove the old device API references in the Lightning repo and test suite.
[(#1057)](https://github.com/PennyLaneAI/pennylane-lightning/pull/1057)

Expand Down
4 changes: 1 addition & 3 deletions mpitests/test_apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def circuit():
assert np.allclose(local_state_vector, local_expected_output_cpu, atol=tol, rtol=0)


class TestApply: # pylint: disable=missing-function-docstring,too-many-arguments
class TestApply: # pylint: disable=missing-function-docstring,too-many-arguments,too-many-positional-arguments
"""Tests whether the device can apply supported quantum gates."""

@pytest.fixture(params=fixture_params)
Expand Down Expand Up @@ -371,8 +371,6 @@ def test_dev_reset(self, tol, dev_mpi):
comm.Scatter(state_vector, local_state_vector, root=0)
dev_cpu = qml.device("lightning.qubit", wires=num_wires, c_dtype=c_dtype)

dev_cpu._statevector.reset_state()

def circuit():
qml.PauliX(wires=[0])
qml.PauliX(wires=[0])
Expand Down
56 changes: 56 additions & 0 deletions mpitests/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,62 @@ def test_create_device():
dev = qml.device(device_name, mpi=True, wires=4)


def test_unsupported_dynamic_wires():
with pytest.raises(
DeviceError,
match="does not support dynamic wires allocation.",
):
dev = qml.device(device_name, mpi=True)


@pytest.mark.parametrize(
"circuit_in, n_wires, wires_list",
[
(
QuantumScript(
[
qml.RX(0.1, 0),
qml.CNOT([1, 0]),
qml.RZ(0.1, 1),
qml.CNOT([2, 1]),
],
[qml.expval(qml.Z(0))],
),
3,
[0, 1, 2],
),
(
QuantumScript(
[
qml.RX(0.1, 0),
qml.CNOT([1, 4]),
qml.RZ(0.1, 4),
qml.CNOT([2, 1]),
],
[qml.expval(qml.Z(6))],
),
7,
[0, 1, 4, 2, 6],
),
],
)
def test_dynamic_wires_from_circuit_fixed_wires(circuit_in, n_wires, wires_list):
"""Test that dynamic_wires_from_circuit creates correct statevector and circuit."""
dev = qml.device(device_name, mpi=True, wires=n_wires)
circuit_out = dev.dynamic_wires_from_circuit(circuit_in)

assert circuit_out.num_wires == circuit_in.num_wires
assert circuit_out.wires == qml.wires.Wires(wires_list)
assert circuit_out.operations == circuit_in.operations
assert circuit_out.measurements == circuit_in.measurements

assert dev._statevector._mpi_handler.use_mpi
assert (
dev._statevector._mpi_handler.num_local_wires
+ dev._statevector._mpi_handler.num_global_wires
) == n_wires


def test_unsupported_mpi_buf_size():
with pytest.raises(ValueError, match="Unsupported mpi_buf_size value"):
dev = qml.device(device_name, mpi=True, wires=4, mpi_buf_size=-1)
Expand Down
2 changes: 1 addition & 1 deletion pennylane_lightning/core/_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.41.0-dev19"
__version__ = "0.41.0-dev20"
55 changes: 47 additions & 8 deletions pennylane_lightning/core/lightning_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class LightningBase(Device):
A class that serves as a base class for Lightning simulators.

Args:
wires (int or list): number or list of wires to initialize the device with
wires (Optional[int, list]): number or list of wires to initialize the device with. Defaults to ``None`` if not specified, and the device will allocate the number of wires depending on the circuit to execute.
sync (bool): immediately sync with host after applying operations on the device
c_dtype: Datatypes for statevector representation. Must be one of
``np.complex64`` or ``np.complex128``.
Expand All @@ -59,7 +59,7 @@ class LightningBase(Device):

def __init__( # pylint: disable=too-many-arguments
self,
wires: Union[int, List],
wires: Union[int, List] = None,
*,
c_dtype: Union[np.complex64, np.complex128],
shots: Union[int, List],
Expand All @@ -70,7 +70,10 @@ def __init__( # pylint: disable=too-many-arguments
self._c_dtype = c_dtype
self._batch_obs = batch_obs

if isinstance(wires, int):
# State-vector is dynamically allocated just before execution
self._statevector = None

if isinstance(wires, int) or wires is None:
self._wire_map = None # should just use wires as is
else:
self._wire_map = {w: i for i, w in enumerate(self.wires)}
Expand Down Expand Up @@ -102,6 +105,17 @@ def _setup_execution_config(self, config: ExecutionConfig):

"""

@abstractmethod
def dynamic_wires_from_circuit(self, circuit):
"""Allocate the underlying quantum state from the pre-defined wires or a given circuit if applicable. Circuit wires will be mapped to Pennylane ``default.qubit`` standard wire order.

Args:
circuit (QuantumTape): The circuit to execute.

Returns:
QuantumTape: The updated circuit with the wires mapped to the standard wire order.
"""

@abstractmethod
def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig):
"""This function defines the device transform program to be applied and an updated device configuration.
Expand Down Expand Up @@ -316,7 +330,12 @@ def compute_derivatives(
batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs)

return tuple(
self.jacobian(circuit, self._statevector, batch_obs=batch_obs, wire_map=self._wire_map)
self.jacobian(
self.dynamic_wires_from_circuit(circuit),
self._statevector,
batch_obs=batch_obs,
wire_map=self._wire_map,
)
for circuit in circuits
)

Expand All @@ -337,9 +356,12 @@ def execute_and_compute_derivatives(
batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs)
results = tuple(
self.simulate_and_jacobian(
c, self._statevector, batch_obs=batch_obs, wire_map=self._wire_map
self.dynamic_wires_from_circuit(circuit),
self._statevector,
batch_obs=batch_obs,
wire_map=self._wire_map,
)
for c in circuits
for circuit in circuits
)
return tuple(zip(*results))

Expand Down Expand Up @@ -392,7 +414,13 @@ def compute_vjp(
"""
batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs)
return tuple(
self.vjp(circuit, cots, self._statevector, batch_obs=batch_obs, wire_map=self._wire_map)
self.vjp(
self.dynamic_wires_from_circuit(circuit),
cots,
self._statevector,
batch_obs=batch_obs,
wire_map=self._wire_map,
)
for circuit, cots in zip(circuits, cotangents)
)

Expand All @@ -415,7 +443,11 @@ def execute_and_compute_vjp(
batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs)
results = tuple(
self.simulate_and_vjp(
circuit, cots, self._statevector, batch_obs=batch_obs, wire_map=self._wire_map
self.dynamic_wires_from_circuit(circuit),
cots,
self._statevector,
batch_obs=batch_obs,
wire_map=self._wire_map,
)
for circuit, cots in zip(circuits, cotangents)
)
Expand Down Expand Up @@ -460,6 +492,13 @@ def loop(i, y):
# has jax dependency, so can't import up top
from .lightning_interpreter import LightningInterpreter

if self.wires is None:
raise NotImplementedError("Wires must be specified for integration with plxpr capture.")

self._statevector = self.LightningStateVector(
num_wires=len(self.wires), dtype=self._c_dtype
)

interpreter = LightningInterpreter(
self._statevector, self.LightningMeasurements, shots=self.shots
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,4 @@ template <class PrecisionT, class Derived> class StateVectorBase {
}
};

} // namespace Pennylane
} // namespace Pennylane
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,4 @@ class StateVectorLQubitRaw final
updateData(new_data.data(), new_data.size());
}
};
} // namespace Pennylane::LightningQubit
} // namespace Pennylane::LightningQubit
62 changes: 49 additions & 13 deletions pennylane_lightning/lightning_gpu/lightning_gpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@
:doc:`/lightning_gpu/installation` guide for more details.

Args:
wires (int): the number of wires to initialize the device with
wires (Optional[int, list]): the number of wires to initialize the device with. Defaults to ``None`` if not specified, and the device will allocate the number of wires depending on the circuit to execute.
c_dtype: Datatypes for statevector representation. Must be one of
``np.complex64`` or ``np.complex128``.
shots (int): How many times the circuit should be evaluated (or sampled) to estimate
Expand Down Expand Up @@ -235,7 +235,7 @@

def __init__( # pylint: disable=too-many-arguments
self,
wires: Union[int, List],
wires: Union[int, List] = None,
*,
c_dtype: Union[np.complex128, np.complex64] = np.complex128,
shots: Union[int, List] = None,
Expand Down Expand Up @@ -268,15 +268,22 @@
self._dp = DevPool()
self._use_async = use_async

# Creating the state vector
self._mpi_handler = MPIHandler(mpi, mpi_buf_size, len(self.wires), c_dtype)

self._statevector = self.LightningStateVector(
num_wires=len(self.wires),
dtype=c_dtype,
mpi_handler=self._mpi_handler,
use_async=self._use_async,
)
# Create the state vector only for MPI, otherwise created dynamically before execution
if mpi:
if wires is None:
raise qml.DeviceError(
"Lightning-GPU-MPI does not support dynamic wires allocation."
)
self._mpi_handler = MPIHandler(mpi, mpi_buf_size, len(self.wires), c_dtype)
self._statevector = self.LightningStateVector(
num_wires=len(self.wires),
dtype=c_dtype,
mpi_handler=self._mpi_handler,
use_async=self._use_async,
)
else:
self._statevector = None
self._mpi_handler = None

@property
def name(self):
Expand Down Expand Up @@ -316,6 +323,35 @@

return replace(config, **updated_values, device_options=new_device_options)

def dynamic_wires_from_circuit(self, circuit):
"""Allocate a state-vector from the pre-defined wires or a given circuit if applicable. Circuit wires will be mapped to Pennylane ``default.qubit`` standard wire order.

Args:
circuit (QuantumTape): The circuit to execute.

Returns:
QuantumTape: The updated circuit with the wires mapped to the standard wire order.
"""
if self._mpi_handler and self._mpi_handler.use_mpi:
return circuit

if self.wires is None:
num_wires = circuit.num_wires

Check warning on line 339 in pennylane_lightning/lightning_gpu/lightning_gpu.py

View check run for this annotation

Codecov / codecov/patch

pennylane_lightning/lightning_gpu/lightning_gpu.py#L339

Added line #L339 was not covered by tests
# Map to follow default.qubit wire order for dynamic wires
circuit = circuit.map_to_standard_wires()

Check warning on line 341 in pennylane_lightning/lightning_gpu/lightning_gpu.py

View check run for this annotation

Codecov / codecov/patch

pennylane_lightning/lightning_gpu/lightning_gpu.py#L341

Added line #L341 was not covered by tests
else:
num_wires = len(self.wires)

if (self._statevector is None) or (self._statevector.num_wires != num_wires):
self._statevector = self.LightningStateVector(
num_wires=num_wires,
dtype=self._c_dtype,
mpi_handler=None,
use_async=self._use_async,
)

return circuit

def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig):
"""This function defines the device transform program to be applied and an updated device configuration.

Expand Down Expand Up @@ -379,7 +415,7 @@
[circuit], _ = qml.map_wires(circuit, self._wire_map)
results.append(
self.simulate(
circuit,
self.dynamic_wires_from_circuit(circuit),
self._statevector,
postselect_mode=execution_config.mcm_config.postselect_mode,
)
Expand Down Expand Up @@ -433,7 +469,7 @@
Note that this function can return measurements for non-commuting observables simultaneously.
"""
if circuit.shots and (any(isinstance(op, MidMeasureMP) for op in circuit.operations)):
if self._mpi_handler.use_mpi:
if self._mpi_handler and self._mpi_handler.use_mpi:
raise qml.DeviceError(
"Lightning-GPU-MPI does not support Mid-circuit measurements."
)
Expand Down
Loading
Loading