@@ -35,10 +35,10 @@ class PBI(Scalarizer):
3535 direction. The paper uses ``5`` in its experiments; there is no single best value, and the
3636 paper notes that a too large or too small value worsens the result.
3737 :param weights: The preference vector :math:`r`, giving the direction along which the values are
38- decomposed. It must have the same shape as the values passed at call time. To approximate the
39- whole Pareto front rather than a single trade-off, it should be re-sampled from a Dirichlet
40- distribution and reassigned before every call, e.g. for ``m`` objectives
41- ``pbi.weights = torch.distributions.Dirichlet(torch.ones(m)).sample()``.
38+ decomposed. Its values should be non-negative. It must have the same shape as the values
39+ passed at call time. To approximate the whole Pareto front rather than a single trade-off, it
40+ should be re-sampled from a Dirichlet distribution and reassigned before every call, e.g. for
41+ ``m`` objectives `` pbi.weights = torch.distributions.Dirichlet(torch.ones(m)).sample()``.
4242 :param reference: The reference (ideal) point :math:`z^*` subtracted from the values. It should
4343 be a lower bound on the values. If ``None``, the origin is used, which assumes non-negative
4444 values. If provided, it must have the same shape as the values passed at call time.
@@ -78,9 +78,13 @@ def forward(self, values: Tensor, /) -> Tensor:
7878 direction = self .weights .flatten ()
7979 direction = direction / direction .norm ()
8080
81- d1 = ( f * direction ). sum ()
81+ d1 = f @ direction
8282 perpendicular = f - d1 * direction
83- d2 = torch .sqrt ((perpendicular * perpendicular ).sum () + _EPSILON )
83+ # `perpendicular` has a zero norm when the values lie exactly on the preference direction
84+ # (always the case for a single-objective input, which has no perpendicular component). The
85+ # norm's gradient is then undefined, so we add a small constant under the square root to keep
86+ # it finite; this shifts the result by at most around 1e-6 there and is negligible elsewhere.
87+ d2 = torch .sqrt (perpendicular @ perpendicular + _EPSILON )
8488 return d1 + self .theta * d2
8589
8690 def __repr__ (self ) -> str :
0 commit comments