potassco / clingo

🤔 A grounder and solver for logic programs.
https://potassco.org/clingo
MIT License
606 stars 81 forks source link

How to get atom numbers for symbols associated with facts in the Python Observer? #493

Closed ejgroene closed 5 months ago

ejgroene commented 5 months ago

L.S.

I want to translate the ground form of ASP to boolean logic for an PLC system.

For that, I need to lookup the symbols associated with facts using the atoms (numbers) passed to rule().

However, the Python API does not give me those:

  1. The Observer added to the Control in Python gets called with output_atom(symbol, atom), but for facts the atom is always zero.

  2. The rule(choice, head, body) method gets called and the head contains an atom (number) that refers to a symbol. However, this symbol is unknown to the Python application because of 1.

  3. No other methods are called on the Observer (as I made sure by a catchall deliberatly raising an exception).

  4. Neither the Control nor the default Backend seem to have Symbols for facts.

My current solution is to trace the atoms of the facts by having two separate lists for them (complete code attached as .txt):

 13         self.symbols = {}
 15         self.fact_atoms = []
 16         self.fact_symbols = []

In rule() I add the symbols:

 24     def rule(self, choice, head, body):
 25         if not body:
 26             self.fact_atoms.append(head[0])
 27         else:
 28             self.rules[head[0]] = body

In output_atom() I add the atoms:

 33     def output_atom(self, symbol, atom):
 34         if atom == 0:
 35             self.fact_symbols.append(symbol)
 36         else:
 37             self.symbols[atom] = symbol

And then I make the symbol table complete by zipping the symbols and the atoms:

 39     def resolve_facts(self):
 40         self.symbols.update(zip(self.fact_atoms, self.fact_symbols))

This works well, up till now, also with externals, which seem to influence the numbering in such a way that a simple counter to track the atoms does not work anymore.

Why is the API not giving the atoms used for facts? Is the approach above solid or can it break at any time? Is there a better way to get the atoms for facts?

Thank you in advance! Erik

ejgroene commented 5 months ago

I just noticed that I simplified the use case in the attachment too much so it doesn't show the problem anymore. I prepare a new one.

rkaminsk commented 5 months ago

The different ways to achieve what you want.

  1. You can preserve the numeric values in the output: https://github.com/potassco/clingo/pull/457.
  2. You can use the symbolic atoms of the control object to get the program literals of symbol atoms: https://potassco.org/clingo/python-api/5.7/clingo/symbolic_atoms.html#clingo.symbolic_atoms.SymbolicAtom.literal.
ejgroene commented 5 months ago

Thank you!

I tried all the options for preserving atoms/symbols, but it did not make any difference. I also found that for conditional literals, the grounder passes a rule with a head for which no symbol exists. Some sort of anonymous rule that I now duly use for substitution, and that seems to make the problem go away.

Regarding the SymbolicAtoms, I see that I don't have to maintain the symbols myself, so that comes in handy. Is that actually being the point of SymbolicAtoms? Just to have symbols bound to atom numbers? Or is there more to it?

Erik

rkaminsk commented 5 months ago

The symbol table is something stored by the system and the API provides access to it. You can inspect what information the system has about an atom. For example, whether an atom is a fact or external or its numeric id.

Note that conditional literals are not the only place where the system introduces program atoms that are not associated with symbols. Aggregates, minimize directives, and also show statements can lead to unnamed program atoms.

ejgroene commented 5 months ago

Thank you Roland, this really helped me. This particular problem is gone now.