probmods / webppl

Probabilistic programming for the web
http://webppl.org
Other
613 stars 89 forks source link

semantics of mem is questionable #896

Open ngoodman opened 6 years ago

ngoodman commented 6 years ago

i think there may be a bug, or at least questionable choice in the semantics of mem. consider:

Infer({method: 'rejection', samples: 100}, function() {
  var memfn = mem(function(buttons) {return uniform(0,1)})
//   memfn('a');

  var inner = Infer({method: 'rejection', samples: 100}, function() {
    var p = memfn('a')
    condition(flip(p))
    return flip(p)
  })

  return sample(inner)
})

different results are obtained if the memfn('a'); line is uncommented. it does not seem to me like this should happen.

the semantics we converged on for mem in Church was that all of the random sampling (for all inputs to the function) was to be considered as happening where the memoized function was created, even if the sampling was deferred in practice. in webppl it seems that we are breaking this convention, at least when a memoized function crosses an Infer boundary.

(this bug came to light in https://github.com/probmods/probmods2/pull/56)

daphnab commented 6 years ago

This issue I reported on the probmods page may also be relevant:

https://github.com/probmods/probmods2/issues/55

my workaround using cache suggests that the semantics for cache are perhaps also not quite working as intended (the documentation suggests that cache should be fully deterministic, but current functioning appears to be consistent with Noah's description of the Church mem semantics above).

null-a commented 6 years ago

I don't have any good ideas about how to fix this at the moment, but here are a few thoughts on earlier comments:

in webppl it seems that we are breaking this convention, at least when a memoized function crosses an Infer boundary.

@ngoodman is right about the significance if the Infer boundary. This arises because mem is implemented by memoizing the function using WebPPL's global store as a cache. I think this is fine for the case of a single Infer, but in the nested example the result of calling memfn('a') is not automatically shared across executions of the inner model, because Infer arranges for each such execution to have a fresh store.

Adding the call to memfn('a') in the outer model adds the result to the cache before entering Infer, which means that all executions of the inner model see it. (Since each execution of a model by Infer sees the store as it was when Infer was called.)

(the documentation suggests that cache should be fully deterministic, but current functioning appears to be consistent with Noah's description of the Church mem semantics above).

@daphnab It seems there may be some confusion here. The docs are trying to say that cache should only be applied to a deterministic function. If cache is applied to a stochastic function, all bets are off. For example, the use of cache in this model causes exhaustive enumeration to produce different results depending on the strategy used:

var model = function() {
  var f = cache(function() { return flip(); })
  return [f('a'), f('b')].join(',')
}
Infer({model, method: 'enumerate', strategy: 'depthFirst'})

// Depth first:
// Marginal:
//    "true,true" : 0.5
//    "false,false" : 0.25
//    "false,true" : 0.25

// Breadth first:
// Marginal:
//     "true,true" : 0.25
//     "true,false" : 0.25
//     "false,true" : 0.25
//     "false,false" : 0.25

If cache is replaced with mem in the above, then all strategies return the expected distribution.

I imagine that cache only appears to have the desired mem semantics for algorithms that always re-run the entire model, and that when the algorithm uses fancier control flow that will break down.

daphnab commented 6 years ago

@null-a thanks for the clarification about cache! In that case, is there currently a way to make an embedded non-parametric inference (as in the original inference about inference chapter examples)? The current work around in the chapter solves the issue by evaluating the a priori likeliest values outside the inner inference (so it's technically not quite correct, since a priori low probability sequences will still not have consistent probabilities assigned to them). My only other solution was to implement my own rejection sampling rather than using Infer but that's obviously also quite limited.

null-a commented 6 years ago

is there currently a way to make an embedded non-parametric inference

@daphnab I don't have anything better than your solution at the moment I'm afraid.

ngoodman commented 6 years ago

i worked around this in ch6 of probmods, by getting rid of mem.

at some point we should find a solution, though this is a pretty unused corner case....