ML-KULeuven / problog

ProbLog is a Probabilistic Logic Programming Language for logic programs with probabilities.
https://dtai.cs.kuleuven.be/problog/
297 stars 34 forks source link

Uniform with n outcomes #71

Closed nathanielvirgo closed 2 years ago

nathanielvirgo commented 2 years ago

[note: this is a question rather than an issue; the project web page seems to say that's ok. If there's a mailing list etc. where I can ask instead, please let me know.]

This is probably a basic Prolog question rather than anything specific to ProbLog, but I'd like to have a random variable that's uniformly distributed over n outcomes. I've figured out that I can write

1/5::w(S) :- between(1,5,S).

which gives me a uniform distribution over 5 outcomes. However, I can't figure out how to make that "5" into a global constant. I know that Prolog doesn't have variable assignment, but my knowledge is too basic to understand what I should do instead. What would be the usual way of achieving this?

nathanielvirgo commented 2 years ago

Oh, actually the code in my previous post doesn't work as expected - w(S)=1/5 for each S between 0 and 5, but they are not mutually exclusive!

So I guess I have two questions: (i) how can I get a uniform distribution over 5 outcomes without typing "1/5" five times, and (ii) how can I replace the magic number 5 with some kind of declared constant?

A closely related question would be, what if I want to make a tunable distribution over n outcomes? That is, how can I replace the five-fold repetition with some "n" in the following code:

t(_)::w(1); t(_)::w(2); t(_)::w(3); t(_)::w(4); t(_)::w(5).
wannesm commented 2 years ago

For your first question there are two directions. The first is not really uniform selection but introduces a variable in the rule you wrote:

1/Max::w(S) :- Max=5, between(1,Max,S).

The problem here is that w(1) and w(2) can be true at the same time. So it's not a uniform distributions over the values. You thus probably you want to remember the choice you made and select just one value (thus build an annotated disjunction dynamically), in that case you can use the select_uniform (or select_weighted) from the lists library:

:- use_module(library(lists)).

random_uniform_value(ID, Min, Max, Value) :-
    findall(X, between(Min,Max,X), L),
    select_uniform(ID, L, Value, _Rest).

w(Value) :- random_uniform_value(w, 1, 5, Value).

query(w(Value)).

https://dtai.cs.kuleuven.be/problog/editor.html#task=prob&hash=a4fba6fbb46b544cf90b153f5ce67eeb

nathanielvirgo commented 2 years ago

Thanks @wannesm, that's really helpful - it's exactly what I wanted regarding a uniform distribution.

I'm still really hopeful that there's a way to do the weighted version, the equivalent of t(_)::w(1); t(_)::w(2); t(_)::w(3); t(_)::w(4); t(_)::w(5)..

rmanhaeve commented 2 years ago

There's also the weighted version of the select_uniform predicate: select_weighted(+ID, +Weights, +Values, ?Value, ?Rest)