예제 #1
0
    def test_crop_float_unit_convertion_signal2D(self):
        # Should convert the unit to nm
        d = np.arange(512 * 512).reshape(512, 512)
        s = Signal2D(d)
        s.axes_manager[0].name = "x"
        s.axes_manager[0].scale = 0.01
        s.axes_manager[0].units = "µm"
        s.axes_manager[1].name = "y"
        s.axes_manager[1].scale = 0.01
        s.axes_manager[1].units = "µm"
        s.crop(0, 0.0, 0.5, convert_units=True)  # also convert the other axis
        s.crop(1, 0.0, 500.0, convert_units=True)
        np.testing.assert_almost_equal(s.axes_manager[0].scale, 10.0)
        np.testing.assert_almost_equal(s.axes_manager[1].scale, 10.0)
        assert s.axes_manager[0].units == "nm"
        assert s.axes_manager[1].units == "nm"
        np.testing.assert_allclose(s.data, d[:50, :50])

        # Should keep the unit to µm
        d = np.arange(512 * 512).reshape(512, 512)
        s = Signal2D(d)
        s.axes_manager[0].name = "x"
        s.axes_manager[0].scale = 0.01
        s.axes_manager[0].units = "µm"
        s.axes_manager[1].name = "y"
        s.axes_manager[1].scale = 0.01
        s.axes_manager[1].units = "µm"
        s.crop(0, 0.0, 5.0, convert_units=True)
        s.crop(1, 0.0, 5.0, convert_units=True)
        np.testing.assert_almost_equal(s.axes_manager[0].scale, 0.01)
        np.testing.assert_almost_equal(s.axes_manager[1].scale, 0.01)
        assert s.axes_manager[0].units == "µm"
        assert s.axes_manager[1].units == "µm"
        np.testing.assert_allclose(s.data, d[:500, :500])
예제 #2
0
    def __init__(self, *args, **kwargs):
        """Basic unit of data in the program.

        Spectrums can be any dimension of array but all of the data should be of the
        same type from the same area... (Maybe define this better later... You can use it kind of however you want though)
        Extends the Signal2D Class from hyperspy so there is that added functionality

        Parameters
        ----------
        data : numpy array
           The signal data. It can be an array of any dimensions.
        axes : dictionary (optional)
            Dictionary to define the axes (see the
            documentation of the AxesManager class for more details).
        attributes : dictionary (optional)
            A dictionary whose items are stored as attributes.
        metadata : dictionary (optional)
            A dictionary containing a set of parameters
            that will to stores in the `metadata` attribute.
            Some parameters might be mandatory in some cases.
        original_metadata : dictionary (optional)
            A dictionary containing a set of parameters
            that will to stores in the `original_metadata` attribute. It
            typically contains all the parameters that has been
            imported from the original data file.
        Notes: For more parameters see hyperspy's Signal2D Class
        """
        Signal2D.__init__(self, *args, **kwargs)
        self.manav = MaskSlicer(self, isNavigation=True)
        self.masig = MaskSlicer(self, isNavigation=False)
        self.mask_passer= None
예제 #3
0
 def test_input_signal_and_original_image_wrong_dim(self):
     s = Signal2D(np.zeros((100, 50)))
     s_orig = Signal2D(np.zeros((100, 50, 9)))
     with pytest.raises(ValueError):
         Sublattice(atom_position_list=self.peaks,
                    image=s,
                    original_image=s_orig)
예제 #4
0
class TestCorrelations:
    @pytest.fixture
    def flat_pattern(self):
        pd = PolarDiffraction2D(data=np.ones(shape=(2, 2, 5, 5)))
        pd.axes_manager.signal_axes[0].scale = 0.5
        pd.axes_manager.signal_axes[0].name = "theta"
        pd.axes_manager.signal_axes[1].scale = 2
        pd.axes_manager.signal_axes[1].name = "k"
        return pd

    def test_correlation_signal(self, flat_pattern):
        ac = flat_pattern.get_angular_correlation()
        assert isinstance(ac, Correlation2D)

    def test_axes_transfer(self, flat_pattern):
        ac = flat_pattern.get_angular_correlation()
        assert (ac.axes_manager.signal_axes[0].scale ==
                flat_pattern.axes_manager.signal_axes[0].scale)
        assert (ac.axes_manager.signal_axes[1].scale ==
                flat_pattern.axes_manager.signal_axes[1].scale)
        assert (ac.axes_manager.signal_axes[1].name ==
                flat_pattern.axes_manager.signal_axes[1].name)

    @pytest.mark.parametrize(
        "mask",
        [None,
         np.zeros(shape=(5, 5)),
         Signal2D(np.zeros(shape=(2, 2, 5, 5)))])
    def test_masking_correlation(self, flat_pattern, mask):
        ap = flat_pattern.get_angular_correlation(mask=mask)
        assert isinstance(ap, Correlation2D)

    def test_correlation_inplace(self, flat_pattern):
        ac = flat_pattern.get_angular_correlation(inplace=True)
        assert ac is None
        assert isinstance(flat_pattern, Correlation2D)

    @pytest.mark.parametrize(
        "mask",
        [None,
         np.zeros(shape=(5, 5)),
         Signal2D(np.zeros(shape=(2, 2, 5, 5)))])
    def test_masking_angular_power(self, flat_pattern, mask):
        ap = flat_pattern.get_angular_power(mask=mask)
        print(ap)
        assert isinstance(ap, Power2D)

    def test_axes_transfer_power(self, flat_pattern):
        ac = flat_pattern.get_angular_power()
        assert ac.axes_manager.signal_axes[0].scale == 1
        assert (ac.axes_manager.signal_axes[1].scale ==
                flat_pattern.axes_manager.signal_axes[1].scale)
        assert (ac.axes_manager.signal_axes[1].name ==
                flat_pattern.axes_manager.signal_axes[1].name)

    def test_power_inplace(self, flat_pattern):
        ac = flat_pattern.get_angular_power(inplace=True)
        assert ac is None
        assert isinstance(flat_pattern, Power2D)
예제 #5
0
 def test_copy_all_axes_manager_wrong_shape(self):
     s0 = Signal2D(np.zeros((5, 40, 50)))
     s1 = Signal2D(np.zeros((6, 40, 50)))
     s2 = Signal2D(np.zeros((2, 5, 40, 50)))
     with pytest.raises(ValueError):
         pst._copy_signal_all_axes_metadata(s0, s1)
     with pytest.raises(ValueError):
         pst._copy_signal_all_axes_metadata(s0, s2)
예제 #6
0
파일: tools.py 프로젝트: shellystem/TEMUL
def array2signal2d(numpy_array, scale=1.0, rotate_flip=False):
    if rotate_flip:
        signal = Signal2D(np.rot90(np.fliplr(numpy_array)))
    else:
        signal = Signal2D(numpy_array)
    signal.axes_manager[-1].scale = scale
    signal.axes_manager[-2].scale = scale
    return signal
예제 #7
0
 def __init__(self, *args, **kwargs):
     Signal2D.__init__(self, *args, **kwargs)
     # Set default attributes
     if 'Acquisition_instrument' in self.metadata.as_dictionary():
         if 'SEM' in self.metadata.as_dictionary()['Acquisition_instrument']:
             self.metadata.set_item(
                 "Acquisition_instrument.TEM",
                 self.metadata.Acquisition_instrument.SEM)
             del self.metadata.Acquisition_instrument.SEM
     self.decomposition.__func__.__doc__ = BaseSignal.decomposition.__doc__
예제 #8
0
 def test_wrong_dimensions(self):
     s1 = Signal1D(np.random.random(200))
     s3 = Signal2D(np.random.random((4, 200, 200)))
     s4 = Signal2D(np.random.random((3, 4, 200, 200)))
     with pytest.raises(ValueError):
         afr.get_atom_positions(s1, 2)
     with pytest.raises(ValueError):
         afr.get_atom_positions(s3, 2)
     with pytest.raises(ValueError):
         afr.get_atom_positions(s4, 2)
예제 #9
0
 def __init__(self, *args, **kwargs):
     Signal2D.__init__(self, *args, **kwargs)
     # Attributes defaults
     if 'Acquisition_instrument.TEM' not in self.metadata:
         if 'Acquisition_instrument.SEM' in self.metadata:
             self.metadata.set_item(
                 "Acquisition_instrument.TEM",
                 self.metadata.Acquisition_instrument.SEM)
             del self.metadata.Acquisition_instrument.SEM
     self.decomposition.__func__.__doc__ = BaseSignal.decomposition.__doc__
예제 #10
0
 def __init__(self, *args, **kwargs):
     """
     If a hyperspy Signal2D is given, it is transformed into a ModifiedImage.
     In that case, additional *args and **kwargs are discarded.
     """
     if len(args) > 0 and isinstance(args[0], Image):
         # Pretend it is a hs signal, copy axes and metadata
         sdict = args[0]._to_dictionary()
         Image.__init__(self, **sdict)
     else:
         Image.__init__(self, *args, **kwargs)
예제 #11
0
    def get_diffraction_variance(self, dqe, set_data_type=None):
        """Calculates the variance in scattered intensity as a function of
        scattering vector.

        Parameters
        ----------

        dqe : float
            Detective quantum efficiency of the detector for Poisson noise
            correction.
        data_type : numpy data type.
            For numpy data types, see
            https://docs.scipy.org/doc/numpy-1.13.0/user/basics.types.html.
            This is incorporated as squaring the numbers in meansq_dp results
            in considerably larger than the ones in the original array. This can
            result in an overflow error that is difficult to distinguish. Hence
            the data can be converted to a different data type to accommodate.



        Returns
        -------

        vardps : DiffractionVariance2D
            A DiffractionVariance2D object containing the mean DP, mean
            squared DP, and variance DP.
        """

        dp = self.signal
        mean_dp = dp.mean((0, 1))
        if set_data_type is None:
            meansq_dp = Signal2D(np.square(dp.data)).mean((0, 1))
        else:
            meansq_dp = Signal2D(np.square(
                dp.data.astype(set_data_type))).mean((0, 1))

        normvar = (meansq_dp.data / np.square(mean_dp.data)) - 1.
        var_dp = Signal2D(normvar)
        corr_var_array = var_dp.data - (np.divide(dqe, mean_dp.data))
        corr_var_array[np.isinf(corr_var_array)] = 0
        corr_var_array[np.isnan(corr_var_array)] = 0
        corr_var = Signal2D(corr_var_array)
        vardps = stack((mean_dp, meansq_dp, var_dp, corr_var))
        sig_x = vardps.data.shape[1]
        sig_y = vardps.data.shape[2]

        dv = DiffractionVariance2D(vardps.data.reshape((2, 2, sig_x, sig_y)))

        dv = transfer_signal_axes(dv, self.signal)

        return dv
예제 #12
0
 def setup_method(self, method):
     # Create test image 100x100 pixels:
     im = Signal2D(np.arange(50000).reshape([10, 50, 100]))
     im.axes_manager[0].scale = 1e-1
     im.axes_manager[1].scale = 1e-2
     im.axes_manager[2].scale = 1e-3
     self.im = im
예제 #13
0
    def test_model2D_linear_many_gaussians(self, nav2d):
        mesh, x, y = self.mesh, self.x, self.y
        gausslow, gausshigh = -8, 8
        gauss_step = 8
        X, Y = mesh
        z = np.zeros(X.shape)
        g = Gaussian2D()
        for i in np.arange(gausslow, gausshigh + 1, gauss_step):
            for j in np.arange(gausslow, gausshigh + 1, gauss_step):
                g.centre_x.value = i
                g.centre_y.value = j
                g.A.value = 10
                z += g.function(X, Y)

        s = Signal2D(z)
        s.axes_manager[-2].offset = x[0]
        s.axes_manager[-1].offset = y[0]

        s.axes_manager[-2].scale = x[1] - x[0]
        s.axes_manager[-1].scale = y[1] - y[0]

        if nav2d:
            s = hs.stack([s] * 2)
            s = hs.stack([s] * 3)

        m = s.create_model()
        for i in np.arange(gausslow, gausshigh + 1, gauss_step):
            for j in np.arange(gausslow, gausshigh + 1, gauss_step):
                g = Gaussian2D(centre_x=i, centre_y=j)
                g.set_parameters_not_free()
                g.A.free = True
                m.append(g)

        m.fit(optimizer='lstsq')
        np.testing.assert_allclose(s.data, m.as_signal().data)
예제 #14
0
def library(diffraction_pattern):
    dp = diffraction_pattern.mean((0, 1))
    im = Signal2D(np.ones((10, 10)))
    cdl = CalibrationDataLibrary(
        au_x_grating_dp=dp, au_x_grating_im=im, moo3_dp=dp, moo3_im=im
    )
    return cdl
예제 #15
0
파일: segments.py 프로젝트: ptim0626/pyxem
    def get_ncc_matrix(self):
        """Get the normalised correlation coefficient (NCC) matrix containing
        the NCC between each pair of segments.

        Returns
        -------
        ncc_matrix : Signal2D
            Normalised correlation coefficient matrix for loadings and factors.
        """
        # Set up empty matrices of correct size to store NCC values.
        num_comp = np.shape(self.loadings.data)[0]
        ncc_loadings = np.zeros((num_comp, num_comp))
        ncc_factors = np.zeros((num_comp, num_comp))
        factors = self.factors.map(np.nan_to_num, inplace=False).copy()
        loadings = self.loadings.map(np.nan_to_num, inplace=False).copy()
        # Iterate through loadings calculating NCC values.
        for i in np.arange(num_comp):
            ncc_loadings[i] = list(
                map(
                    lambda x: norm_cross_corr(x, template=loadings.data[i]),
                    loadings.data,
                ))
        # Iterate through factors calculating NCC values.
        for i in np.arange(num_comp):
            ncc_factors[i] = list(
                map(lambda x: norm_cross_corr(x, template=factors.data[i]),
                    factors.data))
        # Convert matrix to Signal2D and set axes
        ncc_sig = Signal2D(np.array((ncc_loadings, ncc_factors)))
        ncc_sig.axes_manager.signal_axes[0].name = "index"
        ncc_sig.axes_manager.signal_axes[1].name = "index"
        ncc_sig.metadata.General.title = "Normalised Correlation Coefficient"

        return ncc_sig
예제 #16
0
파일: segments.py 프로젝트: ptim0626/pyxem
    def get_ncc_matrix(self):
        """Get the normalised correlation coefficient (NCC) matrix containing
        the NCC between each pair of segments.

        Returns
        -------
        ncc_matrix : Signal2D
            Normalised correlation coefficient matrix.
        """
        # TODO: This code should be factored out for reuse in other ncc method.
        # Set up an empty matrix of correct size to store NCC values.
        num_comp = np.shape(self.segments.data)[0]
        ncc_matrix = np.zeros((num_comp, num_comp))
        # Iterate through segments calculating NCC values.
        for i in np.arange(num_comp):
            ncc_matrix[i] = list(
                map(
                    lambda x: norm_cross_corr(x,
                                              template=self.segments.data[i]),
                    self.segments.data,
                ))
        # Convert matrix to Signal2D and set axes
        ncc_sig = Signal2D(ncc_matrix)
        ncc_sig.axes_manager.signal_axes[0].name = "segment index"
        ncc_sig.axes_manager.signal_axes[1].name = "segment index"
        ncc_sig.metadata.General.title = "Normalised Correlation Coefficient"
        return ncc_sig
예제 #17
0
def get_atomic_resolution_tem_signal2d():
    """Get an artificial atomic resolution TEM Signal2D.

    Returns
    -------
    :py:class:`~hyperspy._signals.signal2d.Signal2D`

    Example
    -------
    >>> s = hs.datasets.artificial_data.get_atomic_resolution_tem_signal2d()
    >>> s.plot()

    """
    from hyperspy.signals import Signal2D
    from hyperspy import components2d

    sX, sY = 2, 2
    x_array, y_array = np.mgrid[0:200, 0:200]
    image = np.zeros_like(x_array, dtype=np.float32)
    gaussian2d = components2d.Gaussian2D(sigma_x=sX, sigma_y=sY)
    for x in range(10, 195, 20):
        for y in range(10, 195, 20):
            gaussian2d.centre_x.value = x
            gaussian2d.centre_y.value = y
            image += gaussian2d.function(x_array, y_array)

    s = Signal2D(image)
    return s
예제 #18
0
    def test_0d(self):
        data_shape = (10, 8)
        data = np.ones(data_shape) * 100.0
        s = Signal2D(data)
        mask0 = pst._get_angle_sector_mask(s, angle0=0.0, angle1=np.pi / 2)
        np.testing.assert_array_equal(mask0, np.zeros_like(mask0,
                                                           dtype=np.bool))
        assert mask0.shape == data_shape

        s.axes_manager.signal_axes[0].offset = -5
        s.axes_manager.signal_axes[1].offset = -5
        mask1 = pst._get_angle_sector_mask(s, angle0=0.0, angle1=np.pi / 2)
        assert mask1[:5, :5].all()
        mask1[:5, :5] = False
        np.testing.assert_array_equal(mask1, np.zeros_like(mask1,
                                                           dtype=np.bool))

        s.axes_manager.signal_axes[0].offset = -15
        s.axes_manager.signal_axes[1].offset = -15
        mask2 = pst._get_angle_sector_mask(s, angle0=0.0, angle1=np.pi / 2)
        assert mask2.all()

        s.axes_manager.signal_axes[0].offset = -15
        s.axes_manager.signal_axes[1].offset = -15
        mask3 = pst._get_angle_sector_mask(s,
                                           angle0=np.pi * 3 / 2,
                                           angle1=np.pi * 2)
        assert not mask3.any()
 def test_simple(self):
     s = Signal2D(np.zeros((110, 200)))
     x, y, semi_len0, semi_len1, rotation = 60, 70, 12, 9, 0.2
     xx, yy = np.meshgrid(s.axes_manager[0].axis, s.axes_manager[1].axis)
     ellipse_image = mdtd._get_elliptical_ring(xx, yy, x, y, semi_len0,
                                               semi_len1, rotation, 3)
     assert ellipse_image.any()
예제 #20
0
    def plot(self, fig=None):
        """Plots the current marker in a flat image

        Parameters
        ----------
        fig : {Image, None}
            if an already plotted image, then updates. Otherwise creates a new
            one.

        Returns
        -------
        fig: Image
            the resulting image. If passed again, will be updated
            (computationally cheaper operation).
        """
        marker = self.samf.metadata.marker.copy()

        if marker.ndim > 2:
            marker = np.sum(
                marker, tuple([i for i in range(0, marker.ndim - 2)]))
        elif marker.ndim < 2:
            marker = np.atleast_2d(marker)

        from hyperspy.signals import Signal2D
        if not isinstance(
                fig, Signal2D) or fig._plot.signal_plot.figure is None:
            fig = Signal2D(marker)
            fig.plot()
            self.close_plot = fig._plot.signal_plot.close
        else:
            fig.data = marker
            fig._plot.signal_plot.update()
        return fig
예제 #21
0
def test_snapping_axis_values(snap):
    s = Signal2D(np.arange(100).reshape(10, 10))
    s.axes_manager[0].offset = 5

    r = roi.Line2DROI(x1=6, y1=0, x2=12, y2=4, linewidth=0)
    s.plot()
    _ = r.interactive(s, snap=snap)
예제 #22
0
def test_error_message():
    im = Signal2D(np.arange(50000).reshape([10, 50, 100]))
    im.plot()
    im._plot.close()
    p = roi.Point1DROI(0.5)
    with pytest.raises(Exception, match='does not have an active plot.'):
        p.add_widget(signal=im, axes=[0, ], color="cyan")
 def test_cover_no_signal(self):
     s = Signal2D(np.zeros((50, 56)))
     xx, yy = np.meshgrid(s.axes_manager[0].axis, s.axes_manager[1].axis)
     x, y, semi_len0, semi_len1, rotation = 523, 620, 20, 30, 2.0
     ellipse_image = mdtd._get_elliptical_disk(xx, yy, x, y, semi_len0,
                                               semi_len1, rotation)
     assert not ellipse_image.any()
 def test_simple(self):
     s = Signal2D(np.zeros((110, 200)))
     xx, yy = np.meshgrid(s.axes_manager[0].axis, s.axes_manager[1].axis)
     x, y, semi_len0, semi_len1, rotation = 60, 70, 12, 9, 0.2
     ellipse_image = mdtd._get_elliptical_disk(xx, yy, x, y, semi_len0,
                                               semi_len1, rotation)
     assert s.data.shape == ellipse_image.shape
예제 #25
0
    def get_direct_beam_mask(self, radius, center=None):
        """Generate a signal mask for the direct beam.

        Parameters
        ----------
        radius : float
            Radius for the circular mask in pixel units.
        center : tuple, optional
            User specified (x, y) position of the diffraction pattern center.
            i.e. the direct beam position. If None (default) it is assumed that
            the direct beam is at the center of the diffraction pattern.

        Return
        ------
        signal-mask : ndarray
            The mask of the direct beam
        """
        shape = self.axes_manager.signal_shape
        if center is None:
            center = (shape[1] - 1) / 2, (shape[0] - 1) / 2

        signal_mask = Signal2D(
            circular_mask(shape=shape, radius=radius, center=center))

        return signal_mask
예제 #26
0
 def test_get_one_string(self):
     s = Signal2D(np.zeros([3, 2, 2]))
     m = markers.text(x=list(range(3)), y=list(range(3)), text='one')
     m.axes_manager = s.axes_manager
     assert m.get_data_position('text') == 'one'
     s.axes_manager[0].index = 2
     assert m.get_data_position('text') == 'one'
예제 #27
0
 def setup_method(self, method):
     rng = np.random.RandomState(123)
     self.im = Signal2D(rng.random_sample(size=(2, 3, 4, 5)))
     self.im.axes_manager.signal_axes[0].units = "nm"
     self.im.axes_manager.signal_axes[1].units = "nm"
     self.im.axes_manager.signal_axes[0].scale = 10.0
     self.im.axes_manager.signal_axes[1].scale = 10.0
예제 #28
0
def _test_plot_rectange_markers():
    # Create test image 100x100 pixels:
    im = Signal2D(np.arange(100).reshape([10, 10]))

    # Add four line markers:
    m1 = markers.line_segment(
        x1=2, y1=2, x2=7, y2=2, color='red', linewidth=3)
    m2 = markers.line_segment(
        x1=2, y1=2, x2=2, y2=7, color='red', linewidth=3)
    m3 = markers.line_segment(
        x1=2, y1=7, x2=7, y2=7, color='red', linewidth=3)
    m4 = markers.line_segment(
        x1=7, y1=2, x2=7, y2=7, color='red', linewidth=3)

    # Add rectangle marker at same position:
    m = markers.rectangle(x1=2, x2=7, y1=2, y2=7,
                          linewidth=4, color='blue', ls='dotted')

    # Plot image and add markers to img:
    im.plot()
    im.add_marker(m)
    im.add_marker(m1)
    im.add_marker(m2)
    im.add_marker(m3)
    im.add_marker(m4)
    return im
예제 #29
0
 def test_changing_scale_offset(self):
     s = Signal2D(arange(100).reshape(10, 10))
     signal_axis0 = s.axes_manager.signal_axes[0]
     signal_axis1 = s.axes_manager.signal_axes[1]
     signal_extent = (
         signal_axis0.low_value,
         signal_axis0.high_value,
         signal_axis1.low_value,
         signal_axis1.high_value,
     )
     assert signal_extent == s.axes_manager.signal_extent
     signal_axis0.scale = 0.2
     signal_axis1.scale = 0.7
     signal_extent = (
         signal_axis0.low_value,
         signal_axis0.high_value,
         signal_axis1.low_value,
         signal_axis1.high_value,
     )
     assert signal_extent == s.axes_manager.signal_extent
     signal_axis0.offset = -11
     signal_axis1.scale = 23
     signal_extent = (
         signal_axis0.low_value,
         signal_axis0.high_value,
         signal_axis1.low_value,
         signal_axis1.high_value,
     )
     assert signal_extent == s.axes_manager.signal_extent
예제 #30
0
    def get_map(self, k_region=[3.0, 6.0], symmetry=None):
        """Creates a 2 dimensional map of from the power spectrum.

        Parameters
        ----------
        k_region: array-like
           upper and lower k values to integrate over, allows both ints and floats for indexing
        symmetry: int or array-like
            specific integers or list of symmetries to average over when creating the map of the correlations.
        Returns
        ----------
        symmetry_map: 2-d array
            2 dimensional map of from the power spectrum
        """
        if symmetry is None:
            sym_map = self.isig[:, k_region[0]:k_region[1]].sum(
                axis=[-1, -2]).transpose()

        elif isinstance(symmetry, int):
            sym_map = self.isig[symmetry, k_region[0]:k_region[1]].sum(
                axis=[-1]).transpose()

        else:
            sym_map = Signal2D(
                data=np.zeros(self.axes_manager.navigation_shape))
            for sym in symmetry:
                sym_map = self.isig[sym, k_region[0]:k_region[1]].sum(
                    axis=[-1]).transpose() + sym_map
        return sym_map