class_docstr: Gaussian guide with optimal conditional independence structure.
  
  This is equivalent to a full rank :class:`AutoMultivariateNormal` guide,
  but with a sparse precision matrix determined by dependencies and plates in
  the model [1]. Depending on model structure, this can have asymptotically
  better statistical efficiency than :class:`AutoMultivariateNormal` .
  
  This guide implements multiple backends for computation. All backends use
  the same statistically optimal parametrization. The default "dense" backend
  has computational complexity similar to :class:`AutoMultivariateNormal` .
  The experimental "funsor" backend can be asymptotically cheaper in terms of
  time and space (using Gaussian tensor variable elimination [2,3]), but
  incurs large constant overhead. The "funsor" backend requires `funsor
  <https://funsor.pyro.ai>`_ which can be installed via ``pip install
  pyro-ppl[funsor]``.
  
  The guide currently does not depend on the model's ``*args, **kwargs``.
  
  Example::
  
      guide = AutoGaussian(model)
      svi = SVI(model, guide, ...)
  
  Example using experimental funsor backend::
  
      !pip install pyro-ppl[funsor]
      guide = AutoGaussian(model, backend="funsor")
      svi = SVI(model, guide, ...)
  
  **References**
  
  [1] S.Webb, A.Goliński, R.Zinkov, N.Siddharth, T.Rainforth, Y.W.Teh, F.Wood (2018)
      "Faithful inversion of generative models for effective amortized inference"
      https://dl.acm.org/doi/10.5555/3327144.3327229
  [2] F.Obermeyer, E.Bingham, M.Jankowiak, J.Chiu, N.Pradhan, A.M.Rush, N.Goodman
      (2019)
      "Tensor Variable Elimination for Plated Factor Graphs"
      http://proceedings.mlr.press/v97/obermeyer19a/obermeyer19a.pdf
  [3] F. Obermeyer, E. Bingham, M. Jankowiak, D. Phan, J. P. Chen
      (2019)
      "Functional Tensors for Probabilistic Programming"
      https://arxiv.org/abs/1910.10775
  
  :param callable model: A Pyro model.
  :param callable init_loc_fn: A per-site initialization function.
      See :ref:`autoguide-initialization` section for available functions.
  :param float init_scale: Initial scale for the standard deviation of each
      (unconstrained transformed) latent variable.
  :param str backend: Back end for performing Gaussian tensor variable
      elimination. Defaults to "dense"; other options include "funsor".
class_docstr_tok: ["Gaussian","guide","with","optimal","conditional","independence","structure",".","This","is","equivalent","to","a","full","rank",":","class",":","`","AutoMultivariateNormal","`","guide",",","but","with","a","sparse","precision","matrix","determined","by","dependencies","and","plates","in","the","model","[","1","]",".","Depending","on","model","structure",",","this","can","have","asymptotically","better","statistical","efficiency","than",":","class",":","`","AutoMultivariateNormal","`",".","This","guide","implements","multiple","backends","for","computation",".","All","backends","use","the","same","statistically","optimal","parametrization",".","The","default","``","dense","''","backend","has","computational","complexity","similar","to",":","class",":","`","AutoMultivariateNormal","`",".","The","experimental","``","funsor","''","backend","can","be","asymptotically","cheaper","in","terms","of","time","and","space","(","using","Gaussian","tensor","variable","elimination","[","2,3","]",")",",","but","incurs","large","constant","overhead",".","The","``","funsor","''","backend","requires","`","funsor","<","https",":","\/\/funsor.pyro.ai",">","`","_","which","can","be","installed","via","``","pip","install","pyro-ppl","[","funsor","]","``",".","The","guide","currently","does","not","depend","on","the","model","'s","``","*","args",",","*","*","kwargs","``",".","Example",":",":","guide","=","AutoGaussian","(","model",")","svi","=","SVI","(","model",",","guide",",","...",")","Example","using","experimental","funsor","backend",":",":","!","pip","install","pyro-ppl","[","funsor","]","guide","=","AutoGaussian","(","model",",","backend=","''","funsor","''",")","svi","=","SVI","(","model",",","guide",",","...",")","*","*","References","*","*","[","1","]","S.Webb",",","A.Goli\u0144ski",",","R.Zinkov",",","N.Siddharth",",","T.Rainforth",",","Y.W.Teh",",","F.Wood","(","2018",")","``","Faithful","inversion","of","generative","models","for","effective","amortized","inference","''","https",":","\/\/dl.acm.org\/doi\/10.5555\/3327144.3327229","[","2","]","F.Obermeyer",",","E.Bingham",",","M.Jankowiak",",","J.Chiu",",","N.Pradhan",",","A.M.Rush",",","N.Goodman","(","2019",")","``","Tensor","Variable","Elimination","for","Plated","Factor","Graphs","''","http",":","\/\/proceedings.mlr.press\/v97\/obermeyer19a\/obermeyer19a.pdf","[","3","]","F.","Obermeyer",",","E.","Bingham",",","M.","Jankowiak",",","D.","Phan",",","J.","P.","Chen","(","2019",")","``","Functional","Tensors","for","Probabilistic","Programming","''","https",":","\/\/arxiv.org\/abs\/1910.10775",":","param","callable","model",":","A","Pyro","model",".",":","param","callable","init_loc_fn",":","A","per-site","initialization","function",".","See",":","ref",":","`","autoguide-initialization","`","section","for","available","functions",".",":","param","float","init_scale",":","Initial","scale","for","the","standard","deviation","of","each","(","unconstrained","transformed",")","latent","variable",".",":","param","str","backend",":","Back","end","for","performing","Gaussian","tensor","variable","elimination",".","Defaults","to","``","dense","''",";","other","options","include","``","funsor","''","."]
code: def _setup_prototype(self, *args, **kwargs) -> None:
      super()._setup_prototype(*args, **kwargs)
  
      self.locs = PyroModule()
      self.scales = PyroModule()
      self.white_vecs = PyroModule()
      self.prec_sqrts = PyroModule()
      self._factors = OrderedDict()
      self._plates = OrderedDict()
      self._event_numel = OrderedDict()
      self._unconstrained_event_shapes = OrderedDict()
  
      # Trace model dependencies.
      model = self._original_model[0]
      self._original_model = None
      self.dependencies = poutine.block(get_dependencies)(
          model, args, kwargs
      )["prior_dependencies"]
  
      # Eliminate observations with no upstream latents.
      for d, upstreams in list(self.dependencies.items()):
          if all(
              self.prototype_trace.nodes[u]["is_observed"]
              for u in upstreams
          ):
              del self.dependencies[d]
              del self.prototype_trace.nodes[d]
  
      # Collect factors and plates.
      for d, site in self.prototype_trace.nodes.items():
          # Prune non-essential parts of the trace to save memory.
          pruned_site, site = site, site.copy()
          pruned_site.clear()
  
          # Collect factors and plates.
          if site["type"] != "sample" or site_is_subsample(site):
              continue
          assert all(f.vectorized for f in site["cond_indep_stack"])
          self._factors[d] = self._compress_site(site)
          plates = frozenset(site["cond_indep_stack"])
          if site["fn"].batch_shape != _plates_to_shape(plates):
              raise ValueError(
                  f"Shape mismatch at site '{d}'. "
                  "Are you missing a pyro.plate() or .to_event()?"
              )
          if site["is_observed"]:
              # Break irrelevant observation plates.
              plates &= frozenset().union(
                  *(
                      self._plates[u]
                      for u in self.dependencies[d]
                      if u != d
                  )
              )
          self._plates[d] = plates
  
          # Create location-scale parameters, one per latent variable.
          if site["is_observed"]:
              # This may slightly overestimate, e.g. for Multinomial.
              self._event_numel[d] = site["fn"].event_shape.numel()
              # Account for broken irrelevant observation plates.
              for f in set(site["cond_indep_stack"]) - plates:
                  self._event_numel[d] *= f.size
              continue
          with helpful_support_errors(site):
              init_loc = (
                  biject_to(site["fn"].support)
                  .inv(site["value"])
                  .detach()
              )
          batch_shape = site["fn"].batch_shape
          event_shape = init_loc.shape[len(batch_shape) :]
          self._unconstrained_event_shapes[d] = event_shape
          self._event_numel[d] = event_shape.numel()
          event_dim = len(event_shape)
          deep_setattr(
              self.locs, d, PyroParam(init_loc, event_dim=event_dim)
          )
          deep_setattr(
              self.scales,
              d,
              PyroParam(
                  torch.full_like(init_loc, self._init_scale),
                  constraint=self.scale_constraint,
                  event_dim=event_dim,
              ),
          )
  
      # Create parameters for dependencies, one per factor.
      for d, site in self._factors.items():
          u_size = 0
          for u in self.dependencies[d]:
              if not self._factors[u]["is_observed"]:
                  broken_shape = _plates_to_shape(
                      self._plates[u] - self._plates[d]
                  )
                  u_size += broken_shape.numel() * self._event_numel[u]
          d_size = self._event_numel[d]
          if site["is_observed"]:
              d_size = min(d_size, u_size)  # just an optimization
          batch_shape = _plates_to_shape(self._plates[d])
  
          # Create parameters of each Gaussian factor.
          white_vec = init_loc.new_zeros(batch_shape + (d_size,))
          # We initialize with noise to avoid singular gradient.
          prec_sqrt = torch.rand(
              batch_shape + (u_size, d_size),
              dtype=init_loc.dtype,
              device=init_loc.device,
          )
          prec_sqrt.sub_(0.5).mul_(self._init_scale)
          if not site["is_observed"]:
              # Initialize the [d,d] block to the identity matrix.
              prec_sqrt.diagonal(dim1=-2, dim2=-1).fill_(1)
          deep_setattr(
              self.white_vecs, d, PyroParam(white_vec, event_dim=1)
          )
          deep_setattr(
              self.prec_sqrts, d, PyroParam(prec_sqrt, event_dim=2)
          )
  
imports: import itertools
  from abc import ABCMeta, abstractmethod
  from collections import OrderedDict, defaultdict
  from contextlib import ExitStack
  from types import SimpleNamespace
  from typing import Callable, Dict, Optional, Set, Tuple, Union
  import torch
  from torch.distributions import biject_to
  import pyro
  import pyro.distributions
  import pyro.poutine
  from pyro.distributions import constraints
  from pyro.infer.inspect import get_dependencies, is_sample_site
  from pyro.nn.module import PyroModule, PyroParam
  from pyro.ops.linalg import ignore_torch_deprecation_warnings
  from pyro.poutine.runtime import am_i_wrapped, get_plates
  from pyro.poutine.util import site_is_subsample
  from .guides import AutoGuide
  from .initialization import InitMessenger, init_to_feasible
  from .utils import deep_getattr, deep_setattr, helpful_support_errors
label_desc: Use image node_id 3 for calling the AutoGaussian obj's underlying member method code with example usage: obj._setup_prototype() without return types