From 42fd77798f4021429cb367769f99bdaea05f4620 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 8 Apr 2026 01:29:24 +0100 Subject: [PATCH 1/7] Update translation: lectures/autodiff.md --- lectures/autodiff.md | 503 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 lectures/autodiff.md diff --git a/lectures/autodiff.md b/lectures/autodiff.md new file mode 100644 index 0000000..059e67a --- /dev/null +++ b/lectures/autodiff.md @@ -0,0 +1,503 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.17.2 +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +--- + +# ماجراهایی با مشتق‌گیری خودکار + + +```{include} _admonition/gpu.md +``` + +## مرور کلی + +این درس مقدمه‌ای جامع‌تر بر مشتق‌گیری خودکار با استفاده از Google JAX ارائه می‌دهد و بر پایه {doc}`معرفی مختصر قبلی ما ` بنا شده است. + +مشتق‌گیری خودکار یکی از عناصر کلیدی یادگیری ماشین و هوش مصنوعی مدرن است. + +به همین دلیل، سرمایه‌گذاری قابل توجهی بر روی آن انجام شده و پیاده‌سازی‌های قدرتمند متعددی در دسترس است. + +یکی از بهترین این پیاده‌سازی‌ها، روتین‌های مشتق‌گیری خودکار موجود در JAX است. + +در حالی که سایر بسته‌های نرم‌افزاری نیز این قابلیت را ارائه می‌دهند، نسخه JAX به ویژه قدرتمند است زیرا به خوبی با سایر اجزای اصلی JAX (مانند کامپایل JIT و موازی‌سازی) ادغام می‌شود. + +مشتق‌گیری خودکار نه تنها برای هوش مصنوعی، بلکه برای بسیاری از مسائل مدل‌سازی ریاضی نیز قابل استفاده است؛ از جمله بهینه‌سازی غیرخطی چندبُعدی و مسائل یافتن ریشه. + +علاوه بر آنچه در Anaconda موجود است، این درس به کتابخانه‌های زیر نیاز دارد: + +```{code-cell} ipython3 +:tags: [hide-output] + +!pip install jax +``` + +به واردسازی‌های زیر نیاز داریم: + +```{code-cell} ipython3 +import jax +import jax.numpy as jnp +import matplotlib.pyplot as plt +import numpy as np +from sympy import symbols +``` + +## مشتق‌گیری خودکار چیست؟ + +مشتق‌گیری خودکار (Autodiff) تکنیکی برای محاسبه مشتقات روی کامپیوتر است. + +### مشتق‌گیری خودکار، تفاضل محدود نیست + +مشتق $f(x) = \exp(2x)$ برابر است با: + +$$ + f'(x) = 2 \exp(2x) +$$ + +یک کامپیوتر که نمی‌داند چگونه مشتق بگیرد، ممکن است این مشتق را با نسبت تفاضل محدود تقریب بزند: + +$$ + (Df)(x) := \frac{f(x+h) - f(x)}{h} +$$ + +که در آن $h$ یک عدد مثبت کوچک است. + +```{code-cell} ipython3 +def f(x): + "Original function." + return np.exp(2 * x) + +def f_prime(x): + "True derivative." + return 2 * np.exp(2 * x) + +def Df(x, h=0.1): + "Approximate derivative (finite difference)." + return (f(x + h) - f(x))/h + +x_grid = np.linspace(-2, 1, 200) +fig, ax = plt.subplots() +ax.plot(x_grid, f_prime(x_grid), label="$f'$") +ax.plot(x_grid, Df(x_grid), label="$Df$") +ax.legend() +plt.show() +``` + +این نوع مشتق عددی اغلب نادقیق و ناپایدار است. + +یکی از دلایل آن این است که: + +$$ + \frac{f(x+h) - f(x)}{h} \approx \frac{0}{0} +$$ + +اعداد کوچک در صورت و مخرج باعث خطاهای گرد کردن می‌شوند. + +این وضعیت در ابعاد بالا و با مشتقات مرتبه بالاتر به صورت نمایی بدتر می‌شود. + ++++ + +### مشتق‌گیری خودکار، حساب نمادین نیست + ++++ + +حساب نمادین تلاش می‌کند از قواعد مشتق‌گیری برای تولید یک عبارت بسته واحد که نمایانگر مشتق است استفاده کند. + +```{code-cell} ipython3 +m, a, b, x = symbols('m a b x') +f_x = (a*x + b)**m +f_x.diff((x, 6)) # 6-th order derivative +``` + +حساب نمادین برای محاسبات با کارایی بالا مناسب نیست. + +یک نقطه ضعف این است که حساب نمادین نمی‌تواند از طریق جریان کنترل مشتق بگیرد. + +همچنین، استفاده از حساب نمادین ممکن است شامل محاسبات اضافی باشد. + +به عنوان مثال، در نظر بگیرید: + +$$ + (f g h)' + = (f' g + g' f) h + (f g) h' +$$ + +اگر در $x$ ارزیابی کنیم، $f(x)$ و $g(x)$ هرکدام دو بار ارزیابی می‌شوند. + +همچنین، محاسبه $f'(x)$ و $f(x)$ ممکن است شامل جملات مشترک باشد (مثلاً $f(x) = \exp(2x) \implies f'(x) = 2f(x)$) اما این در جبر نمادین مورد بهره‌برداری قرار نمی‌گیرد. + ++++ + +### مشتق‌گیری خودکار + +مشتق‌گیری خودکار توابعی تولید می‌کند که مشتقات را در مقادیر عددی ارسال‌شده توسط کد فراخوان ارزیابی می‌کنند، نه اینکه یک عبارت نمادین واحد نمایانگر کل مشتق تولید کنند. + +مشتقات با تجزیه محاسبات به اجزای کوچک‌تر از طریق قاعده زنجیر ساخته می‌شوند. + +قاعده زنجیر تا جایی اعمال می‌شود که جملات به توابع پایه‌ای تقلیل یابند که برنامه می‌داند چگونه به طور دقیق از آن‌ها مشتق بگیرد (جمع، تفریق، توان‌گیری، سینوس و کسینوس و غیره). + ++++ + +## برخی آزمایش‌ها + ++++ + +بیایید با برخی توابع مقدار حقیقی روی $\mathbb R$ شروع کنیم. + ++++ + +### یک تابع قابل مشتق‌گیری + ++++ + +بیایید مشتق‌گیری خودکار JAX را با یک تابع نسبتاً ساده آزمایش کنیم. + +```{code-cell} ipython3 +def f(x): + return jnp.sin(x) - 2 * jnp.cos(3 * x) * jnp.exp(- x**2) +``` + +از `grad` برای محاسبه گرادیان یک تابع مقدار حقیقی استفاده می‌کنیم: + +```{code-cell} ipython3 +f_prime = jax.grad(f) +``` + +بیایید نتیجه را رسم کنیم: + +```{code-cell} ipython3 +x_grid = jnp.linspace(-5, 5, 100) +``` + +```{code-cell} ipython3 +fig, ax = plt.subplots() +ax.plot(x_grid, [f(x) for x in x_grid], label="$f$") +ax.plot(x_grid, [f_prime(x) for x in x_grid], label="$f'$") +ax.legend() +plt.show() +``` + +### تابع قدر مطلق + ++++ + +اگر تابع قابل مشتق‌گیری نباشد چه اتفاقی می‌افتد؟ + +```{code-cell} ipython3 +def f(x): + return jnp.abs(x) +``` + +```{code-cell} ipython3 +f_prime = jax.grad(f) +``` + +```{code-cell} ipython3 +fig, ax = plt.subplots() +ax.plot(x_grid, [f(x) for x in x_grid], label="$f$") +ax.plot(x_grid, [f_prime(x) for x in x_grid], label="$f'$") +ax.legend() +plt.show() +``` + +در نقطه غیرقابل مشتق‌گیری $0$، `jax.grad` مشتق راست را برمی‌گرداند: + +```{code-cell} ipython3 +f_prime(0.0) +``` + +### مشتق‌گیری از طریق جریان کنترل + ++++ + +بیایید سعی کنیم از طریق برخی حلقه‌ها و شرط‌ها مشتق بگیریم. + +```{code-cell} ipython3 +def f(x): + def f1(x): + for i in range(2): + x *= 0.2 * x + return x + def f2(x): + x = sum((x**i + i) for i in range(3)) + return x + y = f1(x) if x < 0 else f2(x) + return y +``` + +```{code-cell} ipython3 +f_prime = jax.grad(f) +``` + +```{code-cell} ipython3 +x_grid = jnp.linspace(-5, 5, 100) +``` + +```{code-cell} ipython3 +fig, ax = plt.subplots() +ax.plot(x_grid, [f(x) for x in x_grid], label="$f$") +ax.plot(x_grid, [f_prime(x) for x in x_grid], label="$f'$") +ax.legend() +plt.show() +``` + +### مشتق‌گیری از طریق درون‌یابی خطی + ++++ + +می‌توانیم از طریق درون‌یابی خطی مشتق بگیریم، حتی اگر تابع هموار نباشد: + +```{code-cell} ipython3 +n = 20 +xp = jnp.linspace(-5, 5, n) +yp = jnp.cos(2 * xp) + +fig, ax = plt.subplots() +ax.plot(x_grid, jnp.interp(x_grid, xp, yp)) +plt.show() +``` + +```{code-cell} ipython3 +f_prime = jax.grad(jnp.interp) +``` + +```{code-cell} ipython3 +f_prime_vec = jax.vmap(f_prime, in_axes=(0, None, None)) +``` + +```{code-cell} ipython3 +fig, ax = plt.subplots() +ax.plot(x_grid, f_prime_vec(x_grid, xp, yp)) +plt.show() +``` + +## گرادیان کاهشی + ++++ + +بیایید پیاده‌سازی گرادیان کاهشی را امتحان کنیم. + +به عنوان یک کاربرد ساده، از گرادیان کاهشی برای یافتن برآوردهای پارامتر حداقل مربعات معمولی در رگرسیون خطی ساده استفاده خواهیم کرد. + ++++ + +### یک تابع برای گرادیان کاهشی + ++++ + +در اینجا یک پیاده‌سازی از گرادیان کاهشی ارائه شده است. + +```{code-cell} ipython3 +def grad_descent(f, # Function to be minimized + args, # Extra arguments to the function + x0, # Initial condition + λ=0.1, # Initial learning rate + tol=1e-5, + max_iter=1_000): + """ + Minimize the function f via gradient descent, starting from guess x0. + + The learning rate is computed according to the Barzilai-Borwein method. + + """ + + f_grad = jax.grad(f) + x = jnp.array(x0) + df = f_grad(x, args) + ϵ = tol + 1 + i = 0 + while ϵ > tol and i < max_iter: + new_x = x - λ * df + new_df = f_grad(new_x, args) + Δx = new_x - x + Δdf = new_df - df + λ = jnp.abs(Δx @ Δdf) / (Δdf @ Δdf) + ϵ = jnp.max(jnp.abs(Δx)) + x, df = new_x, new_df + i += 1 + + return x + +``` + +### داده‌های شبیه‌سازی‌شده + +ما می‌خواهیم تابع گرادیان کاهشی خود را با کمینه‌سازی مجموع مربعات کمترین در یک مسئله رگرسیون آزمایش کنیم. + +بیایید برخی داده‌های شبیه‌سازی‌شده تولید کنیم: + +```{code-cell} ipython3 +n = 100 +key = jax.random.key(1234) +x = jax.random.uniform(key, (n,)) + +α, β, σ = 0.5, 1.0, 0.1 # Set the true intercept and slope. +key, subkey = jax.random.split(key) +ϵ = jax.random.normal(subkey, (n,)) + +y = α * x + β + σ * ϵ +``` + +```{code-cell} ipython3 +fig, ax = plt.subplots() +ax.scatter(x, y) +plt.show() +``` + +بیایید با محاسبه شیب و عرض از مبدأ برآوردشده با استفاده از راه‌حل‌های فرم بسته شروع کنیم. + +```{code-cell} ipython3 +mx = x.mean() +my = y.mean() +α_hat = jnp.sum((x - mx) * (y - my)) / jnp.sum((x - mx)**2) +β_hat = my - α_hat * mx +``` + +```{code-cell} ipython3 +α_hat, β_hat +``` + +```{code-cell} ipython3 +fig, ax = plt.subplots() +ax.scatter(x, y) +ax.plot(x, α_hat * x + β_hat, 'k-') +ax.text(0.1, 1.55, rf'$\hat \alpha = {α_hat:.3}$') +ax.text(0.1, 1.50, rf'$\hat \beta = {β_hat:.3}$') +plt.show() +``` + +### کمینه‌سازی تابع زیان مربعات با گرادیان کاهشی + ++++ + +بیایید ببینیم آیا می‌توانیم همان مقادیر را با تابع گرادیان کاهشی خود به دست آوریم. + +ابتدا تابع زیان کمترین مربعات را تنظیم می‌کنیم. + +```{code-cell} ipython3 +@jax.jit +def loss(params, data): + a, b = params + x, y = data + return jnp.sum((y - a * x - b)**2) +``` + +حال آن را کمینه می‌کنیم: + +```{code-cell} ipython3 +p0 = jnp.zeros(2) # Initial guess for α, β +data = x, y +α_hat, β_hat = grad_descent(loss, data, p0) +``` + +بیایید نتایج را رسم کنیم. + +```{code-cell} ipython3 +fig, ax = plt.subplots() +x_grid = jnp.linspace(0, 1, 100) +ax.scatter(x, y) +ax.plot(x_grid, α_hat * x_grid + β_hat, 'k-', alpha=0.6) +ax.text(0.1, 1.55, rf'$\hat \alpha = {α_hat:.3}$') +ax.text(0.1, 1.50, rf'$\hat \beta = {β_hat:.3}$') +plt.show() +``` + +توجه کنید که همان برآوردهایی را به دست می‌آوریم که از راه‌حل‌های فرم بسته به دست آوردیم. + ++++ + +### افزودن یک جمله مربعی + +حال بیایید برازش یک چندجمله‌ای مرتبه دوم را امتحان کنیم. + +این تابع زیان جدید ماست. + +```{code-cell} ipython3 +@jax.jit +def loss(params, data): + a, b, c = params + x, y = data + return jnp.sum((y - a * x**2 - b * x - c)**2) +``` + +اکنون در سه بُعد کمینه‌سازی می‌کنیم. + +بیایید آن را امتحان کنیم. + +```{code-cell} ipython3 +p0 = jnp.zeros(3) +α_hat, β_hat, γ_hat = grad_descent(loss, data, p0) + +fig, ax = plt.subplots() +ax.scatter(x, y) +ax.plot(x_grid, α_hat * x_grid**2 + β_hat * x_grid + γ_hat, 'k-', alpha=0.6) +ax.text(0.1, 1.55, rf'$\hat \alpha = {α_hat:.3}$') +ax.text(0.1, 1.50, rf'$\hat \beta = {β_hat:.3}$') +plt.show() +``` + +## تمرین‌ها + +```{exercise-start} +:label: auto_ex1 +``` + +تابع `jnp.polyval` چندجمله‌ای‌ها را ارزیابی می‌کند. + +به عنوان مثال، اگر `len(p)` برابر با ۳ باشد، `jnp.polyval(p, x)` مقدار زیر را برمی‌گرداند: + +$$ + f(p, x) := p_0 x^2 + p_1 x + p_2 +$$ + +از این تابع برای رگرسیون چندجمله‌ای استفاده کنید. + +تابع زیان (تجربی) به صورت زیر است: + +$$ + \ell(p, x, y) + = \sum_{i=1}^n (y_i - f(p, x_i))^2 +$$ + +مقدار $k=4$ را تنظیم کنید و حدس اولیه `params` را برابر `jnp.zeros(k)` قرار دهید. + +از گرادیان کاهشی برای یافتن آرایه `params` که تابع زیان را کمینه می‌کند استفاده کنید و نتیجه را رسم کنید (مشابه مثال‌های بالا). + +```{exercise-end} +``` + +```{solution-start} auto_ex1 +:class: dropdown +``` + +یک راه‌حل ممکن به این صورت است. + +```{code-cell} ipython3 +def loss(params, data): + x, y = data + return jnp.sum((y - jnp.polyval(params, x))**2) +``` + +```{code-cell} ipython3 +k = 4 +p0 = jnp.zeros(k) +p_hat = grad_descent(loss, data, p0) +print('Estimated parameter vector:') +print(p_hat) +print('\n\n') + +fig, ax = plt.subplots() +ax.scatter(x, y) +ax.plot(x_grid, jnp.polyval(p_hat, x_grid), 'k-', alpha=0.6) +plt.show() +``` + +```{solution-end} +``` \ No newline at end of file From fc517cfee70ab9fcd16f9c9fa2260a1bf1db33da Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 8 Apr 2026 01:29:25 +0100 Subject: [PATCH 2/7] Update translation: .translate/state/autodiff.md.yml --- .translate/state/autodiff.md.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .translate/state/autodiff.md.yml diff --git a/.translate/state/autodiff.md.yml b/.translate/state/autodiff.md.yml new file mode 100644 index 0000000..7f3afa4 --- /dev/null +++ b/.translate/state/autodiff.md.yml @@ -0,0 +1,6 @@ +source-sha: 05ce95691fd97e48da39dd6d58fe032c03e8813d +synced-at: "2026-04-08" +model: claude-sonnet-4-6 +mode: NEW +section-count: 5 +tool-version: 0.13.1 From cc81afc1465dc11cba1d624ec70e92abd96890e3 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 8 Apr 2026 01:29:25 +0100 Subject: [PATCH 3/7] Update translation: lectures/jax_intro.md --- lectures/jax_intro.md | 254 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 210 insertions(+), 44 deletions(-) diff --git a/lectures/jax_intro.md b/lectures/jax_intro.md index b177527..7aa18d7 100644 --- a/lectures/jax_intro.md +++ b/lectures/jax_intro.md @@ -34,10 +34,14 @@ translation: JIT compilation::Evaluating a more complicated function: ارزیابی یک تابع پیچیده‌تر JIT compilation::Evaluating a more complicated function::With NumPy: با NumPy JIT compilation::Evaluating a more complicated function::With JAX: با JAX - JIT compilation::Compiling the Whole Function: کامپایل کل تابع + JIT compilation::How JIT compilation works: نحوه کار کامپایل JIT + JIT compilation::Compiling the whole function: کامپایل کل تابع JIT compilation::Compiling non-pure functions: کامپایل توابع غیرخالص JIT compilation::Summary: خلاصه - Gradients: گرادیان‌ها + Vectorization with `vmap`: برداری‌سازی با `vmap` + Vectorization with `vmap`::A simple example: یک مثال ساده + Vectorization with `vmap`::Combining transformations: ترکیب تبدیل‌ها + 'Automatic differentiation: a preview': 'مشتق‌گیری خودکار: یک پیش‌نمایش' Exercises: تمرین‌ها --- @@ -78,16 +82,16 @@ JAX یک کتابخانه محاسبات علمی با کارایی بالا ا ```{code-cell} ipython3 import jax +import jax.numpy as jnp +import matplotlib.pyplot as plt +import matplotlib.patches as mpatches +import numpy as np import quantecon as qe ``` -علاوه بر این، ما `import numpy as np` را با موارد زیر جایگزین می‌کنیم +توجه کنید که ما `jax.numpy as jnp` را import می‌کنیم که یک رابط مشابه NumPy فراهم می‌کند. -```{code-cell} ipython3 -import jax.numpy as jnp -``` - -اکنون می‌توانیم از `jnp` به جای `np` برای عملیات معمول آرایه استفاده کنیم: +در اینجا برخی عملیات استاندارد آرایه با استفاده از `jnp` آمده است: ```{code-cell} ipython3 a = jnp.asarray((1.0, 3.2, -1.5)) @@ -143,7 +147,6 @@ jnp.linalg.inv(B) # Inverse of identity is identity jnp.linalg.eigh(B) # Computes eigenvalues and eigenvectors ``` - ### تفاوت‌ها اکنون به برخی از تفاوت‌های بین عملیات آرایه JAX و NumPy نگاه کنیم. @@ -177,7 +180,6 @@ jnp.ones(3) برای مثال، با NumPy می‌توانیم بنویسیم ```{code-cell} ipython3 -import numpy as np a = np.linspace(0, 1, 3) a ``` @@ -244,7 +246,6 @@ a (اگرچه در واقع می‌تواند داخل توابع کامپایل‌شده JIT کارآمد باشد -- اما بیایید این را فعلاً کنار بگذاریم.) - ## برنامه‌نویسی تابعی از مستندات JAX: @@ -274,8 +275,6 @@ a * وضعیت سراسری را تغییر نمی‌دهد * داده‌های ارسال شده به تابع را تغییر نمی‌دهد (داده‌های تغییرناپذیر) - - ### مثال‌ها در اینجا مثالی از یک تابع *غیرخالص* آورده شده است @@ -311,7 +310,6 @@ def add_tax_pure(prices, tax_rate): اکنون که می‌فهمیم توابع خالص چیستند، بیایید بررسی کنیم که چگونه رویکرد JAX به اعداد تصادفی این خلوص را حفظ می‌کند. - ## اعداد تصادفی اعداد تصادفی در JAX نسبت به آنچه در NumPy یا Matlab می‌یابید بسیار متفاوت هستند. @@ -322,7 +320,6 @@ def add_tax_pure(prices, tax_rate): علاوه بر این، کنترل کامل وضعیت تصادفی برای برنامه‌نویسی موازی، مانند زمانی که می‌خواهیم آزمایش‌های مستقل را در چندین رشته اجرا کنیم، ضروری است. - ### تولید اعداد تصادفی در JAX، وضعیت مولد اعداد تصادفی به صورت صریح کنترل می‌شود. @@ -331,7 +328,7 @@ def add_tax_pure(prices, tax_rate): ```{code-cell} ipython3 seed = 1234 -key = jax.random.PRNGKey(seed) +key = jax.random.key(seed) ``` اکنون می‌توانیم از کلید برای تولید چند عدد تصادفی استفاده کنیم: @@ -361,6 +358,78 @@ jax.random.normal(key, (3, 3)) jax.random.normal(subkey, (3, 3)) ``` +نمودار زیر نشان می‌دهد که چگونه `split` یک درخت از کلیدها را از یک ریشه واحد تولید می‌کند، با هر کلید که نمونه‌های تصادفی مستقل تولید می‌کند. + +```{code-cell} ipython3 +:tags: [hide-input] + +fig, ax = plt.subplots(figsize=(8, 4)) +ax.set_xlim(-0.5, 6.5) +ax.set_ylim(-0.5, 3.5) +ax.set_aspect('equal') +ax.axis('off') + +box_style = dict(boxstyle="round,pad=0.3", facecolor="white", + edgecolor="black", linewidth=1.5) +box_used = dict(boxstyle="round,pad=0.3", facecolor="#d4edda", + edgecolor="black", linewidth=1.5) + +# Root key +ax.text(3, 3, "key₀", ha='center', va='center', fontsize=11, + bbox=box_style) + +# Level 1 +ax.annotate("", xy=(1.5, 2), xytext=(3, 2.7), + arrowprops=dict(arrowstyle="->", lw=1.5)) +ax.annotate("", xy=(4.5, 2), xytext=(3, 2.7), + arrowprops=dict(arrowstyle="->", lw=1.5)) +ax.text(1.5, 2, "key₁", ha='center', va='center', fontsize=11, + bbox=box_style) +ax.text(4.5, 2, "subkey₁", ha='center', va='center', fontsize=11, + bbox=box_used) +ax.text(5.7, 2, "→ draw", ha='left', va='center', fontsize=10, + color='green') + +# Label the split +ax.text(2, 2.65, "split", ha='center', va='center', fontsize=9, + fontstyle='italic', color='gray') + +# Level 2 +ax.annotate("", xy=(0.5, 1), xytext=(1.5, 1.7), + arrowprops=dict(arrowstyle="->", lw=1.5)) +ax.annotate("", xy=(2.5, 1), xytext=(1.5, 1.7), + arrowprops=dict(arrowstyle="->", lw=1.5)) +ax.text(0.5, 1, "key₂", ha='center', va='center', fontsize=11, + bbox=box_style) +ax.text(2.5, 1, "subkey₂", ha='center', va='center', fontsize=11, + bbox=box_used) +ax.text(3.7, 1, "→ draw", ha='left', va='center', fontsize=10, + color='green') + +ax.text(0.7, 1.65, "split", ha='center', va='center', fontsize=9, + fontstyle='italic', color='gray') + +# Level 3 +ax.annotate("", xy=(0, 0), xytext=(0.5, 0.7), + arrowprops=dict(arrowstyle="->", lw=1.5)) +ax.annotate("", xy=(1.5, 0), xytext=(0.5, 0.7), + arrowprops=dict(arrowstyle="->", lw=1.5)) +ax.text(0, 0, "key₃", ha='center', va='center', fontsize=11, + bbox=box_style) +ax.text(1.5, 0, "subkey₃", ha='center', va='center', fontsize=11, + bbox=box_used) +ax.text(2.7, 0, "→ draw", ha='left', va='center', fontsize=10, + color='green') +ax.text(0, 0.65, "split", ha='center', va='center', fontsize=9, + fontstyle='italic', color='gray') + +ax.text(3, -0.5, "⋮", ha='center', va='center', fontsize=14) + +ax.set_title("PRNG Key Splitting Tree", fontsize=13, pad=10) +plt.tight_layout() +plt.show() +``` + این نحو برای کاربر NumPy یا Matlab غیرعادی به نظر می‌رسد --- اما وقتی به برنامه‌نویسی موازی پیش می‌رویم، منطقی خواهد بود. تابع زیر `k` ماتریس تصادفی `n x n` (شبه) مستقل را با استفاده از `split` تولید می‌کند. @@ -378,7 +447,7 @@ def gen_random_matrices(key, n=2, k=3): ```{code-cell} ipython3 seed = 42 -key = jax.random.PRNGKey(seed) +key = jax.random.key(seed) matrices = gen_random_matrices(key) ``` @@ -396,11 +465,10 @@ def gen_random_matrices(key, n=2, k=3): ``` ```{code-cell} ipython3 -key = jax.random.PRNGKey(seed) +key = jax.random.key(seed) matrices = gen_random_matrices(key) ``` - ### چرا وضعیت تصادفی صریح؟ چرا JAX به این رویکرد نسبتاً پرمخاطب برای تولید اعداد تصادفی نیاز دارد؟ @@ -428,7 +496,6 @@ print(np.random.randn()) # Updates state of random number generator * غیرقطعی است: ورودی‌های یکسان (در این مورد هیچ) خروجی‌های متفاوت می‌دهند * دارای عوارض جانبی است: وضعیت مولد اعداد تصادفی سراسری را تغییر می‌دهد - #### رویکرد JAX همانطور که در بالا دیدیم، JAX رویکرد متفاوتی اتخاذ می‌کند و تصادفی بودن را از طریق کلیدها صریح می‌کند. @@ -446,7 +513,7 @@ def random_sum_jax(key): با همان کلید، همیشه نتیجه یکسانی دریافت می‌کنیم: ```{code-cell} ipython3 -key = jax.random.PRNGKey(42) +key = jax.random.key(42) random_sum_jax(key) ``` @@ -470,7 +537,6 @@ random_sum_jax(key) نکته آخر در بخش بعدی گسترش داده می‌شود. - ## کامپایل JIT کامپایلر just-in-time (JIT) JAX اجرا را با تولید کد ماشین کارآمد که با هم اندازه وظیفه و هم سخت‌افزار متفاوت است، تسریع می‌کند. @@ -552,7 +618,6 @@ with qe.Timer(): به همین دلیل است که JAX منتظر می‌ماند تا اندازه آرایه را قبل از کامپایل ببیند --- که نیاز به یک رویکرد JIT-کامپایل شده به جای ارائه باینری‌های از پیش کامپایل شده دارد. - #### تغییر اندازه آرایه‌ها در اینجا اندازه ورودی را تغییر می‌دهیم و زمان‌های اجرا را مشاهده می‌کنیم. @@ -578,14 +643,13 @@ with qe.Timer(): این به این دلیل است که کامپایلر JIT روی اندازه آرایه تخصصی می‌شود تا موازی‌سازی را بهره‌برداری کند --- و از این رو کد کامپایل شده جدیدی را هنگام تغییر اندازه آرایه تولید می‌کند. - ### ارزیابی یک تابع پیچیده‌تر بیایید همان کار را با یک تابع پیچیده‌تر امتحان کنیم. ```{code-cell} def f(x): - y = np.cos(2 * x**2) + np.sqrt(np.abs(x)) + 2 * np.sin(x**4) - 0.1 * x**2 + y = np.cos(2 * x**2) + np.sqrt(np.abs(x)) + 2 * np.sin(x**4) - x**2 return y ``` @@ -603,8 +667,6 @@ with qe.Timer(): y = f(x) ``` - - #### با JAX اکنون بیایید دوباره با JAX امتحان کنیم. @@ -637,8 +699,60 @@ with qe.Timer(): نتیجه مشابه مثال `cos` است --- JAX سریع‌تر است، به ویژه در اجرای دوم پس از کامپایل JIT. -علاوه بر این، با JAX، ترفند دیگری در آستین داریم: +علاوه بر این، با JAX، ترفند دیگری در آستین داریم --- می‌توانیم *کل* تابع را JIT-کامپایل کنیم، نه فقط عملیات‌های منفرد. + +### نحوه کار کامپایل JIT +هنگامی که `jax.jit` را به یک تابع اعمال می‌کنیم، JAX آن را *ردیابی* می‌کند: به جای اجرای فوری عملیات‌ها، دنباله عملیات‌ها را به صورت یک گراف محاسباتی ثبت می‌کند و آن گراف را به کامپایلر [XLA](https://openxla.org/xla) تحویل می‌دهد. + +سپس XLA عملیات‌ها را در یک هسته کامپایل شده واحد بهینه‌سازی و ادغام می‌کند که متناسب با سخت‌افزار موجود (CPU، GPU، یا TPU) طراحی شده است. + +نمودار زیر این خط لوله را برای یک تابع ساده نشان می‌دهد: + +```{code-cell} ipython3 +:tags: [hide-input] + +fig, ax = plt.subplots(figsize=(7, 2)) +ax.set_xlim(-0.2, 7.2) +ax.set_ylim(0.2, 2.2) +ax.axis('off') + +# Boxes for pipeline stages +stages = [ + (0.7, 1.2, "Python\nfunction"), + (2.6, 1.2, "computational\ngraph"), + (4.5, 1.2, "optimized\nkernel"), + (6.4, 1.2, "fast\nexecution"), +] + +colors = ["#e3f2fd", "#fff9c4", "#f3e5f5", "#d4edda"] + +for (x, y, label), color in zip(stages, colors): + box = mpatches.FancyBboxPatch( + (x - 0.7, y - 0.5), 1.4, 1.0, + boxstyle="round,pad=0.15", + facecolor=color, edgecolor="black", linewidth=1.5) + ax.add_patch(box) + ax.text(x, y, label, ha='center', va='center', fontsize=9) + +# Arrows with labels +arrows = [ + (1.4, 1.9, "trace"), + (3.3, 3.8, "XLA"), + (5.2, 5.7, "run"), +] + +for x_start, x_end, label in arrows: + ax.annotate("", xy=(x_end, 1.2), xytext=(x_start, 1.2), + arrowprops=dict(arrowstyle="->", lw=1.5, color="gray")) + ax.text((x_start + x_end) / 2, 1.55, label, + ha='center', fontsize=8, color='gray') + +plt.tight_layout() +plt.show() +``` + +اولین فراخوانی به یک تابع JIT-کامپایل شده سربار کامپایل دارد، اما فراخوانی‌های بعدی با همان شکل‌ها و نوع‌های ورودی از کد کامپایل شده کش‌شده استفاده می‌کنند و با سرعت کامل اجرا می‌شوند. ### کامپایل کل تابع @@ -723,7 +837,6 @@ f(x) درس اخلاقی داستان: هنگام استفاده از JAX، توابع خالص بنویسید! - ### خلاصه اکنون می‌توانیم ببینیم که چرا هم توسعه‌دهندگان و هم کامپایلرها از توابع خالص بهره می‌برند. @@ -740,25 +853,81 @@ f(x) * توابع خالص راحت‌تر قابل تمایز هستند (autodiff) * توابع خالص راحت‌تر موازی‌سازی و بهینه‌سازی می‌شوند (به وضعیت قابل تغییر مشترک وابسته نیستند) +## برداری‌سازی با `vmap` -## گرادیان‌ها +یکی دیگر از تبدیل‌های قدرتمند JAX، `jax.vmap` است که به‌طور خودکار +تابعی که برای یک ورودی منفرد نوشته شده را برداری‌سازی می‌کند تا روی دسته‌ها عمل کند. -JAX می‌تواند از تمایز خودکار برای محاسبه گرادیان‌ها استفاده کند. +این کار نیاز به نوشتن دستی کد برداری‌شده یا استفاده از حلقه‌های صریح را از بین می‌برد. -این می‌تواند برای بهینه‌سازی و حل سیستم‌های غیرخطی بسیار مفید باشد. +### یک مثال ساده -ما کاربردهای قابل توجهی را بعداً در این مجموعه سخنرانی‌ها خواهیم دید. +فرض کنید تابعی داریم که آمارهای خلاصه را برای یک آرایه منفرد محاسبه می‌کند: -فعلاً، در اینجا یک تصویر بسیار ساده شامل تابع است +```{code-cell} ipython3 +def summary(x): + return jnp.mean(x), jnp.median(x) +``` + +می‌توانیم آن را روی یک بردار منفرد اعمال کنیم: ```{code-cell} ipython3 -def f(x): - return (x**2) / 2 +x = jnp.array([1.0, 2.0, 5.0]) +summary(x) +``` + +حال فرض کنید یک ماتریس داریم و می‌خواهیم این آمارها را برای هر سطر محاسبه کنیم. + +بدون `vmap`، به یک حلقه صریح نیاز داریم: + +```{code-cell} ipython3 +X = jnp.array([[1.0, 2.0, 5.0], + [4.0, 5.0, 6.0], + [1.0, 8.0, 9.0]]) + +for row in X: + print(summary(row)) ``` -بیایید مشتق بگیریم: +با این حال، حلقه‌های Python کُند هستند و نمی‌توانند به‌طور کارآمد توسط JAX کامپایل یا موازی‌سازی شوند. + +استفاده از `vmap` محاسبه را روی شتاب‌دهنده نگه می‌دارد و با سایر +تبدیل‌های JAX مانند `jit` و `grad` ترکیب می‌شود: ```{code-cell} ipython3 +batch_summary = jax.vmap(summary) +batch_summary(X) +``` + +تابع `summary` برای یک آرایه منفرد نوشته شده بود، و `vmap` به‌طور خودکار +آن را برای عمل سطربه‌سطر روی یک ماتریس ارتقا داد --- بدون حلقه، بدون تغییر شکل. + +### ترکیب تبدیل‌ها + +یکی از نقاط قوت JAX این است که تبدیل‌ها به‌طور طبیعی با هم ترکیب می‌شوند. + +برای مثال، می‌توانیم یک تابع برداری‌شده را با JIT کامپایل کنیم: + +```{code-cell} ipython3 +fast_batch_summary = jax.jit(jax.vmap(summary)) +fast_batch_summary(X) +``` + +این ترکیب `jit`، `vmap`، و (همان‌طور که در ادامه خواهیم دید) `grad` در قلب +طراحی JAX قرار دارد و آن را به‌ویژه برای محاسبات علمی و یادگیری ماشین بسیار قدرتمند می‌سازد. + +## مشتق‌گیری خودکار: یک پیش‌نمایش + +JAX می‌تواند از مشتق‌گیری خودکار برای محاسبه گرادیان‌ها استفاده کند. + +این ویژگی می‌تواند برای بهینه‌سازی و حل سیستم‌های غیرخطی بسیار مفید باشد. + +در اینجا یک مثال ساده با تابع $f(x) = x^2 / 2$ آورده شده است: + +```{code-cell} ipython3 +def f(x): + return (x**2) / 2 + f_prime = jax.grad(f) ``` @@ -766,11 +935,9 @@ f_prime = jax.grad(f) f_prime(10.0) ``` -بیایید تابع و مشتق را رسم کنیم، با توجه به اینکه $f'(x) = x$. +بیایید تابع و مشتق آن را رسم کنیم، با توجه به اینکه $f'(x) = x$. ```{code-cell} ipython3 -import matplotlib.pyplot as plt - fig, ax = plt.subplots() x_grid = jnp.linspace(-4, 4, 200) ax.plot(x_grid, f(x_grid), label="$f$") @@ -779,8 +946,7 @@ ax.legend(loc='upper center') plt.show() ``` -ما بررسی بیشتر تمایز خودکار با JAX را در [ماجراجویی‌ها با تمایز خودکار](https://jax.quantecon.org/autodiff.html) موکول می‌کنیم. - +مشتق‌گیری خودکار موضوعی عمیق با کاربردهای فراوان در اقتصاد و مالی است. ما یک بررسی جامع‌تر را در {doc}`درس مربوط به مشتق‌گیری خودکار ` ارائه می‌دهیم. ## تمرین‌ها @@ -822,7 +988,7 @@ def compute_call_price_jax(β=β, ρ=ρ, ν=ν, M=M, - key=jax.random.PRNGKey(1)): + key=jax.random.key(1)): s = jnp.full(M, np.log(S0)) h = jnp.full(M, h0) @@ -866,4 +1032,4 @@ with qe.Timer(): ``` ```{solution-end} -``` \ No newline at end of file +``` From 641af6cb5d05dc024293881663652dd09f754b6c Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 8 Apr 2026 01:29:26 +0100 Subject: [PATCH 4/7] Update translation: .translate/state/jax_intro.md.yml --- .translate/state/jax_intro.md.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.translate/state/jax_intro.md.yml b/.translate/state/jax_intro.md.yml index 32469fe..d5ece3d 100644 --- a/.translate/state/jax_intro.md.yml +++ b/.translate/state/jax_intro.md.yml @@ -1,6 +1,6 @@ -source-sha: c4c03c80c1eb4318f627d869707d242d19c8cf09 -synced-at: "2026-03-20" -model: unknown -mode: RESYNC -section-count: 6 -tool-version: 0.13.0 +source-sha: 05ce95691fd97e48da39dd6d58fe032c03e8813d +synced-at: "2026-04-08" +model: claude-sonnet-4-6 +mode: UPDATE +section-count: 7 +tool-version: 0.13.1 From d46baa1f6cae8ffd5499f1db2adfac7e5dc46c46 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 8 Apr 2026 01:29:26 +0100 Subject: [PATCH 5/7] Update translation: lectures/numpy_vs_numba_vs_jax.md --- lectures/numpy_vs_numba_vs_jax.md | 60 +++++++++++++++++++------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/lectures/numpy_vs_numba_vs_jax.md b/lectures/numpy_vs_numba_vs_jax.md index 9e25d1f..670a953 100644 --- a/lectures/numpy_vs_numba_vs_jax.md +++ b/lectures/numpy_vs_numba_vs_jax.md @@ -24,6 +24,7 @@ translation: Sequential operations::Numba Version: نسخه Numba Sequential operations::JAX Version: نسخه JAX Sequential operations::Summary: خلاصه + Overall recommendations: توصیه‌های کلی --- (parallel)= @@ -69,13 +70,17 @@ tags: [hide-output] ```{code-cell} ipython3 import random +from functools import partial + import numpy as np +import numba import quantecon as qe import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.axes3d import Axes3D from matplotlib import cm import jax import jax.numpy as jnp +from jax import lax ``` ## عملیات برداری شده @@ -113,7 +118,7 @@ ax.plot_surface(x, y, f(x, y), rstride=2, cstride=2, - cmap=cm.jet, + cmap=cm.viridis, alpha=0.7, linewidth=0.25) ax.set_zlim(-0.5, 1.0) @@ -139,7 +144,6 @@ for x in grid: m = z ``` - ### برداری‌سازی NumPy اگر به برداری‌سازی به سبک NumPy تغییر دهیم، می‌توانیم از یک شبکه بسیار بزرگتر استفاده کنیم و کد نسبتاً سریع اجرا می‌شود. @@ -164,14 +168,11 @@ print(f"NumPy result: {z_max_numpy:.6f}") (موازی‌سازی نمی‌تواند بسیار کارآمد باشد زیرا فایل باینری قبل از اینکه اندازه آرایه‌های `x` و `y` را ببیند کامپایل می‌شود.) - ### مقایسه با Numba حالا بیایید ببینیم آیا می‌توانیم با استفاده از Numba با یک حلقه ساده به عملکرد بهتری دست یابیم. ```{code-cell} ipython3 -import numba - @numba.jit def compute_max_numba(grid): m = -np.inf @@ -185,9 +186,9 @@ def compute_max_numba(grid): grid = np.linspace(-3, 3, 3_000) with qe.Timer(precision=8): - z_max_numpy = compute_max_numba(grid) + z_max_numba = compute_max_numba(grid) -print(f"Numba result: {z_max_numpy:.6f}") +print(f"Numba result: {z_max_numba:.6f}") ``` بیایید دوباره اجرا کنیم تا زمان کامپایل حذف شود. @@ -203,7 +204,6 @@ with qe.Timer(precision=8): از طرف دیگر، روال Numba از حافظه بسیار کمتری استفاده می‌کند، زیرا ما فقط با یک شبکه یک‌بعدی کار می‌کنیم. - ### Numba موازی شده حالا بیایید موازی‌سازی با Numba را با استفاده از `prange` امتحان کنیم: @@ -278,7 +278,6 @@ with qe.Timer(precision=8): برای دستگاه‌های قدرتمندتر و اندازه‌های شبکه بزرگتر، موازی‌سازی می‌تواند افزایش سرعت قابل توجهی ایجاد کند، حتی روی CPU. - ### کد برداری شده با JAX در ظاهر، کد برداری شده در JAX شبیه به کد NumPy است. @@ -299,7 +298,7 @@ def f(x, y): ```{code-cell} ipython3 grid = jnp.linspace(-3, 3, 3_000) -x_mesh, y_mesh = np.meshgrid(grid, grid) +x_mesh, y_mesh = jnp.meshgrid(grid, grid) with qe.Timer(precision=8): z_max = jnp.max(f(x_mesh, y_mesh)) @@ -316,11 +315,10 @@ with qe.Timer(precision=8): z_max.block_until_ready() ``` -پس از کامپایل، JAX به دلیل شتاب GPU به طور قابل توجهی سریعتر از NumPy است. +پس از کامپایل، JAX به ویژه روی GPU به طور قابل توجهی سریعتر از NumPy است. سربار کامپایل یک هزینه یک‌بار مصرف است که زمانی که تابع به طور مکرر فراخوانی می‌شود، بازگشت سرمایه دارد. - ### JAX به علاوه vmap یک مشکل با کد NumPy و کد JAX وجود دارد: @@ -382,7 +380,6 @@ with qe.Timer(precision=8): ما این ایده‌ها را بیشتر هنگام حل مسائل بزرگتر بررسی خواهیم کرد. - ### نسخه 2 vmap می‌توانیم با استفاده از vmap همچنان کارآمدتر از نظر حافظه باشیم. @@ -417,7 +414,7 @@ def compute_max_vmap_v2(grid): with qe.Timer(precision=8): z_max = compute_max_vmap_v2(grid).block_until_ready() -print(f"JAX vmap v1 result: {z_max:.6f}") +print(f"JAX vmap v2 result: {z_max:.6f}") ``` بیایید دوباره اجرا کنیم تا زمان کامپایل حذف شود: @@ -429,7 +426,6 @@ with qe.Timer(precision=8): اگر این را روی GPU اجرا می‌کنید، همانطور که ما این کار را می‌کنیم، باید افزایش سرعت قابل توجه دیگری را ببینید. - ### خلاصه به نظر ما، JAX برنده برای عملیات برداری شده است. @@ -444,7 +440,6 @@ with qe.Timer(precision=8): برای اکثر موارد مواجه شده در اقتصاد، اقتصادسنجی و امور مالی، بسیار بهتر است که برای موازی‌سازی کارآمد به کامپایلر JAX تحویل دهیم تا اینکه سعی کنیم این روال‌ها را خودمان کدنویسی دستی کنیم. - ## عملیات ترتیبی برخی عملیات ذاتاً ترتیبی هستند -- و از این رو برداری کردن آنها دشوار یا غیرممکن است. @@ -453,7 +448,6 @@ with qe.Timer(precision=8): برای مقایسه این انتخاب‌ها، مسئله تکرار روی نقشه درجه دوم را که در {doc}`سخنرانی Numba ` خود دیدیم، دوباره بررسی خواهیم کرد. - ### نسخه Numba در اینجا نسخه Numba آمده است. @@ -497,9 +491,6 @@ Numba این عملیات ترتیبی را به طور بسیار کارآمد (ما `n` را ایستا نگه می‌داریم زیرا بر اندازه آرایه تأثیر می‌گذارد و از این رو JAX می‌خواهد روی مقدار آن در کد کامپایل شده تخصصی شود.) ```{code-cell} ipython3 -from jax import lax -from functools import partial - cpu = jax.devices("cpu")[0] @partial(jax.jit, static_argnums=(1,), device=cpu) @@ -542,7 +533,6 @@ JAX نیز برای این عملیات ترتیبی کاملاً کارآمد هم JAX و هم Numba عملکرد قوی پس از کامپایل ارائه می‌دهند، با این که Numba معمولاً (اما نه همیشه) سرعت‌های کمی بهتری در عملیات کاملاً ترتیبی ارائه می‌دهد. - ### خلاصه در حالی که هم Numba و هم JAX عملکرد قوی برای عملیات ترتیبی ارائه می‌دهند، *تفاوت‌های قابل توجهی در خوانایی کد و سهولت استفاده وجود دارد*. @@ -555,4 +545,30 @@ JAX نیز برای این عملیات ترتیبی کاملاً کارآمد علاوه بر این، آرایه‌های تغییرناپذیر JAX به این معنی است که نمی‌توانیم به سادگی عناصر آرایه را در جا به‌روزرسانی کنیم و تکرار مستقیم الگوریتم مورد استفاده توسط Numba را سخت می‌کند. -برای این نوع عملیات ترتیبی، Numba برنده واضح از نظر وضوح کد و سهولت پیاده‌سازی، و همچنین عملکرد بالا است. \ No newline at end of file +برای این نوع عملیات ترتیبی، Numba برنده واضح از نظر وضوح کد و سهولت پیاده‌سازی، و همچنین عملکرد بالا است. + +## توصیه‌های کلی + +حال قدمی به عقب بر می‌داریم و مبادلات را خلاصه می‌کنیم. + +برای **عملیات برداری‌سازی‌شده**، JAX قوی‌ترین انتخاب است. + +به لطف کامپایل JIT و موازی‌سازی کارآمد روی CPU و GPU، در سرعت با NumPy برابری می‌کند یا از آن پیشی می‌گیرد. + +تبدیل `vmap` مصرف حافظه را کاهش می‌دهد و اغلب نسبت به برداری‌سازی سنتی مبتنی بر meshgrid، کد روشن‌تری ارائه می‌دهد. + +علاوه بر این، توابع JAX به‌صورت خودکار مشتق‌پذیر هستند، همان‌طور که در {doc}`autodiff` بررسی می‌کنیم. + +برای **عملیات ترتیبی**، Numba مزایای آشکاری دارد. + +کد طبیعی و خوانا است --- صرفاً یک حلقه پایتون با یک decorator --- و کارایی آن عالی است. + +JAX می‌تواند مسائل ترتیبی را از طریق `lax.scan` مدیریت کند، اما نحو آن کمتر شهودی است و برای کارهای کاملاً ترتیبی، بهره‌وری اضافی ناچیز است. + +با این حال، `lax.scan` یک مزیت مهم دارد: از مشتق‌گیری خودکار در طول حلقه پشتیبانی می‌کند، که Numba قادر به انجام آن نیست. + +اگر نیاز دارید از طریق یک محاسبه ترتیبی مشتق بگیرید (مثلاً محاسبه حساسیت‌های یک مسیر نسبت به پارامترهای مدل)، JAX علی‌رغم نحو کمتر طبیعی‌اش، انتخاب بهتری است. + +در عمل، بسیاری از مسائل ترکیبی از هر دو الگو هستند. + +یک قاعده سرانگشتی مناسب: برای پروژه‌های جدید، به‌ویژه زمانی که شتاب‌دهی سخت‌افزاری یا مشتق‌پذیری ممکن است مفید باشد، به‌طور پیش‌فرض از JAX استفاده کنید، و هنگامی که یک حلقه ترتیبی فشرده نیاز به سرعت و خوانایی دارد، به Numba متوسل شوید. From 35fd179036c75f4e6e64ab9bc0037ac8f35bf281 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 8 Apr 2026 01:29:27 +0100 Subject: [PATCH 6/7] Update translation: .translate/state/numpy_vs_numba_vs_jax.md.yml --- .translate/state/numpy_vs_numba_vs_jax.md.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.translate/state/numpy_vs_numba_vs_jax.md.yml b/.translate/state/numpy_vs_numba_vs_jax.md.yml index e836b11..78c2946 100644 --- a/.translate/state/numpy_vs_numba_vs_jax.md.yml +++ b/.translate/state/numpy_vs_numba_vs_jax.md.yml @@ -1,6 +1,6 @@ -source-sha: c4c03c80c1eb4318f627d869707d242d19c8cf09 -synced-at: "2026-03-20" -model: unknown -mode: RESYNC -section-count: 2 -tool-version: 0.13.0 +source-sha: 05ce95691fd97e48da39dd6d58fe032c03e8813d +synced-at: "2026-04-08" +model: claude-sonnet-4-6 +mode: UPDATE +section-count: 3 +tool-version: 0.13.1 From bce7709f0a6bd9e2699651dd2d998afda647e26b Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 8 Apr 2026 01:29:27 +0100 Subject: [PATCH 7/7] Update translation: lectures/_toc.yml --- lectures/_toc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/lectures/_toc.yml b/lectures/_toc.yml index 97c429c..0ea886b 100644 --- a/lectures/_toc.yml +++ b/lectures/_toc.yml @@ -25,6 +25,7 @@ parts: - file: numba - file: jax_intro - file: numpy_vs_numba_vs_jax + - file: autodiff - caption: Working with Data numbered: true chapters: