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.

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.

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")).

Warning

  • If the operation impacts the current browsing state, make sure to call the _outdate method.

  • If the operation makes the current control outdated (for instance, adds an atom or a program), make sure to call the _init_ctl method.

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.

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._init_ctl()[source]#

Creates the domain control and loads the domain files.

ClingoBackend._create_ctl()[source]#

Initializes the control object (domain-control). It is used when the server is started or after a restart.

See also

_load_file

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

_load_file

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

ClingoBackend._outdate()[source]#

Outdates all the dynamic values when a change has been made. Any current interaction in the models wil be terminated by canceling the search and removing the iterator.

See also

_clear_cache

Solving#

These methods are involved in how the domain control is solved. They can be overwritten for things such as theory extensions.

ClingoBackend._ground(program='base', arguments=None)[source]#

Grounds the provided program

Parameters
  • program (str) – The name of the program to ground (defaults to β€œbase”)

  • arguments (list, optional) – The list of arguments to ground the program. Defaults to an empty list.

ClingoBackend._prepare()[source]#

Does any preparation before a solve call.

ClingoBackend._on_model(model)[source]#

This method is called each time a model is obtained by the domain control. It sets the model, and optimization attributes. It can be extended to add custom features of the model.

Parameters

model (clingo.Model) – The found clingo model

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

ClingoBackend._set_external(symbol, name)[source]#

Sets the external value of a symbol.

Parameters
  • symbol (clingo.Symbol) – The clingo symbol to be set

  • name (str) – Either β€œtrue”, β€œfalse” or β€œrelease”

ClingoBackend._add_assumption(symbol, value='true')[source]#

Adds an assumption to the list of assumptions.

Parameters
  • symbol (clingo.Symbol) – The clingo symbol to be added as a True assumption

  • value (true) – The value of the assumption either β€œtrue” or β€œfalse”

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.

ClingoBackend._update_ui_state()[source]#

Updates the UI state by calling all domain state methods and creating a new control object (ui_control) using the UI files provided

class UIState[source]#

The UIState is the low-level-access-class for handling the facts defining the UI state