COMP9334 Project, Term 1, 2023:
Priority queueing for multi-phase jobs Due Date: 5:00pm Friday 21 April 2023
Version 1.01 Updates to the project, including any corrections and clarifications, will be posted on the course website. Make sure that you check the course website regularly for updates.
1 Introduction and learning objectives
When you were learning about operational analysis earlier in the term, we talked about jobs that require multiple visits to the CPU (or servers) to receive their service. In this project, you will use simulation to study how priority queueing can be used to improve the performance of a multiserver system that works on jobs that require multiple visits to the servers. In this project, you will learn:
- To use discrete event simulation to simulate a computer system
- To use simulation to solve a design problem
- To use statistically sound methods to analyse simulation outputs
2 Support provided and computing resources
If you have problems doing this project, you can post your question on the course forum. We strongly encourage you to do this as asking questions and trying to answer them is a great way to learn. Do not be afraid that your question may appear to be silly, the other students may very well have the same question! Please note that if your forum post
Figure 1: The system for this project.
shows part of your solution or code, you must mark that forum post private. Another way to get help is to attend a consultation (see the Timetable section of the course website for dates and times).
If you need computing resources to run your simulation program, you can do it on the VLAB remote computing facility provided by the School.
3 Multi-server system configuration and job characteristics for this project
The configuration of the system that you will use in this project is shown in Figure 1. The system consists of a dispatcher and n servers where n > 1. The dispatcher has two queues: a high priority queue and a low priority queue. You can assume that both queues have infinite queueing slots. You have not learnt about priority queues yet but the following description will explain how priority queues are used.
We will use the word job to refer to a request that requires service from this system. A job may require one or more visits to the servers in order to get all its work completed. These visits of a job take place one after another with a possible time gap between two consecutive visits. Jobs in this system do not use parallel processing so each job does not use more than one server at a time.
We will now explain how this system handles a new job. When a new job (i.e., an external arrival) arrives at the system, the dispatcher will send the job to any one of the idle servers if there is at least one idle server. If all the servers are busy, the dispatcher will place this job at the end of the high priority queue.
After a job has completed a visit to the server, the job either requires or does not require further visits to the servers. If the job does not require further visits to the servers, then the job will depart from the system permanently. If the job requires further visits to the servers, then the job will be sent back to the dispatcher. We will use the term re-circulated jobs to refer to those jobs that are sent back to the dispatcher from the servers because these jobs require further visits to the servers.
A job that arrives at the dispatcher can either be a new job or a re-circulated job, see Figure
- We have already explained how the dispatcher handles new jobs. we can state the rule that the dispatcher uses: When a re-circulated job arrives at the dispatcher, the dispatcher will classify this job as low priority if its value of c is greater than or equal to h; otherwise the job is a high priority job. Let us consider an example.
Example 1 In this example, we assume the threshold h has a value of 2. Let us consider a job which requires altogether 3 server visits before it will permanently depart from the system. So, this job will re-circulate to the dispatcher two times: once with a value of c = 1 and the other with c = 2. • When this job re-circulates to the dispatcher the first time, its value of c will be 1. Since c ≥ h does not hold, the dispatcher will consider this job as a high priority job on this occasion. • The second time that this job re-circulates to the dispatcher, its value of c will be 2. Since c ≥ h holds, the dispatcher will consider this job as a low priority job on this occasion. We have now explained how the dispatcher classifies an arriving re-circulated job into either a high or low priority job. We have yet to explain the detailed working of the dispatcher. We will do that together with the description of how departures are handled. This is because the arrival of a re-circulated job at a dispatcher follows the job’s earlier departure from a server, see Figure
- The following steps describe how a job, which has completed a server visit, will be handled. For ease of referral, we will use the term tagged job to refer to this job that has just completed its server visit. • The tagged job is considered to be a permanent departure if the number of complete visits that it has already made is equal to the total number of visits that this job requires. If the tagged job is not a permanent departure, then it will be re-circulated to the dispatcher. The server that was working on the tagged job would send a message to the dispatcher to inform it that it is available to serve another job.
We remark that the above description means that the dispatcher uses the non-preemptive queueing discipline. We will be discussing queueing disciplines in Week 7 and you can read about it on p. 500 of [1]. However, the above description should be enough for you to get your project going now even before we discuss priority queues in Week 7.
We make the following assumptions on the system in Figure 1. First, it takes the dispatcher negligible time to process a job, to classify a job and to send a job to an available server. Second, it takes a negligible time for a server to send a re-circulated job to the dispatcher and to inform the dispatcher on its availability. As a consequence of these assumptions, it means that: (1) If a job arriving at the dispatcher is to be sent to an available server right away, then its arrival time at the dispatcher is the same as its arrival time at the chosen server; (2) The departure time of a job from the dispatcher is the same as its arrival time at the chosen server; and (3) The departure time of a re-circulated job from a server is the same as its arrival time at the dispatcher. Ultimately, these assumptions imply that the response time of the system depends only on the queues and the servers.
We have now completed our description of the operation of the system in Figure 1. We will provide a number of numerical examples to further explain its operation in Section 4. You will see from the numerical examples in Section 4 that the threshold h can be used to influence the system’s mean response time. So, a design problem that you will consider in this project is to determine the value of the threshold h to minimise the mean response time of the system. You can read in [1] how priority queueing can be used to reduce the mean response time of computer systems.
4 Examples
We will now present two examples to illustrate the operation of the system that you will simulate in this project. In all these examples, we assume that the system is initially empty.
4.1 Example 1: number of servers n = 2 and threshold h = 1
In this example, we assume the there are n = 2 servers in the system and the threshold h for determining whether a re-circulated job is of low or high priority is 1. In this example, each job requires one or two visits to the servers before it permanently departs from the system. Table 1 shows, for each job, its arrival time and the service times for its visits. If there is only one service time in the third column in Table 1, then it means the job only requires one server visit. If there are two service times, then the job requires two server visits. For example, Job 1 in Table 1 requires two visits where the first and second visits require, respectively, 3 and 10 time units of service times. As another example, Job 3 requires only one visit and the service time required for that visit is 6 time units.
Job index | Arrival time | Service times of the job’s server visits |
---|---|---|
1 | 0.9 | 3, 10 |
2 | 1.5 | 2, 1 |
3 | 2.2 | 6 |
4 | 3.3 | 2 |
5 | 8.0 | 1, 4 |
Table 1: Data for Example 1.
In this example, a job will be identified with using the tuple (i, c/r) where i is the job’s index (see the first column of Table 1), c is the number of complete servers visits made by the job and r is the total number of server visits required by the job. For example, • The job (1, 0/2) refers to the job with index 1. We know from Table 1 that Job 1 requires 2 visits to the servers and this is indicated by “/2”. The notation “0/2” says that this job has done zero complete visits to the servers. When Job 1 re-circulates to the dispatcher for the first time, its tuple becomes (1, 1/2). • The job (5, 1/2) refers to Job 5 which requires altogether 2 visits to the servers. The notation “1/2” says that this job has done one complete visit to the servers out of the two required visits.
The events in the system in Figure 1 are the arrival of a new job to the dispatcher and the completion of a visit at a server. Note that we have not included the arrival of a re-circulated job to the dispatcher as an event. This is because the arrival of a re-circulated job at the dispatcher is immediately after the completion of a server visit. So the simulation will handle the arrival of re-circulated job at the dispatcher and its associated server completion together. We will illustrate how the simulation of the system works using “on-paper simulation”. The quantities that you need to keep track of are: • Next arrival time is the time that the next new job will arrive • For each server, we keep track its server status, which can be busy or idle. • We also keep track of the following information on the job that is being processed in the server: – Next completion time is the time at which the job will complete its current server visit. If the server is idle, the next completion time is set to ∞. Note that there is a next completion time for each server. – The time that this job arrived at the system. This is needed for calculating the response time of the job when it permanently departs from the system. – A list of the service times for the future server visits of this job. Note that we enclose the list of service times within a pair of square brackets [ ]. – The job’s tuple.
For example, the job information “3.5, 1.5, [1], (2,0/2)” indicates that current visit will be completed at time 3.5 and this job arrived at the system at time 1.5. The “0/2” indicates that the job has not completed any server visits so the current visit is the job’s first visit to the server. The “[1]” indicates that the job needs one more visit in the future and this visit will require a service time of 1. Note that if the job has no more future visits to make, then we will use [ ] to indicate that. • The contents of the high and low priority queues. Each job in the queue is identified by 3 fields: the job’s tuple, the job’s arrival time to the system, a list of the job’s service times for its future server visits. For example, we write a job in a queue as [(1,1/2), 0.9, [10] ] which means the job (1,1/2) arrived at the system at time 0.9, has 1 visit completed and its future visit to the server will require a service time of 10.
The “on-paper simulation” is shown in Table 2. The notes in the last column explain what updates you need to do for each event. Recall that the two event types in this simulation are the arrival of a new job to the dispatcher and the completion of a visit at a server, we will simply refer to these two events as Arrival and Completion in the “Event type” column (i.e., second column) in Table 2.
......
The above description has not explained what happens if an arrival event and a completion event are at the same time. We will leave it unspecified. If we ask you to simulate in trace driven mode, we will ensure that such situation will not occur. If the inter-arrival time and service time are generated randomly, the chance of this situation occurring is practically zero so you do not have to worry about it.
Table 3 summarises the arrival, departure and response times of the jobs in this example. The mean response time is the sum of the last column in the table divided by the number of jobs that have permanently departed from the system, which is 37.8 5 = 7.56.
Table 4 shows the times at which the server visits are completed. These are also the times for the completion events.
Job | Arrival time | Departure time | Response time |
---|---|---|---|
1 | 0.9 | 16.9 | 16.0 |
2 | 1.5 | 6.9 | 5.4 |
3 | 2.2 | 9.5 | 7.3 |
4 | 3.3 | 5.9 | 2.6 |
5 | 8.0 | 14.5 | 6.5 |
Table 3: The arrival and departure times of the jobs in Example 1.
Arrival time | Completion time | Number of completed server visits |
---|---|---|
1.5 | 3.5 | 1 |
0.9 | 3.9 | 1 |
3.3 | 5.9 | 1 |
1.5 | 6.9 | 2 |
2.2 | 9.5 | 1 |
8.0 | 10.5 | 1 |
8.0 | 14.5 | 2 |
0.9 | 16.9 | 2 |
Table 4: The completion times for the server visits.
4.2 Example 2: number of servers n = 2, threshold h = 2
This example is identical to Example 1 except that the threshold h = 2. Since all the jobs in Example 1 require at most two visits, therefore all the re-circulated jobs will go into the high priority queue and the low priority queue is not used at all. For this value of h = 2, the departure times of the jobs are the same as the case for h = 1. The server visit completion times are also the same as those in Table 4. Note that Tables 5 and 6 in Version 1.00 are incorrect, and they have been removed from Version 1.01. There are no Tables 5 and 6 in this document. The next table number is 7.
4.3 Example 3: number of servers n = 3, threshold h = 1
This example is based on the arrivals and service times in Table 7. The number of servers n is 3 and threshold h = 1.
Table 8 summarises the arrival and departure times of all the jobs. The mean response time of the 4 jobs in this example is 52.6 4 = 13.15.
Table 9 shows the times at which the server visits are completed. These are also the times for the completion events. ......
5 Project description
This project consists of two main parts. The first part is to develop a simulation program for the system in Fig. 1. The system has already been described in Section 3 and illustrated in Section 4. In the second part, you will use the simulation program that you have developed to solve a design problem.
5.1 Simulation program
You must write your simulation program in one (or a combination) of the following languages: Python 3 (note: version 3 only), C, C++, or Java. All these languages are available on the CSE system.
We will test your program on the CSE system so your submitted program must be able to run on a CSE computer. Note that it is possible that due to version and/or operating system differences, code that runs on your own computer may not work on the CSE system. It is your responsibility to ensure that your code works on the CSE system.
Note that our description uses the following variable names:
- A variable mode of string type. This variable is to control whether your program will run simulation using randomly generated arrival times and service times; or in trace driven mode. The value that the parameter mode can take is either random or trace.
- A variable time_end which stops the simulation if the master clock exceeds this value. This variable is only relevant when mode is random. This variable is a positive floating point number.
Note that your simulation program must be a general program which allows different parameter values to be used. When we test your program, we will vary the parameter values. You can assume that we will only use valid inputs for testing. For the simulation, you can always assume that the system is empty initially.
5.1.1 The random mode
When your simulation is working in the random mode, it will generate the inter-arrival times and the workload of a job in the following manner.
- We use {a1 , a2 , . . . , ak , . . . , ...} to denote the inter-arrival times of the jobs arriving at the dispatcher. These inter-arrival times have the following properties: (a) Each ak is the product of two random numbers a1k and a2k , i.e ak = a1k a2k ∀k = 1, 2, ... (b) The sequence a1k is exponentially distributed with a mean arrival rate λ requests/s. (c) The sequence a2k is uniformly distributed in the interval [a2l , a2u ]. Note: The easiest way to generate the inter-arrival times is to multiply an exponentially distributed random number with the given rate and a uniformly distributed random number in the given range. It would be more difficult to use the inverse transform method in this case, though it is doable.
- The workload of a job is characterised by; (i) the number of server visits that the job requires; and (ii) the service times of all these server visits. The first step to generate the workload of a job is to generate a random positive integer to use as the number of server visits that this job requires. You will be given a sequence of J non-negative real numbers p1 , p2 , ..., pk , ... and pJ with the property k=1 pk = 1. Given these numbers, we want the probability that a job needs k server visits to be equal to pk , for k = 1, ..., J. For example, if you are given the sequence 0.5, 0.2, 0.3, then the jobs generated has the following properties: (a) Prob[a job requires 1 server visit] = 0.5 (b) Prob[a job requires 2 server visits] = 0.2 (c) Prob[a job requires 3 server visits] = 0.3 Note that you may interpret J as the maximum number of server visits that a generated job requires. In the example above, we have J = 3, which implies that all generated jobs need at most 3 server visits.
- If a job requires k server visits, then you will need to generate k random service times for each of the k server visits. These k service times are independent and they all come from the same probability distribution. The service time per server visit is generated by the probability density function (PDF) g(t) ......
Note that this probability density function has two parameters: α and β. You can assume that α > 0 and β > 3. As an example, if a job requires 3 server visits, then you will need to generate 3 random numbers which come from the probability distribution whose PDF is given by g(t).
5.1.2 The trace mode
When your simulation is working in the trace mode, it will read the list of inter-arrival times and the list of service times of the server visits from two separate ASCII files. We will explain the format of these files in Sections 6.1.3 and 6.1.4 . An important requirement for the trace mode is that your program is required to simulate until all jobs have departed from the system. You can refer to Table 2 for an illustration. Hint: Do not write two separate programs for the random and trace modes because they share a lot in common. A few if–else statements at the right places are what you need to have both modes in one program.
5.2 Determining the threshold h that minimises the mean response time
After writing your simulation program, your next step is to use your simulation program to determine the threshold h that can minimise the mean response time. For this design problem, you will assume the following parameter values: • Number of servers: n = 6 • For inter-arrival times: λ = 3.9 3.1, a2` = 0.91, a2u = 1.27 • For the number of server visits required for each job: the sequence p1 , p2 , p3 , p4 , p5 is 0.52, 0.21, 0.15, 0.08, 0.04 0.32, 0.21, 0.15, 0.08, 0.24. • For the service time per server visit: β = 3.4, α = 0.3. In solving this design problem, you need to ensure that you use statistically sound methods to compare systems. You will need to consider simulation controls such as length of simulation, number of replications, transient removals and so on. You will need to justify in your report on how you determine the value of the threshold h.
6 Testing your simulation program
In order for us to test the correctness of your simulation program, we will run your program using a number of test cases. The aim of this section is to describe the expected input/output file format and how the testing will be performed. Each test is specified by 4 configurations files. We will index the tests from 1. If 12 tests are used, then the indices for the tests are 1, 2, ...., 12. The names of the configuration files are: • For Test 1, the configuration files are mode_1.txt, para_1.txt, interarrival_1.txt and service_1.txt. The files are similarly named for indices 2, 3, .., 9. • For Test 10, the configuration files are mode_10.txt, para_10.txt, interarrival_10.txt and service_10.txt. The files are similarly named if the test index is a 2-digit number. We will refer to these files using the generic names mode .txt, para .txt etc. We will describe the format of the configuration files in Section 6.1 Each test should produce 2 output files whose format will be described in Section 6.2. We will explain how testing will be conducted in Sections 6.3 and 6.5.
6.1 Configuration file format
Note that Test 1 is the same as Example 1 discussed in Section 4.1. We will use that test to illustrate the file format. 6.1.1
mode *.txt
This file is to indicate whether the simulation should run in the random or trace mode. The file contains one string, which can either be random or trace. 6.1.2
para *.txt
If the simulation mode is trace, then this file has two lines. The first line is the value of n (= number of servers) and the second line has the value of h (= threshold for priority queueing). If the test is Example 1 in Section 4.1, then the contents of this file are: 2 1 These values are in the sample file para_1.txt. If the simulation mode is random, then the file has three lines. The meaning of the first two lines are the same as above. The last line contains the value of time_end, which is the end time of the simulation. The contents of the sample file para_7.txt are shown below where the last line indicates that the simulation should run until 1000. ......
where you will find the service times of the server visits of each job in a line of the file. Note that the symbol NaN is a Python floating point number to denote not a number and is often used to indicate an absence of numbers. In this example, if there are two numbers on the line, the job requires two server visits; if there is a number and an NaN, the job is requires only one server visit. The following shows the contents of service 3.txt for trace mode simulation: The following shows what the contents of a file for service times may look like for the trace mode simulation mode:
Note that there are 4 entries in each line where the number 4 corresponds to the maximum number of server visits among all the jobs. You can conveniently load the contents of this file by using the function numpy.loadtxt() into a numpy array. You may also find the function numpy.isnan() useful. In general, if the maximum number of server visits among all jobs is V , then there are V entries in each line of service .txt. For random mode, the file service .txt contains one line, corresponding to the values of β and α. You can assume that the data we provide for trace mode are consistent in the following way: The number of inter-arrival times and the number of lines of service times are equal.
6.2 Output file format
In order to test your simulation program, we need two output files per test. One file containing the mean response time. The other file contains the completion times of the server visits from the servers. We want to start by clarifying what we mean by mean response time. You can calculate the response time of a job by subtracting the time that this job arrives at the system as a new job from the time it permanently departs from the system. Tables 1 and ?? illustrate this concept. For trace mode, the mean response time will be calculated using all the jobs provided in the interarrival .txt and service .txt. This is because, as mentioned in Section 5.1.2, a trace mode simulation is required to simulate until all jobs have permanently departed from the system. For random mode, the mean response time should be calculated using all those jobs that have permanently departed the system by time_end. In other words, for those jobs which are still in the queue or are being processed in the server at timeend, you do not include these jobs when calculating the mean response time. Note that you do not have to consider transient removal for the mean response before you write the result to the output file. However, you should consider transient removal when you do your design. The mean response time should be written to a file whose filename has the form mrt.txt. For Example 1 in Section 4.1, the expected contents of this file are: 7.5600 The other file dep_.txt contains the completion times of of the server visits from the servers. For Example 1 in Section 4.1, the expected contents of this file are:
Note the following requirements for the file containing the completion times:
- Each line contains 4 entries.
- Each line provides the information on the completion time of a server visit.
- For each line, the first entry is the arrival time of the job to the system (i.e., as a new job), the fourth entry is the total number of server visits required by this job, the third entry is the number of complete server visits that this job has made at the time given by the second entry. Let us take the first line 1.5000 3.5000 1 2. It says that the job that arrives at the system at time 1.5 requires a total of 2 server visits, and at time 3.5, this job has completed 1 server visit. You should be able to reconcile the contents of the above file with Example 1 in Section 4.1.
- The server visits must be ordered according to ascending completion times.
- If the simulation is in the trace mode, we expect the simulation to finish after all jobs have been processed. Therefore, the number of lines in dep_*.txt should be equal to the total number of server visits of all jobs.
- If the simulation is in the random mode, the file should contain all the server visits that have been completed by timeend. All mean response times, arrival times and completion times in mrt.txt and dep_.txt should be printed as floating point numbers to exactly 4 decimal places. Note that your simulation should be performed in full floating point precision and you should only do the rounding when you are writing the output files.
6.3 The testing framework
When you submit your project, you must include a Linux bash shell script with the name run_test.sh so that we can run your program on the CSE system. This shell script is required because you are allowed to use a computer language of your choice. Let us first recall that each test is specified by a four configuration files and should produce two output files. For example, test number 1 is specified by the configuration files mode_1.txt, interarrival_1.txt, service_1.txt and para_1.txt; and test number 1 is expected to produce the output files mrt_1.txt and dep_1.txt.
This means that the shell script run_test.sh has one input argument which is the test number to be used. Let us for the time being assume that you use Python (Version 3) to write your simulation program and you call your simulation program main.py. If the file main.py is in the same directory as run_test.sh, then run_test.sh can be the following one-line shell script: python3 main.py $1
The shell script will pass the test number (which is in the input argument $1) to your simulation program main.py. This also implies that your simulation program should accept one input argument which is the test number.
Just in case you are not familiar with shell script, we have provided two sample files: run_test.sh and main.py to illustrate the interaction between a shell script and a Python (Version 3) file. You need to make sure run_test.sh is executable. If you run the command ./run_test.sh 2, it will produce a file with the name dummy_2.txt in the directory output/. You can also try using other input arguments for the sample shell script. You can use these sample files to help you to develop your code.
If you use C, C++ or Java, then your run_test.sh should first compile the source code and then run the executable. You should of course pass the test number to the executable as an input. You can put your code in the same directory that contains run_test.sh or in a subdirectory below it. For example, you may have a subdirectory src/ for your code like the following: the directory containing run test.sh config/ output/ src/
6.4 Sample files
You should download the file sample project files 25Mar.zip from the project page on the course website. The zip archive has the following directory structure: Base directory containing cf output with ref.py, run test.sh and main.py config/ output/ ref/ Details on the zip-archive are: • The sub-directory config/ contains configuration files that you can use for testing. – The files mode_1.txt, mode_2.txt, ..., mode_9.txt and mode10.txt. Note that Tests 1–6 are for trace mode while Tests 7–10 are for random mode. – The files para.txt, interarrival_.txt and service_.txt for from 1 to 10, as the input to the simulation.
– Note that Tests 1 and 2 are the same as respectively, Example 1 and Example 2, in Section 4. Also Tests 3 and 4 are the same as Examples 3 and 4 in Section 4. • The sub-directory output/ is empty. Your simulation program should place the output files in this sub-dirrectory. • The sub-directory ref/ contains the expected simulation results. • The Python file cf_output_with_ref.py which illustrates how we will compare your output against the reference output. This file takes in one input argument, which is the test number. For example, if you want to check your simulation outputs for test 2, you use: python3 cf_output_with_ref.py 2 Note the following: – The file cf_output_with_ref.py expects the directory structure shown earlier. – For trace mode, we will check your mean response time and the completion times.
6.5 Carrying out your own testing on the CSE system
It is important for you to note the assumption on directory structure mentioned in Section 6.3. You must ensure your shell script and program files are written with this assumption in mind. Since we will be testing your work on the CSE system, we strongly advise you to carry out the following on the CSE system before submission. • Create a new folder in your CSE account and cd to that folder. We will refer to this directory as the base directory. – Copy your shell script run_test.sh and program files to the base directory 1 . – Copy the config and ref directories, as well as their contents, to the base directory – Create an empty directory output 1 Remark : In actual testing, we will copy your submitted project.zip (see Section 7.3) to this base directory and unzip it. We expect that run test.sh is in this base directory after unzipping.
• Make sure your shell script is executable. • Run your shell script for each test one by one. Make sure that each run produces the appropriate output files for that test in the output directory. • Copy cf_output_with_ref.py to the base directory. Run it to compare your output against the reference output.
These steps are the same as those that we will use for testing. It is important to know that we will create an empty output/ directory before we run your code. This means your code does NOT have to create the output/ directory.
The submission portal will make an attempt to run test number 1 with your submitted files, see Section 7.3.
6.6 Getting started and base code
For this project, we do not require you to write your code from scratch. You are allowed to build your project by using: (i) the sample code from COMP9334; or (ii) the code in the public domain as long as it meets the requirements below.
If you intend to use Python 3 to write your simulation code, the best way to get started is to use the M/M/m simulation code provided with the solution to Week 4B’s revision problem and modify from there. Sample code for trace driven simulation is provided with the lecture in Week 4B.
There is also a lot of discrete event simulation code in Python 3, C, C++ and Java in the public domain. You are allowed to use the public domain code as a basis for your project work as long as it meets the following requirements:
- The code has a clearly identifiable author
- The code has a date which is before the date that this project document is released.
- You provide us with an URL of the source code.
- You clearly state the changes that you have made on the original code to adapt it to the specifications of this project. If you use any public domain code in your project, your project report must include the information to satisfy the above four requirements. If you would like to use a certain public domain source but you are not sure whether it meets our requirements, you can consult the lecturer on the forum using a private message. If your project work is based on the COMP9334 sample code, then your report must state that the COMP9334 sample code has been used and provide information to satisfy Requirement 4 above.