0

Right now I have a single choice predicate that defines my search space.

#const nRounds = 3.
#const nPlayers = 17.
#const nSeats = nRounds * nPlayers.
#const nRooms = 3.
#const nDecks = 6.

nSeats { seat(1..nPlayers, 1..nRooms, 1..nDecks) } nSeats.

I would like to restrict this search space as I'm starting to run into performance problems. In my set up each each player can only appear in 4 "seat" predicates so I would like something along the lines of:

#for i in 1..nPlayers
nRounds { seat(i, 1..nRooms, 1..nDecks) } nRounds.
#endfor

which would basically turn into something like this internally:

nRounds { seat(1, 1..nRooms, 1..nDecks) } nRounds.
nRounds { seat(2, 1..nRooms, 1..nDecks) } nRounds.
nRounds { seat(3, 1..nRooms, 1..nDecks) } nRounds.
...

I could, of course, "spell this out myself" i.e. use another language to generate these lines but I don't see why this wouldn't exist in clingo.

The reason I am looking for a way to do this in the first place is because I expect that (nRooms*nDecks choose nRounds) * nPlayers is going to be much smaller than (nRooms*nDecks*nPlayers) choose nRounds*nPlayers which is in turn much better than {seat(P, R, D)} with its 2^(nRooms*nDecks*nPlayers) which is what I started with.

Even with constraints in my code that restrict the actual possible sets I can end up with so that all three of these representations are equivalent, there are still performance benefits to gain from such manual search space pruning right?

rudolfovic
  • 3,163
  • 2
  • 14
  • 38

1 Answers1

1

The "for loop" you are looking for is a straight forward rule:

nRounds { seat(S, 1..nRooms, 1..nDecks) } nRounds :- S = 1..nPlayers.

Check the difference to your code with clingo --text

I would also advise you to seperate the seat(P,R,D) predicate into several smaller ones (depending on your problem. Like each player is assigned a room p2r(P,R) and every room is assigned to a deck r2d(R,D). This way you save a lot of unecessary combinations in your constraints later on. Of course my two predicate may not make sense for your problem but I'm sure you will find a combination to split your seat predicate.

Max Ostrowski
  • 575
  • 1
  • 3
  • 15
  • Brilliant, so I imagine you can do the same with sets e.g. `S = (a;b;c)`? There are multiple decks and each is played in multiple rooms so I don't think this could be simplified much further. Think quiz night - the decks could be Biology, Chemistry, Physics, each played in parallel in multiple rooms by different people. I could split `seat(P,R,D)` in to `seat(P, ID)` and `round(ID, R, D)` but I don't think that would make any difference as ultimately `|ID|` would equal `|R|*|D|`, right? – rudolfovic May 13 '21 at 13:10
  • Sure, just give it a try and test with `--text`. `round(1..5). player(1..5). deck(1..5). room(1..5). %Assign every player every round exactly one room 1{where(P,R,X) : room(X)} 1 :- player(P), round(R). %Assign every room every round exactly one deck 1{what(X,R,D) : deck(D)}1:- room(X), round(R).` – Max Ostrowski May 14 '21 at 09:38
  • Is there any difference between `{what(X,R,D): deck(D)}` and `{what(X,R,1..5)}`? – rudolfovic May 14 '21 at 14:24
  • No, just more reusable and readable. And of course deck can be names not only numbers. – Max Ostrowski May 14 '21 at 15:53