Chrome, JavaScript

Monte Carlo Simulation On Compute Engine/App Script

App script on Ido's project placeThis week in Google Developer Live Israel we show you how to work with Google App script in order to run Monte Carlo simulations and get the results in a nice informative way inside google spreadsheets. We took it one step farther, and explained how to run these script on your own instance machine inside Compute Engine with nodeJS. This is powerful option because with the ability to ‘try’ quickly on App Script and than take the code to ‘production’ (=more efficient way) on Compute engine give you more productivity.

So what we are doing? First let’s touch on the definition. This is a problem solving technique used to approximate the probability of certain outcomes by running multiple trial runs, called simulations, using random input variables. The cool aspect is that we are steering the scenarios by using randomness. The randomness helps us ‘try’ each future scenario in a way that includes our probability per variable. This gives us the ability to fine tune our trial runs to answer a question with several variables. In simple words, in our case, it will help us find the best way to spend the time of our sales person.

 Monte Carlo simulations Steps

1. Define a model with a domain of possible inputs.
2. Generate inputs values randomly, but sampling from a probability distribution over the domain.
3. Perform a deterministic computation on the inputs.
4. Aggregate the results.

Our Case

What should our sales friend do in the next 50 days? She can only do 25 big deals that take 2 days per deal or run on 50 small deals that take one day per deal? What is the best blend of big and small deals for her skill sets? We make few assumptions (of course) like:

  • The probability for closing a big deal – Let’s say 30% chance.
  • The probability for closing a small deal – Let’s say 50% chance.
  • What is the value of each deal. For example: $25K for big deal and $10K for small one.
  • The amounts of deals that she can do in X days. For example: a big deal will take two days and a small deal only one day. So if we have 10 days we can do a maximum of five big deals or a maximum of 10 small deals.

All these parameters will drive the results, so you wish to base them as close as possible to reality. The next step, after we define our possible inputs for the problem is to generate inputs randomly and measure the results. In our case, we printed the results back on google sheet and use the charts in order to have a better look on them.

The main usage for App Script in this case is to have a quick ‘look and feel’ for our data. Once, we are happy with this initial phase and we are ready to test 10K (or 10B) scenarios it’s time for us to move our code to Compute Engine. Here we will have Google’s scale under our hands with the ability to simulate our ‘problems’ on lots of machines without any worries about ‘administrative tasks’ like creating images of our virtual machines and deploying them on 10K machines.

Screen Shot 2013-07-11 at 2.43.33 PMHere is the code that will run this simulation on Node.js inside Compute Engine.


/*************************************************
* This is a simple Monte Carlo simulation to see
* whether we should
* go for X BIG deal and/or Y SMALL deals.
*
* What is the best blend?
*
* Author: Ido Green | plus.google.com/+greenido
* Date: 16 July 2013
*
* **********************************************/
// fun with files – so we could have CSV with our results
fs = require("fs");
// Few Important assumptions that drive the outcome
//
// Number of times to run the simulation
var senarios = 1000;
// % chance of making big deal
var bigDealPerc = 0.3;
// % chance of making a small deal
var smallDealPerc = 0.5;
// Amount of deal const. you can make in X days.
// We gave 'big' deal twice the effort of a small deal.
var amountOfDealsExecution = 50;
var amountBigDeals = 25;
var amountSmallDeals = 50;
// values per deal in $
var bigDeal = 27000;
var smallDeal = 15000;
// This is the heart of the simulation
// We run X senarios and see what is the best 'blend' of deals
function findMaxAmount() {
var outData = "Amount,Big wins, Big Losses\n";
var maxAmount = 0;
var maxDeals = 0;
for (var i=0; i < senarios; i++) {
var maxSenario = runSenarioBlending();
console.log(i + ") Amount: " + maxSenario.moneyWeMade +
" Big Deals: " +
(maxSenario.winsBig + maxSenario.lossBig) );
if (maxAmount < maxSenario.moneyWeMade) {
maxAmount = maxSenario.moneyWeMade;
maxDeals = maxSenario.winsBig + maxSenario.lossBig;
}
outData += maxSenario.moneyWeMade + ","
+ maxSenario.winsBig + "," + maxSenario.lossBig + "\n";
}
console.log ("== max amount: " + maxAmount +
" deals: "+ maxDeals);
fs.writeFile("x-decisions-node-results.csv", outData);
}
// Running on all the deals' comninations and find the best blend
function runSenarioBlending() {
var maxSenario = new Object();
var maxAmount = 0;
for (var i=0; i < amountBigDeals; i++) {
var statsObj = runDealsBlending(i+3);
if (statsObj.moneyWeMade > maxAmount) {
maxSenario = statsObj;
maxAmount = statsObj.moneyWeMade;
}
}
return maxSenario;
}
//
//
//
function runDealsBlending(curLine) {
var winsBig = 0;
var lossBig = 0;
var winSmall = 0;
var lossSmall = 0;
var curTrial = 0;
var moneyWeMade = 0;
var retObj = new Object();
for (var b=0; b < curLine2; b++) {
if (winBigDeal()) {
// We won this big deal
moneyWeMade += bigDeal;
winsBig++;
}
else {
lossBig++;
}
}
for (var s=0; s < amountSmallDeals &&
((b*2)+s) < amountOfDealsExecution; s++) {
if (winSmallDeal()) {
// won small deal
moneyWeMade += smallDeal;
winSmall++;
}
else {
lossSmall++;
}
}
retObj.winsBig = winsBig;
retObj.winSmall = winSmall;
retObj.lossBig = lossBig;
retObj.lossSmall = lossSmall;
retObj.moneyWeMade = moneyWeMade;
//console.log(winsBig + " , " + winSmall + " , " + lossBig + " , " + lossSmall + " , "+ moneyWeMade + " \n");
return retObj;
};
// determines if we win in a given trial when we aim for BIG deal
function winBigDeal() {
if (Math.random() < bigDealPerc) {
return true;
}
return false;
};
// determines if we win in a given trials when we aim for small deal
function winSmallDeal() {
if (Math.random() < smallDealPerc) {
return true;
}
return false;
};
/*****************************
*
* Start the Simulation
*
******************************/
var startTime = Date.now();
findMaxAmount();
var endTime = Date.now();
console.log("\n===== Took us: " + (endTime startTime) + " milliseconds");

Advertisement
Standard

3 thoughts on “Monte Carlo Simulation On Compute Engine/App Script

  1. Michael Anguiano says:

    Ido: Great presentation on Monte Carlo simulations. You have a very cool Google App Script demo at the very beginning of the video. Can you provide that Google App Script code? Thanks.

    • greenido says:
      Sure! .gist table { margin-bottom: 0; } This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /*************************************************************************** * * This is a simple Monte Carlo simulation to see whether our sale * person should execute a strategy of 'many' big deals and 'few' small ones * or vis versa. * * Author: Ido Green | plus.google.com/+greenido * Date: 16 July 2013 * * *************************************************************************/ //Below are the important assumptions that drive the simulation // The % chance of making 'big' deal var bigDealPerc = 0.4; // The % chance of making 'small' deal var smallDealPerc = 0.5; // the max amounts of deals we can do in X days var amountOfDealsExecution = 50; var amountBigDeals = 25; var amountSmallDeals = 50; // values per deal var bigDeal = 30000; var smallDeal = 10000; // // Run on the amount // function runSenarioBlending() { try { var maxAmount = 0; for (var i=0; i < amountBigDeals; i++) { var profit = runDealsBlending(i+3); SpreadsheetApp.flush(); if (profit > maxAmount) { maxAmount = profit; } } var outStr = "The max amount: $" + maxAmount; Browser.msgBox(outStr); Logger.log(outStr); } catch(err) { Logger.log("Error: runSenarioBlending(): " + err); } //return maxAmount; } // // We do X Big Deals and than the rest of Y Small Deals // function runDealsBlending(curLine) { var winsBig = 0; var lossBig = 0; var winSmall = 0; var lossSmall = 0; var curTrial = 0; var moneyWeMade = 0; // Try the X big deals for (var b=0; b < curLine–2; b++) { if (winBigDeal()) { // We got the Big deal! moneyWeMade += bigDeal; winsBig++; } else { lossBig++; } } // Try the rest of the deals on small ones for (var s=0; s < amountSmallDeals && ((b*2)+s) < amountOfDealsExecution; s++) { if (winSmallDeal()) { // We got the Small deal! moneyWeMade += smallDeal; winSmall++; } else { lossSmall++; } } // update our spreadsheet var curSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Deals"); curSheet.getRange("a"+curLine).setValue(winsBig); curSheet.getRange("c"+curLine).setValue(winSmall); curSheet.getRange("b"+curLine).setValue(lossBig); curSheet.getRange("d"+curLine).setValue(lossSmall); curSheet.getRange("e"+curLine).setValue(moneyWeMade); return moneyWeMade; }; // // Determines if we win in a given trial when we aim for BIG deal // function winBigDeal() { if (Math.random() < bigDealPerc) { // We won big deal return true; } return false; }; // // Determines if we win in a given trials when we aim for small deal // function winSmallDeal() { if (Math.random() < smallDealPerc) { // We won small deal return true; } return false; }; function onOpen() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var menuEntries = []; // When the user selects "addMenuExample" menu, and clicks "Menu Entry 1", the function function1 is executed. menuEntries.push({name: "Run One Deal Simulation", functionName: "runDealSim"}); menuEntries.push({name: "Run Many Simulations", functionName: "runSenario"}); menuEntries.push({name: "Run Many Control Simulations", functionName: "runSenarioBlending"}); menuEntries.push({name: "Find Max Profit", functionName: "findMaxAmount"}); ss.addMenu("GDL Sim Example", menuEntries); } ////////////////////////////////////////////////////////////////////////////////////////////////// // // For the next GDL IL // ////////////////////////////////////////////////////////////////////////////////////////////////// var trials = 25; //Number of trials to run for simulation var senarios = 10; // num of senarios… // Main function to find the max return from many simulations function findMaxAmount() { for (var i=0; i < senarios; i++) { var amount = runSenarioBlending(); SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Deals").getRange("e"+(40+i)).setValue(amount); Logger.log(i + ") Amount: " + amount + " n " ); } } function runSenario() { for (var i=0; i < senarios; i++) runDealSim(i+3); } // function runDealSim(curLine) { var winsBig = 0; var lossBig = 0; var winSmall = 0; var lossSmall = 0; var curTrial = 0; var moneyWeMade = 0; while (curTrial < trials) { if (winBigDeal()) { moneyWeMade += bigDeal; winsBig++; } else { lossBig++; } if (winSmallDeal()) { moneyWeMade += smallDeal; winSmall++; } else { lossSmall++; } //Keep track of the number of trials curTrial++; } SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Deals").getRange("a"+curLine).setValue(winsBig); SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Deals").getRange("c"+curLine).setValue(winSmall); SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Deals").getRange("b"+curLine).setValue(lossBig); SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Deals").getRange("d"+curLine).setValue(lossSmall); SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Deals").getRange("e"+curLine).setValue(moneyWeMade); }; //////////////////////// // init //var trails = 10; var cube = new Array(6); function cleanCube() { for(var j=0; j<6; j++) { cube[j]=0; } } // function findProp() { for (var i=0; i < trails; i++) { runSim(); var luckyNum = cube[3]; SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Prop").getRange("b"+(i+2)).setValue(luckyNum); } } // function runSim() { cleanCube(); for (var i=0; i < trails; i++) { var face = Math.floor((Math.random()*6)+1); // Give us a rand between 1-6 cube[face–1]++; } printResults(); } function printResults() { for(var j=2; j<8; j++) { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sim").getRange("c"+j).setValue(cube[j–2]); } Logger.log("The cube results: " + cube); } view raw AppsScript – Monte Carlo simulation.js hosted with ❤ by GitHub Good luck.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s