def num_modular_vf(self,
                       bivector,
                       function,
                       mesh,
                       pt_output=False,
                       tf_output=False,
                       dict_output=False):
        """
            Calculates the modular vector field Z of a given Poisson bivector field P relative to the volume form
            f*Omega_0 defined as Z(g):= div(X_g) where Omega_0 = dx1^...^dx('dim'), f a non zero function and div(X_g)
            is the divergence respect to Omega of the Hamiltonian vector field
            X_g of f relative to P.
            Clearly, the modular vector field is Omega-dependent. If h*Omega is another volume form, with h a nowhere
            vanishing function on M, then
                Z' = Z - (1/h)*X_h,
            is the modular vector field of P relative to h*Omega.

        Parameters
        ==========
        :bivector:
            Is a Poisson bivector in a dictionary format with tuple type 'keys' and string type 'values'.
        :function:
            Is a function scalar h: M --> R that is a non zero and is a string type.
        :mesh:
            Is a numpy array where each value is a list of float values that representa a point in R^{dim}.
        :pt_output/tf_output:
            Is a boolean flag to indicates if the result is given in a tensor from PyTorch/TensorFlow, its
            default value is False.

        Returns
        =======
            The default result is a NumPy array that contains all the evaluations of the modular vector field Z of a
            given Poisson bivector field P relative to the volume.
            This value can be converted to Tensor PyTorch, TensorFlow or dictionary for by setting their respective
            flag as True in the params.The result is a normal form in a dictionary format with integer type 'keys'
            and symbol type 'values'.

        Example
        ========
            >>> import numpy as np
            >>> from poisson import NumPoissonGeometry
            >>> # Instance the class to dimension 3
            >>> npg3 = NumericalPoissonGeometry(3)
            >>> # Defines a simple bivector
            >>> bivector = {e: f'1/2*(x1**2 + x2**2 + x3**2 + x4**2)' for e in tuple(itls.combinations(range(1, dim + 1), 2))}  # noqa
            >>> mesh = np.array([[1., 1., 1.,]])
            >>> # Evaluates the mesh into
            >>> npg3.num_modular_vf(bivector, 1, mesh)
            >>> [[[-2.]
                  [ 0.]
                  [ 2.]]]
            >>> npg3.num_modular_vf(bivector, 1, mesh, pt_output=True)
            >>> tf.Tensor(
                [[[-2.]
                  [ 0.]
                  [ 2.]]], shape=(1, 3, 1), dtype=float64)
            >>> npg3.num_modular_vf(bivector, 1, mesh, tf_output=True)
            >>> tensor([[[-2.],
                         [ 0.],
                         [ 2.]]], dtype=torch.float64)
            >>> npg3.num_modular_vf(bivector, 1, mesh, dict_output=True)
            >>> [{(1,): -2.0, (2,): 0.0, (3,): 2.0}]
        """
        # Evaluates the modluar vector field in each point from mesh and save the result in Numpy array
        modular_vf = self.pg.modular_vf(bivector, function)
        dict_list = dict_mesh_eval(modular_vf, mesh, self.pg.coords)
        np_result = dicts_to_matrices(dict_list, self.pg.dim)
        # return the result in a TensorFlow tensor if the flag is True
        if tf_output:
            # TODO apply lambdify method to tensorflow
            return tf.convert_to_tensor(np_result)
        # return the result in a PyTorch tensor if the flag is True
        if pt_output:
            return torch.from_numpy(np_result)
        # return the result in dictionary type
        if dict_output:
            return dict_list
        # return the result in Numpy array
        return np_result
    def num_flaschka_ratiu_bivector(self,
                                    casimirs_list,
                                    mesh,
                                    tf_output=False,
                                    pt_output=False,
                                    dict_output=False):
        """
            Calculate a Poisson bivector from Flaschka-Ratui formula where all Casimir function is in "casimir"
            variable. This Poisson bivector is the following form:
                i_π Ω := dK_1^...^dK_m-2
            where K_1, ..., K_m-2 are casimir functions and (M, Ω) is a diferentiable manifold with volument form Ω
            and dim(M)=m.

        Parameters
        ==========
        :casimirs_list:
            Is a list of Casimir functions where each element is a string type.
        :mesh:
            Is a numpy array where each value is a list of float values that representa a point in R^{dim}.
        :pt_output/tf_output:
            Is a boolean flag to indicates if the result is given in a tensor from PyTorch/TensorFlow, its
            default value is False.

        Returns
        =======
            The default result is a NumPy array that contains all the evaluations of lineal normal form from a
            bivector. This value can be converted to Tensor PyTorch, TensorFlow or dictionary for by setting their
            respective flag as True in the params.The result is a normal form in a dictionary format with integer
            type 'keys' and symbol type 'values'.

        Example
        ========
            >>> import numpy as np
            >>> from poisson import NumPoissonGeometry
            >>> # Instance the class to dimension 4
            >>> npg4 = NumPoissonGeometry(4)
            >>> # Defines Casimir functions
            >>> casimirs = ['x1**2 +x2**2 +x3**2', 'x4']
            >>> # Calculate a simple mesh
            >>> mesh = np.array([[1., 1., 1., 1.]])
            >>> # Evaluates the mesh into
            >>> npg4.num_flaschka_ratiu_bivector(bivector, mesh)
            >>> [[[ 0. -2.  2.  0.]
                  [ 2.  0. -2.  0.]
                  [-2.  2.  0.  0.]
                  [ 0.  0.  0.  0.]]]
            >>> npg4.num_flaschka_ratiu_bivector(bivector, mesh, pt_output=True)
            >>> tensor([[[ 0., -2.,  2.,  0.],
                         [ 2.,  0., -2.,  0.],
                         [-2.,  2.,  0.,  0.],
                         [ 0.,  0.,  0.,  0.]]], dtype=torch.float64)
            >>> npg4.num_flaschka_ratiu_bivector(bivector, mesh, tf_output=True)
            >>> tf.Tensor(
                [[[ 0. -2.  2.  0.]
                  [ 2.  0. -2.  0.]
                  [-2.  2.  0.  0.]
                  [ 0.  0.  0.  0.]]], shape=(1, 4, 4), dtype=float64)
            >>> npg4.num_flaschka_ratiu_bivector(bivector, mesh, dict_output=True)
            >>> [{(1, 2): -2.0, (1, 3): 2.0, (2, 3): -2.0}]
        """
        flaschka_ratiu_bivector = self.pg.flaschka_ratiu_bivector(
            casimirs_list)
        # Evaluates the linear normal form from a bivector in each point from mesh and save the result in Numpy array
        dict_list = dict_mesh_eval(flaschka_ratiu_bivector, mesh,
                                   self.pg.coords)
        np_result = dicts_to_matrices(dict_list, self.pg.dim)
        # return the result in a PyTorch tensor if the flag is True
        if pt_output:
            return torch.from_numpy(np_result)
        # return the result in a TensorFlow tensor if the flag is True
        if tf_output:
            # TODO apply lambdify method to tensorflow
            return tf.convert_to_tensor(np_result)
        # return the result in dictionary type
        if dict_output:
            return dict_list
        # return the result in Numpy array
        return np_result
    def num_linear_normal_form_R3(self,
                                  linear_bivector,
                                  mesh,
                                  tf_output=False,
                                  pt_output=False,
                                  dict_output=False):
        """
            Evaluates a normal form for Lie-Poisson bivector fields on R^3 (modulo linear isomorphisms) in all
            points given of a mesh.
        Parameters
        ==========
        :linear_bivector:
            Is a Lie-Poisson bivector in a dictionary format with integer type 'keys' and string type 'values'.
        :mesh:
            Is a numpy array where each value is a list of float values that representa a point in R^{dim}.
        :pt_output/tf_output:
            Is a boolean flag to indicates if the result is given in a tensor from PyTorch/TensorFlow, its
            default value is False.

        Returns
        =======
            The default result is a NumPy array that contains all the evaluations of lineal normal form from a
            bivector. This value can be converted to Tensor PyTorch, TensorFlow or dictionary for by setting
            their respective flag as True in the params.The result is a normal form in a dictionary format with
            integer type 'keys' and symbol type 'values'.

        Example
        ========
            >>> import numpy as np
            >>> from poisson import NumPoissonGeometry
            >>> # Instance the class to dimension 3
            >>> npg3 = NumPoissonGeometry(3)
            >>> # For bivector (3*x3 - x1)*Dx1^Dx2 - (x1 + 2*x2)*Dx1^Dx3 + (x1 + x2 - x3)*Dx2^Dx3
            >>> bivector = {(1, 2): '3*x3 - x1', (1, 3): '-(x1 + 2*x2)', (2, 3): 'x1 + x2 - x3'}
            >>> # Defines a simple mesh
            >>> mesh = np.array([[1., 1., 1.]])
            >>> # Evaluates the mesh
            >>> npg3.num_linear_normal_form_R3(bivector, mesh)
            >>> [[[ 0. -1. -1.]
                  [ 1.  0.  1.]
                  [ 1. -1.  0.]]]
            >>> npg3.num_linear_normal_form_R3(bivector, mesh, pt_output=True)
            >>> tensor([[[ 0., -1., -1.],
                         [ 1.,  0.,  1.],
                         [ 1., -1.,  0.]]], dtype=torch.float64)
            >>> npg3.num_linear_normal_form_R3(bivector, mesh, tf_output=True)
            >>> tf.Tensor(
                [[[ 0. -1. -1.]
                  [ 1.  0.  1.]
                  [ 1. -1.  0.]]], shape=(1, 3, 3), dtype=float64)
            >>> npg3.num_linear_normal_form_R3(bivector, mesh, dict_output=True)
        """
        # Get the lineal normal form from a bivector
        lin_normal_form = self.pg.linear_normal_form_R3(linear_bivector)
        # Evaluates the linear normal form from a bivector in each point from mesh and save the result in Numpy array
        dict_list = dict_mesh_eval(lin_normal_form, mesh, self.pg.coords)
        np_result = dicts_to_matrices(dict_list, self.pg.dim)
        # return the result in a PyTorch tensor if the flag is True
        if pt_output:
            return torch.from_numpy(np_result)
        # return the result in a TensorFlow tensor if the flag is True
        if tf_output:
            # TODO apply lambdify method to tensorflow
            return tf.convert_to_tensor(np_result)
        # return the result in dictionary type
        if dict_output:
            return dict_list
        # return the result in Numpy array
        return np_result
    def num_coboundary_operator(self,
                                bivector,
                                multivector,
                                mesh,
                                tf_output=False,
                                pt_output=False,
                                dict_output=False):
        """
            Evalueates the Schouten-Nijenhuis bracket between a given (Poisson) bivector field and a (arbitrary)
            multivector field in all points given of a mesh.
            The Lichnerowicz-Poisson operator is defined as
                [P,A](df1,...,df(a+1)) = sum_(i=1)^(a+1) (-1)**(i)*{fi,A(df1,...,î,...,df(a+1))}_P
                                     + sum(1<=i<j<=a+1) (-1)**(i+j)*A(d{fi,fj}_P,..î..^j..,df(a+1))
            where P = Pij*Dxi^Dxj (i < j), A = A^J*Dxj_1^Dxj_2^...^Dxj_a.

        Parameters
        ==========
        :bivector:
            Is a Poisson bivector in a dictionary format with tuple type 'keys' and string type 'values'.
        :multivector:
            Is a multivector filed in a dictionary format with integer type 'keys' and string type 'values'.
        :mesh:
            Is a numpy array where each value is a list of float values that representa a point in R^{dim}.
        :dict_output:
            Is a boolean flag to indicates if the result is given in a bivector in dictionary format, its
            default value is False.
        :pt_output/tf_output:
            Is a boolean flag to indicates if the result is given in a tensor from PyTorch/TensorFlow, its
            default value is False.

        Returns
        =======
            The default result is a NumPy array that contains all the evaluations of the Schouten-Nijenhuis bracket.
            This value can be converted to Tensor PyTorch, TensorFlow or dictionary for by setting their respective
            flag as True in the params.

        Example
        ========
            >>> import numpy as np
            >>> from poisson import NumPoissonGeometry
            >>> # Instance the class to dimension 3
            >>> npg3 = NumPoissonGeometry(3)
            >>> # For bivector x3*Dx1^Dx2 - x2*Dx1^Dx3 + x1*Dx2^Dx3
            >>> bivector = {(1, 2): 'x3', (1, 3): '-x2', (2, 3): 'x1'}
            >>> # Defines a one form W
            >>> W = {(1,): 'x1 * exp(-1/(x1**2 + x2**2 - x3**2)**2) / (x1**2 + x2**2)',
                     (2,): 'x2 * exp(-1/(x1**2 + x2**2 - x3**2)**2) / (x1**2 + x2**2)',
                     (3,): 'exp(-1 / (x1**2 + x2**2 - x3**2)**2)'}
            >>> mesh = np.array([[0., 0., 1.]])
            >>> # Evaluates the mesh into {f,g}
            >>> npg3.num_coboundary_operator(bivector, multivector, mesh)
            >>> [[[ 0.        ,  0.36787945,  0.        ],
                  [-0.36787945,  0.        ,  0.        ],
                  [ 0.        ,  0.        ,  0.        ]]]
            >>> npg3.num_coboundary_operator(bivector, multivector, mesh, dict_output=True)
            >>> [{(1, 2): 0.36787944117144233}]
            >>> npg3.num_coboundary_operator(bivector, multivector, mesh, pt_output=True)
            >>> tensor([[[ 0.0000,  0.3679,  0.0000],
                         [-0.3679,  0.0000,  0.0000],
                         [ 0.0000,  0.0000,  0.0000]]], dtype=torch.float64)
            >>> npg3.num_coboundary_operator(bivector, mesh, multivector, tf_output=True)
            >>> tf.Tensor: shape=(1, 3, 3), dtype=float64, numpy=
                array([[[ 0.        ,  0.36787945,  0.        ],
                        [-0.36787945,  0.        ,  0.        ],
                        [ 0.        ,  0.        ,  0.        ]]]
        """
        image_bivector = self.pg.lichnerowicz_poisson_operator(
            bivector, multivector)
        dict_eval = dict_mesh_eval(image_bivector, mesh, self.pg.coords)
        if isinstance(image_bivector, int):
            np_result = np.array(dict_eval)
        else:
            multivector_dim = check_multivector_dim(
                bivector) + check_multivector_dim(multivector) - 1
            # This block creates an evalutes numerically an multivector
            np_array = []
            for item in dict_eval:
                tensor_base, index_base = create_tensor_base(
                    self.pg.dim, multivector_dim)
                result = add_tensor_values(item, tensor_base, index_base)
                np_array.append(result.tolist())
            # Add all evaluation in np array
            np_result = np.array(np_array)
        # return the result in dictionary type
        if dict_output:
            return dict_eval
        # return the result in a TensorFlow tensor if the flag is True
        if pt_output:
            return torch.from_numpy(np_result)
        # return the result in a PyTorch tensor if the flag is True
        if tf_output:
            # TODO apply lambdify method to tensorflow
            return tf.convert_to_tensor(np_result)
        # return the result in Numpy array
        return np_result
    def num_curl_operator(self,
                          multivector,
                          function,
                          mesh,
                          tf_output=False,
                          pt_output=False,
                          dict_output=False):
        """
            Evaluates the divergence of multivector field in all points given of a mesh.

        Parameters
        ==========
        :multivector:
            Is a multivector filed in a dictionary format with integer type 'keys' and string type 'values'.
        :function:
            Is a nowhere vanishing function in a string type. If the function is constant you can input the
            number type.
        :mesh:
            Is a numpy array where each value is a list of float values that representa a point in R^{dim}.
        :dict_output:
            Is a boolean flag to indicates if the result is given in a bivector in dictionary format, its
            default value is False.
        :pt_output/tf_output:
            Is a boolean flag to indicates if the result is given in a tensor from PyTorch/TensorFlow, its
            default value is False.

        Returns
        =======
            The default result is a NumPy array that contains all the evaluations of the divergence of multivertor
            field in matricial format. This value can be converted to Tensor PyTorch, TensorFlow or dictionary form
            by setting their respective flag as True in the params.

        Example
        ========
            >>> import numpy as np
            >>> from poisson import NumPoissonGeometry
            >>> # Instance the class to dimension 4
            >>> npg4 = NumPoissonGeometry(4)
            >>> # For bivector 2*x4*Dx1^Dx3 + 2*x3*Dx1^Dx4 - 2*x4*Dx2^Dx3 + 2*x3*Dx2^Dx4 + (x1-x2)*Dx3^Dx4
            >>> bivector = {(1, 3): '2*x4', (1, 4): '2*x3', (2, 3): '-2*x4', (2, 4): '2*x3', (3, 4): 'x1 - x2')}
            >>> mesh = np.array([0., 0., 0. ])
            >>> # Evaluates the mesh into {f,g}
            >>> npg4.num_curl_operator(bivector, function, mesh)
            >>> [[[ 0.]
                  [ 0.]
                  [ 0.]
                  [ 0.]]]]
            >>> npg4.num_curl_operator(bivector, function, mesh, dict_output=True)
            >>> [{0: 0}]
            >>> npg4.num_curl_operator(bivector, function, mesh, pt_output=True)
            >>> tensor([[[ 0.],
                         [ 0.],
                         [ 0.],
                         [ 0.]]], dtype=torch.float64)
            >>> npg4.num_curl_operator(bivector, mesh, function, tf_output=True)
            >>> tf.Tensor(
                [[[ 2.5]
                  [-5. ]
                  [ 2.5]]], shape=(1, 3, 1), dtype=float64)
        """
        curl_operator = self.pg.curl_operator(multivector, function)
        dict_eval = dict_mesh_eval(curl_operator, mesh, self.pg.coords)
        multivector_dim = check_multivector_dim(multivector) - 1
        # This block creates an evalutes numerically an multivector
        np_array = []
        for item in dict_eval:
            tensor_base, index_base = create_tensor_base(
                self.pg.dim, multivector_dim)
            result = add_tensor_values(item, tensor_base, index_base)
            np_array.append(result.tolist())
        # Add all evaluation in np array
        np_result = np.array(np_array)
        # return the result in dictionary type
        if dict_output:
            return dict_eval
        # return the result in a PyTorch tensor if the flag is True
        if pt_output:
            return torch.from_numpy(np_result)
        # return the result in a TensorFlow tensor if the flag is True
        if tf_output:
            # TODO apply lambdify method to tensorflow
            return tf.convert_to_tensor(np_result)
        # return the result in Numpy array
        return np_result
    def num_bivector(self,
                     bivector,
                     mesh,
                     pt_output=False,
                     tf_output=False,
                     dict_output=False):
        """ Evaluates a bivector field into at each point of the mesh.

        Parameters
        ==========
        :bivector:
            Is a Poisson bivector in a dictionary format with tuple type 'keys' and string type 'values'.
        :mesh:
            Is a numpy array where each value is a list of float values that representa a point in R^{dim}.
        :pt_out/tf_out:
            Is a boolean flag to indicates if the result is given in a tensor from PyTorch/TensorFlow, its
            default value is False.

        Returns
        =======
            The default result is a NumPy array that contains all the evaluations of a bivector. This value
            can be converted to Tensor PyTorch or TensorFlow by setting their respective flag as True in
            the params.

        Example
        ========
            >>> import numpy as np
            >>> from poisson import NumPoissonGeometry
            >>> # Instance the class to dimension 3
            >>> npg3 = NumPoissonGeometry(3)
            >>> # For bivector x3*Dx1^Dx2 - x2*Dx1^Dx3 + x1*Dx2^Dx3
            >>> bivector ={(1, 2): ‘x3’, (1, 3): ‘-x2’, (2, 3): ‘x1’}
            >>> # Creates a simple mesh
            >>> mesh = np.array([[0., 0., 1.]])
            >>> # Evaluates the mesh into bivector
            >>> npg3.num_bivector(bivector, mesh)
            >>> [[[ 0.  1. -0.]
                  [-1.  0.  0.]
                  [ 0. -0.  0.]]
            >>> npg3.num_bivector(bivector, mesh, tf_output=True)
            >>> tf.Tensor(
                [[[ 0.  1. -0.]
                  [-1.  0.  0.]
                  [ 0. -0.  0.]]], shape=(1, 3, 3), dtype=float64)
            >>> npg3.num_bivector(bivector, mesh, pt_output=True)
                tensor([[[ 0.,  1., -0.],
                         [-1.,  0.,  0.],
                         [ 0., -0.,  0.]]], dtype=torch.float64)
            >>> npg3.num_bivector(bivector, mesh, dict_output=True)
            >>> [{(1, 2): 1.0, (1, 3): -0.0, (2, 3): 0.0}]
        """
        # Evaluates all point from the mesh in the bivector
        dict_list = np.array(dict_mesh_eval(bivector, mesh, self.coords))
        # save in a np array
        np_result = dicts_to_matrices(dict_list, self.dim)
        # return the result in a PyTorch tensor if the flag is True
        if pt_output:
            return torch.from_numpy(np_result)
        # return the result in a TensorFlow tensor if the flag is True
        if tf_output:
            # TODO apply lambdify method to tensorflow
            return tf.convert_to_tensor(np_result)
        # return the result in dictionary type
        if dict_output:
            return dict_list
        # return the result in Numpy array
        return np_result