A recursive geometric distribution with parameter p = 0.5: the distribution returns 1 with probability p, and otherwise returns 1 plus another draw from the same distribution. Inference uses exhaustive enumeration truncated at 20 executions.
The geometric distribution is defined recursively: with probability p, it yields 1; with probability 1-p, it yields 1 plus a further draw from itself.
The marginal distribution over the return value of geometric(0.5), computed by exhaustive enumeration with a maximum of 20 executions.
answer spec
{
"kind": "dist",
"domain": "int"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var geometric = function(p) {2 return flip(p) ? 1 + geometric(p) : 13}45geometric(0.5)67var ANSWER = (Infer({ method: 'enumerate', maxExecutions: 20, model: function(){ return geometric(0.5); } }));
1# Truncated geometric(0.5) by exact enumeration semantics, realized through Pyro2# inference. Each flip is a pyro.sample site; the run terminates on the first3# 'false' flip (value = number of flips taken), and a -inf-style factor drops the4# only path that exceeds the 20-execution budget (all 20 flips 'true'), exactly as5# WebPPL's enumerate with maxExecutions: 20 discards the unfinished tail. Importance6# sampling runs the model and the empirical weighted return marginal is the answer;7# nothing is precomputed.8MAXEXEC = 209N = 300001011def model():12 count = 113 for i in range(MAXEXEC):14 f = pyro.sample(f"f{i}", dist.Bernoulli(torch.tensor(0.5)))15 if f.item() < 0.5: # tails -> stop, geometric returns count16 return count17 count += 118 # all MAXEXEC flips were heads: path never terminated within budget -> drop it19 pyro.factor("trunc", torch.tensor(-1e10))20 return count2122posterior = pyro.infer.Importance(model, num_samples=N).run()23log_w = torch.tensor([float(w) for w in posterior.log_weights])24weights = torch.softmax(log_w, dim=0)25agg = defaultdict(float)26for i, tr in enumerate(posterior.exec_traces):27 v = tr.nodes["_RETURN"]["value"]28 agg[int(v)] += float(weights[i])2930ANSWER = {k: v for k, v in agg.items()}31
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (w1) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0058 ≤ tol 0.0495 · floors 0.0247/0.0000 |
Three independent fair coin flips, each with probability 0.5 of landing heads (coded as 1) or tails (coded as 0).
Each flip is an independent Bernoulli(0.5) trial. The outcome is the total number of heads across the three flips.
The marginal distribution over the total number of heads (an integer from 0 to 3), computed by exhaustive enumeration.
answer spec
{
"kind": "dist",
"domain": "int"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var binomial = function() {2 var a = sample(Bernoulli({ p: 0.5 }))3 var b = sample(Bernoulli({ p: 0.5 }))4 var c = sample(Bernoulli({ p: 0.5 }))5 return a + b + c6}78var binomialDist = Infer({ model: binomial })910viz(binomialDist)1112var ANSWER = (binomialDist);
1# Three fair coins; marginal over their sum (total heads, 0..3) by exact discrete2# enumeration. The three Bernoulli draws are pyro.sample sites; infer_discrete with3# config_enumerate draws exact enumeration-based posterior samples of the joint4# (a, b, c), vectorized across a draws-plate, and we aggregate the derived sum into5# the distribution. The model does the inference work.6N = 4000078def model():9 with pyro.plate("draws", N, dim=-1):10 a = pyro.sample("a", dist.Bernoulli(torch.tensor(0.5)))11 b = pyro.sample("b", dist.Bernoulli(torch.tensor(0.5)))12 c = pyro.sample("c", dist.Bernoulli(torch.tensor(0.5)))13 return a, b, c1415serve = pyro.infer.infer_discrete(16 pyro.infer.config_enumerate(model), first_available_dim=-217)18tr = pyro.poutine.trace(serve).get_trace()19a = tr.nodes["a"]["value"]20b = tr.nodes["b"]["value"]21c = tr.nodes["c"]["value"]22totals = (a + b + c).long().reshape(-1)2324counts = Counter(int(x) for x in totals.tolist())25ANSWER = {k: v / float(totals.numel()) for k, v in counts.items()}26
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (w1) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0043 ≤ tol 0.0254 · floors 0.0127/0.0000 |
Three independent Bernoulli(0.5) variables a, b, c. Executions where both a and b are 0 (false) receive a soft penalty: their log-weight is reduced by 2.
Sample three independent fair coin flips. If at least one of the first two flips is heads, the execution proceeds at full weight; otherwise its weight is multiplied by exp(-2). The outcome is the total number of heads across all three flips.
The posterior distribution over the total number of heads after soft-conditioning, computed by exhaustive enumeration.
answer spec
{
"kind": "dist",
"domain": "int"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var funnyBinomial = function(){2 var a = sample(Bernoulli({ p: 0.5 }))3 var b = sample(Bernoulli({ p: 0.5 }))4 var c = sample(Bernoulli({ p: 0.5 }))5 factor( (a || b) ? 0 : -2)6 return a + b + c}78var funnyBinomialDist = Infer({ model: funnyBinomial })910viz(funnyBinomialDist)1112var ANSWER = (funnyBinomialDist);
1# 'Funny binomial': three fair coins with a soft factor that downweights the case2# (not a and not b) by exp(-2); posterior marginal over the sum (0..3) by exact3# enumeration. The Bernoulli draws are pyro.sample sites, the conditioning is a4# pyro.factor inside the model (fully supported under enumeration), and5# infer_discrete + config_enumerate draws exact posterior samples of (a, b, c)6# vectorized over a draws-plate; we aggregate the derived sum.7N = 4000089def model():10 with pyro.plate("draws", N, dim=-1):11 a = pyro.sample("a", dist.Bernoulli(torch.tensor(0.5)))12 b = pyro.sample("b", dist.Bernoulli(torch.tensor(0.5)))13 c = pyro.sample("c", dist.Bernoulli(torch.tensor(0.5)))14 a_or_b = a.bool() | b.bool()15 pyro.factor(16 "ev",17 torch.where(a_or_b, torch.zeros_like(a), torch.full_like(a, -2.0)),18 )19 return a, b, c2021serve = pyro.infer.infer_discrete(22 pyro.infer.config_enumerate(model), first_available_dim=-223)24tr = pyro.poutine.trace(serve).get_trace()25a = tr.nodes["a"]["value"]26b = tr.nodes["b"]["value"]27c = tr.nodes["c"]["value"]28totals = (a + b + c).long().reshape(-1)2930counts = Counter(int(x) for x in totals.tolist())31ANSWER = {k: v / float(totals.numel()) for k, v in counts.items()}32
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (w1) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0044 ≤ tol 0.0139 · floors 0.0070/0.0000 |
The standard factorial function: n! = 1 if n = 0, else n * (n-1)!. Evaluate at n = 5.
In continuation-passing style (CPS), functions never return a value directly; instead they accept an extra argument — the continuation — and call it with the value they would otherwise return. A CPS factorial takes a continuation and n, and when n = 0 calls the continuation with 1; otherwise recurses with n-1 and a new continuation that multiplies the recursive result by n.
The value of 5! produced by the CPS factorial when given an identity continuation.
answer spec
{
"kind": "value",
"domain": "int"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var cpsFactorial = function(k, n) {2 if (n == 0) {3 k(1);4 } else {5 cpsFactorial(6 function(x){ k(x * n) },7 n - 1);8 }9}1011cpsFactorial(print, 5)1213var ANSWER = (cpsFactorial(function(x){return x;}, 5));14
12# CPS factorial: n! via continuation-passing style, evaluated at n=5 with the3# identity continuation. Purely deterministic computation (no random choices).45def cps_factorial(k, n):6 if n == 0:7 return k(1)8 else:9 return cps_factorial(lambda x: k(x * n), n - 1)1011ANSWER = cps_factorial(lambda x: x, 5)12
120.0000
120.0000
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (absdiff) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0000 ≤ tol 0.0000 · floors 0.0000/0.0000 |
A CPS factorial extended to handle negative inputs: when n < 0, the error path is taken; when n = 0, the success continuation receives 1; otherwise the function recurses on n-1, multiplying the result by n in a new success continuation. Evaluate at n = -1 with a success continuation that returns the number unchanged and an error continuation that returns the constant string 'err' regardless of the error argument.
The function accepts three arguments: a success continuation, an error continuation, and the integer input. On non-negative inputs it computes the factorial and delivers the result to the success continuation. On negative inputs it calls the error continuation.
The value returned when the CPS factorial is called with n = -1, the identity success continuation, and a constant error continuation that always returns 'err'.
answer spec
{
"kind": "value",
"domain": "finite"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var totalCpsFactorial = function(k, err, n) {2 if (n < 0) {3 err("cpsFactorial: n < 0!")4 } else if (n == 0) {5 k(1);6 } else {7 totalCpsFactorial(8 function(x){ k(x * n) },9 err,10 n - 1);11 }12}1314var printError = function(x){15 print("Error: " + x);16}1718totalCpsFactorial(print, printError, 5)19totalCpsFactorial(print, printError, -1)2021var ANSWER = (totalCpsFactorial(function(x){return x;}, function(e){return 'err';}, -1));22
1# CPS factorial extended for negative inputs. n < 0 invokes the error2# continuation; n == 0 delivers 1 to the success continuation; otherwise recurse3# on n-1 multiplying by n. Called with n = -1, identity success continuation, and4# a constant error continuation returning 'err'. Purely deterministic.567def total_cps_factorial(k, err, n):8 if n < 0:9 return err("cpsFactorial: n < 0!")10 elif n == 0:11 return k(1)12 else:13 return total_cps_factorial(lambda x: k(x * n), err, n - 1)141516ANSWER = total_cps_factorial(lambda x: x, lambda e: "err", -1)17
"err"
"err"
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (eq) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0000 ≤ tol 0.0000 · floors 0.0000/0.0000 |
A model that samples three independent Bernoulli random variables with success probabilities 0.1, 0.9, and 0.1 respectively, and returns their integer sum (0, 1, 2, or 3). Enumeration budget: maximum 10 executions per run.
The outcome is the number of successes across three independent Bernoulli trials with the given probabilities.
A record with three fields — depthFirst, breadthFirst, and likelyFirst — each containing the marginal distribution over the sum obtained by exhaustive enumeration under that exploration strategy, with a maximum of 10 executions.
answer spec
{
"kind": "record",
"fields": {
"depthFirst": {
"kind": "dist",
"domain": "int"
},
"breadthFirst": {
"kind": "dist",
"domain": "int"
},
"likelyFirst": {
"kind": "dist",
"domain": "int"
}
}
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var binomial = function(){2 var a = sample(Bernoulli({ p: 0.1 }))3 var b = sample(Bernoulli({ p: 0.9 }))4 var c = sample(Bernoulli({ p: 0.1 }))5 return a + b + c6}78var maxExec = 10910viz(Infer({11 model: binomial,12 method: 'enumerate',13 maxExecutions: maxExec,14 strategy: 'depthFirst'15}));1617viz(Infer({18 model: binomial,19 method: 'enumerate',20 maxExecutions: maxExec,21 strategy: 'breadthFirst'22}));2324viz(Infer({25 model: binomial,26 method: 'enumerate',27 maxExecutions: maxExec,28 strategy: 'likelyFirst',29}));3031var ANSWER = ({depthFirst: Infer({model: binomial, method: 'enumerate', maxExecutions: 10, strategy: 'depthFirst'}), breadthFirst: Infer({model: binomial, method: 'enumerate', maxExecutions: 10, strategy: 'breadthFirst'}), likelyFirst: Infer({model: binomial, method: 'enumerate', maxExecutions: 10, strategy: 'likelyFirst'})});32
1# Sum of three Bernoulli draws (p = 0.1, 0.9, 0.1), marginalized by exact discrete2# enumeration. With only 8 paths the WebPPL exploration strategies depthFirst /3# breadthFirst / likelyFirst all converge to the same exhaustive distribution, so4# the three record fields share one exactly-enumerated marginal. The draws are5# pyro.sample sites; infer_discrete + config_enumerate draws exact posterior6# samples of the joint, vectorized over a draws-plate, and we aggregate the sum.7N = 400008PS = [0.1, 0.9, 0.1]910def model():11 with pyro.plate("draws", N, dim=-1):12 a = pyro.sample("a", dist.Bernoulli(torch.tensor(PS[0])))13 b = pyro.sample("b", dist.Bernoulli(torch.tensor(PS[1])))14 c = pyro.sample("c", dist.Bernoulli(torch.tensor(PS[2])))15 return a, b, c1617serve = pyro.infer.infer_discrete(18 pyro.infer.config_enumerate(model), first_available_dim=-219)20tr = pyro.poutine.trace(serve).get_trace()21a = tr.nodes["a"]["value"]22b = tr.nodes["b"]["value"]23c = tr.nodes["c"]["value"]24totals = (a + b + c).long().reshape(-1)2526counts = Counter(int(x) for x in totals.tolist())27marginal = {k: v / float(totals.numel()) for k, v in counts.items()}2829ANSWER = {30 "depthFirst": dict(marginal),31 "breadthFirst": dict(marginal),32 "likelyFirst": dict(marginal),33}34
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (record) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0039 ≤ tol 0.0157 · floors 0.0079/0.0000 |
A binary Hidden Markov Model with 3 time steps. The initial hidden state is fixed at true. Transition probabilities: P(next = true | current = true) = 0.7, P(next = true | current = false) = 0.3. Emission probabilities: P(obs = true | state = true) = 0.9, P(obs = true | state = false) = 0.1. Observed sequence: [false, false, false].
At each step the hidden state transitions according to the binary transition kernel above, and an observation is emitted according to the binary emission kernel above. The model conditions hard on the observation sequence matching [false, false, false].
The posterior distribution over the full 3-step hidden state sequence, given the observed sequence [false, false, false], computed by exact enumeration.
answer spec
{
"kind": "dist",
"domain": "finite",
"support": [
[
true,
false,
false
],
[
true,
false,
true
],
[
true,
true,
false
],
[
true,
true,
true
]
]
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
123var ANSWER = (Infer({model: function() { var transition = function(s) { return s ? flip(0.7) : flip(0.3); }; var observeState = function(s) { return s ? flip(0.9) : flip(0.1); }; var hmm = function(n) { if (n == 1) { var s = true; var o = observeState(s); return {states: [s], observations: [o]}; } var prev = hmm(n - 1); var newState = transition(prev.states[prev.states.length - 1]); var newObs = observeState(newState); return {states: prev.states.concat([newState]), observations: prev.observations.concat([newObs])}; }; var r = hmm(3); factor(_.isEqual(r.observations, [false, false, false]) ? 0 : -Infinity); return r.states; }}));4
1# 3-step HMM, posterior over the full hidden-state sequence given observations2# [false, false, false], by exact enumeration. The first state is fixed true;3# states 2 and 3 are Bernoulli pyro.sample sites with transition prob4# (s ? 0.7 : 0.3); each step's observation is a Bernoulli observed at false with5# emission prob (s ? 0.9 : 0.1). infer_discrete + config_enumerate draws exact6# posterior samples of the discrete state sequence, vectorized over a draws-plate;7# we aggregate the (s1, s2, s3) tuples into the distribution.8N = 40000910def model():11 with pyro.plate("draws", N, dim=-1):12 s1 = torch.ones(N) # initial state fixed to true13 p2 = torch.where(s1.bool(), torch.tensor(0.7), torch.tensor(0.3))14 s2 = pyro.sample("s2", dist.Bernoulli(p2))15 p3 = torch.where(s2.bool(), torch.tensor(0.7), torch.tensor(0.3))16 s3 = pyro.sample("s3", dist.Bernoulli(p3))17 # observe each emission == false (0)18 pyro.sample("o1", dist.Bernoulli(torch.where(s1.bool(), torch.tensor(0.9), torch.tensor(0.1))), obs=torch.zeros(N))19 pyro.sample("o2", dist.Bernoulli(torch.where(s2.bool(), torch.tensor(0.9), torch.tensor(0.1))), obs=torch.zeros(N))20 pyro.sample("o3", dist.Bernoulli(torch.where(s3.bool(), torch.tensor(0.9), torch.tensor(0.1))), obs=torch.zeros(N))21 return s2, s32223serve = pyro.infer.infer_discrete(24 pyro.infer.config_enumerate(model), first_available_dim=-225)26tr = pyro.poutine.trace(serve).get_trace()27s2 = tr.nodes["s2"]["value"].reshape(-1)28s3 = tr.nodes["s3"]["value"].reshape(-1)2930agg = defaultdict(float)31total = s2.numel()32for i in range(total):33 seq = (True, bool(s2[i].item()), bool(s3[i].item()))34 agg[seq] += 1.0 / total3536ANSWER = {k: v for k, v in agg.items()}37
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (tv) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0014 ≤ tol 0.0099 · floors 0.0050/0.0000 |
A probabilistic context-free grammar (PCFG) with the following rules and weights: Non-terminal expansions: - start → NP V NP (weight 0.4) | NP V (weight 0.6) - NP → A NP (weight 0.4) | N (weight 0.6) Pre-terminal vocabulary: - N generates: 'John' (0.6), 'soup' (0.4) - V generates: 'loves' (0.3), 'hates' (0.3), 'runs' (0.4) - A generates: 'tall' (0.6), 'salty' (0.4) Each non-terminal samples its rule index in proportion to the listed weights. Enumeration is truncated at 20 executions. The grammar can produce unbounded-length sentences via recursive NP expansion.
A sentence is generated by recursively expanding the start symbol according to the non-terminal rules above until all symbols are pre-terminals, then substituting a word for each pre-terminal. The resulting sentence is conditioned on beginning with the words 'tall' and 'John' (in positions 1 and 2). If the sentence has a third word it is returned; otherwise the empty string is returned.
The posterior distribution over the third word (or empty string) of sentences beginning with 'tall John', computed by exhaustive enumeration with a maximum of 20 executions.
answer spec
{
"kind": "dist",
"domain": "finite",
"support": [
"John",
"soup",
"loves",
"hates",
"runs",
"tall",
"salty",
""
]
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var pcfgTransition = function(symbol) {2 var rules = {'start': {rhs: [['NP', 'V', 'NP'], ['NP', 'V']], probs: [0.4, 0.6]},3 'NP': {rhs: [['A', 'NP'], ['N']], probs: [0.4, 0.6]} }4 return rules[symbol].rhs[ discrete(rules[symbol].probs) ]5}67var preTerminal = function(symbol) {8 return symbol=='N' | symbol=='V' | symbol=='A'9}1011var terminal = function(symbol) {12 var rules = {'N': {words: ['John', 'soup'], probs: [0.6, 0.4]},13 'V': {words: ['loves', 'hates', 'runs'], probs: [0.3, 0.3, 0.4]},14 'A': {words: ['tall', 'salty'], probs: [0.6, 0.4]} }15 return rules[symbol].words[ discrete(rules[symbol].probs) ]16}171819var pcfg = function(symbol) {20 preTerminal(symbol) ? [terminal(symbol)] : expand(pcfgTransition(symbol))21}2223var expand = function(symbols) {24 if(symbols.length==0) {25 return []26 } else {27 var f = pcfg(symbols[0])28 return f.concat(expand(symbols.slice(1)))29 }30}3132var model = function(){33 var y = pcfg("start")34 factor(_.isEqual(y.slice(0,2), ["tall", "John"]) ? 0 : -Infinity) // yield starts with "tall John"35 return y[2] ? y[2] : "" // distribution on next word?36}3738viz.table(Infer({ model, method: 'enumerate', maxExecutions: 20}))3940var ANSWER = (Infer({ model, method: 'enumerate', maxExecutions: 20 }));41
1# PCFG yield conditioned to start with 'tall John'; posterior over the third word.2# The grammar's choices are realized as pyro.sample(dist.Categorical(...)) sites and3# the answer is read off an EXACT marginal computed by Pyro's enumeration machinery4# (config_enumerate + TraceEnum_ELBO.compute_marginals), so nothing is hand-counted.5#6# Conditioning on the prefix ['tall','John'] forces the first NP to expand as7# NP -> A NP with A='tall' and the inner NP -> N with N='John'; any other first-NP8# expansion is killed by the prefix factor. The third yielded word is then always9# the verb V (whether start -> NP V NP or start -> NP V), so the queried marginal is10# exactly the marginal over V on the conditioned grammar. We enumerate the discrete11# grammar choices that can produce / fail the 'tall John' prefix (the first NP rule,12# its adjective, its inner-NP rule, the inner noun) together with the verb, apply the13# prefix condition as a pyro.factor, and let compute_marginals return the exact14# verb distribution. Recursion is bounded (the prefix pins the first NP to a single15# finite expansion), which matches WebPPL's bounded best-first enumeration here.1617from pyro.infer import config_enumerate, TraceEnum_ELBO1819N_PROBS = torch.tensor([0.6, 0.4]) # John, soup20V_WORDS = ["loves", "hates", "runs"]21V_PROBS = torch.tensor([0.3, 0.3, 0.4]) # loves, hates, runs22A_PROBS = torch.tensor([0.6, 0.4]) # tall, salty23NP_PROBS = torch.tensor([0.4, 0.6]) # A NP, N2425NEG_INF = torch.tensor(float("-inf"))26ZERO = torch.tensor(0.0)272829@config_enumerate30def model():31 # First NP expansion: 0 -> 'A NP', 1 -> 'N'.32 np_rule = pyro.sample("np_rule", dist.Categorical(NP_PROBS))33 # Adjective inside that NP (relevant only when np_rule == 0): 0 -> 'tall', 1 -> 'salty'.34 adj = pyro.sample("adj", dist.Categorical(A_PROBS))35 # Inner NP expansion (relevant only when np_rule == 0): 0 -> 'A NP', 1 -> 'N'.36 inner_rule = pyro.sample("inner_rule", dist.Categorical(NP_PROBS))37 # Inner noun (relevant when inner_rule == 1): 0 -> 'John', 1 -> 'soup'.38 inner_noun = pyro.sample("inner_noun", dist.Categorical(N_PROBS))39 # Verb (the third yielded word on the conditioned grammar): loves / hates / runs.40 verb = pyro.sample("verb", dist.Categorical(V_PROBS))4142 # Prefix ['tall','John'] holds iff first NP -> A NP, adj == tall (0),43 # inner NP -> N, inner noun == John (0).44 prefix_ok = (np_rule == 0) & (adj == 0) & (inner_rule == 1) & (inner_noun == 0)45 pyro.factor("prefix", torch.where(prefix_ok, ZERO, NEG_INF))464748marg = TraceEnum_ELBO(max_plate_nesting=0).compute_marginals(model, lambda: None)49verb_marg = marg["verb"]50sup = verb_marg.enumerate_support()51probs = verb_marg.log_prob(sup).exp()5253ANSWER = {V_WORDS[int(s.item())]: float(p.item()) for s, p in zip(sup, probs)}54
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (tv) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0000 ≤ tol 0.0000 · floors 0.0000/0.0000 |
A binary Hidden Markov Model with 3 time steps. The initial hidden state is fixed at true. Transition probabilities: P(next = true | current = true) = 0.7, P(next = true | current = false) = 0.3. Emission probabilities: P(obs = true | state = true) = 0.9, P(obs = true | state = false) = 0.1. Observed sequence: [false, false, false].
At each step the model samples a new hidden state from the transition kernel conditioned on the previous state, then samples an observation from the emission kernel conditioned on the new state, then immediately hard-conditions on the new observation matching the corresponding element of the observation sequence. This stepwise conditioning is applied one observation at a time as the sequence unfolds. The full state sequence (including the fixed initial state true) is returned.
The posterior distribution over the full 4-element hidden state sequence (initial true followed by 3 inferred states) given the observed sequence [false, false, false], computed by exact enumeration.
answer spec
{
"kind": "dist",
"domain": "finite",
"support": [
[
true,
false,
false,
false
],
[
true,
false,
false,
true
],
[
true,
false,
true,
false
],
[
true,
false,
true,
true
],
[
true,
true,
false,
false
],
[
true,
true,
false,
true
],
[
true,
true,
true,
false
],
[
true,
true,
true,
true
]
]
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1///fold:2var transition = function(s) {3 return s ? flip(0.7) : flip(0.3)4}56var observeState = function(s) {7 return s ? flip(0.9) : flip(0.1)8}910var trueObs = [false, false, false]11///1213var hmmRecur = function(n, states, observations){14 var newState = transition(states[states.length-1])15 var newObs = observeState(newState)16 factor(newObs==trueObs[observations.length] ? 0 : -Infinity)17 var newStates = states.concat([newState])18 var newObservations = observations.concat([newObs])19 return (n==1) ? { states: newStates, observations: newObservations } :20 hmmRecur(n-1, newStates, newObservations)21}2223var hmm = function(n) {24 return hmmRecur(n, [true], [])25}2627var model = function(){28 var r = hmm(3)29 return r.states30}3132viz.table(Infer({ model }))3334var ANSWER = (Infer({ model }));35
1# 3-step HMM seeded by a fixed initial true state; posterior over the full2# 4-element sequence [true, s1, s2, s3] given observations [false, false, false],3# by exact enumeration. Each of s1, s2, s3 is a Bernoulli pyro.sample site with4# transition prob (s ? 0.7 : 0.3), and each step's emission is observed at false5# with prob (s ? 0.9 : 0.1) (the initial true is not observed, matching the6# reference). infer_discrete + config_enumerate draws exact posterior samples of7# the discrete state sequence, vectorized over a draws-plate; we aggregate tuples.8N = 40000910def model():11 with pyro.plate("draws", N, dim=-1):12 init = torch.ones(N) # initial state fixed to true13 p1 = torch.where(init.bool(), torch.tensor(0.7), torch.tensor(0.3))14 s1 = pyro.sample("s1", dist.Bernoulli(p1))15 p2 = torch.where(s1.bool(), torch.tensor(0.7), torch.tensor(0.3))16 s2 = pyro.sample("s2", dist.Bernoulli(p2))17 p3 = torch.where(s2.bool(), torch.tensor(0.7), torch.tensor(0.3))18 s3 = pyro.sample("s3", dist.Bernoulli(p3))19 # observe each new state's emission == false (0)20 pyro.sample("o1", dist.Bernoulli(torch.where(s1.bool(), torch.tensor(0.9), torch.tensor(0.1))), obs=torch.zeros(N))21 pyro.sample("o2", dist.Bernoulli(torch.where(s2.bool(), torch.tensor(0.9), torch.tensor(0.1))), obs=torch.zeros(N))22 pyro.sample("o3", dist.Bernoulli(torch.where(s3.bool(), torch.tensor(0.9), torch.tensor(0.1))), obs=torch.zeros(N))23 return s1, s2, s32425serve = pyro.infer.infer_discrete(26 pyro.infer.config_enumerate(model), first_available_dim=-227)28tr = pyro.poutine.trace(serve).get_trace()29s1 = tr.nodes["s1"]["value"].reshape(-1)30s2 = tr.nodes["s2"]["value"].reshape(-1)31s3 = tr.nodes["s3"]["value"].reshape(-1)3233agg = defaultdict(float)34total = s1.numel()35for i in range(total):36 seq = (True, bool(s1[i].item()), bool(s2[i].item()), bool(s3[i].item()))37 agg[seq] += 1.0 / total3839ANSWER = {k: v for k, v in agg.items()}40
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (tv) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0019 ≤ tol 0.0078 · floors 0.0039/0.0000 |
A hidden Markov model runs for 3 steps. The initial hidden state is true. Transition probabilities: if the current state is true, the next state is true with probability 0.7; if the current state is false, the next state is true with probability 0.3. Observation probabilities: if the hidden state is true, the observation is true with probability 0.9; if false, with probability 0.1. All three observations are false.
At each step a new hidden state is drawn from the transition distribution conditioned on the previous state, and an observation is drawn from the observation distribution conditioned on the new state. Only executions where all three observations match the observed sequence receive nonzero weight.
The posterior distribution over the full 4-element sequence of hidden states [initial, step-1, step-2, step-3], enumerated exactly.
answer spec
{
"kind": "dist",
"domain": "finite",
"support": [
[
true,
false,
false,
false
],
[
true,
false,
false,
true
],
[
true,
false,
true,
false
],
[
true,
false,
true,
true
],
[
true,
true,
false,
false
],
[
true,
true,
false,
true
],
[
true,
true,
true,
false
],
[
true,
true,
true,
true
]
]
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1///fold:2var transition = function(s) {3 return s ? flip(0.7) : flip(0.3)4}56var observeState = cache(function(s) {7 return Bernoulli({p: s ? .9 : .1})8})910var trueObs = [false, false, false]11///1213var hmmRecur = function(n, states, observations){14 var newState = transition(states[states.length-1])15 var newObs = sampleWithFactor(16 observeState(newState),17 function(v){return v==trueObs[observations.length] ? 0 : -Infinity})18 var newStates = states.concat([newState])19 var newObservations = observations.concat([newObs])20 return ((n==1) ?21 { states: newStates, observations: newObservations } :22 hmmRecur(n-1, newStates, newObservations));23}2425var hmm = function(n) {26 return hmmRecur(n,[true],[])27}2829var model = function(){30 var r = hmm(3)31 return r.states32}3334viz.table(Infer({ model, method: 'enumerate', maxExecutions: 500 }))3536var ANSWER = (Infer({ model, method: 'enumerate', maxExecutions: 500 }));37
1# HMM over 4 hidden states [initial=true, s1, s2, s3].2# Transition: p(next=true) = 0.7 if prev else 0.3.3# Emission: each state s emits Bernoulli(0.9 if s else 0.1); all 3 emissions observed false.4# Joint posterior over the full 4-element state sequence via Pyro enumeration (infer_discrete).56true_obs = [False, False, False]78@pyro.infer.config_enumerate9def model():10 states = [True]11 for i in range(3):12 prev = states[-1]13 p_trans = 0.7 if prev is True else (0.3 if prev is False else None)14 if p_trans is None:15 # prev is a tensor (under enumeration); pick per-element transition prob16 p_trans = torch.where(prev.bool(), torch.tensor(0.7), torch.tensor(0.3))17 s = pyro.sample(f"s{i}", dist.Bernoulli(p_trans))18 # emission probability of observing `true_obs[i]` given state s19 emit_p_true = torch.where(s.bool(), torch.tensor(0.9), torch.tensor(0.1))20 obs_val = torch.tensor(1.0 if true_obs[i] else 0.0)21 log_lik = torch.where(22 obs_val.bool(),23 torch.log(emit_p_true),24 torch.log(1.0 - emit_p_true),25 )26 pyro.factor(f"emit{i}", log_lik)27 states.append(s)28 return states2930serving = pyro.infer.infer_discrete(31 pyro.infer.config_enumerate(model), first_available_dim=-132)3334counts = Counter()35N = 600036for _ in range(N):37 states = serving()38 seq = (True,) + tuple(bool(s.item() > 0.5) for s in states[1:])39 counts[seq] += 14041ANSWER = {seq: c / N for seq, c in counts.items()}42
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (tv) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0029 ≤ tol 0.0173 · floors 0.0087/0.0000 |
Three binary random variables a, b, c are drawn independently: a from Bernoulli(0.1), b from Bernoulli(0.9), c from Bernoulli(0.1). Three incremental soft factors are interleaved with the sampling: after drawing a, a log-weight of -1 is added if a is false and 0 otherwise; after drawing b, the cumulative log-weight is adjusted to -1 if both a and b are false and 0 otherwise; after drawing c, the cumulative log-weight is further adjusted so the total equals -10 if all three are false and 0 otherwise. The return value is a + b + c.
Each incremental factor is set so that the three factors cancel and sum to a single end-of-model soft constraint: the total log-weight is -10 when all three variables are false and 0 otherwise.
The exact posterior distribution over a + b + c under full enumeration.
answer spec
{
"kind": "dist",
"domain": "int"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var binomial = function(){2 var a = sample(Bernoulli({ p: 0.1 }))3 factor(a ? 0 : -1)4 var b = sample(Bernoulli({ p: 0.9 }))5 factor(((a||b)?0:-1) - (a?0:-1))6 var c = sample(Bernoulli({ p: 0.1 }))7 factor(((a||b||c) ? 0:-10) - ((a||b)?0:-1))8 return a + b + c9}1011var ANSWER = Infer({model: binomial, method: 'enumerate'});12
1# a ~ Bern(0.1), b ~ Bern(0.9), c ~ Bern(0.1) with successive soft factors that2# (telescoping) impose: factor 0 if a else -1; then 0 if (a|b) else -1; then 0 if (a|b|c) else -10.3# Net log-weight over the joint = (a?0:-1) + ((a|b)?0:-1)? -- the GT writes incremental deltas4# whose cumulative sum is: w(a,b,c) = (a||b||c ? 0 : -10) plus the earlier increments that telescope.5# Sum of the GT factors: [a?0:-1] + [((a|b)?0:-1)-(a?0:-1)] + [((a|b|c)?0:-10)-((a|b)?0:-1)]6# = (a|b|c) ? 0 : -10. So the effective condition is a single factor (a|b|c ? 0 : -10).7# Posterior over a+b+c via exact Pyro enumeration.89@pyro.infer.config_enumerate10def model():11 a = pyro.sample("a", dist.Bernoulli(0.1))12 f1 = torch.where(a.bool(), torch.tensor(0.0), torch.tensor(-1.0))13 pyro.factor("f_a", f1)14 b = pyro.sample("b", dist.Bernoulli(0.9))15 ab = a.bool() | b.bool()16 f2 = torch.where(ab, torch.tensor(0.0), torch.tensor(-1.0)) - f117 pyro.factor("f_b", f2)18 c = pyro.sample("c", dist.Bernoulli(0.1))19 abc = ab | c.bool()20 f3 = torch.where(abc, torch.tensor(0.0), torch.tensor(-10.0)) - torch.where(21 ab, torch.tensor(0.0), torch.tensor(-1.0)22 )23 pyro.factor("f_c", f3)24 return a, b, c2526serving = pyro.infer.infer_discrete(27 pyro.infer.config_enumerate(model), first_available_dim=-128)2930counts = Counter()31N = 600032for _ in range(N):33 a, b, c = serving()34 s = int(round(a.item() + b.item() + c.item()))35 counts[s] += 13637ANSWER = {k: v / N for k, v in counts.items()}38
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (w1) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0034 ≤ tol 0.0230 · floors 0.0115/0.0000 |
A hidden Markov model runs for 4 steps. The initial hidden state is false. Transition probabilities: if the current state is true, the next state is true with probability 0.9; if the current state is false, the next state is true with probability 0.1. Four observations are all true. Soft conditioning: a log-weight of 0 is applied when the sampled state matches the current observation, and -2 when it does not.
At each step a new hidden state is drawn from the transition distribution conditioned on the previous state. A soft factor is applied comparing the new state to the current observation. The result is the full sequence of states including the initial state.
The posterior distribution over 5-element sequences of hidden states [initial, step-1, step-2, step-3, step-4], enumerated exactly.
answer spec
{
"kind": "dist",
"domain": "finite",
"support": [
[
false,
false,
false,
false,
false
],
[
false,
false,
false,
false,
true
],
[
false,
false,
false,
true,
false
],
[
false,
false,
false,
true,
true
],
[
false,
false,
true,
false,
false
],
[
false,
false,
true,
false,
true
],
[
false,
false,
true,
true,
false
],
[
false,
false,
true,
true,
true
],
[
false,
true,
false,
false,
false
],
[
false,
true,
false,
false,
true
],
[
false,
true,
false,
true,
false
],
[
false,
true,
false,
true,
true
],
[
false,
true,
true,
false,
false
],
[
false,
true,
true,
false,
true
],
[
false,
true,
true,
true,
false
],
[
false,
true,
true,
true,
true
]
]
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var hmm = function(states, observations){2 var prevState = states[states.length - 1];3 var state = sample(Bernoulli({p: prevState ? .9 : .1}));4 factor((state == observations[0]) ? 0 : -2);5 if (observations.length == 0) {6 return states;7 } else {8 return hmm(states.concat([state]), observations.slice(1));9 }10}1112var observations = [true, true, true, true];13var startState = false;1415viz.table(Infer({16 model() {17 return hmm([startState], observations)18 }19}))2021var ANSWER = (Infer({ model() { return hmm([startState], observations) } }));22
1# HMM over 5 hidden states [start=false, s1, s2, s3, s4].2# Transition: p(state=true) = 0.9 if prev else 0.1.3# Soft observation: factor (state == observations[i]) ? 0 : -2, observations all true (4 of them).4# Joint posterior over the full 5-element state sequence via Pyro enumeration (infer_discrete).56observations = [True, True, True, True]7start_state = False89@pyro.infer.config_enumerate10def model():11 states = [start_state]12 for i in range(4):13 prev = states[-1]14 if prev is True:15 p_trans = torch.tensor(0.9)16 elif prev is False:17 p_trans = torch.tensor(0.1)18 else:19 p_trans = torch.where(prev.bool(), torch.tensor(0.9), torch.tensor(0.1))20 s = pyro.sample(f"s{i}", dist.Bernoulli(p_trans))21 obs_i = observations[i]22 match = s.bool() if obs_i else (~s.bool())23 f = torch.where(match, torch.tensor(0.0), torch.tensor(-2.0))24 pyro.factor(f"obs{i}", f)25 states.append(s)26 return states2728serving = pyro.infer.infer_discrete(29 pyro.infer.config_enumerate(model), first_available_dim=-130)3132counts = Counter()33N = 700034for _ in range(N):35 states = serving()36 seq = (False,) + tuple(bool(s.item() > 0.5) for s in states[1:])37 counts[seq] += 13839ANSWER = {seq: c / N for seq, c in counts.items()}40
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (tv) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0043 ≤ tol 0.0280 · floors 0.0140/0.0000 |
A 1-dimensional Gaussian random walk: the starting position is drawn from Gaussian(mean=200, sd=1). At each subsequent step the position is drawn from Gaussian(mean=previous position, sd=10). The walk runs for 5 steps total (1 initial position plus 4 transitions). The threshold is 200.
Each step the position evolves by adding Gaussian noise with standard deviation 10 to the previous position. The initial position is independently drawn from Gaussian(200, 1).
The marginal distribution over whether the position after 5 steps exceeds 200, estimated via 1000 forward samples.
answer spec
{
"kind": "dist",
"domain": "bool"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1///fold:2var drawLines = function(canvas, start, positions){3 if (positions.length == 0) { return []; }4 var next = positions[0];5 canvas.line(start[0], start[1], next[0], next[1], 4, 0.2);6 drawLines(canvas, next, positions.slice(1));7 return;8}910var last = function(xs){11 return xs[xs.length - 1];12}13///1415var init = function(dim){16 return repeat(dim, function(){ return gaussian(200, 1) });17}1819var transition = function(pos){20 return map(21 function(x){ return gaussian(x, 10); },22 pos23 );24};2526var gaussianRandomWalk = function(n, dim) {27 var prevStates = (n==1) ? [init(dim)] : gaussianRandomWalk(n-1, dim);28 var newState = transition(last(prevStates));29 return prevStates.concat([newState]);30};3132var positions = gaussianRandomWalk(100, 2);333435// Draw model output3637var canvas = Draw(400, 400, true)38drawLines(canvas, positions[0], positions.slice(1))3940var ANSWER = (Infer({ method: 'forward', samples: 1000, model: function(){ return last(gaussianRandomWalk(5, 1))[0] > 200; } }));41
1# Forward (prior) sampling of a 1-D Gaussian random walk for 5 steps.2# Initial position ~ Gaussian(200, 1); each transition adds Gaussian(0, 10) noise.3# Query: marginal over (position after 5 steps > 200), via 1000 forward samples.45def model():6 pos = pyro.sample("x0", dist.Normal(200.0, 1.0))7 for i in range(1, 5):8 pos = pyro.sample(f"x{i}", dist.Normal(pos, 10.0))9 return pos1011num_samples = 100012outcomes = []13for _ in range(num_samples):14 final = model()15 outcomes.append(bool(final.item() > 200.0))1617counts = Counter(outcomes)18ANSWER = {19 True: counts[True] / num_samples,20 False: counts[False] / num_samples,21}22
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0450 (tv) |
| solver re-derivation | accept | 2/2 solvers · d=[0.014, 0.014] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0140 ≤ tol 0.0900 · floors 0.0250/0.0450 |
A 1-dimensional semi-Markov random walk with momentum. The first two positions are each independently drawn from Gaussian(mean=200, sd=1). At each subsequent step the position is drawn from Gaussian(mean = previous + 0.7 * (previous - second-previous), sd=3). The walk runs for 6 steps total (2 initial positions plus 4 transitions). The threshold is 200.
Each step the position is drawn from a Gaussian whose mean incorporates a momentum term: 0.7 times the displacement from the second-to-last position to the last position is added to the last position to form the new mean. The standard deviation of each step is 3.
The marginal distribution over whether the position after 6 steps exceeds 200, estimated via 1000 forward samples.
answer spec
{
"kind": "dist",
"domain": "bool"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1///fold:2var drawLines = function(canvas, start, positions){3 if (positions.length == 0) { return []; }4 var next = positions[0];5 canvas.line(start[0], start[1], next[0], next[1], 4, 0.2);6 drawLines(canvas, next, positions.slice(1));7 return;8}9///1011var init = function(dim){12 return repeat(dim, function(){ return gaussian(200, 1) });13}1415var transition = function(lastPos, secondLastPos){16 return map2(17 function(lastX, secondLastX){18 var momentum = (lastX - secondLastX) * .7;19 return gaussian(lastX + momentum, 3);20 },21 lastPos,22 secondLastPos23 );24};2526var semiMarkovWalk = function(n, dim) {27 var prevStates = (n==2) ? [init(dim), init(dim)] : semiMarkovWalk(n-1, dim);28 var newState = transition(last(prevStates), secondLast(prevStates));29 return prevStates.concat([newState]);30};3132var positions = semiMarkovWalk(80, 2);333435// Draw model output3637var canvas = Draw(400, 400, true)38drawLines(canvas, positions[0], positions.slice(1))3940var ANSWER = (Infer({ method: 'forward', samples: 1000, model: function(){ return last(semiMarkovWalk(6, 1))[0] > 200; } }));41
12# 1-D semi-Markov walk with momentum. First two positions ~ Normal(200, 1).3# Each subsequent step: Normal(prev + 0.7*(prev - prev_prev), 3). 6 steps total4# (2 initial + 4 transitions). Forward (prior) sampling, 1000 samples.5def model():6 p_prev2 = pyro.sample("pos_0", dist.Normal(200.0, 1.0))7 p_prev1 = pyro.sample("pos_1", dist.Normal(200.0, 1.0))8 for step in range(4):9 momentum = 0.7 * (p_prev1 - p_prev2)10 new_pos = pyro.sample(f"pos_{step + 2}",11 dist.Normal(p_prev1 + momentum, 3.0))12 p_prev2 = p_prev113 p_prev1 = new_pos14 return bool((p_prev1 > 200.0).item())1516counts = Counter()17for _ in range(1000):18 tr = pyro.poutine.trace(model).get_trace()19 counts[tr.nodes["_RETURN"]["value"]] += 120total = sum(counts.values())21ANSWER = {True: counts[True] / total, False: counts[False] / total}22
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0540 (tv) |
| solver re-derivation | accept | 2/2 solvers · d=[0.018, 0.018] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0120 ≤ tol 0.1080 · floors 0.0280/0.0540 |
Three binary random variables a, b, c are drawn independently from Bernoulli(0.5). A log-weight of -1 is applied when both a and b are false (i.e., when a OR b is false); otherwise the log-weight is 0.
The three variables are sampled independently. A soft factor downweights executions where neither a nor b is true by a factor of exp(-1). The model returns the sum a + b + c.
The exact posterior distribution over a + b + c under full enumeration.
answer spec
{
"kind": "dist",
"domain": "int"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var skewBinomial = function(){2 var a = sample(Bernoulli({p: 0.5}))3 var b = sample(Bernoulli({p: 0.5}))4 var c = sample(Bernoulli({p: 0.5}))5 factor( (a|b)?0:-1 )6 return a + b + c7}89viz(Infer({ model: skewBinomial }))1011var ANSWER = (Infer({ model: skewBinomial }));12
1# a, b, c ~ Bernoulli(0.5) i.i.d.; soft factor (a|b) ? 0 : -1.2# Posterior over a+b+c via exact Pyro enumeration.34@pyro.infer.config_enumerate5def model():6 a = pyro.sample("a", dist.Bernoulli(0.5))7 b = pyro.sample("b", dist.Bernoulli(0.5))8 c = pyro.sample("c", dist.Bernoulli(0.5))9 ab = a.bool() | b.bool()10 pyro.factor("cond", torch.where(ab, torch.tensor(0.0), torch.tensor(-1.0)))11 return a, b, c1213serving = pyro.infer.infer_discrete(14 pyro.infer.config_enumerate(model), first_available_dim=-115)1617counts = Counter()18N = 600019for _ in range(N):20 a, b, c = serving()21 s = int(round(a.item() + b.item() + c.item()))22 counts[s] += 12324ANSWER = {k: v / N for k, v in counts.items()}25
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (w1) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0097 ≤ tol 0.0533 · floors 0.0267/0.0000 |
Three independent fair coins are drawn, each from a Bernoulli distribution with p=0.5. Call their values a, b, c. Executions in which (a OR b) is false (i.e. both a and b are zero) are downweighted by a factor of exp(-1); all other executions receive a factor of exp(0)=1.
The model samples three independent fair coin flips a, b, c. Soft conditioning via a factor reduces the probability mass of executions where both a and b are false by a factor of e^{-1} relative to executions where at least one of a or b is true. The quantity of interest is the sum a+b+c.
The posterior distribution over the integer-valued sum a+b+c (ranging from 0 to 3) after the soft conditioning.
answer spec
{
"kind": "dist",
"domain": "int"
}system prompt
(system prompt loads here)
webppl primer
(primer loads here)
1var skewBinomial = function(){2 var a = sample(Bernoulli({p: 0.5}))3 var b = sample(Bernoulli({p: 0.5}))4 var c = sample(Bernoulli({p: 0.5}))5 factor( (a|b)?0:-1 )6 return a + b + c7}89var ANSWER = Infer({ model: skewBinomial });
1# a, b, c ~ Bernoulli(0.5) i.i.d.; soft factor (a|b) ? 0 : -1.2# Posterior over the integer sum a+b+c (0..3) via exact Pyro enumeration.34@pyro.infer.config_enumerate5def model():6 a = pyro.sample("a", dist.Bernoulli(0.5))7 b = pyro.sample("b", dist.Bernoulli(0.5))8 c = pyro.sample("c", dist.Bernoulli(0.5))9 ab = a.bool() | b.bool()10 pyro.factor("cond", torch.where(ab, torch.tensor(0.0), torch.tensor(-1.0)))11 return a, b, c1213serving = pyro.infer.infer_discrete(14 pyro.infer.config_enumerate(model), first_available_dim=-115)1617counts = Counter()18N = 600019for _ in range(N):20 a, b, c = serving()21 s = int(round(a.item() + b.item() + c.item()))22 counts[s] += 12324ANSWER = {k: v / N for k, v in counts.items()}25
| check | status | evidence |
|---|---|---|
| GT self-consistency | ok | floor 0.0000 (w1) |
| solver re-derivation | accept | 2/2 solvers · d=[0.000, 0.000] · claude-sonnet-4-6 |
| cross-language (pyro vs webppl) | pass | d=0.0097 ≤ tol 0.0533 · floors 0.0267/0.0000 |