Job shop scheduling with Clojure's core.logic Here's a job shop scheduler using core.logic. I had a lot of fun coding it. I'd appreciate ideas on how to make the code faster or more readable.

Setup

(ns schejule.no-db
  (:require [clojure.core.logic :refer [== all everyg lvar run run* fresh succeed] :as l]
            [clojure.core.logic.fd :as fd])
  (:refer-clojure :exclude [==]))

(def test-data
  [{:id 1 :job :cooljob :duration 180 :machine 1}
   {:id 2 :job :cooljob :duration 120 :machine 2}
   {:id 3 :job :cooljob :duration 100 :machine 3}
   {:id 4 :job :cooljob :duration 150 :machine 4}
   {:id 5 :job :ajob :duration 180 :machine 1}
   {:id 6 :job :ajob :duration 120 :machine 2}
   {:id 7 :job :ajob :duration 100 :machine 3}
   {:id 8 :job :ajob :duration 150 :machine 4 :startby 300}
   {:id 9 :job :another :duration 180 :machine 1}
   {:id 10 :job :another :duration 120 :machine 2}
   {:id 11 :job :another :duration 100 :machine 3}
   {:id 12 :job :another :duration 150 :machine 3 :startby 400}])


Ratiocinate!

(defn init-jobs
  "Binds tasks in a job to occur in the correct order.
  `lengths` must be a seq of integers defining no. of tasks in nth job."
  [tasks lengths]
  (if (seq lengths)
    (let [l (first lengths)]
      (all
      (everyg (fn [[x y]] (fd/&lt; x y)) (partition 2 1 (take l tasks)))
      (init-jobs (drop l tasks) (next lengths))))
    succeed))

(defn sort-tasks
  "Sorts tasks by `:job`, then by `:id` to prepare for `solvo`"
  [tasks]
  (-&gt;&gt; tasks (sort-by :job) (sort-by :id)))

(defn solvo*
  "Find a feasible ordering for tasks.
  tasks **MUST** be sorted by `:job`, then by `:id`."
  [tasks]
  (let [n (count tasks)
        lengths (-&gt;&gt; tasks (partition-by :job) (map count))
        vars (repeatedly n lvar)]
    (run* [q]
          (== q vars)
          (init-jobs vars lengths)
          (everyg #(fd/in % (fd/interval (- n 1))) vars)
          (fd/distinct vars))))

(defn constructo*
  "Calls solvo on some tasks, then associates ords with tasks.
  Returns a lazy (possibly huge) sequence."
  [tasks]
  (let [ts (sort-tasks tasks)
        feasible* (solvo* ts)]
    (map (fn [tasks ords]
          (map (fn [t ord]
                  (assoc t :ord ord))
                tasks
                ords))
        (repeat ts)
        feasible*)))


Filter

I'm a bit worried about the (lack of) speed of this gargantuan filter expression!

(some (fn [tasks]
        (reduce (fn [solution task]
                  (let [{:keys [machine duration job id startby]}
                        task

                        prev-machine (get-in solution [:prev-task job])

                        prev-end (max
                                  (or (:end-time (last (get-in solution [:tasks machine]))) 0)
                                  (or (:end-time (get-in solution [:tasks prev-machine])) 0))

                        task-w-end (assoc task :end-time (+ prev-end duration))]

                    (when (and startby (&lt; startby prev-end))
                      (reduced nil))
                    (-&gt; solution
                        (update-in [:tasks machine] #(conj % task-w-end))
                        (assoc-in [:prev-task job] machine))))
                {}
                tasks))
      (map (partial sort-by :ord) (constructo* test-data-2)))
A terser way to write sort-tasks would be:

(sort-by (juxt :job :id) tasks)