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

Gifs #185

Merged
merged 12 commits into from
Aug 7, 2024
Merged

Gifs #185

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ Look at our [documentation page](https://clinguin.readthedocs.io/en/latest/) to

Our **[examples folder](https://github.com/potassco/clinguin/tree/master/examples)** shows how to use the range of functionalities in different applications.

<img src="https://github.com/potassco/clinguin/blob/master/examples/angular/placement/out1.png?raw=true" height="150">
<img src="https://github.com/potassco/clinguin/blob/master/examples/angular/graph_coloring/out2.png?raw=true" height="150">
<img src="https://github.com/potassco/clinguin/blob/master/examples/angular/sudoku/out1.png?raw=true" height="150">
<img src="https://github.com/potassco/clinguin/blob/master/examples/angular/jobshop/out.png?raw=true" height="150">


### Extensions

***Integration with different applications***
Expand Down
2 changes: 1 addition & 1 deletion clinguin/parse_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ArgumentParser:
default_backend_exec_string = "from .server.application.backends import *"
default_frontend_exec_string = "from .client.presentation.frontends import *"

default_backend = "ClingoMultishotBackend"
default_backend = "ClingoBackend"
default_frontend = "AngularFrontend"

def __init__(self) -> None:
Expand Down
5 changes: 1 addition & 4 deletions clinguin/server/application/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@

# pylint: disable=cyclic-import
from clinguin.server.application.backends.clingo_backend import ClingoBackend
from clinguin.server.application.backends.clingo_multishot_backend import (
ClingoMultishotBackend,
)
from clinguin.server.application.backends.clingodl_backend import ClingoDLBackend
from clinguin.server.application.backends.clingraph_backend import ClingraphBackend
from clinguin.server.application.backends.explanation_backend import ExplanationBackend

__all__ = [
ClingoMultishotBackend.__name__,
ClingoBackend.__name__,
ClingraphBackend.__name__,
ExplanationBackend.__name__,
ClingoBackend.__name__,
Expand Down
217 changes: 209 additions & 8 deletions clinguin/server/application/backends/clingo_backend.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# pylint: disable=R0801
# pylint: disable=too-many-lines
"""
Module that contains the ClingoMultishotBackend.
Module that contains the ClingoBackend.
"""
import functools
import logging
Expand Down Expand Up @@ -71,7 +71,7 @@ def register_options(cls, parser):

@classmethod
def register_options(cls, parser):
ClingoMultishotBackend.register_options(parser)
ClingoBackend.register_options(parser)

parser.add_argument(
"--my-custom-option",
Expand Down Expand Up @@ -222,6 +222,8 @@ def _init_ds_constructors(self):
self._add_domain_state_constructor("_ds_model") # Keep after brave and cautious
self._add_domain_state_constructor("_ds_opt")
self._add_domain_state_constructor("_ds_unsat") # Keep after all solve calls
self._add_domain_state_constructor("_ds_assume")
self._add_domain_state_constructor("_ds_external")

def _init_command_line(self):
"""
Expand Down Expand Up @@ -450,6 +452,57 @@ def _add_atom(self, predicate_symbol):
if predicate_symbol not in self._atoms:
self._atoms.add(predicate_symbol)

def _set_external(self, symbol, name):
"""
Sets the external value of a symbol.

Args:
symbol (clingo.Symbol): The clingo symbol to be set
name (str): Either "true", "false" or "release"
"""
if name == "release":
self._logger.debug(domctl_log(f"ctl.release_external({symbol})"))
self._ctl.release_external(symbol)
self._externals["released"].add(symbol)

if symbol in self._externals["true"]:
self._externals["true"].remove(symbol)

if symbol in self._externals["false"]:
self._externals["false"].remove(symbol)

elif name == "true":
self._logger.debug(domctl_log(f"ctl.assign_external({symbol}, True)"))
self._ctl.assign_external(symbol, True)
self._externals["true"].add(symbol)

if symbol in self._externals["false"]:
self._externals["false"].remove(symbol)

elif name == "false":
self._logger.debug(domctl_log(f"ctl.assign_external({symbol}, False)"))
self._ctl.assign_external(symbol, False)
self._externals["false"].add(symbol)

if symbol in self._externals["true"]:
self._externals["true"].remove(symbol)

else:
raise ValueError(
f"Invalid external value {name}. Must be true, false or relase"
)

def _add_assumption(self, symbol, value: str = "true"):
"""
Adds an assumption to the list of assumptions.

Args:
symbol (clingo.Symbol): The clingo symbol to be added as a True assumption
value (true): The value of the assumption either "true" or "false" (Defaults to true)
"""
bool_val = value == "true"
self._assumptions.add((symbol, bool_val))

# ---------------------------------------------
# Solving
# ---------------------------------------------
Expand Down Expand Up @@ -750,6 +803,35 @@ def _ds_browsing(self):
prg += "_clinguin_browsing."
return prg + "\n"

@property
def _ds_assume(self):
"""
Adds information about the assumptions

Includes predicate ``_clinguin_assume/2`` for every atom that was assumed.
"""
prg = "#defined _clinguin_assume/2. "
for a, v in self._assumption_list:
v_str = "true" if v else "false"
prg += f"_clinguin_assume({str(a)},{v_str}). "
return prg + "\n"

@property
def _ds_external(self):
"""
Adds information about the external atoms

Includes predicate ``_clinguin_external/2`` for every external atom that has been set.
"""
prg = "#defined _clinguin_external/2. "
for a in self._externals["true"]:
prg += f"_clinguin_external({str(a)},true). "
for a in self._externals["false"]:
prg += f"_clinguin_external({str(a)},false). "
for a in self._externals["released"]:
prg += f"_clinguin_external({str(a)},release). "
return prg + "\n"

@property
def _ds_opt(self):
"""
Expand Down Expand Up @@ -972,18 +1054,137 @@ def next_solution(self, opt_mode="ignore"):
self._outdate()
self._messages.append(("Browsing Information", "No more solutions", "info"))

def select(self):
def clear_assumptions(self):
"""
Select the current solution during browsing.
All atoms in the solution are added as atoms in the backend.
Removes all assumptions.
"""
# pylint: disable=attribute-defined-outside-init
self._outdate()
self._assumptions = set()

def add_assumption(self, atom, value: str = "true"):
"""
Adds an atom `a` as an assumption.
If the value is True (which is the default), the atom is assumed to be true.
This assumption can be considered as an integrity constraint:
`:- not a.` forcing the program to entail the given atom.
If the value is False, the atom is assumed to be false:
This assumption can be considered as an integrity constraint:
`:- a.` forcing the program to never entail the given atom.

Arguments:

atom (str): The clingo symbol to be added as a true assumption
value (str): The value of the assumption either "true" or "false" (Defaults to true)
"""
atom_symbol = parse_term(atom)
if atom_symbol not in [a[0] for a in self._assumptions]:
self._add_assumption(atom_symbol, value)
self._outdate()

def remove_assumption(self, atom):
"""
Removes an atom from the assumptions list regardless of its value.

Arguments:
atom (str): The clingo symbol to be removed
"""
atom_symbol = parse_term(atom)
for a, v in self._assumptions:
if a == atom_symbol:
self._assumptions.remove((a, v))
self._outdate()
return

def remove_assumption_signature(self, atom):
"""
Removes from the list of assumptions those matching the given atom.
Unlike function remove_assumption, this one allows for partial matches using the
placeholder constant `any`

Arguments:

atom (str): The atom description as a symbol,
where the reserver word `any` is used to state that anything can
take part of that position. For instance, `person(anna,any)`,
will remove all assumptions of atom person, where the first argument is anna.
"""
atom_symbol = parse_term(atom)
arity = len(atom_symbol.arguments)
to_remove = []
for s, v in self._assumptions:
if s.match(atom_symbol.name, arity):
for i, a in enumerate(atom_symbol.arguments):
if str(a) != "any" and s.arguments[i] != a:
break
else:
to_remove.append((s, v))
continue
for a in to_remove:
self._assumptions.remove(a)
if len(to_remove) > 0:
self._outdate()

def set_external(self, atom, value):
"""
Sets the value of an external. Externals must be defined in the domain files using `#external`.
The truth value of external atoms can then be provided by the user via this function.

Arguments:

atom (str): The clingo symbol to be set
value (str): The value (release, true or false)
"""
symbol = parse_term(atom)
name = value
self._outdate()

self._set_external(symbol, name)

def select(self, show_prg: str = ""):
"""
Select the current solution during browsing.
All atoms in the solution are added as assumptions in the backend.

Arguments:

show_program (str): An optional show program to filter atoms

"""
if self._model is None:
self._messages.append(
"No solution", "There is no solution to be selected", "danger"
("No solution", "There is no solution to be selected", "danger")
)
for s in self._model: # pylint: disable=E1133
self._add_atom(s)
self._logger.error(
"No solution. No model has been computed that can be selected"
)
else:
symbols_to_ignore = self._externals["true"]
symbols_to_ignore.union(self._externals["false"])
if show_prg == "":
model = self._model
else:
model = []
ctl = Control(["--warn=none"])
try:
ctl.add("base", [], show_prg.strip('"'))
except RuntimeError as exc:
raise Exception(
"Show program can't be parsed. Make sure it is a valid clingo program."
) from exc
prg = "\n".join([f"{str(s)}." for s in self._model])
ctl.add("base", [], prg)
ctl.ground([("base", [])])

def add_shown(m):
for s in m.symbols(shown=True):
model.append(s)

ctl.solve(on_model=add_shown)
for s in model: # pylint: disable=E1133
if s not in symbols_to_ignore:
self._add_assumption(s)
self._outdate()

def stop_browsing(self):
"""
Expand Down
Loading
Loading