diff --git a/dlmo.mzn b/dlmo.mzn index d3b996d..c720fa2 100644 --- a/dlmo.mzn +++ b/dlmo.mzn @@ -30,6 +30,7 @@ array[Patient] of var bool: first_detected = [ | pt in Patient ]; + % --- Constraint --- % The DLMO should be detected on the second round (if not detected on the first round) @@ -42,12 +43,16 @@ constraint forall (pt in Patient) ( ); % --- Objective --- -solve minimize +% solve minimize +% Pareto Front objectives +var int: total_tests ::output = % Number of tests on first round (length(Patient) * first_num_samples) % Number of test on second round + (count(pt in Patient) (not first_detected[pt]) * second_num_samples); +var int: total_not_detected ::output = length(Patient) - count(first_detected); + % --- Output --- output ["First Round:\n"] @@ -57,7 +62,7 @@ output ["First Round:\n"] ++ ["\n\nSecond Round:\n"] ++ [format(2, i) ++ " " | i in Horizon] ++ ["\n |"] ++ [if i in fix(first_sample-(second_num_samples-1))..fix(first_sample-1) \/ i in fix(first_sample + (first_num_samples - 1))..fix(first_sample + (first_num_samples - 1) + (second_num_samples - 1) - 1) then "====|" else "----|" endif | i in -5..1] -++ ["\n\nTotal number of tests = \(_objective);"] +++ ["\n\nTotal number of tests = \(total_tests);"] ++ ["\n\n\nfirst_sample = \(first_sample);\n"] diff --git a/pareto.py b/pareto.py new file mode 100644 index 0000000..8ecbdc2 --- /dev/null +++ b/pareto.py @@ -0,0 +1,54 @@ +import minizinc + + +def pareto_front(instance, objectives): + front = [] + result = None + with instance.branch() as instance: + failed = False + while not failed: + result = instance.solve() + # print(result) + # print("----------------------------------------") + if result == None or result.status == minizinc.Status.UNSATISFIABLE: + failed = True + else: + to_remove = [] + for i in range(len(front)): + if all([result[o] <= front[i][o] for o in objectives]): + to_remove.append(i) + # print(to_remove[::-1]) + for r in to_remove[::-1]: + front.pop(r) + instance.add_string( + "constraint " + + " \/ ".join([f"{o} < {result[o]}" for o in objectives]) + + ";\n" + ) + front.append(result) + return front + + +if __name__ == "__main__": + inst = minizinc.Instance( + minizinc.Solver.lookup("chuffed"), minizinc.Model("dlmo.mzn") + ) + inst.add_file("full.dzn") + + front = pareto_front(inst, ["total_tests", "total_not_detected"]) + + for sol in front: + print( + ",".join( + [ + str(sol["total_tests"]), + str(sol["total_not_detected"]), + str(sol["first_sample"]), + str(sol["first_num_samples"]), + str(sol["second_num_samples"]), + ] + ) + ) + print("----------------------------------------------------") + for sol in front: + print(sol) diff --git a/pareto_front.txt b/pareto_front.txt new file mode 100644 index 0000000..d461045 --- /dev/null +++ b/pareto_front.txt @@ -0,0 +1,109 @@ +792,82,-3,3,3 +845,39,-4,4,3 +958,24,-4,5,2 +979,23,-5,5,3 +1108,8,-5,6,2 +1274,0,-5,7,8 +---------------------------------------------------- +First Round: +-5 -4 -3 -2 -1 0 1 2 + |----|----|====|====|----|----|----| + +First try (%): 54.94505494505495 + + +Second Round: +-5 -4 -3 -2 -1 0 1 2 + |====|====|----|----|====|====|----| + +Total number of tests = 792; + + +first_sample = -3; +first_num_samples = 3; +second_num_samples = 3; +First Round: +-5 -4 -3 -2 -1 0 1 2 + |----|====|====|====|----|----|----| + +First try (%): 78.57142857142857 + + +Second Round: +-5 -4 -3 -2 -1 0 1 2 + |====|----|----|----|====|====|----| + +Total number of tests = 845; + + +first_sample = -4; +first_num_samples = 4; +second_num_samples = 3; +First Round: +-5 -4 -3 -2 -1 0 1 2 + |----|====|====|====|====|----|----| + +First try (%): 86.81318681318682 + + +Second Round: +-5 -4 -3 -2 -1 0 1 2 + |====|----|----|----|----|====|----| + +Total number of tests = 958; + + +first_sample = -4; +first_num_samples = 5; +second_num_samples = 2; +First Round: +-5 -4 -3 -2 -1 0 1 2 + |====|====|====|====|----|----|----| + +First try (%): 87.36263736263736 + + +Second Round: +-5 -4 -3 -2 -1 0 1 2 + |----|----|----|----|====|====|----| + +Total number of tests = 979; + + +first_sample = -5; +first_num_samples = 5; +second_num_samples = 3; +First Round: +-5 -4 -3 -2 -1 0 1 2 + |====|====|====|====|====|----|----| + +First try (%): 95.60439560439561 + + +Second Round: +-5 -4 -3 -2 -1 0 1 2 + |----|----|----|----|----|====|----| + +Total number of tests = 1108; + + +first_sample = -5; +first_num_samples = 6; +second_num_samples = 2; +First Round: +-5 -4 -3 -2 -1 0 1 2 + |====|====|====|====|====|====|----| + +First try (%): 100.0 + + +Second Round: +-5 -4 -3 -2 -1 0 1 2 + |----|----|----|----|----|----|====| + +Total number of tests = 1274; + + +first_sample = -5; +first_num_samples = 7; +second_num_samples = 8; diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..c99edfe --- /dev/null +++ b/poetry.lock @@ -0,0 +1,21 @@ +[[package]] +name = "minizinc" +version = "0.5.0" +description = "Access MiniZinc directly from Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +dzn = ["lark-parser (>=0.7.5)"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.8" +content-hash = "49ec9364d3548273c0b3fdc5f79b951566db8dd93f01d69b758811a0f578da22" + +[metadata.files] +minizinc = [ + {file = "minizinc-0.5.0-py3-none-any.whl", hash = "sha256:678f6cc5894290960aef63df143033912541050c17912bf1fd5d28d814eb3d74"}, + {file = "minizinc-0.5.0.tar.gz", hash = "sha256:d60580d9c71cb8a8d819f117803f1ab922907367f3d09aaf9e2e0c668ccc38ac"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8a45eed --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "dlmo-model" +version = "0.1.0" +description = "" +authors = ["Jip J. Dekker "] + +[tool.poetry.dependencies] +python = "^3.8" +minizinc = "^0.5.0" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api"