Package daredare :: Package extern :: Module toolkithelpers
[hide private]
[frames] | no frames]

Source Code for Module daredare.extern.toolkithelpers

  1  # toolkithelpers: 
  2  # 1) qzswitch port 
  3  # 2) qzdiv port 
  4  # 3) the numpy-octave bridge class 
  5   
  6  from numpy import mat, c_, r_, multiply, where, sqrt, newaxis 
  7  from numpy.linalg import solve 
  8  from numpy.matlib import diag 
  9   
10 -def qzswitch(i, A2, B2, Q, Z):
11 #print i, A2, B2, Q, Z 12 Aout = A2.copy(); Bout = B2.copy(); Qout = Q.copy(); Zout = Z.copy() 13 ix = i-1 # from 1-based to 0-based indexing... 14 # use all 1x1-matrices for convenient conjugate-transpose even if real: 15 a = mat(A2[ix, ix]); d = mat(B2[ix, ix]); b = mat(A2[ix, ix+1]); 16 e = mat(B2[ix, ix+1]); c = mat(A2[ix+1, ix+1]); f = mat(B2[ix+1, ix+1]) 17 wz = c_[c*e - f*b, (c*d - f*a).H] 18 xy = c_[(b*d - e*a).H, (c*d - f*a).H] 19 n = sqrt(wz*wz.H) 20 m = sqrt(xy*xy.H) 21 if n[0,0] == 0: return (Aout, Bout, Qout, Zout) 22 wz = solve(n, wz) 23 xy = solve(m, xy) 24 wz = r_[ wz, \ 25 c_[-wz[:,1].H, wz[:,0].H]] 26 xy = r_[ xy, \ 27 c_[-xy[:,1].H, xy[:,0].H]] 28 Aout[ix:ix+2, :] = xy * Aout[ix:ix+2, :] 29 Bout[ix:ix+2, :] = xy * Bout[ix:ix+2, :] 30 Aout[:, ix:ix+2] = Aout[:, ix:ix+2] * wz 31 Bout[:, ix:ix+2] = Bout[:, ix:ix+2] * wz 32 Zout[:, ix:ix+2] = Zout[:, ix:ix+2] * wz 33 Qout[ix:ix+2, :] = xy * Qout[ix:ix+2, :] 34 return (Aout, Bout, Qout, Zout)
35
36 -def qzdiv(stake, A2, B2, Q, Z):
37 Aout = A2.copy(); Bout = B2.copy(); Qout = Q.copy(); Zout = Z.copy() 38 n, jnk = A2.shape 39 # remember diag returns 1d 40 root = mat(abs(c_[diag(A2)[:,newaxis], diag(B2)[:,newaxis]])) 41 root[:,1] /= where(root[:,0]<1e-13, -root[:,1], root[:,0]) 42 for i in range(1,n+1)[::-1]: # always first i rows, decreasing 43 m = None 44 for j in range(1,i+1)[::-1]: # search backwards in the first i rows 45 #print root.shape 46 #print n, i, j 47 #print 'i,j in qzdiv', i,j 48 if root[j-1,1] > stake or root[j-1,1] < -0.1: 49 m = j # get last relevant row 50 break 51 if m == None: return (Aout, Bout, Qout, Zout) 52 for k in range(m,i): # from relev. row to end of first part 53 (Aout, Bout, Qout, Zout) = qzswitch(k, Aout, Bout, Qout, Zout) 54 root[k-1:k+1, 1] = root[k-1:k+1, 1][::-1] 55 return (Aout, Bout, Qout, Zout)
56 57 import numpy as n 58 from subprocess import Popen, PIPE
59 -class octave4numpy:
60 ''' 61 Lets octave functions calculate things, collects results as numpy-matrices. 62 63 Example usage qz decomposition with octave syntax 64 [AA,BB,Q,Z,V,W,lambda] = qz(A,B): 65 66 1) "session approach" 67 myoct = octave4numpy() 68 myoct.definemats(['A', 'B'], [a, b]) # a and b: numpy arrays/matrices 69 (AA,BB,Q,Z,V,W,lda) = myoct.execfunc('qz', ['A', 'B'], 7) 70 # (7 is the number of returned objects, must be specified here in advance!) 71 # (possibly execute other funcs after this) 72 myoct.close() 73 74 2) or the "shortcut approach", quick way for one-time use: 75 myoct = octave4numpy('qz', (a, b), 7) 76 (AA,BB,Q,Z,V,W,lda) = myoct.results 77 # (connection to octave is automatically closed in this variant) 78 79 If something else than 'octave' is needed to invoke your octave version, 80 specify the command string in optional keyword arg cmd, like so: 81 myoct = octave4numpy(cmd = 'octave2.0') 82 83 The user should not pass 1d-numpy-arrays; be explicit about row or col! 84 85 to do: 86 - test on windows 87 - allow to set precision 88 - test if it also works with complex numbers (it should) 89 ... 90 '''
91 - def __init__(self, func = None, args = None, numout = 1, cmd = 'octave'):
92 ''' 93 opens the octave connection via pipes (is this really cross-platform?) 94 95 optionally already executes a function for quick use, func is the name 96 of an octave function; then assignments must match the number of return 97 objects from that specific octave function 98 ''' 99 self.o = Popen([cmd, '-q'], stdin = PIPE, stdout = PIPE) # -q: quiet 100 if type(func) == type('f'): # a function name was specified 101 assert args != None 102 if type(args) == type([]): args = tuple(args) # forgive user error 103 elif type(args) != type((1,)): # allow single matrix here 104 args = (args,) # convert to tuple 105 # use generic names 'a0', 'a1', ... 106 argnamelist = ['a' + str(ix) for ix in range(len(args))] 107 self.definemats(argnamelist, args) 108 self.results = self.execfunc(func, argnamelist, numout) 109 self.octreturncode = self.close()
110
111 - def definemats(self, namelist, mlist):
112 ''' 113 transfers matrices (values, can be 1x1) to octave for further use 114 115 names must be valid denominator strings 116 117 examples: 118 myoct.definemats('a', m) 119 myoct.definemats(['a', 'b'], [m1, m2]) 120 121 (since octave also accepts j for imaginary parts, should also work for 122 complex numbers) 123 ''' 124 if type(namelist) == type('n'): # just one string instead of list 125 namelist = [namelist] 126 mlist = [mlist] 127 assert len(namelist) == len(mlist) 128 for name in namelist: 129 m = mlist[namelist.index(name)] 130 out = ';'.join( \ 131 [','.join(map(str, row)) for row in n.mat(m).tolist()] ) 132 out = '[' + out + ']' 133 self.o.stdin.write(name + '=' + out + '\n') 134 # just to clear stdout from noise: 135 self.getreaction()
136
137 - def getreaction(self, items = 1):
138 ''' 139 parse (and thereby chop off) octave's stdout 140 141 Converting the string to a numpy matrix is left to another method. 142 ''' 143 ## octave's pattern seems to be: 144 ## 1) two '\n' after '=' (and thus before first matrix row) 145 ## 2) one '\n' after each matrix row 146 ## 3) two '\n' after last matrix row (and thus after each item) 147 ## 148 ## After that it's an open-ended file and reading stdout stalls, 149 ## so we must avoid that! 150 # 151 # this is all very unpythonic, but if it works... 152 output = '' 153 itemcount = 0 154 while itemcount < items: 155 line = self.o.stdout.readline() 156 output = output + line 157 if output.endswith('\n\n') and output[-3] != '=': itemcount += 1 158 #print output 159 return output
160
161 - def octstr2nmat(self, octstring):
162 ''' 163 creates numpy matrix from octave's matrix printout 164 165 only one matrix per call should be passed 166 ''' 167 # use only the second part, after the leading '...=' and w/o whitespace: 168 temp = octstring.split('=')[1].strip() 169 # convert row sep 170 temp = temp.replace('\n', ';') 171 ## deal with complex numbers; 172 # octave uses 'i', python 'j' 173 temp = temp.replace('i', 'j') 174 # and octave's formatting 3 + 5i with blanks surrounding the plus sign 175 # produces numpy error, so strip blanks 176 temp = temp.replace(' + ', '+') 177 # (arbitrary number of blanks as col sep should be ok for n.mat) 178 return n.mat(temp)
179
180 - def execfunc(self, funcname, argnames, numout = 1):
181 ''' 182 executes an octave function call and returns matrix results 183 184 For example, the octave function [AA,BB,Q,Z,V,W] = qz(A,B) would be 185 called by 186 (a, b, q, z, v, w) = myoctconn.exefunc('qz', ['myA', 'myB'], 6) 187 188 The arg names must be given as strings, and must have been defined in 189 octave before with definemats. 190 191 If there's only one arg, it is admissible to provide just one arg string 192 instead of an 1-element list 193 ''' 194 if type(argnames) == type('a'): argnames = [argnames] 195 # construct lhs, using generic names 'r0', 'r1', etc. 196 lhs = '[' 197 for returnix in range(numout): 198 lhs = lhs + 'r' + str(returnix) + ',' 199 lhs = lhs[:-1] + ']' # stripping trailing comma 200 # construct rhs 201 rhs = funcname + '(' 202 for argname in argnames: rhs = rhs + argname + ',' 203 rhs = rhs[:-1] + ')' 204 # execute 205 self.o.stdin.write(lhs + '=' + rhs + '\n') 206 outlist = [] 207 for item in range(numout): 208 outlist.append(self.octstr2nmat(self.getreaction())) 209 return outlist
210
211 - def close(self):
212 self.o.communicate('quit') 213 return self.o.returncode
214 215 ######### 216 if __name__ == '__main__': 217 print 'running testcode' 218 # initial and debugging bits and pieces 219 myo = octave4numpy() 220 a = n.hstack( [n.ones((4,2)), n.random.rand(4,2)] ) 221 b = n.hstack( [n.zeros((4,2)), n.random.rand(4,2)] ) 222 myo.definemats('a', a) 223 myo.definemats('b', b) 224 # cleaning up 225 print myo.close() 226 del myo 227 228 # some serious test cases 229 print 'shortcut approach:' 230 c = a 231 d = b 232 myo2 = octave4numpy('qz', (c, d), 7) 233 # and then the result matrices are available as: 234 for result in myo2.results: print result 235 print 'octreturncode: ' + str(myo2.octreturncode) 236 del myo2 237 238 print 'session approach:' 239 e = a 240 f = b 241 myoct = octave4numpy() 242 myoct.definemats(['e', 'f'], [e, f]) 243 (AA,BB,Q,Z,V,W, lam) = myoct.execfunc('qz', ['e', 'f'], 7) 244 myoct.close() 245 print V 246 print W 247