class_docstr: This is a utility class that combines a model, a masker object, and a current input.
  
  The combination of a model, a masker object, and a current input produces a binary set
  function that can be called to mask out any set of inputs. This class attempts to be smart
  about only evaluating the model for background samples when the inputs changed (note this
  requires the masker object to have a .invariants method).
class_docstr_tok: ["This","is","a","utility","class","that","combines","a","model",",","a","masker","object",",","and","a","current","input",".","The","combination","of","a","model",",","a","masker","object",",","and","a","current","input","produces","a","binary","set","function","that","can","be","called","to","mask","out","any","set","of","inputs",".","This","class","attempts","to","be","smart","about","only","evaluating","the","model","for","background","samples","when","the","inputs","changed","(","note","this","requires","the","masker","object","to","have","a",".invariants","method",")","."]
code: def _full_masking_call(self, masks, zero_index=None, batch_size=None):
      if batch_size is None:
          batch_size = len(masks)
      do_delta_masking = (
          getattr(self.masker, "reset_delta_masking", None) is not None
      )
      num_varying_rows = np.zeros(len(masks), dtype=int)
      batch_positions = np.zeros(len(masks) + 1, dtype=int)
      varying_rows = []
      if self._variants is not None:
          delta_tmp = self._variants.copy().astype(int)
      all_outputs = []
      for batch_ind in range(0, len(masks), batch_size):
          mask_batch = masks[batch_ind : batch_ind + batch_size]
          all_masked_inputs = []
          num_mask_samples = np.zeros(len(mask_batch), dtype=int)
          last_mask = np.zeros(mask_batch.shape[1], dtype=bool)
          for i, mask in enumerate(mask_batch):
              # mask the inputs
              delta_mask = mask ^ last_mask
              if do_delta_masking and delta_mask.sum() == 1:
                  delta_ind = np.nonzero(delta_mask)[0][0]
                  masked_inputs = self.masker(
                      delta_ind, *self.args
                  ).copy()
              else:
                  masked_inputs = self.masker(mask, *self.args)
  
              # get a copy that won't get overwritten by the next iteration
              if not getattr(self.masker, "immutable_outputs", False):
                  masked_inputs = copy.deepcopy(masked_inputs)
  
              # wrap the masked inputs if they are not already in a tuple
              if not isinstance(masked_inputs, tuple):
                  masked_inputs = (masked_inputs,)
  
              # masked_inputs = self.masker(mask, *self.args)
              num_mask_samples[i] = len(masked_inputs[0])
  
              # see which rows have been updated, so we can only evaluate the model on the rows we need to
              if i == 0 or self._variants is None:
                  varying_rows.append(
                      np.ones(num_mask_samples[i], dtype=bool)
                  )
                  num_varying_rows[batch_ind + i] = num_mask_samples[i]
              else:
                  # a = np.any(self._variants & delta_mask, axis=1)
                  # a = np.any(self._variants & delta_mask, axis=1)
                  # a = np.any(self._variants & delta_mask, axis=1)
                  # (self._variants & delta_mask).sum(1) > 0
  
                  np.bitwise_and(
                      self._variants, delta_mask, out=delta_tmp
                  )
                  varying_rows.append(
                      np.any(delta_tmp, axis=1)
                  )  # np.any(self._variants & delta_mask, axis=1))
                  num_varying_rows[batch_ind + i] = varying_rows[
                      -1
                  ].sum()
                  # for i in range(20):
                  #     varying_rows[-1].sum()
              last_mask[:] = mask
  
              batch_positions[batch_ind + i + 1] = (
                  batch_positions[batch_ind + i]
                  + num_varying_rows[batch_ind + i]
              )
  
              # subset the masked input to only the rows that vary
              if num_varying_rows[batch_ind + i] != num_mask_samples[i]:
                  if len(self.args) == 1:
                      # _ = masked_inputs[varying_rows[-1]]
                      # _ = masked_inputs[varying_rows[-1]]
                      # _ = masked_inputs[varying_rows[-1]]
                      masked_inputs_subset = masked_inputs[0][
                          varying_rows[-1]
                      ]
                  else:
                      masked_inputs_subset = [
                          v[varying_rows[-1]]
                          for v in zip(*masked_inputs[0])
                      ]
                  masked_inputs = (
                      masked_inputs_subset,
                  ) + masked_inputs[1:]
  
              # define no. of list based on output of masked_inputs
              if len(all_masked_inputs) != len(masked_inputs):
                  all_masked_inputs = [
                      [] for m in range(len(masked_inputs))
                  ]
  
              for i, v in enumerate(masked_inputs):
                  all_masked_inputs[i].append(v)
  
          joined_masked_inputs = tuple(
              [np.concatenate(v) for v in all_masked_inputs]
          )
          outputs = self.model(*joined_masked_inputs)
          _assert_output_input_match(joined_masked_inputs, outputs)
          all_outputs.append(outputs)
      outputs = np.concatenate(all_outputs)
  
      if (
          self.linearize_link
          and self.link != links.identity
          and self._linearizing_weights is None
      ):
          self.background_outputs = outputs[
              batch_positions[zero_index] : batch_positions[
                  zero_index + 1
              ]
          ]
          self._linearizing_weights = link_reweighting(
              self.background_outputs, self.link
          )
  
      averaged_outs = np.zeros(
          (len(batch_positions) - 1,) + outputs.shape[1:]
      )
      max_outs = (
          self._masker_rows
          if self._masker_rows is not None
          else max(len(r) for r in varying_rows)
      )
      last_outs = np.zeros((max_outs,) + outputs.shape[1:])
      varying_rows = np.array(varying_rows)
  
      _build_fixed_output(
          averaged_outs,
          last_outs,
          outputs,
          batch_positions,
          varying_rows,
          num_varying_rows,
          self.link,
          self._linearizing_weights,
      )
  
      return averaged_outs
  
imports: import copy
  import numpy
  import scipy.sparse
  from numba import njit
  from ..None import links
label_desc: Use image node_id 3 for calling the MaskedModel obj's underlying member method code with example usage: obj._full_masking_call(masks, zero_index, batch_size) and returns: averaged_outs