class_docstr: Bayesian or-of-and algorithm.
  Generates patterns that satisfy the minimum support and maximum length and then select the Nrules rules that have the highest entropy.
  In function SA_patternbased, each local maximum is stored in maps and the best BOA is returned.
  Remember here the BOA contains only the index of selected rules from Nrules self.rules_
class_docstr_tok: ["Bayesian","or-of-and","algorithm",".","Generates","patterns","that","satisfy","the","minimum","support","and","maximum","length","and","then","select","the","Nrules","rules","that","have","the","highest","entropy",".","In","function","SA_patternbased",",","each","local","maximum","is","stored","in","maps","and","the","best","BOA","is","returned",".","Remember","here","the","BOA","contains","only","the","index","of","selected","rules","from","Nrules","self.rules_"]
docstr: Parameters
  ----------
  X : array-like, shape = [n_samples, n_features]
      Training data
  y : array_like, shape = [n_samples]
      Labels
  
  feature_names : array_like, shape = [n_features], optional (default: [])
      String labels for each feature.
      If empty and X is a DataFrame, column labels are used.
      If empty and X is not a DataFrame, then features are simply enumerated
docstr_tok: ["Parameters","--","--","--","--","--","X",":","array-like",",","shape","=","[","n_samples",",","n_features","]","Training","data","y",":","array_like",",","shape","=","[","n_samples","]","Labels","feature_names",":","array_like",",","shape","=","[","n_features","]",",","optional","(","default",":","[","]",")","String","labels","for","each","feature",".","If","empty","and","X","is","a","DataFrame",",","column","labels","are","used",".","If","empty","and","X","is","not","a","DataFrame",",","then","features","are","simply","enumerated"]
code: def fit(
      self, X, y, feature_names: list = None, init=[], verbose=False
  ):
      """
      Parameters
      ----------
      X : array-like, shape = [n_samples, n_features]
          Training data
      y : array_like, shape = [n_samples]
          Labels
  
      feature_names : array_like, shape = [n_features], optional (default: [])
          String labels for each feature.
          If empty and X is a DataFrame, column labels are used.
          If empty and X is not a DataFrame, then features are simply enumerated
      """
      # check inputs
      self.attr_level_num = defaultdict(
          int
      )  # any missing value defaults to 0
      self.attr_names = []
  
      X, y, feature_names = check_fit_arguments(
          self, X, y, feature_names
      )
      np.random.seed(self.random_state)
  
      # convert to pandas DataFrame
      X = pd.DataFrame(X, columns=feature_names)
  
      for i, name in enumerate(X.columns):
          self.attr_level_num[name] += 1
          self.attr_names.append(name)
      self.attr_names_orig = deepcopy(self.attr_names)
      self.attr_names = list(set(self.attr_names))
  
      # set up patterns
      self._set_pattern_space()
  
      # parameter checking
      if (
          self.alpha_l is None
          or self.beta_l is None
          or len(self.alpha_l) != self.maxlen
          or len(self.beta_l) != self.maxlen
      ):
          if verbose:
              print(
                  "No or wrong input for alpha_l and beta_l - the model will use default parameters."
              )
          self.C = [1.0 / self.maxlen] * self.maxlen
          self.C.insert(0, -1)
          self.alpha_l = [10] * (self.maxlen + 1)
          self.beta_l = [
              10 * self.pattern_space[i] / self.C[i]
              for i in range(self.maxlen + 1)
          ]
      else:
          self.alpha_l = [1] + list(self.alpha_l)
          self.beta_l = [1] + list(self.beta_l)
  
      # setup
      self._generate_rules(X, y, verbose)
      n_rules_current = len(self.rules_)
      self.rules_len_list = [len(rule) for rule in self.rules_]
      maps = defaultdict(list)
      T0 = 1000  # initial temperature for simulated annealing
      split = 0.7 * self.num_iterations
  
      # run simulated annealing
      for chain in range(self.num_chains):
          # initialize with a random pattern set
          if init != []:
              rules_curr = init.copy()
          else:
              assert (
                  n_rules_current > 1
              ), f"Only {n_rules_current} potential rules found, change hyperparams to allow for more"
              N = sample(range(1, min(8, n_rules_current), 1), 1)[0]
              rules_curr = sample(range(n_rules_current), N)
          rules_curr_norm = self._normalize(rules_curr)
          pt_curr = -100000000000
          maps[chain].append(
              [
                  -1,
                  [pt_curr / 3, pt_curr / 3, pt_curr / 3],
                  rules_curr,
                  [self.rules_[i] for i in rules_curr],
              ]
          )
  
          for iter in range(self.num_iterations):
              if iter >= split:
                  p = np.array(range(1 + len(maps[chain])))
                  p = np.array(list(_accumulate(p)))
                  p = p / p[-1]
                  index = _find_lt(p, random())
                  rules_curr = maps[chain][index][2].copy()
                  rules_curr_norm = maps[chain][index][2].copy()
  
              # propose new rules
              rules_new, rules_norm = self._propose(
                  rules_curr.copy(), rules_curr_norm.copy(), self.q, y
              )
  
              # compute probability of new rules
              cfmatrix, prob = self._compute_prob(rules_new, y)
              T = T0 ** (
                  1 - iter / self.num_iterations
              )  # temperature for simulated annealing
              pt_new = sum(prob)
              with warnings.catch_warnings():
                  if not verbose:
                      warnings.simplefilter("ignore")
                  alpha = np.exp(float(pt_new - pt_curr) / T)
  
              if pt_new > sum(maps[chain][-1][1]):
                  maps[chain].append(
                      [
                          iter,
                          prob,
                          rules_new,
                          [self.rules_[i] for i in rules_new],
                      ]
                  )
                  if verbose:
                      print(
                          (
                              "\n** chain = {}, max at iter = {} ** \n accuracy = {}, TP = {},FP = {}, TN = {}, FN = {}"
                              "\n pt_new is {}, prior_ChsRules={}, likelihood_1 = {}, likelihood_2 = {}\n"
                          ).format(
                              chain,
                              iter,
                              (cfmatrix[0] + cfmatrix[2] + 0.0)
                              / len(y),
                              cfmatrix[0],
                              cfmatrix[1],
                              cfmatrix[2],
                              cfmatrix[3],
                              sum(prob),
                              prob[0],
                              prob[1],
                              prob[2],
                          )
                      )
                      self._print_rules(rules_new)
                      print(rules_new)
              if random() <= alpha:
                  rules_curr_norm, rules_curr, pt_curr = (
                      rules_norm.copy(),
                      rules_new.copy(),
                      pt_new,
                  )
      pt_max = [
          sum(maps[chain][-1][1]) for chain in range(self.num_chains)
      ]
      index = pt_max.index(max(pt_max))
      self.rules_ = maps[index][-1][3]
      return self
  
imports: import itertools
  import operator
  import os
  import warnings
  from os.path import join
  from bisect import bisect_left
  from collections import defaultdict
  from copy import deepcopy
  from itertools import combinations
  from random import sample
  import numpy
  import pandas
  from mlxtend.frequent_patterns import fpgrowth
  from numpy.random import random
  from pandas import read_csv
  from scipy.sparse import csc_matrix
  from sklearn.base import BaseEstimator, ClassifierMixin
  from sklearn.ensemble import RandomForestClassifier
  from sklearn.utils.multiclass import check_classification_targets
  from sklearn.utils.validation import check_X_y, check_is_fitted
  from imodels.rule_set.rule_set import RuleSet
  from imodels.util.arguments import check_fit_arguments
label_desc: Use image node_id 2 for calling the BayesianRuleSetClassifier obj's underlying member method code with example usage: obj.fit(X, y, feature_names, init, verbose) and returns: self