Interpretation of Mueller Matrices based on the Polar decomposition, or the Lu-Chipman decomposition
written by Jaren N. Ashcraft
Katsu has features to simulate polarimetric instrumentation and reduce polarimetric data, resulting in a Mueller matrix. For further insights, we can look into the Polar decomposition by Lu and Chipman [1]. This technique decomposes a Mueller matrix \(\mathbf{M}\) into its constituent depolarizer \(\mathbf{M_{\Delta}}\), diattenuator \(\mathbf{M_{D}}\), and retarder \(\mathbf{M_{R}}\), as shown in the following Equation,
This function is critical for separating depolarization from the Mueller matrix. In this tutorial, we review the methods available in Katsu to perform this decomposition. Below, we initialize a random diattenuator, retarder, and depolarizer.
References
[1] Shih-Yau Lu and Russell A. Chipman, “Interpretation of Mueller matrices based on polar decomposition,” J. Opt. Soc. Am. A 13, 1106-1113 (1996) https://doi.org/10.1364/JOSAA.13.001106
[12]:
import numpy as np
from katsu.mueller import (
linear_diattenuator,
linear_retarder,
depolarizer
)
# create a Mueller matrix
M_diattenuator = linear_diattenuator(np.random.random(), np.random.random())
M_retarder = linear_retarder(np.random.random(), np.random.random())
M_depolarizer = depolarizer(np.random.random(), np.random.random(), np.random.random(), np.random.random())
print('Diattenuator')
print(M_diattenuator)
print('-'*30)
print('Retarder')
print(M_retarder)
print('-'*30)
print('Depolarizer')
print(M_depolarizer)
print('-'*30)
Diattenuator
[[0.85802071 0.10370314 0.09697308 0. ]
[0.10370314 0.85250275 0.00590091 0. ]
[0.09697308 0.00590091 0.85171026 0. ]
[0. 0. 0. 0.8461923 ]]
------------------------------
Retarder
[[ 1. 0. 0. 0. ]
[ 0. 0.91262605 -0.0027427 -0.40878621]
[ 0. -0.0027427 0.99991391 -0.01283196]
[ 0. 0.40878621 0.01283196 0.91253996]]
------------------------------
Depolarizer
[[1. 0. 0. 0. ]
[0. 0.11585728 0.12764087 0. ]
[0. 0.12764087 0.82168709 0. ]
[0. 0. 0. 0.27848385]]
------------------------------
In reality, optics we measure will be some combination of these three.
[13]:
M_under_test = M_depolarizer @ M_retarder @ M_diattenuator
print('As-measured Mueller Matrix')
print(M_under_test)
As-measured Mueller Matrix
[[ 0.85802071 0.10370314 0.09697308 0. ]
[ 0.02327453 0.09059165 0.1090549 -0.04146236]
[ 0.09148722 0.10223165 0.700155 -0.05307461]
[ 0.01215214 0.0970703 0.00371534 0.21504085]]
If we wanted to know what component of this is a diattenuator, Katsu has the routines in [1] built into katsu.mueller to do so. As shown below, the decomposition is capable of extracting the diattenuator from the total Mueller matrix.
[ ]:
from katsu.mueller import decompose_diattenuator
M_d = decompose_diattenuator(M_under_test)
print('Diattenuattor from Polar Decomposition')
print(M_d)
print('-'*30)
print('Diattenuattor we Specified')
print(M_diattenuator)
print('-'*30)
Diattenuattor from Polar Decomposition
[[0.85802071 0.10370314 0.09697308 0. ]
[0.10370314 0.85250275 0.00590091 0. ]
[0.09697308 0.00590091 0.85171026 0. ]
[0. 0. 0. 0.8461923 ]]
------------------------------
Diattenuattor we Specified
[[0.85802071 0.10370314 0.09697308 0. ]
[0.10370314 0.85250275 0.00590091 0. ]
[0.09697308 0.00590091 0.85171026 0. ]
[0. 0. 0. 0.8461923 ]]
------------------------------
Katsu can perform a simmilar operation to parse the retarder from the total Mueller matrix. The decompose_retarder method has the option to return both the diattenuator and retarder, which we show below. The diattenuator that returned is the same as what we gave it, but the retarder is not. Why could this be?
[16]:
from katsu.mueller import decompose_retarder
M_r, M_d = decompose_retarder(M_under_test, return_all=True) # if false, just returns retarder
print('Diattenuattor from Polar Decomposition')
print(M_d)
print('-'*30)
print('Diattenuattor we Specified')
print(M_diattenuator)
print('-'*30)
print('Retarder from Polar Decomposition')
print(M_r)
print('-'*30)
print('Retarder we Specified')
print(M_retarder)
print('-'*30)
Diattenuattor from Polar Decomposition
[[0.85802071 0.10370314 0.09697308 0. ]
[0.10370314 0.85250275 0.00590091 0. ]
[0.09697308 0.00590091 0.85171026 0. ]
[0. 0. 0. 0.8461923 ]]
------------------------------
Diattenuattor we Specified
[[0.85802071 0.10370314 0.09697308 0. ]
[0.10370314 0.85250275 0.00590091 0. ]
[0.09697308 0.00590091 0.85171026 0. ]
[0. 0. 0. 0.8461923 ]]
------------------------------
Retarder from Polar Decomposition
[[ 1.00000000e+00 0.00000000e+00 -7.04731412e-18 0.00000000e+00]
[ 0.00000000e+00 1.05384290e-01 1.27312114e-01 -4.89987408e-02]
[ 1.38777878e-17 1.14234735e-01 8.21266266e-01 -6.27216840e-02]
[ 1.73472348e-18 1.13840357e-01 3.57349440e-03 2.54127639e-01]]
------------------------------
Retarder we Specified
[[ 1. 0. 0. 0. ]
[ 0. 0.91262605 -0.0027427 -0.40878621]
[ 0. -0.0027427 0.99991391 -0.01283196]
[ 0. 0.40878621 0.01283196 0.91253996]]
------------------------------
It’s because we haven’t separated depolarization yet. The structure of a depolarizer looks very much like a kind of retarder. Indeed this is because depolarization can arize from rapidly varying polarization, like scatter. To perform the final step in the decomposition, we call decompose_depolarizer with the return_all=True keyword argument to get our original Mueller matrix separated into a depolarizer, retarder, and diattenuator.
[ ]:
from katsu.mueller import decompose_depolarizer
M_a, M_r, M_d = decompose_depolarizer(M_under_test, return_all=True) # if false, just returns depolarizer
print('Diattenuattor from Polar Decomposition')
print(M_d)
print('-'*30)
print('Diattenuattor we Specified')
print(M_diattenuator)
print('-'*30)
Diattenuattor from Polar Decomposition
[[0.85802071 0.10370314 0.09697308 0. ]
[0.10370314 0.85250275 0.00590091 0. ]
[0.09697308 0.00590091 0.85171026 0. ]
[0. 0. 0. 0.8461923 ]]
------------------------------
Diattenuattor we Specified
[[0.85802071 0.10370314 0.09697308 0. ]
[0.10370314 0.85250275 0.00590091 0. ]
[0.09697308 0.00590091 0.85171026 0. ]
[0. 0. 0. 0.8461923 ]]
------------------------------
[20]:
print('Retarder from Polar Decomposition')
print(M_r)
print('-'*30)
print('Retarder we Specified')
print(M_retarder)
print('-'*30)
Retarder from Polar Decomposition
[[ 1.00000000e+00 0.00000000e+00 -7.04731412e-18 0.00000000e+00]
[-2.69840694e-33 9.12626052e-01 -2.74270322e-03 -4.08786212e-01]
[ 3.06626967e-33 -2.74270322e-03 9.99913905e-01 -1.28319629e-02]
[ 7.70371978e-34 4.08786212e-01 1.28319629e-02 9.12539957e-01]]
------------------------------
Retarder we Specified
[[ 1. 0. 0. 0. ]
[ 0. 0.91262605 -0.0027427 -0.40878621]
[ 0. -0.0027427 0.99991391 -0.01283196]
[ 0. 0.40878621 0.01283196 0.91253996]]
------------------------------
[22]:
print('Depolarizer from Polar Decomposition')
print(M_a)
print('-'*30)
print('Depolarizer we Specified')
print(M_depolarizer)
print('-'*30)
Depolarizer from Polar Decomposition
[[ 1.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 1.15857278e-01 1.27640865e-01 -6.81314585e-18]
[ 1.38777878e-17 1.27640865e-01 8.21687089e-01 -5.84221487e-18]
[ 1.73472348e-18 -6.81314585e-18 -5.84221487e-18 2.78483848e-01]]
------------------------------
Depolarizer we Specified
[[1. 0. 0. 0. ]
[0. 0.11585728 0.12764087 0. ]
[0. 0.12764087 0.82168709 0. ]
[0. 0. 0. 0.27848385]]
------------------------------
And with the full depolarizing decomposition, we successfully return the depolarizer, retarder, and diattenuator that we put into the initial Mueller matrix. This tool is a powerful method of decomposing a Mueller matrix into components that are easier to digest.