Skip to content

Added an OCaml autograder #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions OCaml-toposortlab/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM ocaml/opam:ubuntu-20.04-ocaml-4.10

RUN opam install base yojson
RUN eval $(opam env)
USER root


# Install autodriver
WORKDIR /home
RUN useradd autolab
RUN useradd autograde
RUN mkdir autolab autograde output
RUN chown autolab:autolab autolab
RUN chown autolab:autolab output
RUN chown autograde:autograde autograde
RUN git clone https://github.com/autolab/Tango.git
WORKDIR Tango/autodriver
RUN make clean && make
RUN cp autodriver /usr/bin/autodriver
RUN chmod +s /usr/bin/autodriver

# Clean up
WORKDIR /home
RUN apt-get remove -y git
RUN apt-get -y autoremove
RUN rm -rf Tango/

# Check installation
RUN ls -l /home
RUN which autodriver
29 changes: 29 additions & 0 deletions OCaml-toposortlab/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# OCaml-toposortlab

### Assessment Language
OCaml

### Autograder Language
OCaml


### Autograding Environment Packages
The following opam packages:
- yojson
- base

A Dockerfile is provided to install the necessary packages.
### Assessment Scenario
Students are required to implement a topological sort function in OCaml matching a graph interface.

### Handin Format
Single file called toposort.ml

### autograder.tar Directory Content
The standard directories in a Dune project:
```
bin/
lib/
test/
dune-project
```
11 changes: 11 additions & 0 deletions OCaml-toposortlab/autograde-Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
all: copy

copy:
tar -xvf autograde.tar
cp toposort.ml toposortlab_dune/lib
cd toposortlab_dune && \
dune build && \
dune test

clean:
rm -r toposortlab_dune
Binary file added OCaml-toposortlab/autograde.tar
Binary file not shown.
23 changes: 23 additions & 0 deletions OCaml-toposortlab/toposort.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
open Core

type graph = {
nodes : int list;
edges : (int * int) list;
}

let topological_sort (g : graph) : int list =
let visited = Hash_set.create (module Int) in
(* Construct the adjacency list representation of the graph*)
let adj_list = Int.Map.of_alist_multi g.edges in
let rec dfs_helper (acc : int list) (node : int) =
if Hash_set.mem visited node then
acc
else
let () = Hash_set.add visited node in
let neighbors = (match Int.Map.find adj_list node with
| Some neighbors -> neighbors
| None -> []) in
let acc = List.fold ~init:acc ~f:(dfs_helper) neighbors in
node :: acc
in
List.fold ~init:[] ~f:(dfs_helper) g.nodes
Comment on lines +8 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Topological sort algorithm has an issue with cycle detection

The topological sort implementation works well for DAGs (Directed Acyclic Graphs), but doesn't handle cycles correctly. In the presence of cycles, the algorithm will still produce output but it won't be a valid topological sorting.

Consider adding cycle detection:

open Core

type graph = {
  nodes : int list;
  edges : (int * int) list;
}

let topological_sort (g : graph) : int list = 
  let visited = Hash_set.create (module Int) in
+  let temp_visited = Hash_set.create (module Int) in
  (* Construct the adjacency list representation of the graph*)
  let adj_list = Int.Map.of_alist_multi g.edges in
+  let rec dfs_helper (acc : int list) (node : int) = 
+    if Hash_set.mem temp_visited node then
+      failwith "Cycle detected in graph"
+    else if Hash_set.mem visited node then
      acc
    else
-      let () = Hash_set.add visited node in
+      let () = Hash_set.add temp_visited node in
      let neighbors = (match Int.Map.find adj_list node with
      | Some neighbors -> neighbors
      | None -> []) in
      let acc = List.fold ~init:acc ~f:(dfs_helper) neighbors in
+      let () = Hash_set.add visited node in
+      let () = Hash_set.remove temp_visited node in
      node :: acc
  in
-  List.fold ~init:[] ~f:(dfs_helper) g.nodes
+  try
+    List.fold ~init:[] ~f:(dfs_helper) g.nodes
+  with
+    | Failure msg -> failwith ("Topological sort failed: " ^ msg)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let topological_sort (g : graph) : int list =
let visited = Hash_set.create (module Int) in
(* Construct the adjacency list representation of the graph*)
let adj_list = Int.Map.of_alist_multi g.edges in
let rec dfs_helper (acc : int list) (node : int) =
if Hash_set.mem visited node then
acc
else
let () = Hash_set.add visited node in
let neighbors = (match Int.Map.find adj_list node with
| Some neighbors -> neighbors
| None -> []) in
let acc = List.fold ~init:acc ~f:(dfs_helper) neighbors in
node :: acc
in
List.fold ~init:[] ~f:(dfs_helper) g.nodes
open Core
type graph = {
nodes : int list;
edges : (int * int) list;
}
let topological_sort (g : graph) : int list =
let visited = Hash_set.create (module Int) in
let temp_visited = Hash_set.create (module Int) in
(* Construct the adjacency list representation of the graph*)
let adj_list = Int.Map.of_alist_multi g.edges in
let rec dfs_helper (acc : int list) (node : int) =
if Hash_set.mem temp_visited node then
failwith "Cycle detected in graph"
else if Hash_set.mem visited node then
acc
else
let () = Hash_set.add temp_visited node in
let neighbors = (match Int.Map.find adj_list node with
| Some neighbors -> neighbors
| None -> []) in
let acc = List.fold ~init:acc ~f:(dfs_helper) neighbors in
let () = Hash_set.add visited node in
let () = Hash_set.remove temp_visited node in
node :: acc
in
try
List.fold ~init:[] ~f:(dfs_helper) g.nodes
with
| Failure msg -> failwith ("Topological sort failed: " ^ msg)