Browse code

2D: write loss in a way that works, also for linear functions

Bas Nijholt authored on 25/10/2017 15:12:10
Showing 1 changed files
... ...
@@ -471,32 +471,16 @@ class BalancingLearner(BaseLearner):
471 471
 
472 472
 # Learner2D and helper functions.
473 473
 
474
-def triangle_area(points):
475
-    """The area of a triangle span by `points`.
476
-
477
-    Parameters
478
-    ----------
479
-    points : numpy array
480
-        A sequence of the positions of the vertices of the triangle,
481
-        with shape (..., 3, ndim).
482
-
483
-    Returns
484
-    -------
485
-    area : numpy array
486
-        Areas of the triangles.
487
-    """
488
-    a, b, c = np.rollaxis(points, 1)
489
-    return 0.5 * np.cross(b - a, c - a, axis=1)
490
-
491
-
492
-def _deviation_from_linear_estimate(ip):
474
+def _losses_per_triangle(ip):
493 475
     tri = ip.tri
476
+    vs = ip.values.ravel()
477
+
494 478
     gradients = interpolate.interpnd.estimate_gradients_2d_global(
495
-        tri, ip.values.ravel(), tol=1e-6)
479
+        tri, vs, tol=1e-6)
496 480
     p = tri.points[tri.vertices]
497 481
     g = gradients[tri.vertices]
498
-    v = ip.values.ravel()[tri.vertices]
499
-    ndim = p.shape[1]
482
+    v = vs[tri.vertices]
483
+    ndim = p.shape[-1]
500 484
 
501 485
     dev = 0
502 486
     for j in range(ndim):
... ...
@@ -505,10 +489,15 @@ def _deviation_from_linear_estimate(ip):
505 489
         dev += abs(vest - v).max(axis=1)
506 490
 
507 491
     q = p[:, :-1, :] - p[:, -1, None, :]
508
-    vol = abs(q[:, 0, 0] * q[:, 1, 1] - q[:, 0, 1] * q[:, 1, 0])
509
-    vol /= special.gamma(1 + ndim)
510
-    return dev * vol
492
+    areas = abs(q[:, 0, 0] * q[:, 1, 1] - q[:, 0, 1] * q[:, 1, 0])
493
+    areas /= special.gamma(1 + ndim)
494
+    areas = np.sqrt(areas)
511 495
 
496
+    vs_scale = vs[tri.vertices].ptp()
497
+    if vs_scale != 0:
498
+        dev /= vs_scale
499
+
500
+    return dev * areas + areas**2
512 501
 
513 502
 class Learner2D(BaseLearner):
514 503
     """Learns and predicts a function 'f: ℝ^2 → ℝ'.
... ...
@@ -561,9 +550,8 @@ class Learner2D(BaseLearner):
561 550
         self._stack = []
562 551
         self._interp = {}
563 552
 
564
-        x, y = np.array(self.bounds)
565
-        xy_scale = np.array([x.ptp(), y.ptp()]) / 2
566
-        xy_mean = np.array([x.mean(), y.mean()])
553
+        xy_mean = np.mean(self.bounds, axis=1)
554
+        xy_scale = np.ptp(self.bounds, axis=1)
567 555
 
568 556
         def scale(points):
569 557
             return (points - xy_mean) / xy_scale
... ...
@@ -662,14 +650,6 @@ class Learner2D(BaseLearner):
662 650
                 self._stack.pop(i)
663 651
                 break
664 652
 
665
-    def _losses_per_triangle(self, ip):
666
-        dev = _deviation_from_linear_estimate(ip)
667
-        ps = ip.tri.points[ip.tri.vertices]
668
-        vs = ip.values[ip.tri.vertices]
669
-        triangle_size = triangle_area(ps) / 4  # /4 because the area=4
670
-        losses = np.hypot(dev / vs.ptp(), 0.5 * triangle_size)
671
-        return losses
672
-
673 653
     def _fill_stack(self, stack_till=None):
674 654
         if stack_till is None:
675 655
             stack_till = 1
... ...
@@ -681,7 +661,7 @@ class Learner2D(BaseLearner):
681 661
         ip = self.ip_combined()
682 662
         tri = ip.tri
683 663
 
684
-        losses = self._losses_per_triangle(ip)
664
+        losses = _losses_per_triangle(ip)
685 665
 
686 666
         def point_exists(p):
687 667
             eps = np.finfo(float).eps * self.points_combined.ptp() * 100
... ...
@@ -761,7 +741,7 @@ class Learner2D(BaseLearner):
761 741
         if n <= 4 or bounds_are_not_done:
762 742
             return np.inf
763 743
         ip = self.ip() if real else self.ip_combined()
764
-        losses = self._losses_per_triangle(ip)
744
+        losses = _losses_per_triangle(ip)
765 745
         return losses.max()
766 746
 
767 747
     def remove_unfinished(self):
... ...
@@ -774,8 +754,8 @@ class Learner2D(BaseLearner):
774 754
         x, y = self.bounds
775 755
         lbrt = x[0], y[0], x[1], y[1]
776 756
         if self.n_real >= 4:
777
-            x = np.linspace(-1, 1, n_x)
778
-            y = np.linspace(-1, 1, n_y)
757
+            x = np.linspace(-0.5, 0.5, n_x)
758
+            y = np.linspace(-0.5, 0.5, n_y)
779 759
             ip = self.ip()
780 760
             z = ip(x[:, None], y[None, :])
781 761
             return hv.Image(np.rot90(z), bounds=lbrt)