code: def add_rule(cls, name, pattern, replacement, attr=None):
          
          from qnet.utils.check_rules import check_rules_dict
          if attr is None:
              attr = cls._rules_attr()
          if name in getattr(cls, attr):
              raise ValueError(
                  "Duplicate key '%s': rule already exists" % name)
          getattr(cls, attr).update(check_rules_dict(
              [(name, (pattern, replacement))]))
docs: Add an algebraic rule for :meth:`create` to the class
  
          Args:
              name (str): Name of the rule. This is used for debug logging to
                  allow an analysis of which rules where applied when creating an
                  expression. The `name` can be arbitrary, but it must be unique.
                  Built-in rules have names ``'Rxxx'`` where ``x`` is a digit
              pattern (.Pattern): A pattern constructed by :func:`.pattern_head`
                  to match a :class:`.ProtoExpr`
              replacement (callable): callable that takes the wildcard names
                  defined in `pattern` as keyword arguments and returns an
                  evaluated expression.
              attr (None or str): Name of the class attribute to which to add the
                  rule. If None, one of ``'_rules'``, ``'_binary_rules'`` is
                  automatically chosen
  
          Raises:
              TypeError: if `name` is not a :class:`str` or `pattern` is not a
                  :class:`.Pattern` instance
              ValueError: if `pattern` is not set up to match a
                  :class:`.ProtoExpr`; if there there is already a rule with the
                  same `name`; if `replacement` is not a callable or does not
                  take all the wildcard names in `pattern` as arguments
              AttributeError: If invalid `attr`
  
          Note:
              The "automatic" rules added by this method are applied *before*
              expressions are instantiated (against a corresponding
              :class:`.ProtoExpr`). In contrast,
              :meth:`apply_rules`/:meth:`apply_rule` are applied to fully
              instantiated objects.
  
              The :func:`.temporary_rules` context manager may be used to create
              a context in which rules may be defined locally.