@@ -167,8 +167,11 @@ def predict(
167167 Used for inference only; weights are not touched.
168168 datamodule : DataModule
169169 Test split if present, else validation, else train.
170- return_format : {"auto", "numpy", "torch"}, default "auto"
171- ``"auto"`` follows whatever the model emits.
170+ return_format : {"auto", "numpy", "torch", "pennylane"}, default "auto"
171+ ``"auto"`` follows whatever the model emits. ``"pennylane"``
172+ returns a ``pennylane.numpy.tensor`` rather than a bare
173+ ``ndarray``, for callers composing the result into further
174+ pnp-based code (a custom cost function, ``utils.diagnostic``).
172175
173176 Returns
174177 -------
@@ -292,14 +295,30 @@ def _collect(all_preds: list, return_format: str):
292295 return torch .cat (
293296 [p if _is_torch (p ) else torch .as_tensor (p ) for p in all_preds ], dim = 0
294297 )
298+ elif target in ("numpy" , "pennylane" ):
299+ # Both share this merge: a batch can be a torch tensor, a pnp
300+ # tensor or a mid-trace ArrayBox depending on backend, and
301+ # "pennylane" only needs one extra wrap over the merged result
302+ # rather than repeating that per-batch normalisation itself.
303+ merged = np .concatenate (
304+ [
305+ p .detach ().cpu ().numpy () if _is_torch (p ) else np .asarray (p )
306+ for p in all_preds
307+ ],
308+ axis = 0 ,
309+ )
295310
296- return np .concatenate (
297- [
298- p .detach ().cpu ().numpy () if _is_torch (p ) else np .asarray (p )
299- for p in all_preds
300- ],
301- axis = 0 ,
302- )
311+ if target == "pennylane" :
312+ import pennylane .numpy as pnp
313+
314+ return pnp .array (merged , requires_grad = False )
315+
316+ return merged
317+ else :
318+ raise ValueError (
319+ f"Unknown return_format { return_format !r} ; expected one of "
320+ "'auto', 'numpy', 'torch', 'pennylane'."
321+ )
303322
304323 def __repr__ (self ) -> str :
305324 return (
0 commit comments