docs: def _binary_op(cls,
              x: 'TensorFluent',
              y: 'TensorFluent',
              op: Callable[[tf.Tensor, tf.Tensor], tf.Tensor],
              dtype: tf.DType) -> 'TensorFluent':
          '''Returns a TensorFluent for the binary `op` applied to fluents `x` and `y`.
  
          Args:
              x: The first operand.
              y: The second operand.
              op: The binary operator.
              dtype: The output's data type.
  
          Returns:
              A TensorFluent wrapping the binary operator's output.
          '''
          # scope
          s1 = x.scope.as_list()
          s2 = y.scope.as_list()
          scope, perm1, perm2 = TensorFluentScope.broadcast(s1, s2)
          if x.batch and perm1 != []:
              perm1 = [0] + [p+1 for p in perm1]
          if y.batch and perm2 != []:
              perm2 = [0] + [p+1 for p in perm2]
          x = x.transpose(perm1)
          y = y.transpose(perm2)
  
          # shape
          reshape1, reshape2 = TensorFluentShape.broadcast(x.shape, y.shape)
          if reshape1 is not None:
              x = x.reshape(reshape1)
          if reshape2 is not None:
              y = y.reshape(reshape2)
  
          # dtype
          x = x.cast(dtype)
          y = y.cast(dtype)
  
          # operation
          t = op(x.tensor, y.tensor)
  
          # batch
          batch = x.batch or y.batch
  
          return TensorFluent(t, scope, batch=batch)