Week 9

Eyes on the goal …. Hello everyone, hope this blog finds you well.

In this week we tackled a major issue, that could make the entire project go to crap, making this the most challenging but fun week in my GSoC journey upto now.

Aim :

My aim for this week is to reduce almost 12-15 Mb of disk space. Pocketpy is an small interpreter, the entire repository not being anything more than 3Mb in size. At this point my numpy directory itself is almost 15Mb in size. That means at this point numpy is an independent project, which cannot be merged into the main pocketpy repository. This was clearly mentioned to me as an expectation for a successful project by my mentors.

These are some of disadvantages of adding a project of high disk space to a lightweight project:

  • Increases build time.
  • Requires higher disk space usage.
  • Introduces complexity in dependency management.
  • Leads to longer download and setup times.

Work :

I raised the following issue this week -

  • Issue#46 : Giving up on np.matmul will save about ~14Mb of disk space

I made the following pull requests this week -

  • PR#43 : Added the boolean dtype
  • PR#44 : Add round member function for ndarray class
  • PR#47 : Add an array creation registry.

    The current code for array creation looks as follows. But there was a need for potentially better design. The improvement here is that, is it somehow possible to take in the dtpye dynamically and not separately in an if-else routine (make the vectors dynamically wrt to the dtpye given as well).
    This will remove redundancy of same code and avoid bloating while building the executable.

    The new approach to tackle this is as follows : 1. Prepare a template that can be used to represent vector of any dimension. The definition looks something like this -

      template <typename T, std::size_t N>
      struct nvector_impl {
          using type = std::vector<typename nvector_impl<T, N - 1>::type>;
      };
        
      template <typename T>
      struct nvector_impl<T, 0> {
          using type = T;
      };
        
      template <typename T, std::size_t N>
      using nvector = typename nvector_impl<T, N>::type;
    
  1. Then I can write a single function to transform a nvector<int, N> to nvector<T, N> to adjust my code for various dtypes.
    I thought of the function like this -
     template <typename T, std::size_t N>
     nvector<T, N> transform(nvector<int, N>& values) {
         nvector<T, N> result;
         if constexpr(N != 0) {
             for(auto& value: values) {
                 result.push_back(transform<T, N - 1>(value));
             }
         } else {
             result = T(values);
         }
         return result;
     }
    
  2. In the end, all of my snippets can be written by the same pattern.
     template<std::size_t N>
     void register_f(py::module_& m){
       m.def("array", [](const nvector<int_, N>& values, const std::string& dtype){
         if(dtype == "bool"){
           return std::unique_ptr<ndarray_base>(new ndarray_bool(transform<bool_>(values)));
         }
         // ...
       });
     }
    
    • PR#48 : Discard xflens for space relaxation.
      This pull request fixes the issue I have mentioned above.

Explaining everything on a deeper note the issue is as follows -
Since we wanted to make xtensor headers to be ideally < 5Mb to integrate with pocketpy main. I observed the following

  04:24:51  |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xtensor
  1.9M	xtensor
  04:25:13  |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xtensor-blas/
  132K	xtensor-blas/
  04:25:23  |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xflens
  13M	xflens
  04:25:32  |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xtl
  836K	xtl

You would notice that xtensor is not that heavy in terms of disk size. Infact xflens is contributing to about 90% of the size. We are using xtensor-blas at only one point in our code, to implement the matmul function here. https://github.com/pocketpy/gsoc-2024-dev/blob/d0e6874475315f9a71d669678b4b9b71f94c1cb7/numpy/include/numpy.hpp#L236-L237

This is because xtensor does not support matmul, it supports dot in xt::linalg which is dependent on xtensor-blas which inturn depends onxflens headers.
So this leads to the question that should we give up np.matmul from our module, if the use cases are partial in the first place. It will give us huge relaxation in disk space.

  • PR#49 : Added a custom matmul implementation.
    arr1 = np.array([1, 2, 3, 4, 5])
    assert_equal(arr1 * arr1, np.array([1, 4, 9, 16, 25]))
      
    arr2 = np.array([[1.33, 2.66], [3.99, 5.33]])
    assert np.allclose(arr2 * arr2, np.array([[1.7689, 7.0756], [15.9201, 28.4089]]))
    

    Future Work :

    In this upcoming week, we will write code to register more dtypes and fix existing issues that came across in the testing phase.

Address

Mumbai, Maharashtra, India