Trouble creating a real-valued vector out of elements of multiple complex arrays

Hello, I am doing an optimization problem and want to extend a minimizing vector to become an initial guess for the same problem in a larger parameter space (one can think of it as being in a larger dimension), with all extra parameters being zero. I am having some trouble figuring out the code to do this, although I believe I am most of the way there:

First I create a function which will take a real valued vector and create complex matrices A4 and B4

I then create the same matrices in the larger parameter space out of zeros (A6, B6), and replace their upper left corner elements with that of A4 and B6:

function Initializer(x::Vector)
   
    A4 = reshape(Complex.(x[1:2:7],x[2:2:8]),2,2)
    B4 = reshape(Complex.(x[9:2:15],x[10:2:16]),2,2)

    A6 = reshape(Complex.(zeros(9),zeros(9)),3,3)
    B6 = reshape(Complex.(zeros(9),zeros(9)),3,3)

    A6[1:2,1:2] = A4
    B6[1:2,1:2] = B4
    
    global X0 = ?

end

A6 and B6 are exactly what I want, but I need to be able to create them as they are in a different function. So I want to create a global variable (call it X0) which will be able to populate a new function in the exact way that A6 and B6 are created in “Initializer()”, which would look like the following:

function Create(x::Vector)
    
    A6 = reshape(Complex.(x[1:2:17],x[2:2:18]),3,3)
    B6 = reshape(Complex.(x[19:2:35],x[20:2:36]),3,3)
end

Where Create(X0) would give me the same A6 and B6 that I am left with at the end of the Initialize function. Any help would be greatly appreciated!

*Edited to make less confusing

I don’t understand what you are trying to do here. Please provide an MWE, with desired inputs and outputs.

Also, note that the second Initializer overwrites the first (is this intended), and globals are unlikely to be the right approach.

3 Likes

I wrote the initializer function twice in order to illustrate my point more clearly, they are not meant to be two separate functions.

This may be more clear for what I want:

  1. Pass a initial vector to Initializer()
  2. Initializer creates A6 and B6, which it uses to create an output vector X0
  3. Create(X0) then creates A6 and B6 exactly as initializer created them

My problem is just the syntax to create a vector X0 out of the values of the complex matrices A6 and B6 so that it will do what I describe above. What are my options for converting a complex array into a real valued vector? Using reshape() I cannot separate the real and imaginary parts: (a+bim) into [a,b,…].

Unfortunately This is as close to a MWE as I can do without knowing the syntax I am asking for:

function Initializer(x::Vector)
   
    A4 = reshape(Complex.(x[1:2:7],x[2:2:8]),2,2)
    B4 = reshape(Complex.(x[9:2:15],x[10:2:16]),2,2)

    A6 = reshape(Complex.(zeros(9),zeros(9)),3,3)
    B6 = reshape(Complex.(zeros(9),zeros(9)),3,3)

    A6[1:2,1:2] = A4
    B6[1:2,1:2] = B4
    @show A6, B6
    
    #X0 = ? syntax here to create X0 vector out of real values of matrices A6 and B6 for use in Create()

end
function Create(x::Vector)
    
    A6 = reshape(Complex.(x[1:2:17],x[2:2:18]),3,3)
    B6 = reshape(Complex.(x[19:2:35],x[20:2:36]),3,3)
    @show A6, B6
end
Create(X0) #creates A6 and B6 as they appeared in Initializer

*Edited to make more clear what I am asking

You have an error in Create:

x[20:2:35]

has 8 elements, but you need 9 for an 3x3 matrix

Thank you - I have fixed the typo.

x0=collect(Iterators.flatten([ x[1:4],zeros(2),x[5:8],zeros(8),x[9:12],zeros(2),x[13:16],zeros(8) ]))

non generic solution

I’m afraid I’ll have to read your question a few more times later in order to understand it, but in the mean time I have two minor remarks:

A6 = reshape(Complex.(zeros(9),zeros(9)),3,3)
B6 = reshape(Complex.(zeros(9),zeros(9)),3,3)

It’s much faster and more idiomatic to write this with the zeros function

A6 = zeros(ComplexF64, 3, 3)
B6 = zeros(ComplexF64, 3, 3)

Did you intend them to be complex integers or floats, though?

A4 = reshape(Complex.(x[1:2:7],x[2:2:8]),2,2)
B4 = reshape(Complex.(x[9:2:15],x[10:2:16]),2,2)

A faster and simpler way here is to use reinterpret:

AB = reinterpret(Complex{Int}, x)
A4 = reshape(AB[1:4], 2, 2)  # if you could also use `view` here, that's even faster
B4 = reshape(AB[5:end], 2, 2)
1 Like

Thank you, however I admit I was hoping there would be a generic solution which can be extrapolated to different cases easily - a way to basically reverse the process I did in creating A4 and B4 from a real-value vector - but this time creating a vector out of A6 and B6

OP, you are editing your question quite eratic. This is not good for readers who try to understand whats going on.

And, yes, it is quite easy to move from the non generic solution to a generic one. Just add index variables and exchange the values to the variables. Should be doable now.

1 Like

Thank you for the cleaner syntax, and I intended them to be floats. Also, using reinterpret is very nice and I will use that in the future, unfortunately I have all of my minimizing vectors already which were created using this syntax so I need to stay with this construction if possible:

A4 = reshape(Complex.(x[1:2:7],x[2:2:8]),2,2)
B4 = reshape(Complex.(x[9:2:15],x[10:2:16]),2,2)

For all others, this is the original problem:

x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

function Initializer(x::Vector)
   
    A4 = reshape(Complex.(x[1:2:7],x[2:2:8]),2,2)
    B4 = reshape(Complex.(x[9:2:15],x[10:2:16]),2,2)

    A6 = reshape(Complex.(zeros(9),zeros(9)),3,3)
    B6 = reshape(Complex.(zeros(9),zeros(9)),3,3)

    A6[1:2,1:2] = A4
    B6[1:2,1:2] = B4
    
	(A6,B6)
end


function Create(x::Vector)
    
    A6 = reshape(Complex.(x[1:2:17],x[2:2:18]),3,3)
    B6 = reshape(Complex.(x[19:2:35],x[20:2:36]),3,3)
	
	(A6,B6)
end

x0=collect(Iterators.flatten([ x[1:4],zeros(2),x[5:8],zeros(8),x[9:12],zeros(2),x[13:16],zeros(8) ]))

(t1,t2)=Initializer(x)
(p1,p2)=Create(x0)

t1==p1

t2==p2

Now it needs to be made generic, which is probably other sizes of x and other dimensions of the result matrices (other than 3x3).

1 Like

Okay, thank you

@CPPhysics what we need now, is the generic input you are looking for. Like
x= some arbitrary length vector? or what else you have in mind.

The idea is that I already have the vectors for the A4 and B4 case and I am trying to extend them to the A6 and B6 case, replacing all of the “extra” vector elements with zeros. So the generic input is just some vector of real floats, length 16 in this case (I shouldn’t have chosen the vector x that I did with integers, x = randn(16) is more like that I’d have to start with.

It doesn’t matter if x is 16-element Array{Int64,1} or 16-element Array{Float64,1}

May I ask:

Is it, that you don’t want to create x0 out of x but from A4 and B4?

I want to create X0 out of x, the reason I bring A4 and B4 into it is to illustrate what I need in terms of the matrices, and also because I didn’t understand any other way to do it. These are simplified functions than those I actually use however this may put things in context:

I use an optimization package to minimize a function created out of A4 and B4. This optimization run outputs a minimization vector “minx”. I then want to convert this minx into a new, larger vector X0 which will be able to fill larger matrices A6 and B6 in the way that Initializer() creates them (A4,B4 in the top left corner of A6,B6 with zeros in the remaining matrix elements). I will use this X0 as a starting point for my optimization run of the function created out of A6 and B6. I am looking for a way to extend results from a lower parameter space to a higher parameter space. For my problem I know that this will work mathematically if it is carried out the way I describe.

ok, anyways, to answer the original question as “creating a real-valued vector out of elements of multiple complex arrays”:

A4 is made of the first 8 elements of x. To get back from A4 to x[1:8] you may use the following code:

a=[ real.(A4) imag.(A4) ]
x2=a[collect(Iterators.flatten([s:4:length(a) for s in 1:4]))]
x[1:8]==x2

I just want to get rid of this, as it was something I considered as part of the solution during my way through this all. Don’t want to confuse you, just ignore this, if you don’t need it.

1 Like

Yes, thank you, the ‘Iterators.flatten()’ function in this context is very helpful; regardless of anything else I can use this to get to the syntax I need. I asked my question so thoroughly/openly to see what options I had for creating simple syntax for my problem, but in the end this is what I need and I will move forward from here. Thank you for working through this with me.

1 Like

I’m just going to go through a few manipulations in script form first and perhaps this will give you some ideas. Let’s initialize a vector numbered 1 to 16 for clarity.

julia> xi = collect(1:16)
16-element Array{Int64,1}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16

I’m going to assume it is known apriori that there are two complex matrices and that they are square. Additionally the expanded matrix you need is one greater in each dimension.

In case those assumptions are incorrect, you can recode the following. Please do explain what your assumptions are when asking a question or that there are no assumptions.

julia>
n_matrices = 2
# divide by two below because we need two entries for complex numbers
n_rows = Int64(sqrt(length(xi)/n_matrices/2))
n_cols = n_rows
n_rows_expanded = n_rows+1
n_cols_expanded = n_cols+1
p = params = (nm=n_matrices,nr=n_rows,nc=n_cols,nre=n_rows_expanded,nce=n_cols_expanded)

(nm = 2, nr = 2, nc = 2, nre = 3, nce = 3)

Using the params above, we are now going to interpret xi:

julia> xir = reshape(xi,(2,p.nr,p.nc,p.nm))

2×2×2×2 Array{Int64,4}:
[:, :, 1, 1] =
 1  3
 2  4

[:, :, 2, 1] =
 5  7
 6  8

[:, :, 1, 2] =
  9  11
 10  12

[:, :, 2, 2] =
 13  15
 14  16

julia> A = xir[1,:,:,1] + im*xir[2,:,:,1]
2×2 Array{Complex{Int64},2}:
 1+2im  5+6im
 3+4im  7+8im

julia> B = xir[1,:,:,2] + im*xir[2,:,:,2]
2×2 Array{Complex{Int64},2}:
  9+10im  13+14im
 11+12im  15+16im

Now let’s add zeros around the matrices. The term for this is called padding. There are several options for this such padarray or PaddedView. Let’s use the latter:

julia > using PaddedViews

julia> Ae = PaddedView(0,A,(p.nre,p.nce))
3×3 PaddedView(0 + 0im, ::Array{Complex{Int64},2}, (Base.OneTo(3), Base.OneTo(3))) with eltype Complex{Int64}:
 1+2im  5+6im  0+0im
 3+4im  7+8im  0+0im
 0+0im  0+0im  0+0im

julia> Be = PaddedView(0,B,(p.nre,p.nce))
3×3 PaddedView(0 + 0im, ::Array{Complex{Int64},2}, (Base.OneTo(3), Base.OneTo(3))) with eltype Complex{Int64}:
  9+10im  13+14im  0+0im
 11+12im  15+16im  0+0im
  0+0im    0+0im   0+0im

If we ever needed plain old complex arrays, we can use collect.

julia> collect(Ae)
3×3 Array{Complex{Int64},2}:
 1+2im  5+6im  0+0im
 3+4im  7+8im  0+0im
 0+0im  0+0im  0+0im

julia> collect(Be)
3×3 Array{Complex{Int64},2}:
  9+10im  13+14im  0+0im
 11+12im  15+16im  0+0im
  0+0im    0+0im   0+0im

Now let’s go the other way and attempt to compact Ae and Be down back to a vector.

julia> X0 = cat(Ae,Be,dims=3)
3×3×2 Array{Complex{Int64},3}:
[:, :, 1] =
 1+2im  5+6im  0+0im
 3+4im  7+8im  0+0im
 0+0im  0+0im  0+0im

[:, :, 2] =
  9+10im  13+14im  0+0im
 11+12im  15+16im  0+0im
  0+0im    0+0im   0+0im

julia> X0 = cat(real.(X0),imag.(X0),dims=4)
3×3×2×2 Array{Int64,4}:
[:, :, 1, 1] =
 1  5  0
 3  7  0
 0  0  0

[:, :, 2, 1] =
  9  13  0
 11  15  0
  0   0  0

[:, :, 1, 2] =
 2  6  0
 4  8  0
 0  0  0

[:, :, 2, 2] =
 10  14  0
 12  16  0
  0   0  0

julia> X0 = permutedims(X0,(4,1,2,3))
2×2×3×3 Array{Int64,4}:
[:, :, 1, 1] =
 1   2
 9  10

[:, :, 2, 1] =
  3   4
 11  12

[:, :, 3, 1] =
 0  0
 0  0

[:, :, 1, 2] =
  5   6
 13  14

[:, :, 2, 2] =
  7   8
 15  16

[:, :, 3, 2] =
 0  0
 0  0

[:, :, 1, 3] =
 0  0
 0  0

[:, :, 2, 3] =
 0  0
 0  0

[:, :, 3, 3] =
 0  0
 0  0

julia> X0 = vec(X0)
36-element Array{Int64,1}:
  1
  2
  3
  4
  0
  0
  5
  6
  7
  8
  ⋮
 16
  0
  0
  0
  0
  0
  0
  0
  0

Great now we compacted Ae and Be back into a vector. Now depending on how things are structured and how the optimization routines work, we may need to also encode the parameters in the vector:

julia> X0_with_params = [X0; collect(params)]
41-element Array{Int64,1}:
 1
 2
 3
 4
 0
 0
 5
 6
 7
 8
 ⋮
 0
 0
 0
 0
 2
 2
 2
 3
 3

julia> param_keys = keys(p)

Recreating Ae and Be is just a matter of unpacking the parameters and repeating the earlier steps.

julia> p = (;zip(param_keys,X0_with_params[end-4:end])...)
(nm = 2, nr = 2, nc = 2, nre = 3, nce = 3)
julia> X0 = reshape(X0_with_params[1:end-5],(2,p.nre,p.nce,p.nm));
julia> Ae_recreated = X0[1,:,:,1] + im*X0[2,:,:,1]
3×3 Array{Complex{Int64},2}:
 1+2im  5+6im  0+0im
 3+4im  7+8im  0+0im
 0+0im  0+0im  0+0im

julia> Be_recreated = X0[1,:,:,2] + im*X0[2,:,:,2]
3×3 Array{Complex{Int64},2}:
  9+10im  13+14im  0+0im
 11+12im  15+16im  0+0im
  0+0im    0+0im   0+0im

julia> Ae == Ae_recreated
true

julia> Be == Be_recreated
true
1 Like