# Matlab sub2ind in Fortran

**URL:** <https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259>\
**Category:** Uncategorized\
**Created:** [June 26, 2024, 12:19pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259 "2024-06-26T12:19:24Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![aledinola](https://avatars.discourse-cdn.com/v4/letter/a/3ec8ea/32.png) [@aledinola](https://fortran-lang.discourse.group/u/aledinola)\
**Post date:** [June 26, 2024, 12:19pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/1 "2024-06-26T12:19:24Z")

</div>

Is there a Fortran implementation of the Matlab function sub2ind out there? Simple google search did not help. The function is very simple, it converts subscripts of a multidimensional array into linear indexes. For example, if A is a 2 dim array with n1 rows and n2 columns, the subscripts (i1,i2) are mapped to

Lin = (i2-1)\*nx+i1

I wrote sub2ind in Fortran myself for the case of 2 dim arrays but I would like to generalize up to, say, 4 or 5 dimensions. What I plan to do is two implement sub2ind for 2,3,4,5 dimensions separatley and then use

```fortran

Module mymod

Implicit none

interface sub2ind
module procedure sub2ind2
module procedure sub2ind3
Etc
end interface

contains

function sub2ind2(nvec,i1,i2) result(lin)

integer, intent(in) :: nvec(2)
integer, intent(in) :: i1,i2
Integer :: lin

Lin = (i2-1)*nvec(1) + i1

end function sub2ind2

End module mymod

```

However, I would like to have a well-tested code with error messages etc, so I figured that before I reinvent the wheel I should ask on this forum

---

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [June 26, 2024, 12:30pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/2 "2024-06-26T12:30:50Z")

</div>

I’ve been thinking about writing a recursive template for this using Fypp: [Introduction — Fypp 3.2 documentation](https://fypp.readthedocs.io/en/stable/fypp.html#def-directive) (see comment near the bottom of `def` about recursive invocation). You can also think of this as a [fold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)) operation.

As a side-note, not directly answering your question, but potentially offering an alternative solution, Fortran has the notion pointer array remapping:

```fortran
real, target :: a(100) ! Pretend it's a 5-by-20 array
Real, pointer :: b(:,:) => null()

b(1:5,1:20) => a

```

Now you can reference elements of `b` without needing to do the linear index computation.

---

<div class="post-metadata">

**Author:** ![FedericoPerini](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/federicoperini/32/1750_2.png) [@FedericoPerini](https://fortran-lang.discourse.group/u/FedericoPerini)\
**Post date:** [June 26, 2024, 1:50pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/3 "2024-06-26T13:50:00Z")

</div>

maybe useful?

> <https://github.com/gnu-octave/octave/blob/4655d9cacde5bd99d09891f670ed2072328c1e09/liboctave/array/Array-util.cc#L505-L591>

---

<div class="post-metadata">

**Author:** ![rwmsu](https://avatars.discourse-cdn.com/v4/letter/r/48db29/32.png) [@rwmsu](https://fortran-lang.discourse.group/u/rwmsu)\
**Post date:** [June 26, 2024, 1:57pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/4 "2024-06-26T13:57:36Z")

</div>

Here is my version but its not much different than the OPs version.

```auto
  PURE Subroutine sub2ind(isize, col, row, ind)

    Implicit NONE

    Integer, Intent(IN) :: isize(2)
    Integer, Intent(IN) :: col(:)
    Integer, Intent(IN) :: row(:)
    Integer, Intent(INOUT) :: ind(:)

    Integer :: i, j, ie, ni

    ie = SIZE(col)
    ni = isize(1)

    Do i=1,ie
      ind(i) = col(i) + (row(i)-1)*ni
    End Do

  End Subroutine sub2ind

```

---

<div class="post-metadata">

**Author:** ![aledinola](https://avatars.discourse-cdn.com/v4/letter/a/3ec8ea/32.png) [@aledinola](https://fortran-lang.discourse.group/u/aledinola)\
**Post date:** [June 26, 2024, 1:59pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/5 "2024-06-26T13:59:00Z")

</div>

Thanks! but this works only for two dimensions, right?

---

<div class="post-metadata">

**Author:** ![shahmoradi](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/shahmoradi/32/3151_2.png) [@shahmoradi](https://fortran-lang.discourse.group/u/shahmoradi)\
**Post date:** [June 26, 2024, 3:44pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/6 "2024-06-26T15:44:22Z")

</div>

You may want to also check out the [`pm_matrixIndex :: getMatIndex()`](https://www.cdslab.org/paramonte/fortran/2/interfacepm__matrixIndex_1_1getMatIndex.html) of the ParaMonte library. It has a comprehensive set of index-conversion routines for many of the [major LAPACK packing formats](https://www.cdslab.org/paramonte/fortran/2/namespacepm__matrixPack.html), including the one that you are looking for (called [lfpack](https://www.cdslab.org/paramonte/fortran/2/namespacepm__matrixPack.html#details) in the library). If you would like to simply extract the module and use it in your codebase, try the following in a Git-aware terminal:

```bash
git clone https://github.com/cdslaborg/paramonte.git
cd paramonte
./install.sh --mod pm_matrixIndex

```

The last command will copy the [pm\_matrixIndex](https://www.cdslab.org/paramonte/fortran/2/namespacepm__matrixIndex.html) and all its dependencies for inclusion in your project in the installation directory `./bin/<library_build_specs>/fpp` in the root directory of the Git project.

If you simply prefer to reimplement the algorithm in your codebase, here is the [actual implementation](https://github.com/cdslaborg/paramonte/blob/main/src/fortran/main/pm_matrixIndex%40routines.inc.F90). Even if you intend to reimplement, I highly recommend to work with preprocessed source files using the above commands instead of the actual implementation source code linked above.

---

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [June 26, 2024, 6:43pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/7 "2024-06-26T18:43:03Z")

</div>

Here is rather odd-looking way to do this in Fortran 2018 using [reduce](https://fortran-lang.discourse.group/t/applications-of-reduce-intrinsic/2622) (and probably not very efficient based on what I saw in Compiler Explorer). A newer version of the Intel Fortran compiler is needed.

```auto
module fold
    implicit none
    public
contains
    function flatidx(extent,index) result(idx)
        integer, intent(in) :: extent(:), index(:)
        integer :: idx
        type :: pair
            integer :: e, i
        end type
        type(pair) :: tmp(size(extent))
        type(pair) :: res
        tmp%e = extent
        tmp%i = index
        res = reduce(tmp,op)
        idx = res%i
    contains
       pure function op(a,b) result(c)
        type(pair), intent(in) :: a, b
        type(pair) :: c
        c = pair(a%e*b%e, a%i + a%e*(b%i - 1))
       end function
    end function
end module

program test_fold
use fold, only: flatidx
implicit none
integer :: i, j

! 1-d
do i = 1, 8
    print *, i, flatidx([8],[i])
end do

! 2-d
do j = 1, 8
    do i = 1, 4
        print *, i, j, flatidx([4,8],[i,j])
    end do
end do
end program

```

The solution is inspired by the blog posts:

- [Folding a Global Linear ID from CUDA Dimensions and Indices](https://pkeir.github.io/blog/2021/10/04/folding-a-linear-index-for-cuda/) | by Paul Keir
- [Map, Filter, Reduce in Fortran 2018](https://medium.com/modern-fortran/map-filter-reduce-in-fortran-2018-e40b93668ed9) | by @milancurcic

---

<div class="post-metadata">

**Author:** ![HugoMVale](https://avatars.discourse-cdn.com/v4/letter/h/e19adc/32.png) [@HugoMVale](https://fortran-lang.discourse.group/u/HugoMVale)\
**Post date:** [June 26, 2024, 7:05pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/8 "2024-06-26T19:05:51Z")

</div>

I guess this also works for any rank, but I did not test carefully.

Edit: Just checked against [Octave Online](https://octave-online.net/) and I do get the same results.

```fortran

module array
   implicit none
   private
   public :: sub2ind

contains

   integer function sub2ind(a, indices) result(res)
      integer, intent(in) :: a(..)
      integer, intent(in) :: indices(:)

      integer, allocatable :: a_shape(:)
      integer :: a_rank, stride, i

      a_shape = shape(a)
      a_rank = rank(a)

      res = 1
      stride = 1
      do i = 1, a_rank
         res = res + (indices(i) - 1)*stride
         stride = stride*a_shape(i)
      end do
   end function

end module array

program test
   use array, only: sub2ind
   implicit none

   integer :: a(2, 3), b(3, 2, 4), c(3, 4, 5, 6)

   print *, sub2ind(a, [1, 3])
   print *, sub2ind(a, [2, 2])

   print *, sub2ind(b, [3, 1, 1])
   print *, sub2ind(b, [3, 2, 1])
   print *, sub2ind(b, [1, 2, 1])
   print *, sub2ind(b, [3, 2, 4])

   print *, sub2ind(c, [1, 2, 4, 6])

end program

```

---

<div class="post-metadata">

**Author:** ![irukoa](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/irukoa/32/3306_2.png) [@irukoa](https://fortran-lang.discourse.group/u/irukoa)\
**Post date:** [June 26, 2024, 7:53pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/9 "2024-06-26T19:53:02Z")

</div>

I created my own library to shift between array and index representation:

> **[GitHub - irukoa/MAC: Multidimensional Array Containers](https://github.com/irukoa/MAC)**
>
> Multidimensional Array Containers. Contribute to irukoa/MAC development by creating an account on GitHub.

A minimal example,

```fortran
program example
  use MAC, only: container_specifier
  implicit none
  type(container_specifier) :: a

  !Create a 2 x 3 x 4 3-dimensional array handle.
  call a%specify(dimension_specifier=[2, 3, 4])
  print*, a%ind([2, 1, 3]) !14.
  print*, a%ind([3, 1, 3]) !Out of bounds (returns 0).

  !Create a 2 x 5 2-dimensional array handle.
  call a%specify(dimension_specifier=[2, 5])
  print*, a%ind([1,3]) !5.
end program example

```

I took the [idea](https://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays) from this website.

---

<div class="post-metadata">

**Author:** ![irukoa](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/irukoa/32/3306_2.png) [@irukoa](https://fortran-lang.discourse.group/u/irukoa)\
**Post date:** [June 26, 2024, 7:57pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/10 "2024-06-26T19:57:19Z")

</div>

You can also specify that the array is in row-major order

```fortran
 call a%specify(dimension_specifier=[2, 5], layout="row")
 print*, a%ind([1,3]) !3.

```

---

<div class="post-metadata">

**Author:** ![aledinola](https://avatars.discourse-cdn.com/v4/letter/a/3ec8ea/32.png) [@aledinola](https://fortran-lang.discourse.group/u/aledinola)\
**Post date:** [June 27, 2024, 6:30pm UTC](https://fortran-lang.discourse.group/t/matlab-sub2ind-in-fortran/8259/11 "2024-06-27T18:30:39Z")

</div>

Nice code! I would add a check on the input arguments though. You want to make sure that if the array `a` has `n` dimensions, then the vector of subscripts has `n` elements

```fortran
if (rank(a) /= size(indices)) then
error stop "some message"
endif

```
