def test_excluder_spread_axes(self):
        sp = SpiralGenerator(["s1", "s2"], ["mm", "mm"], centre=[0, 0], radius=1, scale=0.5, alternate=True)
        y = LineGenerator("y", "mm", 0, 1, 3, True)
        z = LineGenerator("z", "mm", -2, 3, 6, True)
        e = ROIExcluder([CircularROI([0., 0.], 1.0)], ["s1", "z"])
        g = CompoundGenerator([z, y, sp], [e], [])

        g.prepare()

        s1_pos, s2_pos = sp.positions["s1"], sp.positions["s2"]
        s1_pos = np.tile(np.append(s1_pos, s1_pos[::-1]), 9)
        s2_pos = np.tile(np.append(s2_pos, s2_pos[::-1]), 9)
        y_pos = np.tile(np.repeat(np.array([0, 0.5, 1.0, 1.0, 0.5, 0]), sp.size), 3)
        z_pos = np.repeat(np.array([-2, -1, 0, 1, 2, 3]), sp.size * 3)

        mask_func = lambda ps1, pz: ps1**2 + pz**2 <= 1
        mask = mask_func(s1_pos, z_pos)

        expected_s1 = s1_pos[mask]
        expected_s2 = s2_pos[mask]
        expected_y = y_pos[mask]
        expected_z = z_pos[mask]
        expected_positions = [{'s1':ps1, 's2':ps2, 'y':py, 'z':pz}
                for (ps1, ps2, py, pz) in zip(expected_s1, expected_s2, expected_y, expected_z)]
        positions = [point.positions for point in list(g.iterator())]

        self.assertEqual(positions, expected_positions)
    def test_mixed_alternating_generators(self):
        x_pos = np.array([0, 1, 2])
        x_bounds = np.array([-0.5, 0.5, 1.5, 2.5])
        y_pos = np.array([10, 11, 12])
        z_pos = np.array([20, 21, 22])
        w_pos = np.array([30, 31, 32])
        gx = Mock(axes=["x"],
                  positions={"x": x_pos},
                  bounds={"x": x_bounds},
                  size=3,
                  alternate=True)
        gy = Mock(axes=["y"], positions={"y": y_pos}, size=3, alternate=True)
        gz = Mock(axes=["z"], positions={"z": z_pos}, size=3, alternate=False)
        gw = Mock(axes=["w"], positions={"w": w_pos}, size=3, alternate=False)

        mask = np.array([1, 1, 0, 1, 1, 1, 1, 0, 1] * 9)
        indices = np.nonzero(mask)[0]
        e = Mock(axes=["x", "y", "z", "w"])
        e.create_mask.return_value = mask

        d = Dimension([gw, gz, gy, gx], [e])
        d.prepare()

        expected_x = np.append(np.tile(np.append(x_pos, x_pos[::-1]), 13),
                               x_pos)[indices]
        expected_y = np.repeat(
            np.append(np.tile(np.append(y_pos, y_pos[::-1]), 4), y_pos),
            3)[indices]
        expected_z = np.tile(np.repeat(z_pos, 9), 3)[indices]
        expected_w = np.repeat(w_pos, 27)[indices]
        self.assertEqual(False, d.alternate)
        self.assertEqual(expected_x.tolist(), d.get_positions("x").tolist())
        self.assertEqual(expected_y.tolist(), d.get_positions("y").tolist())
        self.assertEqual(expected_z.tolist(), d.get_positions("z").tolist())
        self.assertEqual(expected_w.tolist(), d.get_positions("w").tolist())
Example #3
0
    def test_spread_excluder_multi_axes_per_gen(self):
        gx1_pos = np.array([1, 2, 3, 4, 5])
        gx2_pos = np.array([11, 10, 9, 8, 7])
        gy_pos = np.array([-1, 0, 1])
        gz_pos = np.array([1, 0, -1, -2, -3])
        mask_x1z_func = lambda px, pz: (px-4)**2 + (pz+1)**2 <= 1
        exz = Mock(axes=["gx1", "gz"], create_mask=Mock(side_effect=mask_x1z_func))
        gx = Mock(axes=["gx1", "gx2"], positions={"gx1":gx1_pos, "gx2":gx2_pos}, size=5, alternate=False)
        gy = Mock(axes=["gy"], positions={"gy":gy_pos}, size=3, alternate=False)
        gz = Mock(axes=["gz"], positions={"gz":gz_pos}, size=5, alternate=False)
        d = Dimension.merge_dimensions([Dimension(gz), Dimension(gy), Dimension(gx)])

        d.apply_excluder(exz)
        d.prepare()

        x1_positions = np.tile(gx1_pos, 15)
        x2_positions = np.tile(gx2_pos, 15)
        y_positions = np.repeat(np.tile(gy_pos, 5), 5)
        z_positions = np.repeat(gz_pos, 15)

        mask = mask_x1z_func(x1_positions, z_positions)
        expected_x1 = x1_positions[mask].tolist()
        expected_x2 = x2_positions[mask].tolist()
        expected_y = y_positions[mask].tolist()
        expected_z = z_positions[mask].tolist()

        self.assertEqual(expected_x1, d.get_positions("gx1").tolist())
        self.assertEqual(expected_x2, d.get_positions("gx2").tolist())
        self.assertEqual(expected_y, d.get_positions("gy").tolist())
        self.assertEqual(expected_z, d.get_positions("gz").tolist())
Example #4
0
    def test_high_dimensional_excluder(self):
        w_pos = np.array([0, 1, 2, 3, 4, 5])
        x_pos = np.array([0, 1, 2, 3, 4, 5])
        y_pos = np.array([0, 1, 2, 3, 4, 5])
        z_pos = np.array([0, 1, 2, 3, 4, 5])
        mask_function = lambda pw, px, py, pz: (pw-2)**2 + (px-2)**2 + (py-1)**2 + (pz-3)**2 <= 1.1
        excluder = Mock(axes=["w", "x", "y", "z"], create_mask=Mock(side_effect=mask_function))
        gw = Mock(axes=["w"], positions={"w":w_pos}, size=len(w_pos), alternate=False)
        gx = Mock(axes=["x"], positions={"x":x_pos}, size=len(x_pos), alternate=False)
        gy = Mock(axes=["y"], positions={"y":y_pos}, size=len(y_pos), alternate=False)
        gz = Mock(axes=["z"], positions={"z":z_pos}, size=len(z_pos), alternate=False)
        d = Dimension.merge_dimensions([Dimension(gz), Dimension(gy), Dimension(gx), Dimension(gw)])

        d.apply_excluder(excluder)
        d.prepare()

        w_positions = np.tile(w_pos, len(x_pos) * len(y_pos) * len(z_pos))
        x_positions = np.repeat(np.tile(x_pos, len(y_pos) * len(z_pos)), len(w_pos))
        y_positions = np.repeat(np.tile(y_pos, len(z_pos)), len(w_pos) * len(x_pos))
        z_positions = np.repeat(z_pos, len(w_pos) * len(x_pos) * len(y_pos))
        mask = mask_function(w_positions, x_positions, y_positions, z_positions)
        w_expected = w_positions[mask].tolist()
        x_expected = x_positions[mask].tolist()
        y_expected = y_positions[mask].tolist()
        z_expected = z_positions[mask].tolist()

        self.assertEqual(w_expected, d.get_positions("w").tolist())
        self.assertEqual(x_expected, d.get_positions("x").tolist())
        self.assertEqual(y_expected, d.get_positions("y").tolist())
        self.assertEqual(z_expected, d.get_positions("z").tolist())
Example #5
0
 def test_prepare(self):
     d = Dimension(
         Mock(axes=["x", "y"],
              positions={
                  "x": np.array([0]),
                  "y": np.array([0])
              },
              size=30))
     m1 = np.array([0, 1, 0, 1, 1, 0], dtype=np.int8)
     m2 = np.array([1, 1, 0, 0, 1], dtype=np.int8)
     d._masks = [{
         "repeat": 2,
         "tile": 2.5,
         "mask": m1
     }, {
         "repeat": 2,
         "tile": 3,
         "mask": m2
     }]
     e1 = np.array([0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0], dtype=np.int8)
     e1 = np.append(np.tile(e1, 2), e1[:len(e1) // 2])
     e2 = np.array([1, 1, 1, 1, 0, 0, 0, 0, 1, 1], dtype=np.int8)
     e2 = np.tile(e2, 3)
     expected = e1 & e2
     d.prepare()
     self.assertEqual(expected.tolist(), d.mask.tolist())
    def prepare(self):
        """
        Create and return a mask for every point in the dimension

        e.g. (with [y1, y2, y3] and [x1, x2, x3] both alternating)
        y:    y1, y1, y1, y2, y2, y2, y3, y3, y3
        x:    x1, x2, x3, x3, x2, x1, x1, x2, x3
        mask: m1, m2, m3, m4, m5, m6, m7, m8, m9

        Returns:
            np.array(int8): One dimensional mask array
        """
        if self._prepared:
            return
        mask = np.full(self._max_length, True, dtype=np.int8)
        for m in self._masks:
            assert len(m["mask"]) * m["repeat"] * m["tile"] == len(mask), \
                "Mask lengths are not consistent"
            expanded = np.repeat(m["mask"], m["repeat"])
            if m["tile"] % 1 != 0:
                ex = np.tile(expanded, int(m["tile"]))
                expanded = np.append(ex, expanded[:int(len(expanded) // 2)])
            else:
                expanded = np.tile(expanded, int(m["tile"]))
            mask &= expanded
        # we have to assume the "returned" mask may be edited in place
        # so we have to store a copy
        self.mask = mask
        self.indices = self.mask.nonzero()[0]
        self.size = len(self.indices)
        self._prepared = True
    def test_positions_non_alternate(self):
        g1, g2, g3 = Mock(), Mock(), Mock()
        g1.axes, g2.axes, g3.axes = ["g1"], ["g2"], ["g3"]
        g1.size, g2.size, g3.size = 3, 4, 5
        g1.positions = {"g1": np.array([0, 1, 2])}
        g1.bounds = {"g1": np.array([-0.5, 0.5, 1.5, 2.5])}
        g2.positions = {"g2": np.array([-1, 0, 1, 2])}
        g2.bounds = {"g2": np.array([-1.5, -0.5, 0.5, 1.5, 2.5])}
        g3.positions = {"g3": np.array([-2, 0, 2, 4, 6])}
        g3.bounds = {"g3": np.array([-3, -1, 1, 3, 5, 7])}
        g1.alternate = False
        g2.alternate = False
        g3.alternate = False
        e1, e2 = Mock(), Mock()
        e1.axes = ["g1", "g2"]
        e2.axes = ["g2", "g3"]
        m1 = np.repeat(np.array([0, 1, 0, 1, 1, 0]), 10)
        m2 = np.tile(np.array([1, 0, 0, 1, 1, 1]), 10)
        e1.create_mask.return_value = m1
        e2.create_mask.return_value = m2

        d = Dimension([g1, g2, g3], [e1, e2])
        d.prepare()

        expected_mask = m1 & m2
        expected_indices = expected_mask.nonzero()[0]
        expected_g1 = np.repeat(g1.positions["g1"], 5 * 4)[expected_indices]
        expected_g2 = np.tile(np.repeat(g2.positions["g2"], 5),
                              3)[expected_indices]
        expected_g3 = np.tile(g3.positions["g3"], 3 * 4)[expected_indices]
        self.assertEqual(expected_mask.tolist(), d.mask.tolist())
        self.assertEqual(expected_indices.tolist(), d.indices.tolist())
        self.assertEqual(expected_g1.tolist(), d.positions["g1"].tolist())
        self.assertEqual(expected_g2.tolist(), d.positions["g2"].tolist())
        self.assertEqual(expected_g3.tolist(), d.positions["g3"].tolist())
Example #8
0
    def get_mesh_map(self, axis):
        """
        Retrieve the mesh map (indices) for a given axis within the dimension.

        Args:
            axis (str): axis to get positions for
        Returns:
            Positions (np.array): Array of mesh indices
        """
        # the points for this axis must be scaled and then indexed
        if not self._prepared:
            raise ValueError("Must call prepare first")
        # scale up points for axis
        gen = [g for g in self.generators if axis in g.axes][0]
        points = gen.positions[axis]
        # just get index of points instead of actual point value
        points = np.arange(len(points))

        if gen.alternate:
            points = np.append(points, points[::-1])
        tile = 0.5 if self.alternate else 1
        repeat = 1
        for g in self.generators[:self.generators.index(gen)]:
            tile *= g.size
        for g in self.generators[self.generators.index(gen) + 1:]:
            repeat *= g.size
        points = np.repeat(points, repeat)
        if tile % 1 != 0:
            p = np.tile(points, int(tile))
            points = np.append(p, points[:int(len(points) // 2)])
        else:
            points = np.tile(points, int(tile))
        return points[self.indices]
Example #9
0
    def test_excluder_over_spread_axes(self):
        gw_pos = np.array([0.1, 0.2])
        gx_pos = np.array([0, 1, 2, 3])
        gy_pos = np.array([10, 11, 12, 13])
        gz_pos = np.array([100, 101, 102, 103])
        go_pos = np.array([1000, 1001, 1002])
        mask_xz_func = lambda px, pz: (px - 1)**2 + (pz - 102)**2 <= 1
        exz = Mock(axes=["gx", "gz"],
                   create_mask=Mock(side_effect=mask_xz_func))
        gw = Mock(axes=["gw"],
                  positions={"gw": gw_pos},
                  size=2,
                  alternate=False)
        gx = Mock(axes=["gx"],
                  positions={"gx": gx_pos},
                  size=4,
                  alternate=False)
        gy = Mock(axes=["gy"],
                  positions={"gy": gy_pos},
                  size=4,
                  alternate=False)
        gz = Mock(axes=["gz"],
                  positions={"gz": gz_pos},
                  size=4,
                  alternate=False)
        go = Mock(axes=["go"],
                  positions={"go": go_pos},
                  size=3,
                  alternate=False)
        dw = Dimension(gw)
        dx = Dimension(gx)
        dy = Dimension(gy)
        dz = Dimension(gz)
        do = Dimension(go)
        d = Dimension.merge_dimensions([do, dz, dy, dx, dw])

        d.apply_excluder(exz)
        d.prepare()

        x_positions = np.tile(np.array([0, 1, 2, 3]), 16)
        y_positions = np.repeat(np.tile(np.array([10, 11, 12, 13]), 4), 4)
        z_positions = np.repeat(np.array([100, 101, 102, 103]), 16)
        x_positions = np.tile(np.repeat(x_positions, gw.size), go.size)
        y_positions = np.tile(np.repeat(y_positions, gw.size), go.size)
        z_positions = np.tile(np.repeat(z_positions, gw.size), go.size)

        mask = mask_xz_func(x_positions, z_positions)
        expected_x = x_positions[mask].tolist()
        expected_y = y_positions[mask].tolist()
        expected_z = z_positions[mask].tolist()

        self.assertEqual(expected_x, d.get_positions("gx").tolist())
        self.assertEqual(expected_y, d.get_positions("gy").tolist())
        self.assertEqual(expected_z, d.get_positions("gz").tolist())
Example #10
0
 def test_apply_excluders_over_multiple_gens(self):
     gx_pos = np.array([1, 2, 3, 4, 5])
     gy_pos = np.zeros(5)
     hx_pos = np.zeros(5)
     hy_pos = np.array([-1, -2, -3])
     mask = np.full(15, 1, dtype=np.int8)
     e = Mock(axes=["gx", "hy"], create_mask=Mock(return_value=mask))
     g = Mock(axes=["gx", "gy"],
              positions={
                  "gx": gx_pos,
                  "gy": gy_pos
              },
              size=len(gx_pos),
              alternate=False)
     h = Mock(axes=["hx", "hy"],
              positions={
                  "hx": hx_pos,
                  "hy": hy_pos
              },
              size=len(hy_pos),
              alternate=False)
     d = Dimension(g)
     d.generators = [g, h]
     d.size = g.size * h.size
     d.apply_excluder(e)
     d._masks[0]["mask"] = d._masks[0]["mask"].tolist()
     self.assertEqual([{
         "repeat": 1,
         "tile": 1,
         "mask": mask.tolist()
     }], d._masks)
     self.assertTrue((np.repeat(np.array([1, 2, 3, 4, 5]),
                                3) == e.create_mask.call_args[0][0]).all())
     self.assertTrue((np.tile(np.array([-1, -2, -3]),
                              5) == e.create_mask.call_args[0][1]).all())
Example #11
0
    def apply_excluder(self, excluder):
        """Apply an excluder with axes matching some axes in the dimension to
        produce an internal mask"""
        if self._prepared:
            raise ValueError("Can not apply excluders after"
                             "prepare has been called")
        axis_inner = excluder.axes[0]
        axis_outer = excluder.axes[1]
        gen_inner = [g for g in self.generators if axis_inner in g.axes][0]
        gen_outer = [g for g in self.generators if axis_outer in g.axes][0]
        points_x = gen_inner.positions[axis_inner]
        points_y = gen_outer.positions[axis_outer]
        if self.generators.index(gen_inner) > self.generators.index(gen_outer):
            gen_inner, gen_outer = gen_outer, gen_inner
            axis_inner, axis_outer = axis_outer, axis_inner
            points_x, points_y = points_y, points_x

        if gen_inner is gen_outer and self.alternate:
            points_x = np.append(points_x, points_x[::-1])
            points_y = np.append(points_y, points_y[::-1])
        elif self.alternate:
            points_x = np.append(points_x, points_x[::-1])
            points_x = np.repeat(points_x, gen_outer.size)
            points_y = np.append(points_y, points_y[::-1])
            points_y = np.tile(points_y, gen_inner.size)
        elif gen_inner is not gen_outer:
            points_x = np.repeat(points_x, gen_outer.size)
            points_y = np.tile(points_y, gen_inner.size)

        if axis_inner == excluder.axes[0]:
            excluder_mask = excluder.create_mask(points_x, points_y)
        else:
            excluder_mask = excluder.create_mask(points_y, points_x)
        tile = 0.5 if self.alternate else 1
        repeat = 1
        found_axis = False
        for g in self.generators:
            if axis_inner in g.axes or axis_outer in g.axes:
                found_axis = True
            else:
                if found_axis:
                    repeat *= g.size
                else:
                    tile *= g.size

        m = {"repeat":repeat, "tile":tile, "mask":excluder_mask}
        self._masks.append(m)
Example #12
0
 def test_get_positions_single_gen(self):
     gx_pos = np.tile(np.array([0, 1, 2, 3]), 4)
     gy_pos = np.repeat(np.array([0, 1, 2, 3]), 4)
     mask_func = lambda px, py: (px-1)**2 + (py-2)**2 <= 1
     g = Mock(axes=["gx", "gy"], positions={"gx":gx_pos, "gy":gy_pos}, size=16, alternate=False)
     e = Mock(axes=["gx", "gy"], create_mask=Mock(side_effect=mask_func))
     d = Dimension(g)
     d.apply_excluder(e)
     d.prepare()
     self.assertEqual([1, 0, 1, 2, 1], d.get_positions("gx").tolist())
     self.assertEqual([1, 2, 2, 2, 3], d.get_positions("gy").tolist())
Example #13
0
 def test_apply_excluders_with_scaling(self):
     g1_pos = np.array([1, 2, 3])
     g2_pos = np.array([-1, -2])
     mask_func = lambda px, py: np.full(len(px), 1, dtype=np.int8)
     g1 = Mock(axes=["g1"], positions={"g1":g1_pos}, size=len(g1_pos))
     g2 = Mock(axes=["g2"], positions={"g2":g2_pos}, size=len(g2_pos))
     e = Mock(axes=["g1", "g2"], create_mask=Mock(side_effect=mask_func))
     d = Dimension(g1)
     d.alternate = True
     d.generators = [Mock(size=5, axes=[]), g1, g2, Mock(size=7, axes=[])]
     d.size = 5 * len(g1_pos) * len(g2_pos) * 7
     d.apply_excluder(e)
     d._masks[0]["mask"] = d._masks[0]["mask"].tolist()
     expected_mask = [1] * 12
     self.assertEqual([{"repeat":7, "tile":2.5, "mask":expected_mask}], d._masks)
     self.assertTrue((np.repeat(np.append(g1_pos, g1_pos[::-1]), 2) == e.create_mask.call_args[0][0]).all())
     self.assertTrue((np.tile(np.append(g2_pos, g2_pos[::-1]), 3) == e.create_mask.call_args[0][1]).all())
Example #14
0
    def prepare(self):
        """
        Prepare data structures required to determine size and
        filtered positions of the dimension.
        Must be called before get_positions or get_mesh_map are called.
        """
        axis_positions = {}
        axis_bounds_lower = {}
        axis_bounds_upper = {}
        masks = []
        # scale up all position arrays
        # inner generators are tiled by the size of out generators
        # outer generators have positions repeated by the size of inner generators
        repeats, tilings, dim_size = 1, 1, 1
        for g in self.generators:
            repeats *= g.size
            dim_size *= g.size

        for gen in self.generators:
            repeats /= gen.size
            for axis in gen.axes:
                positions = gen.positions[axis]
                if gen.alternate:
                    positions = np.append(positions, positions[::-1])
                    positions = np.repeat(positions, repeats)
                    p = np.tile(positions, (tilings // 2))
                    if tilings % 2 != 0:
                        positions = np.append(
                            p, positions[:int(len(positions) // 2)])
                    else:
                        positions = p
                else:
                    positions = np.repeat(positions, repeats)
                    positions = np.tile(positions, tilings)
                axis_positions[axis] = positions
            tilings *= gen.size

        # produce excluder masks
        for excl in self.excluders:
            arrays = [axis_positions[axis] for axis in excl.axes]
            excluder_mask = excl.create_mask(*arrays)
            masks.append(excluder_mask)

        # AND all masks together (empty mask is all values selected)
        mask = masks[0] if len(masks) else np.full(
            dim_size, True, dtype=np.int8)
        for m in masks[1:]:
            mask &= m

        gen = self.generators[-1]
        if getattr(gen, "bounds", None):
            tilings = np.prod(np.array([g.size for g in self.generators[:-1]]))
            if gen.alternate:
                tilings /= 2.
            for axis in gen.axes:
                upper_base = gen.bounds[axis][1:]
                lower_base = gen.bounds[axis][:-1]
                upper, lower = upper_base, lower_base
                if gen.alternate:
                    upper = np.append(upper_base, lower_base[::-1])
                    lower = np.append(lower_base, upper_base[::-1])
                upper = np.tile(upper, int(tilings))
                lower = np.tile(lower, int(tilings))
                if tilings % 1 != 0:
                    upper = np.append(upper, upper_base)
                    lower = np.append(lower, lower_base)
                axis_bounds_upper[axis] = upper
                axis_bounds_lower[axis] = lower

        self.mask = mask
        self.indices = self.mask.nonzero()[0]
        self.size = len(self.indices)
        self.positions = {
            axis: axis_positions[axis][self.indices]
            for axis in axis_positions
        }
        self.upper_bounds = {
            axis: self.positions[axis]
            for axis in self.positions
        }
        self.lower_bounds = {
            axis: self.positions[axis]
            for axis in self.positions
        }
        for axis in axis_bounds_lower:
            self.upper_bounds[axis] = axis_bounds_upper[axis][self.indices]
            self.lower_bounds[axis] = axis_bounds_lower[axis][self.indices]
        self._prepared = True
    def apply_excluder(self, excluder):
        """Apply an excluder with axes matching some axes in the dimension to
        produce an internal mask"""
        if self._prepared:
            raise ValueError("Can not apply excluders after"
                             "prepare has been called")
        # find generators referenced by excluder
        matched_gens = [
            g for g in self.generators
            if len(set(g.axes) & set(excluder.axes)) != 0
        ]
        if len(matched_gens) == 0:
            raise ValueError(
                "Excluder references axes not present in dimension : %s" %
                str(excluder.axes))
        g_start = self.generators.index(matched_gens[0])
        g_end = self.generators.index(matched_gens[-1])
        point_arrays = {
            axis:
            [g for g in matched_gens if axis in g.axes][0].positions[axis]
            for axis in excluder.axes
        }

        if self.alternate:
            for axis in point_arrays.keys():
                arr = point_arrays[axis]
                point_arrays[axis] = np.append(arr, arr[::-1])

        # scale up all point arrays using generators within the range
        # inner generators are tiled by the size of outer generators
        # outer generators have points repeated by the size of inner ones
        axes_tiling = {axis: 1 for axis in excluder.axes}
        axes_repeats = {axis: 1 for axis in excluder.axes}
        axes_seen = []
        axes_to_see = [axis for axis in excluder.axes]
        for g in self.generators[g_start:g_end + 1]:
            found_axes = [axis for axis in g.axes if axis in excluder.axes]
            axes_to_see = [
                axis for axis in axes_to_see if axis not in found_axes
            ]
            for axis in axes_to_see:
                axes_tiling[axis] *= g.size
            for axis in axes_seen:
                axes_repeats[axis] *= g.size
            axes_seen.extend(found_axes)
        for axis in point_arrays.keys():
            arr = point_arrays[axis]
            point_arrays[axis] = np.tile(np.repeat(arr, axes_repeats[axis]),
                                         axes_tiling[axis])

        arrays = [point_arrays[axis] for axis in excluder.axes]
        excluder_mask = excluder.create_mask(*arrays)

        # record the tiling/repeat information for generators outside the axis range
        tile = 0.5 if self.alternate else 1
        repeat = 1
        for g in self.generators[0:g_start]:
            tile *= g.size
        for g in self.generators[g_end + 1:]:
            repeat *= g.size

        m = {"repeat": repeat, "tile": tile, "mask": excluder_mask}
        self._masks.append(m)
Example #16
0
    def prepare(self):
        self.num = 1
        self.dimensions = []
        # we're going to mutate these structures
        excluders = list(self.excluders)
        generators = list(self.generators)

        # special case if we have rectangular regions on line generators
        # we should restrict the resulting grid rather than merge dimensions
        # this changes the alternating case a little (without doing this, we
        # may have started in reverse direction)
        for rect in [r for r in excluders \
                if isinstance(r.roi, RectangularROI) and r.roi.angle == 0]:
            axis_1, axis_2 = rect.scannables[0], rect.scannables[1]
            gen_1 = [g for g in generators if axis_1 in g.axes][0]
            gen_2 = [g for g in generators if axis_2 in g.axes][0]
            if gen_1 is gen_2:
                continue
            if isinstance(gen_1, LineGenerator) \
                    and isinstance(gen_2, LineGenerator):
                gen_1.produce_points()
                gen_2.produce_points()
                valid = np.full(gen_1.num, True, dtype=np.int8)
                valid &= gen_1.points[
                    axis_1] <= rect.roi.width + rect.roi.start[0]
                valid &= gen_1.points[axis_1] >= rect.roi.start[0]
                points_1 = gen_1.points[axis_1][valid.astype(np.bool)]
                valid = np.full(gen_2.num, True, dtype=np.int8)
                valid &= gen_2.points[
                    axis_2] <= rect.roi.height + rect.roi.start[1]
                valid &= gen_2.points[axis_2] >= rect.roi.start[1]
                points_2 = gen_2.points[axis_2][valid.astype(np.bool)]
                new_gen1 = LineGenerator(gen_1.name, gen_1.units, points_1[0],
                                         points_1[-1], len(points_1),
                                         gen_1.alternate_direction)
                new_gen2 = LineGenerator(gen_2.name, gen_2.units, points_2[0],
                                         points_2[-1], len(points_2),
                                         gen_2.alternate_direction)
                generators[generators.index(gen_1)] = new_gen1
                generators[generators.index(gen_2)] = new_gen2
                excluders.remove(rect)

        for generator in generators:
            generator.produce_points()
            self.axes_points.update(generator.points)
            self.axes_points_lower.update(generator.points_lower)
            self.axes_points_upper.update(generator.points_upper)
            self.num *= generator.num

            dim = {
                "size": generator.num,
                "axes": list(generator.axes),
                "generators": [generator],
                "masks": [],
                "tile": 1,
                "repeat": 1,
                "alternate": generator.alternate_direction
            }
            self.dimensions.append(dim)

        for excluder in excluders:
            axis_1, axis_2 = excluder.scannables
            # ensure axis_1 is "outer" axis (if separate generators)
            gen_1 = [g for g in generators if axis_1 in g.axes][0]
            gen_2 = [g for g in generators if axis_2 in g.axes][0]
            gen_diff = generators.index(gen_1) \
                - generators.index(gen_2)
            if gen_diff < -1 or gen_diff > 1:
                raise ValueError(
                    "Excluders must be defined on axes that are adjacent in " \
                        "generator order")
            if gen_diff == 1:
                gen_1, gen_2 = gen_2, gen_1
                axis_1, axis_2 = axis_2, axis_1
                gen_diff = -1

            #####
            # first check if region spans two dimensions - merge if so
            #####
            dim_1 = [i for i in self.dimensions if axis_1 in i["axes"]][0]
            dim_2 = [i for i in self.dimensions if axis_2 in i["axes"]][0]
            dim_diff = self.dimensions.index(dim_1) \
                - self.dimensions.index(dim_2)
            if dim_diff < -1 or dim_diff > 1:
                raise ValueError(
                    "Excluders must be defined on axes that are adjacent in " \
                        "generator order")
            if dim_diff == 1:
                dim_1, dim_2 = dim_2, dim_1
                dim_diff = -1
            if dim_1["alternate"] != dim_2["alternate"] \
                    and dim_1 is not self.dimensions[0]:
                raise ValueError(
                    "Generators tied by regions must have the same " \
                            "alternate_direction setting")
            # merge "inner" into "outer"
            if dim_diff == -1:
                # dim_1 is "outer" - preserves axis ordering

                # need to appropriately scale the existing masks
                # masks are "tiled" by the size of generators "below" them
                # and their elements are "repeated" by the size of generators
                # above them, so:
                # |mask| * duplicates * repeates == |generators in index|
                scale = 1
                for g in dim_2["generators"]:
                    scale *= g.num
                for m in dim_1["masks"]:
                    m["repeat"] *= scale
                scale = 1
                for g in dim_1["generators"]:
                    scale *= g.num
                for m in dim_2["masks"]:
                    m["tile"] *= scale
                dim_1["masks"] += dim_2["masks"]
                dim_1["axes"] += dim_2["axes"]
                dim_1["generators"] += dim_2["generators"]
                dim_1["size"] *= dim_2["size"]
                dim_1["alternate"] |= dim_2["alternate"]
                self.dimensions.remove(dim_2)
            dim = dim_1

            #####
            # generate the mask for this region
            #####
            # if gen_1 and gen_2 are different then the outer axis will have to
            # have its elements repeated and the inner axis will have to have
            # itself repeated - gen_1 is always inner axis

            points_1 = self.axes_points[axis_1]
            points_2 = self.axes_points[axis_2]

            doubled_mask = False  # used for some cases of alternating generators

            if gen_1 is gen_2 and dim["alternate"]:
                # run *both* axes backwards
                # but our mask will be a factor of 2 too big
                doubled_mask = True
                points_1 = np.append(points_1, points_1[::-1])
                points_2 = np.append(points_2, points_2[::-1])
            elif dim["alternate"]:
                doubled_mask = True
                points_1 = np.append(points_1, points_1[::-1])
                points_2 = np.append(points_2, points_2[::-1])
                points_2 = np.tile(points_2, gen_1.num)
                points_1 = np.repeat(points_1, gen_2.num)
            elif gen_1 is not gen_2:
                points_1 = np.repeat(points_1, gen_2.num)
                points_2 = np.tile(points_2, gen_1.num)
            else:
                # copy the points arrays anyway so the regions can
                # safely perform any array operations in place
                # this is advantageous in the cases above
                points_1 = np.copy(points_1)
                points_2 = np.copy(points_2)

            if axis_1 == excluder.scannables[0]:
                mask = excluder.create_mask(points_1, points_2)
            else:
                mask = excluder.create_mask(points_2, points_1)

            #####
            # Add new mask to index
            #####
            tile = 0.5 if doubled_mask else 1
            repeat = 1
            found_axis = False
            # tile by product of generators "before"
            # repeat by product of generators "after"
            for g in dim["generators"]:
                if axis_1 in g.axes or axis_2 in g.axes:
                    found_axis = True
                else:
                    if found_axis:
                        repeat *= g.num
                    else:
                        tile *= g.num
            m = {"repeat": repeat, "tile": tile, "mask": mask}
            dim["masks"].append(m)
        # end for excluder in excluders
        #####

        tile = 1
        repeat = 1
        #####
        # Generate full index mask and "apply"
        #####
        for dim in self.dimensions:
            mask = np.full(dim["size"], True, dtype=np.int8)
            for m in dim["masks"]:
                assert len(m["mask"]) * m["repeat"] * m["tile"] == len(mask), \
                        "Mask lengths are not consistent"
                expanded = np.repeat(m["mask"], m["repeat"])
                if m["tile"] % 1 != 0:
                    ex = np.tile(expanded, int(m["tile"]))
                    expanded = np.append(ex, expanded[:len(expanded) // 2])
                else:
                    expanded = np.tile(expanded, int(m["tile"]))
                mask &= expanded
            dim["mask"] = mask
            dim["indicies"] = np.nonzero(mask)[0]
            if len(dim["indicies"]) == 0:
                raise ValueError("Regions would exclude entire scan")
            repeat *= len(dim["indicies"])
        self.num = repeat
        for dim in self.dimensions:
            l = len(dim["indicies"])
            repeat /= l
            dim["tile"] = tile
            dim["repeat"] = repeat
            tile *= l

        for dim in self.dimensions:
            tile = 1
            repeat = 1
            for g in dim["generators"]:
                repeat *= g.num
            for g in dim["generators"]:
                repeat /= g.num
                d = {"tile": tile, "repeat": repeat}
                tile *= g.num
                self.generator_dim_scaling[g] = d
    def prepare(self):
        self.num = 1
        self.dimensions = []
        # we're going to mutate these structures
        excluders = list(self.excluders)
        generators = list(self.generators)

        # special case if we have rectangular regions on line generators
        # we should restrict the resulting grid rather than merge dimensions
        # this changes the alternating case a little (without doing this, we
        # may have started in reverse direction)
        for rect in [r for r in excluders \
                if isinstance(r.roi, RectangularROI) and r.roi.angle == 0]:
            axis_1, axis_2 = rect.scannables[0], rect.scannables[1]
            gen_1 = [g for g in generators if axis_1 in g.axes][0]
            gen_2 = [g for g in generators if axis_2 in g.axes][0]
            if gen_1 is gen_2:
                continue
            if isinstance(gen_1, LineGenerator) \
                    and isinstance(gen_2, LineGenerator):
                gen_1.produce_points()
                gen_2.produce_points()
                valid = np.full(gen_1.num, True, dtype=np.int8)
                valid &= gen_1.points[axis_1] <= rect.roi.width + rect.roi.start[0]
                valid &= gen_1.points[axis_1] >= rect.roi.start[0]
                points_1 = gen_1.points[axis_1][valid.astype(np.bool)]
                valid = np.full(gen_2.num, True, dtype=np.int8)
                valid &= gen_2.points[axis_2] <= rect.roi.height + rect.roi.start[1]
                valid &= gen_2.points[axis_2] >= rect.roi.start[1]
                points_2 = gen_2.points[axis_2][valid.astype(np.bool)]
                new_gen1 = LineGenerator(
                    gen_1.name, gen_1.units, points_1[0], points_1[-1],
                    len(points_1), gen_1.alternate_direction)
                new_gen2 = LineGenerator(
                    gen_2.name, gen_2.units, points_2[0], points_2[-1],
                    len(points_2), gen_2.alternate_direction)
                generators[generators.index(gen_1)] = new_gen1
                generators[generators.index(gen_2)] = new_gen2
                excluders.remove(rect)

        for generator in generators:
            generator.produce_points()
            self.axes_points.update(generator.points)
            self.axes_points_lower.update(generator.points_lower)
            self.axes_points_upper.update(generator.points_upper)
            self.num *= generator.num

            dim = {"size":generator.num,
                "axes":list(generator.axes),
                "generators":[generator],
                "masks":[],
                "tile":1,
                "repeat":1,
                "alternate":generator.alternate_direction}
            self.dimensions.append(dim)

        for excluder in excluders:
            axis_1, axis_2 = excluder.scannables
            # ensure axis_1 is "outer" axis (if separate generators)
            gen_1 = [g for g in generators if axis_1 in g.axes][0]
            gen_2 = [g for g in generators if axis_2 in g.axes][0]
            gen_diff = generators.index(gen_1) \
                - generators.index(gen_2)
            if gen_diff < -1 or gen_diff > 1:
                raise ValueError(
                    "Excluders must be defined on axes that are adjacent in " \
                        "generator order")
            if gen_diff == 1:
                gen_1, gen_2 = gen_2, gen_1
                axis_1, axis_2 = axis_2, axis_1
                gen_diff = -1


            #####
            # first check if region spans two dimensions - merge if so
            #####
            dim_1 = [i for i in self.dimensions if axis_1 in i["axes"]][0]
            dim_2 = [i for i in self.dimensions if axis_2 in i["axes"]][0]
            dim_diff = self.dimensions.index(dim_1) \
                - self.dimensions.index(dim_2)
            if dim_diff < -1 or dim_diff > 1:
                raise ValueError(
                    "Excluders must be defined on axes that are adjacent in " \
                        "generator order")
            if dim_diff == 1:
                dim_1, dim_2 = dim_2, dim_1
                dim_diff = -1
            if dim_1["alternate"] != dim_2["alternate"] \
                    and dim_1 is not self.dimensions[0]:
                raise ValueError(
                    "Generators tied by regions must have the same " \
                            "alternate_direction setting")
            # merge "inner" into "outer"
            if dim_diff == -1:
                # dim_1 is "outer" - preserves axis ordering

                # need to appropriately scale the existing masks
                # masks are "tiled" by the size of generators "below" them
                # and their elements are "repeated" by the size of generators
                # above them, so:
                # |mask| * duplicates * repeates == |generators in index|
                scale = 1
                for g in dim_2["generators"]:
                    scale *= g.num
                for m in dim_1["masks"]:
                    m["repeat"] *= scale
                scale = 1
                for g in dim_1["generators"]:
                    scale *= g.num
                for m in dim_2["masks"]:
                    m["tile"] *= scale
                dim_1["masks"] += dim_2["masks"]
                dim_1["axes"] += dim_2["axes"]
                dim_1["generators"] += dim_2["generators"]
                dim_1["size"] *= dim_2["size"]
                dim_1["alternate"] |= dim_2["alternate"]
                self.dimensions.remove(dim_2)
            dim = dim_1

            #####
            # generate the mask for this region
            #####
            # if gen_1 and gen_2 are different then the outer axis will have to
            # have its elements repeated and the inner axis will have to have
            # itself repeated - gen_1 is always inner axis

            points_1 = self.axes_points[axis_1]
            points_2 = self.axes_points[axis_2]

            doubled_mask = False # used for some cases of alternating generators

            if gen_1 is gen_2 and dim["alternate"]:
                # run *both* axes backwards
                # but our mask will be a factor of 2 too big
                doubled_mask = True
                points_1 = np.append(points_1, points_1[::-1])
                points_2 = np.append(points_2, points_2[::-1])
            elif dim["alternate"]:
                doubled_mask = True
                points_1 = np.append(points_1, points_1[::-1])
                points_2 = np.append(points_2, points_2[::-1])
                points_2 = np.tile(points_2, gen_1.num)
                points_1 = np.repeat(points_1, gen_2.num)
            elif gen_1 is not gen_2:
                points_1 = np.repeat(points_1, gen_2.num)
                points_2 = np.tile(points_2, gen_1.num)
            else:
                # copy the points arrays anyway so the regions can
                # safely perform any array operations in place
                # this is advantageous in the cases above
                points_1 = np.copy(points_1)
                points_2 = np.copy(points_2)


            if axis_1 == excluder.scannables[0]:
                mask = excluder.create_mask(points_1, points_2)
            else:
                mask = excluder.create_mask(points_2, points_1)

            #####
            # Add new mask to index
            #####
            tile = 0.5 if doubled_mask else 1
            repeat = 1
            found_axis = False
            # tile by product of generators "before"
            # repeat by product of generators "after"
            for g in dim["generators"]:
                if axis_1 in g.axes or axis_2 in g.axes:
                    found_axis = True
                else:
                    if found_axis:
                        repeat *= g.num
                    else:
                        tile *= g.num
            m = {"repeat":repeat, "tile":tile, "mask":mask}
            dim["masks"].append(m)
        # end for excluder in excluders
        #####

        tile = 1
        repeat = 1
        #####
        # Generate full index mask and "apply"
        #####
        for dim in self.dimensions:
            mask = np.full(dim["size"], True, dtype=np.int8)
            for m in dim["masks"]:
                assert len(m["mask"]) * m["repeat"] * m["tile"] == len(mask), \
                        "Mask lengths are not consistent"
                expanded = np.repeat(m["mask"], m["repeat"])
                if m["tile"] % 1 != 0:
                    ex = np.tile(expanded, int(m["tile"]))
                    expanded = np.append(ex, expanded[:len(expanded)//2])
                else:
                    expanded = np.tile(expanded, int(m["tile"]))
                mask &= expanded
            dim["mask"] = mask
            dim["indicies"] = np.nonzero(mask)[0]
            if len(dim["indicies"]) == 0:
                raise ValueError("Regions would exclude entire scan")
            repeat *= len(dim["indicies"])
        self.num = repeat
        for dim in self.dimensions:
            l = len(dim["indicies"])
            repeat /= l
            dim["tile"] = tile
            dim["repeat"] = repeat
            tile *= l

        for dim in self.dimensions:
            tile = 1
            repeat = 1
            for g in dim["generators"]:
                repeat *= g.num
            for g in dim["generators"]:
                repeat /= g.num
                d = {"tile":tile, "repeat":repeat}
                tile *= g.num
                self.generator_dim_scaling[g] = d