dynare/mex/sources/libkordersim/tensors.f08

304 lines
12 KiB
Plaintext
Raw Normal View History

! Copyright © 2021-2023 Dynare Team
!
! This file is part of Dynare.
!
! Dynare is free software: you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! Dynare is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License
! along with Dynare. If not, see <https://www.gnu.org/licenses/>.
module tensors
use iso_c_binding
use partitions
! use matlab_mat
implicit none (type, external)
! A type to contain a folded or unfolded tensor [gᵥᵐ]
type tensor
real(real64), pointer, contiguous, dimension(:,:) :: m
! real(real64), dimension(:,:), allocatable :: m
end type tensor
! A type to contain the unfolded tensor [gᵥᵐ]. For each offset j, ind(j)
! contains the associated unfolded index and s(j) stores the number of state
! variables in the index, i.e. if v = (x,u[,σ]) where x is the s-sized state
! variables vector and ind(j) = (α₁,…,αₘ), s(j) stores the number of
! coordinates αᵢ such that αᵢ ≤ s.
type unfolded_tensor
type(index), dimension(:), allocatable :: ind
integer, dimension(:), allocatable :: s
real(real64), dimension(:,:), pointer, contiguous :: m
end type unfolded_tensor
! A type to contain the folded tensor [gᵥᵐ]. For each offset j, ind(j)
! contains the associated folded index, count(j) contains the number of
! equivalent unfolded indices and s(j) stores the number of state variables
! in the index, i.e. if v = (x,u[,σ]) where x is the s-sized state variables
! vector and ind(j) = (α₁,…,αₘ), s(j) stores the number of coordinates αᵢ
! such that αᵢ ≤ s.
type folded_tensor
type(index), dimension(:), allocatable :: ind
integer, dimension(:), allocatable :: count
integer, dimension(:), allocatable :: s
real(real64), dimension(:,:), pointer, contiguous :: m
end type folded_tensor
contains
! Fills the ind and s members of a list of unfolded tensors g
! with a number of state variables ns. More precisely, g contains the
! information associated with the unfolded tensors [gᵥᵐ] with m=1,...,q
! 1. Filling g%ind
! The unfolded indices of [gᵥᵐ⁺¹] are the unfolded indices of [gᵥᵐ]
! with all numbers 1,...,n appended upfront. In other words, any
! index of [gᵥᵐ⁺¹] writes (α₁,α) with α₁∈ { 1, ..., n } and α
! an index of [gᵥᵐ]
! 2. Filling g%s
! s(α₁,α) is s(α)+1 if α₁ ≤ ns and s(α) otherwise
subroutine fill_list_unfolded_tensor(g, s, n)
type(unfolded_tensor), dimension(:), intent(inout) :: g
integer, intent(in) :: s, n ! number of state variables and size of v
integer :: q, m, j, k, l, length
q = size(g)
! Initialization
m = 1
length = n
allocate(g(1)%ind(length), g(1)%s(length))
do j=1,n
g(1)%ind(j) = index(1,j)
if (j<=s) then
g(1)%s(j) = 1
else
g(1)%s(j) = 0
end if
end do
! Transmission
do m=2,q
length = n*length
allocate(g(m)%ind(length), g(m)%s(length))
l = 1
do j=1,n
do k=1,size(g(m-1)%ind)
g(m)%ind(l) = index(m)
g(m)%ind(l)%coor(1) = j
g(m)%ind(l)%coor(2:m) = g(m-1)%ind(k)%coor
if (j<=s) then
g(m)%s(l) = g(m-1)%s(k)+1
else
g(m)%s(l) = g(m-1)%s(k)
end if
l = l+1
end do
end do
end do
end subroutine fill_list_unfolded_tensor
! Allocates the members ind, count and s of a tensor [gᵥᵐ] denoted g
! using the Pascal triangle p. n is the size of v.
subroutine allocate_folded_tensor(g, m, n, p)
type(folded_tensor), intent(inout) :: g
integer, intent(in) :: m, n !
type(pascal_triangle),intent(in) :: p
integer :: d
d = get(m,n+m-1,p)
allocate(g%ind(d), g%count(d), g%s(d))
end subroutine allocate_folded_tensor
! Fills the already allocated members ind, count and s of a tensor [gᵥᵐ]
! denoted g using the Pascal triangle p. n is the size of v.
! 1. Filling g%ind
! The algorithm to get the folded index associated with a folded offset
! relies on the definition of the lexicographic order.
! Consideri appended upfront.ng α = (α₁,…,αₘ) with αᵢ ∈ { 1, ..., n },
! the next index α' is such that there exists i that verifies
! αⱼ = αⱼ' for all j < i, αᵢ' > αᵢ. Note that all the coordinates
! αᵢ', ... , αₘ' need to be as small as the lexicographic order allows
! for α' to immediately follow α.
! Suppose j is the latest incremented coordinate:
! if αⱼ < n, then αⱼ' = αⱼ + 1
! otherwise αⱼ = n, set αₖ' = αⱼ₋₁ + 1 for all k ≥ j-1
! if αⱼ₋₁ = n, set j := j-1
! otherwise, set j := m
! 2. Filling g%count
! The algorithm to count the number of equivalent unfolded indices
! works as follows. A folded index can be written as
! α = (x₁, ..., x₁, ..., xₚ, ..., xₚ) such that x₁ < x₂ < ... < xₚ.
! Denote kᵢ the number of coordinates equal to xᵢ.
! The number of unfolded indices equivalent to α is
! c(α) = ⎛ m ⎞
! ⎝ k₁, k₂, ..., kₚ ⎠
! k is an array such that k(,j) contains the number of coordinates
! equal to for the folded index asociated with offset j.
! Suppose j is the latest incremented coordinate.
! If αⱼ < n, then αⱼ' = αⱼ + 1, k(αⱼ) := k(αⱼ)-1, k(αⱼ') := k(αⱼ')+1.
! In this case, c(α') = c(α)*(k(αⱼ)+1)/k(αⱼ')
! otherwise, αⱼ = n: set αₖ' = αⱼ₋₁ + 1 for all k ≥ j-1,
! k(αⱼ₋₁) := k(αⱼ₋₁)-1, k(n) := 0, k(αⱼ₋₁') = m-(j-1)+1
! In this case, we compute c(α') with the multinomial formula above
! 3. Filling g%s
! Suppose j is the latest incremented coordinate.
! If αⱼ < n, then αⱼ' = αⱼ + 1: s' = s-1 if αⱼ = ns and s'=s otherwise
! Otherwise, αⱼ = n: set αₖ' = αⱼ₋₁ + 1 for all k ≥ j-1. Thus,
! s' = m if αⱼ₋₁ < ns; s'=s-1 if αⱼ₋₁ = ns; s'=s otherwise
subroutine fill_folded_tensor(g, k, m, s, n, p)
type(folded_tensor), intent(inout) :: g
integer, contiguous, intent(inout) :: k(:,:)
integer, intent(in) :: m, s, n
type(pascal_triangle) :: p
integer :: j, lastinc
g%ind(1) = index(m, 1)
g%count(1) = 1
g%s(1) = m
k = 0
k(:,1) = 0
k(1,1) = m
j = 2
lastinc = m
do while (j <= size(g%ind))
g%ind(j) = index(g%ind(j-1)%coor)
k(:,j) = k(:,j-1)
if (g%ind(j-1)%coor(lastinc) == n) then
g%ind(j)%coor(lastinc-1:m) = g%ind(j-1)%coor(lastinc-1)+1
k(g%ind(j-1)%coor(lastinc-1),j) = k(g%ind(j-1)%coor(lastinc-1),j-1)-1
k(n,j) = 0
k(g%ind(j)%coor(lastinc-1),j) = m - (lastinc-1) + 1
g%count(j) = multinomial(k(:,j),m,p)
if (g%ind(j-1)%coor(lastinc-1) < s) then
g%s(j) = m
elseif (g%ind(j-1)%coor(lastinc-1) == s) then
g%s(j) = g%s(j-1)-1
else
g%s(j) = g%s(j-1)
end if
if (g%ind(j)%coor(m) == n) then
lastinc = lastinc-1
else
lastinc = m
end if
else
g%ind(j)%coor(lastinc) = g%ind(j-1)%coor(lastinc)+1
k(g%ind(j)%coor(lastinc),j) = k(g%ind(j)%coor(lastinc),j-1)+1
g%count(j) = g%count(j-1)*k(g%ind(j-1)%coor(lastinc),j)/k(g%ind(j)%coor(lastinc),j)
k(g%ind(j-1)%coor(lastinc),j) = k(g%ind(j-1)%coor(lastinc),j-1)-1
if (g%ind(j-1)%coor(lastinc) == s) then
g%s(j) = g%s(j-1)-1
else
g%s(j) = g%s(j-1)
end if
end if
j = j+1
end do
end subroutine
! Fills a tensor [gᵥᵐ] given the tensor [gᵥᵐ⁺¹]
! 1. Filling g%ind
! If (α₁,…,αₘ₊₁) is a folded index for [gᵥᵐ⁺¹], then (α₂,…,αₘ₊₁) is a folded
! index of [gᵥᵐ]. The folded indices of [gᵥᵐ] are thus the tails of the first
! ⎛ m+n-1 ⎞ folded indices of [gᵥᵐ⁺¹]
! ⎝ m ⎠
! 2. Filling g%count
! If α=(α₁,…,αₘ₊₁) is a folded index for [gᵥᵐ⁺¹], then α'=(α₂,…,αₘ₊₁) is a
! folded index of [gᵥᵐ]. We thus have c(α') = c(α)*k(α₁)/(m+1) and
! perform k(α₁):=k(α₁)-1.
! 3. Filling g%s
! If α=(α₁,…,αₘ₊₁) is a folded index for [gᵥᵐ⁺¹], then α'=(α₂,…,αₘ₊₁) is a
! folded index of [gᵥᵐ]. We thus have s(α') = s(α)-1 if α₁ ≤ s and
! s(α') = s(α) otherwise
subroutine fill_folded_tensor_backward(g_m, k, g_mp1, m, s)
type(folded_tensor), intent(inout) :: g_m ! tensor [gᵥᵐ]
! k array from previous fill_folded_tensor_backward or
! fill_folded_tensor call. See definition in fill_folded_tensor
integer, contiguous, intent(inout) :: k(:,:)
type(folded_tensor), intent(in) :: g_mp1 ! tensor [gᵥᵐ⁺¹]
integer, intent(in) :: m, s ! s is the number of state variables
integer :: j
do j=1,size(g_m%ind)
g_m%ind(j)%coor = g_mp1%ind(j)%coor(2:m+1)
g_m%count(j) = g_mp1%count(j)*k(g_mp1%ind(j)%coor(1),j)/(m+1)
k(g_mp1%ind(j)%coor(1),j) = k(g_mp1%ind(j)%coor(1),j)-1
if (g_mp1%ind(j)%coor(1) <= s) then
g_m%s(j) = g_mp1%s(j)-1
else
g_m%s(j) = g_mp1%s(j)
end if
end do
end subroutine
! Fills the ind, count and s members of a list of folded tensors g
! with a number of state variables ns. More precisely, g contains the
! information associated with the folded tensors [gᵥᵐ] with m=1,...,q
subroutine fill_list_folded_tensor(g, s, n, p)
type(folded_tensor), dimension(:), intent(inout) :: g
integer, intent(in) :: s, n ! number of state variables and size of v
type(pascal_triangle) :: p
integer :: q, m
integer, dimension(:,:), allocatable :: k
q = size(g)
! Case m = q
m = q
call allocate_folded_tensor(g(m), m, n, p)
allocate(k(n,size(g(m)%ind)))
call fill_folded_tensor(g(m), k, m, s, n, p)
! Case m < q
do m=q-1,1,-1
call allocate_folded_tensor(g(m), m, n, p)
call fill_folded_tensor_backward(g(m), k, g(m+1), m, s)
end do
end subroutine fill_list_folded_tensor
end module tensors
! After putting a comment on MATLAB-related lines
! gfortran -o tensors tensors.f08 sort.f08 pascal.f08 partitions.f08
! ./tensors
! program test
! use pascal
! use tensors
! implicit none (type, external)
! type(pascal_triangle) :: p
! type(folded_tensor) :: g, h
! integer :: n, m, s, i, j
! integer, allocatable, dimension(:,:) :: k
! n = 3
! m = 3
! s = 2
! p = pascal_triangle(n+m-1)
! call allocate_folded_tensor(g, m, n, p)
! allocate(k(n, size(g%ind)))
! call fill_folded_tensor(g, k, c_null_ptr, m, s, n, p)
! ! List of folded indices, counts of equivalent unfolded indices
! ! and counts of state variables of [gᵥᵐ]
! ! Folded indices and offsets
! ! 0,0,0 1 1,1,1 7 2,2,2 10
! ! 0,0,1 2 1,1,2 8
! ! 0,0,2 3 1,2,2 9
! ! 0,1,1 4
! ! 0,1,2 5
! ! 0,2,2 6
! do i=1, size(k,2)
! print '(3i2)', (g%ind(i)%coor(j), j=1,m)
! print '(i2)', g%count(i)
! print '(i2)', g%s(i)
! end do
! call allocate_folded_tensor(h, m-1, n, p)
! call fill_folded_tensor_backward(h, k, g, c_null_ptr, m-1, s)
! ! List of folded indices, counts of equivalent unfolded indices
! ! and counts of state variables of [gᵥᵐ⁻¹]
! do i=1, size(h%ind)
! print '(2i2)', (h%ind(i)%coor(j), j=1,m-1)
! print '(i2)', h%count(i)
! print '(i2)', h%s(i)
! end do
! end program test