Repository: CCSI-Toolset/FOQUS

Issue:
Potential Issue with Unscaling Formula from nodeVars.py
During development for #1197, we found a potential mathematical inconsistency in the formula for the "Log" unscaling function. 
https://github.com/CCSI-Toolset/FOQUS/blob/fb0777c385d6c24286129e25bca2da6f1557e6a5/foqus_lib/framework/graph/nodeVars.py#L727-L728

For #1197, we found that the following alternative seems to work: 
```py
def unscale_log(array_in, lo, hi):
    result = lo * np.power(hi / lo, array_in)
    return result
```
For reference, a plot of the scaling formulas can be found here: https://www.desmos.com/calculator/dxyj2nmpln

Discussion:
@bpaul4: @franflame thank you for opening this issue. You are correct and I agree with your suggested form. The original scaled form is

$scaled = 10 \frac{\log unscaled - \log min}{\log max - \log min}$

Solving for $unscaled$ yields

$\log \frac{unscaled}{min} = \frac{scaled}{10} \log \frac{max}{min}$

$unscaled = min (10 \log \frac{max}{min})^\frac{scaled}{10} = min (\frac{max}{min})^\frac{scaled}{10}$

which matches your alternative formula.

@sotorrio1: I concur with @bpaul4, the math checks out. 

Solution patch:
diff --git a/foqus_lib/framework/graph/nodeVars.py b/foqus_lib/framework/graph/nodeVars.py
index 015b8dd1..b28436e7 100644
--- a/foqus_lib/framework/graph/nodeVars.py
+++ b/foqus_lib/framework/graph/nodeVars.py
@@ -725,7 +725,7 @@ class NodeVars(object):
             elif self.scaling == "Linear":
                 out = val * (self.max - self.min) / 10.0 + self.min
             elif self.scaling == "Log":
-                out = math.pow(self.min * (self.max / self.min), (val / 10.0))
+                out = self.min * math.pow((self.max / self.min), (val / 10.0))
             elif self.scaling == "Power":
                 out = math.log10(
                     (val / 10.0) * (math.pow(10, self.max) - math.pow(10, self.min))


Test patch:
diff --git a/foqus_lib/unit_test/nodeVars_test.py b/foqus_lib/unit_test/nodeVars_test.py
index dea27021..40b4c1ae 100644
--- a/foqus_lib/unit_test/nodeVars_test.py
+++ b/foqus_lib/unit_test/nodeVars_test.py
@@ -91,90 +91,90 @@ class testNodeVarsSteady(unittest.TestCase):
 
     def testScaleLinear(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
         var.value = 4.12
         var.scaling = "Linear"
         var.scale()
-        self.assertAlmostEqual(var.scaled, 1.3)
+        self.assertAlmostEqual(var.scaled, 0.9217, places=3)
 
     def testScaleLog(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
         var.value = 4.12
         var.scaling = "Log"
         var.scale()
-        self.assertAlmostEqual(var.scaled, 4.3986, places=3)
+        self.assertAlmostEqual(var.scaled, 2.8614, places=3)
 
     def testScalePower(self):
         var = self.makeVar()
-        var.min = 0
+        var.min = 0.1
         var.max = 2
         var.value = 0.2
         var.scaling = "Power"
         var.scale()
-        self.assertAlmostEqual(var.scaled, 0.0591, places=3)
+        self.assertAlmostEqual(var.scaled, 0.0330, places=3)
 
     def testScaleLog2(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
         var.value = 4.12
         var.scaling = "Log 2"
         var.scale()
-        self.assertAlmostEqual(var.scaled, 3.3646, places=3)
+        self.assertAlmostEqual(var.scaled, 2.6235, places=3)
 
     def testScalePower2(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
         var.value = 4.12
         var.scaling = "Power 2"
         var.scale()
-        self.assertAlmostEqual(var.scaled, 0.3877, places=3)
+        self.assertAlmostEqual(var.scaled, 0.2627, places=3)
 
     def testUnScaleLinear(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
-        var.scaled = 1.3
+        var.scaled = 0.9217
         var.scaling = "Linear"
         var.unscale()
-        self.assertAlmostEqual(var.value, 4.12)
+        self.assertAlmostEqual(var.value, 4.12, places=3)
 
     def testUnScaleLog(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
-        var.scaled = 4.3986
+        var.scaled = 2.8614
         var.scaling = "Log"
         var.unscale()
         self.assertAlmostEqual(var.value, 4.12, places=3)
 
     def testUnScalePower(self):
         var = self.makeVar()
-        var.min = 0
+        var.min = 0.1
         var.max = 2
-        var.scaled = 0.059080120451
+        var.scaled = 0.0330
         var.scaling = "Power"
         var.unscale()
         self.assertAlmostEqual(var.value, 0.2, places=3)
 
     def testUnScaleLog2(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
-        var.scaled = 3.3646
+        var.scaled = 2.6235
         var.scaling = "Log 2"
         var.unscale()
         self.assertAlmostEqual(var.value, 4.12, places=3)
 
     def testUnScalePower2(self):
         var = self.makeVar()
-        var.min = 1
+        var.min = 2
         var.max = 25
-        var.scaled = 0.3877365362129
+        var.scaled = 0.2627
         var.scaling = "Power 2"
         var.unscale()
         self.assertAlmostEqual(var.value, 4.12, places=3)