Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions qdp/qdp-python/qumat_qdp/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,31 @@ def __init__(self, device_id: int = 0) -> None:
self._backend_name: str = "rust"

def qubits(self, n: int) -> QdpBenchmark:
"""Set the number of qubits for benchmarked encodings.

:param n: Number of qubits in each encoded output state.
:returns: This builder for fluent chaining.
"""
self._num_qubits = n
return self

def encoding(self, method: str) -> QdpBenchmark:
"""Set the encoding method to benchmark.

:param method: Encoding name, for example ``"amplitude"``,
``"angle"``, ``"basis"``, ``"iqp"``, or ``"iqp-z"``.
:returns: This builder for fluent chaining.
"""
self._encoding_method = method
return self

def batches(self, total: int, size: int = 64) -> QdpBenchmark:
"""Set the number and size of benchmark batches.

:param total: Number of timed batches to process.
:param size: Number of vectors in each batch.
:returns: This builder for fluent chaining.
"""
self._total_batches = total
self._batch_size = size
return self
Expand All @@ -119,6 +136,11 @@ def prefetch(self, n: int) -> QdpBenchmark:
return self

def warmup(self, n: int) -> QdpBenchmark:
"""Set the number of warmup batches run before timing.

:param n: Number of batches to execute before measurements begin.
:returns: This builder for fluent chaining.
"""
self._warmup_batches = n
return self

Expand Down
10 changes: 10 additions & 0 deletions qdp/qdp-python/qumat_qdp/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,14 @@ def encode(
num_qubits: int,
encoding_method: str = "amplitude",
) -> Any:
"""Encode input samples with the configured backend.

:param data: Input samples accepted by the selected backend.
:param num_qubits: Number of qubits in the output state vector.
:param encoding_method: Encoding strategy, such as ``"amplitude"``,
``"angle"``, ``"basis"``, ``"iqp"``, ``"iqp-z"``, or
``"phase"`` when supported by the backend.
:returns: Backend-native encoded tensor or tensor-like result.
:raises ValueError: If the backend does not support ``encoding_method``.
"""
return self._engine_adapter.encode(data, num_qubits, encoding_method)
19 changes: 19 additions & 0 deletions qdp/qdp-python/qumat_qdp/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ class QdpTensor:
backend: str

def __dlpack__(self, stream: int | None = None) -> Any:
"""Return a DLPack capsule for the wrapped backend tensor.

:param stream: Optional consumer stream to pass through to the wrapped
tensor's ``__dlpack__`` implementation.
:returns: A DLPack capsule representing ``value``.
:raises RuntimeError: If the wrapped value does not implement
``__dlpack__``.
"""
if not hasattr(self.value, "__dlpack__"):
raise RuntimeError(
f"Backend '{self.backend}' returned object without __dlpack__ support: "
Expand All @@ -40,6 +48,12 @@ def __dlpack__(self, stream: int | None = None) -> Any:
return self.value.__dlpack__(stream=stream)

def __dlpack_device__(self) -> Any:
"""Return the DLPack device descriptor for the wrapped tensor.

:returns: The ``(device_type, device_id)`` tuple reported by ``value``.
:raises RuntimeError: If the wrapped value does not implement
``__dlpack_device__``.
"""
if not hasattr(self.value, "__dlpack_device__"):
raise RuntimeError(
f"Backend '{self.backend}' returned object without __dlpack_device__ support: "
Expand All @@ -48,6 +62,11 @@ def __dlpack_device__(self) -> Any:
return self.value.__dlpack_device__()

def to_torch(self) -> Any:
"""Convert the wrapped tensor to a PyTorch tensor via DLPack.

:returns: A ``torch.Tensor`` sharing storage with the backend tensor
when the backend's DLPack producer supports zero-copy exchange.
"""
import torch

return torch.from_dlpack(self)
Expand Down
63 changes: 63 additions & 0 deletions qdp/qdp-python/qumat_qdp/triton_amd.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ def _is_rocm_runtime() -> bool:


def is_triton_amd_available() -> bool:
"""Return whether the Triton AMD backend appears usable.

:returns: ``True`` when PyTorch reports a ROCm device, Triton imports, and
the active Triton target is HIP or cannot be queried reliably.
"""
if not _is_rocm_runtime() or triton_mod is None:
return False
try:
Expand Down Expand Up @@ -148,6 +153,10 @@ def __post_init__(self) -> None:
)

def check_runtime(self) -> None:
"""Validate that the process can use the Triton AMD backend.

:raises RuntimeError: If PyTorch ROCm support or Triton is unavailable.
"""
if not _is_rocm_runtime():
raise RuntimeError(
"Triton AMD backend unavailable: no PyTorch ROCm device detected."
Expand Down Expand Up @@ -230,6 +239,14 @@ def _pair_indices(self, num_qubits: int) -> Any:
return pairs

def encode_amplitude(self, data: Any, num_qubits: int) -> Any:
"""Encode real-valued samples as normalized amplitudes.

:param data: One- or two-dimensional samples with width at most
``2**num_qubits``.
:param num_qubits: Number of qubits in the encoded state.
:returns: Complex tensor of shape ``(batch, 2**num_qubits)``.
:raises ValueError: If the sample width exceeds the state length.
"""
torch_mod = self._require_torch()
x = self._to_2d(data, dtype=self._real_dtype())
batch, sample_size = x.shape
Expand All @@ -250,6 +267,14 @@ def encode_amplitude(self, data: Any, num_qubits: int) -> Any:
return amp.to(self._complex_dtype())

def encode_angle(self, data: Any, num_qubits: int) -> Any:
"""Encode samples with product-state angle encoding.

:param data: One- or two-dimensional angle samples with exactly
``num_qubits`` values per sample.
:param num_qubits: Number of qubits in the encoded state.
:returns: Complex tensor of shape ``(batch, 2**num_qubits)``.
:raises ValueError: If the sample width is not ``num_qubits``.
"""
torch_mod = self._require_torch()
real_dtype = self._real_dtype()
angles = self._to_2d(data, dtype=real_dtype)
Expand All @@ -273,6 +298,14 @@ def encode_angle(self, data: Any, num_qubits: int) -> Any:
return amp.to(self._complex_dtype())

def encode_basis(self, data: Any, num_qubits: int) -> Any:
"""Encode integer basis-state indices as one-hot quantum states.

:param data: One-dimensional indices or a two-dimensional column of
indices in ``[0, 2**num_qubits - 1]``.
:param num_qubits: Number of qubits in the encoded state.
:returns: Complex one-hot tensor of shape ``(batch, 2**num_qubits)``.
:raises ValueError: If indices are empty, malformed, or out of range.
"""
torch_mod = self._require_torch()
idx = torch_mod.as_tensor(data, device=self._device(), dtype=torch_mod.int64)
if idx.ndim == 2:
Expand Down Expand Up @@ -352,6 +385,17 @@ def encode_iqp(
*,
enable_zz: bool = True,
) -> Any:
"""Encode samples with the IQP feature map.

:param data: IQP parameters. With ``enable_zz=True``, each sample must
contain ``num_qubits + num_qubits * (num_qubits - 1) // 2`` values;
otherwise each sample must contain ``num_qubits`` values.
:param num_qubits: Number of qubits in the encoded state.
:param enable_zz: Include pairwise ZZ interactions when ``True``.
:returns: Complex tensor of shape ``(batch, 2**num_qubits)``.
:raises ValueError: If the parameter width does not match the IQP
variant.
"""
torch_mod = self._require_torch()
real_dtype = self._real_dtype()
params = self._to_2d(data, dtype=real_dtype)
Expand Down Expand Up @@ -443,6 +487,14 @@ def _encode_phase_triton(self, phases: Any, num_qubits: int) -> Any:
return out

def encode_phase(self, data: Any, num_qubits: int) -> Any:
"""Encode samples as equal-magnitude states with data-dependent phase.

:param data: One- or two-dimensional phase samples with exactly
``num_qubits`` values per sample.
:param num_qubits: Number of qubits in the encoded state.
:returns: Complex tensor of shape ``(batch, 2**num_qubits)``.
:raises ValueError: If the sample width is not ``num_qubits``.
"""
torch_mod = self._require_torch()
real_dtype = self._real_dtype()
phases = self._to_2d(data, dtype=real_dtype)
Expand Down Expand Up @@ -473,6 +525,17 @@ def encode(
num_qubits: int,
encoding_method: str = "amplitude",
) -> Any:
"""Encode input samples using a named Triton AMD encoding.

:param data: Input samples for the selected encoding method.
:param num_qubits: Number of qubits in the encoded state.
:param encoding_method: One of ``"amplitude"``, ``"angle"``,
``"basis"``, ``"iqp"``, ``"iqp-z"``, or ``"phase"``.
:returns: Complex tensor of shape ``(batch, 2**num_qubits)``.
:raises RuntimeError: If the Triton AMD runtime is unavailable.
:raises ValueError: If ``encoding_method`` is unsupported or inputs are
invalid for the selected encoder.
"""
self.check_runtime()

method = encoding_method.lower()
Expand Down
Loading