Skip to content

Fixed printing for arrays as per CPython #1552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

anutosh491
Copy link
Collaborator

@anutosh491 anutosh491 commented Feb 27, 2023

This is a Pr in succession to #1512. Currently I've added support for 1d arrays .
For something like the following

from ltypes import i32
from numpy import empty, int32

def main0():
    x: i32 = 2
    arr1: i32[x] = empty(x, dtype=int32)
    arr2: i32[x] = empty(x, dtype=int32)
    arr1[0] = 100
    arr1[1] = 200
    arr2[0] = 300
    arr2[1] = 400
    print(arr1, arr2)
    print(arr1, arr2, sep="abc")
    print(arr1, arr2, end="pqr\n")
    print(arr1, arr2, sep="abc", end="pqr\n")

main0()

On main

100 200 
300 400 
100 200abc300 400 
100 200 
300 400pqr
100 200abc300 400pqr

On branch (as expected through CPython)

[100 200] [300 400]
[100 200]abc[300 400]
[100 200] [300 400]pqr
[100 200]abc[300 400]pqr

@certik
Copy link
Contributor

certik commented Feb 27, 2023

How would this now print in LFortran?

@anutosh491
Copy link
Collaborator Author

anutosh491 commented Feb 27, 2023

Currently in LFotran , if we run a very basic program trying to print two arrays together , we have the following

(lf) anutosh491@spbhat68:~/lpython/lfortran$ cat examples/expr2.f90 
program arrays_01
implicit none
integer :: a(2), b(2)
a = [100,200]
b = [300,400]
print *, a, b
end program arrays_01
(lf) anutosh491@spbhat68:~/lpython/lfortran$ ./src/bin/lfortran ./examples/expr2.f90
100
200

300
400

(lf) anutosh491@spbhat68:~/lpython/lfortran$

So basically

  1. We aren't using square brackets
  2. default seperator between 2 elements of the array is a newline
  3. default seperator between 2 elements of the print statement is a newline + emptyline
  4. default end for the print statement is a newline + emptyline

@certik
Copy link
Contributor

certik commented Feb 27, 2023

I think LFortran should behave like GFortran by default:

$ lfortran x.f90 
100
200

300
400

$ gfortran x.f90 
$ ./a.out 
         100         200         300         400

But with a command line option it would behave like this:

$ lfortran --nice-print x.f90 
[100, 200] [300, 400]

@anutosh491
Copy link
Collaborator Author

Sure I'll open a Pr in LFortran soon to address this . Does the Pr here look good to you ?

@faze-geek
Copy link
Contributor

So basically

  1. We aren't using square brackets
  2. default seperator between 2 elements of the array is a newline
  3. default seperator between 2 elements of the print statement is a newline + emptyline
  4. default end for the print statement is a newline + emptyline

I had opened a similar issue regarding printing of arrays in Lfortran a couple of days back - lfortran/lfortran#1348

print(arr1, arr2)
print(arr1, arr2, sep="abc")
print(arr1, arr2, end="pqr\n")
print(arr1, arr2, sep="abc", end="pqr\n")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is only limited to 1D arrays. Let's test it for 2D, 3D and 4D arrays as well?

Copy link
Collaborator Author

@anutosh491 anutosh491 Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I had mentioned in the PR description that currently only 1D arrays work properly (I'll highlight it for everyone) . For 2D We have the following

def main0():
    Nx: i32 = 2; Ny: i32 = 2
    arr: i32[2, 2] = empty([Ny, Nx], dtype=int32)
    i: i32
    j: i32
    for i in range(Ny):
        for j in range(Nx):
            arr[i, j] = i * j

    print(arr)

main0()

(lf) anutosh491@spbhat68:~/lpython/lpython$ ./src/bin/lpython ./examples/expr2.py
[0 0 
0 1]
(lf) anutosh491@spbhat68:~/lpython/lpython$ python examples/expr2.py 
[[0 0]
 [0 1]]

I'll try to address n-dimensional arrays in this Pr or a subsequent PR as soon as I realize the approach for it !

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's stopping us from implementing printing >= 3D arrays? Is it an algorithmic blocker? Or ASR design is insufficient to support it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem like an ASR design problem to me as of now . It's just that the current implementation of print_array_using_doloop is tailored specifically for 1D arrays and would need to be modified to handle higher dimensions.
Getting outputs isn't a problem ( I tried for 4D and 5D too) but suppose we have the following

(lf) anutosh491@spbhat68:~/lpython/lpython$ cat examples/expr2.py 
from numpy import empty, int32
def main0():
    Nx: i32 = 3;
    arr: i32[3, 3, 3] = empty([Nx, Nx, Nx], dtype=int32)
    i: i32
    j: i32
    k: i32
    for i in range(Nx):
        for j in range(Nx):
            for k in range(Nx):
                 arr[i, j, k] = i * j * k

    print(arr)

main0()

(lf) anutosh491@spbhat68:~/lpython/lpython$ ./src/bin/lpython ./examples/expr2.py
[0 0 0 
0 0 0 
0 0 0 

0 0 0 
0 1 2 
0 2 4 

0 0 0 
0 2 4 
0 4 8 
]
(lf) anutosh491@spbhat68:~/lpython/lpython$ python examples/expr2.py 
[[[0 0 0]
  [0 0 0]
  [0 0 0]]

 [[0 0 0]
  [0 1 2]
  [0 2 4]]

 [[0 0 0]
  [0 2 4]
  [0 4 8]]]

Here print_array_using_doloop is only responsible for printing the elements out , without any formatting .

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what comes to my mind currently is that we recursively keep cutting down a dimension in visit_Print and keep printing the 1D array which we would obtain finally (as 1D arrays are being printed perfectly) . This can be done here

+++ b/src/libasr/pass/print_arr.cpp
@@ -150,6 +150,12 @@ public:
                     print_body.clear();
                 }
                 pass_result.push_back(al, print_open_bracket);
+                int n_dims = PassUtils::get_rank(x.m_values[i]);
+                if (n_dims > 1) {
+                    // Create a print object having a subarray reference 
+                    // with a lesser dimension than what we have originally
+                    // and call visit_Print on it . 
+                }
                 print_stmt = print_array_using_doloop(x.m_values[i], x.base.base.loc);

We might need to frame a ASR::expr_t* create_subarray_ref(ASR::expr_t* arr_expr, int dim_idx, int dim_size, Allocator& al) or something for this just like we have create_array_ref !
However, this approach can lead to a large number of nested calls, which may impact performance !

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see. How would you write a C function for printing arrays of general N-dimensions using only loops? Once that figured out then translating that C code into ASR should be easy then.

However, this approach can lead to a large number of nested calls, which may impact performance !

Well if you are talking about recursive approach to print array then also follow the same approach, write code in C to print arrays of general N dimensions and then translate it into ASR.

Copy link
Collaborator Author

@anutosh491 anutosh491 Mar 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay , not sure how I could get it translated into ASR but a C function which is recursive and also takes care of formatting would be the following

#include <stdio.h>

void print_array(int *arr, int *shape, int ndim) {
    printf("[");
    for (int i = 0; i < shape[0]; i++) {
        if (ndim > 1) {
            print_array(arr + i * shape[1], shape + 1, ndim - 1);
        } else {
            printf("%d", arr[i]);
        }
        if (i < shape[0] - 1) {
            printf(" ");
        }
    }
    printf("]\n");
}

int main() {
    int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int shape[] = {2, 3};
    print_array((int *) arr, shape, 2);
    return 0;
}

This would give us

[[1 2 3]
 [4 5 6]]

This would require the array , ndim and the size along each dimension !

Copy link
Collaborator

@czgdp1807 czgdp1807 Mar 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Now there are two ways to deal with this,

  1. Include your printing function in C backend and call it from LLVM/C backend.
  2. Following this approach in ASR would be very tricky as you are dealing with pointers directly. However the pattern I see here is printing each row in a new line with opening brackets and spaces prefixed and some closing brackets suffixed. Correct? If so, then I guess it can done using nested loops. Consider the following example,
>>> arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
>>> arr
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]]])

Printing using loops (algorithm not exact code and row major order printing) would like,

do i = 1, 2
    do j = 1, 2
        do k = 1, 3
            if k < 3 then
                print *, arr[i, j, k], ", "
            else
                print *, arr[i, j, k], "]"
            end if
        end do
        print *, "\n"
    end do
    print *, "]\n"
end do
print *, "]\n"

Add an extra closing bracket for each loop (except the innermost). Note that while generating code for printing you know the dimensions of array being printed at compile time. If the dimensions are not known (say the output of reshape is being printed then print it in a single line).

@czgdp1807 czgdp1807 marked this pull request as draft February 28, 2023 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants