Skip to content

Tutorials#3

Open
r-fedorov wants to merge 9 commits into
mainfrom
tutorials
Open

Tutorials#3
r-fedorov wants to merge 9 commits into
mainfrom
tutorials

Conversation

@r-fedorov

@r-fedorov r-fedorov commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features
    • Added intermediate and output irrep filtering to reduced tensor products.
    • Added historical MLX tutorial tensor helpers, plus new tutorials/notebooks for tensor operations and data types.
    • Introduced a use_custom_kernel toggle for v2.1.0.6 point-cloud models, propagated through message passing and convolutions.
  • Documentation
    • Added guidance for symmetry-reduced products and updated “where to start” with notebook recommendations.
    • Added a tutorial compatibility guide for running legacy workflows.
  • Tests
    • Expanded coverage for filtering behavior, tutorial helper correctness/plotting, and kernel-mode propagation and numerical equivalence.

@review-notebook-app

Copy link
Copy Markdown

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @r-fedorov, your pull request is larger than the review limit of 300000 diff characters

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5e1b037a-74e6-439c-9784-812d3d5b4212

📥 Commits

Reviewing files that changed from the base of the PR and between 8ee261d and ca0d595.

📒 Files selected for processing (11)
  • docs/index.md
  • e3nn_mlx/models/v2106/gate_points_message_passing.py
  • e3nn_mlx/models/v2106/gate_points_networks.py
  • e3nn_mlx/models/v2106/points_convolution.py
  • evals/mlx_cases.py
  • tests/test_documentation_contract.py
  • tests/test_evals.py
  • tests/test_v2106_convolution.py
  • tests/test_v2106_message_passing.py
  • tests/test_v2106_networks.py
  • tutorials/README.md
💤 Files with no reviewable changes (1)
  • tests/test_documentation_contract.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tutorials/README.md

📝 Walkthrough

Walkthrough

This PR adds MLX-compatible spherical and Cartesian tensor helpers, tutorial notebooks and compatibility documentation, intermediate and output irrep filtering for ReducedTensorProducts, and configurable custom-kernel propagation through v2106 models and evaluation builders.

Changes

Reduced tensor-product filtering

Layer / File(s) Summary
Filtered coupling and validation
e3nn_mlx/ops_reduce_tensor.py, docs/guide/tensor_products.md, tests/test_upstream_o3_reduce_tensor.py, tests/test_documentation_contract.py
ReducedTensorProducts accepts intermediate and final irrep filters, prunes coupling channels, validates filter values, and documents and tests the filtered construction.
MLX tensor helpers
Layer / File(s) Summary
Tensor representations and operations
tutorials/tensor_helpers.py
Adds IrrepTensor, SphericalTensor, and CartesianTensor with representation metadata, spherical operations, tensor products, geometry conversion, plotting, and Cartesian/irrep transformations.
Helper behavior tests
tests/test_tutorial_tensor_helpers.py
Tests construction, arithmetic, products, geometry, plotting conventions, Cartesian projections, legacy behavior, and rank validation.
Tutorial package and notebooks
Layer / File(s) Summary
Compatibility exports and visualization utilities
tutorials/__init__.py, tutorials/README.md, tutorials/other_helpers.py, docs/index.md, .gitignore
Exports tutorial tensor classes, documents historical compatibility, adds interactive visualization helpers, links notebook introductions, and ignores .venv_tutorials.
Tutorial notebooks
tutorials/*.ipynb
Adds notebooks covering spherical harmonics, Cartesian-to-irrep conversions, rotations, geometry visualization, tensor operations, dot products, and tensor products.

Custom-kernel routing

Layer / File(s) Summary
Model and evaluation propagation
e3nn_mlx/models/v2106/*.py, evals/mlx_cases.py
Adds use_custom_kernel configuration to v2106 networks and forwards it through message passing, convolutions, geometry operations, pooling, and evaluation builders.
Kernel mode tests
tests/test_evals.py, tests/test_v2106_*.py
Verifies propagation through constructors and operations, preservation during copying, and numerical equivalence between kernel modes.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

  • lamalab-org/e3nn_mlx#1: Related v2106 custom-kernel propagation into spherical-harmonic and scatter_sum call sites.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too generic and does not describe the main changes in the PR. Use a concise, specific title that names the main change, such as adding tutorial documentation and tensor helper support.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tutorials

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
tutorials/other_helpers.py (1)

140-152: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the dead, buggy first plot definition (shadowed by Lines 210-243).

This plot is overridden by the later definition of the same name, so it is unreachable. It also contains a bug: at Line 149 d['surfacecolor'].abs() is called on a NumPy array (trace runs np.asarray), which has no .abs() method and would raise AttributeError. Delete this duplicate to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/other_helpers.py` around lines 140 - 152, Remove the earlier
duplicate plot function definition, including its trace-building and
figure-display logic. Keep the later plot definition around Lines 210–243 as the
sole implementation, since the earlier version is shadowed and contains the
invalid d['surfacecolor'].abs() call.
tests/test_upstream_o3_reduce_tensor.py (1)

107-128: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the new filter validation paths.

Only the combined happy-path (filter_ir_mid + filter_ir_out both valid) is tested. The new ValueError branches in ReducedTensorProducts.__init__ for invalid filter_ir_mid/filter_ir_out content, and the case of using each filter independently, are untested.

♻️ Suggested additional test
def test_upstream_reduced_tensor_rejects_invalid_filters() -> None:
    irreps = o3.Irreps.spherical_harmonics(2)
    with pytest.raises(ValueError, match="filter_ir_mid"):
        o3.ReducedTensorProducts("ij=ji", i=irreps, filter_ir_mid=[object()])
    with pytest.raises(ValueError, match="filter_ir_out"):
        o3.ReducedTensorProducts("ij=ji", i=irreps, filter_ir_out=[object()])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_upstream_o3_reduce_tensor.py` around lines 107 - 128, Add tests
alongside test_upstream_reduced_tensor_supports_intermediate_and_output_filters
for ReducedTensorProducts filter validation: assert ValueError with matching
“filter_ir_mid” and “filter_ir_out” messages when each filter receives invalid
content, and add separate cases exercising filter_ir_mid alone and filter_ir_out
alone with valid filters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_documentation_contract.py`:
- Around line 181-185: Correct the contract entry in
tests/test_documentation_contract.py by keeping the filter_ir_mid claim while
pointing its source to an existing tracked documentation file containing the
exact excerpt, or restore docs/guide/tutorial_compatibility.md with that claim.
Ensure the referenced path and excerpt both resolve without changing the
contract text or associated test symbol.

In `@tutorials/data_types.ipynb`:
- Around line 629-630: Update the matrix construction near CartesianTensor to
transpose M with axes (1, 0) instead of (0, 1), so M + M.transpose(1, 0)
actually symmetrizes the matrix while preserving the existing formula and
conversion flow.

In `@tutorials/other_helpers.py`:
- Around line 201-206: Update random_point_signal to use MLX APIs throughout:
generate points with mx.random.normal(shape=(n, 3)), normalize them with
mx.linalg.norm(points, axis=-1, keepdims=True), and construct the result via
SphericalTensor.from_geometry(points, lmax) instead of the foreign
io.SphericalTensor/from_geometry_adjusted call.

---

Nitpick comments:
In `@tests/test_upstream_o3_reduce_tensor.py`:
- Around line 107-128: Add tests alongside
test_upstream_reduced_tensor_supports_intermediate_and_output_filters for
ReducedTensorProducts filter validation: assert ValueError with matching
“filter_ir_mid” and “filter_ir_out” messages when each filter receives invalid
content, and add separate cases exercising filter_ir_mid alone and filter_ir_out
alone with valid filters.

In `@tutorials/other_helpers.py`:
- Around line 140-152: Remove the earlier duplicate plot function definition,
including its trace-building and figure-display logic. Keep the later plot
definition around Lines 210–243 as the sole implementation, since the earlier
version is shadowed and contains the invalid d['surfacecolor'].abs() call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c2c1ac1f-be7b-44e5-b489-ede5ad31a625

📥 Commits

Reviewing files that changed from the base of the PR and between 9e671a0 and 8ee261d.

📒 Files selected for processing (13)
  • .gitignore
  • docs/guide/tensor_products.md
  • e3nn_mlx/ops_reduce_tensor.py
  • tests/test_documentation_contract.py
  • tests/test_tutorial_tensor_helpers.py
  • tests/test_upstream_o3_reduce_tensor.py
  • tutorials/README.md
  • tutorials/__init__.py
  • tutorials/data_types.ipynb
  • tutorials/e3nn_MRS_invariants_tutorial_lecture_pt1.ipynb
  • tutorials/operations_on_spherical_tensors.ipynb
  • tutorials/other_helpers.py
  • tutorials/tensor_helpers.py

Comment thread tests/test_documentation_contract.py Outdated
Comment on lines +181 to +185
(
"docs/guide/tutorial_compatibility.md",
"`filter_ir_mid` restricts every sequential Clebsch--Gordan coupling, including the final coupling.",
"tests/test_upstream_o3_reduce_tensor.py::test_upstream_reduced_tensor_supports_intermediate_and_output_filters",
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Confirm docs/guide/tutorial_compatibility.md exists and contains the exact excerpt.
fd -HI 'tutorial_compatibility.md'
rg -n "filter_ir_mid.*restricts every sequential Clebsch" docs 2>/dev/null

Repository: lamalab-org/e3nn_mlx

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -HI 'tutorial_compatibility.md|tensor_products.md|documentation_contract.py' .

echo
echo "== relevant contract entry and assertions =="
FILE="$(fd -HI 'test_documentation_contract.py' . | head -n 1)"
if [ -n "${FILE:-}" ]; then
  wc -l "$FILE"
  sed -n '160,195p;400,430p' "$FILE" | cat -n
fi

echo
echo "== exact excerpt search (all tracked text) =="
python3 - <<'PY'
from pathlib import Path
needle = '`filter_ir_mid` restricts every sequential Clebsch--Gordan coupling, including the final coupling.'
hits=[]
for p in Path('.').rglob('*'):
    if p.is_file() and not any(part.startswith('.') for part in p.parts):
        try:
            text=p.read_text(encoding='utf-8')
        except Exception:
            continue
        if needle in text:
            lines=[n for n,line in enumerate(text.splitlines(),1) if needle in line]
            hits.append((str(p), lines[:3]))
if hits:
    for path, lines in hits:
        print(f"{path}: {lines}")
else:
    print("NO HITS")
PY

echo
echo "== docs/guide files mentioning filter_ir_mid or Clebsch =="
rg -n "filter_ir_mid|Clebsch" docs 2>/dev/null || true

Repository: lamalab-org/e3nn_mlx

Length of output: 4436


Add or correct the documented source for this contract entry.

"docs/guide/tutorial_compatibility.md" is not present, and the exact excerpt does not exist anywhere under tracked files. Keep the contract but either restore the intended file or update the target to existing documentation containing this claim.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_documentation_contract.py` around lines 181 - 185, Correct the
contract entry in tests/test_documentation_contract.py by keeping the
filter_ir_mid claim while pointing its source to an existing tracked
documentation file containing the exact excerpt, or restore
docs/guide/tutorial_compatibility.md with that claim. Ensure the referenced path
and excerpt both resolve without changing the contract text or associated test
symbol.

Comment on lines +629 to +630
"M = mx.random.normal(shape=(3,3))\n",
"matrix = CartesianTensor(M + M.transpose(0, 1), formula='ij=ji').to_irrep_tensor()\n",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

M.transpose(0, 1) is a no-op, so this isn't symmetrizing.

transpose(0, 1) is the identity permutation on a 2D array, making M + M.transpose(0, 1) equal to 2*M rather than M + Mᵀ. The intended transpose is transpose(1, 0) (or M.T), consistent with the rank-3/rank-4 cells (Lines 692-694, 731-733). The printed Rs happens to stay correct because formula='ij=ji' projects onto the symmetric irreps regardless, but the input construction is misleading.

🐛 Proposed fix
-matrix = CartesianTensor(M + M.transpose(0, 1), formula='ij=ji').to_irrep_tensor()
+matrix = CartesianTensor(M + M.transpose(1, 0), formula='ij=ji').to_irrep_tensor()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"M = mx.random.normal(shape=(3,3))\n",
"matrix = CartesianTensor(M + M.transpose(0, 1), formula='ij=ji').to_irrep_tensor()\n",
"M = mx.random.normal(shape=(3,3))\n",
"matrix = CartesianTensor(M + M.transpose(1, 0), formula='ij=ji').to_irrep_tensor()\n",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/data_types.ipynb` around lines 629 - 630, Update the matrix
construction near CartesianTensor to transpose M with axes (1, 0) instead of (0,
1), so M + M.transpose(1, 0) actually symmetrizes the matrix while preserving
the existing formula and conversion flow.

Comment on lines +201 to +206
def random_point_signal(lmax, N=5, r_min=1e-1, std=1.):
n = N
points = mx.norm.random(n, 3) * std / mx.sqrt(3.)
points /= points.norm(2, -1, keepdim=True)
# select = (points.norm(2, -1) > r_min).nonzero()
return io.SphericalTensor(lmax, p_val=1, p_arg=-1).from_geometry_adjusted(points)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

random_point_signal references several nonexistent/foreign APIs and will fail if called.

  • Line 203 mx.norm.random(n, 3) is not an MLX API (MLX uses mx.random.normal(shape=(n, 3))).
  • Line 204 points.norm(2, -1, keepdim=True) is Torch tensor syntax, not MLX (mx.linalg.norm(points, axis=-1, keepdims=True)).
  • Line 206 io.SphericalTensor(...).from_geometry_adjusted(...)io here is ase.io; this is a leftover e3nn reference. The MLX helper is SphericalTensor.from_geometry(points, lmax).

This function cannot run as written.

🐛 Proposed fix
-def random_point_signal(lmax, N=5, r_min=1e-1, std=1.):
-    n = N
-    points = mx.norm.random(n, 3) * std / mx.sqrt(3.)
-    points /= points.norm(2, -1, keepdim=True)
-#     select = (points.norm(2, -1) > r_min).nonzero()
-    return io.SphericalTensor(lmax, p_val=1, p_arg=-1).from_geometry_adjusted(points)
+def random_point_signal(lmax, N=5, r_min=1e-1, std=1.0):
+    points = mx.random.normal(shape=(N, 3)) * std / math.sqrt(3.0)
+    points = points / mx.linalg.norm(points, axis=-1, keepdims=True)
+    return SphericalTensor.from_geometry(points, lmax)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def random_point_signal(lmax, N=5, r_min=1e-1, std=1.):
n = N
points = mx.norm.random(n, 3) * std / mx.sqrt(3.)
points /= points.norm(2, -1, keepdim=True)
# select = (points.norm(2, -1) > r_min).nonzero()
return io.SphericalTensor(lmax, p_val=1, p_arg=-1).from_geometry_adjusted(points)
def random_point_signal(lmax, N=5, r_min=1e-1, std=1.0):
points = mx.random.normal(shape=(N, 3)) * std / math.sqrt(3.0)
points = points / mx.linalg.norm(points, axis=-1, keepdims=True)
return SphericalTensor.from_geometry(points, lmax)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tutorials/other_helpers.py` around lines 201 - 206, Update
random_point_signal to use MLX APIs throughout: generate points with
mx.random.normal(shape=(n, 3)), normalize them with mx.linalg.norm(points,
axis=-1, keepdims=True), and construct the result via
SphericalTensor.from_geometry(points, lmax) instead of the foreign
io.SphericalTensor/from_geometry_adjusted call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant