Thank you for pointing to that first link 1. Between that and the other link 1 I had found, I have been able to assemble something that works. That being said, the way that I define WeightedLossFunc is a slightly hacky work-around, because I don’t know how to define its backward method when I need to account for some additional transform with its own backward to deal with. For instance, I have something like:
class WeightedLossFunc(Function):
    @staticmethod
    def forward(ctx, weights, *differences):
        batch_size = differences[0].size(0)
        # batch meaned squared errors
        squared_errors = (torch.cat([
            d.view(batch_size, -1).pow(2)
            .sum(dim=1, keepdim=True)
            for d in differences], dim=1)
                          .mean(dim=0, keepdim=True))
        # weights must be positive, norm 1.
        weights[weights < 0] = 1e-6
        weights = F.normalize(weights, p=2, dim=0)
        ctx.save_for_backward(squared_errors, weights, *differences)
        wse = squared_errors.mm(weights.view(-1, 1)).squeeze()
        return wse
    @staticmethod
    def backward(ctx, grad_output):
        squared_errors, weights = ctx.saved_tensors[:2]
        differences = ctx.saved_tensors[2:]
        batch_size = differences[0].size(0)
        grad_differences = grad_weights = None
        if ctx.needs_input_grad[0]:
            # compute grad_weights
            grad_weights = squared_errors.squeeze()
            # project gradient onto Tangent Space of Sphere at point weights
            grad_weights = WaveletLossFunc._orthoproject(grad_weights, weights)
        if ctx.needs_input_grad[1]:
            # compute all grad_differences
            grad_differences = [2 * d * weights[i] / batch_size
                                for i, d in enumerate(differences)]
        return (grad_weights, *grad_differences)

(Actually, I return -grad_weights in my own code, because I don’t want the weights to be concentrated on the part of the data that achieves the lowest loss. I don’t know if this naïve choice really achieves what I want here, or if I should actually implement something GAN-like that trains adversarially. Initial tests seem to suggest it’s working fine.) Then the loss itself is implemented in the following fashion:
class WeightedLoss(nn.Module):
    def __init__(self, some_parameters):
        super().__init__()
        self.some_transform = SomeTransform(some_parameters)
        weights = F.normalize(torch.rand(self.some_transform.output_length), 
                              dim=0)
        self.weights = nn.Parameter(weights)
        self.weighted_loss = WeightedLossFunc.apply
    def forward(self, x, y):
        transformed_differences = self.some_transform(x - y)
        return self.weighted_loss(self.weights, *transformed_differences)

I guess what I’d like to know is whether there’s an easy way to write WeightedLossFunc.backward if I were to move the transformed_differences = self.some_transform(x - y) call into WeightedLossFunc.forward. I think that both codes should function identically to one another, but one of them makes more sense from an organizational/structural perspective; my current way is a little kludgy. I think that knowing this would also serve to write down one of the final missing pieces in PyTorch’s autograd tutorial.