lucid-dl 2.11.0__tar.gz → 2.11.3__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.
Files changed (149) hide show
  1. {lucid_dl-2.11.0/lucid_dl.egg-info → lucid_dl-2.11.3}/PKG-INFO +13 -18
  2. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/README.md +11 -16
  3. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/__init__.py +4 -2
  4. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_backend/core.py +89 -9
  5. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_backend/metal.py +5 -1
  6. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_func/__init__.py +162 -0
  7. lucid_dl-2.11.0/lucid/_tensor/tensor_ops.py → lucid_dl-2.11.3/lucid/_tensor/base.py +64 -0
  8. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_tensor/tensor.py +63 -19
  9. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/autograd/__init__.py +4 -1
  10. lucid_dl-2.11.3/lucid/datasets/mnist.py +242 -0
  11. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/crossvit.py +1 -1
  12. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/efficientformer.py +2 -2
  13. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/maxvit.py +1 -1
  14. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/pvt.py +2 -2
  15. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imggen/__init__.py +1 -0
  16. lucid_dl-2.11.3/lucid/models/imggen/ncsn.py +402 -0
  17. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imggen/vae.py +1 -1
  18. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/efficientdet.py +24 -8
  19. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/rcnn.py +1 -1
  20. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/util.py +5 -0
  21. lucid_dl-2.11.3/lucid/nn/_kernel/__init__.py +1 -0
  22. lucid_dl-2.11.3/lucid/nn/_kernel/activation.py +188 -0
  23. lucid_dl-2.11.3/lucid/nn/_kernel/attention.py +125 -0
  24. {lucid_dl-2.11.0/lucid/_backend → lucid_dl-2.11.3/lucid/nn/_kernel}/conv.py +4 -13
  25. lucid_dl-2.11.3/lucid/nn/_kernel/embedding.py +72 -0
  26. lucid_dl-2.11.3/lucid/nn/_kernel/loss.py +416 -0
  27. lucid_dl-2.11.3/lucid/nn/_kernel/norm.py +365 -0
  28. {lucid_dl-2.11.0/lucid/_backend → lucid_dl-2.11.3/lucid/nn/_kernel}/pool.py +7 -27
  29. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/__init__.py +4 -0
  30. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_activation.py +19 -13
  31. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_attention.py +9 -0
  32. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_conv.py +5 -16
  33. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_loss.py +31 -32
  34. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_norm.py +60 -69
  35. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_pool.py +7 -7
  36. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_util.py +5 -1
  37. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/init/_dist.py +1 -0
  38. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/module.py +106 -11
  39. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/types.py +24 -2
  40. lucid_dl-2.11.3/lucid/visual/__init__.py +2 -0
  41. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/visual/graph.py +3 -0
  42. lucid_dl-2.11.3/lucid/visual/mermaid.py +818 -0
  43. {lucid_dl-2.11.0 → lucid_dl-2.11.3/lucid_dl.egg-info}/PKG-INFO +13 -18
  44. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid_dl.egg-info/SOURCES.txt +11 -3
  45. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/setup.py +2 -2
  46. lucid_dl-2.11.0/lucid/datasets/mnist.py +0 -113
  47. lucid_dl-2.11.0/lucid/visual/__init__.py +0 -1
  48. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/LICENSE +0 -0
  49. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_backend/__init__.py +0 -0
  50. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_func/bfunc.py +0 -0
  51. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_func/gfunc.py +0 -0
  52. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_func/ufunc.py +0 -0
  53. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_fusion/__init__.py +0 -0
  54. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_fusion/base.py +0 -0
  55. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_fusion/func.py +0 -0
  56. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_tensor/__init__.py +0 -0
  57. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_util/__init__.py +0 -0
  58. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/_util/func.py +0 -0
  59. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/data/__init__.py +0 -0
  60. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/data/_base.py +0 -0
  61. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/data/_util.py +0 -0
  62. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/datasets/__init__.py +0 -0
  63. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/datasets/_base.py +0 -0
  64. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/datasets/cifar.py +0 -0
  65. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/einops/__init__.py +0 -0
  66. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/einops/_func.py +0 -0
  67. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/error.py +0 -0
  68. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/linalg/__init__.py +0 -0
  69. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/linalg/_func.py +0 -0
  70. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/__init__.py +0 -0
  71. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/__init__.py +0 -0
  72. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/alex.py +0 -0
  73. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/coatnet.py +0 -0
  74. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/convnext.py +0 -0
  75. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/cspnet.py +0 -0
  76. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/cvt.py +0 -0
  77. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/dense.py +0 -0
  78. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/efficient.py +0 -0
  79. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/inception.py +0 -0
  80. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/inception_next.py +0 -0
  81. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/inception_res.py +0 -0
  82. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/lenet.py +0 -0
  83. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/mobile.py +0 -0
  84. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/resnest.py +0 -0
  85. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/resnet.py +0 -0
  86. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/resnext.py +0 -0
  87. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/senet.py +0 -0
  88. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/sknet.py +0 -0
  89. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/swin.py +0 -0
  90. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/vgg.py +0 -0
  91. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/vit.py +0 -0
  92. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/xception.py +0 -0
  93. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imgclf/zfnet.py +0 -0
  94. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/imggen/ddpm.py +0 -0
  95. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/__init__.py +0 -0
  96. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/detr.py +0 -0
  97. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/fast_rcnn.py +0 -0
  98. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/faster_rcnn.py +0 -0
  99. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/yolo/__init__.py +0 -0
  100. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/yolo/yolo_v1.py +0 -0
  101. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/yolo/yolo_v2.py +0 -0
  102. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/yolo/yolo_v3.py +0 -0
  103. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/objdet/yolo/yolo_v4.py +0 -0
  104. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/seq2seq/__init__.py +0 -0
  105. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/seq2seq/transformer.py +0 -0
  106. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/models/util.py +0 -0
  107. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/__init__.py +0 -0
  108. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_drop.py +0 -0
  109. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_linear.py +0 -0
  110. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/functional/_spatial.py +0 -0
  111. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/fused.py +0 -0
  112. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/init/__init__.py +0 -0
  113. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/__init__.py +0 -0
  114. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/activation.py +0 -0
  115. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/attention.py +0 -0
  116. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/conv.py +0 -0
  117. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/drop.py +0 -0
  118. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/einops.py +0 -0
  119. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/linear.py +0 -0
  120. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/loss.py +0 -0
  121. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/norm.py +0 -0
  122. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/pool.py +0 -0
  123. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/rnn.py +0 -0
  124. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/sparse.py +0 -0
  125. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/transformer.py +0 -0
  126. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/modules/vision.py +0 -0
  127. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/parameter.py +0 -0
  128. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/nn/util.py +0 -0
  129. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/__init__.py +0 -0
  130. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/_base.py +0 -0
  131. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/ada.py +0 -0
  132. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/adam.py +0 -0
  133. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/lr_scheduler/__init__.py +0 -0
  134. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/lr_scheduler/_base.py +0 -0
  135. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/lr_scheduler/_schedulers.py +0 -0
  136. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/prop.py +0 -0
  137. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/optim/sgd.py +0 -0
  138. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/port.py +0 -0
  139. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/random/__init__.py +0 -0
  140. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/random/_func.py +0 -0
  141. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/transforms/__init__.py +0 -0
  142. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/transforms/_base.py +0 -0
  143. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/transforms/image.py +0 -0
  144. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/weights/__init__.py +0 -0
  145. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid/weights/__init__.pyi +0 -0
  146. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid_dl.egg-info/dependency_links.txt +0 -0
  147. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid_dl.egg-info/requires.txt +0 -0
  148. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/lucid_dl.egg-info/top_level.txt +0 -0
  149. {lucid_dl-2.11.0 → lucid_dl-2.11.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lucid-dl
3
- Version: 2.11.0
3
+ Version: 2.11.3
4
4
  Summary: Lumerico's Comprehensive Interface for Deep Learning
5
5
  Home-page: https://github.com/ChanLumerico/lucid
6
6
  Author: ChanLumerico
@@ -8,7 +8,7 @@ Author-email: greensox284@gmail.com
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.12
11
+ Requires-Python: >=3.14
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: numpy
@@ -33,7 +33,7 @@ Dynamic: summary
33
33
  [![PyPI Total Downloads](https://static.pepy.tech/personalized-badge/lucid-dl?period=total&units=NONE&left_color=GRAY&right_color=yellow&left_text=total%20downloads)](https://pepy.tech/projects/lucid-dl)
34
34
  ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/ChanLumerico/lucid.svg)
35
35
  ![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)
36
- ![Lines of Code](https://img.shields.io/badge/lines%20of%20code-27.7k-purple.svg)
36
+ ![Lines of Code](https://img.shields.io/badge/dynamic/json?label=Lines%20of%20Code&color=purple&url=https%3A%2F%2Fraw.githubusercontent.com%2FChanLumerico%2Flucid%2Fmain%2Floc%2Floc_badge.json&query=%24.linesOfCode&cacheSeconds=3600)
37
37
 
38
38
  **Lucid** is a minimalist deep learning framework built entirely from scratch in Python. It offers a pedagogically rich environment to explore the foundations of modern deep learning systems, including autodiff, neural network modules, and GPU acceleration — all while staying lightweight, readable, and free of complex dependencies.
39
39
 
@@ -48,24 +48,19 @@ Whether you're a student, educator, or an advanced researcher seeking to demysti
48
48
 
49
49
  ### 🔥 What's New
50
50
 
51
- - Now supports [**`Safetensors`**](https://github.com/huggingface/safetensors) for Lucid neural module porting along with the legacy `.lcd` format
51
+ - Added additional `nn.Module` hooks for richer introspection during training:
52
52
 
53
- - Branched a Stand-alone Autograd Engine as `lucid.autograd`
53
+ ```python
54
+ def register_forward_pre_hook(self, hook: Callable, *, with_kwargs: bool = False)
54
55
 
55
- - Provides a generalized API of computing gradients:
56
-
57
- ```python
58
- import lucid.autograd as autograd
59
- x = lucid.Tensor([1., 2.], requires_grad=True)
60
- y = (x ** 2).sum()
61
- autograd.grad(y, x) # ∂y/∂x
62
- ```
56
+ def register_forward_hook(self, hook: Callable, *, with_kwargs: bool = False)
63
57
 
64
- - Introduced **Backward Fusion** for CPU execution:
65
- - Automatically fuses selected operation patterns during backpropagation to reduce graph overhead
66
- - Supports identity/unary fusion (e.g. `log∘exp`, double negation, and view-like ops such as reshape/squeeze)
67
- - Uses heuristic thresholds to avoid fusion overhead on small tensors
68
- - Disabled by default on GPU paths to ensure stable performance
58
+ def register_backward_hook(self, hook: Callable)
59
+
60
+ def register_full_backward_pre_hook(self, hook: Callable)
61
+
62
+ def register_full_backward_hook(self, hook: Callable)
63
+ ```
69
64
 
70
65
  ## 🔧 How to Install
71
66
 
@@ -5,7 +5,7 @@
5
5
  [![PyPI Total Downloads](https://static.pepy.tech/personalized-badge/lucid-dl?period=total&units=NONE&left_color=GRAY&right_color=yellow&left_text=total%20downloads)](https://pepy.tech/projects/lucid-dl)
6
6
  ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/ChanLumerico/lucid.svg)
7
7
  ![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)
8
- ![Lines of Code](https://img.shields.io/badge/lines%20of%20code-27.7k-purple.svg)
8
+ ![Lines of Code](https://img.shields.io/badge/dynamic/json?label=Lines%20of%20Code&color=purple&url=https%3A%2F%2Fraw.githubusercontent.com%2FChanLumerico%2Flucid%2Fmain%2Floc%2Floc_badge.json&query=%24.linesOfCode&cacheSeconds=3600)
9
9
 
10
10
  **Lucid** is a minimalist deep learning framework built entirely from scratch in Python. It offers a pedagogically rich environment to explore the foundations of modern deep learning systems, including autodiff, neural network modules, and GPU acceleration — all while staying lightweight, readable, and free of complex dependencies.
11
11
 
@@ -20,24 +20,19 @@ Whether you're a student, educator, or an advanced researcher seeking to demysti
20
20
 
21
21
  ### 🔥 What's New
22
22
 
23
- - Now supports [**`Safetensors`**](https://github.com/huggingface/safetensors) for Lucid neural module porting along with the legacy `.lcd` format
23
+ - Added additional `nn.Module` hooks for richer introspection during training:
24
24
 
25
- - Branched a Stand-alone Autograd Engine as `lucid.autograd`
25
+ ```python
26
+ def register_forward_pre_hook(self, hook: Callable, *, with_kwargs: bool = False)
26
27
 
27
- - Provides a generalized API of computing gradients:
28
-
29
- ```python
30
- import lucid.autograd as autograd
31
- x = lucid.Tensor([1., 2.], requires_grad=True)
32
- y = (x ** 2).sum()
33
- autograd.grad(y, x) # ∂y/∂x
34
- ```
28
+ def register_forward_hook(self, hook: Callable, *, with_kwargs: bool = False)
35
29
 
36
- - Introduced **Backward Fusion** for CPU execution:
37
- - Automatically fuses selected operation patterns during backpropagation to reduce graph overhead
38
- - Supports identity/unary fusion (e.g. `log∘exp`, double negation, and view-like ops such as reshape/squeeze)
39
- - Uses heuristic thresholds to avoid fusion overhead on small tensors
40
- - Disabled by default on GPU paths to ensure stable performance
30
+ def register_backward_hook(self, hook: Callable)
31
+
32
+ def register_full_backward_pre_hook(self, hook: Callable)
33
+
34
+ def register_full_backward_hook(self, hook: Callable)
35
+ ```
41
36
 
42
37
  ## 🔧 How to Install
43
38
 
@@ -64,9 +64,11 @@ inf = math.inf
64
64
 
65
65
  Int = types.Int
66
66
  Int8, Int16, Int32, Int64 = (types.Int8, types.Int16, types.Int32, types.Int64)
67
+ Char, Short, Long = (Int8, Int16, Int64)
67
68
 
68
69
  Float = types.Float
69
70
  Float16, Float32, Float64 = (types.Float16, types.Float32, types.Float64)
71
+ Half, Double = (Float16, Float64)
70
72
 
71
73
  Complex = types.Complex
72
74
  Complex64 = types.Complex64
@@ -306,9 +308,9 @@ def register_model(func: _ModuleReturnFunc) -> _ModuleReturnFunc:
306
308
 
307
309
 
308
310
  def _conv_view_limit_mb() -> int:
309
- from lucid._backend import conv as _conv_backend
311
+ from lucid._kernel import conv as _conv_kernel
310
312
 
311
- return _conv_backend.get_conv_view_limit_mb()
313
+ return _conv_kernel.get_conv_view_limit_mb()
312
314
 
313
315
 
314
316
  def __getattr__(name: str) -> Any:
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Callable, Tuple, ClassVar
2
+ from typing import Callable, Self, Tuple, ClassVar
3
3
  import functools
4
4
  import weakref
5
5
 
@@ -37,6 +37,7 @@ def func_op(
37
37
  requires_grad = False
38
38
  is_free = True
39
39
  dtype_hint: _BuiltinNumeric | Numeric | None = None
40
+ inplace_target: _TensorLike | None = None
40
41
 
41
42
  if n_in is None:
42
43
  tensor_args = args
@@ -68,8 +69,42 @@ def func_op(
68
69
  + f"('{type(op_self).__name__}')."
69
70
  )
70
71
 
72
+ if op_self._inplace:
73
+ if n_ret != 1:
74
+ raise ValueError("inplace op must have a single return value.")
75
+ if not (0 <= op_self._inplace_target < len(tensors)):
76
+ raise ValueError("inplace_target is out of range.")
77
+
78
+ target = tensors[op_self._inplace_target]
79
+ if lucid.grad_enabled() and target.requires_grad and target.is_leaf:
80
+ raise RuntimeError(
81
+ "A leaf tensor with 'requires_grad=True' "
82
+ "cannot be subjected to inplace operations."
83
+ )
84
+ inplace_target = target
85
+
86
+ proxy = target.new_tensor()
87
+ proxy._op = target._op
88
+ proxy._prev = list(target._prev)
89
+ proxy._backward_op = target._backward_op
90
+ proxy._backward_hooks = list(target._backward_hooks)
91
+ proxy.grad = None
92
+ proxy._version = target._version
93
+
94
+ if hasattr(target, "_is_free"):
95
+ proxy._is_free = target._is_free
96
+ if hasattr(target, "_is_bool_tensor"):
97
+ proxy._is_bool_tensor = target._is_bool_tensor
98
+
99
+ tensors = (
100
+ tensors[: op_self._inplace_target]
101
+ + (proxy,)
102
+ + tensors[op_self._inplace_target + 1 :]
103
+ )
104
+
71
105
  non_tensor_args = args[n_in:] if n_in is not None else ()
72
106
  new_args = (*tensors, *non_tensor_args)
107
+
73
108
  func_return_pairs = forward_func(op_self, *new_args, **kwargs)
74
109
 
75
110
  tensor_refs = tuple(weakref.ref(t) for t in tensors)
@@ -92,6 +127,25 @@ def func_op(
92
127
 
93
128
  if num_returns == 1:
94
129
  func_return_pairs: _FuncOpReturnType = (func_return_pairs,)
130
+ elif op_self._inplace:
131
+ raise ValueError("inplace op must have a single return value.")
132
+
133
+ if op_self._inplace:
134
+ (ret_value, grad_func) = func_return_pairs[0]
135
+ target = inplace_target
136
+ if target is None:
137
+ raise RuntimeError("Missing inplace target tensor.")
138
+ target.data = ret_value.data
139
+
140
+ if ret_value.dtype is bool:
141
+ target._is_bool_tensor = True
142
+ target.dtype = bool
143
+ else:
144
+ target._is_bool_tensor = False
145
+ target.dtype = ret_value.dtype
146
+
147
+ target._version += 1
148
+ func_return_pairs = ((target, grad_func),)
95
149
 
96
150
  results: Tuple[_TensorLike, ...] = tuple()
97
151
  for result, grad_func in func_return_pairs:
@@ -113,6 +167,7 @@ def func_op(
113
167
  forward_op_ref=weakref.ref(op_self),
114
168
  grad_func=grad_func,
115
169
  tensor_refs=tensor_refs,
170
+ versions=tuple(t._version for t in tensors),
116
171
  device=device,
117
172
  )
118
173
 
@@ -138,15 +193,15 @@ def func_op(
138
193
 
139
194
 
140
195
  def unary_func_op(has_gradient: bool = True, device: _DeviceType = "cpu") -> Callable:
141
- return func_op(1, 1, has_gradient=has_gradient, device=device)
196
+ return func_op(1, 1, has_gradient, device)
142
197
 
143
198
 
144
199
  def binary_func_op(has_gradient: bool = True, device: _DeviceType = "cpu") -> Callable:
145
- return func_op(2, 1, has_gradient=has_gradient, device=device)
200
+ return func_op(2, 1, has_gradient, device)
146
201
 
147
202
 
148
203
  def poly_func_op(has_gradient: bool = True, device: _DeviceType = "cpu") -> Callable:
149
- return func_op(None, 1, has_gradient=has_gradient, device=device)
204
+ return func_op(None, 1, has_gradient, device)
150
205
 
151
206
 
152
207
  class Operation(ABC):
@@ -154,11 +209,18 @@ class Operation(ABC):
154
209
 
155
210
  def __init__(self) -> None:
156
211
  self.result: _TensorLike | tuple[_TensorLike, ...] | None = None
212
+ self._inplace: bool = False
213
+ self._inplace_target: int = 0
157
214
  self._flops: int | None = None
158
215
 
159
216
  def clear(self) -> None:
160
217
  self.result = None
161
218
 
219
+ def inplace(self, target: int = 0) -> Self:
220
+ self._inplace = True
221
+ self._inplace_target = target
222
+ return self
223
+
162
224
  @abstractmethod
163
225
  def cpu(self, *args, **kwargs) -> _FuncOpReturnType: ...
164
226
 
@@ -201,12 +263,14 @@ class BackwardOperation:
201
263
  forward_op_ref: weakref.ref[Operation] | None,
202
264
  grad_func: _GradFuncType | None,
203
265
  tensor_refs: tuple[weakref.ref[_TensorLike]],
266
+ versions: tuple[int, ...] = (),
204
267
  device: _DeviceType | None = "cpu",
205
268
  custom_closure: Callable[[], None] | None = None,
206
269
  ) -> None:
207
270
  self.forward_op_ref = forward_op_ref
208
271
  self.grad_func = grad_func
209
272
  self.tensor_refs = tensor_refs
273
+ self.versions = versions
210
274
  self.device = device
211
275
 
212
276
  self.custom_closure = custom_closure
@@ -215,18 +279,36 @@ class BackwardOperation:
215
279
  if self.grad_func is None and self.custom_closure is None:
216
280
  raise ValueError("Either 'grad_func' or 'custom_closure' must be provided.")
217
281
 
282
+ if len(tensor_refs) != len(versions):
283
+ raise ValueError("Numbers of 'tensor_refs' and 'versions' do not match.")
284
+
218
285
  def override_grad_func(self, new_grad_func: _GradFuncType) -> None:
219
286
  if self.custom_closure is not None:
220
287
  return
221
288
  self.grad_func = new_grad_func
222
289
 
223
290
  def override_tensor_refs(
224
- self, new_tensor_refs: tuple[weakref.ref[_TensorLike]]
291
+ self,
292
+ new_tensor_refs: tuple[weakref.ref[_TensorLike]],
293
+ new_versions: tuple[int, ...] | None = None,
225
294
  ) -> None:
226
295
  self.tensor_refs = new_tensor_refs
227
296
  self.num_inputs = len(new_tensor_refs)
297
+ if new_versions is not None:
298
+ if len(new_versions) != len(new_tensor_refs):
299
+ raise ValueError(
300
+ "Numbers of 'tensor_refs' and 'versions' do not match."
301
+ )
302
+ self.versions = new_versions
228
303
 
229
304
  def __call__(self) -> None:
305
+ live_tensors = tuple(ref() for ref in self.tensor_refs)
306
+ if not live_tensors or any(t is None for t in live_tensors):
307
+ return
308
+
309
+ if any(ver != t._version for ver, t in zip(self.versions, live_tensors)):
310
+ raise RuntimeError(f"Tensor version mismatch detected.")
311
+
230
312
  if self.custom_closure is not None:
231
313
  self.custom_closure()
232
314
  return
@@ -240,16 +322,14 @@ class BackwardOperation:
240
322
  if self.num_inputs == 1 or not isinstance(grads, tuple):
241
323
  grads = (grads,)
242
324
 
243
- live_tensors = tuple(ref() for ref in self.tensor_refs)
244
- if any(t is None for t in live_tensors):
245
- return
246
-
247
325
  if len(grads) != len(live_tensors):
248
326
  raise ValueError(
249
327
  f"Expected {len(live_tensors)} gradients, got {len(grads)}."
250
328
  )
251
329
 
252
330
  for tensor, grad in zip(live_tensors, grads):
331
+ if not tensor.requires_grad and grad is None:
332
+ continue
253
333
  new_grad = lucid._match_grad_shape(tensor.data, grad, device=self.device)
254
334
  lucid._set_tensor_grad(tensor, new_grad)
255
335
 
@@ -4,6 +4,8 @@ import platform
4
4
 
5
5
  import numpy as np
6
6
 
7
+ from lucid.types import _TensorLike
8
+
7
9
  try:
8
10
  import mlx.core as mx
9
11
  except ModuleNotFoundError as e:
@@ -105,7 +107,9 @@ def parse_mlx_indexing(index: Any) -> Any:
105
107
  return index
106
108
 
107
109
 
108
- def post_step_eval(param: Any, state: Mapping[str, Any] | None = None) -> None:
110
+ def post_step_eval(
111
+ param: _TensorLike | Any, state: Mapping[str, Any] | None = None
112
+ ) -> None:
109
113
  is_gpu = False
110
114
  if hasattr(param, "is_gpu"):
111
115
  try:
@@ -34,18 +34,50 @@ def add(a: Tensor, b: Tensor, /) -> Tensor:
34
34
  return bfunc.add()(a, b)
35
35
 
36
36
 
37
+ def add_(a: Tensor, b: Tensor) -> Tensor:
38
+ return bfunc.add().inplace()(a, b)
39
+
40
+
37
41
  def sub(a: Tensor, b: Tensor, /) -> Tensor:
38
42
  return bfunc.sub()(a, b)
39
43
 
40
44
 
45
+ def sub_(a: Tensor, b: Tensor, /) -> Tensor:
46
+ return bfunc.sub().inplace()(a, b)
47
+
48
+
41
49
  def multiply(a: Tensor, b: Tensor, /) -> Tensor:
42
50
  return bfunc.multiply()(a, b)
43
51
 
44
52
 
53
+ def mul_(a: Tensor, b: Tensor, /) -> Tensor:
54
+ return bfunc.multiply().inplace()(a, b)
55
+
56
+
45
57
  def div(a: Tensor, b: Tensor, /, floor: bool = False) -> Tensor:
46
58
  return bfunc.truediv()(a, b) if not floor else bfunc.floordiv()(a, b)
47
59
 
48
60
 
61
+ def div_(a: Tensor, b: Tensor, /, floor: bool = False) -> Tensor:
62
+ return (
63
+ bfunc.truediv().inplace()(a, b)
64
+ if not floor
65
+ else bfunc.floordiv().inplace()(a, b)
66
+ )
67
+
68
+
69
+ def minimum_(a: Tensor, b: Tensor, /) -> Tensor:
70
+ return bfunc.minimum().inplace()(a, b)
71
+
72
+
73
+ def maximum_(a: Tensor, b: Tensor, /) -> Tensor:
74
+ return bfunc.maximum().inplace()(a, b)
75
+
76
+
77
+ def power_(a: Tensor, b: Tensor, /) -> Tensor:
78
+ return bfunc.power().inplace()(a, b)
79
+
80
+
49
81
  def _equal(a: Tensor, b: Tensor, /) -> Tensor:
50
82
  return bfunc._equal()(a, b)
51
83
 
@@ -237,6 +269,92 @@ def cube(a: Tensor, /) -> Tensor:
237
269
  return ufunc.cube()(a)
238
270
 
239
271
 
272
+ def neg_(a: Tensor, /) -> Tensor:
273
+ return ufunc._neg().inplace()(a)
274
+
275
+
276
+ def exp_(a: Tensor, /) -> Tensor:
277
+ return ufunc.exp().inplace()(a)
278
+
279
+
280
+ def log_(a: Tensor, /) -> Tensor:
281
+ return ufunc.log().inplace()(a)
282
+
283
+
284
+ def log2_(a: Tensor, /) -> Tensor:
285
+ return ufunc.log2().inplace()(a)
286
+
287
+
288
+ def sqrt_(a: Tensor, /) -> Tensor:
289
+ return ufunc.sqrt().inplace()(a)
290
+
291
+
292
+ def sin_(a: Tensor, /) -> Tensor:
293
+ return ufunc.sin().inplace()(a)
294
+
295
+
296
+ def cos_(a: Tensor, /) -> Tensor:
297
+ return ufunc.cos().inplace()(a)
298
+
299
+
300
+ def tan_(a: Tensor, /) -> Tensor:
301
+ return ufunc.tan().inplace()(a)
302
+
303
+
304
+ def arcsin_(a: Tensor, /) -> Tensor:
305
+ return ufunc.arcsin().inplace()(a)
306
+
307
+
308
+ def arccos_(a: Tensor, /) -> Tensor:
309
+ return ufunc.arccos().inplace()(a)
310
+
311
+
312
+ def arctan_(a: Tensor, /) -> Tensor:
313
+ return ufunc.arctan().inplace()(a)
314
+
315
+
316
+ def sinh_(a: Tensor, /) -> Tensor:
317
+ return ufunc.sinh().inplace()(a)
318
+
319
+
320
+ def cosh_(a: Tensor, /) -> Tensor:
321
+ return ufunc.cosh().inplace()(a)
322
+
323
+
324
+ def tanh_(a: Tensor, /) -> Tensor:
325
+ return ufunc.tanh().inplace()(a)
326
+
327
+
328
+ def clip_(
329
+ a: Tensor, /, min_value: _Scalar | None = None, max_value: _Scalar | None = None
330
+ ) -> Tensor:
331
+ if min_value is None:
332
+ min_value = lucid.min(a).item()
333
+ if max_value is None:
334
+ max_value = lucid.max(a).item()
335
+ return ufunc.clip(min_value, max_value).inplace()(a)
336
+
337
+
338
+ def abs_(a: Tensor, /) -> Tensor:
339
+ return ufunc._abs().inplace()(a)
340
+
341
+
342
+ def sign_(a: Tensor, /) -> Tensor:
343
+ return ufunc.sign().inplace()(a)
344
+
345
+
346
+ def reciprocal_(a: Tensor, /) -> Tensor:
347
+ return ufunc.reciprocal().inplace()(a)
348
+
349
+
350
+ def square_(a: Tensor, /) -> Tensor:
351
+ return ufunc.square().inplace()(a)
352
+
353
+
354
+ def cube_(a: Tensor, /) -> Tensor:
355
+ return ufunc.cube().inplace()(a)
356
+
357
+
240
358
  @property
241
359
  def _T(a: Tensor, /) -> Tensor:
242
360
  return ufunc._T()(a)
@@ -301,6 +419,18 @@ def ceil(a: Tensor) -> Tensor:
301
419
  return ufunc.ceil()(a)
302
420
 
303
421
 
422
+ def round_(a: Tensor, /, decimals: int = 0) -> Tensor:
423
+ return ufunc.round(decimals).inplace()(a)
424
+
425
+
426
+ def floor_(a: Tensor) -> Tensor:
427
+ return ufunc.floor().inplace()(a)
428
+
429
+
430
+ def ceil_(a: Tensor) -> Tensor:
431
+ return ufunc.ceil().inplace()(a)
432
+
433
+
304
434
  def cumprod(a: Tensor, axis: int = -1) -> Tensor:
305
435
  return ufunc.cumprod(axis)(a)
306
436
 
@@ -595,3 +725,35 @@ Tensor.swapaxes = swapaxes
595
725
  Tensor.round = round
596
726
  Tensor.floor = floor
597
727
  Tensor.ceil = ceil
728
+
729
+ Tensor.add_ = add_
730
+ Tensor.sub_ = sub_
731
+ Tensor.mul_ = mul_
732
+ Tensor.div_ = div_
733
+ Tensor.minimum_ = minimum_
734
+ Tensor.maximum_ = maximum_
735
+ Tensor.power_ = power_
736
+
737
+ Tensor.neg_ = neg_
738
+ Tensor.exp_ = exp_
739
+ Tensor.log_ = log_
740
+ Tensor.log2_ = log2_
741
+ Tensor.sqrt_ = sqrt_
742
+ Tensor.sin_ = sin_
743
+ Tensor.cos_ = cos_
744
+ Tensor.tan_ = tan_
745
+ Tensor.arcsin_ = arcsin_
746
+ Tensor.arccos_ = arccos_
747
+ Tensor.arctan_ = arctan_
748
+ Tensor.sinh_ = sinh_
749
+ Tensor.cosh_ = cosh_
750
+ Tensor.tanh_ = tanh_
751
+ Tensor.clip_ = clip_
752
+ Tensor.abs_ = abs_
753
+ Tensor.sign_ = sign_
754
+ Tensor.reciprocal_ = reciprocal_
755
+ Tensor.square_ = square_
756
+ Tensor.cube_ = cube_
757
+ Tensor.round_ = round_
758
+ Tensor.floor_ = floor_
759
+ Tensor.ceil_ = ceil_
@@ -133,3 +133,67 @@ class _TensorBase:
133
133
  def nonzero(self) -> Self: ...
134
134
 
135
135
  def diagonal(self, offset: int = 0, axis1: int = 0, axis2: int = 1) -> Self: ...
136
+
137
+
138
+ class _TensorInplace:
139
+ def add_(self, other: Self) -> Self: ...
140
+
141
+ def sub_(self, other: Self) -> Self: ...
142
+
143
+ def mul_(self, other: Self) -> Self: ...
144
+
145
+ def div_(self, other: Self, /, floor: bool = False) -> Self: ...
146
+
147
+ def minimum_(self, other: Self) -> Self: ...
148
+
149
+ def maximum_(self, other: Self) -> Self: ...
150
+
151
+ def power_(self, other: Self) -> Self: ...
152
+
153
+ def neg_(self) -> Self: ...
154
+
155
+ def exp_(self) -> Self: ...
156
+
157
+ def log_(self) -> Self: ...
158
+
159
+ def log2_(self) -> Self: ...
160
+
161
+ def sqrt_(self) -> Self: ...
162
+
163
+ def sin_(self) -> Self: ...
164
+
165
+ def cos_(self) -> Self: ...
166
+
167
+ def tan_(self) -> Self: ...
168
+
169
+ def arcsin_(self) -> Self: ...
170
+
171
+ def arccos_(self) -> Self: ...
172
+
173
+ def arctan_(self) -> Self: ...
174
+
175
+ def sinh_(self) -> Self: ...
176
+
177
+ def cosh_(self) -> Self: ...
178
+
179
+ def tanh_(self) -> Self: ...
180
+
181
+ def clip_(
182
+ self, min_value: _Scalar | None = None, max_value: _Scalar | None = None
183
+ ) -> Self: ...
184
+
185
+ def abs_(self) -> Self: ...
186
+
187
+ def sign_(self) -> Self: ...
188
+
189
+ def reciprocal_(self) -> Self: ...
190
+
191
+ def square_(self) -> Self: ...
192
+
193
+ def cube_(self) -> Self: ...
194
+
195
+ def round_(self, decimals: int = 0) -> Self: ...
196
+
197
+ def floor_(self) -> Self: ...
198
+
199
+ def ceil_(self) -> Self: ...