Repository: pyro-ppl/pyro

Issue:
Warning when adding priors that do not match anything [feature request]
Would be a nice feature if you got an error (or at least were warned) when setting a prior that does not match anything through `pyro.random_module`. 

        network_dist = pyro.random_module('network', network, prior={
            'linaer.weight': dist.Normal(...),
            ...
        })


Discussion:

Resolving patch:
diff --git a/pyro/poutine/lift_messenger.py b/pyro/poutine/lift_messenger.py
--- a/pyro/poutine/lift_messenger.py
+++ b/pyro/poutine/lift_messenger.py
@@ -1,7 +1,10 @@
 from __future__ import absolute_import, division, print_function
 
+import warnings
+
 from pyro import params
 from pyro.distributions import Distribution
+from pyro.poutine.util import is_validation_enabled
 
 from .messenger import Messenger
 
@@ -26,6 +29,22 @@ def __init__(self, prior):
         super(LiftMessenger, self).__init__()
         self.prior = prior
 
+    def __enter__(self):
+        if is_validation_enabled() and isinstance(self.prior, dict):
+            self._param_hits = set()
+            self._param_misses = set()
+        return super(LiftMessenger, self).__enter__()
+
+    def __exit__(self, *args, **kwargs):
+        if is_validation_enabled() and isinstance(self.prior, dict):
+            extra = set(self.prior) - self._param_hits
+            if extra:
+                warnings.warn(
+                    "pyro.module prior did not find params ['{}']. "
+                    "Did you instead mean one of ['{}']?"
+                    .format("', '".join(extra), "', '".join(self._param_misses)))
+        return super(LiftMessenger, self).__exit__(*args, **kwargs)
+
     def _pyro_sample(self, msg):
         return None
 
@@ -47,7 +66,11 @@ def _pyro_param(self, msg):
                     msg["args"] = ()
                     msg["kwargs"] = {}
                     msg["infer"] = {}
+                if is_validation_enabled():
+                    self._param_hits.add(param_name)
             else:
+                if is_validation_enabled():
+                    self._param_misses.add(param_name)
                 return None
         elif isinstance(self.prior, Distribution):
             # prior is a distribution
diff --git a/pyro/primitives.py b/pyro/primitives.py
--- a/pyro/primitives.py
+++ b/pyro/primitives.py
@@ -455,6 +455,7 @@ def enable_validation(is_validate=True):
     """
     dist.enable_validation(is_validate)
     infer.enable_validation(is_validate)
+    poutine.enable_validation(is_validate)
 
 
 @contextmanager


Test patch:
diff --git a/tests/poutine/test_poutines.py b/tests/poutine/test_poutines.py
--- a/tests/poutine/test_poutines.py
+++ b/tests/poutine/test_poutines.py
@@ -1,6 +1,8 @@
 from __future__ import absolute_import, division, print_function
 
 import functools
+import logging
+import warnings
 from unittest import TestCase
 
 import pytest
@@ -16,6 +18,8 @@
 from pyro.poutine.util import all_escape, discrete_escape
 from tests.common import assert_equal
 
+logger = logging.getLogger(__name__)
+
 
 def eq(x, y, prec=1e-10):
     return (torch.norm(x - y).item() < prec)
@@ -369,12 +373,24 @@ def test_unlifted_param(self):
 
     def test_random_module(self):
         pyro.clear_param_store()
-        lifted_tr = poutine.trace(pyro.random_module("name", self.model, prior=self.prior)).get_trace()
+        with pyro.validation_enabled():
+            lifted_tr = poutine.trace(pyro.random_module("name", self.model, prior=self.prior)).get_trace()
         for name in lifted_tr.nodes.keys():
             if lifted_tr.nodes[name]["type"] == "param":
                 assert lifted_tr.nodes[name]["type"] == "sample"
                 assert not lifted_tr.nodes[name]["is_observed"]
 
+    def test_random_module_warn(self):
+        pyro.clear_param_store()
+        bad_prior = {'foo': None}
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            with pyro.validation_enabled():
+                poutine.trace(pyro.random_module("name", self.model, prior=bad_prior)).get_trace()
+            assert len(w), 'No warnings were raised'
+            for warning in w:
+                logger.info(warning)
+
     def test_random_module_prior_dict(self):
         pyro.clear_param_store()
         lifted_nn = pyro.random_module("name", self.model, prior=self.nn_prior)