class_docstr: Base class for the decision rules based on the Hoeffding bound.
  
  It defines properties and operations shared by all the rule-based systems, for instance, how
  to perform rule expansion, how to monitor inputs, and whether or not a rule is currently
  covering an input datum.
  
  Parameters
  ----------
  template_splitter
      The attribute observer algorithm used to monitor and perform split attempts in numerical
      features. This splitter is be deep-copied to all the decision rules. The same attribute
      observer algorithms used by the Hoeffding Trees can be used with Hoeffding rules.
  split_criterion
      The criterion used to rank the split candidates. The same split criteria used by the
      Hoeffding Trees can be applied to the Hoeffding rules.
  attributes
      Other parameters passed to the rules via `**kwargs`.
docstr: Attempt to expand a decision rule.
  
  If the expansion succeeds, a new rule with all attributes reset and updated literals is
  returned along a boolean flag set to `True` to indicate the expansion. If the expansion
  fails, `self` is returned along with the boolean flag set to `False`.
  
  Parameters
  ----------
  delta
      The split test significance.
  tau
      The tie-breaking threshold.
  
  Returns
  -------
  rule, expanded
      The (potentially expanded) rule and an indicator of whether or not an expansion was
      successfully performed.
code: def expand(self, delta, tau):
      """
      Attempt to expand a decision rule.
  
      If the expansion succeeds, a new rule with all attributes reset and updated literals is
      returned along a boolean flag set to `True` to indicate the expansion. If the expansion
      fails, `self` is returned along with the boolean flag set to `False`.
  
      Parameters
      ----------
      delta
          The split test significance.
      tau
          The tie-breaking threshold.
  
      Returns
      -------
      rule, expanded
          The (potentially expanded) rule and an indicator of whether or not an expansion was
          successfully performed.
  
      """
      suggestions = []
      for att_id, splitter in self.splitters.items():
          suggestions.append(
              splitter.best_evaluated_split_suggestion(
                  criterion=self.split_criterion,
                  pre_split_dist=self.statistics,
                  att_idx=att_id,
                  binary_only=True,
              )
          )
      suggestions.sort()
  
      should_expand = False
      if len(suggestions) < 2:
          should_expand = True
      else:
          b_split = suggestions[-1]
          sb_split = suggestions[-2]
  
          hb = self._hoeffding_bound(
              self.split_criterion.range_of_merit(self.statistics),
              delta,
          )
  
          if b_split.merit > 0 and (
              b_split.merit - sb_split.merit > hb or hb < tau
          ):
              should_expand = True
  
      if should_expand:
          b_split = suggestions[-1]
          is_numerical = b_split.numerical_feature
          branch_no = self.split_criterion.select_best_branch(
              b_split.children_stats
          )
  
          if is_numerical:
              lit = NumericLiteral(
                  b_split.feature, b_split.split_info, branch_no != 0
              )
  
              literal_updated = False
              for literal in self.literals:
                  if lit.on == literal.on and lit.neg == literal.neg:
                      # Update thresholds rather than adding a new literal
                      if not literal.neg and lit.at < literal.at:
                          literal.at = lit.at
                          literal_updated = True
                          break
                      elif literal.neg and lit.at > literal.at:
                          literal.at = lit.at
                          literal_updated = True
                          break
  
              # No threshold was updated, thus a new literal is added
              if not literal_updated:
                  self.literals.append(lit)
          else:
              lit = NominalLiteral(
                  b_split.feature, b_split.split_info, branch_no != 0
              )
              # Add a new literal
              self.literals.append(lit)
  
          # Reset all the statistics stored in the decision rule
          updated_rule = self.clone()
          # Keep the literals
          updated_rule.literals.extend(self.literals)
  
          return updated_rule, True
  
      # If the expansion attempt failed, update the expansion tracker
      self._last_expansion_attempt_at = self.total_weight
      return self, False
  
imports: from __future__ import annotations
  import abc
  import copy
  import math
  import numbers
  import typing
  from river import base, tree
label_desc: Use image node_id 7 for calling the HoeffdingRule obj's underlying member method code with example usage: obj.expand(delta, tau) and returns: self,  False, updated_rule,  True