potassco / clingo

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

Segv with add_atom and ground() multiple times, with Script, on M1 #488

Closed ejgroene closed 6 months ago

ejgroene commented 7 months ago

Hi,

Attached is a small program that segfaults 1 out of a handful of times.

It uses Python and clingo 3.7.1 to embed a Script in ASP. It is reduced to the minimum that still reproduces the problem quickly. It is the combination of running control.ground() multiple times AND adding atoms to the backend. Running once never crashes, even when creating atoms. Not creating atoms while running multiple times als does not crash. It is really the combination.

When it runs correctly, it gives me the expected result, namely the atom bool('VTL-BOOL') in the result, and I can refer to that atom in the ASP code that follows after the VTL-script (removed for clarity).

But it gives a segmentation fault quite regularly, depending on how often the loops below are executed.

Since I cannot attach Python code, I'll include it here:

import clingo
import clingo.script

assert clingo.version() == (5,7,1)

class VtlScript:

    def __init__(self, backend):
        self.backend = backend

    def execute(self, location, code):
        for i in range(100):                                      # <= 1 is enough for segv, increase this to get segv really fast
            self.backend.add_atom(clingo.Function('VTL-BOOL'))  # <= without this, no segv

def run_vtl_asp(program):
    control = clingo.Control()
    with control.backend() as backend:
        vtls = VtlScript(backend)
        clingo.script.register_script('vtl', vtls)
        control.add(program)
        control.ground()

for i in range(10):                                             # <= with 1 run, no segv
    print("run", i)
    run_vtl_asp(""" #script (vtl) my vtl script #end. """)

Since it is hopelessly complicated to get a core dump on an M1 and it is also hopelessly complicated to get any debugger to work, I cannot give you an stacktrace. I tried but failed. I was hoping someone has such an environment ready to test this out.

I run Python 3.11 with Clingo 3.7.1, both installed via miniconda. MacBook Pro 2021 M1 Max 32 GB Sonoma 14.2.1 (latest)

Any help would be appreciated. Erik

rkaminsk commented 7 months ago

Hi, the program is not correct. The backend has to be closed before grounding; it cannot be used while grounding. Segfaults are possible here.

rkaminsk commented 7 months ago

You could use something like this. In principle it is possible to use the control object in the execute function of the script. However, currently there is no nice way to pass it there.

import clingo
import clingo.script

class VtlScript:
    def __init__(self):
        self.control = None

    def execute(self, location, code):
        with self.control.backend() as backend:
            for i in range(10):
                atm = backend.add_atom(clingo.Function('vtl', [clingo.Number(i)]))  # <= without this, no segv
                backend.add_rule([atm], [])
        self.control.ground()
        self.control.solve(on_model=print)

# a script should be registered only once
vtls = VtlScript()
clingo.script.register_script('vtl', vtls)

def run_vtl_asp(program):
    control = clingo.Control()
    # set the control object of the script
    # this has to be done with care, for example, there should be only one thread
    # the script interface could be extended to pass the control object at hand to execute
    # to make this use case safe
    vtls.control = control
    control.add(program)

for i in range(10):                                             # <= with 1 run, no segv
    print("run", i)
    run_vtl_asp(""" #script (vtl) my vtl script #end. """)
ejgroene commented 7 months ago

Hi Roland,

Thank you for your quick reply. I begin to understand the various objects a bit better. Is there an overview somewhere that I missed, so I can read up?

[EDIT: I got it working now!]

I am trying to create a cross-over language in which logic can be expressed in VTL (railway oriented) or ASP. The idea is to provide a migration path to gradually move from VTL to ASP. Meanwhile, the result must be output in VTL so it can be compiled and fed to the controllers, and it must be output in ASP, to be able to apply formal methods and unit tests.

I see now that all scripts are executed first, in control.add(). That forces me to reconsider my approach, since VTL depends on order. For example: a = a * b in VTL means a(T) :- a(T-1), b in ASP.

As for this issue: I think it can be closed. Although later on, it could be that I will contribute a patch to give meaningful error messages instead of segv. I am quite good at C++ and Python, but not yet in ASP, so don't expect it soon.

As for tips on my approach to the cross-over langue, that is possible better sought elsewhere?

Best regards, Erik

rkaminsk commented 7 months ago

A script is intended to be called while grounding. Maybe it is not even the right choice for your problem. A translated VTL program could be added directly using Control.add or the ast module.

If you just want to pass your VTL program via the script tag, you could use the ast module to extract the tag and keep the rest of the program unchanged. Afterward, you can add a translation to the program.

Unfortunately, there are is no comprehensive user guide of the API. However, there is some info available in the our papers and the examples:

ejgroene commented 6 months ago

Roland,

Thank you for the paper. Its is an interesting read. I have updated my plans. But first a short holiday!

Erik

ejgroene commented 6 months ago

Roland, thank you for you answers. The article was an insightful read. I decided to take another approach to this problem, as the other programming language has order in it, which I lose in ASP and representing it would require me to bing each and every atom into the time domain, which is a bit to cumbersome for every day use. So I decided to make a hook in the other language and allow some ASP to be dropped into it. The other way around actually. Thanks so far!