From 9f996ae15a559b9724d64bde08afa825a0b338c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oriol=20Tint=C3=B3?= <oriol.tinto@lmu.de>
Date: Wed, 14 Jun 2023 16:04:49 +0200
Subject: [PATCH] Fix bisection methods to return the parameter that fulfills
 the conditions more close to the target constrains.

---
 .../compression/analyzer/analyzer_utils.py    | 29 ++++++++++++-------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/enstools/compression/analyzer/analyzer_utils.py b/enstools/compression/analyzer/analyzer_utils.py
index a1c7870..6fb53f6 100644
--- a/enstools/compression/analyzer/analyzer_utils.py
+++ b/enstools/compression/analyzer/analyzer_utils.py
@@ -150,6 +150,7 @@ def continuous_bisection_method(parameter_range: tuple,
                                 retry_repeated=5,
                                 threshold=0.1,
                                 direct_relation=True,
+                                results=None,
                                 ):
     """
     Recursively refine a parameter range by evaluating the parameter that lies in the middle of the range.
@@ -170,8 +171,13 @@ def continuous_bisection_method(parameter_range: tuple,
     :param direct_relation: If True, the relation between the function and the parameter is direct, that is,
                             increasing the parameter will increase the function value. If False, the relation is
                             inverse, that is, increasing the parameter will decrease the function value.
+    :param results: A dictionary that stores the function values for each parameter that has been evaluated.
+
     :return: The best value of the parameter that meets the exit conditions.
     """
+
+    if results is None:
+        results = {}
     # Get start and end from parameter range
     start, end = parameter_range
 
@@ -186,24 +192,23 @@ def continuous_bisection_method(parameter_range: tuple,
     # TODO: use logging and a debug mode to print this kind of things
     logging.debug("start=%.2e,end=%.2e value_at_middle=%f", start, end, float(value_at_middle))
 
-    # If the value at the middle is positive (all thresholds are fulfilled) we can return the parameter at the middle,
-    # otherwise select the safer one.
-    parameter_to_return = middle if value_at_middle > 0.0 else end
+    # Save result
+    results[middle] = value_at_middle
 
     # In case the accuracy exit condition is reached, return the parameter value at that point
-    if 0.0 <= value_at_middle < threshold:
-        return parameter_to_return
+    if 0.0 <= value_at_middle < threshold or depth >= max_depth or\
+            (value_at_middle == last_value and retry_repeated == 0):
+        positive_results = {k: v for k,v in results.items() if v > 0}
+        if positive_results:
+            return min(positive_results, key=positive_results.get)
+        else:
+            return middle
+
 
     # If the value is the same that the last try, we can retry few times
     if value_at_middle == last_value:
-        if retry_repeated == 0:
-            return parameter_to_return
         retry_repeated -= 1
 
-    # In case having reached the maximum depth, return the proper value
-    if depth >= max_depth:
-        return parameter_to_return
-
     # # Otherwise, set new parameter range and call the function again
     if comparison(value_at_middle, direct_relation=direct_relation):
         new_start, new_end = start, middle
@@ -218,6 +223,7 @@ def continuous_bisection_method(parameter_range: tuple,
                                        retry_repeated=retry_repeated,
                                        threshold=threshold,
                                        direct_relation=direct_relation,
+                                       results=results
                                        )
 
 
@@ -246,6 +252,7 @@ def discrete_bisection_method(parameters_list: list,
     :param threshold: The threshold for the method exit condition.
     :param direct_relation: Boolean indicating whether the relation between the parameter and the function value is
     direct or inverse.
+    :param results: A dictionary that stores the function values for each parameter that has been evaluated.
 
     :return: The parameter value that satisfies the constrain function.
 
-- 
GitLab