From 1bb6e8ae71b151b219c02404dbee34ceeb34e39c Mon Sep 17 00:00:00 2001
From: VIGNET Pierre <pierre.vignet@irisa.fr>
Date: Wed, 20 Nov 2019 17:20:32 +0100
Subject: [PATCH] [cmd] solutions_search; (re)Implementation of find_macs api;
 solutions search is now 3-4x times more performant

---
 command_line/cadbiom_cmd/solution_search.py | 78 ++++++++++++++-------
 1 file changed, 53 insertions(+), 25 deletions(-)

diff --git a/command_line/cadbiom_cmd/solution_search.py b/command_line/cadbiom_cmd/solution_search.py
index 810a4da..d40c3de 100644
--- a/command_line/cadbiom_cmd/solution_search.py
+++ b/command_line/cadbiom_cmd/solution_search.py
@@ -212,17 +212,29 @@ def main2(chart_file, mac_file, mac_step_file, mac_complete_file, mac_strong_fil
         current_start_prop = start_prop
 
 
-    i = len(previous_frontier_places)-1 if previous_frontier_places else 0
-    #with PyCallGraph(output=GraphvizOutput()):
-    while True:
-        LOGGER.info("%s:: START PROP: %s", final_prop, current_start_prop)
+    # Compute the number of previously computed frontier places
+    current_nb_sols = len(previous_frontier_places) if previous_frontier_places else 0
 
+    if current_nb_sols >= limit:
         # EXIT
-        i += 1
-        if i > limit:
-            LOGGER.info("%s:: Reaching the limitation of the number of solutions!",
-                        final_prop)
-            return
+        LOGGER.info("%s:: Reaching the limitation of the number of solutions!",
+                    final_prop)
+        return
+
+
+    ## Find macs in an optimized way with find_macs() (see the docstring for more info)
+    LOGGER.info("%s:: Start property: %s", final_prop, current_start_prop)
+    # with PyCallGraph(output=GraphvizOutput()):
+    find_macs(mcla,
+              mac_file, mac_step_file, mac_complete_file,
+              steps, final_prop, current_start_prop, inv_prop, limit, current_nb_sols)
+
+    return
+
+    ## Alternative and deprecated way
+    ## Find mac one by one with find_mac() (see the docstring for more info)
+    while True:
+        LOGGER.info("%s:: Start property: %s", final_prop, current_start_prop)
 
         ret = \
             find_mac(mcla,
@@ -269,29 +281,42 @@ def main2(chart_file, mac_file, mac_step_file, mac_complete_file, mac_strong_fil
 
 def find_macs(mcla,
               mac_file, mac_step_file, mac_complete_file,
-              steps, final_prop, start_prop, inv_prop):
-    """__mac_exhaustive_search, on cherche d´abord des solutions non minimales
+              steps, final_prop, start_prop, inv_prop, limit, current_nb_sols):
+    """Search for many solutions, save timings, and save frontiers.
+
+    For every new solution, the system is NOT reinitialized, and a satisfiability
+    test is made ONLY when there is no solution for the current step.
+    This test is made to evaluate the minimal number of steps for reachability.
+
+    Unlike find_mac(), this function is autonomous and takes into account the
+    limitation of the number of solutions.
+
+    .. TODO:: Handle all_macs flag like the old method with find_mac()
+        Not used very often but can be usefull sometimes...
+
+    __mac_exhaustive_search, on cherche d´abord des solutions non minimales
     (lfsol = self.__sq_dimacs_frontier_solutions(query, nb_step, 2))
     pour ensuite les élaguer en supprimant les places non essentielles à la
     satisfiabilité de la propriété
     (current_mac = self.__prune_frontier_solution(small_sol, query, nb_step)).
     Ce processus récursif est le plus "time-consuming" car nous n´avons pas
     le controle sur les solutions fournies par SAT et les solutions non minimales
-    sont en général très éloignées de la solution minimale,
-    cad contiennent beaucoup plus de places.
+    sont en général très éloignées de la solution minimale, c.-à-d. contiennent
+    beaucoup plus de places (ces places excédentaires sont dispensables).
 
+    :param limit: Limit the number of solutions.
+    :param current_nb_sols: The current number of solutions already found.
+        This number is used to limit the number of searched solutions.
+    :type limit: <int>
+    :type current_nb_sols: <int>
     :return: None
     """
-
     # Build query
     query = MCLSimpleQuery(start_prop, inv_prop, final_prop)
 
-    vmac_list = mcla.mac_search(query, steps)
-
-    # If yes, in how many steps ?
-    # min_step = mcla.unfolder.get_current_step()
-    for i, frontier_sol in enumerate(mcla.mac_search(query, steps)):
-        print("i", i)
+    # Iterate on solutions
+    for i, frontier_sol in enumerate(mcla.mac_search(query, steps), current_nb_sols):
+        LOGGER.info("%s:: Current solution n°%s", final_prop, i + 1)
 
         LOGGER.debug("%s:: Next MAC object:\n%s", final_prop, frontier_sol)
 
@@ -311,11 +336,14 @@ def find_macs(mcla,
         # with open(mac_step_file, 'a') as file:
         #     file.write(str(min_step)+'\n')
 
-    # Save min steps
-    # min_step = mcla.unfolder.get_current_step() - 1 # Magic number !
-    # LOGGER.debug("%s:: Save minimal steps: %s", final_prop, min_step)
-    # with open(mac_step_file, 'a') as file:
-    #     file.write(str(min_step)+'\n')
+        # Stop when limitation is reached
+        if i + 1 >= limit:
+            # EXIT
+            LOGGER.info("%s:: Reaching the limitation of the number of solutions!",
+                        final_prop)
+            return
+
+    LOGGER.info("%s:: STOP the search! No more MAC.", final_prop)
 
 
 def find_mac(mcla,
-- 
GitLab