home edit +

Modeling Sequential Actions with Multiple Agents

We’re interested in modeling data generated by multiple users, so let’s extend our sequential planning setup to a setup where multiple users contribute actions to a partially shared goal. We won’t model agents thinking about each other (for now).

///fold:
var call = function(f, a, b, c, d) {
  return f(a, b, c, d);
}

var moveFunctions = {
  push: function(state, s) {
    return state.concat([s]);
  },
  pop: function(state) {
    return state.slice(1);
  },
  swap: function(state, i) {
    var a = state[i];
    var b = state[i + 1];
    return state.slice(0, i).concat([b, a]).concat(state.slice(i + 2));    
  }
};

var randomSymbol = function() {
  return uniformDraw(['a', 'b', 'c', 'd', 'e']);
};

var samplePush = function(state) {
  return {
    name: 'push',
    data: randomSymbol()
  }
};

var samplePop = function(state) {
  return {
    name: 'pop',
    data: null
  }
};

var sampleSwap = function(state) {
  return {
    name: 'swap',
    data: randomInteger(state.length - 1)
  }
};

var availableMoves = function(state) {
  if (state.length >= 2) {
    return [samplePush, samplePop, sampleSwap];
  } else if (state.length >= 1) {
    return [samplePush, samplePop];
  } else {
    return [samplePush];
  }
};

var sampleMove = function(state) {
  var sampler = uniformDraw(availableMoves(state));
  return sampler(state);
};

var applyMove = function(state, move) {
  var moveFunc = moveFunctions[move.name];
  return moveFunc(state, move.data);
};

var applyMoves = function(state, moves) {
  if (moves.length === 0) {
    return state;
  } else {
    return applyMoves(applyMove(state, moves[0]), moves.slice(1));
  }
};

var sampleMoves = function(state, n) {
  if (n === 0) {
    return [];
  } else {
    var move = sampleMove(state);
    var newState = applyMove(state, move);
    return [move].concat(sampleMoves(newState, n - 1));
  }
};

var showMove = function(state, move, prefix) {
  var nextState = applyMove(state, move);
  print(
    (prefix || "") +
    JSON.stringify(state) + 
    " --{" + move.name + 
    ((move.data !== null) ? " " + move.data : "") + "}--> " + 
    JSON.stringify(nextState));
}

var showMoves = function(state, moves) {
  if (moves.length === 0) {
    return;
  } else {    
    var move = moves[0];
    showMove(state, move);
    var nextState = applyMove(state, move);
    return showMoves(nextState, moves.slice(1));
  }
};

wpEditor.put('sampleMoves', sampleMoves);
wpEditor.put('applyMoves', applyMoves);
wpEditor.put('showMoves', showMoves);
///

var targetState = ['a', 'b', 'c', 'd'];

var agents = [
  { 
    name: 'random agent', 
    move: function(state){ 
      return sampleMove(state);
    },
    probability: .3
  }, 
  { 
    name: 'goal-directed agent', 
    move: function(state) {
      var n = Math.abs(targetState.length) * 2;
      var moves = Infer({method: 'rejection'}, function() {
        var i = randomInteger(n * 2);
        var moves = sampleMoves(state, i);
        var finalState = applyMoves(state, moves);
        condition(_.isEqual(finalState, targetState));
        return moves;
      }).support()[0];
      return moves[0];
    },
    probability: .7
  }
];

var sampleAgent = function() {
  // var ps = _.map(agents, 'probability');
  // return agents[discrete(ps)]; -- `discrete` is currently broken
  return uniformDraw(agents);
};

var generateMoves = function(state, n) {
  if (n === 0) {
    return [];
  } else {
    var agent = sampleAgent();
    print(agent.name + " acting");
    var move = call(agent.move, state);
    var nextState = applyMove(state, move)
    var datum = {
      agent: agent,
      move: move
    };
    return [datum].concat(generateMoves(nextState, n - 1));
  }
};

var showTrace = function(state, trace) {
  if (trace.length === 0) {
    return;
  } else {
    var t = trace[0];
    showMove(state, t.move, t.agent.name + ": ");
    var nextState = applyMove(state, t.move);
    return showTrace(nextState, trace.slice(1));
  }
};

var trace = generateMoves([], 20);

showTrace([], trace);

Our goal-directed agent isn’t very goal directed yet, though—it doesn’t attempt to minimize the number of moves, which results in a lot of random moves, even from the goal-directed agent.