2021-12-08
Slides by Jo Devriendt & Benjamin Callewaert
Let's quiz the homework :)
Let's quiz the homework :)
Let's quiz the homework :)
Let's quiz the homework :)
vocabulary People {
type Person // type
parentOf: (Person * Person) -> Bool // predicate
man: (Person) -> Bool // predicate
woman: (Person) -> Bool // predicate
brotherOf: (Person * Person) -> Bool // predicate
sisterOf: (Person * Person) -> Bool // predicate
age: (Person) -> Int // functor
eldestDaughter: () -> Person // functor
}
builtin type
constant: functor with arity 0
structure Simpsons : People {
Person := {marge, homer, lisa, bart, maggie}
parentOf := { (marge, bart), (marge, lisa), (marge, maggie),
(homer, bart), (homer, lisa), (homer, maggie) }
man := { (homer), (bart) }
woman := ??
brotherOf := ??
sisterOf := ??
age := { (homer) -> 36, (marge) -> 34, (bart) -> 10,
(lisa) -> 8, (maggie) -> 1 }
eldestDaughter := ??
}
domain element
structure Simpsons : People {
Person := {marge, homer, lisa, bart, maggie}
parentOf := { (marge, bart), (marge, lisa), (marge, maggie),
(homer, bart), (homer, lisa), (homer, maggie) }
man := { (homer), (bart) }
woman := ??
brotherOf := ??
sisterOf := ??
age := { (homer) -> 36, (marge) -> 34, (bart) -> 10,
(lisa) -> 8, (maggie) -> 1 }
eldestDaughter := ??
}
vocabulary People {
type Person // type
parentOf: (Person * Person) -> Bool // predicate
man: (Person) -> Bool // predicate
woman: (Person) -> Bool // predicate
brotherOf: (Person * Person) -> Bool // predicate
sisterOf: (Person * Person) -> Bool // predicate
age: (Person) -> Int // functor
eldestDaughter: () -> Person // functor
}
set
relation
relation
function
structure Simpsons : People {
Person := {marge, homer, lisa, bart, maggie}
parentOf := { (marge, bart), (marge, lisa), (marge, maggie),
(homer, bart), (homer, lisa), (homer, maggie) }
man := { (homer), (bart) }
woman := { (marge), (lisa), (maggie) }
brotherOf := { (bart, lisa), (bart, maggie) }
sisterOf := { (lisa, bart), (maggie, bart), (lisa, maggie),
(maggie, lisa) }
age := { (homer) -> 36, (marge) -> 34, (bart) -> 10,
(lisa) -> 8, (maggie) -> 1 }
eldestDaughter := { () -> lisa }
}
vocabulary People {
type Person // type
parentOf: (Person * Person) -> Bool // predicate
man: (Person) -> Bool // predicate
woman: (Person) -> Bool // predicate
brotherOf: (Person * Person) -> Bool // predicate
sisterOf: (Person * Person) -> Bool // predicate
age: (Person) -> Int // functor
eldestDaughter: () -> Person // functor
}
vocabulary MapCol {
type Country
type Color
border: (Country * Country) -> Bool
colorOf: (Country) -> Color
}
structure Correct : MapCol {
Country := { BE, NL, DE, LU, FR }
Color := { R, G, B, Y}
border := { (NL, BE), (NL, DE), (BE, LU),
(BE, FR), (LU, FR), (LU, DE), (DE, FR) }
colorOf := { (BE)->B, (NL)->R,
(DE)->G, (LU)->Y, (FR)->R }
}
structure Nonsense : MapCol {
Country := { BE, NL, DE, LU, FR }
Color := { R, G, B, Y}
border := { (BE, BE), (FR, NL) }
colorOf := { (BE)->R, (NL)->R, (DE)->R, (LU)->R, (FR)->R }
}
??
maggie
eldestDaughter()
age(eldestDaughter())
eldestDaughter() = maggie
maggie ~= eldestDaughter()
age(eldestDaughter()) ~= 0
brotherOf(eldestDaughter(), bart)
NOTE: FO(.) is typed!
age(0) = maggie
vocabulary People {
type Person := {homer, marge, bart, lisa, maggie}
parentOf: (Person * Person) -> Bool // predicate
man: (Person) -> Bool // predicate
woman: (Person) -> Bool // predicate
brotherOf: (Person * Person) -> Bool // predicate
sisterOf: (Person * Person) -> Bool // predicate
age: (Person) -> Int // functor
eldestDaughter: () -> Person // functor
}
homer
man
0 ~= 1
bart = brotherOf(lisa)
parentOf(marge, eldestDaughter())
age(lisa) = eldestDaughter()
age(eldestDaughter())
age(age(age(age(lisa))))
age(bart) ~= age(eldestDaughter())
sisterOf(eldestDaughter(),bart)
eldestDaughter()
parentOf(bart, eldestDaughter())
term, atom or bug?
term (domain element)
bug: predicate without input
atom, maps to true/false (0 and 1 ar domain elements of builtIn type Int)
bug, brotherOf takes 2 arguments
atom (false in above structure, but that does not matter)
bug, eldestDaughter() maps to Person, age maps to Int
term, maps to Int
bug, age maps to Int and takes Person as input, so can not be applied to itself
atom
atom
term
atom (correct predicate)
A structure is a "possible world". Terms and atoms are statements which can be evaluated in a structure. This evaluation under a structure is (again) called an interpretation.
Informally, a structure is a solution, interpretations are values, and predicates & functors are variables. Statements such as terms, atoms, formulas have a derived value.
Given a structure U, the interpretation of
maggie
eldestDaughter()
age(eldestDaughter())
age := {(homer) -> 36,
(marge) -> 34,
(bart) -> 10,
(lisa) -> 8,
(maggie) -> 1}
eldestDaughter :=
{() -> lisa}
Interpretations of terms are always domain elements of some type
Given a structure U, the interpretation of
eldestDaughter() = maggie
maggie ~= eldestDaughter()
age(eldestDaughter()) ~= 0
brotherOf(eldestDaughter(), bart)
Interpretations of atoms are truth values:
true or false
structure Simpsons : People {
Person := {marge, homer, lisa, bart, maggie}
parentOf := { (marge, bart), (marge, lisa), (marge, maggie),
(homer, bart), (homer, lisa), (homer, maggie) }
man := { (homer), (bart) }
woman := { (marge), (lisa), (maggie) }
brotherOf := { (bart, lisa), (bart, maggie) }
sisterOf := { (lisa, bart), (maggie, bart), (lisa, maggie),
(maggie, lisa) }
age := { (homer) -> 36, (marge) -> 34, (bart) -> 10,
(lisa) -> 8, (maggie) -> 1 }
eldestDaughter := { () -> lisa }
}
eldestDaughter() = maggie
maggie ~= eldestDaughter()
age(eldestDaughter()) ~= 0
brotherOf(eldestDaughter(), bart)
false
true
true
false
homer
0 ~= 1
eldestDaughter()
brotherOf(bart,lisa)
age(eldestDaughter())
age(bart) ~= age(eldestDaughter())
parentOf(bart, eldestDaughter())
if not bug, what is interpretation in Simpsons?
term: homer (interpretation odomain element is itself)
atom: true
term: lisa
atom: true
term: 8
atom: true
atom: false
brotherOf(eldestDaughter(), bart)
~brotherOf(eldestDaughter(), bart)
~brotherOf(eldestDaughter(), bart) & bart ~= eldestDaughter()
brotherOf(eldestDaughter(), bart) | bart ~= eldestDaughter()
eldestDaughter()=lisa => woman(eldestDaughter())
eldestDaughter()=lisa <=> woman(eldestDaughter())
A QF formula is either
Order of operations:
A QF formula is either
Given a structure U, the interpretation of
Tricky!
P | Q | P => Q |
---|---|---|
false | false | true |
false | true | true |
true | false | false |
true | true | true |
P | Q | P <=> Q |
---|---|---|
false | false | true |
false | true | false |
true | false | false |
true | true | true |
Truth table
rain() | cloudy() | rain() => cloudy() |
---|---|---|
false | false | true |
false | true | true |
true | false | false |
true | true | true |
rain() => cloudy()
"If it rains, then it is cloudy."
This is still true even if there are clouds but no rain!
rain() | cloudy() | ~rain() | cloudy() |
---|---|---|
false | false | true |
false | true | true |
true | false | false |
true | true | true |
rain() => cloudy()
"If it rains, then it is cloudy."
Note that the implication is equivalent to the following disjunction:
~rain() | cloudy()
Logical Equivalence: Two formulas F and G are logically equivalent F ≡ G if the truth values of both formulas F and G are always the same.
(P ⇒ Q) ≡ (¬Q ⇒ ¬P) (contraposition)
Same meaning - equivalent
Same meaning - equivalent
Same meaning - equivalent
Verify via truth tables
age(eldestDaughter())~=10
sisterOf(eldestDaughter(),lisa)
man(homer) | woman(homer)
sisterOf(bart,maggie) | (brotherOf(bart,lisa) & brotherOf(bart,marge))
woman(homer) <=> ~man(homer)
age(eldestDaughter())=10 <=> age(bart)=10
age(eldestDaughter())=10 => age(bart)=10
age(eldestDaughter())=8 => age(bart)=10 & age(maggie)=1
What is the interpretation of the following QF formulas?
structure Simpsons : People {
Person := {marge, homer, lisa, bart, maggie}
parentOf := { (marge, bart), (marge, lisa), (marge, maggie),
(homer, bart), (homer, lisa), (homer, maggie) }
man := { (homer), (bart) }
woman := { (marge), (lisa), (maggie) }
brotherOf := { (bart, lisa), (bart, maggie) }
sisterOf := { (lisa, bart), (maggie, bart), (lisa, maggie),
(maggie, lisa) }
age := { (homer) -> 36, (marge) -> 34, (bart) -> 10,
(lisa) -> 8, (maggie) -> 1 }
eldestDaughter := { () -> lisa }
}
true
false
true
false
true
false
true
true
age(eldestDaughter())~=10
sisterOf(eldestDaughter(),lisa)
man(homer) | woman(homer)
sisterOf(bart,maggie) | (brotherOf(bart,lisa) & brotherOf(bart,marge))
woman(homer) <=> ~man(homer)
age(eldestDaughter())=10 <=> age(bart)=10
age(eldestDaughter())=10 => age(bart)=10
age(eldestDaughter())=8 => age(bart)=10 & age(maggie)=1
What is the interpretation of the following QF formulas?
Are these brackets necessary?
Order of operations:
~ > & > | > =>/<= > <=>
age(eldestDaughter())~=10
sisterOf(eldestDaughter(),lisa)
man(homer) | woman(homer)
sisterOf(bart,maggie) | (brotherOf(bart,lisa) & brotherOf(bart,marge))
woman(homer) <=> ~man(homer)
age(eldestDaughter())=10 <=> age(bart)=10
age(eldestDaughter())=10 => age(bart)=10
age(eldestDaughter())=8 => age(bart)=10 & age(maggie)=1
structure Simpsons : People {
Person := {homer, marge, bart, lisa, maggie}
parentOf := ??
man := ??
woman := ??
brotherOf := ??
sisterOf := ??
age := ??
eldestDaughter := ??
}
Given the above formulas, complete the following structure with interpretations such that all of the formulas are true:
Each introduces a variable ranging over some type:
Quantors allow statements over a range of elements at the same time. There are two basic quantors:
These variables can be used as terms in a subsequent formula:
forall is true iff the subsequent formula is true for all domain elements in the range.
exists is true iff the subsequent formula is true for at least one domain element in the range.
Interpretations of formulas are truth values:
true or false
In IDP, 'forall' is written as '!' and 'exists' is written as '?'.
forall can be seen as a "big and" (conjunction) over its range
exists can be seen as a "big or" (disjunction) over its range
! x in Person: ~man(x) | ~woman(x)
(~man(homer) | ~woman(homer))
& (~man(marge) | ~woman(marge))
& (~man(bart) | ~woman(bart))
& (~man(lisa) | ~woman(lisa))
& (~man(maggie) | ~woman(maggie))
is equivalent to
? x in Person: ~(man(x) | woman(x))
~(man(homer) | woman(homer))
| ~(man(marge) | woman(marge))
| ~(man(bart) | woman(bart))
| ~(man(lisa) | woman(lisa))
| ~(man(maggie) | woman(maggie))
is equivalent to
This is a good approximation of how IDP handles quantors internally
! x in Person: ~man(x) | ~woman(x)
? x in Person: ~(man(x) | woman(x))
! x in Person: ? y in Person: parentOf(y,x)
! x in Person: (? y in Person: sisterOf(x,y)) => woman(x)
What is the interpretation of these formulas?
structure Simpsons : People {
Person := {marge, homer, lisa, bart, maggie}
parentOf := { (marge, bart), (marge, lisa), (marge, maggie),
(homer, bart), (homer, lisa), (homer, maggie) }
man := { (homer), (bart) }
woman := { (marge), (lisa), (maggie) }
brotherOf := { (bart, lisa), (bart, maggie) }
sisterOf := { (lisa, bart), (maggie, bart), (lisa, maggie),
(maggie, lisa) }
age := { (homer) -> 36, (marge) -> 34, (bart) -> 10,
(lisa) -> 8, (maggie) -> 1 }
eldestDaughter := { () -> lisa }
}
true
false
true
false
! x in Country: ! y in Country: border(x,y) => colorOf(x)~=colorOf(y)
Truth value of below formula in either structure?
structure Correct : MapCol {
Country := { BE, NL, DE, LU, FR }
Color := { R, G, B, Y}
border := { (NL, BE), (NL, DE), (BE, LU),
(BE, FR), (LU, FR), (LU, DE), (DE, FR) }
colorOf := { (BE)->B, (NL)->R,
(DE)->G, (LU)->Y, (FR)->R }
}
structure Nonsense : MapCol {
Country := { BE, NL, DE, LU, FR }
Color := { R, G, B, Y}
border := { (BE, BE), (FR, NL) }
colorOf := { (BE)->R, (NL)->R, (DE)->R, (LU)->R, (FR)->R }
}
age(eldestDaughter())~=10
sisterOf(eldestDaughter(),lisa)
man(homer) | woman(homer)
sisterOf(bart,maggie) | (brotherOf(bart,lisa) & brotherOf(bart,marge))
woman(homer) <=> ~man(homer)
age(eldestDaughter())=10 <=> age(bart)=10
age(eldestDaughter())=10 => age(bart)=10
age(eldestDaughter())=8 => age(bart)=10 & age(maggie)=1
2021-12-15
! x in Person: ~man(x) | ~woman(x).
? x in Person: ~(man(x) | woman(x)).
! x in Person: ? y in Person: parentOf(y,x).
! x in Person: (? y in Person: sisterOf(x,y)) => woman(x).
All constraints end with '.'
Shows where the constraint (and quantors inside it) end.
To use domain elements in formula, type interpretation must be given in vocabularium:
vocabulary People {
type Person := {homer, marge, bart, lisa, maggie}
parentOf: (Person * Person) -> Bool // predicate
man: (Person) -> Bool // predicate
woman: (Person) -> Bool // predicate
brotherOf: (Person * Person) -> Bool // predicate
sisterOf: (Person * Person) -> Bool // predicate
age: (Person) -> Int // functor
eldestDaughter: () -> Person // functor
}
#{x in Person : ~(man(x) | woman(x))} = 0.
"the number of persons that are not man or woman is zero"
E.g.:
#{x in Person : ~(man(x) | woman(x))}
"count"
range with variable x
subformula
! x in Person : man(x) | woman(x).
Equivalently:
Score(CInsert) = (if Insert() then 3 else 0).
"the score of CInsert is 3 if we have an insert, else it is 0"
E.g.:
if Insert() then 3 else 0
case if true
condition
case if false
Insert() => Score(CInsert) = 3.
~Insert() => Score(CInsert) = 0.
Equivalently:
if Insert() then 3 else 0
case if true
condition
case if false
If-then-else allow a simple way to encode (first-hit) decision tables. Can you see how?
With the use of mathematical symbols we can construct more complex numerical terms
Given two terms t1, t2, the following are valid numerical terms:
Given two terms t1, t2, the following are valid numerical atoms:
Watch out! => and <= are implications, not inequalities!
age(eldestDaughter()) = age(Bart)-2.
2*age(Bart) = age(eldestDaughter()).
age(eldestDaughter()) % 3 = 2.
! x, y in Person: parentOf(x,y) => age(x) > age(y)+18.
! x, y in Person: parentOf(x,y) => age(y) > age(x)+18.
What is the truth value of the following formulas?
structure Simpsons : People {
Person := {marge, homer, lisa, bart, maggie}
parentOf := { (marge, bart), (marge, lisa), (marge, maggie),
(homer, bart), (homer, lisa), (homer, maggie) }
man := { (homer), (bart) }
woman := { (marge), (lisa), (maggie) }
brotherOf := { (bart, lisa), (bart, maggie) }
sisterOf := { (lisa, bart), (maggie, bart), (lisa, maggie),
(maggie, lisa) }
age := { (homer) -> 36, (marge) -> 34, (bart) -> 10,
(lisa) -> 8, (maggie) -> 1 }
eldestDaughter := { () -> lisa }
}
true
false
true
false
true
A "forall" is a "big and" over a range.
A "exists" is a "big or" over a range.
A "sum aggregate" / "sum lambda" is a "big sum" over a range.
condition ("filters" range)
value ("maps" to numeric value)
sum aggregate - old syntax
sum{x in Person : man(x) : age(x)} < 100.
sum(lambda x in Person: if man(x) then age(x) else 0) < 100.
sum lambda - new syntax
{
! x in Person: sick(x) <- hasFever(x).
! x in Person: sick(x) <- hasRunnyNose(x) & coughing(x).
}
"A person will be sick if they have a fever or if the have both a runny nose and are coughing. In all other cases, they will not be sick."
Rules covering different cases
If no rule applies, the defined symbol is false.
{
! x in Person: sick(x) <- hasFever(x).
! x in Person: sick(x) <- hasRunnyNose(x) & coughing(x).
}
"A person will be sick if they have a fever or if the have both a runny nose and are coughing. In all other cases, they will not be sick."
! x in Person: hasFever(x) => sick(x).
! x in Person: hasRunnyNose(x) & coughing(x) => sick(x).
! x in Person: sick(x) <=> hasFever(x).
! x in Person: sick(x) <=> hasRunnyNose(x) & coughing(x).
In what situations (structures!) are the below different?
{
! x in Person: sick(x) <- hasFever(x).
! x in Person: sick(x) <- hasRunnyNose(x) & coughing(x).
}
"A person will be sick if they have a fever or if the have both a runny nose and are coughing. In all other cases, they will not be sick."
! x in Person: sick(x) <=> hasFever(x) | (hasRunnyNose(x) & coughing(x)).
Equivalently:
{
! x in Person: sick(x) <- hasFever(x).
! x in Person: sick(x) <- hasRunnyNose(x) & coughing(x).
}
head of rule
body of rule with description of a case
arrow '<-' denoting a rule
defined predicate
forall quantification over the type of the predicate
brackets show which rules belong together
{
! x in City: reachable(x) <- x = kontich.
! x in City: reachable(x) <- ? y in City: reachable(y) & rail(x,y).
}
This is what's being defined!
induction step
base case
No equivalent non-definition formula exists in predicate logic
// range notation for type interpretation
type ExRes := {0..3}
// functor interpretation with "else"
TmaxT := { (Fluoroloy_A02) -> 150} else 200
// sum via lambda
TotalCost() = sum(lambda c in component : Score(c) + Cost(Material(c))).
// quantification over predicate
? (x,y) in rangemap_CostIndex_CostIndex: x < Quantity() =< y.
// builtin truth values
true.
false.
Let's try three examples in the interactive consultant:
interactive-consultant.idp-z3.be
vocabulary V {
type Country
type Color
border: (Country * Country) -> Bool
colorOf: (Country) -> Color
}
theory T : V {
// two countries with a border should have a different color
// ...
}
structure S : V {
Country := { BE, NL, DE, LU, FR }
Color := { R, G, B, Y}
border := { (NL, BE), (NL, DE), (BE, LU),
(BE, FR), (LU, FR), (LU, DE), (DE, FR) }
}
Known interpretation
No known interpretation: this is what we want to solve
"Two countries with a border should have a different color."
This is a statement about all ('!') pairs (two variables: x,y) of countries:
! x in Country: ! y in Country: ... something about x, y ...
! x in Country: ! y in Country: border(x,y) => ... something ...
What do we know about these two countries? That if they have a border, then something must be true:
What must be true? The color of the countries should be different:
! x in Country: ! y in Country: border(x,y) => colorOf(x) ~= colorOf(y).
vocabulary V {
type Index := {1..4}
type Diag := {1..7}
n : () -> Index
toDiag1: (Index * Index) -> Diag
toDiag2: (Index * Index) -> Diag
queen: (Index * Index) -> Bool
}
structure S : V {
n := 4
}
theory T : V {
! x, y in Index: toDiag1(x, y) = x - y + n().
! x, y in Index: toDiag2(x, y) = x + y - 1.
// every row has exactly one queen
// every column has exactly one queen
// every diagonal has at most one queen
}
"Every row has exactly one queen."
This is a statement about every ('!') row (one variable: x):
! x in Index: ... something about x ...
"has ... queen" -> queens reside on squares, which are combinations of rows and columns. We already introduced a row (it's 'x'), now we need to express something about a column 'y'.
! x in Index: ... y in Index ... something about x and y...
"exactly one" suggests a cardinality aggregate to introduce y.
! x in Index: #{y in Index: ... something about x and y ... } = 1.
Now we have: for each row x, there exists exactly one column y.
... where there is a queen!
! x in Index: #{y in Index: queen(x,y) } = 1.
vocabulary V {
type Row := {1..4} // The rows of the grid (1 to 4)
type Column := {1..4} // The columns of the grid (1 to 4)
type Block := {1..4} // 4 blocks of 2x2 where the numbers need to be entered
type Number := {1..4} // The numbers of the grid (1 to 4)
blockOf: (Row * Column) -> Block
// This declares the block of each cell.
// This means that blockOf(r,c)=b if and only if
// b is the block of the cell on row r and column c.
solution: (Row * Column) -> Number
// The solution: a number assigned to every cell.
// A cell is represented by its row and column.
// For example: solution(1,2) = 3 means that the
// cell on row 1 and column 2 contains a 3.
}
theory T : V {
// On every row every number is present.
// In every column every number is present.
// In every block every number is present.
}
structure S : V {
// Fix which cells lie in which block
blockOf := {(1,1)->1, (1,2)->1, (2,1)->1, (2,2)->1,
(3,1)->2, (3,2)->2, (4,1)->2, (4,2)->2,
(1,3)->3, (1,4)->3, (2,3)->3, (2,4)->3,
(3,3)->4, (3,4)->4, (4,3)->4, (4,4)->4}
}
! x in Person: !y in Person: brotherOf(x,y) => sisterOf(y,x).
! x in Person: !y in Person: brotherOf(x,y) <= sisterOf(y,x).
! x in Person: #{y in Person: parentOf(y,x)} < 2.
? x in Person: ! y in Person: ! z in Person: parentOf(y,x) & parentOf(z,x) => y=z.
true
false
false
true
2021-12-22
sick() => takeMedicine().
tax() < 10000.
sick() <=> hasFever() | (hasRunnyNose() & coughing()).
tax() = (if income()<limit() then 0 else income()*0.4).
(if income()<limit() then 0 else income()*0.4) < 10000.
(hasFever() | (hasRunnyNose() & coughing())) => takeMedicine().
"only people above 18 should drink alcohol"
! x in People: drinksalcohol(x) => age(x)>=18.
"there is someone over 18 who does not drink alcohol"
? x in People: age(x)>=18 & ~drinksalcohol(x).
"adults are those people older than 18"
! x in People: adult(x) <=> age(x)>=18.
"nobody is both a man and a woman"
! x in People: ~(man(x) & woman(x)).
"there exists someone who is neither man nor woman"
? x in People: ~(man(x) | woman(x)).
"nonbinary people are those who identify neither as man nor as woman"
! x in People: nonbinary(x) <=> ~(man(x) | woman(x)).
Always start small!
In this exercise we’ll try to model a simple scheduling problem. A company has five tasks that need to be executed: task1, task2, task3, task4 and task5. To do this the company has 6 employees: bart, ben, jo, deise, andrea and joost. But every employee is able to complete certain tasks. Bart can complete task1. Ben can complete task1 and task2. Jo can complete task2 and task4. Andrea can complete task1, task2, and taks3. Deise can complete task 3 and task5. And finally, Joost can complete all tasks. The aim of the company is to minimize the total salary cost. Only employees that execute a task get paid. The salary of the employees is very different. Bart earns 20 euro. Ben earns 30 euro, Jo gets 50 euro, Andrea gets 100 euro, Deise 120 euro and Joost 150 euro. Finally, there are a couple of constraints:
Every task needs to be executed.
An employee can only execute a task if he can complete it.
An employee can only work on 1 task.
The ultimate goal of the company is to minimize the total salary that needs to be paid.
Task
Create a vocabulary that can represent this problem. Think about when to use a predicate or a function, which types will be needed, etc …
Make a structure using this vocabulary that incorporates the info from the story above (bart earns 20 euros, Andrea can complete task1, task2, and task3, …)
Complete the structure with an arbitrary solution to test your constraints (written in the next step) .
Make sure that all constraints are satisfied by writing a good theory.
Find a solution that minimizes the total wage cost