API Documentation
The heart of the Clorm ORM involves defining the mapping from ground predicates to member variables of a Python object. There are two aspects to this: fields, that define how logical terms are mapped to Python objects, and predicate definitions, that define predicate and function names, their arities and the appropriate field for each of the parameters.
Fields
Fields provide a specification for how to convert clingo.Symbol
objects into the
appropriate/intuitive Python object. As of Clorm version 1.5, the preferred mechanism to
specify fields is to use standard Python type annotations. The primitive logical terms
integer and string are specified using the standard Python type int
and str
respectively, while logical constant terms are specified using a specially defined type:
- class clorm.ConstantStr
A special
str
sub-class for specifying constant logical terms.
Internally within Clorm the type specifiers are mapped to a set of special classes that contain
the functions for converting between Python and the clingo.Symbol
objects. These special
classes are referred to as field definitions and are subclassed from a common base class
BaseField
.
- class clorm.BaseField(default: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>, index: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>)
Define a mapping from ASP logical terms to Python objects.
A field is used as part of a
ComplexTerm
orPredicate
definition. It specifies the translation between ASP terms and Python objects.It contains two class functions
cltopy
andpytocl
that implement the translation from Clingo to Python and Python to Clingo respectively. ForBaseField
these functions are abstract.StringField
,IntegerField
, andConstantField
are standard sub-classes that provide translations for the ASP simple terms; string, integer and constant.These
BaseField
subclasses can be further sub-classes to build a chain of translations.To sub-class BaseField (or one of its sub-classes) simply specify
cltopy
andpytocl
functions that take an input and perform some translation to an output format.RawField
is a specialBaseField
sub-class that provides direct pass-through for raw clingo symbol objects. It cannot be sub-classed further.Note: the
cltopy
andpytocl
functions are legitmately allowed to throw either aTypeError
orValueError
exception when provided with bad input. These exceptions will be treated as a failure to unify when trying to unify clingo symbols to facts. However, any other exception is passed through as a genuine error. This should be kept in mind if you are writing your own field class.Example
import datetime class DateField(StringField): pytocl = lambda dt: dt.strftime("%Y%m%d") cltopy = lambda s: datetime.datetime.strptime(s,"%Y%m%d").date()
Because
DateField
sub-classesStringField
, rather than sub-classingBaseField
directly, it forms a longer data translation chain:clingo symbol object – BaseField – StringField – DateField – python date object
Here the
DateField.cltopy
is called at the end of the chain of translations, so it expects a Python string object as input and outputs a date object.DateField.pytocl
does the opposite and inputs a date object and is expected to output a Python string object.- Parameters:
default – A default value (or function) to be used when instantiating a
Predicate
orComplexTerm
object. If a Pythoncallable
object is specified (i.e., a function or functor) then it will be called (with no arguments) when the predicate/complex-term object is instantiated.index (bool) – Determine if this field should be indexed by default in a
FactBase`
. Defaults toFalse
.
- abstract static cltopy(v)
Called when translating data from Clingo to Python
- abstract static pytocl(v)
Called when translating data from Python to Clingo
- property default
Returns the default value for the field (or
None
if no default was set).Note: 1) if a function was specified as the default then testing
default
will call this function and return the value, 2) if your BaseField sub-class allows a default value ofNone
then you need to check thehas_default
property to distinguish between no default value and aNone
default value.
- property has_default
Returns whether a default value has been set
- property has_default_factory
Returns whether a default value has been set
- property index
Returns whether this field should be indexed by default in a FactBase
For sub-classes of BaseField
the abstract member functions
cltopy()
and pytocl()
must be implemented
to provide the conversion from Clingo to Python and Python to Clingo (respectively).
Clorm provides three standard sub-classes corresponding to the string, constant, and integer
primitive logical terms: StringField
, ConstantField
, and
IntegerField
.
- class clorm.StringField(default: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>, index: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>)
A field to convert between a Clingo.String object and a Python string.
- class clorm.ConstantField(default: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>, index: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>)
A field to convert between a simple
Clingo.Function
object and a Python string.Note: currently
ConstantField
treats a string with a starting “-” as a negated constant. In hindsight this was a mistake and is now deprecated. While I don’t think anyone actually used this functionality (since it was never documented) nevertheless I will keep it there until the Clorm version 2.0 release.
- class clorm.IntegerField(default: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>, index: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>)
A field to convert between a Clingo.Number object and a Python integer.
- clorm.field(basefield: Type[BaseField] | Tuple[_FieldDefinition, ...]) Any
- clorm.field(basefield: Type[BaseField] | Tuple[_FieldDefinition, ...], *, default: _T) _T
- clorm.field(basefield: Type[BaseField] | Tuple[_FieldDefinition, ...], *, default: _T, kw_only: bool) _T
- clorm.field(basefield: Type[BaseField] | Tuple[_FieldDefinition, ...], *, default_factory: Callable[[], _T]) _T
Return a field definition.
This function is used to override the type annotation field specification. A different field can be specificied as well as default values.
This function operates in a similar way to
dataclass.field()
in that it allows for more field specification options.
Special Fields
Clorm provides a number of fields for some special cases. The SimpleField
can
be used to define a field that matches to any primitive type. Note, however that special care
should be taken when using SimpleField
. While it is easy to disambiguate the
Clingo to Python direction of the translation, it is harder to disambiguate between a string
and constant when converting from Python to Clingo, since strings and constants are both
represented using str
. For this direction a regular expression is used to perform the
disambiguation, and the user should therefore be careful not to pass strings that look like
constants if the intention is to treat it like a logical string.
- class clorm.SimpleField(default: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>, index: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>)
A class that represents a field corresponding to any simple term: string, constant, or integer.
Converting from an ASP string, constant, or integer will produce the expected Python string or integer object. However, since ASP strings and constants both map to Python strings therefore converting from Python to ASP is less straightforward. In this case it uses a regular expression to determine if the string matches an ASP constant or if it should be treated as a quoted string.
Because of this potential for ambiguity it is often better to use the distinct
IntegerField
,ConstantField
, andStringField
classes rather than theSimpleField
class.
A RawField
is useful when it is necessary to match any term, whether it is a
primitive term or a complex term. This encoding provides no traslation of the underlying Clingo
Symbol object and simply wraps it in a special Raw
class.
- class clorm.Raw(symbol: Symbol | NoSymbol)
A wrapper around a raw
Symbol
object.The
Raw
object is for use with aRawField
field definition. It provides a thin wrapper around aclingo.Symbol
(ornoclingo.Symbol
) objects.RawField
unifies with all symbols as it doesn’t try to access any of the underlying structure of the symbol object. Instead the symbol object is wrapped in aRaw
object. To access the underlyingclingo.Symbol
object simply access the read-onlysymbol
property.- property symbol
Access the underlying clingo.Symbol object``.
- class clorm.RawField(default: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>, index: ~typing.Any = <clorm.orm.core._MISSING_TYPE object>)
A field that unifies with any raw symbol.
RawField
unifies with all symbols as it doesn’t try to access any of the underlying structure of the symbol object. Instead the symbol object is wrapped in aRaw
object. To access the underlyingclingo.Symbol
object simply access the read-onlysymbol
property of theRaw
object.
Finally, there are a number of function generators that can be used to define some special cases; refining or combining existing fields or allowing for a complicated pattern of fields. While Clorm doesn’t explicitly allow recursive terms to be defined it does provide a number of encodings of list like terms. Note, it is sometimes possible to avoid the explicit use of these functions and instead to rely on some predefined type annotation mappings.
- clorm.refine_field(field_class: Type[_BF], values: Iterable[_T] | Callable[[_T], bool], *, name: str | None = None) Type[_BF]
Factory function that returns a field sub-class with restricted values.
A helper factory function to define a sub-class of a BaseField (or sub-class) that restricts the allowable values. For example, if you have a constant in a predicate that is restricted to the days of the week (“monday”, …, “sunday”), you then want the Python code to respect that restriction and throw an error if the user enters the wrong value (e.g. a spelling error such as “wednsday”). Restrictions are also useful for unification if you want to unify based on some specific value.
Example
WorkDayField = refine_field(ConstantField, ["monday", "tuesday", "wednesday", "thursday", "friday"]) class WorksOn(Predicate): employee = ConstantField() workday = WorkdDayField()
Instead of a passing a list of values the second parameter can also be a function/functor. If is parameter is callable then it is treated as a function that takes a field value and returns true if it is a valid value.
Example
PosIntField = refine_field(NumberField,lambda x : x >= 0)
The function must be called using positional arguments with either 2 or 3 arguments. For the 3 argument case a class name is specified for the name of the new field. For the 2 argument case an anonymous field class name is automatically generated.
Example
WorkDayField = refine_field(ConstantField, ["monday", "tuesday", "wednesday", "thursday", "friday"])
Positional argument:
field_class: the field that is being sub-classed
values|functor: a list of values or a functor to determine validity
Optional keyword-only arguments:
name: name for new class (default: anonymously generated).
- clorm.define_enum_field(parent_field: Type[BaseField], enum_class: Type[Enum], *, name: str = '') Type[BaseField]
Factory function that returns a BaseField sub-class for an Enum
Enums are part of the standard library since Python 3.4. This method provides an alternative to using refine_field() to provide a restricted set of allowable values.
Example
class IO(ConstantStr,Enum): IN="in" OUT="out" # A field that unifies against ASP constants "in" and "out" IOField = define_enum_field(ConstantField,IO) # Note, when specified within a Predicate it is possible to avoid calling this # function directly and the predicate class should simply reference the ``IO`` type # annotation. class X(Predicate): x: IO
Positional argument:
field_class: the field that is being sub-classed
enum_class: the Enum class
Optional keyword-only arguments:
name: name for new class (default: anonymously generated).
- clorm.combine_fields(fields: Sequence[Type[BaseField]], *, name: str = '') Type[BaseField]
Factory function that returns a field sub-class that combines other fields
A helper factory function to define a sub-class of BaseField that combines other BaseField subclasses. The subclass is defined such that it’s
pytocl()
(respectivelycltopy()
) function tries to return the value returned by the underlying sub-field’spytocl()
( (respectivelycltopy()
) function. If the first sub-field fails then the second is called, and so on until there are no matching sub-fields. If there is no match then a TypeError is raised.Example
MixedField = combine_fields([ConstantField,IntegerField])
Positional args:
field_subclasses: the fields to combine
Optional keyword-only arguments:
name: name for new class (default: anonymously generated).
- clorm.define_nested_list_field(element_field: Type[BaseField], *, headlist: bool = True, reverse: bool = False, name: str = '') Type[BaseField]
Factory function that returns a BaseField sub-class for nested lists
This function is a helper factory function to define a sub-class of BaseField that can covert a list of elements to/from ASP.
ASP doesn’t have an explicit notion of a sequence or list, but sometimes it is useful to encode a list as a series of nested pairs. There are two basic ways to do this, with each way having two sub-forms:
(head,list) - the list is encoded recursively with a first head element and a second element representing the remainder of the list. The end of the list is indicated by an empty tuple.
Example:
(1,(2,(3,()))) % Encodes a sequence (1,2,3)
The sub-form is for the list to be treated as reversed in the ASP encoding.
Example:
(3,(2,(1,()))) % Encodes a sequence (1,2,3)
(list,tail) - the list is encoded recursively as a sub-list first element and a second tail element. The empty sub-list is indicated by an empty tuple.
Example:
((((),1),2),3) % Encodes a sequence (1,2,3)
Again the sub-form version is to reverse the list.
((((),3),2),1) % Encodes a sequence (1,2,3)
The choice of nested list encodings will depend on how it is used within the ASP code. However, the head-list-pair approach in non-reverse order is also used in lisp and prolog so for this reason it is set as the default approach here.
Note: the fields of facts should be immutable. This means that you must use a tuple and not a list object when creating the sequence.
Example
# Unifies against a nested sequence of constants NestedListField = define_nested_list_field(ConstantField,name="NLField")
Positional args:
element_field: the field type for each sequence element
Optional keyword-only arguments:
name: name for new class (default: anonymously generated).
headlist: use head-list encoding (default: True)
reverse: use the reverse order for the list (default: False)
- clorm.define_flat_list_field(element_field: Type[BaseField], *, name: str = '') Type[BaseField]
Factory function that returns a BaseField sub-class for flat lists
This function is a helper factory function to define a sub-class of BaseField to can covert a list/tuple of elements to and from arbitrary length ASP tuples.
Note: this is different to defining a fixed-length tuple in Clorm as a sub-class of clorm.Predicate. The elements of a predicate can be can be part of a query search. In contrast the variable length tuple defined by this function provide a more straightforward mapping but doesn’t allow for individual elements of the tuple to be referenced in a query.
Example
# Unifies against a flat sequence of constants FlatListField = define_flat_list_field(ConstantField)
Positional args:
element_field: the field type for each sequence element
Optional keyword-only arguments:
name: name for new class (default: anonymously generated).
Predicates and complex terms
In logical terminology predicates and terms are both considered non logical symbols; where
the logical symbols are the operator symbols for conjunction, negation, implication,
etc. While simple terms (constants, strings, and integers) are handled by Clorm as special
cases, complex terms and predicates are both encapsulated in the Predicate
class, with
ComplexTerm
simply being an alias to this class.
- class clorm.Predicate(*args, **kwargs)
Encapsulates an ASP predicate or complex term in an easy to access object.
This is the heart of the ORM model for defining the mapping of a complex term or predicate to a Python object.
ComplexTerm
is simply an alias forPredicate
.Example
class Booking(Predicate): date: str time: str name: str = field(StringField, default="relax") b1 = Booking("20190101", "10:00") b2 = Booking("20190101", "11:00", "Dinner")
Field names can be any valid Python variable name subject to the following restrictions:
it cannot start with a “_”, or
it cannot be be one of the following reserved words: “meta”, “raw”, “clone”, or “Field”.
The constructor creates a predicate instance (i.e., a fact) or complex term.
Note: Using the
raw
parameter is no longer supported. You should use the unify() function or Unifier class instead.- Parameters:
**kwargs –
if a single named parameter
raw
is specified then it will try to unify the parameter with the specification, ornamed parameters corresponding to the field names.
- Field
A BaseField sub-class corresponding to a field definition for this class.
- clorm.simple_predicate(predicate_name: str, arity: int, *, name: str = '', module: str = '') Type[Predicate]
Factory function to define a predicate with only RawField arguments.
A helper factory function that takes a name and an arity and returns a predicate class that is suitable for unifying with predicate instances of that name and arity. It’s parameters are all specified as RawFields.
This function is useful for debugging ASP programs. There may be some auxillary predicates that you aren’t interested in extracting their values but instead you simply want to print them to the screen in some order.
The function must be called using positional arguments with either 2 or 3 arguments. For the 3 argument case a class name is specified for the name of the new predicate. For the 2 argument case an anonymous predicate class name is automatically generated.
Positional argument:
predicate_name: the name of the ASP predicate to match against
arity: the arity for the ASP predicate
Optional keyword-only arguments:
name: name for new class (default: anonymously generated).
Fact Bases and Queries
Predicate
instances correspond to facts. A FactBase
provides a container
for storing facts. It allows predicate fields to be indexed and provides a basic
query mechanism for accessing elements.
- class clorm.FactBase(facts: Iterable[Predicate] | Callable[[], Iterable[Predicate]] | None = None, indexes: Iterable[PredicatePath] | None = None)
A fact base is a container for facts (i.e., Predicate sub-class instances)
FactBase
can be behave like a specialisedset
object, but can also behave like a minimalist database. It stores facts forPredicate
types (where a predicate type loosely corresponds to a table in a database) and allows for certain fields to be indexed in order to perform more efficient queries.The initaliser can be given a collection of predicates. If it is passed another FactBase then it simply makes a copy (including the indexed fields).
FactBase also has a special mode when it is passed a functor instead of a collection. In this case it performs a delayed initialisation. This means that the internal data structures are only populated when the FactBase is actually used. This mode is particularly useful when extracting facts from models. Often a program will only want to keep the data from the final model (for example, with optimisation we often want the best model before a timeout). Delayed initialisation is useful will save computation as only the last model will be properly initialised.
- Parameters:
facts ([Predicate]|FactBase|callable) – a list of facts (predicate instances), a fact base, or a functor that generates a list of facts. If a functor is passed then the fact base performs a delayed initialisation. If a fact base is passed and no index is specified then an index will be created matching in input fact base.
indexes (Field) – a list of fields that are to be indexed.
- add(arg: Predicate | Iterable[Predicate]) None
Add a single fact or a collection of facts.
Because a
FactBase
can only holdPredicate
sub-class instances this member function has been overloaded to take either a singlePredicate
sub-class instance or a collection ofPredicate
sub-class instances.- Parameters:
arg – a single fact or a collection of facts.
- asp_str(*, width: int = 0, commented: bool = False, sorted: bool = False) str
Return a ASP string representation of the fact base.
The generated ASP string representation is syntactically correct ASP code so is suitable for adding as the input to to an ASP program (or writing to a file for later use in an ASP program).
By default the order of the facts in the string is arbitrary. Because FactBase is built on a OrderedDict (which preserves insertion order) the order of the facts will be deterministic between runs of the same program. However two FactBases containing the same facts but constructed in different ways will not produce the same output string. In order to guarantee the same output the sorted flag can be specified.
- Parameters:
width – tries to fill to a given width by putting more than one fact on a line if necessary (default: 0).
commented – produces commented ASP code by adding a predicate signature and turning the Predicate sub-class docstring into a ASP comments (default: False).
sorted – sort the output facts, first by predicates (name,arity) and then by the natural order of the instances for that predicate (default :False).
- clear()
Clear the fact base of all facts.
- difference(*others: Iterable[Predicate] | Callable[[], Iterable[Predicate]]) FactBase
Implements the set difference() function
- difference_update(*others: Iterable[Predicate] | Callable[[], Iterable[Predicate]]) None
Implements the set difference_update() function
- intersection(*others: Iterable[Predicate] | Callable[[], Iterable[Predicate]]) FactBase
Implements the set intersection() function
- intersection_update(*others: Iterable[Predicate] | Callable[[], Iterable[Predicate]]) None
Implements the set intersection_update() function
- query(__ent0: Type[_T0]) UnGroupedQuery[_T0]
- query(__ent0: Type[_T0], __ent1: Type[_T1]) UnGroupedQuery[Tuple[_T0, _T1]]
- query(__ent0: Type[_T0], __ent1: Type[_T1], __ent2: Type[_T2]) UnGroupedQuery[Tuple[_T0, _T1, _T2]]
- query(__ent0: Type[_T0], __ent1: Type[_T1], __ent2: Type[_T2], __ent3: Type[_T3]) UnGroupedQuery[Tuple[_T0, _T1, _T2, _T3]]
- query(__ent0: Type[_T0], __ent1: Type[_T1], __ent2: Type[_T2], __ent3: Type[_T3], __ent4: Type[_T4]) UnGroupedQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]
- query(*roots: Any) UnGroupedQuery[Any]
Define a query using the new Query API
Query
.The parameters consist of a predicates (or aliases) to query (like an SQL FROM clause).
- Parameters:
*predicates – predicate or predicate aliases
- Returns:
Returns a Query object for specifying a query.
- select(root)
Define a select query using the old Query API.
Note
This interface will eventually be deprecated when the new
Query API
is finalised. The entry point to this Query API is through theFactBase.query()
method.- Parameters:
predicate – The predicate to query.
- Returns:
Returns a Select query object for specifying a query.
- symmetric_difference(other: Iterable[Predicate] | Callable[[], Iterable[Predicate]]) FactBase
Implements the set symmetric_difference() function
- symmetric_difference_update(other: Iterable[Predicate] | Callable[[], Iterable[Predicate]]) None
Implements the set symmetric_difference_update() function
- union(*others: Iterable[Predicate] | Callable[[], Iterable[Predicate]]) FactBase
Implements the set union() function
A FactBase
can generate formatted ASP facts using the function
FactBase.add()
. This string of facts can be
passed to the solver or written to a file to be read. Mirroring this
functionality an ASP string or file containing facts can also be read directly
into a FactBase
(without the indirect process of having to create a
clingo.Control
object).
- clorm.parse_fact_string(aspstr: str, unifier: Iterable[Type[Predicate]], *, factbase: FactBase | None = None, raise_nomatch: bool = False, raise_nonfact: bool = False) FactBase
Parse a string of ASP facts into a FactBase
Facts must be of a simple form that can correspond to clorm predicate instances. Rules that are NOT simple facts include: any rule with a body, a disjunctive fact, a choice rule, a theory atom, a literal with an external @-function reference, a literal that requires some mathematical calculation (eg., “p(1+1).”)
NOTE: Currently, this function is not safe when running in NOCLINGO mode and will raise a NotImplementedError if called.
- Parameters:
aspstr – an ASP string containing the facts
factbase – if no factbase is specified then create a new one
unifier – a list of clorm.Predicate classes to unify against
raise_nomatch – raise UnifierNoMatchError on a fact that cannot unify
raise_nonfact – raise FactParserError on any non simple fact (eg. complex rules)
- clorm.parse_fact_files(files: Sequence[str], unifier: Iterable[Type[Predicate]], *, factbase: FactBase | None = None, raise_nomatch: bool = False, raise_nonfact: bool = False) FactBase
Parse the facts from a list of files into a FactBase
Facts must be of a simple form that can correspond to clorm predicate instances. Rules that are NOT simple facts include: any rule with a body, a disjunctive fact, a choice rule, a theory atom, a literal with an external @-function reference, a literal that requires some mathematical calculation (eg., “p(1+1).”)
NOTE: Currently, this function is not safe when running in NOCLINGO mode and will raise a NotImplementedError if called.
- Parameters:
files – a list of ASP files containing the facts
factbase – if no factbase is specified then create a new one
unifier – a list of clorm.Predicate classes to unify against
raise_nomatch – raise UnifierNoMatchError on a fact that cannot unify
raise_nonfact – raise FactParserError on any non simple fact (eg. complex rules)
One of the more important features of a FactBase
is its ability to be
queried. There are a number of classes and functions to support the
specification of fact base queries.
- class clorm.Placeholder
An abstract class for defining parameterised queries.
Currently, Clorm supports 4 placeholders: ph1_, ph2_, ph3_, ph4_. These correspond to the positional arguments of the query execute function call.
- class clorm.Select
An abstract class that defines the interface to original Query API.
Note
This interface will eventually be deprecated when the new
Query API
is finalised.Select
query objects cannot be constructed directly. Instead aSelect
object is returned by theFactBase.select()
function. Given aFactBase
objectfb
, a specification is of the form:query = fb.select(<predicate>).where(<expression>).order_by(<ordering>)
where
<predicate>
specifies the predicate type to search for,<expression>
specifies the search criteria and<ordering>
specifies a sort order when returning the results. Thewhere()
andorder_by()
clauses are omitted when not required.- abstract where(*expressions)
Set the select statement’s where clause.
The where clause consists of a set of boolean and comparison expressions. This expression specifies a search criteria for matching facts within the corresponding
FactBase
.Boolean expression are built from other boolean expression or a comparison expression. Comparison expressions are of the form:
<PredicatePath> <compop> <value>
where
<compop>
is a comparison operator such as==
,!=
, or<=
and<value>
is either a Python value or another predicate path object refering to a field of the same predicate or a placeholder.A placeholder is a special value that issubstituted when the query is actually executed. These placeholders are named
ph1_
,ph2_
,ph3_
, andph4_
and correspond to the 1st to 4th arguments of theget
,get_unique
orcount
function call.- Args:
expressions: one or more comparison expressions.
- Returns:
Returns a reference to itself.
- abstract order_by(*fieldorder)
Provide an ordering over the results.
- Parameters:
fieldorder – an ordering over fields
- Returns:
Returns a reference to itself.
- abstract get(*args, **kwargs)
Return all matching entries.
- get_unique(*args, **kwargs)
Return the unique matching entry (or raise an exception)
- count(*args, **kwargs)
Return the number of matches.
- class clorm.Delete
An abstract class that defines the interface to a original delete query API.
Note
This interface will eventually be deprecated when the new
Query API
is finalised.Delete
query objects cannot be constructed directly. Instead aDelete
object is returned by theFactBase.delete()
function. Given aFactBase
objectfb
, a specification is of the form:query = fb.delete(<predicate>).where(<expression>)
where
<predicate>
specifies the predicate type to search for,<expression>
specifies the search criteria. Thewhere()
clause can be omitted in which case all predicates of that type will be deleted.- abstract where(*expressions)
Set the select statement’s where clause.
See the documentation for
Select.where()
for further details.
- abstract execute(*args, **kwargs)
Function to execute the delete query
- class clorm.Query
An abstract class that defines the interface to the Clorm Query API v2.
Note
This new Query API replaces the old Select/Delete mechanism and offers many more features than the old API, especially allowing joins similar to an SQL join between tables.
This interface is complete and unlikely to change - however it is being left open for the moment in case there is strong user feedback.
Query
objects cannot be constructed directly.Instead a
Query
object is returned by theFactBase.query()
function. Queries can take a number of different forms but contain many of the components of a traditional SQL query. A predicate definition (as opposed to a predicate instance or fact) can be viewed as an SQL table and the parameters of a predicate can be viewed as the fields of the table.The simplest query must at least specify the predicate(s) to search for. This is specified as parameters to the
FactBase.query()
function. Relating this to a traditional SQL query, thequery
clause can be viewed as an SQLFROM
specification.The query is typicaly executed by iterating over the generator returned by the
Query.all()
end-point.from clorm import FactBase, Predicate, IntegerField, StringField class Option(Predicate): oid = IntegerField name = StringField cost = IntegerField cat = StringField class Chosen(Predicate): oid = IntegerField fb=FactBase([Option(1,"Do A",200,"foo"),Option(2,"Do B",300,"bar"), Option(3,"Do C",400,"foo"),Option(4,"Do D",300,"bar"), Option(5,"Do E",200,"foo"),Option(6,"Do F",500,"bar"), Chosen(1),Chosen(3),Chosen(4),Chosen(6)]) q1 = fb.query(Chosen) # Select all Chosen instances result = set(q1.all()) assert result == set([Chosen(1),Chosen(3),Chosen(4),Chosen(6)])
If there are multiple predicates involved in the search then the query must also contain a
Query.join()
clause to specify the predicates parameters/fields to join.q2 = fb.query(Option,Chosen).join(Option.oid == Chosen.oid)
Note
As an aside, while a
query
clause typically consists of predicates, it can also contain predicate aliases created through thealias()
function. This allows for queries with self joins to be specified.When a query contains multiple predicates the result will consist of tuples, where each tuple contains the facts matching the signature of predicates in the
query
clause. Mathematically the tuples are a subset of the cross-product over instances of the predicates; where the subset is determined by thejoin
clause.result = set(q2.all()) assert result == set([(Option(1,"Do A",200,"foo"),Chosen(1)), (Option(3,"Do C",400,"foo"),Chosen(3)), (Option(4,"Do D",300,"bar"),Chosen(4)), (Option(6,"Do F",500,"bar"),Chosen(6))])
A query can also contain a
where
clause as well as anorder_by
clause. When theorder_by
clause contains a predicate path then by default it is ordered in ascending order. However, this can be changed to descending order with thedesc()
function modifier.from clorm import desc q3 = q2.where(Option.cost > 200).order_by(desc(Option.cost)) result = list(q3.all()) assert result == [(Option(6,"Do F",500,"bar"),Chosen(6)), (Option(3,"Do C",400,"foo"),Chosen(3)), (Option(4,"Do D",300,"bar"),Chosen(4))]
The above code snippet highlights a feature of the query construction process. Namely, that these query construction functions can be chained and can also be used as the starting point for another query. Each construction function returns a modified copy of its parent. So in this example query
q3
is a modified version of queryq2
.Returning tuples of facts is often not the most convenient output format and instead you may only be interested in specific predicates or parameters within each fact tuple. For example, in this running example it is unnecessary to return the
Chosen
facts. To provide the output in a more useful format a query can also contain aselect
clause that specifies the items to return. Essentially, this specifies a _projection_ over the elements of the result tuple.q4 = q3.select(Option) result = list(q4.all()) assert result == [Option(6,"Do F",500,"bar"), Option(3,"Do C",400,"foo"), Option(4,"Do D",300,"bar")]
A second mechanism for accessing the data in a more convenient format is to use a
group_by
clause. In this example, we may want to aggregate all the chosen options, for example to sum the costs, based on their membership of the"foo"
and"bar"
categories. The Clorm query API doesn’t directly support aggregation functions, as you could do in SQL, so some additional Python code is required.q5 = q2.group_by(Option.cat).select(Option.cost) result = [(cat, sum(list(it))) for cat, it in q5.all()] assert result == [("bar",800), ("foo", 600)]
The above are not the only options for a query. Some other query modifies include:
Query.distinct()
to return distinct elements, andQuery.bind()
to bind the value of any placeholders in thewhere
clause to specific values.A query is executed using a number of end-point functions. As already shown the main end-point is
Query.all()
to return a generator for iterating over the results.Alternatively, if there is at least one element then to return the first result only (throwing an exception only if there are no elements) use the
Query.first()
method.Or if there must be exactly one element (and to throw an exception otherwise) use
Query.singleton()
.To count the elements of the result there is
Query.count()
.Finally to delete all matching facts from the underlying FactBase use
Query.delete()
.- abstract join(*expressions)
Specifying how the predicate/tables in the query are to be joined.
Joins are expressions that connect the predicates/tables of the query. They range from a pure SQL-like inner-join through to an unrestricted cross-product. The standard form is:
<PredicatePath> <compop> <PredicatePath>
with the full cross-product expressed using a function:
cross(<PredicatePath>,<PredicatePath>)
Every predicate/table in the query must be reachable to every other predicate/table through some form of join. For example, given predicate definitions
F
,G
,H
, each with a fieldanum
:query = fb.query(F,G,H).join(F.anum == G.anum,cross(F,H))
generates an inner join between
F
andG
, but a full cross-product betweenF
andH
.Finally, it is possible to perform self joins using the function
alias
that generates an alias for the predicate/table. For .. rubric:: Examplefrom clorm import alias
FA=alias(F)
query = fb.query(F,G,FA).join(F.anum == FA.anum,cross(F,G))
generates an inner join between
F
and itself, and a full cross-product betweenF
andG
.- Parameters:
expressions – one or more join expressions.
- Returns:
Returns the modified copy of the query.
- abstract where(*expressions)
Sets a list of query conditions.
The where clause consists of a single (or list) of simple/complex boolean and comparison expressions. This expression specifies a search criteria for matching facts within the corresponding
FactBase
.Boolean expression are built from other boolean expression or a comparison expression. Comparison expressions are of the form:
<PredicatePath> <compop> <value>
where
<compop>
is a comparison operator such as==
,!=
, or<=
and<value>
is either a Python value or another predicate path object refering to a field of the same predicate or a placeholder.A placeholder is a special value that allows queries to be parameterised. A value can be bound to each placeholder. These placeholders are named
ph1_
,ph2_
,ph3_
, andph4_
and correspond to the 1st to 4th arguments when thebind()
function is called. Placeholders also allow for named arguments using the “ph_(“<name>”) function.- Args:
expressions: one or more comparison expressions.
- Returns:
Returns the modified copy of the query.
- abstract order_by(*expressions)
Specify an ordering over the results.
- Parameters:
field_order – an ordering over fields
- Returns:
Returns the modified copy of the query.
- abstract group_by(*expressions)
Specify a grouping over the results.
The grouping specification is similar to an ordering specification but it modifies the behaviour of the query to return a pair of elements, where the first element of the pair is the group identifier (based on the specification) and the second element is an iterator over the matching elements.
When both a
group_by
andorder_by
clause is provided theorder_by
clause is used to sort the elements within each matching group.- Parameters:
field_order – an ordering over fields to group by
- Returns:
Returns the modified copy of the query.
- abstract select(*outsig)
Provides a projection over the query result tuples.
Mathematically the result tuples of a query are the cross-product over instances of the predicates. However, returning tuples of facts is often not the most convenient output format and instead you may only be interested in specific parameters/fields within each tuple of facts. The
select
clause specifies a _projection_ over each query result tuple.Each query result tuple is guaranteed to be distinct, since the query is a filtering over the cross-product of the predicate instances. However, the specification of a projection can result in information being discarded and can therefore cause the projected query results to no longer be distinct. To enforce uniqueness the
Query.distinct()
flag can be specified. Essentially this is the same as anSQL SELECT DISTINCT ...
statement.Note: when
Query.select()
is used with theQuery.delete()
end-point the select signature must specify predicates and not parameter/fields within the predicates.Finally, instead of a projection specification, a single Python callable object can be specified with input parameters matching the query signature. This callable object will then be called on each query tuple and the results will make up the modified query result. This provides the greatest flexibility in controlling the output of the query.
- Parameters:
output_signature – the signature that defines the projection or a callable object.
- Returns:
Returns the modified copy of the query.
- abstract distinct()
Return only distinct elements in the query.
This flag is only meaningful when combined with a
Query.select()
clause that removes distinguishing elements from the tuples.- Returns:
Returns the modified copy of the query.
- abstract bind(*args, **kwargs)
Bind placeholders to specific values.
If the
where
clause has placeholders then these placeholders must be bound to actual values before the query can be executed.- Parameters:
*args – positional arguments corresponding to positional placeholders
**kwargs – named arguments corresponding to named placeholders
- Returns:
Returns the modified copy of the query.
- abstract tuple()
Force returning a tuple even for singleton elements.
In the general case the output signature of a query is a tuple; consisting either of a tuple of facts or a tuple of parameters/fields if a
select
projection has been specified.However, if the output signature is a singleton tuple then by default the API changes its behaviour and removes the tuple, returning only the element itself. This typically provides a much more useful and intutive interface. For example, if you want to perform a sum aggregation over the results of a query, aggregating over the value of a specific parameter/field, then specifying just that parameter in the
select
clause allows you to simply pass the query generator to the standard Pythonsum()
function without needing to perform a list comprehension to extract the value to be aggregated.If there is a case where this default behaviour is not wanted then specifying the
tuple
flag forces the query to always return a tuple of elements even if the output signature is a singleton tuple.- Returns:
Returns the modified copy of the query.
- abstract heuristic(join_order)
Allows the query engine’s query plan to be modified.
This is an advanced option that can be used if the query is not performing as expected. For multi-predicate queries the order in which the joins are performed can affect performance. By default the Query API will try to optimise this join order based on the
join
expressions; with predicates with more restricted joins being higher up in the join order.This join order can be controlled explicitly by the
fixed_join_order
heuristic function. Assuming predicate definitionsF
andG
the query:from clorm import fixed_join_order
query=fb.query(F,G).heuristic(fixed_join_order(G,F)).join(...)
forces the join order to first be the
G
predicate followed by theF
predicate.- Parameters:
join_order – the join order heuristic
- Returns:
Returns the modified copy of the query.
- abstract all()
Returns a generator that iteratively executes the query.
Note. This call doesn’t execute the query itself. The query is executed when iterating over the elements from the generator.
- Returns:
Returns a generator that executes the query.
- abstract singleton()
Return the single matching element.
An exception is thrown if there is not exactly one matching element or a
group_by()
clause has been specified.- Returns:
Returns the single matching element (or throws an exception)
- abstract count()
Return the number of matching element.
Typically the number of elements consist of the number of tuples produced by the cross-product of the predicates that match the criteria of the
join()
andwhere()
clauses.However, if a
select()
projection andunique()
flag is specified then thecount()
will reflect the modified the number of unique elements based on the projection of the query.Furthermore, if a
group_by()
clause is specified thencount()
returns a generator that iterates over pairs where the first element of the pair is the group identifier and the second element is the number of matching elements within that group.- Returns:
Returns the number of matching elements
- abstract first()
Return the first matching element.
An exception is thrown if there are no one matching element or a
group_by()
clause has been specified.- Returns:
Returns the first matching element (or throws an exception)
- abstract delete()
Delete matching facts from the
FactBase()
.In the simple case of a query with no joins then
delete()
simply deletes the matching facts. If there is a join then the matches consist of tuples of facts. In this casedelete()
will remove all facts from the tuple. This behaviour can be modified by using aselect()
projection clause that selects only specific predicates. Note: when combined withselect()
the output signature must specify predicates and not parameter/fields within predicates.An exception is thrown if a
group_by()
clause has been specified.- Returns:
Returns the number of facts deleted.
- abstract modify(fn)
Modify matching facts in a
FactBase()
.Clorm facts are immutable, so in order to simulate something like an SQL
UPDATE
operation, themodify()
query end-point provides a convenient way to replace or modify matching facts in a query with some (related) facts.This call operates in the same way as the
delete()
end-point but the matching facts are first passed to the parameter functionfn
. This function must return a pair consisting of 1) the facts to delete from the factbase, 2) the facts to add to the fact base. These two sets of facts are maintained until the end of the query and the delete set of facts are removed followed by the add set being added.Note, this behaviour could also achieved by iterating over the results of the query normally and maintaining separate “delete” and “add” lists which are then acted upon at the end of the query. The advantage of the
modify()
call is to provide a simple and declarative way to do it which can be convenient especially when chaining multiple modifications of a factbase.The function
fn
must have the same input signature as the query output. If the output offn
must be a pair consisting of the elements to delete and element to add. For flexibility, each set can beNone
or a single clorm fact or an iterator over clorm facts.An exception is thrown if a
group_by()
or ``uclause has been specified.Example assuming a FactBase
fb
consisting ofF
andG
predicate facts:- def mod_fn(f: F, g: G):
return ({f, g}, {f.clone(a=10), g.clone(a=10)})
fb.query(F,G).join(F.a == G.a).where(F.a == 2).modify(mod_fn)
fb.query(F,G).join(F.a == G.a).where(F.a == 10).select(F).modify(lambda f: (None, {f.clone(a=20)}))
- Returns:
Returns a (delete_count, add_count) pair indicating the total number of facts deleted and added to the factbase.
- abstract replace(fn)
Replace matching facts in a
FactBase()
.This function is a convenient special case of the more general
modify()
function.While the
modify()
parameter function must return a del-add pair, thereplace()
parameter function returns only an add entry, since all matching facts will be deleted.- Returns:
Returns a (delete_count, add_count) pair indicating the total number of facts deleted and added to the factbase.
- abstract query_plan(*args, **kwargs)
Return a query plan object outlining the query execution.
A query plan outlines the query will be executed; the order of table joins, the searches based on indexing, and how the sorting is performed. This is useful for debugging if the query is not behaving as expected.
Currently, there is no fixed specification for the query plan object. All the user can do is display it for reading and debugging purposes.
- Returns:
Returns a query plan object that can be stringified.
- class clorm.PredicatePath(pathseq: List[PathIdentity | str])
PredicatePath implements the intuitive query syntax.
Every defined
Predicate
sub-class has a correspondingPredicatePath
sub-class that mirrors its field definitions. This allows it to be used when specifying the components of a query; such as the sign and the fields or sub-fields of a predicate (eg.,Pred.sign
,Pred.a.b
orPred.a[0]
).When the API user refers to a field (or sign) of a Predicate sub-class they are redirected to the corresponding
PredicatePath
object of that predicate sub-class.While instances of this class (and sub-classes) are externally exposed through the API, users should not explicitly instantiate instances themselves.
Predicate path subclasses provide attributes and indexed items for refering to sub-paths. When a user specifies
Pred.a.b.c
thePredicate
sub-classPred
seemlessly passes off to an associatedPredicatePath
object, which then returns a path corresponding to the specifications.Fields can be specified either by name through a chain of attributes or using the array indexes. This is implemented in the overloaded
__getitem__
function which allows for name or positional argument specifications.The most important aspect of a predicate path object is that it overloads the boolean operators to return a comparison condition. This is what allows for query specifications such as
Pred.a.b == 2
orPred.a.b == ph1_
.Finally, because the name
meta
is a Clorm keyword and can’t be used as a field name it is used as a property referring to an internal class with functions for use by the internals of the library. API users should not use this property.
Query Support Functions
The following functions support the query specification.
- clorm.path(arg, exception=True)
Returns the
PredicatePath
corresponding to some component.This function is useful for users for the special case of referring to the
PredicatePath
that corresponding to aPredicate
object. For example to specify a comparison in a query to match a specific instance to some placeholder you need to reference the predicate using a path.Example:
from clorm import FactBase, Predicate, ConstantField, path class F(Predicate): a = ConstantField fb = FactBase([F("foo"),F("bar")]) qBad=fb.query(F).where(F == F("bar")) # This won't do what you expect qGood=fb.query(F).where(path(F) == F("bar"))
Note
The technical reason for not supporting the more intuitive syntax above is that it would require overloading the comparison operators of the predicate class itself; which would break the behaviour of Python in many other contexts.
- Returns:
Returns a
PredicatePath
object corresponding to the input specification.
- clorm.alias(predicate: Type[_P], name: str = '') Type[_P]
- clorm.alias(predicate: PredicatePath | Hashable, name: str = '') Type[Predicate]
Return an alias
PredicatePath
instance for aPredicate
sub-class.A predicate alias can be used to support self joins in queries. The alias has all the same fields (and sub-fields) as the “normal” path associated with the predicate.
For example, consider a simple (and not properly normalised) friend fact base with a predicate that uniquely identifies people and friends, by a id number, and you want to output the friend connections in an intuitive manner.
Example
from clorm import FactBase, Predicate, IntegerField, StringField, alias class F(Predicate): pid = IntegerField name = StringField fid = IntegerField fb=FactBase([F(1,"Adam",3),F(2,"Betty",4),F(3,"Carol",1),F(4,"Dan",2)]) FA = alias(F) q=fb.query(F,FA).join(F.pid == FA.fid).select(F.name,FA.name) for p,f in q.all(): print("Person {} => Friend {}".format(p,f))
- Returns:
Returns an alias
PredicatePath
for the predicate.
- clorm.cross(*args)
Return a cross-product join condition
- clorm.ph_(value, *args, **kwargs)
A function for building new placeholders, either named or positional.
- clorm.not_(*conditions)
Return a boolean condition that is the negation of the input condition
- clorm.and_(*conditions)
Return a the conjunction of two of more conditions
- clorm.or_(*conditions)
Return the disjunction of two of more conditions
- clorm.in_(path, seq)
Return a query operator to test membership of an item in a collection
- clorm.notin_(path, seq)
Return a query operator to test non-membership of an item in a collection
Calling Python From an ASP Program
Clorm provides a number of decorators that can make it easier to call Python from within an ASP program. The basic idea is that Clorm provides all the information required to convert data between native Python types and clingo.Symbol objects. Therefore functions can be written by only dealing with Python data and the Clorm decorators will wrap these functions with the appropriate data conversions based on a given signature.
- clorm.make_function_asp_callable(*args: Any) Callable[[...], Any]
A decorator for making a function callable from within an ASP program.
Can be called in a number of ways. Can be called as a decorator with or without arguments. If called with arguments then the arguments must correspond to a type cast signature.
A type cast signature specifies the type conversions required between a python function that is called from within an ASP program and a set of corresponding Python types.
A type cast signature is specified in terms of the fields that are used to define a predicate. It is a list of elements where the first n-1 elements correspond to type conversions for a functions inputs and the last element corresponds to the type conversion for a functions output.
- Parameters:
sigs (*sigs) – A list of function signature elements.
[ (- Inputs. Match the sub-elements) – -1] define the input signature while the last element defines the output signature. Each input must be a a BaseField (or sub-class).
Output (-) – Must be BaseField (or sub-class) or a singleton list containing a BaseField (or sub-class).
If no arguments are provided then the function signature is derived from the function annotations. The function annotations must conform to the signature above.
If called as a normal function with arguments then the last element must be the function to be wrapped and the previous elements conform to the signature profile.
- clorm.make_method_asp_callable(*args: Any) Callable[[...], Any]
A decorator for making a member function callable from within an ASP program.
See
make_function_asp_callable
for details. The only difference is that the first element of the function is ignore as it is assumed to be theself
orcls
parameter.
It may also be useful to deal with a predeclared type cast signature.
- class clorm.TypeCastSignature(*sigs: Any, module: str | None = None)
Defines a signature for converting to/from Clingo data types.
- Args:
module: Name of the module where the signature is defined sigs(*sigs): A list of signature elements.
Inputs. Match the sub-elements [:-1] define the input signature while the last element defines the output signature. Each input must be a BaseField (or sub-class).
Output: Must be BaseField (or sub-class) or a singleton list containing a BaseField (or sub-class).
Example
import datetime class DateField(StringField): pytocl = lambda dt: dt.strftime("%Y%m%d") cltopy = lambda s: datetime.datetime.strptime(s,"%Y%m%d").date() drsig = TypeCastSignature(DateField, DateField, [DateField], module = "__main__") @drsig.wrap_function def date_range(start, end): return [ start + timedelta(days=x) for x in range(0,end-start) ]
The function
date_range
that takes a start and end date and returns the list of dates within that range.When decorated with the signature it provides the conversion code so that the decorated function expects a start and end date encoded as Clingo.String objects (matching YYYYMMDD format) and returns a list of Clingo.String objects corresponding to the dates in that range.
- static is_return_element(se: Any) bool
An output element must be an output field or a singleton iterable containing an output fields; where an output field is a BaseField sub-class or tuple that recursively reduces to a BaseField sub-class.
- wrap_function(fn)
Function wrapper that adds data type conversions for wrapped function.
- Parameters:
fn – A function satisfing the inputs and output defined by the TypeCastSignature.
- wrap_method(fn)
Member function wrapper that adds data type conversions for wrapped member functions.
- Parameters:
fn – A function satisfing the inputs and output defined by the TypeCastSignature.
From Clingo 5.4 onwards, the Clingo grounding function allows a context parameter to be specified. This parameter defines a context object for the methods that are called by ASP using the @-syntax.
- class clorm.ContextBuilder
Context builder simplifies the task of building grounding context for clingo. This is a new clingo feature for Clingo 5.4 where a context can be provided to the grounding function. The context encapsulates the external Python functions that can be called from within an ASP program.
ContextBuilder
allows arbitrary functions to be captured within a context and assigned a conversion signature. It also allows the function to be given a different name when called from within the context.The context builder’s
register
andregister_name
member functions can be called as decorators or as normal functions. A useful feature of these functions is that when called as decorators they do not wrap the original function but instead return the original function and only wrap the function when called from within the context. This is unlike themake_function_asp_callable
andmake_method_asp_callable
functions which when called as decorators will replace the original function with the wrapped version.Example:
The following nonsense ASP program contains embedded python with functions registered with the context builder (highlighting different ways the register functions can be called). A context object is then created by the context builder and used during grounding. It will produce the answer set:
f(5), g(6), h("abcd").
f(@addi(1,4)). g(@addi_alt(2,4)). h(@adds("ab","cd")). #script(python). from clorm import IntegerField,StringField,ContextBuilder IF=IntegerField SF=StringField cb=ContextBuilder() # Uses the function annotation to define the conversion signature @cb.register def addi(a : IF, b : IF) -> IF : return a+b # Register with a different name @cb.register_name("addi_alt") def add2(a : IF, b : IF) -> IF : return a+b # Register with a different name and override the signature in the # function annotation cb.register_name("adds", SF, SF, SF, addi) ctx=cb.make_context() def main(prg): prg.ground([("base",[])],context=ctx) prg.solve() #end.
- make_context(cls_name='Context')
Return a context object that encapsulates the registered functions
- register(*args)
Register a function with the context builder.
- Parameters:
*args – the last argument must be the function to be registered. If there is more than one argument then the earlier arguments define the data conversion signature. If there are no earlier arguments then the signature is extracted from the function annotations.
- register_name(func_name, *args)
Register a function with assigning it a new name witin the context.
- Parameters:
func_name – the new name for the function within the context.
*args – the last argument must be the function to be registered. If there is more than one argument then the earlier arguments define the data conversion signature. If there are no earlier arguments then the signature is extracted from the function annotations.
Integration with the Solver
Clorm provides some helper functions to get clorm facts into and out of the solver (ie a clingo.Control object).
- clorm.control_add_facts(ctrl: Control, facts: Iterable[Predicate | Symbol]) None
Assert a collection of facts to the solver
Provides a flexible approach to asserting facts to the solver. The facts can be either clingo.Symbol objects or clorm facts and can be in any type of be in any collection (including a clorm.FactBase).
- Parameters:
ctrl – a clingo.Control object
facts – the collection of facts to be asserted into the solver
- clorm.symbolic_atoms_to_facts(symbolic_atoms: SymbolicAtoms, unifier: Iterable[Type[Predicate]], *, facts_only: bool = False, factbase: FactBase | None = None) FactBase
Extract clorm.FactBase from clingo.SymbolicAtoms
A clingo.SymbolicAtoms object is returned from the clingo.Control.symbolic_atoms property. This property is a view into the internal atoms within the solver and can be examined at anytime (ie. before/after the grounding/solving). Some of the atoms are trivially true (as determined by the grounder) while others may only be true in some models.
- Parameters:
symbolic_atoms – a clingo.SymbolicAtoms object
unifier – a list of Clorm Predicate sub-classes to unify against
facts_only (default False) – return facts only or include contingent literals
factbase (default None) – add to existing FactBase or return a new FactBase
- clorm.unify(unifier: Iterable[Type[Predicate]] | SymbolPredicateUnifier, symbols: Iterable[Symbol | NoSymbol], ordered: Literal[True]) List[Predicate]
- clorm.unify(unifier: Iterable[Type[Predicate]] | SymbolPredicateUnifier, symbols: Iterable[Symbol | NoSymbol]) FactBase
Unify raw symbols against a list of predicates or a SymbolPredicateUnifier.
Symbols are tested against each predicate unifier until a match is found. Since it is possible to define multiple predicate types that can unify with the same symbol, the order of the predicates in the unifier matters. With the ordered option set to True a list is returned that preserves the order of the input symbols.
- Parameters:
unifier – a list of predicate classes or a SymbolPredicateUnifier object.
symbols – the symbols to unify.
(default (ordered) – False): optional to return a list rather than a FactBase.
- Returns:
- a FactBase containing the unified facts, indexed by any specified indexes,
or a list if the ordered option is specified
To further simplify the interaction with the solver, Clorm provides a clingo
replacement module that offers better integration with Clorm facts and fact
bases. This module simply wraps and extends a few key Clingo classes.
Instead of:
import clingo
use:
import clorm.clingo
For convenience the clingo.Control
class can also be monkey patched so that it can used seemlessly
with existing code bases.
from clorm import monkey; monkey.patch()
import clingo
Here we document only the extended classes and the user is referred to the Clingo API documentation for more details.
- class clorm.clingo.Control(*args: Any, **kwargs: Any)
Control object for the grounding/solving process.
Behaves like
clingo.Control
but with modifications to deal with Clorm facts and fact bases.Adds an additional parameter
unifier
to specify how any generated clingo models will be unified with the clorm Predicate definitions. The unifier can be specified as a list of predicates or as a SymbolPredicateUnifier object.An existing
clingo.Control
object can be passed using thecontrol_
parameter.Control object for the grounding/solving process.
- Parameters:
arguments – Arguments to the grounder and solver.
logger – Function to intercept messages normally printed to standard error.
message_limit – The maximum number of messages passed to the logger.
Notes
Note that only gringo options (without –text) and clasp’s search options are supported. Furthermore, you must not call any functions of a Control object while a solve call is active.
- add(*args, **kwargs) None
Extend the logic program with the given non-ground logic program in string form.
This function provides two overloads:
```python def add(self, name: str, parameters: Sequence[str], program: str) -> None:
…
- def add(self, program: str) -> None:
return self.add(“base”, [], program)
- Parameters:
name – The name of program block to add.
parameters – The parameters of the program block to add.
program – The non-ground program in string form.
See also
- add_facts(facts: Iterable[Predicate | Symbol]) None
Add facts to the control object. Note: facts must be added before grounding.
This function can take an arbitrary collection containing a mixture of
clorm.Predicate
andclingo.Symbol
objects. Aclorm.FactBase
is also a valid collection but it can only containclorm.Predicate
instances.- Parameters:
facts – a collection of
clorm.Predicate
orclingo.Symbol
objects
- assign_external(external: Iterable[Predicate | Symbol | int], truth: bool | None) None
Assign a truth value to an external fact (or collection of facts)
A fact can be a raw clingo.Symbol object, a clorm.Predicate instance, or a program literal (an int). If the external is a collection then the truth value is assigned to all elements in the collection.
This function extends
clingo.Control.release_external
.Assign a truth value to an external atom.
- Parameters:
external – A symbol or program literal representing the external atom.
truth – A Boolean fixes the external to the respective truth value; and None leaves its truth value open.
See also
Control.release_external
,clingo.solving.SolveControl.symbolic_atoms
,clingo.symbolic_atoms.SymbolicAtom.is_external
Notes
The truth value of an external atom can be changed before each solve call. An atom is treated as external if it has been declared using an #external directive, and has not been released by calling Control.release_external or defined in a logic program with some rule. If the given atom is not external, then the function has no effect.
For convenience, the truth assigned to atoms over negative program literals is inverted.
- backend() Backend
Returns a Backend object providing a low level interface to extend a logic program.
See also
clingo.backend
- cleanup() None
Cleanup the domain used for grounding by incorporating information from the solver.
This function cleans up the domain used for grounding. This is done by first simplifying the current program representation (falsifying released external atoms). Afterwards, the top-level implications are used to either remove atoms from the domain or mark them as facts.
See also
Notes
Any atoms falsified are completely removed from the logic program. Hence, a definition for such an atom in a successive step introduces a fresh atom.
With the current implementation, the function only has an effect if called after solving and before any function is called that starts a new step.
Typically, it is not necessary to call this function manually because automatic cleanups are enabled by default.
- get_const(name: str) Symbol | None
Return the symbol for a constant definition of form:
#const name = symbol.
- Parameters:
name – The name of the constant to retrieve.
- Return type:
The function returns None if no matching constant definition exists.
- ground(parts: Sequence[Tuple[str, Sequence[Symbol]]] = (('base', ()),), context: Any = None) None
Ground the given list of program parts specified by tuples of names and arguments.
- Parameters:
parts – List of tuples of program names and program arguments to ground.
context – A context object whose methods are called during grounding using the @-syntax (if omitted, those from the main module are used).
Notes
Note that parts of a logic program without an explicit #program specification are by default put into a program called base without arguments.
- interrupt() None
Interrupt the active solve call.
Notes
This function is thread-safe and can be called from a signal handler. If no search is active, the subsequent call to Control.solve is interrupted. The result of the Control.solve method can be used to query if the search was interrupted.
- load(path: str) None
Extend the logic program with a (non-ground) logic program in a file.
- Parameters:
path – The path of the file to load.
- register_observer(observer: Observer, replace: bool = False) None
Registers the given observer to inspect the produced grounding.
- Parameters:
observer – The observer to register. See below for a description of the requirede interface.
replace – If set to true, the output is just passed to the observer and nolonger to the underlying solver (or any previously registered observers).
See also
clingo.backend
- register_propagator(propagator: Propagator) None
Registers the given propagator with all solvers.
- Parameters:
propagator – The propagator to register.
See also
clingo.propagator
- release_external(external: Iterable[Predicate | Symbol | int]) None
Release an external fact (or collection of facts)
A fact can be a raw clingo.Symbol object, a clorm.Predicate instance, or a program literal (an int). If the external is a collection then the truth value is assigned to all elements in the collection.
This function extends
clingo.Control.release_external
.Release an external atom represented by the given symbol or program literal.
This function causes the corresponding atom to become permanently false if there is no definition for the atom in the program. Otherwise, the function has no effect.
- Parameters:
external – The symbolic atom or program atom to release.
Notes
If the program literal is negative, the corresponding atom is released.
Examples
The following example shows the effect of assigning and releasing and external atom.
>>> from clingo.symbol import Function >>> from clingo.control import Control >>> >>> ctl = Control() >>> ctl.add("base", [], "a. #external b.") >>> ctl.ground([("base", [])]) >>> ctl.assign_external(Function("b"), True) >>> print(ctl.solve(on_model=print)) b a SAT >>> ctl.release_external(Function("b")) >>> print(ctl.solve(on_model=print)) a SAT
- solve(*args: Any, **kwargs: Any) SolveHandle | SolveResult
Run the clingo solver.
This function extends
clingo.Control.solve()
in two ways:1) The
assumptions
argument is generalised so that in the list of argument-boolean pairs the argument can be be a clingo symbol, or clorm predicate instance, or a collection of clingo symbols or clorm predicates.2) It produces either a
clorm.clingo.SolveHandle
wrapper object or aclorm.clingo.Model
wrapper objects as appropriate (depending on theyield_
,async_
, andon_model
parameters).Starts a search.
- Parameters:
assumptions – List of (atom, boolean) tuples or program literals (see clingo.symbolic_atoms.SymbolicAtom.literal) that serve as assumptions for the solve call, e.g., solving under assumptions [(Function(“a”), True)] only admits answer sets that contain atom a.
on_model – Optional callback for intercepting models. A clingo.solving.Model object is passed to the callback. The search can be interruped from the model callback by returning False.
on_unsat – Optional callback to intercept lower bounds during optimization.
on_statistics – Optional callback to update statistics. The step and accumulated statistics are passed as arguments.
on_finish – Optional callback called once search has finished. A clingo.solving.SolveResult also indicating whether the solve call has been intrrupted is passed to the callback.
on_core – Optional callback called with the assumptions that made a problem unsatisfiable.
yield – The resulting clingo.solving.SolveHandle is iterable yielding clingo.solving.Model objects.
async – The solve call and the method clingo.solving.SolveHandle.resume of the returned handle are non-blocking.
- Returns:
The return value depends on the parameters. If either yield_ or
async_ is true, then a handle is returned. Otherwise, a
clingo.solving.SolveResult is returned.
See also
clingo.solving
Notes
If neither yield_ nor async_ is set, the function returns a clingo.solving.SolveResult right away.
In gringo or in clingo with lparse or text output enabled, this function just grounds and returns a clingo.solving.SolveResult where clingo.solving.SolveResult.unknown is true.
If this function is used in embedded Python code, you might want to start clingo using the –outf=3 option to disable all output from clingo.
Asynchronous solving is only available in clingo with thread support enabled. Furthermore, the on_model and on_finish callbacks are called from another thread. To ensure that the methods can be called, make sure to not use any functions that block Python’s GIL indefinitely.
This function as well as blocking functions on the clingo.solving.SolveHandle release the GIL but are not thread-safe.
- property configuration
Object to change the configuration.
- property control_: Control
Returns the underlying clingo.Control object.
- property enable_cleanup
Whether to enable automatic calls to Control.cleanup.
- property enable_enumeration_assumption
Whether do discard or keep learnt information from enumeration modes.
If the enumeration assumption is enabled, then all information learnt from clasp’s various enumeration modes is removed after a solve call. This includes enumeration of cautious or brave consequences, enumeration of answer sets with or without projection, or finding optimal models; as well as clauses added with clingo.solving.SolveControl.add_clause.
Notes
Initially the enumeration assumption is enabled.
In general, the enumeration assumption should be enabled whenever there are multiple calls to solve. Otherwise, the behavior of the solver will be unpredictable because there are no guarantees which information exactly is kept. There might be small speed benefits when disabling the enumeration assumption for single shot solving.
- property is_conflicting
Whether the internal program representation is conflicting.
If this (read-only) property is true, solve calls return immediately with an unsatisfiable solve result.
Notes
Conflicts first have to be detected, e.g., initial unit propagation results in an empty clause, or later if an empty clause is resolved during solving. Hence, the property might be false even if the problem is unsatisfiable.
- property statistics
A dict containing solve statistics of the last solve call.
See also
clingo.statistics
Notes
The statistics correspond to the –stats output of clingo. The detail of the statistics depends on what level is requested on the command line. Furthermore, there are some functions like Control.release_external that start a new solving step resetting the current step statistics. It is best to access the statistics right after solving.
This property is only available in clingo.
- property symbolic_atoms
An object to inspect the symbolic atoms.
See also
clingo.symbolic_atoms
- property theory_atoms
An iterator over the theory atoms in a program.
See also
clingo.theory_atoms
- property unifier: SymbolPredicateUnifier | None
Get/set the unifier.
Unifier can be specified as a SymbolPredicateUnifier or a collection of Predicates. Always returns a SymbolPredicateUnifier (or None).
- class clorm.clingo.Model(model: Model, unifier: List[Type[Predicate]] | SymbolPredicateUnifier | None = None)
Provides access to a model during a solve call.
Objects mustn’t be created manually. Instead they are returned by
clorm.clingo.Control.solve
callbacks.Behaves like
clingo.Model
but offers better integration with clorm facts and fact bases.Provides access to a model during a solve call and provides a SolveContext object to influence the running search.
Notes
The string representation of a model object is similar to the output of models by clingo using the default output.
Model objects cannot be constructed from Python. Instead they are obained during solving (see Control.solve). Furthermore, the lifetime of a model object is limited to the scope of the callback it was passed to or until the search for the next model is started. They must not be stored for later use.
- contains(fact: Predicate | Symbol) bool
Return whether the fact or symbol is contained in the model. Extends
clingo.Model.contains
to allow for clorm facts as well as a clingo symbols.Efficiently check if an atom is contained in the model.
- Parameters:
atom – The atom to lookup.
- Return type:
Whether the given atom is contained in the model.
Notes
The atom must be represented using a function symbol.
- extend(symbols: Sequence[Symbol]) None
Extend a model with the given symbols.
- Parameters:
symbols – The symbols to add to the model.
Notes
This only has an effect if there is an underlying clingo application, which will print the added symbols.
- facts(*args: Any, **kwargs: Any) FactBase
Returns a FactBase containing the facts in the model that unify with the SymbolPredicateUnifier.
This function provides a wrapper around the
clingo.Model.symbols
functions, but instead of returning a list of symbols it returns a FactBase containing the facts represented asclorm.Predicate
sub-class instances.- Parameters:
unifier (list | SymbolPredicateUnifier) – used to unify and instantiate FactBase (Default: passed via the constructor if specified in the clorm.clingo.Control object)
atoms – select all atoms in the model (Default: False)
terms – select all terms displayed with #show statements (Default: False)
shown – select all atoms and terms (Default: False)
theory – select atoms added with Model.extend() (Default: False)
complement – return the complement of the answer set w.r.t. to the atoms known to the grounder. (Default: False)
raise_on_empty – raise a ValueError if the resulting FactBase is empty (Default: False)
- is_consequence(literal: int) bool | None
Check if the given program literal is a consequence.
The function returns True, False, or None if the literal is a consequence, not a consequence, or it is not yet known whether it is a consequence, respectively.
While enumerating cautious or brave consequences, there is partial information about which literals are consequences. The current state of a literal can be requested using this function. If this function is used during normal model enumeration, the function just returns whether a literal is true of false in the current model.
- Parameters:
literal – The given program literal.
- Return type:
Whether the given program literal is a consequence.
- is_true(literal: int) bool
Check if the given program literal is true.
- Parameters:
literal – The given program literal.
- Return type:
Whether the given program literal is true.
- symbols(atoms: bool = False, terms: bool = False, shown: bool = False, theory: bool = False, complement: bool = False) Sequence[Symbol]
Return the list of atoms, terms, or CSP assignments in the model.
- Parameters:
atoms – Select all atoms in the model (independent of #show statements).
terms – Select all terms displayed with #show statements in the model.
shown – Select all atoms and terms as outputted by clingo.
theory – Select atoms added with Model.extend.
complement – Return the complement of the answer set w.r.t. to the atoms known to the grounder.
- Return type:
The selected symbols.
Notes
Atoms are represented using functions (Symbol objects), and CSP assignments are represented using functions with name “$” where the first argument is the name of the CSP variable and the second its value.
- property context
Object that allows for controlling the running search.
- property cost
Return the list of integer cost values of the model.
The return values correspond to clasp’s cost output.
- property model_: Model
Returns the underlying clingo.Model object.
- property number
The running number of the model.
- property optimality_proven
Whether the optimality of the model has been proven.
- property priority
Return the priorities of the model’s cost values.
- property thread_id
The id of the thread which found the model.
- property type
The type of the model.
- class clorm.clingo.SolveHandle(handle: SolveHandle, unifier: List[Type[Predicate]] | SymbolPredicateUnifier | None = None)
Handle for solve calls.
Objects mustn’t be created manually. Instead they are returned by
clorm.clingo.Control.solve
.Behaves like
clingo.SolveHandle
but iterates overclorm.clingo.Model
objects.Handle for solve calls.
They can be used to control solving, like, retrieving models or cancelling a search.
See also
Notes
A SolveHandle is a context manager and must be used with Python’s with statement.
Blocking functions in this object release the GIL. They are not thread-safe though.
- cancel() None
Cancel the running search.
See also
clingo.control.Control.interrupt
- core() List[int]
The subset of assumptions that made the problem unsatisfiable.
- get() SolveResult
Get the result of a solve call.
If the search is not completed yet, the function blocks until the result is ready.
- model() Model | None
Get the current model if there is any.
- resume() None
Discards the last model and starts searching for the next one.
Notes
If the search has been started asynchronously, this function starts the search in the background.
- wait(timeout: float | None = None) bool
Wait for solve call to finish or the next result with an optional timeout.
If a timeout is given, the behavior of the function changes depending on the sign of the timeout. If a postive timeout is given, the function blocks for the given amount time or until a result is ready. If the timeout is negative, the function will block until a result is ready, which also corresponds to the behavior of the function if no timeout is given. A timeout of zero can be used to poll if a result is ready.
- Parameters:
timeout – If a timeout is given, the function blocks for at most timeout seconds.
- Return type:
Indicates whether the solve call has finished or the next result is ready.
- property solvehandle_: SolveHandle
Access the underlying clingo.SolveHandle object.