Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# CS208 2022/2023 Semester One; Coursework 1
This coursework is the first of two for CS208 Semester One
("Logic"). You will writing a Python program to encode a problem into
logical constraints to be solved by a SAT solver.
This document explains (i) the problem you are going to solve; (ii)
the tasks you need to complete; (iii) some documentation of another
example encoding that you can use for reference; and (iv) practical
instructions on how to get started and how to submit your work.
## Getting Started
This repository contains Python implementations of a SAT solver and
supporting code, and an implementation of the Package Installation
Problem described in Week 2. There is a file ready for you to
implement a solution to the resource allocation problem described
below.
To get started:
1. Clone this repository, either by using `git clone`, or downloading
the `zip` file.
2. Once you have a copy of the files, make sure that they work by
running `python` on the `packages.py` file. The output should look
like this:
```
> python3 packages.py
Test 1: following dependency chains
Clauses:
-libE1 \/ -libE2
-progA \/ libC
-progA \/ libD
-libC \/ libE1
-libD \/ libE1
progA
{'progA': True, 'libC': True, 'libD': True, 'libE1': True, 'libE2': False}
.... some more tests ....
```
If you have any problems getting hold of the files, installing Python,
or getting it to work, please get in contact via
[Mattermost](https://mattermost.cis.strath.ac.uk/learning/channels/cs208-2022-23).
## The exercise: Solving Allocation of Tasks to Machines
The aim of this exercise is to write a program to solve the problem of
assigning tasks to machines, using an encoding into logical
constraints. We make the following assumptions:
1. We have `num_tasks` tasks to complete.
2. We have `num_machines` machines to do them on.
3. Every task must be assigned to some machine.
4. Certain pairs of tasks conflict, and must not be assigned to the
same machine.
Given `num_tasks`, `num_machines` and a list of `conflicts`, we have
to find an assignment of tasks to machines that gives every task a
machine without assigning conflicting tasks to the same machine.
We will number the tasks `0...num_tasks-1` and the machines
`0...num_machines-1`. This works well with Pythons `range(n)` function
that returns all the numbers `0..n-1` to iterate over.
**Example:** If we have three tasks and one machine, with no tasks
conflicting, then we can assign all three tasks to the one machine.
**Example:** If we have three tasks and one machine, but tasks `0` and
`1` conflict, then there is no assignment that works.
**Example:** If we have three tasks and three machines, with every
pair of tasks conflicting (so `(0,1)`, `(1,2)`, and `(0,2)`), then we
can assign each task to a separate machine. Note that there are 6 ways
of doing this.
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
### The Encoding
To encode the allocation task in logic, we will use the following
strategy:
1. We will encode the assertion that task `X` is assigned to machine
`Y` by the atomic proposition `assignXtoY`. If a valuation sets
`assignXtoY` to `True`, then task `X` is assigned to machine
`Y`. If a valuation sets it to `False`, then this assignment is not
made.
2. To encode the requirement that every task `X` must be assigned to a
machine, we add a clause like this for each `X`:
```
assignXto0 \/ assignXto1 \/ ... \/ assignXto<num_machines-1>
```
3. For each conflict `(X,Y)`, we encode the fact that tasks `X` and
`Y` cannot be assigned to the same machine by adding a clause for
each machine `Z` like so:
```
¬assignXtoZ \/ ¬assignYtoZ
```
### What you have to do
You have to complete the following tasks by writing code in the file
`resource_allocation.py`. See the comments in that file for guidance.
1. Implement the strategy above to compute allocations. This means
filling out the missing parts of the `resource_allocation` function
to implement the encoding strategy above. *(5 marks)*
There are example allocations at the end of the file, please feel
free to add your own for testing.
2. The printing of the assignment of tasks to machines is quite
unfriendly with the code I have provided. It lists all the
assignments made as well as the assignments not made. Write some
code to replace this that just lists each task and which machine(s)
it is assigned to. *(2 marks)*
3. The basic implementation only computes one possible allocation of
tasks to machines. Adjust the `resource_allocation` function to
print out **all** possible allocations. *(4 marks)*
HINT: You'll have to use a loop that works like this: ask the
solver for a solution, print this out, then add a clause that
states "not this solution", then go round the loop again until the
solver says "no solution".
4. The allocation problem described above assumes that the number of
machines is fixed. Write some more code to find the **smallest**
number of machines for a fixed number of tasks and conflicts
between them. *(4 marks)*
HINT: You'll need to write another loop. You might need to
restructure the `resource_allocation` function a bit to return
whether or not it has found a solution.
When you are happy with your solution, please submit the file
`resource_allocation.py` [via MyPlace](https://classes.myplace.strath.ac.uk/mod/assign/view.php?id=1774227).
## An example to follow: Package Installation Problem
As an example implementation for you to follow, I have included an
implementation of the Package Installation Problem introduced in Week
2 in the file [packages.py](packages.py).
Please read that file carefully to see an example of how to implement
a logical encoding, and how to use the `Numbering` class.
### The Other Python Files
The other Python files are supporting code for you to write your
solution. You should not modify them.
- [numbering.py](numbering.py) contains a class that is useful for
managing a mapping from names to numbers and back again. This is
useful because the SAT solver implemented here uses numbers to
represent atomic propositions. It is used by `packages.py` to give a
number to each package. You should use it to assign numbers to each
- [clauses.py](clauses.py) contains a class that stores a collection
of clauses ready for solving. Call the `add(clause)` method to add a
new clause, and the `solve()` method to solve them. See
`packages.py` for an example of its use.
- [solver.py](solver.py) is an implementation of a SAT solver,
following the strategy described in the lectures.
## Plagiarism Warning
You are welcome to discuss your solution with other students, but **do
not** copy each other's code. Submit your own solution to the
problem. Any suspected plagiarism will be investigated and may result
in you getting 0 marks for this assessment, as well as a record being
made for the rest of your degree. We may use automated plagiarism
checkers to check submissions for signs of copying.