#!/usr/bin/env python
# coding: utf-8
#
# # Least squares examples
# $$
# \newcommand{\eg}{{\it e.g.}}
# \newcommand{\ie}{{\it i.e.}}
# \newcommand{\argmin}{\operatornamewithlimits{argmin}}
# \newcommand{\mc}{\mathcal}
# \newcommand{\mb}{\mathbb}
# \newcommand{\mf}{\mathbf}
# \newcommand{\minimize}{{\text{minimize}}}
# \newcommand{\diag}{{\text{diag}}}
# \newcommand{\cond}{{\text{cond}}}
# \newcommand{\rank}{{\text{rank }}}
# \newcommand{\range}{{\mathcal{R}}}
# \newcommand{\null}{{\mathcal{N}}}
# \newcommand{\tr}{{\text{trace}}}
# \newcommand{\dom}{{\text{dom}}}
# \newcommand{\dist}{{\text{dist}}}
# \newcommand{\R}{\mathbf{R}}
# \newcommand{\Z}{\mathbf{Z}}
# \newcommand{\SM}{\mathbf{S}}
# \newcommand{\ball}{\mathcal{B}}
# \newcommand{\bmat}[1]{\begin{bmatrix}#1\end{bmatrix}}
# $$
# __
ASE2030: Linear algebra and statistics, Inha University.
__
# _ Jong-Han Kim (jonghank@inha.ac.kr)
_
#
#
# ---
#
# ## Stage illumination
#
# A concert hall has a square stage that is divided into $m$ pixels (or regions), and we want to illuminate the stage by using $n$ fixed lamps which are located below the ceiling (but with different heights). We let $y_i$ denote the lighting level in pixel $i$, so the $m$-vector $y$ gives the illumination levels on the stage. We let $x_i$ denote the power level at which lamp $i$ operates, so the $n$-vector $x$ gives the set of lamp powers.
#
# The vector of illumination levels on the stage is a linear function of the lamp powers, so we have $y = Ax$ for some $m\times n$ matrix $A$. The $j$th column of $A$ gives the illumination pattern for lamp $j$, that is, the illumination when lamp $j$ has unit power and all other lamps are off, so it is determined by the relative geometry between the $j$th lamp and the stage surfaces. We assume that $m\gg n$ so we use way smaller number of lamps compared to the number of pixels that we divide the stage into. The $i$th row of $A$ gives the sensitivity of pixel $i$ to the $n$ lamp powers.
#
# The following cell defines the locations of 10 lamps, and builds the matrix $A$ that maps the lamp powers, $x$, to the illumination on the stage floor, $y$. The plot for example shows the achieved stage illumination pattern with the lamp locations, when Lamp-0 and Lamp-6 are on (with specific powers). So it is like the stage seen from the ceiling.
#
# In[9]:
import numpy as np
import matplotlib.pyplot as plt
N = 25
m = N*N
pix_x = np.arange(0,N).reshape(-1,1)@np.ones((1,N))
pix_y = np.ones((N,1))@np.arange(0,N).reshape(1,-1)
pixels = np.hstack(( pix_x.reshape(m,1), pix_y.reshape(m,1), \
np.zeros((m,1)) ))
n = 10
lamp_positions = np.array([[14.1, 21.3, 3.5], # [x-pos, y_pos, height]
[ 4.1, 20.4, 4.0], # for 10 lamps
[22.6, 17.1, 6.0],
[ 5.5, 12.3, 4.0],
[12.2, 9.7, 4.0],
[15.3, 13.8, 6.0],
[21.3, 10.5, 5.5],
[ 3.9, 3.3, 5.0],
[13.1, 4.3, 5.0],
[20.3, 4.2, 4.5]])
A = np.zeros((m,n))
for i in range(m):
for j in range(n):
A[i,j] = 1.0 / np.linalg.norm(pixels[i,:]-lamp_positions[j,:])**2
A *= m/np.sum(A)
x_ex = np.zeros(n) # example
x_ex[0] = 1.5 # lamp-1 on with power=1.5
x_ex[6] = 3.0 # lamp-5 on with power=3.0
y_ex = A.dot(x_ex) # illumination on stage
plt.figure(figsize=(8,6), dpi=100)
plt.imshow(y_ex.reshape(N,N).T, cmap='gray', origin='lower')
plt.plot(lamp_positions[:,0], lamp_positions[:,1], 'o', label='lamps')
for i in range(n):
plt.text(lamp_positions[i,0],lamp_positions[i,1], f" {i}", color="r" )
plt.colorbar()
plt.clim(0.5,1.5)
plt.xlabel(r'$x$-position')
plt.ylabel(r'$y$-position')
plt.title('illumination pattern on stage')
plt.legend()
plt.show()
#
#
# The goal of this problem is to find the vector of lamp powers, $x$, that results in a desired illumination pattern on stage $y^\text{des}$, such as $y^\text{des}= \mathbf{1}$, which is uniform illumination across the stage (the symbol $\mathbf{1}$ represents the one-vector where all of its elements are 1's). In other words we look for $x$ such that $Ax \approx y^\text{des}$. We can solve this by formulating the least squares problem minimizing $\| Ax-y^\text{des} \|_2^2$.
#
#
#
# **_(Problem 2a)_** What is the best lamp powers for uniform illumination across the stage? You may print the numbers out, or show them by a simple plot.
# In[10]:
# your code here
y_des = np.ones(m)
x_hat = np.linalg.lstsq(A, y_des, rcond=None)[0]
print(f"optimal x: {x_hat}")
plt.figure(figsize=(10,4), dpi=100)
plt.plot(x_hat,"o-")
plt.xlabel("lamp id")
plt.ylabel("lamp power")
plt.title("optimal lamp powers")
plt.grid()
plt.show()
#
#
# **_(Problem 2b)_** Display the stage illumination pattern obtained from the above solution, and display the stage illumination pattern obtained from other (non-optimal) simple approaches, for example $x = \mathbf{1}$ (the uniform lamp powers).
#
# Show that the stage illumination pattern obtained from your solution reveals better uniformity. You can compare the histograms of the illumination levels on $m$ pixels for each illumination pattern.
# In[11]:
# your code here
y_opt = A.dot(x_hat)
y_one = A.dot(np.ones(n))
y_rnd = A.dot(np.random.rand(n)*2)
plt.figure(figsize=(14,5), dpi=100)
plt.subplot(121)
plt.imshow(y_opt.reshape(N,N).T, cmap='gray', origin='lower')
plt.plot(lamp_positions[:,0], lamp_positions[:,1], 'o', label='lamps')
for i in range(n):
plt.text(lamp_positions[i,0],lamp_positions[i,1], f" {i}", color="r" )
plt.colorbar()
plt.clim(0.5,1.5)
plt.xlabel(r'$x$-position')
plt.ylabel(r'$y$-position')
plt.title('stage illumination under optimal lamp powers')
plt.legend()
plt.subplot(122)
plt.hist(y_opt)
plt.xlabel('illumination level')
plt.ylabel('frequency')
plt.title('stage illumination distribution under optimal lamp powers')
plt.xlim(0,2)
plt.ylim(0,160)
plt.show()
plt.figure(figsize=(14,5), dpi=100)
plt.subplot(121)
plt.imshow(y_one.reshape(N,N).T, cmap='gray', origin='lower')
plt.plot(lamp_positions[:,0], lamp_positions[:,1], 'o', label='lamps')
for i in range(n):
plt.text(lamp_positions[i,0],lamp_positions[i,1], f" {i}", color="r" )
plt.colorbar()
plt.clim(0.5,1.5)
plt.xlabel(r'$x$-position')
plt.ylabel(r'$y$-position')
plt.title('stage illumination under uniform lamp powers')
plt.legend()
plt.subplot(122)
plt.hist(y_one)
plt.xlabel('illumination level')
plt.ylabel('frequency')
plt.title('stage illumination distribution under uniform lamp powers')
plt.xlim(0,2)
plt.ylim(0,160)
plt.show()
plt.figure(figsize=(14,5), dpi=100)
plt.subplot(121)
plt.imshow(y_rnd.reshape(N,N).T, cmap='gray', origin='lower')
plt.plot(lamp_positions[:,0], lamp_positions[:,1], 'o', label='lamps')
for i in range(n):
plt.text(lamp_positions[i,0],lamp_positions[i,1], f" {i}", color="r" )
plt.colorbar()
plt.clim(0.5,1.5)
plt.xlabel(r'$x$-position')
plt.ylabel(r'$y$-position')
plt.title('stage illumination under random lamp powers')
plt.legend()
plt.subplot(122)
plt.hist(y_rnd)
plt.xlabel('illumination level')
plt.ylabel('frequency')
plt.title('stage illumination distribution under random lamp powers')
plt.xlim(0,2)
plt.ylim(0,160)
plt.show()
# In[11]: