Skip to content

Add row norm function#253

Open
abtonmoy wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
abtonmoy:row-norm-fn
Open

Add row norm function#253
abtonmoy wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
abtonmoy:row-norm-fn

Conversation

@abtonmoy

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds row_norm_fn, a composable update normalization from #188: each row of the momentum is scaled to unit norm, with optional center_rows matching the polargrad functions. Usable as scaled_orthogonalize_fn of OrthogonalizedOptimizer; no new optimizer class.

Placed in orthogonalized_optimizers for composability. As noted in #188 this is not strictly an orthogonalization, so happy to move it if you prefer another home.

Tests

Unit row norms, per-row scale invariance, row-permutation equivariance, right-orthogonal equivariance, centered-output projection, zero-gradient edge case, and composition through OrthogonalizedOptimizer.step(). Verified locally on --device=cpu and --device=cuda, random seed and --seed=42; pre-commit clean.

Add row_norm_fn, a composable row-wise normalization update usable as
scaled_orthogonalize_fn of OrthogonalizedOptimizer. Follow-up to NVIDIA-NeMo#188.

Signed-off-by: Abdul Basit Tonmoy <abdulbasittonmoy@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds row_norm_fn, a composable row-wise L2 normalization function intended as a drop-in scaled_orthogonalize_fn for OrthogonalizedOptimizer. The implementation is mathematically sound and well-tested.

  • Core logic: The double-centering path correctly preserves extra_scale_factor through the final centering step, the eps-clamp produces exact zeros for zero-gradient inputs, and the fp32 upcast/downcast is handled correctly.
  • Tests: All seven parameterized test cases are mathematically valid, including equivariance tests with center_rows=True.

Confidence Score: 5/5

Safe to merge — standalone additive change with no modifications to existing optimizers

The implementation is a pure-function module with correct math for both center_rows paths, proper dtype round-trip, and exact zero output for zero-gradient inputs. No existing code is modified.

No files require special attention.

Important Files Changed

Filename Overview
emerging_optimizers/orthogonalized_optimizers/row_norm.py New row_norm_fn: mathematically correct row-wise L2 normalization with optional double-centering, dtype-safe fp32 upcast, and correct zero-gradient behavior.
tests/test_row_norm.py Comprehensive parameterized tests; all equivariance claims including center_rows=True are mathematically valid.

Reviews (2): Last reviewed commit: "Address review comments" | Re-trigger Greptile

Comment on lines +29 to +48
r"""Row-wise normalization of the update.

Scales each row of ``G`` to unit norm:

.. math::
u_i = G_i \, / \max(\lVert G_i \rVert_2, \epsilon)

Usable as ``scaled_orthogonalize_fn`` of
:class:`~emerging_optimizers.orthogonalized_optimizers.OrthogonalizedOptimizer`.

Args:
grad: The (momentum) tensor to normalize.
center_rows: If True, subtract the per-column mean (the average over the row axis, ``dim=0``)
before and after the update, so each column is zero-mean.
eps: Floor on the row norms.
extra_scale_factor: Extra multiplier on the update.

Returns:
The row-normalized update, same shape and dtype as ``grad``.
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Docstring unit-norm guarantee not qualified for center_rows=True

The docstring opens with "Scales each row of G to unit norm" and shows the math formula u_i = G_i / max(||G_i||_2, ε), implying unit-norm output. This holds only when center_rows=False. With center_rows=True, the post-centering step (update - update.mean(dim=0)) projects the update back onto the zero-column-mean subspace, which generally breaks the unit-row-norm property. A user who reads the docstring and passes center_rows=True expecting unit-norm rows will get incorrectly scaled rows. Notably, test_rows_close_to_unit_norm only exercises the default (center_rows=False) case, which confirms the guarantee doesn't extend to the centered variant. The docstring should clarify this (e.g., "when center_rows=False").

Signed-off-by: Abdul Basit Tonmoy <abdulbasittonmoy@gmail.com>
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