From 32ad7f44dc42d953c37e616b889aa608ee6c4e75 Mon Sep 17 00:00:00 2001 From: SundaramGupta Date: Mon, 14 Sep 2026 08:49:58 +0530 Subject: [PATCH 1/2] feat(aggregation): add Composition class --- torchjd/aggregation/composition.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 torchjd/aggregation/composition.py diff --git a/torchjd/aggregation/composition.py b/torchjd/aggregation/composition.py new file mode 100644 index 00000000..dec608a3 --- /dev/null +++ b/torchjd/aggregation/composition.py @@ -0,0 +1,25 @@ +import torch +from torch import Tensor +from typing import Sequence +from torchjd.aggregation.abstract import Aggregator + +class Composition(Aggregator): + """ + Composes a sequence of aggregators/transforms into a single aggregator. + Executes each aggregator sequentially on the Jacobian matrix. + """ + def __init__(self, aggregators: Sequence[Aggregator]): + super().__init__() + if not aggregators: + raise ValueError("Aggregators sequence cannot be empty.") + self.aggregators = list(aggregators) + + def __call__(self, matrix: Tensor) -> Tensor: + out = matrix + for aggregator in self.aggregators: + out = aggregator(out) + return out + + def __repr__(self) -> str: + names = [agg.__class__.__name__ for agg in self.aggregators] + return f"Composition({', '.join(names)})" From bfb628220805904af59c5645801a61ccc7377b19 Mon Sep 17 00:00:00 2001 From: SundaramGupta Date: Mon, 14 Sep 2026 08:57:05 +0530 Subject: [PATCH 2/2] feat(aggregation): add __lshift__ operator to Aggregator Overload __lshift__ operator to support composability syntax for chaining aggregators and matrix transformations. --- src/torchjd/aggregation/_aggregator_bases.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/torchjd/aggregation/_aggregator_bases.py b/src/torchjd/aggregation/_aggregator_bases.py index 9dd014ca..85be8a44 100644 --- a/src/torchjd/aggregation/_aggregator_bases.py +++ b/src/torchjd/aggregation/_aggregator_bases.py @@ -44,6 +44,15 @@ def __repr__(self) -> str: def __str__(self) -> str: return f"{self.__class__.__name__}" + def __lshift__(self, other: "Aggregator") -> "Composition": + from torchjd.aggregation.composition import Composition + + if isinstance(other, Composition): + return Composition([*other.aggregators, self]) + elif isinstance(self, Composition): + return Composition([*self.aggregators, other]) + return Composition([other, self]) + class WeightedAggregator(Aggregator): """