| """Report Anima MixBit runtime-mode eligibility for the local NVIDIA GPU.""" |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import json |
| import sys |
| from typing import Any |
|
|
|
|
| STANDARD_VALIDATED_NAMES = ( |
| "nvidia geforce rtx 3090", |
| "nvidia geforce rtx 5080 laptop gpu", |
| ) |
| LOW_VRAM_VALIDATED_NAMES = ("nvidia geforce rtx 5080 laptop gpu",) |
|
|
|
|
| def native_nvfp4_eligible(capability: tuple[int, int]) -> bool: |
| """Match the native scaled_mm capability gate used by the runtime.""" |
|
|
| major, minor = capability |
| return (major, minor) == (8, 9) or major >= 9 |
|
|
|
|
| def classify(name: str, capability: tuple[int, int]) -> dict[str, Any]: |
| normalized = name.strip().lower() |
| native_eligible = native_nvfp4_eligible(capability) |
| standard_validated = any(item in normalized for item in STANDARD_VALIDATED_NAMES) |
| low_vram_validated = any(item in normalized for item in LOW_VRAM_VALIDATED_NAMES) |
|
|
| if low_vram_validated: |
| low_vram_status = "validated" |
| elif native_eligible: |
| low_vram_status = "capability_eligible_not_package_validated" |
| else: |
| low_vram_status = "unsupported_by_native_nvfp4_gate" |
|
|
| return { |
| "name": name, |
| "compute_capability": f"{capability[0]}.{capability[1]}", |
| "standard_mode": "validated" if standard_validated else "not_package_validated", |
| "low_vram_mode": low_vram_status, |
| "native_nvfp4_eligible": native_eligible, |
| "recommended_mode": ( |
| "low_vram_or_standard" if native_eligible else "standard" |
| ), |
| } |
|
|
|
|
| def self_test() -> int: |
| expectations = { |
| (8, 0): False, |
| (8, 6): False, |
| (8, 9): True, |
| (9, 0): True, |
| (12, 0): True, |
| } |
| for capability, expected in expectations.items(): |
| actual = native_nvfp4_eligible(capability) |
| if actual != expected: |
| raise AssertionError(f"Capability {capability}: {actual} != {expected}") |
| print("PASS: GPU compatibility rules are internally consistent.") |
| return 0 |
|
|
|
|
| def inspect_device(index: int) -> dict[str, Any]: |
| try: |
| import torch |
| except ImportError as error: |
| return { |
| "ready": False, |
| "error": f"PyTorch is not installed: {error}", |
| } |
|
|
| if not torch.cuda.is_available(): |
| return { |
| "ready": False, |
| "error": "CUDA is not available. This package has no validated CPU runtime.", |
| } |
| if index < 0 or index >= torch.cuda.device_count(): |
| return { |
| "ready": False, |
| "error": f"CUDA device index {index} is out of range.", |
| } |
|
|
| name = torch.cuda.get_device_name(index) |
| capability = tuple(int(value) for value in torch.cuda.get_device_capability(index)) |
| result = classify(name, capability) |
| result.update( |
| { |
| "ready": True, |
| "device_index": index, |
| "pytorch": torch.__version__, |
| "cuda_runtime": torch.version.cuda, |
| } |
| ) |
| return result |
|
|
|
|
| def print_human(result: dict[str, Any]) -> None: |
| if not result.get("ready"): |
| print(f"判定不能: {result['error']}") |
| return |
|
|
| standard = { |
| "validated": "実機検証済み", |
| "not_package_validated": "このパッケージでは未検証", |
| }[result["standard_mode"]] |
| low_vram = { |
| "validated": "実機検証済み", |
| "capability_eligible_not_package_validated": "演算条件適合・パッケージ未検証", |
| "unsupported_by_native_nvfp4_gate": "非対応(native NVFP4条件外)", |
| }[result["low_vram_mode"]] |
| recommendation = ( |
| "VRAMを優先するなら省VRAM、速度と互換性を優先するなら通常" |
| if result["native_nvfp4_eligible"] |
| else "通常" |
| ) |
| print(f"GPU: {result['name']}") |
| print(f"Compute Capability: {result['compute_capability']}") |
| print(f"通常モード: {standard}") |
| print(f"省VRAMモード: {low_vram}") |
| print(f"推奨: {recommendation}") |
|
|
|
|
| def main() -> int: |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--device", type=int, default=0) |
| parser.add_argument("--json", action="store_true") |
| parser.add_argument("--self-test", action="store_true") |
| args = parser.parse_args() |
| if args.self_test: |
| return self_test() |
|
|
| result = inspect_device(args.device) |
| if args.json: |
| print(json.dumps(result, ensure_ascii=False, indent=2)) |
| else: |
| print_human(result) |
| return 0 if result.get("ready") else 2 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|