jax — правило для Cursor: рекомендации по JAX и установка
Правило для Cursor задаёт принципы написания кода на JAX: чистые функции, контроль типов массивов, стабильная компиляция и проверка градиентов.
.cursor/rules/jax.mdcЧто делает jax
Правило применяется к файлам Python по маске **/*.py. Оно помогает агенту соблюдать ограничения преобразований jit, grad, vmap и pmap: передавать состояние явно, обновлять массивы через .at[] и заменять итераторы конструкциями управления потоком JAX. Это снижает риск ошибок из-за изменяемых глобальных переменных и несовместимых с компиляцией циклов.
Отдельные рекомендации касаются числовых типов и производительности. Для вычислений на ускорителях предлагается использовать float32, задавать тип констант явно и сохранять формы массивов между вызовами, чтобы не провоцировать повторную компиляцию. Для пакетной обработки предусмотрен vmap, а для случайных значений требуется разделять ключи через jax.random.split.
Правило также рекомендует аннотации типов с jax.Array, тестирование через pytest и проверку производных с jax.test_util.check_grads. Оно рассчитано на проекты с JAX: обычные Python-циклы внутри функций с jit могут мешать компиляции, поэтому их следует заменять средствами jax.lax.
Какие задачи решает
- Убрать изменяемое глобальное состояние из функций JAX
- Исправить обновление массивов без изменения на месте
- Заменить Python-циклы конструкциями
jax.lax - Снизить повторные компиляции при изменении форм массивов
- Проверить градиенты пользовательских вычислительных функций
Примеры запросов агенту
- Проверь файлы Python на соблюдение правила JAX и исправь изменяемое состояние в функциях.
- Перепиши цикл внутри
jitс помощью подходящей конструкцииjax.lax. - Проверь работу со случайными ключами и добавь тест градиентов для пользовательской функции.
Как установить jax
Вручную: положить файлы
.cursor/rules/jax.mdcВо frontmatter: description, globs, alwaysApply
Вручную: положить файлы
.claude/rules/jax.mdПоле paths во frontmatter ограничивает файлы: globs из источника → paths
AGENTS.md
Codex читает AGENTS.md: вставьте текст правила туда
Вручную: положить файлы
.github/instructions/jax.instructions.mdПоле applyTo задаёт, к каким файлам применять: globs из источника → applyTo
Вручную: положить файлы
.devin/rules/jax.mdВручную: положить файлы
.clinerules/jax.mdПо шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules
Вопросы о jax
Как установить jax в Cursor?
Добавь файл правила по пути .cursor/rules/jax.mdc в проект. После этого правило будет применяться к файлам, подходящим под маску **/*.py.
Что делает правило jax?
Оно направляет агента при написании и проверке кода на JAX: помогает соблюдать функциональную чистоту, корректно работать с массивами и ключами случайных чисел, учитывать особенности компиляции и проверять градиенты.
Как избежать повторной компиляции в JAX?
Правило советует сохранять постоянные формы входных массивов. Если размер зависит от не-массивного аргумента, можно рассмотреть static_argnums для jit.
Исходный текст
jax.mdc · CC0-1.0
---
description: Definitive guidelines for writing high-performance, functionally pure, and maintainable JAX code, focusing on common pitfalls and optimal patterns for accelerators.
globs: **/*.py
---
# jax Best Practices
JAX is the backbone of our AI/ML and numerical computing projects. Adhere to these principles for high-performance, reproducible, and maintainable JAX code.
## 1. Functional Purity: The Absolute Core
JAX transformations (`jit`, `grad`, `vmap`, `pmap`) operate exclusively on **functionally pure** code. This means functions must be free of side-effects: all inputs explicit, all results returned.
* **Avoid mutable global state:** JAX captures global values at first `jit` compilation, leading to stale values.
❌ BAD:
```python
g = 0
def impure_uses_globals(x):
return x + g # `g` is captured at first jit
# ... later g = 10, but jit(impure_uses_globals) still uses g=0
```
✅ GOOD: Pass all state explicitly.
```python
def pure_uses_globals(x, g_val):
return x + g_val
# ... pass g_val=0, then g_val=10
```
* **No in-place array mutation:** JAX arrays are immutable. Use the `.at[]` syntax for functional updates.
❌ BAD:
```python
import jax.numpy as jnp
arr = jnp.zeros((3,3))
arr[1, :] = 1.0 # TypeError!
```
✅ GOOD:
```python
import jax.numpy as jnp
arr = jnp.zeros((3,3))
updated_arr = arr.at[1, :].set(1.0) # Returns a new array
```
* **Avoid Python iterators in `jit`ted code:** Iterators introduce state.
❌ BAD:
```python
from jax import jit
def sum_iterator(it):
total = 0
for x in it: # Python loop with iterator
total += x
return total
# jit(sum_iterator)(iter(range(10))) # Will fail or give unexpected results
```
✅ GOOD: Use JAX control flow primitives.
```python
from jax import lax
import jax.numpy as jnp
def sum_array(arr):
# Use lax.scan or lax.fori_loop for JAX-compatible loops
return lax.fori_loop(0, arr.shape[0], lambda i, x: x + arr[i], 0)
# jit(sum_array)(jnp.arange(10))
```
## 2. Numerical Type Discipline
Prioritize `float32` for performance on accelerators. Avoid implicit `float64` promotion.
* **Explicit `dtype` for constants:**
❌ BAD: Implicitly typed Python floats can lead to `float64` promotion.
```python
import jax.numpy as jnp
x = jnp.ones(5, dtype=jnp.float32)
y = x * 2.0 # 2.0 is a Python float, can cause unwanted promotion
```
✅ GOOD: Use 0-D `jnp.array` with explicit `dtype` or `jnp.float32()`.
```python
import jax.numpy as jnp
x = jnp.ones(5, dtype=jnp.float32)
y = x * jnp.array(2.0, dtype=jnp.float32)
# Or more concisely:
z = x * jnp.float32(2.0)
```
## 3. Performance Considerations: Stable Compilation
Prevent costly recompilations and leverage XLA effectively.
* **Static shapes:** Keep input shapes static or pass them via `static_argnums` to `jit`. Dynamic shapes force recompilation.
❌ BAD:
```python
from jax import jit
def dynamic_shape_func(x):
return x.sum()
# jit(dynamic_shape_func)(jnp.ones(5))
# jit(dynamic_shape_func)(jnp.ones(10)) # Recompiles!
```
✅ GOOD:
```python
from jax import jit
@jit
def static_shape_func(x):
return x.sum()
# Call with consistent shapes
# static_shape_func(jnp.ones(5))
# static_shape_func(jnp.ones(5)) # Uses cached compilation
```
If shapes *must* vary, consider `static_argnums` for non-array arguments that determine shape.
* **JAX control flow primitives:** Always use `lax.scan`, `lax.while_loop`, `lax.cond` inside `jit`ted functions. Python control flow breaks XLA compilation.
❌ BAD:
```python
from jax import jit
@jit
def python_loop(x, n):
for _ in range(n): # Python loop inside jit
x = x * 2
return x
```
✅ GOOD:
```python
from jax import jit, lax
@jit
def jax_loop(x, n):
# lax.fori_loop(start, stop, body_fn, init_val)
return lax.fori_loop(0, n, lambda i, val: val * 2, x)
```
* **Vectorization with `vmap`:** For data-parallel batching, `vmap` is your tool.
```python
from jax import vmap
import jax.numpy as jnp
def elementwise_op(x, y):
return x + y # Operates on scalars or single elements
batched_op = vmap(elementwise_op)
# batched_op(jnp.array([1,2,3]), jnp.array([4,5,6])) # Applies elementwise
```
## 4. Code Organization and Randomness
* **Standard Imports:**
```python
import jax
import jax.numpy as jnp
import jax.random as jr
import jax.lax as lax
```
* **Randomness Management:** Use `jax.random` and explicitly split PRNGKeys. Never reuse a key.
❌ BAD:
```python
import jax.random as jr
key = jr.PRNGKey(0)
val1 = jr.normal(key, (5,)) # Key used
val2 = jr.normal(key, (5,)) # Key reused, not independent!
```
✅ GOOD:
```python
import jax.random as jr
key = jr.PRNGKey(0)
key, subkey1 = jr.split(key)
val1 = jr.normal(subkey1, (5,))
key, subkey2 = jr.split(key)
val2 = jr.normal(subkey2, (5,)) # Independent random values
```
## 5. Type Hints
Use standard Python type hints, especially `jax.Array` for JAX arrays. This improves readability and enables static analysis.
```python
import jax.numpy as jnp
from jax import Array, jit
@jit
def add_arrays(a: Array, b: Array) -> Array:
"""Adds two JAX arrays."""
return a + b
# Example usage:
# result = add_arrays(jnp.ones(5), jnp.ones(5))
```
## 6. Testing Approaches
* **`pytest` is standard:** Use `pytest` for unit and integration tests.
* **Gradient checking:** For custom operations or complex functions, use `jax.test_util.check_grads` to verify gradients.
```python
import jax
import jax.numpy as jnp
from jax.test_util import check_grads
def my_complex_func(x):
return jnp.sin(x) * jnp.exp(x)
# Test gradients numerically vs. analytically
# check_grads(my_complex_func, (jnp.array(1.0),), order=1)
```
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc