webppl
webppl copied to clipboard
default score function treats objects as ordered
Suppose you use Enumerate to create an erp with objects in its support:
var uniformDraw = function (xs) {
return xs[randomInteger(xs.length)];
};
var myERP = Enumerate(function(){
return uniformDraw([{'a' : 1, 'b' : 2}, {'a' : 2, 'b' : 1}])
})
When you try to look up the score of {'b' : 1, 'a' : 2}
, which is equivalent to {'a' : 2, 'b' : 1}
, the score function treats the object as ordered. Thus, the following returns null when it should return -0.69
console.log(myERP.score([], {'b' : 1, 'a' : 2}))
This happens because we use json.stringify()
to look up the value in makeMarginalERP()
instead of something that will properly match objects.
This is something that bit me as well. The way I got around this is to use the object-hash
package from npm which computes a sha1 (or md5) hash of the object and keys the map with that hash. The reason I haven't pushed that change into dev
is because that hash computation is slow.
One thing that we could do is look at the type, as well as the size where necessary, of the support elements to decide if paying that computation cost will be worth it. Clearly there are cases where this is unavoidable; the only issue is minimizing the impact where it isn't necessary.
Actually, the object-hash
way is a little overkill for this particular case, but it has the added bonus of even handling circular references correctly.
Here's a workaround that can be used in some cases until we have a more general solution:
var toPairs = function(obj){
return _.sortBy(_.pairs(obj));
}
var toObject = function(pairs){
return _.object(pairs);
}
var makeObjectScorer = function(erp) {
var newERP = Enumerate(function(){
return toPairs(erp.sample());
});
return function(value) {
return newERP.score([], toPairs(value));
}
}
// Example:
var myERP = Enumerate(function(){
return uniformDraw(
[{'a' : 1, 'b' : 2},
{'b' : 2, 'a' : 1}]
);
});
var myScoreObject = makeObjectScorer(myERP);
print(Math.exp(myERP.score([], {'a' : 1, 'b' : 2}))); // .5 - wrong
print(Math.exp(myScoreObject({'a' : 1, 'b' : 2}))); // 1 - right
Just wanted to bump this as an issue. Here is my minimal example:
var myDist = Infer({model: function(){
return {a: flip(), b: flip()}
}})
myDist.score({b: true, a: true})