Probl

A probabilistic language.

Write down what might happen. Probl works out how likely each outcome is. Use familiar variables, functions and loops to explore game odds, compare decisions and make forecasts.

Try an example and change the assumptions. Everything runs in your browser, with no installation.

Or install the command line with Cargo

cargo install probl-cli --locked
Requires Rust 1.99 or later. Install Rust.
first-roll.problOpen in the playground
let total ~ 2d6report total >= 10 as "Rolling 10 or more"
enumeratedRolling 10 or more    16.67%

What’s the chance of rolling 10 or more with two six-sided dice? 2d6 adds the dice together; report asks for the odds. The answer: 16.67%. Try changing 10 to another number.

How it works

Explore what could happen

Give Probl the choices and their chances. It follows the possible outcomes, combines paths that lead to the same situation, and answers the questions you ask.

  1. 1 Describe the possibilities

    In this model, a 30% chance of rain leaves a 70% chance of sun. Write if 30% and Probl follows both possibilities, keeping track of how likely each one is.

    weather.problOpen in the playground
    let weather = if 30% { "rain" } else { "sun" }report weather
    "rain"30%
    "sun"70%
  2. 2 Combine paths that meet

    Flip a coin to step left or right. Left then right brings you back to the start, just like right then left. Probl combines those two paths: together, they give a 50% chance of being back at zero.

    When only your current position matters, Probl can skip keeping every route separately. That makes many models much smaller.

    walk.problOpen in the playground
    var pos = 0repeat 3 { pos += if 50% { 1 } else { -1 } }report pos
    0 1 −1 ½ +1 ½ −2 ¼ 0 ½ +2 ¼ −3 ⅛ −1 ⅜ +1 ⅜ +3 ⅛
  3. 3 Ask a question

    Use report to ask what you want to know: the chance of rain, the odds of winning, or how long you might wait. Probl brings the possibilities together in one result.

    weather.problOpen in the playground
    enumeratedweather    sun 70.00% · rain 30.00%

Games

Work out the odds

Describe dice rolls, card draws and the rules of each turn. Probl follows the game and calculates the chances of each result.

Ten dice, rolled one at a time
60,466,176 possible roll sequences, but only 51 totals. Probl tracks at most 276 possibilities at once in this model.
A Risk battle, fought to the end
Many different dice rolls leave the same armies standing. Probl combines them before moving on to the next round.
Keep playing until the game ends
Some games need an unknown number of turns. Probl can handle many of these too, and its reports tell you when part of the answer remains unresolved.
04_risk_battle.problOpen in the playground
# Risk: 10 attacking armies against 8 defenders, fought to the end.@mode enumeratevar attackers = 10var defenders = 8while attackers > 1 and defenders > 0 {  # The attacker rolls up to three dice but must leave one army at home;  # the defender rolls up to two. roll(n, d6) gives the dice sorted high to low.  let attack ~ roll(min(3, attackers - 1), d6)  let defend ~ roll(min(2, defenders), d6)  # Highest against highest, second against second. Ties go to the defender.  for i in 0..<min(attack.len(), defend.len()) {    if attack[i] > defend[i] { defenders -= 1 } else { attackers -= 1 }  }}# A full round has 56 × 21 distinct dice outcomes. But once it's over, the# dice are never read again, so the only state left is the two army counts:# the worlds merge back into a handful after every round.report defenders == 0 as "attacker conquers"report attackers - 1 as "armies left to move in"
enumeratedattacker conquers         64.64%armies left to move in    mean 3.05 · sd 2.78 · 5% 0 · median 3 · 95% 8 · 0 █▁▂▃▃▃▃▂▂▁ 9

Keep rolling until you win or lose

In craps, your first roll can win, lose, or set a target. If it sets a target, keep rolling: hit that total again to win, or roll a seven to lose. Probl works out the overall chance of winning: 49.29%.

craps.problOpen in the playground
# Craps, pass line bet: what's the chance of winning?let come_out ~ 2d6               # one world per totalvar win = falseif come_out in [7, 11] {  win = true} else if come_out not in [2, 3, 12] {  loop {                         # keep rolling for the point    let r ~ 2d6    if r == come_out { win = true; break }    if r == 7 { break }  }}report win
enumeratedwin    49.29%

Forecasting

See a range of possible futures

How might revenue grow after a launch? Let growth and customer losses vary, run the model many times, and see both the likely outcomes and the wider range of possibilities.

Monthly revenue after a launch, as the market moves between boom, steady and slump
$0 $50k $100k $150k $200k 1 6 12 18 month $100k a month

90% of runs 50% median

  • 3% to 7% models positive growth, with 90% of draws falling between 3% and 7%.
  • normal_range(-8%, 0%) allows for losses, with 90% of draws falling between −8% and 0%.
  • report … by month shows how the outlook changes each month. Wider bands in the chart mean a wider range of possible results.
sample · 50,000 runs · seed 11MRR ≥ $100k after 18 months    33.3% ± 0.2%
07_launch_forecast.probl (part)Open in the playground
let price = 49                 # $ per customer per monthlet churn ~ 3% to 7%           # share of customers who leave each month: uncertain, but fixedvar market ~ one_of([Boom: 20%, Steady: 60%, Slump: 20%])var signups ~ 60 to 150        # new customers per month at launchvar customers = 0for month in 1..18 {  let new ~ poisson(signups)  let lost ~ binomial(customers, churn)  customers += new - lost  report customers * price by month as "MRR ($)"  market = next_month(market)  let growth ~ match market {  # month-on-month growth in sign-ups    Boom   => 8% to 20%    Steady => 1% to 6%    Slump  => normal_range(-8%, 0%)   # growth can be negative: say which shape you mean  }  signups *= 1 + growth}report customers * price >= 100_000 as "MRR ≥ $100k after 18 months"
test.problOpen in the playground
# A condition affects 1% of the population.let sick ~ bernoulli(1%)# Sensitivity is 95%; the false-positive rate is 8%.observe true from bernoulli(if sick { 95% } else { 8% })report sick
enumerated · evidence 8.87%sick    10.71%

Evidence

Learn from new information

Use observe to tell Probl what you have learned. It rules out outcomes that no longer fit and adjusts the chances of the remaining possibilities.

Suppose a condition affects 1% of people. A test detects it 95% of the time, but also gives a positive result for 8% of people without it. After a positive test, what is the chance of having the condition? In this model, 10.71%.

You can also bring your own data into a model from CSV, JSON or text files.

Beyond dice and coins

Model waiting times and uncertain rates

An arrival time could be anywhere within half an hour. A success rate might be somewhere between zero and one. Probl can work with these ranges too, and calculate results directly for supported models.

continuous.problOpen in the playground
let arrival ~ uniform(0, 30)observe arrival > 10let wait = max(arrival - 15, 0)report wait as "wait (minutes)"report wait > 10 as "waiting more than 10 minutes"
enumerated · evidence 66.67%wait (minutes)                  mean 5.62 · sd 4.96 · 5% 0.00 · median 5.00 · 95% 14.00waiting more than 10 minutes    25.00%

You know an arrival will be after minute 10. How long will you wait beyond minute 15? There is a 25% chance of no extra wait.

posterior.problOpen in the playground
let rate ~ beta(2, 2)observe 3 from binomial(5, rate)report ratereport rate > 50%
enumerated · evidence 21.43%rate          mean 0.56 · sd 0.16 · 5% 0.29 · median 0.56 · 95% 0.81rate > 50%    63.67%

Start with an uncertain success rate, then tell Probl that three of five attempts succeeded. It updates the rate and reports the chance that it is above 50%.

Interpreting results

Understand your results

Two ways to calculate

By default, Probl follows the possible outcomes and combines their probabilities. This is called enumeration. For larger models, you can choose sampling: many random runs that estimate the answer. Probl does not switch automatically.

Reports show how the answer was calculated and flag any unresolved probability. Probabilities use floating-point numbers; fractions marked ≈ are approximate.

enumerated · evidence 8.87%
sample · 50,000 runs · seed 11

Repeat a result

Keep the same model, data, Probl version, date and random seed to repeat a successful run. This makes it easier to share a result and check what changes when you adjust an assumption.

Independent checks

Tests compare results with known answers and, for some models, a separate implementation. Other checks compare calculations with repeated simulations. The testing guide explains what is covered and what remains to check.

Two storms, or the same storm twice?

If two days each have an independent 30% chance of a storm, the chance of both being stormy is 9%. Asking about the same storm twice still gives 30%. Use = to define a chance you can draw from, and ~ to draw one outcome and reuse it.

storm.problOpen in the playground
let storm = bernoulli(30%)report storm and storm as "two independent storms"let happened ~ stormreport happened and happened as "the same storm twice"
enumeratedtwo independent storms    9.00%the same storm twice      30.00%

Get started

Run Probl

The playground

Start with an example, change a few values and run it again. The editor offers suggestions and help as you type. Share a link so someone else can try your model.

Open the playground

The command line

Install Rust (1.99 or later), then run this in your terminal:

cargo install probl-cli --locked

Save the following as first-roll.probl:

first-roll.problOpen in the playground
let total ~ 2d6report total >= 10 as "Rolling 10 or more"

In the folder where you saved the file, run:

probl run first-roll.probl

You’ll see a 16.67% chance of rolling 10 or more. Change the number in the file and run it again.

Examples

22 example models

Examples cover games, forecasting, calendars, decisions, monitoring and risk. Each includes assumptions and expected results, and opens in the playground.

Status and scope

Probl is early-stage and its syntax is still evolving. Some models are too large to calculate fully, and not every kind of calculation is supported. The language guide introduces the available features; the reference explains their limits.

Probl draws on ideas from Squiggle, AnyDice and WebPPL. Source code is available on GitHub under MIT or Apache-2.0, without warranty.