prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  make test: no attribute 'optim' error in 'examples/contrib/oed/ab_test.py' 
  ### Issue Description
  On the latest dev branch, `make test` gives the following error:
  
  _
  examples/contrib/oed/ab_test.py:12: in <module>
      from gp_bayes_opt import GPBayesOptimizer
  examples/contrib/oed/gp_bayes_opt.py:11: in <module>
      class GPBayesOptimizer(pyro.optim.multi.MultiOptimizer):
  E   AttributeError: module 'pyro' has no attribute 'optim'
  
  ### Environment
  For any bugs, please provide the following:
   - OS and python version: CentOS Linux 7 (Core); Python 3.7.1
   - PyTorch version, or if relevant, output of `pip freeze`: PyTorch 1.0.0
   - Pyro version: output of `python -c 'import pyro; print pyro.__version__'`: pyro 0.3.0+9adbdb7
  
  ### Code Snippet
  
  ```
  make install
  make format
  make test
  ```
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `examples/contrib/oed/gp_bayes_opt.py`
  Content:
  ```
  1 import torch
  2 import torch.autograd as autograd
  3 import torch.optim as optim
  4 from torch.distributions import transform_to
  5 
  6 import pyro
  7 import pyro.contrib.gp as gp
  8 from pyro.infer import TraceEnum_ELBO
  9 
  10 
  11 class GPBayesOptimizer(pyro.optim.multi.MultiOptimizer):
  12     """Performs Bayesian Optimization using a Gaussian Process as an
  13     emulator for the unknown function.
  14     """
  15 
  16     def __init__(self, constraints, gpmodel, num_acquisitions, acquisition_func=None):
  17         """
  18         :param torch.constraint constraints: constraints defining the domain of `f`
  19         :param gp.models.GPRegression gpmodel: a (possibly initialized) GP
  20             regression model. The kernel, etc is specified via `gpmodel`.
  21         :param int num_acquisitions: number of points to acquire at each step
  22         :param function acquisition_func: a function to generate acquisitions.
  23             It should return a torch.Tensor of new points to query.
  24         """
  25         if acquisition_func is None:
  26             acquisition_func = self.acquire_thompson
  27 
  28         self.constraints = constraints
  29         self.gpmodel = gpmodel
  30         self.num_acquisitions = num_acquisitions
  31         self.acquisition_func = acquisition_func
  32 
  33     def update_posterior(self, X, y):
  34         X = torch.cat([self.gpmodel.X, X])
  35         y = torch.cat([self.gpmodel.y, y])
  36         self.gpmodel.set_data(X, y)
  37         optimizer = torch.optim.Adam(self.gpmodel.parameters(), lr=0.001)
  38         gp.util.train(self.gpmodel, optimizer,
  39                       loss_fn=TraceEnum_ELBO(strict_enumeration_warning=False).differentiable_loss,
  40                       retain_graph=True)
  41 
  42     def find_a_candidate(self, differentiable, x_init):
  43         """Given a starting point, `x_init`, takes one LBFGS step
  44         to optimize the differentiable function.
  45 
  46         :param function differentiable: a function amenable to torch
  47             autograd
  48         :param torch.Tensor x_init: the initial point
  49 
  50         """
  51         # transform x to an unconstrained domain
  52         unconstrained_x_init = transform_to(self.constraints).inv(x_init)
  53         unconstrained_x = unconstrained_x_init.detach().clone().requires_grad_(True)
  54         # TODO: Use LBFGS with line search by pytorch #8824 merged
  55         minimizer = optim.LBFGS([unconstrained_x], max_eval=20)
  56 
  57         def closure():
  58             minimizer.zero_grad()
  59             if (torch.log(torch.abs(unconstrained_x)) > 25.).any():
  60                 return torch.tensor(float('inf'))
  61             x = transform_to(self.constraints)(unconstrained_x)
  62             y = differentiable(x)
  63             autograd.backward(unconstrained_x,
  64                               autograd.grad(y, unconstrained_x, retain_graph=True))
  65             return y
  66 
  67         minimizer.step(closure)
  68         # after finding a candidate in the unconstrained domain,
  69         # convert it back to original domain.
  70         x = transform_to(self.constraints)(unconstrained_x)
  71         opt_y = differentiable(x)
  72         return x.detach(), opt_y.detach()
  73 
  74     def opt_differentiable(self, differentiable, num_candidates=5):
  75         """Optimizes a differentiable function by choosing `num_candidates`
  76         initial points at random and calling :func:`find_a_candidate` on
  77         each. The best candidate is returned with its function value.
  78 
  79         :param function differentiable: a function amenable to torch autograd
  80         :param int num_candidates: the number of random starting points to
  81             use
  82         :return: the minimiser and its function value
  83         :rtype: tuple
  84         """
  85 
  86         candidates = []
  87         values = []
  88         for j in range(num_candidates):
  89             x_init = self.gpmodel.X.new_empty(1).uniform_(
  90                 self.constraints.lower_bound, self.constraints.upper_bound)
  91             x, y = self.find_a_candidate(differentiable, x_init)
  92             if torch.isnan(y):
  93                 continue
  94             candidates.append(x)
  95             values.append(y)
  96 
  97         mvalue, argmin = torch.min(torch.cat(values), dim=0)
  98         return candidates[argmin.item()], mvalue
  99 
  100     def acquire_thompson(self, num_acquisitions=1, **opt_params):
  101         """Selects `num_acquisitions` query points at which to query the
  102         original function by Thompson sampling.
  103 
  104         :param int num_acquisitions: the number of points to generate
  105         :param dict opt_params: additional parameters for optimization
  106             routines
  107         :return: a tensor of points to evaluate `loss` at
  108         :rtype: torch.Tensor
  109         """
  110 
  111         # Initialize the return tensor
  112         X = self.gpmodel.X.new_empty(num_acquisitions, *self.gpmodel.X.shape[1:])
  113 
  114         for i in range(num_acquisitions):
  115             sampler = self.gpmodel.iter_sample(noiseless=False)
  116             x, _ = self.opt_differentiable(sampler, **opt_params)
  117             X[i, ...] = x
  118 
  119         return X
  120 
  121     def get_step(self, loss, params, verbose=False):
  122         X = self.acquisition_func(num_acquisitions=self.num_acquisitions)
  123         y = loss(X)
  124         if verbose:
  125             print("Acquire at: X")
  126             print(X)
  127             print("y")
  128             print(y)
  129         self.update_posterior(X, y)
  130         return self.opt_differentiable(lambda x: self.gpmodel(x)[0])
  131
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/examples/contrib/oed/gp_bayes_opt.py b/examples/contrib/oed/gp_bayes_opt.py
  --- a/examples/contrib/oed/gp_bayes_opt.py
  +++ b/examples/contrib/oed/gp_bayes_opt.py
  @@ -3,9 +3,9 @@
   import torch.optim as optim
   from torch.distributions import transform_to
   
  -import pyro
   import pyro.contrib.gp as gp
   from pyro.infer import TraceEnum_ELBO
  +import pyro.optim
   
   
   class GPBayesOptimizer(pyro.optim.multi.MultiOptimizer):
  
verification_info: {"golden_diff": "diff --git a/examples/contrib/oed/gp_bayes_opt.py b/examples/contrib/oed/gp_bayes_opt.py\n--- a/examples/contrib/oed/gp_bayes_opt.py\n+++ b/examples/contrib/oed/gp_bayes_opt.py\n@@ -3,9 +3,9 @@\n import torch.optim as optim\n from torch.distributions import transform_to\n \n-import pyro\n import pyro.contrib.gp as gp\n from pyro.infer import TraceEnum_ELBO\n+import pyro.optim\n \n \n class GPBayesOptimizer(pyro.optim.multi.MultiOptimizer):\n", "issue": "make test: no attribute 'optim' error in 'examples/contrib/oed/ab_test.py' \n### Issue Description\r\nOn the latest dev branch, `make test` gives the following error:\r\n\r\n_\r\nexamples/contrib/oed/ab_test.py:12: in <module>\r\n    from gp_bayes_opt import GPBayesOptimizer\r\nexamples/contrib/oed/gp_bayes_opt.py:11: in <module>\r\n    class GPBayesOptimizer(pyro.optim.multi.MultiOptimizer):\r\nE   AttributeError: module 'pyro' has no attribute 'optim'\r\n\r\n### Environment\r\nFor any bugs, please provide the following:\r\n - OS and python version: CentOS Linux 7 (Core); Python 3.7.1\r\n - PyTorch version, or if relevant, output of `pip freeze`: PyTorch 1.0.0\r\n - Pyro version: output of `python -c 'import pyro; print pyro.__version__'`: pyro 0.3.0+9adbdb7\r\n\r\n### Code Snippet\r\n\r\n```\r\nmake install\r\nmake format\r\nmake test\r\n```\r\n\n", "before_files": [{"content": "import torch\nimport torch.autograd as autograd\nimport torch.optim as optim\nfrom torch.distributions import transform_to\n\nimport pyro\nimport pyro.contrib.gp as gp\nfrom pyro.infer import TraceEnum_ELBO\n\n\nclass GPBayesOptimizer(pyro.optim.multi.MultiOptimizer):\n    \"\"\"Performs Bayesian Optimization using a Gaussian Process as an\n    emulator for the unknown function.\n    \"\"\"\n\n    def __init__(self, constraints, gpmodel, num_acquisitions, acquisition_func=None):\n        \"\"\"\n        :param torch.constraint constraints: constraints defining the domain of `f`\n        :param gp.models.GPRegression gpmodel: a (possibly initialized) GP\n            regression model. The kernel, etc is specified via `gpmodel`.\n        :param int num_acquisitions: number of points to acquire at each step\n        :param function acquisition_func: a function to generate acquisitions.\n            It should return a torch.Tensor of new points to query.\n        \"\"\"\n        if acquisition_func is None:\n            acquisition_func = self.acquire_thompson\n\n        self.constraints = constraints\n        self.gpmodel = gpmodel\n        self.num_acquisitions = num_acquisitions\n        self.acquisition_func = acquisition_func\n\n    def update_posterior(self, X, y):\n        X = torch.cat([self.gpmodel.X, X])\n        y = torch.cat([self.gpmodel.y, y])\n        self.gpmodel.set_data(X, y)\n        optimizer = torch.optim.Adam(self.gpmodel.parameters(), lr=0.001)\n        gp.util.train(self.gpmodel, optimizer,\n                      loss_fn=TraceEnum_ELBO(strict_enumeration_warning=False).differentiable_loss,\n                      retain_graph=True)\n\n    def find_a_candidate(self, differentiable, x_init):\n        \"\"\"Given a starting point, `x_init`, takes one LBFGS step\n        to optimize the differentiable function.\n\n        :param function differentiable: a function amenable to torch\n            autograd\n        :param torch.Tensor x_init: the initial point\n\n        \"\"\"\n        # transform x to an unconstrained domain\n        unconstrained_x_init = transform_to(self.constraints).inv(x_init)\n        unconstrained_x = unconstrained_x_init.detach().clone().requires_grad_(True)\n        # TODO: Use LBFGS with line search by pytorch #8824 merged\n        minimizer = optim.LBFGS([unconstrained_x], max_eval=20)\n\n        def closure():\n            minimizer.zero_grad()\n            if (torch.log(torch.abs(unconstrained_x)) > 25.).any():\n                return torch.tensor(float('inf'))\n            x = transform_to(self.constraints)(unconstrained_x)\n            y = differentiable(x)\n            autograd.backward(unconstrained_x,\n                              autograd.grad(y, unconstrained_x, retain_graph=True))\n            return y\n\n        minimizer.step(closure)\n        # after finding a candidate in the unconstrained domain,\n        # convert it back to original domain.\n        x = transform_to(self.constraints)(unconstrained_x)\n        opt_y = differentiable(x)\n        return x.detach(), opt_y.detach()\n\n    def opt_differentiable(self, differentiable, num_candidates=5):\n        \"\"\"Optimizes a differentiable function by choosing `num_candidates`\n        initial points at random and calling :func:`find_a_candidate` on\n        each. The best candidate is returned with its function value.\n\n        :param function differentiable: a function amenable to torch autograd\n        :param int num_candidates: the number of random starting points to\n            use\n        :return: the minimiser and its function value\n        :rtype: tuple\n        \"\"\"\n\n        candidates = []\n        values = []\n        for j in range(num_candidates):\n            x_init = self.gpmodel.X.new_empty(1).uniform_(\n                self.constraints.lower_bound, self.constraints.upper_bound)\n            x, y = self.find_a_candidate(differentiable, x_init)\n            if torch.isnan(y):\n                continue\n            candidates.append(x)\n            values.append(y)\n\n        mvalue, argmin = torch.min(torch.cat(values), dim=0)\n        return candidates[argmin.item()], mvalue\n\n    def acquire_thompson(self, num_acquisitions=1, **opt_params):\n        \"\"\"Selects `num_acquisitions` query points at which to query the\n        original function by Thompson sampling.\n\n        :param int num_acquisitions: the number of points to generate\n        :param dict opt_params: additional parameters for optimization\n            routines\n        :return: a tensor of points to evaluate `loss` at\n        :rtype: torch.Tensor\n        \"\"\"\n\n        # Initialize the return tensor\n        X = self.gpmodel.X.new_empty(num_acquisitions, *self.gpmodel.X.shape[1:])\n\n        for i in range(num_acquisitions):\n            sampler = self.gpmodel.iter_sample(noiseless=False)\n            x, _ = self.opt_differentiable(sampler, **opt_params)\n            X[i, ...] = x\n\n        return X\n\n    def get_step(self, loss, params, verbose=False):\n        X = self.acquisition_func(num_acquisitions=self.num_acquisitions)\n        y = loss(X)\n        if verbose:\n            print(\"Acquire at: X\")\n            print(X)\n            print(\"y\")\n            print(y)\n        self.update_posterior(X, y)\n        return self.opt_differentiable(lambda x: self.gpmodel(x)[0])\n", "path": "examples/contrib/oed/gp_bayes_opt.py"}], "after_files": [{"content": "import torch\nimport torch.autograd as autograd\nimport torch.optim as optim\nfrom torch.distributions import transform_to\n\nimport pyro.contrib.gp as gp\nfrom pyro.infer import TraceEnum_ELBO\nimport pyro.optim\n\n\nclass GPBayesOptimizer(pyro.optim.multi.MultiOptimizer):\n    \"\"\"Performs Bayesian Optimization using a Gaussian Process as an\n    emulator for the unknown function.\n    \"\"\"\n\n    def __init__(self, constraints, gpmodel, num_acquisitions, acquisition_func=None):\n        \"\"\"\n        :param torch.constraint constraints: constraints defining the domain of `f`\n        :param gp.models.GPRegression gpmodel: a (possibly initialized) GP\n            regression model. The kernel, etc is specified via `gpmodel`.\n        :param int num_acquisitions: number of points to acquire at each step\n        :param function acquisition_func: a function to generate acquisitions.\n            It should return a torch.Tensor of new points to query.\n        \"\"\"\n        if acquisition_func is None:\n            acquisition_func = self.acquire_thompson\n\n        self.constraints = constraints\n        self.gpmodel = gpmodel\n        self.num_acquisitions = num_acquisitions\n        self.acquisition_func = acquisition_func\n\n    def update_posterior(self, X, y):\n        X = torch.cat([self.gpmodel.X, X])\n        y = torch.cat([self.gpmodel.y, y])\n        self.gpmodel.set_data(X, y)\n        optimizer = torch.optim.Adam(self.gpmodel.parameters(), lr=0.001)\n        gp.util.train(self.gpmodel, optimizer,\n                      loss_fn=TraceEnum_ELBO(strict_enumeration_warning=False).differentiable_loss,\n                      retain_graph=True)\n\n    def find_a_candidate(self, differentiable, x_init):\n        \"\"\"Given a starting point, `x_init`, takes one LBFGS step\n        to optimize the differentiable function.\n\n        :param function differentiable: a function amenable to torch\n            autograd\n        :param torch.Tensor x_init: the initial point\n\n        \"\"\"\n        # transform x to an unconstrained domain\n        unconstrained_x_init = transform_to(self.constraints).inv(x_init)\n        unconstrained_x = unconstrained_x_init.detach().clone().requires_grad_(True)\n        # TODO: Use LBFGS with line search by pytorch #8824 merged\n        minimizer = optim.LBFGS([unconstrained_x], max_eval=20)\n\n        def closure():\n            minimizer.zero_grad()\n            if (torch.log(torch.abs(unconstrained_x)) > 25.).any():\n                return torch.tensor(float('inf'))\n            x = transform_to(self.constraints)(unconstrained_x)\n            y = differentiable(x)\n            autograd.backward(unconstrained_x,\n                              autograd.grad(y, unconstrained_x, retain_graph=True))\n            return y\n\n        minimizer.step(closure)\n        # after finding a candidate in the unconstrained domain,\n        # convert it back to original domain.\n        x = transform_to(self.constraints)(unconstrained_x)\n        opt_y = differentiable(x)\n        return x.detach(), opt_y.detach()\n\n    def opt_differentiable(self, differentiable, num_candidates=5):\n        \"\"\"Optimizes a differentiable function by choosing `num_candidates`\n        initial points at random and calling :func:`find_a_candidate` on\n        each. The best candidate is returned with its function value.\n\n        :param function differentiable: a function amenable to torch autograd\n        :param int num_candidates: the number of random starting points to\n            use\n        :return: the minimiser and its function value\n        :rtype: tuple\n        \"\"\"\n\n        candidates = []\n        values = []\n        for j in range(num_candidates):\n            x_init = self.gpmodel.X.new_empty(1).uniform_(\n                self.constraints.lower_bound, self.constraints.upper_bound)\n            x, y = self.find_a_candidate(differentiable, x_init)\n            if torch.isnan(y):\n                continue\n            candidates.append(x)\n            values.append(y)\n\n        mvalue, argmin = torch.min(torch.cat(values), dim=0)\n        return candidates[argmin.item()], mvalue\n\n    def acquire_thompson(self, num_acquisitions=1, **opt_params):\n        \"\"\"Selects `num_acquisitions` query points at which to query the\n        original function by Thompson sampling.\n\n        :param int num_acquisitions: the number of points to generate\n        :param dict opt_params: additional parameters for optimization\n            routines\n        :return: a tensor of points to evaluate `loss` at\n        :rtype: torch.Tensor\n        \"\"\"\n\n        # Initialize the return tensor\n        X = self.gpmodel.X.new_empty(num_acquisitions, *self.gpmodel.X.shape[1:])\n\n        for i in range(num_acquisitions):\n            sampler = self.gpmodel.iter_sample(noiseless=False)\n            x, _ = self.opt_differentiable(sampler, **opt_params)\n            X[i, ...] = x\n\n        return X\n\n    def get_step(self, loss, params, verbose=False):\n        X = self.acquisition_func(num_acquisitions=self.num_acquisitions)\n        y = loss(X)\n        if verbose:\n            print(\"Acquire at: X\")\n            print(X)\n            print(\"y\")\n            print(y)\n        self.update_posterior(X, y)\n        return self.opt_differentiable(lambda x: self.gpmodel(x)[0])\n", "path": "examples/contrib/oed/gp_bayes_opt.py"}]}