class_docstr: Higher-Order Factorization Machine base class.
class_docstr_tok: ["Higher-Order","Factorization","Machine","base","class","."]
code: def _update_latents(self, x, g_loss):
      # For notational convenience
      v, l1, l2, sign = (
          self.latents,
          self.l1_latent,
          self.l2_latent,
          utils.math.sign,
      )
  
      # Calculate each latent factor gradient before updating any
      gradients = collections.defaultdict(
          lambda: collections.defaultdict(
              lambda: collections.defaultdict(float)
          )
      )
  
      for d in range(2, self.degree + 1):
          for combination in itertools.combinations(x.keys(), d):
              feature_product = functools.reduce(
                  lambda x, y: x * y, (x[j] for j in combination)
              )
  
              for f in range(self.n_factors):
                  latent_product = functools.reduce(
                      lambda x, y: x * y,
                      (v[j][d][f] for j in combination),
                  )
  
                  for j in combination:
                      gradients[j][d][f] += (
                          feature_product * latent_product / v[j][d][f]
                      )
  
      # Finally update the latent weights
      for j in x.keys():
          for d in range(2, self.degree + 1):
              self.latents[j][d] = self.latent_optimizer.step(
                  w=v[j][d],
                  g={
                      f: g_loss * gradients[j][d][f]
                      + l1 * sign(v[j][d][f])
                      + 2 * l2 * v[j][d][f]
                      for f in range(self.n_factors)
                  },
              )
  
imports: from __future__ import annotations
  import collections
  import functools
  import itertools
  import numpy
  from river import base, optim, utils
  from .base import BaseFM
label_desc: Use image node_id 8 for calling the HOFM obj's underlying member method code with example usage: obj._update_latents(x, g_loss) without return types