@@ -6,32 +6,44 @@ vector of losses) into a single scalar optimizable with a standard `loss.backwar
66This file is for contributors working on scalarizers. For the list of available scalarizers and their
77full API, see [ torchjd.org] ( https://torchjd.org/latest/docs/scalarization/ ) .
88
9- ## The contract
9+ ## The abstraction
1010
11- A scalarizer subclasses ` Scalarizer ` (in [ ` _scalarizer_base.py ` ] ( _scalarizer_base.py ) ) and implements
12- one method:
11+ A scalarizer captures a single decision: ** how to collapse a vector of objective values into one
12+ scalar to minimize** , using only those values. It is the value-level counterpart of an aggregator,
13+ which makes the same kind of decision at the gradient level. Everything after it (backpropagation,
14+ the optimizer step) is standard PyTorch.
15+
16+ Concretely, it subclasses ` Scalarizer ` (in [ ` _scalarizer_base.py ` ] ( _scalarizer_base.py ) ) and
17+ implements one method:
1318
1419``` python
1520def forward (self , values : Tensor, / ) -> Tensor:
1621 ...
1722```
1823
19- - ** Any shape in, scalar out:** it reduces over * all* elements of ` values ` (0-dim, vector, matrix,
20- higher-dim) into a 0-dim scalar.
21- - ** ` values ` , not ` losses ` :** a scalarizer is generic and not tied to losses.
22- - ** Pure and differentiable:** the output depends only on ` values ` and the configured parameters, so
23- that ` scalarizer(values).backward() ` produces the gradient.
24+ - It reduces over * all* elements of ` values ` , of any shape, into a 0-dim scalar.
25+ - The result is a ** differentiable** function of ` values ` and the configured parameters, so that
26+ ` scalarizer(values).backward() ` produces the gradient.
27+
28+ ## What is not a scalarizer
29+
30+ A scalarizer sees only the values. Its gradient-level counterpart lives in the
31+ [ aggregation] ( ../aggregation ) package: an ` Aggregator ` (which, like a scalarizer, can be stateful)
32+ combines the per-objective * gradients* (the Jacobian or its Gramian) into a single gradient.
33+
34+ So if your method needs the model, its parameters, or the per-objective gradients (gradient norms,
35+ for instance), it is an aggregator, not a scalarizer.
2436
2537## Adding one
2638
2739A new scalarizer is a class plus the files that register it. Mirror an existing scalarizer of the
2840same kind:
2941
30- - ` _<name>.py ` — the class.
31- - ` __init__.py ` — the import and an ` __all__ ` entry.
32- - ` docs/source/docs/scalarization/<name>.rst ` — the docs page, added to the ` index.rst ` toctree.
33- - ` tests/unit/scalarization/test_<name>.py ` — the tests.
34- - ` CHANGELOG.md ` — an entry under ` [Unreleased] ` .
42+ - ` _<name>.py ` : the class.
43+ - ` __init__.py ` : the import and an ` __all__ ` entry.
44+ - ` docs/source/docs/scalarization/<name>.rst ` : the docs page, added to the ` index.rst ` toctree.
45+ - ` tests/unit/scalarization/test_<name>.py ` : the tests.
46+ - ` CHANGELOG.md ` : an entry under ` [Unreleased] ` .
3547
3648## State
3749
@@ -46,19 +58,15 @@ weights, a loss history). When it does:
4658- ** ` nn.Parameter ` vs buffer:** trainable state is an ` nn.Parameter ` ; non-trained tensors that must
4759 move with ` .to() ` are registered with ` register_buffer ` .
4860
49- ## What is not a scalarizer
50-
51- A scalarizer only ever sees the loss values.
52-
53- Anything that needs the model, its parameters, or the per-task gradients belongs in the
54- [ aggregation] ( ../aggregation ) package as a ` Weighting ` / ` Aggregator ` , which operates on the Jacobian
55- or its Gramian. If you reach for gradient norms or the network inside ` forward ` , you are writing an
56- aggregator, not a scalarizer.
61+ Randomness is not state: a scalarizer may draw fresh randomness on each call (like the random
62+ baseline) without being ` Stateful ` . There is no stochastic mixin; it just uses the global torch RNG,
63+ so document the behavior and let users seed it with ` torch.manual_seed ` .
5764
5865## Things to be careful about
5966
60- - ** Determinism and side effects:** the output should depend only on ` values ` and the configured
61- parameters. Any state change must be deliberate, explicit, and undone by ` reset() ` .
67+ - ** Determinism and side effects:** the output should depend only on ` values ` , the configured
68+ parameters, and (if the method is intentionally random) the global RNG. Any state change must be
69+ deliberate, explicit, and undone by ` reset() ` .
6270- ** Numerical stability:** keep the reduction finite on the edges of its domain (log-sum-exp
6371 centering, an eps under a norm or in a denominator, etc.), and explain any value shift in a comment
6472 and a ` .. note:: ` .
0 commit comments