Rework of chapter 5
This commit is contained in:
parent
4a6a542f8d
commit
440741fa3d
@ -1,99 +1,323 @@
|
|||||||
# Constraint Satisfaction Problems
|
# Constraint Satisfaction Problems
|
||||||
|
|
||||||
Book pg. 164 to 169
|
> [!NOTE]
|
||||||
|
> From now on States have a factored representation of the following type
|
||||||
|
>
|
||||||
|
> - **Variables** ($\mathcal{X}$): Actual variables in the State
|
||||||
|
> - **Domains**($\mathcal{D}$): Set of values that a variable can get (one per variable)
|
||||||
|
> - **Constraints** ($\mathcal{C}$): Constraints about values assignable to variables
|
||||||
|
|
||||||
We have 3 components
|
These problems concern about finding the right assignment of values to all variables so that
|
||||||
|
the assignment respects all constraints. For example, let's imagine that we could only get
|
||||||
|
cats in spaces with other pets, but small ones. This is indeed a CSP problem where we need to find
|
||||||
|
a solution where cats are never with small pets.
|
||||||
|
|
||||||
- $\mathcal{X}$: Set of variables
|
```python
|
||||||
- $\mathcal{D}$: Set of domains, one (domain) for each variable
|
class StateCSP(State):
|
||||||
- $\mathcal{C}$: Set of constraints
|
|
||||||
|
|
||||||
Here we try to assign a value to variables, such that each variable holds a value contained in their
|
def __init__(
|
||||||
domain, without violating any constraint.
|
self,
|
||||||
|
variables: dict[str, Value],
|
||||||
|
domains: dict[str, set[Value]],
|
||||||
|
# The scope is represented as a list of strings
|
||||||
|
# because these are variables in this representation
|
||||||
|
constraints: dict[list[str], Rule | set[Value]]
|
||||||
|
):
|
||||||
|
self.__variables = variables
|
||||||
|
self.__domains = domains
|
||||||
|
self.__constraints = constraints
|
||||||
|
|
||||||
A solution to this problem must be:
|
```
|
||||||
|
|
||||||
- Consistent: Each assignment is legal
|
In this space, in constrast with the atomic one, we can see if we are doing a mistake during assignments. Whenever we detect such
|
||||||
- Complete: All variables have a value assigned
|
mistakes, we **prune** that path, since it is **not admissable**. This makes the computation a bit faster, since we can detect
|
||||||
|
early failures.
|
||||||
|
|
||||||
## Inference in CSP
|
## Constraints
|
||||||
|
|
||||||
We can inference a solution in these ways
|
> [!CAUTION]
|
||||||
|
> Constraints can be of these types:
|
||||||
|
>
|
||||||
|
> - **Unary**: One variable involved (For example Bob does not like Shirts)
|
||||||
|
> - **Binary**: Two variables involved
|
||||||
|
> - **Global**: More than Two variables are involved
|
||||||
|
|
||||||
- Generate successors and if not consistent assignments, prune and continue
|
### Preference Contstraints
|
||||||
- Constraint Propagation:\
|
|
||||||
Use contraints to reduce possible assignments and mix with search, if needed
|
|
||||||
|
|
||||||
The idea is to treat each assignment as a **vertex** and each contraint as an **edge**, we our tree would not have inconsistent values, making
|
These are constraints that **should be** followed, but technically can be ignored as far as the problem is concerned.
|
||||||
the search faster.
|
|
||||||
|
|
||||||
## Consistency
|
In order to account for these preferences, it is possible to **assign a higher cost** to moves that **break these constraints**
|
||||||
|
|
||||||
### Node consistency
|
> [!NOTE]
|
||||||
|
> In these cases we could also talk about COPs (Constraint Optimization Problems)
|
||||||
|
|
||||||
A domain already satisfies constraints
|
### Common Global Constraints
|
||||||
|
|
||||||
|
- **Aldiff (all different)**: Each variable in the constraint must have a unique value
|
||||||
|
- **Heuristic**: If the set of all values has a size lower than the number of variables, then it is impossible
|
||||||
|
- **Heuristic Algorithm**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def aldiff_heuristic(csp_problem):
|
||||||
|
|
||||||
|
# May be optimized with a heap or priority queue
|
||||||
|
# sorted by domain_size
|
||||||
|
variables : Queue = csp_problem.variables
|
||||||
|
possible_values = set()
|
||||||
|
|
||||||
|
for variable in variables:
|
||||||
|
prossible_values.extend(
|
||||||
|
variable.domain
|
||||||
|
)
|
||||||
|
|
||||||
|
halt = False
|
||||||
|
|
||||||
|
while not halt:
|
||||||
|
|
||||||
|
# Count variables to avoid overflow
|
||||||
|
remaining_variables = len(variables)
|
||||||
|
# Check if there were eliminable variables
|
||||||
|
variables_with_singleton_domain = 0
|
||||||
|
|
||||||
|
# Check if any of these variables in a singleton
|
||||||
|
# variable
|
||||||
|
for _ in range(0, remaining_variables):
|
||||||
|
|
||||||
|
candidate_variable = variables.pop()
|
||||||
|
domain_size = len(variable.domain)
|
||||||
|
|
||||||
|
# Check for domain size
|
||||||
|
match(domain_size):
|
||||||
|
|
||||||
|
# Empty domain, problem can't be solved
|
||||||
|
case 0:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Singleton domain
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
# Add count
|
||||||
|
variables_with_singleton_domain += 1
|
||||||
|
# Take value
|
||||||
|
value = candidate_variable.domain[0]
|
||||||
|
# Remove value from all possible values
|
||||||
|
possible_values.remove(value)
|
||||||
|
|
||||||
|
# Remove value from all domains
|
||||||
|
for variable in variables:
|
||||||
|
variable.domain.remove(value)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Variable has more values, reinsert in queue
|
||||||
|
_:
|
||||||
|
variables.push(variable)
|
||||||
|
|
||||||
|
# No singleton variables found, algorithm
|
||||||
|
# would stall, halt now
|
||||||
|
if variables_with_singleton_domain == 0:
|
||||||
|
halt = True
|
||||||
|
|
||||||
|
# Check that we have enough values to
|
||||||
|
# assign to all variables
|
||||||
|
return len(possible_values) >= len(variables)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Atmost**: It defines a maximum amount of a resource
|
||||||
|
- **Heuristic**: Check if the sum of all minimum values in all scope variables domain is higher than the max value
|
||||||
|
|
||||||
|
## Constraint Propagation
|
||||||
|
|
||||||
|
It's a step in which we **prune** some **values** from **domains**. This step can be alternated with search or be a preprocessing step.
|
||||||
|
|
||||||
|
For the sake of this step, we'll construct some graphs where **variables** are nodes and **binary-constraints** are edges.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> We are not talking about search graphs, as this is another step unreleated with search
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Even though it's not a search, sometimes it can solve our problem before even beginning to search. Moreover each global constraint can become a binary constraint,
|
||||||
|
> making this step always possible to implement (at a higher computational cost of either the machine or the human transforming these constraints)
|
||||||
|
|
||||||
|
### Node Consistency
|
||||||
|
|
||||||
|
A **node** is **node-consistent** when **all values in its domain satifies its unary constraints**
|
||||||
|
|
||||||
### Arc Consistency
|
### Arc Consistency
|
||||||
|
|
||||||
A variable is arc consistent wrt another variable id its domain respects all constraints
|
A **node** is **arc-consistent** when **all values in its domain satisfies all of binary constraints it is involved to**
|
||||||
|
|
||||||
> [!WARNING]
|
AC3 is an Algorithm that can help in achieving this with a Time Complexity of $O(n^3)$ or $O(\text{constraints} \cdot \text{size\_values}^3)$
|
||||||
> Arco Consistency is not useful for every problem
|
|
||||||
|
|
||||||
> [!NOTE]
|
```python
|
||||||
> AC-3 is an algorithm to achieve Arc Consistency (alorithm on page 171 book)
|
def AC3(csp_problem):
|
||||||
|
|
||||||
|
# Take all constraints
|
||||||
|
queue : queue[Constraint] = csp_problem.edges
|
||||||
|
|
||||||
|
# Examine all constraints
|
||||||
|
while len(queue) > 0:
|
||||||
|
|
||||||
|
# Take the first and initialize relevant variables
|
||||||
|
constraint = queue.pop
|
||||||
|
X = constraint.scope[0]
|
||||||
|
Y = constraint.scope[1]
|
||||||
|
|
||||||
|
# Correct domain if needed
|
||||||
|
correction_applied = apply_constraint([X, Y], rule)
|
||||||
|
|
||||||
|
# If correction was applied, check that there
|
||||||
|
# are still admissable variables
|
||||||
|
if correction_applied:
|
||||||
|
|
||||||
|
# If there aren't, then notify problem is not
|
||||||
|
# solvable
|
||||||
|
if len(X.domain) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If there are still admissable variables, focus
|
||||||
|
# on its neighbours
|
||||||
|
for neighbour in (X.neighbours - Y):
|
||||||
|
queue.push(csp_problem.search(neighbour, X))
|
||||||
|
|
||||||
|
# Notify that problem has been correctly constrained
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def apply_constraint(variables: list[Variable], rule: Rule):
|
||||||
|
|
||||||
|
# Gather all sets of values
|
||||||
|
x_domain = set(variables[0].domain)
|
||||||
|
y_domain = set(variables[1].domain)
|
||||||
|
|
||||||
|
# Set modified to false
|
||||||
|
x_domain_modified = False
|
||||||
|
|
||||||
|
# Check if all variables satisfy constraint
|
||||||
|
for x in x_domain:
|
||||||
|
rule_satisfied = False
|
||||||
|
|
||||||
|
# If a single y makes the couple satisfy the constraint,
|
||||||
|
# break early
|
||||||
|
for y in y_domain
|
||||||
|
if rule.check(x, y):
|
||||||
|
rule_satisfied = True
|
||||||
|
break
|
||||||
|
|
||||||
|
# If no y satisfied the couple (x, y) then remove x
|
||||||
|
if not rule_satisfied:
|
||||||
|
x_domain.remove(x)
|
||||||
|
x_domain_modified = True
|
||||||
|
|
||||||
|
# Return if domain was modified
|
||||||
|
return x_domain_modified
|
||||||
|
```
|
||||||
|
|
||||||
### Path Consistency
|
### Path Consistency
|
||||||
|
|
||||||
It is achievable on 3 variables and says that a set of 2 variable is path consistent wrt a 3rd only if for each assignment
|
A **set** of **2 variables** is **path consistent to a 3rd variable** if for **each valid assignment satisfying their arc consistency** there exists **at least a value** for
|
||||||
consistent with constraints between these variables makes it possible to make a legal assignment on the 3rd
|
a **3rd variable** so that it **satisfies its binary constraints with the other two variables**
|
||||||
|
|
||||||
### K-Consistency
|
### K-Consistency
|
||||||
|
|
||||||
basically the same as the one above, but for k-variables, technically we need $O(n^2 d)$ to check if a problem is
|
A **variable** is **k-consistent** if it's also **(k-1)-consistent** down to **1-consistent (node-consistent)**
|
||||||
total k-consistent (consistent from 1 up to k), however it is expensive and only 2-Consistency is usually computed.
|
|
||||||
|
|
||||||
## Global Contraints
|
### Bound Propagation
|
||||||
|
|
||||||
Constraints that take an arbitrary number of variables (but not necessarily all).
|
Sometimes, when we deal with lots of numbers, it's better to represent possible values as a bound (or a union of bounds).
|
||||||
|
|
||||||
### Checking for Aldiff Constraint
|
In this case we need to check that there exists a solution for both the maximum and minimum values for all variables in order to
|
||||||
|
have **bound-consistency**
|
||||||
|
|
||||||
Aldiff constraint says that all variables must have different values.
|
## Backtracking Search
|
||||||
|
|
||||||
A way to check for inconsistencies is to remove variables with single value domains and remove such value
|
This is a step coming **after** the **constraint propagation** when we have multiple values assignable to variables and is a *traditional search*
|
||||||
from all domains, then iterate until there are no more single value domain variables.
|
over the problem.
|
||||||
|
|
||||||
If an empty domain is produced, or there are more variables than domain values, congrats!! You found an inconsistency
|
It can be interleaved with [constraint propagation](#constraint-propagation) at each step to see if we are breaking a constraint
|
||||||
|
|
||||||
### Checking for Atmost Constraint
|
### Commutativity
|
||||||
|
|
||||||
Atmost constraint says that we can assign max tot resources
|
Since our problem **may** not care about the specific value we may take, we can just consider the number of assignments.
|
||||||
|
In other words, we don't care much about the value we are assigning, but rather about the assignment itself
|
||||||
|
|
||||||
Sum all minimum resources for each problem, and if it goes over the max, we have an inconsistency
|
### Heuristics
|
||||||
|
|
||||||
### Bound Consistency
|
To make the algorithm faster, we can have some heuristics to help us choose the best strategy:
|
||||||
|
|
||||||
We check that for each variable X and for both the lower and higher bound of X there exists a value of U that satisfies the constraints between X and each Y.
|
- **Choose Variable to Assign**:
|
||||||
|
- **minimum-remaining-values (MRV)**: consists on choosing the **variable with the least amount of values in its domain**
|
||||||
|
- **Pros**:
|
||||||
|
- Detection of Failures is faster
|
||||||
|
- **Cons**:
|
||||||
|
- There may be still ties among variables
|
||||||
|
- **degree heuristic**: choose the **variable involved the most in constraints**
|
||||||
|
- **Pros**:
|
||||||
|
- Fast detection of Failures
|
||||||
|
- Can be a tie-breaker combined with MRV
|
||||||
|
- **Cons**:
|
||||||
|
- Worse results than MRV
|
||||||
|
- **Choose Value to assign**:
|
||||||
|
- **least-constraining-value**: choose the **value that rules out the fewest choices among the variable neighbours**
|
||||||
|
- **Pros**:
|
||||||
|
- We only case about assigning, not what we assign
|
||||||
|
- **Inference**:
|
||||||
|
- **forward checking**: **propagate constraintsfor all unassigned variables**
|
||||||
|
- **Pros**:
|
||||||
|
- Fast
|
||||||
|
- **Cons**:
|
||||||
|
- Does not detect all inconsistencies
|
||||||
|
- **MAC - Maintaining Arc Consistency**: It's a **call to [AC3](#arc-consistency)** only with **constrainst involving our assigned variable**
|
||||||
|
- **Pros**:
|
||||||
|
- Detects all inconsistencies
|
||||||
|
- **Cons**:
|
||||||
|
- Slower than forward checking
|
||||||
|
- **Backtracking - Where should we go back once we fail**
|
||||||
|
- **chronological backtracing**: go a step back
|
||||||
|
- **Pros**:
|
||||||
|
- Easy to implement
|
||||||
|
- **Cons**:
|
||||||
|
- Slower time of computation
|
||||||
|
- **backjumping**: We go to a step where we made an **assignment that led to a conflict**. This is accomplished by having conflicts sets, sets where we track\
|
||||||
|
**variable assignments** to **variables involved in constraints**
|
||||||
|
- **Pros**:
|
||||||
|
- Faster time of computation
|
||||||
|
- **Cons**:
|
||||||
|
- Need to track a conflict set
|
||||||
|
- Useless if any **inference** heuristic has been used
|
||||||
|
- **conflict-directed backjumping**: We jump to the assignment that led to a **chain of conflicts**. Now, each time an we detect a failure, we propagate the conflict\
|
||||||
|
set over the backtraced variable **recursively**
|
||||||
|
- **Pros**:
|
||||||
|
- Faster time of computation
|
||||||
|
- **Cons**:
|
||||||
|
- Harder to implement
|
||||||
|
- **Constraint Learning**
|
||||||
|
- **no-good**: We record a set of variables that lead to a failure. Whenever we meet such a set, we fail immediately, or we avoid it completely
|
||||||
|
- **Pros**:
|
||||||
|
- Faster time of computation
|
||||||
|
- **Cons**:
|
||||||
|
- Memory expensive
|
||||||
|
|
||||||
## Backtracking Search in CSP
|
## Local Search
|
||||||
|
|
||||||
Book pg 175 to 178
|
The idea is that instead of **building** our goal state, we start from a **completely assigned state** (usually randomically) and we proceed from there on.
|
||||||
|
|
||||||
After finishing the constraint propagation, reducing our options, we still need to search for a solution.
|
Can also be useful during online searching
|
||||||
|
|
||||||
- The order of values is not relevant (we can save up on computation and memoru)
|
### Local Search Heuristics
|
||||||
|
|
||||||
### Forward Checking
|
- **min-conflicts**: Change a random variable (that has conflcts) to a value that would minimize conflicts
|
||||||
|
- **Pros**:
|
||||||
<!-- TODO -->
|
- Almost problem size independent
|
||||||
|
- Somewhat fast
|
||||||
### Intelligent Backtracking (Backjumping)
|
- **Cons**:
|
||||||
|
- Subsceptible to Plateaus (use taboo-search or simulated annealing)
|
||||||
<!-- TODO -->
|
- **constraint weighting**: Concentrate the search over solving constraints that have a higher value (each one starting with a value of 1). Choose the\
|
||||||
|
pair variable/value that minimizes this value and increment each unsolved constraint by one
|
||||||
## The structure of Problems
|
- **Pros**:
|
||||||
|
- Adds Topology to plateaus
|
||||||
<!-- TODO -->
|
- **Cons**:
|
||||||
|
- Difficult to implement
|
||||||
Book from 183 to 187
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user