-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSym_UTB_Matrix.py
300 lines (229 loc) · 9.67 KB
/
Sym_UTB_Matrix.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed May 22 12:52:53 2019
@author: dpuzzuol
Notes:
This class stores a list of symbolic utb matrices, along with some
functionality for working with them. This file also contains some functions
outside of the class that could potentially be useful elsewhere.
"""
from sympy.matrices import BlockMatrix,Matrix, zeros
from sympy.core.symbol import Symbol
class Sym_UTB_Matrix:
###################
# Constructors
###################
def __init__(self, mat_list,spec = 'full'):
self.mat_list = to_noncomm_matrix_list(mat_list, spec = spec)
self.shape = [A.shape[0] for A in self.mat_list]
@classmethod
def from_concise_description(cls, shape, nc_sym, nc_idx, c_sym, c_idx):
'''
constructs the object from the description format output by
the function concise_description()
'''
# need to construct a list of matrices from the concise description
# instantiate a list of zero Matrix objects
mat_list = []
for d in shape:
mat_list.append(zeros(d,d))
# populate the non-commutative symbols
for k in range(len(nc_sym)):
sym = nc_sym[k]
for mat_idx, i, j in nc_idx[k]:
mat_list[mat_idx][i,j] = sym
# populate hte list of commutative symbols
for k in range(len(c_sym)):
sym = c_sym[k]
for mat_idx, i, j in c_idx[k]:
mat_list[mat_idx][i,j] = sym
return Sym_UTB_Matrix(mat_list)
##################
# object methods
##################
def exp_deriv_generators(self, params):
'''
constructs the generators for computing the first derivative of the
time ordered exponential of self, with respect to params
Note: I'll need to either add in stuff that keeps track of where the
blocks go, or just have a convention that is maintained everywhere.
Convention is easier, but it might be nice to also return some data
structure with this info
'''
D,C = self.parameter_decomposition(params)
new_mats= []
for sys_idx in range(len(self.mat_list)):
G = self.mat_list[sys_idx]
m0 = zeros(self.shape[sys_idx])
for d_idx in range(len(params)):
A = C[d_idx].mat_list[sys_idx]
new_mats.append(Matrix(BlockMatrix(
[[G, A],
[m0, G]]
)))
return Sym_UTB_Matrix(new_mats)
def parameter_decomposition(self, params):
'''
Given a list of parameters, which should be a list of commutative
symbols, returns a list of Sym_UTB_Matrix objects, representing
the parameter decomp.
That is, for parameters [a[0], ..., a[k]], returns a list
[D, M0, ..., Mk] s.t. self = M0 + a[0]*M0 + ... + a[k]*Mk
Note: this function assumes that each matrix element of self
is an affine combination with coefficients a[0], ..., a[k], which
will be true for these control problems, but of course is not true
in general
May want to change to affine control decomp (or something)
'''
D = self.subs({a : 0 for a in params})
M = [self.diff(a) for a in params]
return D,M
def subs(self, sub_dict):
'''
substitute symbols in mat_list
'''
return Sym_UTB_Matrix([A.subs(sub_dict) for A in self.mat_list])
def diff(self, sym):
'''
differentiation with respect to a symbol
'''
return Sym_UTB_Matrix([A.diff(sym) for A in self.mat_list])
def direct_sum(self,other):
'''
direct sum two Sym_UTB_Matrix objects
'''
new_list = self.mat_list.copy()
new_list.extend(other.mat_list)
return Sym_UTB_Matrix(new_list)
def add(self,other):
'''
if the shapes are equal, add with another like object
'''
if self.shape == other.shape:
return Sym_UTB_Matrix([A+B for A,B in zip(self.mat_list, other.mat_list)])
def mult(self, other):
'''
if the shapes are equal, multiply with another like object
'''
if self.shape == other.shape:
return Sym_UTB_Matrix([A*B for A,B in zip(self.mat_list, other.mat_list)])
def power(self, k):
'''
multiply with itself
'''
return Sym_UTB_Matrix([A**k for A in self.mat_list])
# check to see if these are the same matrices
# NOTE!!!: this comparison doesn't ignore symbols
def compare(self,other):
return self.mat_list == other.mat_list
def concise_description(self):
'''
Compiles a reduced description of the matrix. In particular, it
compiles a list of every unique non-commutative and commutative symbol,
along with corresponding sets of indices
'''
# list of non-commutative symbols, and list of sets of indices
# for these symbols
non_comm_symbols = []
non_comm_idx = []
# list of commutative symbols, along with corresponding sets of
# indices
comm_symbols = []
comm_idx = []
# iterate through successive off-diagonals
for off_diag in range(max(self.shape)):
# iterate through each matrix
for mat_idx in range(len(self.shape)):
A = self.mat_list[mat_idx]
d = A.shape[0]
if off_diag < d:
for j in range(d-off_diag):
entry = A[j,j+off_diag]
# check if its commutative
if entry.is_commutative:
if entry not in comm_symbols:
comm_symbols.append(entry)
comm_idx.append({(mat_idx, j,j+off_diag)})
else:
comm_idx[comm_symbols.index(entry)].add((mat_idx, j,j+off_diag))
# if it's not a number, assume it is a symbolic sympy
# expression
elif entry not in non_comm_symbols:
non_comm_symbols.append(entry)
non_comm_idx.append({(mat_idx, j,j+off_diag)})
else:
non_comm_idx[non_comm_symbols.index(entry)].add((mat_idx, j,j+off_diag))
return self.shape, non_comm_symbols,non_comm_idx, comm_symbols,comm_idx
def concise_display(self):
'''
Print the output of self.concise_description()
'''
shape, nc_symbols, nc_idx, c_symbols, c_idx = self.concise_description()
print('Shape:')
print('------')
print(self.shape)
print('Non-commutative symbols:')
print('------------------------')
for k in range(len(nc_symbols)):
print('{} {}'.format(nc_symbols[k], nc_idx[k]))
print('Commutative symbols:')
print('--------------------')
for k in range(len(c_symbols)):
print('{} {}'.format(c_symbols[k], c_idx[k]))
'''
Non-class functions
'''
def to_noncomm_matrix_list(mat_list, spec = 'full'):
'''
Converts input into a list of Matrix objects with non-commutative
symbols. Handles several cases for ease of input:
- if the input type is a Matrix, assume it is of the correct form
and just needs to be put into a list
- if the input type is list, change behaviour based on whether
its first entry is a Matrix, if its a single matrix specified
using lists, or if it's a list of matrices each specified with
lists
'''
if type(mat_list) == Matrix:
return [mat_list]
elif type(mat_list) == list:
# if the first entry is a Matrix, assume it is a list
# of matrices
if type(mat_list[0]) == Matrix:
return mat_list
# if the first entry isn't a list, assume it is specified
# as a single matrix in list form
elif type(mat_list[0][0]) != list:
return [to_noncomm_matrix(mat_list)]
# otherwise, assume it is a list of matrices each specified
# as a list
else:
return [to_noncomm_matrix(A, spec = spec) for A in mat_list]
def to_noncomm_matrix(matrix, spec = 'full'):
'''
Converts input into a sympy Matrix object, converting all string
entries into non-commutative matrix symbols. Also takes input type
Matrix, in which case it simply returns the matrix.
'''
if type(matrix) == Matrix:
return matrix
elif type(matrix) == list:
if spec == 'full':
d = len(matrix)
new_mat = zeros(d,d)
for i in range(d):
for j in range(i,d):
new_mat[i,j] = to_noncomm_sym(matrix[i][j])
return new_mat
def to_noncomm_sym(a):
'''
If the input is str, convert to a non-commutative sympy Symbol,
otherwise return it
'''
if type(a) == Symbol:
return a
elif type(a) == str:
return Symbol(a, commutative = False)
else:
return a