ASP-10 | Conditional Literals
Conditional Literals are a natural way to express "if-then" relationships within rules, enabling more intuitive problem descriptions.Photo Credit: Rob Grzywinski
Introduction
We've already seen how rules express "if-then" relationships:p ← q .
"If q is in the answer set then p must be in the answer set".But sometimes we want this "if-then" relationship within a rule, especially in choice rules. That's where conditional literals come in, using the ": " operator:p : q
Here, p is called the consequent (what we want to happen) and q is the antecedent (the condition that must be true).
Simple Example
Conditional literals are very useful to extract one argument from a predicate:population(austria,9 ; france,67 ; germany,83 ; italy,60) .
To extract the population of germany one writes:population_germany(P) : population(germany, P) .
{| population/2 |
|---|
| austria | 9 |
| france | 67 |
| germany | 83 |
| italy | 60 |
} For completeness, one may also write:population_germany(P) ← population(germany, P) .
{| population/2 |
|---|
| austria | 9 |
| france | 67 |
| germany | 83 |
| italy | 60 |
}
The Power of Choice Rules
Where conditional literals really shine is in choice rules. Consider selecting numbers:number(1..5) .
{ pick(X) : number(X) } = 2 .
This elegantly reads "Pick exactly 2 numbers from the range 1 to 5."
Complex Antecedents
Just like rules can have multiple conditions in their body, conditional literals can have multiple conditions in their antecedent:number(1..5) .
{ pick(X) : number(X), X >= 3 } = 2 .
This reads naturally as "Pick exactly 2 numbers that are both in our range and greater than or equal to 3."Or what if we want to pick only even numbers?number(1..6) .
{ pick(X) : number(X), X\2 = 0 } .
Multiple Instances
We can extend this to handle multiple cases. For example, picking numbers for different scenarios:case(a ; b) .
number(1..5) .
{ pick(C, X) : number(X), X >= 3, case(C) } = 2 .
Notice how the conditional literal helps us express "for each case, pick two numbers ≥ 3" in a single, readable rule.{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} Common Pitfall: Statement vs Expression Variables
Here's where many people get tripped up. Consider these two seemingly similar rules:- Statement (global) variables:
{ pick(C, X) } = 2 ← number(X), case(C) .
- Expression (local) variables:
{ pick(C, X) : number(X), case(C) } = 2 .
The version with statement variables (10) is unsatisfiable while the version with expression variables (11) works as intended! Why? Remember that statement (global) variables always instantiate new rules.This is why conditional literals are so valuable — they let us express complex relationships while avoiding the pitfalls of statement variables.
Logical Equivalent
To fully understand conditional literals, it helps t see how they behave with simple truth values. Let's break down all possible combinations:% q ← p
true_true ← ⊤ : ⊤ .
true_false ← ⊤ : ⊥ .
false_true ← ⊥ : ⊤ .
false_false ← ⊥ : ⊥ .
The result is {false_falsetrue_falsetrue_true}
. This is exactly the truth table for the logical implication p → q (which is why conditional literals are also called nested implications).
Complex Example
Let's tackle a challenging problem that showcases the power of conditional literals: Given a set of random numbers, find all sequential pairs in ascending order:number(10 ; 1 ; 4 ; 7 ; 3) .
sequential(X, Z) ← number(X), ⊥ : X < Y, number(Y), Y < Z ; number(Z), X < Z .
Initially one might think that the encoding is simply:sequential(X, Z) ← number(X), number(Z), X < Z .
but as we have seen before (in [Rules Part II|Example: Genealogy]) (15) returns all pairs where the symmetric duplicates have been removed. The encoding needs to ensure that there is no other number Y between the chosen pair X and Z. This is where the conditional literal comes in. Reformatting (14) should make the encoding a bit more clear:sequential(X, Z) ←
number(X), % For each number X
⊥ : X < Y, number(Y), Y < Z ; % If there exists no Y between X and Z
number(Z), X < Z . % And Z is a number greater than X
There are a few things to note:- The antecedent of a conditional literal may contain a conjunction of terms separated by "
,". Since conditional literals may be found between other terms in the body of a rule (which are also separated by ","), the conditional literal is terminated by a ";". This ";" is not to be interpreted as an "or" in this case. - Since we needed the conditional literal to behave as a boolean value based on the value of the antecedent, the literal
⊥ is used as the consequent. (Refer to the truth table above if you're unsure of why this is.) Y is an expression (local) variable — is it located entirely within the body of the rule and specifically within a single term of the body. Recall from [Variables|Variable Scope] that statement (global) variables always instantiate a new rule (see X and Z) whereas expression (local) variables do not.
Takeaways
Conditional literals are powerful tools in ASP that:- Express Natural Relationships: The "
: " operator provides an intuitive way to write "if-then" conditions within rules - Work Well with Choice Rules: They shine when combined with choice rules for selecting elements that meet specific criteria
- Handle Complex Conditions: Multiple conditions in the antecedent allow sophisticated filtering
- Avoid Variable Scope Issues: Using expression variables in conditional literals prevents common pitfalls with statement variables
- Enable Elegant Solutions: As shown in the sequential pairs example, they can express complex logic concisely
When writing ASP programs, conditional literals should be one of your go-to tools for expressing relationships between atoms, especially when working with choice rules or when you need fine-grained control over which atoms are included in answer sets.