Creating your own backend#
By creating your own backend you can extend functionality and edit the existing server workflow. If you are using clingo, we highly recommend extending the ClingoBackend to create your own. This backend contains multiple functionalities already built in which can be overwritten and extended. The following explanation assumes that this is the backend that is being extended.
Code Structure#
Your code structure with your custom backend must be the following:
my_project/
βββ my_backend.py
βββ ui.lp
βββ encoding.lp
βββ instance.lp
my_backend.py
: Contains the backend class that extends the ClingoBackend.
from clinguin.server.application.backends import ClingoBackend
class MyBackend(ClingoBackend):
...
ui.lp
: Contains the UI encoding.encoding.lp
: Contains the domain encoding.instance.lp
: Contains the instance.
Using this structure, the command line, standing inside the folder my_project would be the following:
$ clinguin client-server --domain-files instance.lp encoding.lp --ui-files ui.lp --custom-classes my_backend.py --backend MyBackend
Tip
You can make sure your backend class is available by checking the help using -h
with your custom classes:
$ clinguin client-server --domain-files instance.lp encoding.lp --ui-files ui.lp --custom-classes my_backend.py -h
Note
Using Your Backend
To make your custom backend available to Clinguin, you must provide the path via the command line argument --custom-classes
.
Customizations#
In what follows, we divide the possible extensions for explainability. For more implementation details, look at the source code.
- class ClingoBackend[source]#
This backend contains the basic clingo functionality for a backend using clingo.
- class ClingoBackend[source]#
Bases:
object
This backend contains the basic clingo functionality for a backend using clingo.
Register Options#
By overriding this class method, one can add new arguments to the command line.
These options will be added under a group for the created backend. The value can be set in _init_command_line
.
Examples
- classmethod ClingoBackend.register_options(parser)[source]#
Registers options in the command line.
It can be extended by custom backends to add custom command-line options.
- Parameters
parser (ArgumentParser) β A group of the argparse argument parser
Example
@classmethod def register_options(cls, parser): ClingoBackend.register_options(parser) parser.add_argument( "--my-custom-option", help="Help message", nargs="*", )
Public Operations#
Each backend can define any number of public operations or override the existing ones. These operations are any public method of the class and will be accessible to the UI.
Examples
Example
This example shows how to add a new public operation in a backend
class MyBackend(ClingoBackend):
...
def my_operation(self, arg1, arg2) -> None:
# Do something using the given arguments
# Does not return anything
Use it in your ui-files like this:
when(button1, click, call, my_operation("arg1", "arg2")).
Initialization#
These methods will handle the arguments depending on the current state of the interaction. Some are called at the start after a restart or when a change is done in the solving.
- ClingoBackend._restart()[source]#
Restarts the backend by setting all attributes, initializing controls and grounding. It is automatically called when the server starts.
See also
_init_command_line
,_init_interactive
,_outdate
,_init_ctl
,_ground
- ClingoBackend._init_ds_constructors()[source]#
This method initializes the domain state constructors list and the backup cache dictionary. It also adds the default domain state constructors to the list. This method is called only when the server starts.
- Variables
_domain_state_constructors (list) β A list to store the domain state constructors.
_backup_ds_cache (dict) β A dictionary to store the backup domain state cache.
It can be extended by custom backends to add/edit domain state constructors. Adding a domain state constructor should be done by calling
_add_domain_state_constructor
.Example
@property def _ds_my_custom_constructor(self): # Creates custom program return "my_custom_program." def _init_ds_constructors(self): super()._init_ds_constructors() self._add_domain_state_constructor("_ds_my_custom_constructor")
- ClingoBackend._init_command_line()[source]#
Initializes the attributes based on the command-line arguments provided. This method is called when the server starts or after a restart.
- Variables
_domain_files (list) β The list of domain files provided via command line.
_ui_files (list) β The list of UI files provided via command line.
_constants (dict) β The dictionary of constants provided via command line.
_clingo_ctl_arg (list) β The list of clingo control arguments provided via command line.
If any command line arguments are added in
register_options
, they should be initialized here.Example
def _init_command_line(self): super()._init_command_line() self._my_custom_attr = self._args.my_custom_option
- ClingoBackend._init_interactive()[source]#
Initializes the attributes that will change during the interaction. This method is called when the server starts or after a restart.
- Variables
_context (list) β A list to store the context set by the general handler of requests.
_handler (clingo.SolveHandle) β The handler set while browsing in the next_solution operation.
_iterator (iter) β The iterator set while browsing in the next_solution operation.
_ctl (clingo.Control) β The domain control set in _init_ctl.
_ui_state (
UIState
) β A UIState object used to handle the UI construction,_update_ui_state. (set in every call to) β
_atoms (set[str]) β A set to store the atoms set dynamically in operations during the interaction.
_assumptions (set[(str,bool)]) β A set to store the assumptions set dynamically in operations during the
interaction. (the UI, set dynamically in operations during the) β
_externals (dict) β A dictionary with true, false and released sets of external atoms
_model (list[clingo.Symbol]) β The model set in on_model.
_unsat_core (list[int]) β The unsatisfiable core set in on_model.
_cost (list) β A list to store the cost set in on_model.
_optimal (bool) β A boolean indicating if the solution is optimal, set in on_model.
_optimizing (bool) β A boolean indicating if the solver is currently optimizing, set in on_model.
_messages (list[tuple[str,str,str]]) β A list to store the messages (title, content, type) to be shown in
interaction. β
- ClingoBackend._create_ctl()[source]#
Initializes the control object (domain-control). It is used when the server is started or after a restart.
See also
- ClingoBackend._load_and_add()[source]#
Loads domain files and atoms into the control.
This method iterates over the domain files and atoms specified in the instance and loads them into the control. It raises an exception if a domain file does not exist or if there is a syntax error in the logic program file.
- Raises
Exception β If a domain file does not exist or if there is a syntax error in the logic program file.
See also
- ClingoBackend._load_file(f)[source]#
Loads a file into the control. This method can be overwritten if any pre-processing is needed.
- Parameters
f (str) β The file path
Solving#
These methods are involved in how the domain control is solved. They can be overwritten for things such as theory extensions.
Examples
Setters#
These methods will set different attributes of the backend.
- ClingoBackend._add_domain_state_constructor(method)[source]#
Adds a method name to the domain constructors. The provided method needs to be annotated with
@property
or@cached_property
- Parameters
method (str) β Name of the property method
- ClingoBackend._set_context(context)[source]#
Sets the context. Used by general endpoint handler after a request.
- Parameters
context β The context dictionary
- ClingoBackend._set_constant(name, value)[source]#
Sets a constant in the backend and restarts the control.
- Parameters
name (str) β name of the constant
value (Any) β value of the constant
- ClingoBackend._add_atom(predicate_symbol)[source]#
Adds an atom if it hasnβt been already added
- Parameters
predicate_symbol (clingo.Symbool) β The symbol for the atom
Domain State#
The domain state constructors take care of generating the domain-state. When new information wants to be added, a domain state constructor can be included. These domain constructors will be automatically called by the _domain_state property. But, they need to be previously registered in the constructor using the functions below.
Example
This example shows how to add a custom domain constructor
@property
def _ds_my_custom_constructor(self):
# Creates custom program
return "my_custom_program."
def _init_ds_constructors(self):
super()._init_ds_constructors()
self._add_domain_state_constructor("_ds_my_custom_constructor")
Warning
Make sure any domain constructor added is a property with annotation @property
or @cache_property
if the computation is costly.
Warning
Some domain constructors will be skipped if they are not used in the ui-files. This can be done in a custom method domain_constructor using the following code:
@cache_property
def _ds_my_custom_complex_constructor(self):
# Creates custom program
if not self._ui_uses_predicate("_my_ds_predicate", 1):
return "% NOT USED\n"
return "my_custom_program."
Note
Domain state constructors for this backend are shown in the ClingoBackend and ClingoBackend sections. These constructors can also be overwritten if necessary.
UI Updates#
If any changes want to be made in how the UI state is computed, they can be made by overriding this method.
Examples