0

I have the following code, where the kronecker product of 2 arrays is computed. In this code I want to return the array C which is the kronecker product of the two arrays back to the main function. I have tried pointers instead of void but I am not able to do it. Also I want tje function to compute the kroecker product of the arrays of any sizes so I have used templates here. How can I return the array C (Kronecker product) back to the main.

#include <iostream>
using namespace std;
#include <any>
#include <vector>


template <size_t size_x1, size_t size_y1, size_t size_x2, size_t size_y2>

void Kroneckerproduct(int (&A)[size_x1][size_y1],int (&B)[size_x2][size_y2])
{

 int rowa=size_x1;
 int cola=size_y1; 
 int rowb=size_x2;
 int colb=size_y2;

    int C[rowa * rowb][cola * colb];

    // i loops till rowa
    for (int i = 0; i < rowa; i++) {
        //for (int i = 0; i < size_x1; i++) {

        // k loops till rowb
        for (int k = 0; k < rowb; k++) {
           // for (int k = 0; k < size_x2; k++) {

            // j loops till cola
            for (int j = 0; j < cola; j++) {
                //for (int j = 0; j < size_y1; j++) {

                // l loops till colb
                for (int l = 0; l < colb; l++) {
                   // for (int l = 0; l < size_y2; l++) {

                    // Each element of matrix A is
                    // multiplied by whole Matrix B
                    // resp and stored as Matrix C
                    C[i + l + 1][j + k + 1] = A[i][j] * B[k][l];
                    cout << C[i + l + 1][j + k + 1] << " ";
                }
            }
            cout << endl;
        }
    }




}


int main()
{
    int A[3][2] = { { 1, 2 }, { 3, 4 }, { 1, 0 } },
        B[2][3] = { { 0, 5, 2 }, { 6, 7, 3 } };

    Kroneckerproduct(A, B);
    return 0;
}
  • 3
    You can't return c-style arrays from functions. Consider using `std::array` – Fureeish May 03 '22 at 07:49
  • 2
    Pass an appropriately declared target array by-reference as a third argument and but it on the onus of the caller to provide it. You already know the size restrictions, you can it as another arg using the same non-type template size args to ensure the caller is passing a proper target. – WhozCraig May 03 '22 at 07:52
  • @WhozCraig okay i will try it – user7341333 May 03 '22 at 07:54
  • 1
    the decl would look [something like this](https://pastebin.com/XWPXSj2w). – WhozCraig May 03 '22 at 07:57
  • You can return a `std::array`: https://godbolt.org/z/osdzfqb8h Note that you do not set all elements of the array and also overwrite some of them. – mch May 03 '22 at 07:59
  • 3
    `int C[rowa * rowb][cola * colb];` is not standard C++, though its just because of the way you wrote it. `size_x1` is a constant expression, `rowa` is not. See [Why aren't variable-length arrays part of the C++ standard?](https://stackoverflow.com/questions/1887097/why-arent-variable-length-arrays-part-of-the-c-standard) – 463035818_is_not_an_ai May 03 '22 at 07:59
  • are the variables rowa rowb cola and colb really necessary? – Gonen I May 03 '22 at 08:54
  • @WhozCraig Would there be a concern of code bloat if there are arrays of many different sizes -- after all, it's a quadratic combination of values --, or are modern compilers smart enough to generate a general, parametrized function? – Peter - Reinstate Monica May 03 '22 at 09:54
  • Aside: `[i + l + 1][j + k + 1]` isn't the right index for a kronecker product. Hint: you have to fill all the way to `size_x1 * size_x2`. not `size_x1 + size_x2 + 1` – Caleth May 03 '22 at 09:55
  • @Peter-ReinstateMonica I can't imagine the actual optimized code would be nefarious enough compared to the original effort. The OP has already dictated the source sizes are compile-time distinct; adding that third argument based on those same compile-time distinct sizes shouldn't introduce any additional bloat. That said, a *run-time* sizing solution would obviously negate the potential code replication. – WhozCraig May 03 '22 at 14:30
  • @WhozCraig True, since the array dimensions are compile-time constants their number is limited, unless we machine generate code. (Btw, my concern was about any such template, not at all about your suggestion of a third parameter which is idiomatic, at least for this "C with templates" approach). – Peter - Reinstate Monica May 03 '22 at 14:36

3 Answers3

1

To return an array from a function, you must dynamically allocate it first. This is necessary as the array declared in the function statically is cleared after the function finishes its execution.

To create an array of dimension m x n you can do something like this:

int** c = new int*[m];

for (int i = 0; i < m; i++)
{
    c[i] = new int[n];
}

Now, you can simply return it using return c;

Also, you need to delete this array manually as it was dynamically created. You can do so by using:

for(int i = 0; i < m; i++)    // To delete the inner arrays
    delete [] c[i];   
delete [] c;
1

One heritage from old C is that raw arrays cannot be directly transferred by value. The traditional workaround is to wrap the array in a struct if the size is not so large that might overflow the stack.

You are using a none standard extension of C++ in your program:

int rowa=size_x1;
int cola=size_y1; 
int rowb=size_x2;
int colb=size_y2;
int C[rowa * rowb][cola * colb];

These definitions lead to runtime stack allocated array, which is not standard. A minor modification on modern compilers however makes for a standard solution:

constexpr int rowa=size_x1;
constexpr int cola=size_y1; 
constexpr int rowb=size_x2;
constexpr int colb=size_y2;

Or on older compilers:

enum{
    rowa=size_x1,
    cola=size_y1,
    rowb=size_x2,
    colb=size_y2
};

Next tip: using namespace std; this is bad practice at global scope. Prefixing standard identifiers with std:: makes code more understandable, while avoiding possible name collisions.

Instead of using raw arrays, you'd better use std::array for static sized arrays and std::vector for dynamic sized or large arrays:

#include <array>
template<std::size_t cols, std::size_t rows>
using array2d = std::array<std::array<int, rows>, cols>;

constexpr std::size_t size_x = 3;
constexpr std::size_t size_y = 4;

array2d<size_x,size_y> a;

Moreover, since it looks you are creating a tensor/matrix library, you may consider a redesign to define a template class instead of simple use of arrays.

Red.Wave
  • 2,790
  • 11
  • 17
1

C style arrays aren't regular, which makes them a big pain to deal with. That's why std::array exists.

template <size_t size_x1, size_t size_y1, size_t size_x2, size_t size_y2>
std::array<std::array<int, size_y1 * size_y2>, size_x1 * size_x2> Kroneckerproduct(const std::array<std::array<int, size_y1>, size_x1> & A, const std::array<std::array<int, size_y2>, size_x2> & B)
{
    std::array<std::array<int, size_y1 * size_y2>, size_x1 * size_x2> C;

    for (int i = 0; i < size_x1; i++) {
        for (int k = 0; k < size_x2; k++) {
            for (int j = 0; j < size_y1; j++) {
                for (int l = 0; l < size_y2; l++) {
                    C[(i * size_x2) + l][(j * size_y2) + k] = A[i][j] * B[k][l];
                }
            }
        }
    }

    return C;
}

I've also fixed the indexing of C. When you were printing what you'd just written to an element of C, it wasn't obvious that you were overwriting values. You can see the difference if you print the indexes

std::cout << "C[" << (i + l + 1) << "][" << (j + k + 1) << "] ";

vs

std::cout << "C[" << ((i * size_x2) + l) << "][" << ((j * size_y2) + k) << "] ";
Caleth
  • 52,200
  • 2
  • 44
  • 75