Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve support for writing checkpoints in aspif format #530

Merged
merged 6 commits into from
Dec 11, 2024

Conversation

rkaminsk
Copy link
Member

This PR adds support to write checkpoints in aspif format, which can afterward be read again using a dedicated function.

from clingo.control import Control, BackendType

# create cp1
ctl = Control()
ctl.register_backend(BackendType.Aspif, "cp1.aspif", True)
ctl.add("1 {a; b} 1.")
ctl.ground()
ctl.solve()

# create cp2
ctl = Control()
# can alternatively use load_aspif
ctl.load("cp1.aspif")
# the backend is deliberately registered later here to avoid dumping cp1 again
ctl.register_backend(BackendType.Aspif, "cp2.aspif", True)
ctl.add("c :- a. d :- b.")
ctl.ground()
ctl.solve()

# load cp1 and cp2 combined
ctl = Control(["0"])
# function load cannot be used here
ctl.load_aspif(["cp1.aspif", "cp2.aspif"])
print(ctl.solve(on_model=print))

The output is

a c
b d
SAT

@teiesti and @thojat, let me know what you think of this...

@rkaminsk rkaminsk added this to the v5.7.2 milestone Dec 10, 2024
@rkaminsk rkaminsk self-assigned this Dec 10, 2024
@@ -163,6 +163,17 @@ struct IncrementalControl : Control, private Output::ASPIFOutBackend {
parser.pushFile(std::string(filename), logger_);
parse();
}
void load_aspif(Potassco::Span<char const *> files) override {
for (auto it = end(files), ib = begin(files); it != ib; --it) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: this (mostly) duplicated code is inconsistent regarding the use of begin/end. At least, I'd suggest dropping the (unnecessary) using declarations from the previous version.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not compile without the using declarations. I admit, I did not check why.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The duplication will stay there for clingo series 5 but it will not stay like this in the upcoming 6.)

auto backend = UBackend{}; //Output::make_backend(out, OutputFormat::reify, false, false);
switch (type & 0xFFFFFFFC) {
case clingo_backend_type_e::clingo_backend_type_reify: {
backend = Output::make_backend(std::move(out), Output::OutputFormat::REIFY, (type & 1) == 1, (type &2) == 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: inconsistent spacing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't even see where the inconsistent spacing is anymore. In another project I switched to 100% relying on clang-format. I should setup clang-format at some point but will leave it out of this PR.

libclingo/clingo.h Outdated Show resolved Hide resolved
@thojat
Copy link

thojat commented Dec 11, 2024

I think this adds a lot of convenience for reusing already grounded programs. In the future this feature might also become an easy way to retract programs added with multishot solving, e.g.,

ctl.ground([("base", [])], checkpoint=checkpoint1”)
ctl.ground([(“p1”, [])], checkpoint=checkpoint2”)
ctl.solve()
ctl.restore(checkpoint=checkpoint1”)

As far as I understand the backend is basically an observer that generates the aspif file during grounding, right? Do you think in the future it might be possible to also store the nogoods learned during solving?

@rkaminsk
Copy link
Member Author

I think this adds a lot of convenience for reusing already grounded programs. In the future this feature might also become an easy way to retract programs added with multishot solving, e.g.,

ctl.ground([("base", [])], checkpoint=checkpoint1”)
ctl.ground([(“p1”, [])], checkpoint=checkpoint2”)
ctl.solve()
ctl.restore(checkpoint=checkpoint1”)

Restoring the same control object to a previous state is difficult. Especially, if there shall be guarantees regarding determinism, propagators, etc. The idea here was to at least make writing and loading aspif easy.

As far as I understand the backend is basically an observer that generates the aspif file during grounding, right?

Yes, it uses the same classes that clingo uses internally.

Do you think in the future it might be possible to also store the nogoods learned during solving?

To some extend it is already possible to store/restore learned nogoods. There are clasp's lemma-in and lemma-out options. However, this has to be used with care. The options can only be used if the underlying pogram is exactly the same.

@rkaminsk
Copy link
Member Author

If you want perfect checkpoint/restore functionality, you might want to consider https://criu.org. It should run on any linux system.

@rkaminsk
Copy link
Member Author

Going to merge. In case that further refinements are necessary let's just open a new issue.

@rkaminsk rkaminsk merged commit e3b4e9a into wip Dec 11, 2024
3 of 4 checks passed
@rkaminsk rkaminsk deleted the feature/checkpoint branch December 11, 2024 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants