00001 """
00002 A SLIMpy linear operator consists of a dictionary of values operator_dict and
00003 an undefined pipeobject to be defined upon application to a vector.
00004 """
00005
00006 __copyright__ = """
00007 Copyright 2008 Sean Ross-Ross
00008 """
00009 __license__ = """
00010 This file is part of SLIMpy .
00011
00012 SLIMpy is free software: you can redistribute it and/or modify
00013 it under the terms of the GNU Lesser General Public License as published by
00014 the Free Software Foundation, either version 3 of the License, or
00015 (at your option) any later version.
00016
00017 SLIMpy is distributed in the hope that it will be useful,
00018 but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00020 GNU Lesser General Public License for more details.
00021
00022 You should have received a copy of the GNU Lesser General Public License
00023 along with SLIMpy . If not, see <http://www.gnu.org/licenses/>.
00024 """
00025
00026
00027
00028 from slimpy_base.Core.User.linop.LinearOperatorType import LinearOperatorType, is_oper
00029 from slimpy_base.Environment.InstanceManager import InstanceManager
00030 from slimpy_base.utils.DotTest import DotTester
00031 from copy import copy
00032 from itertools import chain
00033 from numpy import all
00034 import warnings
00035
00036
00037 class LinearOperator( object ):
00038 """
00039 The base linear operator object should not be called by itself.
00040 """
00041
00042 __metaclass__ = LinearOperatorType
00043
00044 name = "Identity"
00045 env = InstanceManager()
00046
00047 __tester__ = DotTester()
00048 __block_diagonal__ = True
00049
00050 def __new__( cls, *params, **kparams ):
00051 if params:
00052 inspace = params[0]
00053 from slimpy_base.User.AumentedMatrix.MetaSpace import MetaSpace
00054
00055 if isinstance(inspace, MetaSpace):
00056 if hasattr(cls, "__meta_space__"):
00057
00058 meta_space_bldr = getattr(cls, "__meta_space__")
00059 return meta_space_bldr( *params, **kparams )
00060
00061 elif hasattr(cls, "__block_diagonal__") and getattr(cls, "__block_diagonal__"):
00062 from slimpy_base.User.AumentedMatrix.HelperFunctions import From_sapce
00063 return From_sapce( cls, *params, **kparams)
00064 else:
00065 raise TypeError( "LinearOperator %(cls)s got MetaSpace instance as first argument\n"
00066 "need '__meta_space__' creator or '__block_diagonal__' flag" %vars( ) )
00067
00068 return object.__new__( cls, *params, **kparams )
00069
00070
00071 def __init__( self, inspace, outspace, *params, **kparams ):
00072 """
00073 The init method is used to initialize the operator_dict with default values.
00074 """
00075 if not kparams.has_key( "adj" ):
00076 kparams['adj'] = False
00077
00078
00079 self.params = params
00080 self.kparams = kparams
00081
00082 self.inSpace = inspace
00083 self.outSpace = outspace
00084
00085
00086 self.__tester__.addLinearOp( self )
00087
00088 def __str__( self ):
00089 """
00090 Returns the operators name and attributes
00091 """
00092 eqs = lambda ( kev, val ): "%s=%s" %( kev, val )
00093 name = self.name
00094 lst = ", ".join( map( eqs, self.kparams.items() ) )
00095 s = "<Linear Operator: %(name)s; %(lst)s>" % vars()
00096 return s
00097
00098 def __repr__( self ):
00099 """
00100 Returns the operators name from operator_dict
00101 """
00102
00103 eqs = lambda ( kev, val ): "%s=%s" %( kev, repr(val) )
00104 name = self.name
00105 lst1 = [ eqs(item) for item in self.kparams.items() ]
00106 lst2 = [ repr(item) for item in self.params ]
00107
00108 args = ", ".join( chain(lst1,lst2) )
00109 name = self.__class__.__name__
00110 return "%(name)s( domain, %(args)s )" %vars()
00111
00112 return self.__str__()
00113
00114 def __eq__( self, other ):
00115
00116 if type(self) != type(other):
00117 return False
00118
00119 slots = ["inSpace","outSpace","params",
00120 "kparams","tname","tparams","tkparams","isadj"]
00121
00122 for name in slots:
00123 h1 = hasattr( self, name )
00124 h2 = hasattr( other, name )
00125
00126 if h1 ^ h2:
00127 return False
00128
00129 if h1 and h2:
00130 a1=getattr( self, name )
00131 a2=getattr( other, name )
00132 if not all(a1 == a2):
00133 return False
00134 return True
00135
00136 def __neg__(self):
00137 return CompoundOperator ( [-Identity(self.range()) , self ] )
00138
00139 def domain( self ):
00140 """
00141 return inSpace
00142 """
00143 return self.inSpace
00144
00145 def range( self ):
00146 """
00147 return outSpace
00148 """
00149 return self.outSpace
00150
00151 def copy( self ):
00152 """
00153 Perform shallow copy of self
00154 """
00155
00156 return copy( self )
00157
00158 def _copy(self):
00159
00160 cls = type(self)
00161 params = self.params
00162 kw = self.kparams
00163 domain = self.inSpace
00164
00165 return cls( domain, *params, **kw )
00166
00167 def adj( self , *other ):
00168 """
00169 The adjoint flips the operator_dict key transp. and updates the domain and range.
00170 """
00171
00172 t = self.copy()
00173
00174 inSpace = copy( t.inSpace )
00175 t.inSpace = copy( t.outSpace )
00176 t.outSpace = inSpace
00177
00178
00179
00180 t.kparams = copy( t.kparams )
00181 t.params = copy( t.params )
00182
00183 t.kparams['adj'] = not t.kparams['adj']
00184
00185 if other:
00186 assert len(other) == 1
00187 return t * other
00188 else:
00189 return t
00190
00191 def _H_( self ):
00192 'proxy for sub classes to use Op.H'
00193 return self.adj()
00194
00195 H = property( _H_ )
00196
00197 def _getadj( self ):
00198 "get the adj key in the kparams"
00199 return self.kparams['adj']
00200
00201 def _setadj( self, val ):
00202 "set the adj key in the kparams"
00203 self.kparams['adj'] = val
00204
00205 isadj = property( _getadj , _setadj )
00206
00207 def __mul__( self, other ):
00208 """
00209 Not defined in this class
00210 """
00211 if other is 0:
00212 return 0
00213 elif other is 1:
00214 return self
00215 elif is_oper(other):
00216 return CompoundOperator( [self,other] )
00217 else:
00218 return self.applyop( other )
00219
00220 def __add__( self, other ):
00221 return ArithmaticOperator( '+', self, other )
00222
00223 def __radd__( self, other ):
00224 return ArithmaticOperator( '+', other, self )
00225
00226 def __sub__( self, other ):
00227
00228 return ArithmaticOperator( '-', self, other )
00229
00230 def __rsub__( self, other ):
00231
00232 return ArithmaticOperator( '-', other, self )
00233
00234 def __call__( self, other ):
00235 """
00236 Not defined in this class
00237 """
00238 if other is 0:
00239 return 0
00240 elif other is 1:
00241 return self
00242 elif is_oper(other):
00243 return CompoundOperator( [self,other] )
00244 else:
00245 return self.applyop( other )
00246
00247 def applyop( self, other ):
00248
00249 return other
00250
00251 def getdim( self ):
00252 """
00253 Returns the dimensions of the operator. Usually not defined.
00254 """
00255 return [len( self.inSpace ), len( self.outSpace )]
00256
00257 def norm( self ):
00258 """
00259 returns Identity
00260 """
00261 warnings.warn("please use SLIMpy.Norm Function", DeprecationWarning, 2)
00262 return Identity( self.outSpace )
00263
00264 def __norm_col__(self):
00265 """
00266 Norm( oper , col=True ) -> oper.__norm_col__( )
00267 """
00268 return NotImplemented
00269
00270 def __norm_row__(self):
00271 """
00272 Norm( oper , col=False ) -> oper.__norm_row__( )
00273 """
00274 return NotImplemented
00275
00276 def normalize( self ):
00277 """
00278 returns comp( [self.norm(), self] )
00279 """
00280 warnings.warn("please use SLIMpy.Normalize Function", DeprecationWarning, 2)
00281 return CompoundOperator( [self.norm(), self] )
00282
00283 def minvelconst( self, *args, **kargs ):
00284 """
00285 returns Identity
00286 """
00287 warnings.warn("please use SLIMpy.MinVelConst Function", DeprecationWarning, 2)
00288 return Identity( self.outSpace )
00289
00290
00291
00292 class Identity( LinearOperator ):
00293 """
00294 I = Identity( space )
00295 Operator that does nothing
00296 """
00297
00298 __block_diagonal__ = True
00299
00300 def __init__( self, space ):
00301 LinearOperator.__init__( self, space, space, adj=False )
00302 self.kparams = {"adj":False}
00303 self.params = []
00304 self._pos_sign = True
00305 return
00306
00307 def __neg__(self):
00308 I = self.copy( )
00309 I._pos_sign = not self._pos_sign
00310
00311 return I
00312
00313 def applyop(self,other):
00314 if self._pos_sign:
00315 return other
00316 else:
00317 return -other
00318
00319
00320 class CompoundOperator( LinearOperator ):
00321 """
00322 C = CompoundOperator( [o1, o2, ... ] )
00323 compound operator class used to chain together
00324 Linear operators
00325 """
00326 def __init__( self, alist ):
00327
00328 self.alist = alist
00329
00330
00331 inspace = self.domain()
00332 outspace = self.range()
00333 LinearOperator.__init__( self, inspace, outspace, adj=False )
00334
00335 def __checklist( self ):
00336 """
00337 check if all of the domains and ranges can
00338 be chained together
00339 """
00340 lambda y, x: bool( y.domain() == x.range() )
00341
00342 lst = self.alist
00343 check = zip( lst, [lst[-1]] + lst[:-1] )
00344
00345 return
00346
00347 def __getitem__( self, item ):
00348 return self.alist[item]
00349
00350
00351
00352
00353 def reverse( self ):
00354 """
00355 return a new object in reverse order
00356 """
00357 alist = copy( self.alist )
00358 alist.reverse()
00359 return alist
00360
00361 def applyop( self, other ):
00362 """
00363 Overloads Applyop to apply each element
00364 in this operator from the right most first
00365 """
00366
00367 for i in range( len( self.alist )-1, -1, -1 ):
00368 other = self[i] * other
00369 return other
00370
00371 def domain( self ):
00372 return self[-1].domain()
00373
00374 def range( self ):
00375 return self[0].range()
00376
00377 def adj( self ):
00378 alist = self.reverse()
00379 newlist = CompoundOperator( [A.adj() for A in alist] )
00380 return newlist
00381
00382
00383 def __str__( self ):
00384 return "Comp:%s"%self.alist.__str__()
00385
00386 def __repr__( self ):
00387 rplst = ", ".join( [ repr(item) for item in self.alist ] )
00388 return "CompoundOperator( [%(rplst)s] )" %vars()
00389
00390 def copy(self):
00391 alist = self.alist
00392 return CompoundOperator( [A.copy() for A in alist] )
00393 pass
00394
00395
00396 class ArithmaticOperator( LinearOperator ):
00397 """
00398 Operator to handle arithmatic
00399 operations on implicit operators
00400 ArithmaticOperator( '-', I, A )*x -> (I-A)*x -> (Ix - Ax)
00401
00402 """
00403 def __init__(self, arithop, oper1, oper2):
00404 self.oper1 = oper1
00405 self.oper2 = oper2
00406 self.arithop = arithop
00407
00408 from slimpy_base.Core.User.Structures.VectorSpace import VectorAddition
00409 domain = VectorAddition( [self.oper1.domain() ,self.oper2.domain()] )
00410 range = VectorAddition( [self.oper1.range() ,self.oper2.range()] )
00411 LinearOperator.__init__( self, domain, range )
00412
00413 def __str__(self):
00414 return "( %(oper1)s %(arithop)s %(oper2)s )" %self.__dict__
00415
00416 def __repr__(self):
00417 oper1 = repr(self.oper1)
00418 oper2 = repr(self.oper2)
00419 arithop= repr(self.arithop)
00420 return "ArithmaticOperator( %(arithop)s, %(oper1)s, %(oper2)s )" %vars()
00421
00422 def applyop(self,other):
00423 res1 = self.oper1 * other
00424 res2 = self.oper2 * other
00425
00426 if self.arithop == '+':
00427 res = res1 + res2
00428
00429 elif self.arithop == '-':
00430 res = res1 - res2
00431
00432 return res