webppl icon indicating copy to clipboard operation
webppl copied to clipboard

default score function treats objects as ordered

Open hawkrobe opened this issue 9 years ago • 3 comments

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.

hawkrobe avatar May 28 '15 19:05 hawkrobe

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.

iffsid avatar May 28 '15 22:05 iffsid

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

stuhlmueller avatar Mar 03 '16 21:03 stuhlmueller

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})

mhtess avatar Mar 14 '19 15:03 mhtess