Update.
authorFrançois Fleuret <francois@fleuret.org>
Wed, 10 Jul 2024 21:26:06 +0000 (23:26 +0200)
committerFrançois Fleuret <francois@fleuret.org>
Wed, 10 Jul 2024 21:26:06 +0000 (23:26 +0200)
grids.py
main.py

index 6e9e6c7..d1653ee 100755 (executable)
--- a/grids.py
+++ b/grids.py
@@ -769,9 +769,97 @@ class Grids(problem.Problem):
                 ):
                     break
 
+    def compute_distance(self, walls, goal_i, goal_j, start_i, start_j):
+        max_length = walls.numel()
+        dist = torch.full_like(walls, max_length)
+
+        dist[goal_i, goal_j] = 0
+        pred_dist = torch.empty_like(dist)
+
+        while True:
+            pred_dist.copy_(dist)
+            d = (
+                torch.cat(
+                    (
+                        dist[None, 1:-1, 0:-2],
+                        dist[None, 2:, 1:-1],
+                        dist[None, 1:-1, 2:],
+                        dist[None, 0:-2, 1:-1],
+                    ),
+                    0,
+                ).min(dim=0)[0]
+                + 1
+            )
+
+            dist[1:-1, 1:-1].minimum_(d)  # = torch.min(dist[1:-1, 1:-1], d)
+            dist = walls * max_length + (1 - walls) * dist
+
+            if dist[start_i, start_j] < max_length or dist.equal(pred_dist):
+                return dist * (1 - walls)
+
     # @torch.compile
-    def task_islands(self, A, f_A, B, f_B):
-        pass
+    def task_path(self, A, f_A, B, f_B):
+        c = torch.randperm(len(self.colors) - 1)[:3] + 1
+        dist = torch.empty(self.height + 2, self.width + 2)
+        for X, f_X in [(A, f_A), (B, f_B)]:
+            nb_rec = torch.randint(3, (1,)) + 1
+            while True:
+                r = self.rec_coo(nb_rec, prevent_overlap=True)
+                X[...] = 0
+                f_X[...] = 0
+                for n in range(nb_rec):
+                    i1, j1, i2, j2 = r[n]
+                    X[i1:i2, j1:j2] = c[0]
+                    f_X[i1:i2, j1:j2] = c[0]
+                while True:
+                    i0, j0 = torch.randint(self.height, (1,)), torch.randint(
+                        self.width, (1,)
+                    )
+                    if X[i0, j0] == 0:
+                        break
+                while True:
+                    i1, j1 = torch.randint(self.height, (1,)), torch.randint(
+                        self.width, (1,)
+                    )
+                    if X[i1, j1] == 0:
+                        break
+                dist[...] = 1
+                dist[1:-1, 1:-1] = (X != 0).long()
+                dist[...] = self.compute_distance(dist, i1 + 1, j1 + 1, i0 + 1, j0 + 1)
+                if dist[i0 + 1, j0 + 1] >= 1 and dist[i0 + 1, j0 + 1] < self.height * 4:
+                    break
+
+            dist[1:-1, 1:-1] += (X != 0).long() * self.height * self.width
+            dist[0, :] = self.height * self.width
+            dist[-1, :] = self.height * self.width
+            dist[:, 0] = self.height * self.width
+            dist[:, -1] = self.height * self.width
+            # dist += torch.rand(dist.size())
+
+            i, j = i0 + 1, j0 + 1
+            while i != i1 + 1 or j != j1 + 1:
+                f_X[i - 1, j - 1] = c[2]
+                r, s, t, u = (
+                    dist[i - 1, j],
+                    dist[i, j - 1],
+                    dist[i + 1, j],
+                    dist[i, j + 1],
+                )
+                m = min(r, s, t, u)
+                if r == m:
+                    i = i - 1
+                elif t == m:
+                    i = i + 1
+                elif s == m:
+                    j = j - 1
+                else:
+                    j = j + 1
+
+            X[i0, j0] = c[2]
+            # f_X[i0, j0] = c[1]
+
+            X[i1, j1] = c[1]
+            f_X[i1, j1] = c[1]
 
     # for X, f_X in [(A, f_A), (B, f_B)]:
     # n = torch.arange(self.height * self.width).reshape(self.height, self.width)
@@ -797,7 +885,7 @@ class Grids(problem.Problem):
             self.task_scale,
             self.task_symbols,
             self.task_ortho,
-            # self.task_islands,
+            #            self.task_path,
         ]
 
     def trivial_prompts_and_answers(self, prompts, answers):
@@ -875,15 +963,17 @@ if __name__ == "__main__":
     # exit(0)
 
     # if True:
-    # nb,nrow = 72,4
-    nb, nrow = 8, 2
+    nb, nrow = 72, 4
+    nb, nrow = 8, 2
 
-    for t in grids.all_tasks():
-        # for t in [grids.task_replace_color]:
+    for t in grids.all_tasks():
+    for t in [grids.task_path]:
         print(t.__name__)
         prompts, answers = grids.generate_prompts_and_answers_(nb, tasks=[t])
         grids.save_quizzes("/tmp", t.__name__, prompts[:nb], answers[:nb], nrow=nrow)
 
+    # exit(0)
+
     nb = 1000
 
     for t in grids.all_tasks():
diff --git a/main.py b/main.py
index 1ef01e9..e6806d4 100755 (executable)
--- a/main.py
+++ b/main.py
@@ -88,14 +88,10 @@ parser.add_argument("--min_to_validate", type=int, default=None)
 
 parser.add_argument("--max_to_validate", type=int, default=None)
 
-parser.add_argument("--accuracy_to_make_c_quizzes", type=float, default=0.975)
+parser.add_argument("--accuracy_to_make_c_quizzes", type=float, default=0.9)
 
 parser.add_argument("--generation_temperature", type=float, default=2.0)
 
-parser.add_argument("--deterministic_validation", action="store_true", default=False)
-
-parser.add_argument("--bidirectional_validation", action="store_true", default=False)
-
 parser.add_argument("--dirty_debug", action="store_true", default=False)
 
 ######################################################################
@@ -435,113 +431,6 @@ def create_c_quizzes(
         quiz_machine.save_quizzes(args.result_dir, f"culture_c_quiz_{n_epoch:04d}", q)
 
 
-######################################################################
-
-
-def create_c_quizzes_(
-    models,
-    quiz_machine,
-    nb_for_train=1000,
-    nb_for_test=100,
-):
-    quizzes_and_nb_correct_records = []
-
-    nb_to_create = nb_for_train + nb_for_test
-
-    # ------------------------------------------------------------
-
-    standard_validity = lambda nb_correct: (nb_correct >= args.min_to_validate) & (
-        nb_correct <= args.max_to_validate
-    )
-
-    file_name = os.path.join(args.result_dir, f"culture_c_quiz_{n_epoch:04d}_logp.dat")
-
-    with open(file_name, "w") as logp_file:
-        while (
-            valid_c_quizzes(quizzes_and_nb_correct_records, standard_validity).size(0)
-            < nb_to_create
-        ):
-            # Select a model at random to generate the new quizzes
-
-            model_for_generation = models[torch.randint(len(models), (1,))]
-
-            c_quizzes = quiz_machine.generate_quizzes(
-                nb_to_create,
-                model_for_generation=model_for_generation,
-                temperature=args.generation_temperature,
-            )
-
-            # if args.prediction_correctness:
-
-            # else:
-            # logproba = quiz_machine.new(quiz_machine.size(0), len(models))
-            # for q,l in zip(quizzes.split(args.batch_size), logits.split(args.batch_size)):
-            # for model in models:
-            # l[...] = F.cross_entropy(model(q))
-
-            c_quizzes = c_quizzes[quiz_machine.non_trivial(c_quizzes)]
-
-            if c_quizzes.size(0) > 0:
-                nb_correct, seq_logproba = quiz_machine.compute_correctness(
-                    c_quizzes,
-                    models,
-                    bidirectional_validation=args.bidirectional_validation,
-                    deterministic_validation=args.deterministic_validation,
-                )
-
-                for n, l in zip(nb_correct, seq_logproba):
-                    s = " ".join([str(x.item()) for x in l])
-                    logp_file.write(f"{n} {s}\n")
-
-                if args.dirty_debug:
-                    nb_correct = torch.randint(
-                        len(models) + 1, nb_correct.size(), device=c_quizzes.device
-                    )
-
-                quizzes_and_nb_correct_records.append((c_quizzes, nb_correct))
-
-            nv = F.one_hot(nb_correct, num_classes=len(models) + 1).sum(0)
-            nv = " ".join([str(x.item()) for x in nv])
-
-            nb_validated = valid_c_quizzes(
-                quizzes_and_nb_correct_records, standard_validity
-            ).size(0)
-
-            log_string(
-                f"keep c_quizzes model {model_for_generation.id} kept {nv} nb_accumulated {nb_validated} / {nb_to_create}"
-            )
-
-    # store the new c_quizzes which have been validated
-
-    new_c_quizzes = valid_c_quizzes(quizzes_and_nb_correct_records, standard_validity)
-
-    quiz_machine.reverse_random_half_in_place(new_c_quizzes)
-
-    quiz_machine.store_c_quizzes(new_c_quizzes[:nb_for_train], for_train=True)
-    quiz_machine.store_c_quizzes(new_c_quizzes[nb_for_train:], for_train=False)
-
-    # save a bunch of images to investigate what quizzes with a
-    # certain nb of correct predictions look like
-
-    for n in range(len(models) + 1):
-        s = (
-            "_validated"
-            if n >= args.min_to_validate and n <= args.max_to_validate
-            else ""
-        )
-
-        q = valid_c_quizzes(
-            quizzes_and_nb_correct_records, criteria=lambda nb_correct: nb_correct == n
-        )[:72]
-
-        quiz_machine.reverse_random_half_in_place(q)
-
-        if q.size(0) > 0:
-            quiz_machine.save_quizzes(
-                args.result_dir, f"culture_c_quiz_{n_epoch:04d}_N{n}{s}", q
-            )
-
-
 ######################################################################
 
 models = []
@@ -643,6 +532,11 @@ if args.dirty_debug:
     nb_new_c_quizzes_for_train = 100
     nb_new_c_quizzes_for_test = 10
 
+    def standard_validity(logproba):
+        l = logproba.sort(dim=-1).values
+        return l[:, 0] < math.log(0.99)
+
+
 ######################################################################
 
 for n_epoch in range(args.nb_epochs):
@@ -652,7 +546,7 @@ for n_epoch in range(args.nb_epochs):
     log_string(f"current_test_accuracies {cta}")
 
     ##################################################
-    # Select, improve, and eval the worst model
+    # Select, improve, and eval the worst models
 
     ranked_models = sorted(models, key=lambda m: float(m.main_test_accuracy))
 
@@ -674,14 +568,12 @@ for n_epoch in range(args.nb_epochs):
         model.TRAINING_LOCK.release()
 
     ##################################################
-    # Replace a fraction of the w_quizzes with fresh ones
+    # Renew the train sets
 
     log_string(
         f"cache_w_quizzes contains {quiz_machine.problem.nb_cached_quizzes()} quizzes"
     )
 
-    # Renew entirely the train set
-
     for model in weakest_models:
         quiz_machine.renew_w_quizzes(model, args.nb_train_samples)