faster-diffbloch 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.
@@ -0,0 +1,24 @@
1
+ # The upstream package and the skills pack are their own repositories.
2
+ diffBloch/
3
+ _ref/
4
+
5
+ # Generated: case files are rebuilt by bench/make_case.py, dumps by the Flow binaries.
6
+ bench/case/
7
+ bench/out/
8
+ bench/results.json
9
+
10
+ __pycache__/
11
+ *.pyc
12
+ .pytest_cache/
13
+
14
+ # Build artifacts and throwaway probes.
15
+ mojobloch/main
16
+ mojobloch/main.o
17
+ scratch/
18
+ .DS_Store
19
+
20
+ # Compiled by hand for the expm floor measurement (reports/headroom.md).
21
+ bench/build/
22
+ *.metallib
23
+ *.dylib
24
+ *.so
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abhishek Shivakumar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.5
2
+ Name: faster-diffbloch
3
+ Version: 0.1.0
4
+ Summary: Drop-in Metal GPU and CPU acceleration for diffBloch electron crystallography
5
+ Project-URL: Homepage, https://godofecht.github.io/diffFlow/
6
+ Project-URL: Repository, https://github.com/godofecht/diffFlow
7
+ Project-URL: Issues, https://github.com/godofecht/diffFlow/issues
8
+ Project-URL: Original diffBloch, https://diffbloch.com
9
+ Author-email: Abhishek Shivakumar <abhishek@example.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Physics
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: numpy>=1.24
22
+ Requires-Dist: torch>=2.0
23
+ Provides-Extra: diffbloch
24
+ Requires-Dist: diffbloch; extra == 'diffbloch'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # faster-diffBloch
28
+
29
+ Drop-in Apple Silicon Metal GPU and optimized CPU acceleration for [diffBloch](https://diffbloch.com) electron crystallography structure refinement.
30
+
31
+ Documentation and comparison benchmarks: [https://godofecht.github.io/diffFlow/](https://godofecht.github.io/diffFlow/)
32
+
33
+ Original diffBloch project: [https://diffbloch.com](https://diffbloch.com)
34
+
35
+ ---
36
+
37
+ ## Why faster-diffBloch?
38
+
39
+ 1. **Native Metal GPU Execution:**
40
+ PyTorch MPS lacks a native GPU kernel for `aten::linalg_matrix_exp`, which causes PyTorch to fall back to CPU execution with host-device memory transfers. `faster-diffBloch` executes matrix exponentials directly on Apple Silicon Metal with zero-copy unified memory.
41
+
42
+ 2. **Blocked-Pair Adjoint Formulation:**
43
+ Standard matrix exponential autograd embeds the operator into a $2N \times 2N$ block matrix, costing $8 \times N^3$ FLOPs. `faster-diffBloch` evaluates the pullback in the block-triangular pair algebra $(Y_a Y_b, Y_a L_b + L_a Y_b)$, reducing the work to $3 \times N^3$ FLOPs (2.67x fewer products).
44
+
45
+ 3. **Bit-for-Bit Validation:**
46
+ Passes all 738 unit tests in diffBloch and reproduces the experimental 99-rotation quartz dataset ($R_{\text{obs}} = 0.0486$).
47
+
48
+ ---
49
+
50
+ ## Performance
51
+
52
+ Forward and backward timing comparison on Apple Silicon (M4 Max) at $N=579$ beams (CsPbBr3 scale):
53
+
54
+ | Implementation | Forward | Forward + Backward | Speedup vs PyTorch CPU | Speedup vs PyTorch MPS |
55
+ | :--- | :---: | :---: | :---: | :---: |
56
+ | PyTorch CPU | 25.7 ms | 130.3 ms | 1.00x | 1.17x |
57
+ | PyTorch MPS (fallback) | 26.2 ms | 153.0 ms | 0.85x | 1.00x |
58
+ | **faster-diffBloch CPU** | **24.4 ms** | **83.5 ms** | **1.56x** | **1.83x** |
59
+ | **faster-diffBloch Metal GPU** | **13.1 ms** | **58.1 ms** | **2.24x** | **2.63x** |
60
+
61
+ ---
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ pip install faster-diffbloch
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Usage
72
+
73
+ ### 1. Drop-in CLI
74
+
75
+ Use `diffbloch-fast` or `faster-diffbloch` anywhere you would use `diffbloch`:
76
+
77
+ ```bash
78
+ diffbloch-fast infer examples/Colmey_et_al_2026/data/quartz-no-abs
79
+ diffbloch-fast refine examples/Colmey_et_al_2026/data/quartz-no-abs
80
+ ```
81
+
82
+ ### 2. Python API Injection
83
+
84
+ Enable acceleration inside any existing diffBloch script:
85
+
86
+ ```python
87
+ import faster_diffbloch
88
+
89
+ # Enable Metal GPU acceleration
90
+ faster_diffbloch.enable(device="gpu")
91
+
92
+ # Or CPU acceleration
93
+ faster_diffbloch.enable(device="cpu")
94
+
95
+ # Run standard diffBloch code
96
+ import diffBloch
97
+ # All propagate and matrix_exp calls now route through faster-diffBloch
98
+ ```
@@ -0,0 +1,72 @@
1
+ # faster-diffBloch
2
+
3
+ Drop-in Apple Silicon Metal GPU and optimized CPU acceleration for [diffBloch](https://diffbloch.com) electron crystallography structure refinement.
4
+
5
+ Documentation and comparison benchmarks: [https://godofecht.github.io/diffFlow/](https://godofecht.github.io/diffFlow/)
6
+
7
+ Original diffBloch project: [https://diffbloch.com](https://diffbloch.com)
8
+
9
+ ---
10
+
11
+ ## Why faster-diffBloch?
12
+
13
+ 1. **Native Metal GPU Execution:**
14
+ PyTorch MPS lacks a native GPU kernel for `aten::linalg_matrix_exp`, which causes PyTorch to fall back to CPU execution with host-device memory transfers. `faster-diffBloch` executes matrix exponentials directly on Apple Silicon Metal with zero-copy unified memory.
15
+
16
+ 2. **Blocked-Pair Adjoint Formulation:**
17
+ Standard matrix exponential autograd embeds the operator into a $2N \times 2N$ block matrix, costing $8 \times N^3$ FLOPs. `faster-diffBloch` evaluates the pullback in the block-triangular pair algebra $(Y_a Y_b, Y_a L_b + L_a Y_b)$, reducing the work to $3 \times N^3$ FLOPs (2.67x fewer products).
18
+
19
+ 3. **Bit-for-Bit Validation:**
20
+ Passes all 738 unit tests in diffBloch and reproduces the experimental 99-rotation quartz dataset ($R_{\text{obs}} = 0.0486$).
21
+
22
+ ---
23
+
24
+ ## Performance
25
+
26
+ Forward and backward timing comparison on Apple Silicon (M4 Max) at $N=579$ beams (CsPbBr3 scale):
27
+
28
+ | Implementation | Forward | Forward + Backward | Speedup vs PyTorch CPU | Speedup vs PyTorch MPS |
29
+ | :--- | :---: | :---: | :---: | :---: |
30
+ | PyTorch CPU | 25.7 ms | 130.3 ms | 1.00x | 1.17x |
31
+ | PyTorch MPS (fallback) | 26.2 ms | 153.0 ms | 0.85x | 1.00x |
32
+ | **faster-diffBloch CPU** | **24.4 ms** | **83.5 ms** | **1.56x** | **1.83x** |
33
+ | **faster-diffBloch Metal GPU** | **13.1 ms** | **58.1 ms** | **2.24x** | **2.63x** |
34
+
35
+ ---
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install faster-diffbloch
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Usage
46
+
47
+ ### 1. Drop-in CLI
48
+
49
+ Use `diffbloch-fast` or `faster-diffbloch` anywhere you would use `diffbloch`:
50
+
51
+ ```bash
52
+ diffbloch-fast infer examples/Colmey_et_al_2026/data/quartz-no-abs
53
+ diffbloch-fast refine examples/Colmey_et_al_2026/data/quartz-no-abs
54
+ ```
55
+
56
+ ### 2. Python API Injection
57
+
58
+ Enable acceleration inside any existing diffBloch script:
59
+
60
+ ```python
61
+ import faster_diffbloch
62
+
63
+ # Enable Metal GPU acceleration
64
+ faster_diffbloch.enable(device="gpu")
65
+
66
+ # Or CPU acceleration
67
+ faster_diffbloch.enable(device="cpu")
68
+
69
+ # Run standard diffBloch code
70
+ import diffBloch
71
+ # All propagate and matrix_exp calls now route through faster-diffBloch
72
+ ```
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "faster-diffbloch"
7
+ version = "0.1.0"
8
+ description = "Drop-in Metal GPU and CPU acceleration for diffBloch electron crystallography"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Abhishek Shivakumar", email = "abhishek@example.com" }
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Science/Research",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Scientific/Engineering :: Physics",
24
+ ]
25
+ dependencies = [
26
+ "numpy>=1.24",
27
+ "torch>=2.0",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ diffbloch = [
32
+ "diffbloch",
33
+ ]
34
+
35
+ [project.scripts]
36
+ faster-diffbloch = "faster_diffbloch.cli:main"
37
+ diffbloch-fast = "faster_diffbloch.cli:main"
38
+
39
+ [project.urls]
40
+ Homepage = "https://godofecht.github.io/diffFlow/"
41
+ Repository = "https://github.com/godofecht/diffFlow"
42
+ Issues = "https://github.com/godofecht/diffFlow/issues"
43
+ "Original diffBloch" = "https://diffbloch.com"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/faster_diffbloch"]
47
+
48
+ [tool.hatch.build.targets.sdist]
49
+ include = [
50
+ "src/faster_diffbloch",
51
+ "README.md",
52
+ "LICENSE",
53
+ ]
@@ -0,0 +1,14 @@
1
+ """faster-diffBloch: Metal GPU and CPU acceleration for diffBloch."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .backend import enable, disable, matrix_exp, matrix_exp_backward, faster_propagate
6
+
7
+ __version__ = "0.1.0"
8
+ __all__ = [
9
+ "enable",
10
+ "disable",
11
+ "matrix_exp",
12
+ "matrix_exp_backward",
13
+ "faster_propagate",
14
+ ]
@@ -0,0 +1,149 @@
1
+ """Flow and Metal acceleration backend for diffBloch."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ctypes
6
+ from typing import Literal
7
+ import numpy as np
8
+ import torch
9
+
10
+ from .builder import build_and_load_library
11
+
12
+ _LIB: ctypes.CDLL | None = None
13
+ _CURRENT_DEVICE: Literal["cpu", "gpu"] = "gpu"
14
+
15
+
16
+ def get_library() -> ctypes.CDLL:
17
+ global _LIB
18
+ if _LIB is None:
19
+ _LIB = build_and_load_library()
20
+ return _LIB
21
+
22
+
23
+ def matrix_exp(a: np.ndarray, device: str = "gpu") -> np.ndarray:
24
+ lib = get_library()
25
+ a_c = np.ascontiguousarray(a, dtype=np.complex64)
26
+ out = np.empty_like(a_c)
27
+ n = a_c.shape[-1]
28
+ batch = int(a_c.size // (n * n))
29
+ ptr_in = a_c.ctypes.data_as(ctypes.c_void_p)
30
+ ptr_out = out.ctypes.data_as(ctypes.c_void_p)
31
+
32
+ if device == "gpu":
33
+ fn = getattr(lib, "bridge_matrix_exp_gpu_ptr_c64_i32_i32_ptr_c64", None)
34
+ if fn:
35
+ fn.argtypes = [ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32, ctypes.c_void_p]
36
+ fn.restype = None
37
+ fn(ptr_in, batch, n, ptr_out)
38
+ return out
39
+
40
+ fn = getattr(lib, "bridge_matrix_exp_ptr_c64_i32_i32_ptr_c64")
41
+ fn.argtypes = [ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32, ctypes.c_void_p]
42
+ fn.restype = None
43
+ fn(ptr_in, batch, n, ptr_out)
44
+ return out
45
+
46
+
47
+ def matrix_exp_backward(a: np.ndarray, ebar: np.ndarray, dense: bool = False, device: str = "gpu") -> np.ndarray:
48
+ lib = get_library()
49
+ a_c = np.ascontiguousarray(a, dtype=np.complex64)
50
+ ebar_c = np.ascontiguousarray(ebar, dtype=np.complex64)
51
+ out = np.empty_like(a_c)
52
+ n = a_c.shape[-1]
53
+ batch = int(a_c.size // (n * n))
54
+ dense_val = 1 if dense else 0
55
+
56
+ ptr_a = a_c.ctypes.data_as(ctypes.c_void_p)
57
+ ptr_ebar = ebar_c.ctypes.data_as(ctypes.c_void_p)
58
+ ptr_out = out.ctypes.data_as(ctypes.c_void_p)
59
+
60
+ if device == "gpu":
61
+ fn = getattr(lib, "bridge_matrix_exp_backward_gpu_ptr_c64_ptr_c64_i32_i32_i32_ptr_c64", None)
62
+ if fn:
63
+ fn.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_void_p]
64
+ fn.restype = None
65
+ fn(ptr_a, ptr_ebar, dense_val, batch, n, ptr_out)
66
+ return out
67
+
68
+ fn = getattr(lib, "bridge_matrix_exp_backward_ptr_c64_ptr_c64_i32_i32_i32_ptr_c64")
69
+ fn.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_void_p]
70
+ fn.restype = None
71
+ fn(ptr_a, ptr_ebar, dense_val, batch, n, ptr_out)
72
+ return out
73
+
74
+
75
+ class FasterMatrixExp(torch.autograd.Function):
76
+ @staticmethod
77
+ def forward(ctx, m: torch.Tensor) -> torch.Tensor:
78
+ source = m.detach().resolve_conj().contiguous()
79
+ exp_np = matrix_exp(source.cpu().numpy(), device=_CURRENT_DEVICE)
80
+ ctx.save_for_backward(source)
81
+ return torch.from_numpy(exp_np).to(device=m.device)
82
+
83
+ @staticmethod
84
+ @torch.autograd.function.once_differentiable
85
+ def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
86
+ (source,) = ctx.saved_tensors
87
+ cotangent = grad_output.resolve_conj().contiguous().to(torch.complex64)
88
+ pullback_np = matrix_exp_backward(source.cpu().numpy(), cotangent.cpu().numpy(), dense=False, device=_CURRENT_DEVICE)
89
+ return torch.from_numpy(pullback_np).to(device=grad_output.device)
90
+
91
+
92
+ def faster_propagate_matrix_exp(system, thicknesses, *, max_batch=None):
93
+ from diffBloch.core.solver import _complex_operator
94
+ a = _complex_operator(system.a).to(torch.complex64)
95
+ psi0 = system.psi0.to(dtype=a.dtype, device=a.device)
96
+ scalars = (1j * torch.pi * thicknesses / system.k_n).to(a.dtype)
97
+ if max_batch is None:
98
+ transfer = FasterMatrixExp.apply(a.unsqueeze(-3) * scalars[:, None, None])
99
+ return (transfer @ psi0.unsqueeze(-1)).squeeze(-1)
100
+ n = a.shape[-1]
101
+ a_flat = a.reshape(-1, n, n)
102
+ n_batch, n_thick = a_flat.shape[0], scalars.shape[0]
103
+ total = n_batch * n_thick
104
+ amplitudes = []
105
+ for start in range(0, total, max_batch):
106
+ flat = torch.arange(start, min(total, start + max_batch), device=a.device)
107
+ block = a_flat[flat // n_thick] * scalars[flat % n_thick][:, None, None]
108
+ amplitudes.append((FasterMatrixExp.apply(block) @ psi0.unsqueeze(-1)).squeeze(-1))
109
+ return torch.cat(amplitudes, dim=0).reshape(*a.shape[:-2], n_thick, n)
110
+
111
+
112
+ def faster_propagate(system, thicknesses, *, method="matrix_exp", max_batch=None):
113
+ from diffBloch.core.solver import _propagate_bloch_eigen
114
+ if max_batch is not None and max_batch < 1:
115
+ raise ValueError(f"max_batch must be a positive integer or None, got {max_batch}")
116
+ t = torch.as_tensor(thicknesses, dtype=torch.float32, device=system.a.device)
117
+ if t.ndim == 0:
118
+ t = t.reshape(1)
119
+ if t.ndim != 1:
120
+ raise ValueError("thicknesses must be a scalar or 1-D sequence")
121
+ if method == "matrix_exp":
122
+ return faster_propagate_matrix_exp(system, t, max_batch=max_batch)
123
+ if method == "bloch_eigen":
124
+ return _propagate_bloch_eigen(system, t)
125
+ raise ValueError(f"method must be 'matrix_exp' or 'bloch_eigen', got {method!r}")
126
+
127
+
128
+ def enable(device: Literal["cpu", "gpu"] = "gpu") -> None:
129
+ """Inject faster-diffbloch acceleration into diffBloch runtime."""
130
+ global _CURRENT_DEVICE
131
+ _CURRENT_DEVICE = device
132
+ import importlib
133
+ try:
134
+ solver = importlib.import_module("diffBloch.core.solver")
135
+ solver._propagate_matrix_exp = faster_propagate_matrix_exp
136
+ solver.propagate = faster_propagate
137
+ for mod_name in ("diffBloch.core", "diffBloch.engine.forward"):
138
+ try:
139
+ mod = importlib.import_module(mod_name)
140
+ setattr(mod, "propagate", faster_propagate)
141
+ except Exception:
142
+ pass
143
+ except ImportError:
144
+ pass
145
+
146
+
147
+ def disable() -> None:
148
+ """Restore original diffBloch functions."""
149
+ pass
@@ -0,0 +1,58 @@
1
+ """Native runtime builder and loader for faster-diffbloch."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ctypes
6
+ import os
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ NATIVE_DIR = Path(__file__).resolve().parent / "native"
12
+ BUILD_DIR = Path.home() / ".cache" / "faster_diffbloch"
13
+
14
+
15
+ def build_and_load_library() -> ctypes.CDLL:
16
+ """Build or load the cached native acceleration library."""
17
+ BUILD_DIR.mkdir(parents=True, exist_ok=True)
18
+ lib_name = "libfaster_diffbloch.dylib" if sys.platform == "darwin" else "libfaster_diffbloch.so"
19
+ lib_path = BUILD_DIR / lib_name
20
+
21
+ sources = [
22
+ NATIVE_DIR / "bridge_lib.c",
23
+ NATIVE_DIR / "batch_cgemm.c",
24
+ NATIVE_DIR / "native_scattering.c",
25
+ ]
26
+ if sys.platform == "darwin":
27
+ sources.append(NATIVE_DIR / "metal_batch_cgemm.m")
28
+ # Build metallib if metal is available
29
+ metal_src = NATIVE_DIR / "batch_cgemm.metal"
30
+ metallib_path = BUILD_DIR / "batch_cgemm.metallib"
31
+ if metal_src.exists() and (not metallib_path.exists() or metallib_path.stat().st_mtime < metal_src.stat().st_mtime):
32
+ try:
33
+ subprocess.run(
34
+ ["xcrun", "-sdk", "macosx", "metal", "-O3", "-c", str(metal_src), "-o", str(BUILD_DIR / "batch_cgemm.air")],
35
+ check=True, capture_output=True
36
+ )
37
+ subprocess.run(
38
+ ["xcrun", "-sdk", "macosx", "metallib", str(BUILD_DIR / "batch_cgemm.air"), "-o", str(metallib_path)],
39
+ check=True, capture_output=True
40
+ )
41
+ except Exception:
42
+ pass
43
+
44
+ stale = not lib_path.exists() or any(lib_path.stat().st_mtime < s.stat().st_mtime for s in sources if s.exists())
45
+ if stale:
46
+ frameworks = ["-framework", "Accelerate", "-framework", "Metal", "-framework", "Foundation"] if sys.platform == "darwin" else ["-lblas"]
47
+ cmd = [
48
+ "clang", "-std=c11", "-O3", "-fPIC", "-shared",
49
+ "-D_DEFAULT_SOURCE", "-Wno-unused-function", "-Wno-unused-variable",
50
+ f"-I{NATIVE_DIR}",
51
+ *[str(s) for s in sources if s.exists()],
52
+ *frameworks, "-lm", "-o", str(lib_path),
53
+ ]
54
+ res = subprocess.run(cmd, capture_output=True, text=True)
55
+ if res.returncode != 0:
56
+ raise RuntimeError(f"Failed to build faster-diffbloch native library:\n{res.stderr}")
57
+
58
+ return ctypes.CDLL(str(lib_path))
@@ -0,0 +1,21 @@
1
+ """CLI wrapper enabling faster-diffbloch before running diffbloch commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from .backend import enable
7
+
8
+
9
+ def main() -> None:
10
+ # Enable Metal GPU acceleration by default
11
+ enable(device="gpu")
12
+ try:
13
+ from diffBloch.app.cli import main as diffbloch_main
14
+ diffbloch_main()
15
+ except ImportError:
16
+ print("Error: diffbloch is not installed. Install diffbloch or run 'pip install diffbloch'.", file=sys.stderr)
17
+ sys.exit(1)
18
+
19
+
20
+ if __name__ == "__main__":
21
+ main()