Running Monte Carlo Simulations in PHP
One of the exciting things in the 1980’s was programming simulations to solve complex analytical problems, and one of the most useful techniques employed was running Monte Carlo simulations. The approach repeatedly runs a simulation many times over to calculate the most likely outcome.
Although PHP isn’t known as a scientific or research programming language, Monte Carlo simulations can easily be written in a PHP web app. In this article, I’ll show you how.
A Real-World Problem
The day before yesterday I had a meeting at 9:00 AM about 100 miles away from my home. At 6:30 I was awake and dressed, and while having breakfast I began to work out the next couple of hours on a notepad. I obviously wanted to arrive at the meeting safely and on time, so I started sketching the drive split in eight stages: urban departure, country road, state road northbound, state road eastbound, state highway eastbound, urban crossing, state highway northbound, and urban arrival. The sketch more or less looked like this:
My wife had filled the gas tank the evening before and ideally I could drive straight out to the country road. The tires seemed alright when I looked at them, but doubt whether or not to make a 10 minute detour to properly check their pressure hounded me. If I stopped and checked the tires, I’d be certain of their pressure. Otherwise, I’d have to travel with the uncertainty. Poor tire pressure could have an effect on my driving stability and speed.
I could leave as early as 6:40 but then my wife would have to take our daughter to school instead of going straight to work. If I waited another 10 minutes, I could be at the school as their gates opened for the morning and spare my wife the trouble. The school is on my way out of town so there’d be no significant added travel time.
I went back to my drawing and added the following:
Sipping my second cup of coffee, I stood by the window. A clear dusk sky and brisk morning breeze agreed with the perfect day forecast on my smartphone which made me believe the drive would be a fast one. To finish my planning, I drew from past experience and wrote down the estimated drive times:
The expected time en route was 115 minutes (1 hour 55 minutes), which I could cover non-stop. My ETA was 8:35 if I left straight for the road and 8:55 if I decided take my daughter to school and check the tires.
But no plan survives its first encounter with the enemy! For some mysterious reason, many other parents decided to drop their children off at school earlier than usual, and I had lost more than 5 minutes on what was planned to be a quick detour. Knowing that my baseline was already compromised, I decided to skip the tire check and drive straight to the country road.
I reached the road actually five minutes sooner than the original worst case estimate and the drive went well until, somewhere between milestones B and C, I ran into dense fog which reduced my visibility. This reduced my average speed and made it harder to overtake the slow but long trucks.
The urban traffic in the town I had to cross was much lighter than usual and it didn’t take me more than 10 minutes to go across. And a few miles onto state highway southbound the fog lifted so I was able to drive at the full legal speed. But when I approached my destination, I realized that some road work that was in progress had blocked the exit I planned to take. This added another 10 minutes to my travel time and needless to say I arrived late.
Modeling the Trip in PHP
I believe most PHP coding is dedicated to profit and non-profit business websites. But PHP can be a fine tool for scientific research and might as well be easier to teach non-professional programmers, like engineers and scientists, than other languages like my beloved Python.
Let’s write the basic code that will help me understand how much earlier or later I could have arrived at the meeting if one or more stages of my plan varied substantially from their baseline estimates. We can begin to model the trip as follows:
<?php
class MyTrip
{
protected $departureTime;
protected $meetingTime;
protected $travelTimes;
public function __construct() {
$this->setDepartureTime('0640');
$this->setMeetingTime('0900');
// travel times in minutes between milestones
$this->setTravelTimes(array(
'AB' => 17,
'BC' => 17,
'CD' => 36,
'DE' => 9,
'EF' => 15,
'FG' => 15,
'GH' => 6
));
}
// for convenience convert time string to minutes past
// midnight
protected static function convertToMinutes($timeStr) {
return substr($timeStr, 0, 2) * 60 +
substr($timeStr, 2, 2);
}
public function setDepartureTime($timeStr) {
$this->departureTime = self::convertToMinutes($timeStr);
}
public function setMeetingTime($timeStr) {
$this->meetingTime = self::convertToMinutes($timeStr);
}
public function setTravelTimes(array $travelTimes) {
$this->travelTimes = $travelTimes;
}
public checkPlan($stopAtSchool = true, $checkTires = true) {
// ...
}
}
Plans must be feasible, and the suitable criteria to judge this one is whether the sum of all times plus the earliest departure time is less than or equal to the time of the meeting. That is what the checkPlan()
method determines:
<?php
public checkPlan($stopAtSchool = true, $checkTires = true) {
// calculate the sum of travel times between milestones
$travelTime = array_sum($this->travelTimes);
// add delay if dropping kid off at school
$schoolDelay = ($stopAtSchool) ? 10 : 0;
// add delay if checking tire pressure
$tiresDelay = ($checkTires) ? 10 : 0;
// find the total schedule baseline
$meetingArriveTime = $this->departureTime + $travelTime +
$schoolDelay + $tiresDelay;
// does the traveller make the meeting on time?
$arriveOnTime = $meetingArriveTime <= $this->meetingTime;
return array($meetingArriveTime, $this->meetingTime,
$arriveOnTime);
}
Now all we have to do is create an instance of the class and ask it to check my plan:
<?php
$trip = new MyTrip();
print_r($trip->checkPlan());
Given the default values, the above will output that my original plan was okay:
Array ( [0] => 535 [1] => 540 [2] => 1 )
I should be there 535 minutes after midnight, and the meeting takes place 540 minutes after midnight. According to the baseline, I’ll arrive at the meeting at 8:45 AM, just 5 minutes before the scheduled time!
But what about the inevitable variations? How can we account for the uncertain elements?
Monte Carlo and Adding Randomness
In a very simplistic way we can define a safety margin to every event and say it could happen 10% earlier and 25% later of the scheduled time. Such margins can be randomly added to the departure delays and every travel time between milestones by multiplying each factor by rand(90,125)/100
.
We can also assign a 50% probability for both decisions to drop my daughter off at school and to check the tires. Again the rand()
function can helps us:
$this->checkTires = rand(1, 100) > 50;
Putting it all together, we can define a method checkPlanRisk()
to computer whether or not I can arrive on time given the many uncertainties that stand in my way:
<?php
public function checkPlanRisk() {
// adjust and sum travel times between milestones
$travelTime = 0;
foreach ($this->travelTimes as $t) {
$travelTime += $t * rand(90, 125) / 100;
}
// decide whether to drop kid off at school and randomly set
// the delay time
$schoolDelay = 0;
if (rand(1, 100) > 50) {
$schoolDelay = 10 * rand(90, 125) / 100;
}
// ditto for checking tires
$tiresDelay = 0;
if (rand(1, 100) > 50) {
$tiresDelay = 10 * rand(90, 125) / 100;
}
// find the total schedule baseline
$meetingArriveTime = $this->departureTime + $travelTime +
$schoolDelay + $tiresDelay;
// does the traveller make the meeting on time?
$arriveOnTime = $meetingArriveTime <= $this->meetingTime;
return array($schoolDelay, $tiresDelay, $meetingArriveTime,
$this->meetingTime, $arriveOnTime);
}
The question now is, how likely is the traveller to arrive on time given the initial conditions and the assumed uncertainty? Monte Carlo simulations answer this by running a large number of times and computing a “level of confidence”, defined as the ratio of arrivals on time to total number of trials.
If we run this method a sufficient number of times and record how often the traveller arrives on time, we can establish some sort of margin of confidence whether the trip is feasible as planned.
<?php
public function runCheckPlanRisk($numTrials) {
$arriveOnTime = 0;
for ($i = 1; $i <= $numTrials; $i++) {
$result = $this->checkPlanRisk();
if ($result[4]) {
$arriveOnTime++;
}
echo "Trial: " . $i;
echo " School delay: " . $result[0];
echo " Tire delay: " . $result[1];
echo " Enroute time: " . $result[2];
if ($result[4]) {
echo " -- Arrive ON TIME";
}
else {
echo " -- Arrive late";
}
$confidence = $arriveOnTime / $i;
echo "nConfidence level: $confidencenn";
}
}
Creating a new instance of MyTrip
and asking it compute the confidence level for 1,000 trials is straightforward:
<?php
$trip = new MyTrip();
$trip->runCheckPlanRisk(1000);
The output will be a screen print-out of 1,000 entries like this:
Trial: 1000 School delay: 0 Delay tires: 11.3 Enroute time: 530.44 -- Arrive ON TIME Confidence level: 0.716
With the parameters above, the confidence level seems to converge to 0.72, which roughly indicates that the traveler has 72% chance of getting to the meeting on time.
Grosso modo, Monte Carlo relies on the convergence of the mean result of a sequence of identical experiments. The mean result is also known as the expected value and can be thought of as the probability of a desired result occurring. Of course this is quite an old concept and such simulations have been done a long time before the arrival of the digital computer.
Conclusion
Today, we have immensely powerful resources at our disposal and a very convenient language like PHP which can be used to create both the simulation logic and a user friendly web interface for it. With a few lines of code, we created a model and applied Monte Carlo simulations with it, producing an intelligible result useful for business decisions. This is a field worth exploring and it would be nice to see some scientific computation functions added to PHP so scientists and engineers could take advantage of this versatile, non-nonsense language.
Code for this article is available on GitHub.
Image via Fotolia