class_docstr: Batch Integrated Negative Posterior Variance for Active Learning.
  
  This acquisition function quantifies the (negative) integrated posterior variance
  (excluding observation noise, computed using MC integration) of the model.
  In that, it is a proxy for global model uncertainty, and thus purely focused on
  "exploration", rather the "exploitation" of many of the classic Bayesian
  Optimization acquisition functions.
  
  See [Seo2014activedata]_, [Chen2014seqexpdesign]_, and [Binois2017repexp]_.
class_docstr_tok: ["Batch","Integrated","Negative","Posterior","Variance","for","Active","Learning",".","This","acquisition","function","quantifies","the","(","negative",")","integrated","posterior","variance","(","excluding","observation","noise",",","computed","using","MC","integration",")","of","the","model",".","In","that",",","it","is","a","proxy","for","global","model","uncertainty",",","and","thus","purely","focused","on","''","exploration","''",",","rather","the","``","exploitation","''","of","many","of","the","classic","Bayesian","Optimization","acquisition","functions",".","See","[","Seo2014activedata","]","_",",","[","Chen2014seqexpdesign","]","_",",","and","[","Binois2017repexp","]","_","."]
code: def forward(self, X: Tensor) -> Tensor:
      # Construct the fantasy model (we actually do not use the full model,
      # this is just a convenient way of computing fast posterior covariances
      fantasy_model = self.model.fantasize(
          X=X,
          sampler=self.sampler,
      )
  
      bdims = tuple(1 for _ in X.shape[:-2])
      if self.model.num_outputs > 1:
          # We use q=1 here b/c ScalarizedObjective currently does not fully exploit
          # LinearOperator operations and thus may be slow / overly memory-hungry.
          # TODO (T52818288): Properly use LinearOperators in scalarize_posterior
          mc_points = self.mc_points.view(-1, *bdims, 1, X.size(-1))
      else:
          # While we only need marginal variances, we can evaluate for q>1
          # b/c for GPyTorch models lazy evaluation can make this quite a bit
          # faster than evaluating in t-batch mode with q-batch size of 1
          mc_points = self.mc_points.view(*bdims, -1, X.size(-1))
  
      # evaluate the posterior at the grid points
      with settings.propagate_grads(True):
          posterior = fantasy_model.posterior(
              mc_points, posterior_transform=self.posterior_transform
          )
  
      neg_variance = posterior.variance.mul(-1.0)
  
      if self.posterior_transform is None:
          # if single-output, shape is 1 x batch_shape x num_grid_points x 1
          return neg_variance.mean(dim=-2).squeeze(-1).squeeze(0)
      else:
          # if multi-output + obj, shape is num_grid_points x batch_shape x 1 x 1
          return neg_variance.mean(dim=0).squeeze(-1).squeeze(-1)
  
file_path: botorch/botorch/acquisition/active_learning.py
imports: from __future__ import annotations
  from typing import Optional
  import torch
  from botorch import settings
  from botorch.acquisition.analytic import AnalyticAcquisitionFunction
  from botorch.acquisition.monte_carlo import MCAcquisitionFunction
  from botorch.acquisition.objective import MCAcquisitionObjective, PosteriorTransform
  from botorch.models.model import Model
  from botorch.sampling.base import MCSampler
  from botorch.sampling.normal import SobolQMCNormalSampler
  from botorch.utils.transforms import concatenate_pending_points, t_batch_mode_transform
  from torch import Tensor
label_desc: Use image node_id 2 for calling the qNegIntegratedPosteriorVariance obj's underlying member method code with example usage: obj.forward(X) and returns: neg_variance, neg_variance