package aima.core.search.csp.inference;
import java.util.Queue;
import aima.core.search.csp.*;
import aima.core.search.framework.QueueFactory;
/**
*
* Artificial Intelligence A Modern Approach (3rd Ed.): Figure 6.3, Page 209.<br>
* <br>
*
* <pre>
* <code>
* function AC-3(csp) returns false if an inconsistency is found and true otherwise
* inputs: csp, a binary CSP with components (X, D, C)
* local variables: queue, a queue of arcs, initially all the arcs in csp
* while queue is not empty do
* (Xi, Xj) = REMOVE-FIRST(queue)
* if REVISE(csp, Xi, Xj) then
* if size of Di = 0 then return false
* for each Xk in Xi.NEIGHBORS - {Xj} do
* add (Xk, Xi) to queue
* return true
*
* function REVISE(csp, Xi, Xj) returns true iff we revise the domain of Xi
* revised = false
* for each x in Di do
* if no value y in Dj allows (x ,y) to satisfy the constraint between Xi and Xj then
* delete x from Di
* revised = true
* return revised
* </code>
* </pre>
*
* Figure 6.3 The arc-consistency algorithm AC-3. After applying AC-3, either
* every arc is arc-consistent, or some variable has an empty domain, indicating
* that the CSP cannot be solved. The name "AC-3" was used by the algorithm's
* inventor (Mackworth, 1977) because it's the third version developed in the
* paper.
*
* @author Ruediger Lunde
*/
public class AC3Strategy<VAR extends Variable, VAL> implements InferenceStrategy<VAR, VAL> {
/**
* Makes a CSP consisting of binary constraints arc-consistent.
*
* @return An object which indicates success/failure and contains data to
* undo the operation.
*/
public InferenceLog<VAR, VAL> apply(CSP<VAR, VAL> csp) {
Queue<VAR> queue = QueueFactory.createLifoQueue();
queue.addAll(csp.getVariables());
DomainLog<VAR, VAL> log = new DomainLog<>();
reduceDomains(queue, csp, log);
return log.compactify();
}
/**
* Reduces the domain of the specified variable to the specified value and
* reestablishes arc-consistency. It is assumed that the provided CSP is
* arc-consistent before the call.
*
* @return An object which indicates success/failure and contains data to
* undo the operation.
*/
public InferenceLog<VAR, VAL> apply(VAR var, Assignment<VAR, VAL> assignment, CSP<VAR, VAL> csp) {
Domain<VAL> domain = csp.getDomain(var);
VAL value = assignment.getValue(var);
assert domain.contains(value);
DomainLog<VAR, VAL> log = new DomainLog<>();
if (domain.size() > 1) {
Queue<VAR> queue = QueueFactory.createLifoQueue();
queue.add(var);
log.storeDomainFor(var, domain);
csp.setDomain(var, new Domain<>(value));
reduceDomains(queue, csp, log);
}
return log.compactify();
}
private void reduceDomains(Queue<VAR> queue, CSP<VAR, VAL> csp, DomainLog<VAR, VAL> log) {
while (!queue.isEmpty()) {
VAR var = queue.remove();
for (Constraint<VAR, VAL> constraint : csp.getConstraints(var)) {
if (constraint.getScope().size() == 2) {
VAR neighbor = csp.getNeighbor(var, constraint);
if (revise(neighbor, var, constraint, csp, log)) {
if (csp.getDomain(neighbor).isEmpty()) {
log.setEmptyDomainFound(true);
return;
}
queue.add(neighbor);
}
}
}
}
}
/**
* Establishes arc-consistency for (xi, xj).
* @return Value true if the domain of xi was modified.
*/
private boolean revise(VAR xi, VAR xj, Constraint<VAR, VAL> constraint,
CSP<VAR, VAL> csp, DomainLog<VAR, VAL> log) {
boolean revised = false;
Assignment<VAR, VAL> assignment = new Assignment<>();
for (VAL vi : csp.getDomain(xi)) {
assignment.add(xi, vi);
boolean found = false;
for (VAL vj : csp.getDomain(xj)) {
assignment.add(xj, vj);
if (constraint.isSatisfiedWith(assignment)) {
found = true;
break;
}
}
if (!found) {
log.storeDomainFor(xi, csp.getDomain(xi));
csp.removeValueFromDomain(xi, vi);
revised = true;
}
}
return revised;
}
}