|
| 1 | +using GSL |
| 2 | + |
| 3 | +function permutations_in_Sn(n::Integer) |
| 4 | + P = permutation_calloc(n) |
| 5 | + while true |
| 6 | + produce(P) |
| 7 | + try permutation_next(P) catch break end |
| 8 | + end |
| 9 | +end |
| 10 | + |
| 11 | +function compose(P::Ptr{gsl_permutation}, Q::Ptr{gsl_permutation}) |
| 12 | + #Compose the permutations |
| 13 | + n=convert(Int64, permutation_size(P)) |
| 14 | + @assert n==permutation_size(Q) |
| 15 | + x=permutation_alloc(n) |
| 16 | + Pv = [x+1 for x in pointer_to_array(permutation_data(P), (n,))] |
| 17 | + Qv = [x+1 for x in pointer_to_array(permutation_data(Q), (n,))] |
| 18 | + Xv = [Qv[i] for i in Pv] |
| 19 | + for i=1:n |
| 20 | + unsafe_assign(permutation_data(x), Xv[i]-1, i) |
| 21 | + end |
| 22 | + permutation_valid(x) |
| 23 | + x |
| 24 | +end |
| 25 | + |
| 26 | +#Compute cycle structure, i.e. lengths of each cycle in the cycle factorization of the permutatation |
| 27 | +function cycle_structure(P::Ptr{gsl_permutation}) |
| 28 | + n=convert(Int64, permutation_size(P)) |
| 29 | + Pcanon = permutation_linear_to_canonical(P) |
| 30 | + PCANON = data(Pcanon) |
| 31 | + HaveNewCycleAtPos = [PCANON[i]>PCANON[i+1] for i=1:n-1] |
| 32 | + cycleindices = [1] |
| 33 | + for i=1:n-1 if HaveNewCycleAtPos[i] cycleindices = [cycleindices; i+1] end end |
| 34 | + cycleindices = [cycleindices; n+1] |
| 35 | + cyclestructure = [cycleindices[i+1]-cycleindices[i] for i=1:length(cycleindices)-1] |
| 36 | + @assert sum(cyclestructure) == n |
| 37 | + cyclestructure |
| 38 | +end |
| 39 | + |
| 40 | +#Returns a vector of indices (starting from 1 in the Julia convention) |
| 41 | +data(P::Ptr{gsl_permutation}) = [convert(Int64, x)+1 for x in pointer_to_array(permutation_data(P), (convert(Int64, permutation_size(P)) ,))] |
| 42 | + |
| 43 | + |
| 44 | +# In random matrix theory one often encounters expressions of the form |
| 45 | +# |
| 46 | +#X = trace(Q * A * Q' * B) |
| 47 | +# |
| 48 | +#where A and B are deterministic matrices with fixed numerical matrix entries and Q is a random matrix that does not have explicitly defined matrix elements. Instead, one takes an expectation over of expressions of this form and this "integrates out" the Qs to produce a numeric result. |
| 49 | +# |
| 50 | +#expectation(X) #= some number |
| 51 | +# |
| 52 | +#I'm curious how far one can implement, in Julia, a data type for Q and expectation() method for which expressions of this form can be written natively as code. This probably strays into the larger question of how much symbolic algebra can be written and handled in Julia, but I think this would be have real payoffs for making possible extremely elegant manipulations of random matrices that doesn't exist in any known computer language. |
| 53 | +# |
| 54 | +#Any thoughts on how feasible this is? Perhaps to begin with, it would be nice to have an expectation() that would work on arbitrary strings of products of matrices. Probably this would involve taking an input expression argument Would like to hear some stuff on t would be nice to start with just how one can write arbitrary products of matrices My initial thoughts are to declare Q::MyRandomMatrix with the type MyRandomMatrix<:AbstractMatrix and to write an expectation(Ex::Expr) function that at first blush traverses the expression Ex, finds a * that takes at least one MyRandomMatrix in its arguments and applies the appropriate substitution. |
| 55 | +# |
| 56 | +#So... 1) How feasible is this and 2) How do I traverse the expression in the way described in the previous paragraph? The "Metaprogramming" manual is somewhat sparse on this other than saying .args[], but that doesn't quite help |
| 57 | + |
| 58 | +function Expectation(X::Expr) |
| 59 | + if X.head != :call |
| 60 | + error(string("Unexpected type of expression: ", X.head)) |
| 61 | + end |
| 62 | + |
| 63 | + n = length(X.args) - 1 |
| 64 | + if n < 1 return eval(X) end #nothing to do Haar-wise |
| 65 | + |
| 66 | + if X.args[1] != :* |
| 67 | + error("Unexpected expression, only products supported") |
| 68 | + end |
| 69 | + |
| 70 | + # Parse expression involving products of matrices to extract the |
| 71 | + # positions of Haar matrices and their ctransposes |
| 72 | + Qidx=[] #Indices for Haar matrices |
| 73 | + Qpidx=[] #Indices for ctranspose of Haar matrices |
| 74 | + Others=[] |
| 75 | + MyQ=None |
| 76 | + for i=1:n |
| 77 | + thingy=X.args[i+1] |
| 78 | + if typeof(thingy)==Symbol |
| 79 | + matrixtype = typeof(eval(thingy)) |
| 80 | + if matrixtype == HaarMatrix |
| 81 | + if MyQ==None MyQ=thingy end |
| 82 | + if MyQ == thingy |
| 83 | + Qidx=[Qidx; i] |
| 84 | + else |
| 85 | + println(string("only one instance of HaarMatrix supported, skipping the other guy ", thingy)) |
| 86 | + end |
| 87 | + else |
| 88 | + Others = [Others; (thingy, i, i+1)] |
| 89 | + end |
| 90 | + println(i, ' ', thingy, "::", typeof(eval(thingy))) |
| 91 | + elseif typeof(thingy)==Expr |
| 92 | + println(i, ' ', thingy, "::Expr") |
| 93 | + if thingy.head==symbol('\'') && length(thingy.args)>=1 #Maybe this is a Q' |
| 94 | + if typeof(thingy.args[1])==Symbol && typeof(eval(thingy.args[1]))==HaarMatrix |
| 95 | + println("Here is a Qtranspose") |
| 96 | + Qpidx=[Qpidx; i] |
| 97 | + end |
| 98 | + end |
| 99 | + else |
| 100 | + error(string("Unexpected token ", thingy ," of type ", typeof(thingy))) |
| 101 | + end |
| 102 | + end |
| 103 | + |
| 104 | + if length(Qidx) == length(Qpidx) == 0 return eval(X) end #nothing to do Haar-wise |
| 105 | + println(MyQ, " is in places ", Qidx) |
| 106 | + println(MyQ, "' is in places ", Qpidx) |
| 107 | + |
| 108 | + n = length(Qidx) |
| 109 | + #If there are different Qs and Q's, the answer is a big fat 0 |
| 110 | + if n != length(Qpidx) return zeros(size(eval(X.args[2]),1)...) end |
| 111 | + |
| 112 | + ################################## |
| 113 | + # Step 2. Enumerate permutations # |
| 114 | + ################################## |
| 115 | + AllTerms = :(+(A*B)) |
| 116 | + delete!(AllTerms.args, 2) |
| 117 | + println("BEGINS WITH ", AllTerms) |
| 118 | + for sigma in @task permutations_in_Sn(n) |
| 119 | + for tau in @task permutations_in_Sn(n) |
| 120 | + sigma_inv=permutation_inverse(sigma) |
| 121 | + #Compose the permutations |
| 122 | + perm=compose(sigma_inv, tau) |
| 123 | + |
| 124 | + #Compute cycle structure, i.e. lengths of each cycle in the cycle |
| 125 | + #factorization of the permutatation |
| 126 | + cyclestruct = cycle_structure(perm) |
| 127 | + Qr = Int64[n+1 for n in Qidx] |
| 128 | + Qpr= Int64[Qpidx[n]+1 for n in data(tau)] |
| 129 | + #Consolidate deltas |
| 130 | + Deltas=Dict() |
| 131 | + for i=1:n |
| 132 | + Deltas[Qr[i]] = Qpr[i] |
| 133 | + Deltas[Qidx[i]] = Qpidx[data(sigma)[i]] |
| 134 | + end |
| 135 | + #Print deltas |
| 136 | + print("V(", cyclestruct, ") ") |
| 137 | + for k in keys(Deltas) |
| 138 | + print("δ(",k,",",Deltas[k],") ") |
| 139 | + end |
| 140 | + for (Symb, col_idx, row_idx) in Others |
| 141 | + print(Symb,"(",col_idx,",",row_idx,") ") |
| 142 | + end |
| 143 | + println() |
| 144 | + print("= V(", cyclestruct, ") ") |
| 145 | + #Substitute |
| 146 | + ReindexedSymbols = [] |
| 147 | + for (Symb, col_idx, row_idx) in Others |
| 148 | + new_col, new_row = col_idx, row_idx |
| 149 | + if has(Deltas, col_idx) new_col = Deltas[col_idx] end |
| 150 | + if has(Deltas, row_idx) new_row = Deltas[row_idx] end |
| 151 | + print(Symb,"(",new_col,",",new_row,") ") |
| 152 | + ReindexedSymbols = [ReindexedSymbols; (Symb, new_col, new_row)] |
| 153 | + end |
| 154 | + println() |
| 155 | + println(ReindexedSymbols) |
| 156 | + println() |
| 157 | + #Reconstruct expression |
| 158 | + println("START PARSING") |
| 159 | + Symb, left_idx, right_idx = delete!(ReindexedSymbols, length(ReindexedSymbols)) |
| 160 | + Expression={{{Symb}, left_idx, right_idx}} |
| 161 | + println("E =", Expression) |
| 162 | + while length(ReindexedSymbols) > 0 |
| 163 | + pop_idx = expr_idx = do_transpose = is_left = nothing |
| 164 | + for expr_iter in enumerate(Expression) |
| 165 | + expr_idx, expr_string = expr_iter |
| 166 | + _, left_idx, right_idx = expr_string |
| 167 | + for iter in enumerate(ReindexedSymbols) |
| 168 | + idx, data = iter |
| 169 | + Symb, col_idx, row_idx = data |
| 170 | + if row_idx == left_idx |
| 171 | + pop_idx = idx |
| 172 | + do_transpose = false |
| 173 | + is_left = true |
| 174 | + println("Case A") |
| 175 | + break |
| 176 | + elseif col_idx == right_idx |
| 177 | + pop_idx = idx |
| 178 | + do_transpose = false |
| 179 | + is_left = false |
| 180 | + println("Case B") |
| 181 | + break |
| 182 | + elseif row_idx == right_idx |
| 183 | + pop_idx = idx |
| 184 | + do_transpose = true |
| 185 | + is_left = false |
| 186 | + println("Case C") |
| 187 | + break |
| 188 | + elseif col_idx == left_idx |
| 189 | + pop_idx = idx |
| 190 | + do_transpose = true |
| 191 | + is_left = true |
| 192 | + println("Case D") |
| 193 | + break |
| 194 | + end |
| 195 | + end |
| 196 | + if pop_idx != nothing break end |
| 197 | + end |
| 198 | + println("Terms left = ", ReindexedSymbols) |
| 199 | + if pop_idx == nothing #Found nothing, start new expression blob |
| 200 | + println("NEW EXPRBLOB: The term is =", ReindexedSymbols[1]) |
| 201 | + Symb, left_idx, right_idx = delete!(ReindexedSymbols, 1) |
| 202 | + insert!(Expression, length(Expression)+1, {{Symb}, left_idx, right_idx}) |
| 203 | + else #Found something |
| 204 | + println("The term is =", ReindexedSymbols[pop_idx]) |
| 205 | + Symb, col_idx, row_idx = delete!(ReindexedSymbols, pop_idx) |
| 206 | + Term = do_transpose ? Expr(symbol("'"), Symb) : Symb |
| 207 | + println(Expression[expr_idx]) |
| 208 | + println(Expression[expr_idx][1]) |
| 209 | + if is_left |
| 210 | + println("insert left") |
| 211 | + insert!(Expression[expr_idx][1], 1, Term) |
| 212 | + Expression[expr_idx][2] = do_transpose ? row_idx : col_idx |
| 213 | + else |
| 214 | + println("insert right") |
| 215 | + insert!(Expression[expr_idx][1], |
| 216 | + length(Expression[expr_idx][1])+1, Term) |
| 217 | + Expression[expr_idx][3] = do_transpose ? col_idx : row_idx |
| 218 | + end |
| 219 | + end |
| 220 | + println("E=",Expression) |
| 221 | + end |
| 222 | + println("DONE PARSING TREE") |
| 223 | + |
| 224 | + # Evaluate closed cycles |
| 225 | + NewExpression=:(+(A*B)) |
| 226 | + delete!(NewExpression.args, 2) |
| 227 | + println("HIHIHI", NewExpression.args) |
| 228 | + for ExprBlob in Expression |
| 229 | + if ExprBlob[2]==ExprBlob[3] #Have a cycle; this is a trace |
| 230 | + println(ExprBlob[1]) |
| 231 | + ex=:(trace(A*B)) |
| 232 | + println(ex.args[2].args) |
| 233 | + ex.args[2].args={:*; ExprBlob[1]...} |
| 234 | + println(ex) |
| 235 | + println(ex.args) |
| 236 | + insert!(NewExpression.args, length(NewExpression.args)+1, ex) |
| 237 | + else #Not a cycle, regular chain of multiplications |
| 238 | + ex=:(A*B) |
| 239 | + ex.args={:*; ExprBlob[1]...} |
| 240 | + insert!(NewExpression.args, length(NewExpression.args)+1, ex) |
| 241 | + end |
| 242 | + end |
| 243 | + println("Final expression is ", NewExpression) |
| 244 | + insert!(AllTerms.args, length(AllTerms.args)+1, NewExpression) |
| 245 | + end |
| 246 | + end |
| 247 | + println("THE ANSWER IS ", AllTerms) |
| 248 | + eval(AllTerms) |
| 249 | +end |
| 250 | + |
| 251 | + |
| 252 | +#Iterate over partitions of n in lexicographic order |
| 253 | +function part(n::Integer) |
| 254 | + if n==0 produce([]) end |
| 255 | + if n<=0 return end |
| 256 | + for p in @task part(n-1) |
| 257 | + p = [p; 1] |
| 258 | + produce(p) |
| 259 | + p = p[1:end-1] |
| 260 | + if length(p) == 1 || (length(p)>1 && p[end]<p[end-1]) |
| 261 | + p[end] += 1 |
| 262 | + produce(p) |
| 263 | + p[end] -= 1 |
| 264 | + end |
| 265 | + end |
| 266 | +end |
| 267 | + |
| 268 | + |
| 269 | + |
| 270 | +############### |
| 271 | +# SANDBOX |
| 272 | +N=5 |
| 273 | +A=randn(N,N) |
| 274 | +B=randn(N,N) |
| 275 | +type HaarMatrix |
| 276 | + beta::Real |
| 277 | +end |
| 278 | +Q = HaarMatrix(2) |
| 279 | + |
| 280 | + |
| 281 | +println("Case 1") |
| 282 | +println("E(A*B) = ", Expectation(:(A*B))) |
| 283 | +println("A*B/N = ", A*B) |
| 284 | + |
| 285 | +println("Case 2") |
| 286 | +println("tr(A)*tr(B)/N = ", trace(A)*trace(B)/N) |
| 287 | +#println("E(A*Q*B*Q') = ", Expectation(:(A*Q*B*Q'))) |
| 288 | +println("E.tr(A*Q*B*Q') = ", trace(Expectation(:(A*Q*B*Q')))) |
| 289 | + |
| 290 | +#println("Case 3") |
| 291 | +#println(Expectation(:(A*B)) == A*B) |
| 292 | +#println("E(A*Q*B*Q'*A*Q*B*Q') = ", Expectation(:(A*Q*B*Q'*A*Q*B*Q'))) |
| 293 | + |
0 commit comments