We can augment the basic idea with such concepts as optimization (for example, to maximize or minimize some expression subject to a set of constraints), or with preferential as well as required constraints.
Constraints are declarative: ideally, they let the user express what is wanted rather than how to do it. They can often be used in multiple ways. For example, given an Ohm's Law constraint V=I*R, if we know V and I we can find R; but if we know I and R we can find V. Constraints also allow partial information to be expressed and combined: one constraint might specify that X is non-negative, another that X is less than 10.
Unfortunately, it is often easier to express constraints than it is to solve them, so that for practical systems we need to limit the class of constraints that can be expressed, give information on how to solve them, or both.
Constraints can be embedded in a fundamental way in a programming language. In 341 we will look at the Constraint Logic Programming (CLP) language framework. CLP(D) is a language framework, parameterized by the domain of the constraints. We'll be using CLP(R), constraint logic programming for the reals. CLP is basically a superset of the logic programming language Prolog, so we'll be learning about logic programming at the same time.
Example CLP languages:
Trees are constructed from atoms (written as a symbol beginning with a lower case letter) and "uninterpreted functors" (function symbols). Examples:
Scope rules are simple: variables locally scoped within a fact, rule, or query.
Syntax: variables begin with a capital letter.
Anonymous variable: _ (underscore)
Examples:
For lists, the vertical bar divides the elements at the front of the list from the rest of the list.
For real numbers, the relational operators are +, <=, <, >= and >. The function symbols are + - * / sin cos pow max min abs.
Examples:
However, if the constraint store contains an unsatisfiable set of constraints, we can stop rewriting immediately.
We may have multiple rules for a given user-defined constraint. We search through the possible rules using a depth-first search, backtracking if one of the choices fails.
A CLP(R) program consists of a collection of rules. Each rule has the form
Dual reading of CLP(R) rules
A goal is a sequence of primitive and user-defined constraints.
Execution model (still an informal description):
Suppose the user presents a goal G, where G = L1, ... Lm. We process the literals in order (left-to-right). If Li is a primitive constraint, we add it to the "constraint store". If the constraint solver can determine that the constraints in the store are unsatisfiable, we either backtrack and try another choice, or signal a failure if there aren't any more choices. (More about choices and backtracking shortly.)
Otherwise Li must be a user-defined constraint. We look for a rule matching that constraint, set up constraints between the arguments of Li and those of the head of the rule, and replace Li with the body of the rule. (This is a bit like calling a procedure, and passing the arguments by setting up equality constraints between the actual and formal parameters.) When we do this replacement, we get fresh variables for those in the rule -- there isn't a problem if we happened to have a variable with the same name in the goal and the rule.
There may be several possible rules that are applicable to process Li. If there are, pick the first one (but remember that there was a choice, in case we need to try another one later).
Continue on in this way until all of the user-defined constraints have been rewritten as primitive constraints. If the primitive constraints are not known to be unsatisfiable, return an answer. The answer is the set of constraints in the store with respect to the variables in the original goal. In some cases this answer will be specific values for each variable in the goal, but in other cases it will be some more general constraints on the variables.
At any point along the way, if the system decides the constraints in the constraint store are unsatisfiable, backtrack and try another choice of rule. (This is done using a depth-first search.) If there are no more possible rules to try, the result is failure.
The user can also reject an answer; in that case, backtrack and try another choice of rule for a user-defined constraint (again, using a depth-first search).