# Using the Mueller module¶

In this notebook, we demonstrate how to use the Mueller module, which defines the MuellerMatrix and StokesVector classes and various operations on them.

The first thing, of course, is to import the package and some other packages that we will be using. We will be creating some random matrices and vectors, so we need random, and we will be graphing a few things, so we need maptplotlib.pyplot.

In [1]:

from pySCATMECH.mueller import *
import random
import matplotlib.pyplot as plt


## Mueller matrices¶

The class MuellerMatrix is used for handling Mueller matrices. Here we show several ways one can create a unit Mueller matrix. The JonesMueller() function converts a 2 x 2 complex Jones matrix into a Mueller matrix. And MuellerUnit() returns a unit Mueller matrix.

In [2]:

m1 = MuellerMatrix([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]])
m2 = MuellerMatrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
m3 = MuellerMatrix(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)
m4 = JonesMueller([[1,0],[0,1]])
m5 = MuellerUnit()
print(m5)

[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]


We can check to see that these are valid matrices. The valid method tests whether a Mueller matrix maps the set of valid Stokes vectors onto itself. The physically_valid method tests whether a Mueller matrix is a convex sum of Jones-Mueller matrices. The latter is a more stringent test of whether the matrix is realizable.

In [3]:

print(m1.valid())
print(m1.physically_valid())

True
True


But, a random matrix is not necessarily valid. In fact, it probably isn’t!

In [4]:

# Returns a random number with a normal distribution...
ran = lambda : random.gauss(0,1)

# Create 20 random matrices...
m1 = [MuellerMatrix([[ran(), ran(), ran(), ran()],
[ran(), ran(), ran(), ran()],
[ran(), ran(), ran(), ran()],
[ran(), ran(), ran(), ran()]]) for i in range(20)]

# See if they are valid...
print("Number valid() is True:", sum([m.valid() for m in m1]))
print("Number physically valid() is True:", sum([m.physically_valid() for m in m1]))

Number valid() is True: 1
Number physically valid() is True: 0


A Mueller matrix made from a random Jones matrix, however, always yields a valid Mueller matrix.

In [5]:

# Returns a random complex number with a normal distribution...
ranc = lambda : random.gauss(0,1)+random.gauss(0,1)*1j

# Create 20 random Jones-Mueller matrices...
m1 = [JonesMueller([[ranc(), ranc()], [ranc(), ranc()]]) for i in range(20)]

# See if they are valid...
print("Number value() is True:", sum([m.valid() for m in m1]))
print("Number physically valid() is True:", sum([m.physically_valid() for m in m1]))

Number value() is True: 20
Number physically valid() is True: 20


To make a random depolarizing Mueller matrix, we average some random Jones-Mueller matrices:

In [6]:

def RandomMuellerMatrix(n):
"""
Creates a random Mueller matrix by averaging n Jones-Mueller matrices.
"""
ranc = lambda : (random.gauss(0,1)+random.gauss(0,1)*1j)/2
return sum([JonesMueller([[ranc(),ranc()],[ranc(),ranc()]]) for i in range(n)])/n

m = RandomMuellerMatrix(4)
print(m)
print("m is a valid Stokes to Stokes mapper:",m.valid())
print("m is the sum of Jones-Mueller matrices:",m.physically_valid())

[[ 0.79126519  0.51754966  0.04396498 -0.09197144]
[-0.21857972 -0.10891561  0.03399935  0.04409256]
[ 0.24109773  0.27199561  0.06684414 -0.02241909]
[ 0.31260027  0.21946993  0.16903187 -0.02210762]]
m is a valid Stokes to Stokes mapper: True
m is the sum of Jones-Mueller matrices: True


## Stokes Vectors¶

The class StokesVector is used for handling Stokes vectors. We can create a Stokes vector a several ways. The Polarization function provides a means for generating arbitrary polarizations by name or parameter. (Note that SCATMECH’s convention for the signs of the Stokes vector elements differs from some others.)

In [7]:

print("Unpolarized Stokes vector:", StokesVector((1,0,0,0)) )

print("Unpolarized Stokes vector:", StokesVector(1,0,0,0) )

print("s-polarized unit polarization from a Jones vector:", JonesStokes((1.,0.)) )

print("p-polarized unit Stokes vector:", Polarization('p') )

print("s-polarized unit Stokes vector:", Polarization(state = 's') )

print("Left-handed circularly polarized unit Stokes vector:", Polarization("L") )

print("Right-handed circularly polarized unit Stokes vector with intensity 2:", Polarization("R", I=2) )

print("Unpolarized Stokes vector:", Polarization("U") )

print("Unpolarized Stokes vector:", Polarization('s', DOP=0) )

print("Linear polarization at 45 degrees:", Polarization(angle = 45*deg) )

print("Elliptically polarized radiation:", Polarization(angle = 34*deg, ellipticity = 20*deg) )

print("Partially polarized radiation:", Polarization("S", DOP = 0.8) )

print("Partially polarized elliptical polarized radiation with intensity 3:\n",
Polarization(angle = 34*deg, ellipticity = 20*deg, DOP = 0.7, I = 3) )

Unpolarized Stokes vector: StokesVector(1,0,0,0)
Unpolarized Stokes vector: StokesVector(1,0,0,0)
s-polarized unit polarization from a Jones vector: StokesVector(1.0,1.0,0.0,0.0)
p-polarized unit Stokes vector: StokesVector(1,-1,0,0)
s-polarized unit Stokes vector: StokesVector(1,1,0,0)
Left-handed circularly polarized unit Stokes vector: StokesVector(1,0,0,1)
Right-handed circularly polarized unit Stokes vector with intensity 2: StokesVector(2,0,0,-2)
Unpolarized Stokes vector: StokesVector(1,0,0,0)
Unpolarized Stokes vector: StokesVector(1,0,0,0)
Linear polarization at 45 degrees: StokesVector(1.0,6.123233995736766e-17,1.0,0.0)
Partially polarized elliptical polarized radiation with intensity 3:
StokesVector(3.0,0.6026271284081782,1.4915544830350964,1.3498539803417322)


Note that polarization sensitivity can also be expressed as a Stokes vector. However, there is often a factor of two that enters for all but unpolarized sensitivity. That is, a perfect polarizer transmits 50 % of unpolarized radiation. We can apply senstivity = True to the Polarization() function, or use a Sensitivity function.

In [8]:

# Only sensitive to p-polarization
# The following two statements are equivalent...
sens = Polarization('p', sensitivity = True)
sens = Sensitivity('p')

print('Signal for p-polarization:',sens @ Polarization('p'))
print('Signal for s-polarization:',sens @ Polarization('s'))

Signal for p-polarization: 1.0
Signal for s-polarization: 0.0


Sensitivity does not need to be full polarizing. By setting DOP in the range [0,1], sensitivity to the orthogonal polarization can be non-zero.

In [9]:

sens = Sensitivity('p',DOP=.8)
print('sens = ', sens)

print('Signal for p-polarization:',sens @ Polarization('p'))
print('Signal for s-polarization:',sens @ Polarization('s'))

sens =  StokesVector(0.6,-0.4,0.0,0.0)
Signal for p-polarization: 1.0
Signal for s-polarization: 0.19999999999999996


In a manner similar to what we did with Mueller matrices, we can create a random Stokes vector by averaging a number of Stokes vectors created from Jones vectors. For a single Jones vector, the Stokes vector is depolarized. For more than one, the Stokes vector is partially polarized or unpolarized. We illustrate that by plotting the degree of polarization (using the DOP() method) as a function of the number of Jones-Stokes vectors we add.

In [10]:

def RandomStokesVector(n):
ranc = lambda : (random.gauss(0,1) + random.gauss(0,1)*1j)/2
return sum([JonesStokes([ranc(),ranc()]) for i in range(n)])/n

plt.figure()
plt.plot(range(1,100),[RandomStokesVector(i).DOP() for i in range(1,100)])
plt.title("Degree of polarization vs. number of Jones-Stokes vectors")
plt.xlabel("n")
plt.ylabel("Degree of polarization")
plt.show()


We can multiply Mueller matrices and Stokes vectors. You can use the @ or * operators or the dot() function to multiply the objects. If a Stokes vector multiplies a Mueller matrix from the left, it is assumed to be transposed first. The product of two Stokes vectors is the inner product, a scalar.

In [11]:

m1 = RandomMuellerMatrix(4)
m2 = RandomMuellerMatrix(4)
s1 = RandomStokesVector(4)
s2 = RandomStokesVector(4)
print("m1 = \n",m1)
print("m2 = \n",m2)
print("s1 = ",s1)
print("s2 = ",s2)
print("m1 * s1 = ",m1 * s1)
print("m1 @ s1 = ",m1 * s1)
print("m1.dot(s1) = ",m1.dot(s1))
print("m1 @ m2 = ",m1 * m2)
print("s1 @ m1 = ",s1 * m1)
print("s1 @ s2 = ",s1 * s2)

m1 =
[[ 0.94335668 -0.16250793  0.46915549 -0.14212114]
[-0.23542067  0.62314123 -0.18648852 -0.05825565]
[-0.01326436 -0.06702373  0.02740091 -0.4182326 ]
[ 0.37937625 -0.06489837  0.54109253  0.07621696]]
m2 =
[[ 0.90393586  0.42883859  0.05893816 -0.0398583 ]
[-0.03255312 -0.05484299  0.28021642  0.20824843]
[ 0.17437584  0.3752689  -0.03803409  0.08744445]
[ 0.35288836  0.34723155  0.26526746 -0.43082467]]
s1 =  StokesVector(1.2205852882966242,-0.07508395251618234,0.8002605801487375,-0.2529729329906206)
s2 =  StokesVector(1.5964694635032322,-0.2407997868272278,0.5994372371241194,-0.8510075390636394)
m1 * s1 =  StokesVector(1.5750484699343372,-0.46864121761363126,0.11657152710872415,0.8816680960253582)
m1 @ s1 =  StokesVector(1.5750484699343372,-0.46864121761363126,0.11657152710872415,0.8816680960253582)
m1.dot(s1) =  [ 1.57504847 -0.46864122  0.11657153  0.8816681 ]
m1 @ m2 =  [[ 0.88968056  0.54017069 -0.0454817   0.03081172]
[-0.28616721 -0.22534394  0.15237873  0.14794223]
[-0.15261965 -0.13695333 -0.13154859  0.16915209]
[ 0.46629398  0.39577053  0.00381207 -0.01415688]]
s1 @ m1 =  StokesVector(1.062536738150666,-0.2823616179668839,0.4716926877767631,-0.5230727916610801)
s1 @ s2 =  2.661695204424655


## Mueller matrix decompositions¶

So, now that we can create some physically valid Mueller matrices, we can explore some decompositions. The Cloude decomposition* decomposes a Mueller matrix into an incoherent, positive (convex) sum of Jones-Mueller matrices.

*S. R. Cloude, “Group theory and polarization algebra,” Optik (Stuttgart) 75, 26–36 (1986) and S. R. Cloude and E. Pottier, “A review of target decomposition theorems in radar polarimetry,” IEEE Trans. Geosci. and Remote Sensing 34, 498–518 (1996).

In [12]:

M = RandomMuellerMatrix(4)
MCD = M.Cloude_Decomposition()
print("M = \n\n",M,"\n\nCloude decomposition =\n")
for m in MCD:
print(m,"\n")

print(f"Purity1 = {(MCD[0][0,0]-MCD[1][0,0])/M[0,0]}")
print(f"Purity2 = {(MCD[0][0,0]+MCD[1][0,0]-2*MCD[2][0,0])/M[0,0]}")
print(f"Purity3 = {(MCD[0][0,0]+MCD[1][0,0]+MCD[2][0,0]-3*MCD[3][0,0])/M[0,0]}")

M =

[[ 0.97198146  0.30841196 -0.13263167  0.11331983]
[-0.01312405  0.05122653  0.24274521  0.03582365]
[-0.14679568  0.04987054 -0.08064983 -0.34434852]
[-0.09716448 -0.16663341 -0.09086824  0.40486368]]

Cloude decomposition =

[[ 0.49815716  0.23393238 -0.05659197  0.11395561]
[-0.07926307 -0.06762069  0.42302718  0.00239692]
[-0.22600176 -0.30168327 -0.00377822 -0.37053662]
[-0.11641932 -0.36930565 -0.03852329  0.2300661 ]]

[[ 0.31877226  0.15566403 -0.09501666  0.02225629]
[ 0.10148151  0.03766427 -0.2769091   0.00788727]
[ 0.13742785  0.28393391 -0.01393898 -0.07703643]
[ 0.06760094  0.10027555 -0.0040224   0.2497188 ]]

[[ 0.15220157 -0.07995128  0.01814329 -0.02246167]
[-0.03555445  0.08268129  0.09841471  0.02611275]
[-0.05939849  0.06973868 -0.06428279  0.10233195]
[-0.04933165  0.10311127 -0.04950611 -0.07273397]]

[[ 0.00285046 -0.00123317  0.00083367 -0.0004304 ]
[ 0.00021197 -0.00149834 -0.00178758 -0.00057329]
[ 0.00117673 -0.00211878  0.00135016  0.00089258]
[ 0.00098556 -0.00071458  0.00118356 -0.00218725]]

Purity1 = 0.18455587955678526
Purity2 = 0.527300463403825
Purity3 = 0.9882694784032228


Notice that if we create the random Mueller matrix from less than four Jones-Mueller matrices, the Cloude decomposition will have only as many non-zero matrices as there were Jones-Mueller matrices. In the following, we plot the average magnitude of each element of the Cloude decomposition as a function of how many Jones-Mueller matrices were added.

In [13]:

jmax = 100
imax = 100
mCD00sums = []
for i in range(1,imax):
mCD00sum = np.array([0,0,0,0])
for j in range(jmax):
M = RandomMuellerMatrix(i)
mCD = Cloude_Decomposition(M)
mCD00sum = mCD00sum + np.array([m[0,0] for m in mCD])
mCD00sums.append(mCD00sum/jmax)
plt.figure()
plt.plot(mCD00sums)
plt.xlabel("Number of Jones-Mueller matrices averaged")
plt.ylabel("Cloude decomposition")
plt.show()


The Lu-Chipman decomposition* decomposes a given Mueller matrix $$\mathbf{M}$$ into the ordered product of three matrices:

$\mathbf{M} = \mathbf{M}_\Delta \mathbf{M}_R \mathbf{M}_D$

where $$\mathbf{M}_\Delta$$ is a depolarizer, $$\mathbf{M}_R$$ is a retarder, and $$\mathbf{M}_D$$ is a diattenuator. Notice that if there is only one Jones-Mueller matrix used to create the random matrix, there will be no depolarization.

*S.-Y. Lu and R. A. Chipman, “Interpretation of Mueller matrices based on polar decomposition,” J. Opt. Soc. Am. A 13, 1106–1113 (1996)

In [14]:

def printMatrix(name,M):
print("\n",name,"=\n")
print(M)

M = RandomMuellerMatrix(4)
depol, ret, diatten = Lu_Chipman_Decomposition(M)
printMatrix("M",M)
printMatrix("Depolarizer",depol)
printMatrix("Retarder",ret)
printMatrix("Diattenuator",diatten)
printMatrix("The product Depolarizer . Retarder .  Diattenuator",depol @ ret @ diatten)


M =

[[ 0.84442092  0.1075449   0.06352588  0.05409045]
[ 0.21973306  0.44426721 -0.13529821  0.11410266]
[ 0.23420748  0.03277545 -0.11947241 -0.13426277]
[ 0.08729154  0.07995964 -0.03015655 -0.0153505 ]]

Depolarizer =

[[1.         0.         0.         0.        ]
[0.20185409 0.54228623 0.00714284 0.07993953]
[0.30106685 0.00714284 0.24083235 0.04079128]
[0.09770437 0.07993953 0.04079128 0.03708036]]

Retarder =

[[ 1.00000000e+00  2.10335221e-17 -1.89193279e-17  0.00000000e+00]
[ 1.11022302e-16  8.54280664e-01 -3.97300271e-01  3.35197020e-01]
[ 4.44089210e-16 -1.03253526e-01 -7.61690669e-01 -6.39660874e-01]
[ 0.00000000e+00  5.09453881e-01  5.11839642e-01 -6.91720264e-01]]

Diattenuator =

[[0.84442092 0.1075449  0.06352588 0.05409045]
[0.1075449  0.84027184 0.00407193 0.00346713]
[0.06352588 0.00407193 0.8357836  0.00204801]
[0.05409045 0.00346713 0.00204801 0.83512216]]

The product Depolarizer . Retarder .  Diattenuator =

[[ 0.84442092  0.1075449   0.06352588  0.05409045]
[ 0.21973306  0.44426721 -0.13529821  0.11410266]
[ 0.23420748  0.03277545 -0.11947241 -0.13426277]
[ 0.08729154  0.07995964 -0.03015655 -0.0153505 ]]


The reverse Lu-Chipman decomposition is similar to the Lu-Chipman decomposition, except that the order of the elements us reversed. That is,

$\mathbf{M} = \mathbf{M}^\prime_D \mathbf{M}^\prime_R \mathbf{M}^\prime_\Delta$

where $$\mathbf{M}^\prime_D$$ is a diattenuator, $$\mathbf{M}^\prime_R$$ is a retarder, and $$\mathbf{M}^\prime_\Delta$$ is a depolarizer. Note that highly depolarizing Mueller matrices often cannot be decomposed in this fashion, an exception will be thrown if this is the case.

In [15]:

M = RandomMuellerMatrix(2)
diatten, ret, depol = Reverse_Lu_Chipman_Decomposition(M)
printMatrix("M",M)
printMatrix("Diattenuator",diatten)
printMatrix("Retarder",ret)
printMatrix("Depolarizer",depol)
printMatrix("The product Diattenuator . Retarder .  Depolarizer", diatten @ ret @ depol)


M =

[[ 0.79050245  0.31080475  0.13624337 -0.2012983 ]
[-0.22657717 -0.64638751 -0.16880211 -0.25056315]
[ 0.19461597  0.0578249   0.53578195 -0.31627667]
[ 0.17512845  0.33887398 -0.24140583 -0.45722454]]

Diattenuator =

[[ 0.87059006 -0.230308    0.2884748   0.30995087]
[-0.230308    0.75822452 -0.04163903 -0.04473893]
[ 0.2884748  -0.04163903  0.77713682  0.05603824]
[ 0.30995087 -0.04473893  0.05603824  0.78519151]]

Retarder =

[[ 1.          0.          0.          0.        ]
[ 0.         -0.87883829 -0.22590361 -0.4202509 ]
[ 0.          0.01769794  0.86476707 -0.50186123]
[ 0.          0.4767914  -0.44849244 -0.75599239]]

Depolarizer =

[[ 1.          0.          0.          0.        ]
[-0.07088282  0.91237903  0.01709567  0.05714633]
[-0.01846463  0.01709567  0.82188915  0.01194215]
[ 0.18385422  0.05714633  0.01194215  0.79183899]]

The product Diattenuator . Retarder .  Depolarizer =

[[ 0.79050245  0.31080475  0.13624337 -0.2012983 ]
[-0.22657717 -0.64638751 -0.16880211 -0.25056315]
[ 0.19461597  0.0578249   0.53578195 -0.31627667]
[ 0.17512845  0.33887398 -0.24140583 -0.45722454]]


The symmetric decomposition, introduced by Ossikovski,* is a decomposition that treats the ordering of the Mueller matrix product as symmetric. That is, the Mueller matrix $$\mathbf{M}$$ is decomposed into

$\mathbf{M} = \mathbf{M}_{D_2} \mathbf{M}_{R_2} \mathbf{M}_\Delta \mathbf{M}_{R_1} \mathbf{M}_{D_1}$

where $$\mathbf{M}_{D_1}$$ and $$\mathbf{M}_{D_2}$$ are a diattenuators, $$\mathbf{M}^\prime_{R_1}$$ and $$\mathbf{M}^\prime_{R_2}$$ are retarders, and $$\mathbf{M}_\Delta$$ is a diagonal depolarizer.

*R. Ossikovski, “Analysis of depolarizing Mueller matrices through a symmetric decomposition,” J. Opt. Soc. Am. A, 26(5), 1109-1118 (2009).

In [16]:

M = RandomMuellerMatrix(2)
diatten2, ret2, depol, ret1, diatten1 = Symmetric_Decomposition(M)
printMatrix("M",M)
printMatrix("diatten2",diatten2)
printMatrix("ret2",ret2)
printMatrix("depol",depol)
printMatrix("ret1",ret1)
printMatrix("diatten1",diatten1)
printMatrix("The product Diattenuator2 . Retarder2 .  Depolarizer . Retarder1 . Diattenuator1",
diatten2 @ ret2 @ depol @ ret1 @ diatten1)


M =

[[ 1.31346355 -0.70433204 -0.05792289 -0.42159147]
[-0.59531681  0.30343934 -0.47341912  0.52095325]
[ 0.31783609 -0.24582591 -0.39663839  0.37667674]
[ 0.27500732 -0.26594446  0.04012768 -0.18661019]]

diatten2 =

[[ 1.         -0.42564353  0.31261478  0.10704983]
[-0.42564353  0.94073622 -0.0722223  -0.02473135]
[ 0.31261478 -0.0722223   0.8954451   0.01816399]
[ 0.10704983 -0.02473135  0.01816399  0.84862125]]

ret2 =

[[ 1.          0.          0.          0.        ]
[ 0.          0.70579373 -0.26909955  0.6553172 ]
[ 0.          0.69389627  0.44894296 -0.56299039]
[ 0.         -0.14269958  0.85207726  0.5035883 ]]

depol =

[[1.31231491 0.         0.         0.        ]
[0.         1.31231491 0.         0.        ]
[0.         0.         0.24752227 0.        ]
[0.         0.         0.         0.24752227]]

ret1 =

[[ 1.          0.          0.          0.        ]
[ 0.         -0.01944212 -0.73046083  0.6826778 ]
[ 0.         -0.9627385   0.19788894  0.18432186]
[ 0.         -0.26973428 -0.65365659 -0.70709015]]

diatten1 =

[[ 1.         -0.50453294 -0.15004366 -0.31959462]
[-0.50453294  0.93027935  0.04234118  0.09018718]
[-0.15004366  0.04234118  0.80049588  0.02682087]
[-0.31959462  0.09018718  0.02682087  0.84503274]]

The product Diattenuator2 . Retarder2 .  Depolarizer . Retarder1 . Diattenuator1 =

[[ 1.31346355 -0.70433204 -0.05792289 -0.42159147]
[-0.59531681  0.30343934 -0.47341912  0.52095325]
[ 0.31783609 -0.24582591 -0.39663839  0.37667674]
[ 0.27500732 -0.26594446  0.04012768 -0.18661019]]


The differential decomposition is simply the matrix logarithm of the Mueller matrix.

In [17]:

M = RandomMuellerMatrix(4)
L = MuellerLog(M)
printMatrix("M",M)
printMatrix("L",L)
printMatrix("MuellerExp(L)",MuellerExp(L))


M =

[[ 0.90254645  0.27385362  0.30832524 -0.28841948]
[-0.32530387 -0.31270702 -0.36702936 -0.22067358]
[-0.10609641 -0.01203455 -0.22551256  0.15574008]
[-0.40125467 -0.27026781 -0.04613747  0.37130898]]

L =

[[-0.28858548  0.16185659  0.85000633 -0.74234137]
[-1.10744528 -2.89640883 -6.81363824  0.49688726]
[ 0.53841303  1.40068116  0.37418308  0.5630028 ]
[-1.01346942 -0.95694393 -2.34927816 -0.91739108]]

MuellerExp(L) =

[[ 0.90254645  0.27385362  0.30832524 -0.28841948]
[-0.32530387 -0.31270702 -0.36702936 -0.22067358]
[-0.10609641 -0.01203455 -0.22551256  0.15574008]
[-0.40125467 -0.27026781 -0.04613747  0.37130898]]


## Other parameters¶

One can obtain other parameters associated with a Mueller matrix.

In [18]:

M = RandomMuellerMatrix(4)

print("Tmax = ",M.Tmax())
print("Tmin = ",M.Tmin())
print("diattenuation = ",M.diattenuation())
print("linear diattenuation = ",M.linear_diattenuation())
print("polarization dependent loss = ",M.polarization_dependent_loss())
print("polarizance = ",M.polarizance())
print("depolarization index = ",M.depolarization_index())
print("extinction_ratio = ",M.extinction_ratio())

Tmax =  1.1130487259621076
Tmin =  0.3550748776407327
diattenuation =  0.5162874886428319
linear diattenuation =  0.5008220205047619
polarization dependent loss =  4.961942308101194
polarizance =  0.5633462437213892
depolarization index =  0.6589112048969448
extinction_ratio =  3.1346873463920426


There is also a class CharacterizedMueller that performs a Lu-Chipman decomposition and extracts parameters from them. This is treated as a separate class, because initialization performs the decomposition, assigning values to the parameters. Note that the parameters differ from those shown above, due to differences in definition.

1. Ghosh, M.F.G. Wood, and I.A. Vitkin, ‘’Mueller matrix decomposition for extraction of individual polarization parameters from complex turbid media exhibiting multiple scattering, optical activity, and linear birefringence,’’ J. Biomedical Opt. 13, 044036 (2008).

In [19]:

parametersM = CharacterizedMueller(M)
print(parametersM)

Mdepol =
[[ 1.          0.          0.          0.        ]
[-0.08476877  0.27377418 -0.03728996  0.09656993]
[-0.20204637 -0.03728996  0.44373316 -0.09981802]
[-0.26307903  0.09656993 -0.09981802  0.59248849]]
Mret =
[[ 1.00000000e+00 -9.45424294e-17 -7.28583860e-17 -5.55111512e-17]
[ 0.00000000e+00 -5.62772927e-01 -5.92775720e-01 -5.76110734e-01]
[-1.24900090e-16 -3.11903969e-01  7.97714968e-01 -5.16107299e-01]
[ 5.55111512e-17  7.65508031e-01 -1.10759990e-01 -6.33821488e-01]]
Mdiatten =
[[ 0.7340618  -0.34245359  0.13371808  0.09206573]
[-0.34245359  0.71472066 -0.03360347 -0.0231362 ]
[ 0.13371808 -0.03360347  0.64178295  0.00903401]
[ 0.09206573 -0.0231362   0.00903401  0.63488175]]
DiattenuationVector = [-0.46651875  0.18216188  0.12541959]
Diattenuation = 0.516287488642832
CircularDiattenuation = 0.12541959458812751
LinearDiattenuation = 0.5008220205047619
DiattenuationAngle = 1.384664060309693 rad (79.33540669919348 deg)
PolarizanceVector = [-0.08476877 -0.20204637 -0.26307903]
Polarizance = 0.342372684079807
DepolarizationCoefficient = 0.4366652735461514
LinearRetardance = 2.2572802432682404 rad (129.33263111753394 deg)
OpticalRotation = 0.4371035715152117 rad (25.04418985791638 deg)