mblt-npu-python 0.0.2__tar.gz → 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/PKG-INFO +7 -5
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/README.md +5 -3
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu/__init__.py +1 -1
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu/npu_backend.py +36 -6
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu/npu_target.py +87 -6
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu_python.egg-info/PKG-INFO +7 -5
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu_python.egg-info/requires.txt +1 -1
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/pyproject.toml +1 -1
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/tests/test_npu_backend_multi_slot.py +3 -2
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/tests/test_target_device.py +149 -2
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/LICENSE +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu/core_mode.py +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu/logging.py +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu/onnx_backend.py +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu/pytest_plugin.py +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu_python.egg-info/SOURCES.txt +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu_python.egg-info/dependency_links.txt +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu_python.egg-info/top_level.txt +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/setup.cfg +0 -0
- {mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/tests/test_onnx_backend.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mblt-npu-python
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: Shared NPU access for the Mobilint Python packages
|
|
5
5
|
Author: Mobilint
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -18,7 +18,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
18
18
|
Requires-Python: <3.13,>=3.10
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
-
Requires-Dist: mobilint-qb-runtime>=1.
|
|
21
|
+
Requires-Dist: mobilint-qb-runtime>=1.4.0
|
|
22
22
|
Requires-Dist: huggingface-hub
|
|
23
23
|
Provides-Extra: test
|
|
24
24
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -84,9 +84,11 @@ finally:
|
|
|
84
84
|
|
|
85
85
|
`MobilintNPUBackend` selects the appropriate implementation from
|
|
86
86
|
`target_device` (default: `"aries-rb"`). `"aries-rb"` selects
|
|
87
|
-
`MobilintAriesBackend`; `"regulus-ra"
|
|
88
|
-
`MobilintRegulusBackend`. The former generic
|
|
89
|
-
remain accepted when loading older
|
|
87
|
+
`MobilintAriesBackend`; `"regulus-ra"`, `"regulus-rb"`, `"regulus-ra-usb"`,
|
|
88
|
+
and `"regulus-rb-usb"` select `MobilintRegulusBackend`. The former generic
|
|
89
|
+
values `"aries"` and `"regulus"` remain accepted when loading older
|
|
90
|
+
configurations. The board name is forwarded to `qbruntime.Accelerator` so the
|
|
91
|
+
runtime opens the matching device (requires `mobilint-qb-runtime>=1.4.0`).
|
|
90
92
|
`backend_class_for()` and `BACKEND_CLASSES` are available for integrations that
|
|
91
93
|
need to inspect the supported targets.
|
|
92
94
|
|
|
@@ -56,9 +56,11 @@ finally:
|
|
|
56
56
|
|
|
57
57
|
`MobilintNPUBackend` selects the appropriate implementation from
|
|
58
58
|
`target_device` (default: `"aries-rb"`). `"aries-rb"` selects
|
|
59
|
-
`MobilintAriesBackend`; `"regulus-ra"
|
|
60
|
-
`MobilintRegulusBackend`. The former generic
|
|
61
|
-
remain accepted when loading older
|
|
59
|
+
`MobilintAriesBackend`; `"regulus-ra"`, `"regulus-rb"`, `"regulus-ra-usb"`,
|
|
60
|
+
and `"regulus-rb-usb"` select `MobilintRegulusBackend`. The former generic
|
|
61
|
+
values `"aries"` and `"regulus"` remain accepted when loading older
|
|
62
|
+
configurations. The board name is forwarded to `qbruntime.Accelerator` so the
|
|
63
|
+
runtime opens the matching device (requires `mobilint-qb-runtime>=1.4.0`).
|
|
62
64
|
`backend_class_for()` and `BACKEND_CLASSES` are available for integrations that
|
|
63
65
|
need to inspect the supported targets.
|
|
64
66
|
|
|
@@ -74,7 +74,13 @@ def normalize_target_device(target_device: str) -> str:
|
|
|
74
74
|
normalized = _TARGET_DEVICE_ALIASES.get(
|
|
75
75
|
target_device.lower(), target_device.lower()
|
|
76
76
|
)
|
|
77
|
-
if normalized not in {
|
|
77
|
+
if normalized not in {
|
|
78
|
+
"aries-rb",
|
|
79
|
+
"regulus-ra",
|
|
80
|
+
"regulus-rb",
|
|
81
|
+
"regulus-ra-usb",
|
|
82
|
+
"regulus-rb-usb",
|
|
83
|
+
}:
|
|
78
84
|
raise ValueError(f"unknown target_device {target_device!r}")
|
|
79
85
|
return normalized
|
|
80
86
|
|
|
@@ -194,7 +200,13 @@ class MobilintNPUBackend:
|
|
|
194
200
|
num_of_cores_in_cluster = 4
|
|
195
201
|
|
|
196
202
|
default_target_device = DEFAULT_TARGET_DEVICE
|
|
197
|
-
supported_target_devices = (
|
|
203
|
+
supported_target_devices = (
|
|
204
|
+
"aries-rb",
|
|
205
|
+
"regulus-ra",
|
|
206
|
+
"regulus-rb",
|
|
207
|
+
"regulus-ra-usb",
|
|
208
|
+
"regulus-rb-usb",
|
|
209
|
+
)
|
|
198
210
|
|
|
199
211
|
def __new__(cls, *args: Any, **kwargs: Any):
|
|
200
212
|
"""Select a board-specific backend while retaining the legacy constructor."""
|
|
@@ -300,9 +312,14 @@ class MobilintNPUBackend:
|
|
|
300
312
|
spec_kwargs["target_cores"] = list(target_cores)
|
|
301
313
|
if target_clusters is not None:
|
|
302
314
|
spec_kwargs["target_clusters"] = list(target_clusters)
|
|
315
|
+
# Config-layer normalization reads ``target_device`` directly from
|
|
316
|
+
# ``spec_kwargs``. Setter-chain normalization runs later without
|
|
317
|
+
# target_device in scope, so seed it on the pending here.
|
|
318
|
+
spec_kwargs["target_device"] = self.target_device
|
|
303
319
|
initial_spec = NPUTargetSpec.from_kwargs(spec_kwargs)
|
|
304
320
|
self._pending: NPUTargetSpecPending = NPUTargetSpecPending(
|
|
305
|
-
baseline=initial_spec
|
|
321
|
+
baseline=initial_spec,
|
|
322
|
+
target_device=self.target_device,
|
|
306
323
|
)
|
|
307
324
|
self._finalized: Optional[NPUTargetSpec] = initial_spec
|
|
308
325
|
|
|
@@ -337,7 +354,11 @@ class MobilintNPUBackend:
|
|
|
337
354
|
self._finalized = self._pending.finalize()
|
|
338
355
|
# Close the current override epoch: the next setter chain
|
|
339
356
|
# accumulates on a fresh baseline with all intent flags cleared.
|
|
340
|
-
|
|
357
|
+
# Carry ``target_device`` forward so a later setter chain that
|
|
358
|
+
# re-expands ``dev_no`` sugar still sees the backend's board.
|
|
359
|
+
self._pending = NPUTargetSpecPending.from_baseline(
|
|
360
|
+
self._finalized, target_device=self.target_device
|
|
361
|
+
)
|
|
341
362
|
return self._finalized
|
|
342
363
|
|
|
343
364
|
# ---- Target-topology accessors ------------------------------------------
|
|
@@ -943,7 +964,9 @@ class MobilintNPUBackend:
|
|
|
943
964
|
if not unique_devs:
|
|
944
965
|
unique_devs = [self._fallback_dev()]
|
|
945
966
|
|
|
946
|
-
self.accs = {
|
|
967
|
+
self.accs = {
|
|
968
|
+
int(d): Accelerator(self.target_device, int(d)) for d in unique_devs
|
|
969
|
+
}
|
|
947
970
|
self.mxq_models = []
|
|
948
971
|
self.model_dev_no = []
|
|
949
972
|
self.n_models = 0
|
|
@@ -1489,7 +1512,12 @@ class MobilintRegulusBackend(MobilintNPUBackend):
|
|
|
1489
1512
|
"""Regulus backend: validates its one-core, single/auto-only topology."""
|
|
1490
1513
|
|
|
1491
1514
|
default_target_device = "regulus-ra"
|
|
1492
|
-
supported_target_devices = (
|
|
1515
|
+
supported_target_devices = (
|
|
1516
|
+
"regulus-ra",
|
|
1517
|
+
"regulus-rb",
|
|
1518
|
+
"regulus-ra-usb",
|
|
1519
|
+
"regulus-rb-usb",
|
|
1520
|
+
)
|
|
1493
1521
|
num_of_clusters = 1
|
|
1494
1522
|
num_of_cores_in_cluster = 1
|
|
1495
1523
|
|
|
@@ -1576,6 +1604,8 @@ BACKEND_CLASSES = {
|
|
|
1576
1604
|
"aries-rb": MobilintAriesBackend,
|
|
1577
1605
|
"regulus-ra": MobilintRegulusBackend,
|
|
1578
1606
|
"regulus-rb": MobilintRegulusBackend,
|
|
1607
|
+
"regulus-ra-usb": MobilintRegulusBackend,
|
|
1608
|
+
"regulus-rb-usb": MobilintRegulusBackend,
|
|
1579
1609
|
}
|
|
1580
1610
|
|
|
1581
1611
|
|
|
@@ -131,6 +131,33 @@ _VALID_CLUSTER_INDICES: frozenset = frozenset(cluster_map.keys())
|
|
|
131
131
|
_VALID_CORE_INDICES: frozenset = frozenset(core_map.keys())
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
def _topology_for_target(target_device: Optional[str]) -> tuple[int, int]:
|
|
135
|
+
"""Return ``(num_clusters, num_cores_per_cluster)`` for a board identifier.
|
|
136
|
+
|
|
137
|
+
Consumed by :func:`_resolve_targets` when expanding ``dev_no`` sugar into
|
|
138
|
+
a default target-core / target-cluster list: without a board-appropriate
|
|
139
|
+
topology, a Regulus config that names only ``target_device`` would be
|
|
140
|
+
populated with Aries's 2×4 grid and then rejected by
|
|
141
|
+
:class:`MobilintRegulusBackend` for referencing cores its single-cluster
|
|
142
|
+
hardware does not have.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
target_device: The board identifier from the caller's config
|
|
146
|
+
(``"aries-rb"``, ``"regulus-rb-usb"``, legacy ``"aries"`` /
|
|
147
|
+
``"regulus"``, ...), or ``None`` when the caller has not
|
|
148
|
+
declared one. Case-insensitive.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
``(2, 4)`` for Aries (and the historical unspecified default so
|
|
152
|
+
callers with legacy configs keep the same sugar expansion); ``(1, 1)``
|
|
153
|
+
for every Regulus variant (RA / RB / RA-USB / RB-USB, the ``regulus``
|
|
154
|
+
family alias, and any future ``regulus-*`` addition).
|
|
155
|
+
"""
|
|
156
|
+
if target_device and target_device.lower().startswith("regulus"):
|
|
157
|
+
return (1, 1)
|
|
158
|
+
return (2, 4)
|
|
159
|
+
|
|
160
|
+
|
|
134
161
|
def _check_cluster_core_indices(c_val: int, k_val: int, entry: Any) -> None:
|
|
135
162
|
"""Raise ``ValueError`` when ``c_val`` / ``k_val`` fall outside the Aries2 topology."""
|
|
136
163
|
if (
|
|
@@ -474,6 +501,16 @@ class NPUTargetSpec:
|
|
|
474
501
|
_pending: Optional["NPUTargetSpecPending"] = field(
|
|
475
502
|
default=None, compare=False, hash=False, repr=False
|
|
476
503
|
)
|
|
504
|
+
# Board identifier carried alongside the canonical fields so a subsequent
|
|
505
|
+
# :meth:`_with` (or a fresh :class:`NPUTargetSpecPending` derived from
|
|
506
|
+
# this spec) still knows which topology to use when ``dev_no`` sugar has
|
|
507
|
+
# to re-expand. Excluded from equality/hash/repr because it is not part
|
|
508
|
+
# of the canonical ``(dev_no, core_mode, cores, clusters)`` identity —
|
|
509
|
+
# two Aries specs with identical grain must still compare equal even if
|
|
510
|
+
# only one of them recorded ``target_device``.
|
|
511
|
+
target_device: Optional[str] = field(
|
|
512
|
+
default=None, compare=False, hash=False, repr=False
|
|
513
|
+
)
|
|
477
514
|
|
|
478
515
|
@property
|
|
479
516
|
def _dev_no_overridden(self) -> bool:
|
|
@@ -539,6 +576,7 @@ class NPUTargetSpec:
|
|
|
539
576
|
dev_no_key = f"{prefix}dev_no"
|
|
540
577
|
cores_key = f"{prefix}target_cores"
|
|
541
578
|
clusters_key = f"{prefix}target_clusters"
|
|
579
|
+
target_device_key = f"{prefix}target_device"
|
|
542
580
|
|
|
543
581
|
core_mode = normalize_core_mode(kwargs.get(core_mode_key, "single"))
|
|
544
582
|
dev_no = _dedup_dev_no(kwargs.get(dev_no_key, _DEFAULT_DEV_NO))
|
|
@@ -546,6 +584,7 @@ class NPUTargetSpec:
|
|
|
546
584
|
dev_list = _normalize_dev_list(dev_no)
|
|
547
585
|
fallback_dev = dev_list[0]
|
|
548
586
|
dev_no_given = dev_no_key in kwargs
|
|
587
|
+
target_device = kwargs.get(target_device_key)
|
|
549
588
|
|
|
550
589
|
raw_cores = kwargs.get(cores_key)
|
|
551
590
|
raw_clusters = kwargs.get(clusters_key)
|
|
@@ -558,6 +597,7 @@ class NPUTargetSpec:
|
|
|
558
597
|
dev_no_given=dev_no_given,
|
|
559
598
|
raw_cores=list(raw_cores) if raw_cores else [],
|
|
560
599
|
raw_clusters=list(raw_clusters) if raw_clusters else [],
|
|
600
|
+
target_device=target_device,
|
|
561
601
|
)
|
|
562
602
|
|
|
563
603
|
# Mutate the caller's ``kwargs`` dict in place for downstream code
|
|
@@ -587,6 +627,7 @@ class NPUTargetSpec:
|
|
|
587
627
|
core_mode=core_mode,
|
|
588
628
|
cores=tuple(cores),
|
|
589
629
|
clusters=tuple(clusters),
|
|
630
|
+
target_device=target_device,
|
|
590
631
|
)
|
|
591
632
|
|
|
592
633
|
def _with(
|
|
@@ -627,7 +668,10 @@ class NPUTargetSpec:
|
|
|
627
668
|
:meth:`NPUTargetSpecPending.finalize`.
|
|
628
669
|
"""
|
|
629
670
|
if self._pending is None:
|
|
630
|
-
base_pending = NPUTargetSpecPending(
|
|
671
|
+
base_pending = NPUTargetSpecPending(
|
|
672
|
+
baseline=replace(self, _pending=None),
|
|
673
|
+
target_device=self.target_device,
|
|
674
|
+
)
|
|
631
675
|
else:
|
|
632
676
|
base_pending = self._pending
|
|
633
677
|
new_pending = base_pending._with(
|
|
@@ -684,6 +728,7 @@ def _resolve_targets(
|
|
|
684
728
|
dev_no_given: bool,
|
|
685
729
|
raw_cores: List[Any],
|
|
686
730
|
raw_clusters: List[Any],
|
|
731
|
+
target_device: Optional[str] = None,
|
|
687
732
|
) -> tuple[List[str], List[str]]:
|
|
688
733
|
"""Canonicalize a ``(dev_no, core_mode, cores, clusters)`` payload.
|
|
689
734
|
|
|
@@ -721,11 +766,24 @@ def _resolve_targets(
|
|
|
721
766
|
)
|
|
722
767
|
|
|
723
768
|
if not cores and not clusters:
|
|
724
|
-
# ``dev_no`` sugar expansion when both target lists are absent.
|
|
769
|
+
# ``dev_no`` sugar expansion when both target lists are absent. The
|
|
770
|
+
# topology is board-dependent: Aries exposes 2 clusters × 4 cores per
|
|
771
|
+
# device, Regulus exposes 1 cluster × 1 core. Without threading
|
|
772
|
+
# ``target_device`` here, a Regulus config that only names
|
|
773
|
+
# ``target_device`` receives Aries's 8-core grid and later fails
|
|
774
|
+
# :class:`MobilintRegulusBackend`'s topology check.
|
|
775
|
+
num_clusters, num_cores = _topology_for_target(target_device)
|
|
725
776
|
if core_mode == "single":
|
|
726
|
-
cores = [
|
|
777
|
+
cores = [
|
|
778
|
+
f"{d}:{c}:{k}"
|
|
779
|
+
for d in dev_list
|
|
780
|
+
for c in range(num_clusters)
|
|
781
|
+
for k in range(num_cores)
|
|
782
|
+
]
|
|
727
783
|
else:
|
|
728
|
-
clusters = [
|
|
784
|
+
clusters = [
|
|
785
|
+
f"{d}:{c}" for d in dev_list for c in range(num_clusters)
|
|
786
|
+
]
|
|
729
787
|
else:
|
|
730
788
|
# Grain unification per core_mode.
|
|
731
789
|
if core_mode == "single":
|
|
@@ -797,6 +855,13 @@ class NPUTargetSpecPending:
|
|
|
797
855
|
strings, or canonical ``"d:c:k"`` strings), or :data:`_UNSET`.
|
|
798
856
|
raw_clusters: The caller's raw ``target_clusters`` override, or
|
|
799
857
|
:data:`_UNSET`.
|
|
858
|
+
target_device: Board identifier carried alongside the override
|
|
859
|
+
history so :meth:`finalize` can pick the right ``dev_no`` sugar
|
|
860
|
+
topology (Aries 2×4 vs Regulus 1×1). Attached by the backend at
|
|
861
|
+
init time (and preserved across chained :meth:`_with` /
|
|
862
|
+
:meth:`from_baseline` calls) because the spec-level normalization
|
|
863
|
+
layer sees target_device only at the config-load entry point;
|
|
864
|
+
per-field setter chains would otherwise lose it.
|
|
800
865
|
"""
|
|
801
866
|
|
|
802
867
|
baseline: NPUTargetSpec
|
|
@@ -804,9 +869,15 @@ class NPUTargetSpecPending:
|
|
|
804
869
|
raw_core_mode: Any = _UNSET
|
|
805
870
|
raw_cores: Any = _UNSET
|
|
806
871
|
raw_clusters: Any = _UNSET
|
|
872
|
+
target_device: Optional[str] = None
|
|
807
873
|
|
|
808
874
|
@classmethod
|
|
809
|
-
def from_baseline(
|
|
875
|
+
def from_baseline(
|
|
876
|
+
cls,
|
|
877
|
+
spec: NPUTargetSpec,
|
|
878
|
+
*,
|
|
879
|
+
target_device: Optional[str] = None,
|
|
880
|
+
) -> "NPUTargetSpecPending":
|
|
810
881
|
"""Return a fresh pending baseline for the next override epoch.
|
|
811
882
|
|
|
812
883
|
:class:`MobilintNPUBackend` calls this immediately after materializing
|
|
@@ -822,13 +893,21 @@ class NPUTargetSpecPending:
|
|
|
822
893
|
spec: Canonical :class:`NPUTargetSpec` to seed the fresh pending's
|
|
823
894
|
baseline with. Typically the finalized result of the previous
|
|
824
895
|
override epoch.
|
|
896
|
+
target_device: Board identifier attached to the fresh pending so
|
|
897
|
+
:meth:`finalize` picks the right ``dev_no`` sugar topology.
|
|
898
|
+
Defaults to ``spec.target_device`` so callers that omit it
|
|
899
|
+
(or specs constructed outside the backend) still keep their
|
|
900
|
+
board; the backend's explicit override wins when provided.
|
|
825
901
|
|
|
826
902
|
Returns:
|
|
827
903
|
A new :class:`NPUTargetSpecPending` whose baseline is ``spec``
|
|
828
904
|
(with any prior ``_pending`` history stripped) and whose intent
|
|
829
905
|
slots are all :data:`_UNSET`.
|
|
830
906
|
"""
|
|
831
|
-
return cls(
|
|
907
|
+
return cls(
|
|
908
|
+
baseline=replace(spec, _pending=None),
|
|
909
|
+
target_device=target_device if target_device is not None else spec.target_device,
|
|
910
|
+
)
|
|
832
911
|
|
|
833
912
|
def _with(
|
|
834
913
|
self,
|
|
@@ -962,6 +1041,7 @@ class NPUTargetSpecPending:
|
|
|
962
1041
|
dev_no_given=dev_no_overridden,
|
|
963
1042
|
raw_cores=raw_cores,
|
|
964
1043
|
raw_clusters=raw_clusters,
|
|
1044
|
+
target_device=self.target_device,
|
|
965
1045
|
)
|
|
966
1046
|
|
|
967
1047
|
# Resolve the effective ``dev_no`` for the returned canonical spec.
|
|
@@ -983,6 +1063,7 @@ class NPUTargetSpecPending:
|
|
|
983
1063
|
cores=tuple(cores),
|
|
984
1064
|
clusters=tuple(clusters),
|
|
985
1065
|
_pending=self,
|
|
1066
|
+
target_device=self.target_device,
|
|
986
1067
|
)
|
|
987
1068
|
|
|
988
1069
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mblt-npu-python
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: Shared NPU access for the Mobilint Python packages
|
|
5
5
|
Author: Mobilint
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -18,7 +18,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
18
18
|
Requires-Python: <3.13,>=3.10
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
-
Requires-Dist: mobilint-qb-runtime>=1.
|
|
21
|
+
Requires-Dist: mobilint-qb-runtime>=1.4.0
|
|
22
22
|
Requires-Dist: huggingface-hub
|
|
23
23
|
Provides-Extra: test
|
|
24
24
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -84,9 +84,11 @@ finally:
|
|
|
84
84
|
|
|
85
85
|
`MobilintNPUBackend` selects the appropriate implementation from
|
|
86
86
|
`target_device` (default: `"aries-rb"`). `"aries-rb"` selects
|
|
87
|
-
`MobilintAriesBackend`; `"regulus-ra"
|
|
88
|
-
`MobilintRegulusBackend`. The former generic
|
|
89
|
-
remain accepted when loading older
|
|
87
|
+
`MobilintAriesBackend`; `"regulus-ra"`, `"regulus-rb"`, `"regulus-ra-usb"`,
|
|
88
|
+
and `"regulus-rb-usb"` select `MobilintRegulusBackend`. The former generic
|
|
89
|
+
values `"aries"` and `"regulus"` remain accepted when loading older
|
|
90
|
+
configurations. The board name is forwarded to `qbruntime.Accelerator` so the
|
|
91
|
+
runtime opens the matching device (requires `mobilint-qb-runtime>=1.4.0`).
|
|
90
92
|
`backend_class_for()` and `BACKEND_CLASSES` are available for integrations that
|
|
91
93
|
need to inspect the supported targets.
|
|
92
94
|
|
|
@@ -28,9 +28,10 @@ from mblt_npu.npu_backend import (
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class _FakeAccelerator:
|
|
31
|
-
"""Records the device number an accelerator was opened for."""
|
|
31
|
+
"""Records the target device name and device number an accelerator was opened for."""
|
|
32
32
|
|
|
33
|
-
def __init__(self, dev_no: int) -> None:
|
|
33
|
+
def __init__(self, target_device: str, dev_no: int) -> None:
|
|
34
|
+
self.target_device = target_device
|
|
34
35
|
self.dev_no = int(dev_no)
|
|
35
36
|
|
|
36
37
|
|
|
@@ -10,7 +10,7 @@ import pytest
|
|
|
10
10
|
|
|
11
11
|
from mblt_npu import MobilintAriesBackend, MobilintNPUBackend, MobilintRegulusBackend
|
|
12
12
|
import mblt_npu.npu_backend as npu_backend
|
|
13
|
-
from mblt_npu.npu_target import _UNSET
|
|
13
|
+
from mblt_npu.npu_target import _UNSET, NPUTargetSpec
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@pytest.mark.parametrize(
|
|
@@ -19,6 +19,8 @@ from mblt_npu.npu_target import _UNSET
|
|
|
19
19
|
("aries-rb", MobilintAriesBackend),
|
|
20
20
|
("regulus-ra", MobilintRegulusBackend),
|
|
21
21
|
("regulus-rb", MobilintRegulusBackend),
|
|
22
|
+
("regulus-ra-usb", MobilintRegulusBackend),
|
|
23
|
+
("regulus-rb-usb", MobilintRegulusBackend),
|
|
22
24
|
],
|
|
23
25
|
)
|
|
24
26
|
def test_target_device_selects_the_product_backend(
|
|
@@ -76,7 +78,10 @@ def test_default_target_device_is_aries_rb() -> None:
|
|
|
76
78
|
assert backend.target_device == "aries-rb"
|
|
77
79
|
|
|
78
80
|
|
|
79
|
-
@pytest.mark.parametrize(
|
|
81
|
+
@pytest.mark.parametrize(
|
|
82
|
+
"target_device",
|
|
83
|
+
["regulus-ra", "regulus-rb", "regulus-ra-usb", "regulus-rb-usb"],
|
|
84
|
+
)
|
|
80
85
|
def test_positional_target_device_selects_the_product_backend(
|
|
81
86
|
target_device: str,
|
|
82
87
|
) -> None:
|
|
@@ -412,3 +417,145 @@ def test_regulus_auto_positional_empty_cluster_targets_are_canonicalized() -> No
|
|
|
412
417
|
backend = MobilintRegulusBackend("", 0, "auto", None, [])
|
|
413
418
|
|
|
414
419
|
assert backend.to_dict()["target_clusters"] == ["0:0"]
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
@pytest.mark.parametrize(
|
|
423
|
+
"target_device",
|
|
424
|
+
["regulus-ra", "regulus-rb", "regulus-ra-usb", "regulus-rb-usb"],
|
|
425
|
+
)
|
|
426
|
+
def test_regulus_dev_no_sugar_expands_to_single_core(target_device: str) -> None:
|
|
427
|
+
"""Expand Regulus ``dev_no`` sugar to its one-core topology, not Aries's grid.
|
|
428
|
+
|
|
429
|
+
Without target-device-aware sugar expansion, a Regulus config that only
|
|
430
|
+
names ``target_device`` receives Aries's 2 clusters × 4 cores default
|
|
431
|
+
and is then rejected by :class:`MobilintRegulusBackend`'s topology check.
|
|
432
|
+
Every Regulus board (PCIe and USB) must produce a single ``d:0:0`` core.
|
|
433
|
+
"""
|
|
434
|
+
|
|
435
|
+
backend = MobilintNPUBackend(target_device=target_device)
|
|
436
|
+
|
|
437
|
+
assert isinstance(backend, MobilintRegulusBackend)
|
|
438
|
+
assert backend.to_dict()["target_cores"] == ["0:0:0"]
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def test_regulus_dev_no_sugar_respects_device_list() -> None:
|
|
442
|
+
"""Expand Regulus ``dev_no=[0, 1]`` sugar to one core per named device."""
|
|
443
|
+
|
|
444
|
+
backend = MobilintNPUBackend(target_device="regulus-rb-usb", dev_no=[0, 1])
|
|
445
|
+
|
|
446
|
+
assert set(backend.to_dict()["target_cores"]) == {"0:0:0", "1:0:0"}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@pytest.mark.parametrize(
|
|
450
|
+
"target_device",
|
|
451
|
+
["regulus-ra", "regulus-rb", "regulus-ra-usb", "regulus-rb-usb"],
|
|
452
|
+
)
|
|
453
|
+
def test_from_kwargs_sugar_uses_regulus_topology(target_device: str) -> None:
|
|
454
|
+
"""Populate ``dev_no`` sugar with Regulus topology at the config layer.
|
|
455
|
+
|
|
456
|
+
Direct :meth:`NPUTargetSpec.from_kwargs` exercises the sugar-expansion
|
|
457
|
+
branch that Model Zoo hits before the concrete backend __init__ can
|
|
458
|
+
populate its own single-core default: when the config layer sees only
|
|
459
|
+
``target_device`` (no ``target_cores`` / ``target_clusters``), the spec
|
|
460
|
+
must emit ``["0:0:0"]`` for every Regulus board so a subsequent
|
|
461
|
+
:class:`MobilintRegulusBackend` construction accepts the pre-populated
|
|
462
|
+
grain.
|
|
463
|
+
"""
|
|
464
|
+
|
|
465
|
+
spec = NPUTargetSpec.from_kwargs(
|
|
466
|
+
{"target_device": target_device, "core_mode": "single"}
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
assert list(spec.cores) == ["0:0:0"]
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
@pytest.mark.parametrize(
|
|
473
|
+
"target_device",
|
|
474
|
+
["regulus-ra", "regulus-rb", "regulus-ra-usb", "regulus-rb-usb"],
|
|
475
|
+
)
|
|
476
|
+
def test_spec_with_override_preserves_regulus_topology(target_device: str) -> None:
|
|
477
|
+
"""Keep a Regulus spec's board identity across a derived ``_with`` override.
|
|
478
|
+
|
|
479
|
+
Regression for the case where ``NPUTargetSpec.from_kwargs`` set
|
|
480
|
+
``target_device`` only on the fresh pending and lost it on the returned
|
|
481
|
+
spec. A follow-up ``spec._with(dev_no=...)`` then created a new pending
|
|
482
|
+
with ``target_device=None``, and the sugar re-expansion produced Aries's
|
|
483
|
+
2×4 grid, which :class:`MobilintRegulusBackend` would then reject for
|
|
484
|
+
referencing cores its single-cluster hardware does not have.
|
|
485
|
+
"""
|
|
486
|
+
|
|
487
|
+
root = NPUTargetSpec.from_kwargs(
|
|
488
|
+
{"target_device": target_device, "core_mode": "single"}
|
|
489
|
+
)
|
|
490
|
+
assert root.target_device == target_device
|
|
491
|
+
|
|
492
|
+
derived = root._with(dev_no=1)
|
|
493
|
+
|
|
494
|
+
assert derived.target_device == target_device
|
|
495
|
+
assert list(derived.cores) == ["1:0:0"]
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def test_from_kwargs_sugar_uses_aries_topology_by_default() -> None:
|
|
499
|
+
"""Fall back to Aries 2×4 sugar when no ``target_device`` is declared.
|
|
500
|
+
|
|
501
|
+
Callers with legacy configs that never named a board must keep receiving
|
|
502
|
+
the historical 8-core Aries default; :func:`_topology_for_target` treats
|
|
503
|
+
``None`` and any non-Regulus string as Aries.
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
spec = NPUTargetSpec.from_kwargs({"core_mode": "single"})
|
|
507
|
+
|
|
508
|
+
assert len(spec.cores) == 8
|
|
509
|
+
assert list(spec.cores) == [
|
|
510
|
+
"0:0:0",
|
|
511
|
+
"0:0:1",
|
|
512
|
+
"0:0:2",
|
|
513
|
+
"0:0:3",
|
|
514
|
+
"0:1:0",
|
|
515
|
+
"0:1:1",
|
|
516
|
+
"0:1:2",
|
|
517
|
+
"0:1:3",
|
|
518
|
+
]
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@pytest.mark.parametrize(
|
|
522
|
+
"target_device",
|
|
523
|
+
["aries-rb", "regulus-ra", "regulus-rb", "regulus-ra-usb", "regulus-rb-usb"],
|
|
524
|
+
)
|
|
525
|
+
def test_create_passes_target_device_to_accelerator(
|
|
526
|
+
monkeypatch: pytest.MonkeyPatch, target_device: str
|
|
527
|
+
) -> None:
|
|
528
|
+
"""Forward the resolved board name to ``qbruntime.Accelerator``.
|
|
529
|
+
|
|
530
|
+
``qbruntime>=1.4`` selects the physical device from the target-device
|
|
531
|
+
string passed as the first positional argument. Regressing that call
|
|
532
|
+
would silently strand USB and Regulus workloads on the default
|
|
533
|
+
accelerator, so pin the argument shape explicitly.
|
|
534
|
+
"""
|
|
535
|
+
|
|
536
|
+
captured: list[tuple[Any, ...]] = []
|
|
537
|
+
|
|
538
|
+
class _Acc:
|
|
539
|
+
def __init__(self, *args: Any) -> None:
|
|
540
|
+
captured.append(args)
|
|
541
|
+
|
|
542
|
+
def dispose(self) -> None:
|
|
543
|
+
pass
|
|
544
|
+
|
|
545
|
+
class _StopCreate(Exception):
|
|
546
|
+
pass
|
|
547
|
+
|
|
548
|
+
def _no_model(*_args: Any, **_kwargs: Any) -> None:
|
|
549
|
+
raise _StopCreate
|
|
550
|
+
|
|
551
|
+
monkeypatch.setattr(npu_backend, "Accelerator", _Acc)
|
|
552
|
+
monkeypatch.setattr(npu_backend, "Model", _no_model)
|
|
553
|
+
monkeypatch.setattr(
|
|
554
|
+
npu_backend.MobilintNPUBackend, "check_model_path", lambda self, path: path
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
backend = MobilintNPUBackend(mxq_path="m.mxq", target_device=target_device, dev_no=0)
|
|
558
|
+
with pytest.raises(_StopCreate):
|
|
559
|
+
backend.create()
|
|
560
|
+
|
|
561
|
+
assert captured == [(target_device, 0)]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mblt_npu_python-0.0.2 → mblt_npu_python-0.1.0}/mblt_npu_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|