# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
# As a basic numerical computation :
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import binom
import sys

question = input('Please Enter Question Number: (1/2)')

def Can_Kocagil_21602218_Hw1(question):
    if question == '1' :
        # %%
        print('QUESTION 1 part (a) \n')

        # Let's create the matrix A :
        A = np.array([[1, 0, -1, 2],
                    [2, 1, -1, 5],
                    [3, 3, 0, 9]])

        # Since alpha ve beta are arbitrary scalars:
        alpha, beta = (np.random.randn() , np.random.randn())

        # Hand driven solution to the system of Ax = b, x_n is:
        x_n = np.array([[alpha - 2 * beta],
                    [-alpha - beta],
                    [alpha],
                    [beta]])

        # Verification of the solution x_n to the system A * x_n = 0 :
        print(f"Proof that x_n solves the linear system of Ax = b is \n {A @ x_n} \n")

        Q1_TEST = lambda x_n : np.isclose(np.zeros((3,1)), A @ x_n)

        print(f'Q1 Verification \n {Q1_TEST(x_n)}')

        # %%
        print('QUESTION 1 part (b) \n')

        # Let's create the matrix A :
        A = np.array([[1, 0, -1, 2],
                    [2, 1, -1, 5],
                    [3, 3, 0, 9]])

        # Let's create output vector b :
        b = np.array([[1],
                    [4],
                    [9]])

        # Particular solution to the system A * x_p = b :
        x_p = np.array([[1],
                    [2],
                    [0],
                    [0]])

        # Verification of the solution x_n to the system A * x_p = 0 :
        print(f"Proof that x_p solves the linear system of Ax = b is \n {A @ x_p} \n")

        Q1_TEST = lambda x_p : np.isclose(b, A @ x_p)

        print(f'Q1 Verification \n {Q1_TEST(x_p)}')

        # %%
        print('QUESTION 1 part (c) \n')

        # Let's create the matrix A :
        A = np.array([[1, 0, -1, 2],
                    [2, 1, -1, 5],
                    [3, 3, 0, 9]])

        # Since alpha ve beta are arbitrary scalars:
        alpha, beta = (np.random.randn() , np.random.randn())

        # Hand driven solution to the system of Ax = b, x_general is:
        x_general = np.array([[alpha - 2 * beta + 1],
                            [-alpha - beta + 2],
                            [alpha],
                            [beta]])

        # Verification of the solution x_general to the system A * x_general = b :
        print(f"Proof that x_general solves the linear system of Ax = b is \n {A @ x_general} \n")

        Q1_TEST = lambda x_general : np.isclose(b, A @ x_general)

        print(f'Q1 Verification \n {Q1_TEST(x_general)}')

        # %%
        print('QUESTION 1 part (d) \n')

        # Let's apply SVD on matrix A :
        U, S, V_T = np.linalg.svd(A)

        # Little bit of calculation :
        (m,n) = A.shape
        S_plus = np.zeros((m,n))
        S_plus[:m, :m] = np.diag(np.concatenate((1 / S[0:2], np.array([0]))))

        print(f"Pseudo-inverse of A, A_plus is \n {V_T.T @ S_plus.T @ U.T} ")

        Q1_TEST = lambda S_plus : np.isclose( np.linalg.pinv(A), V_T.T @ S_plus.T @ U.T)

        print(f'Q1 Verification \n {Q1_TEST(S_plus)}')

        # %%
        print('QUESTION 1 part (e) \n')
        #from tabulate import tabulate
        # Our hand-driven alpha and beta values :
        alphas = [1,0,0,0,-1,2]
        betas  = [1,0,.5,2,0,0]

        # Let's see whether our alpha-beta values are correct or not:
        table = [[(s_alpha,s_beta),np.array([[s_alpha - 2 * s_beta + 1],
                                            [-s_alpha - s_beta + 2],
                                            [s_alpha],
                                            [s_beta]]).T,(A @ np.array([[s_alpha - 2 * s_beta + 1],
                                            [-s_alpha - s_beta + 2],
                                            [s_alpha],
                                            [s_beta]])).T] for  s_alpha,s_beta in zip(alphas,betas)]

        #print(tabulate(table,headers = ['Alpha-Beta','Sparsest x',' A dot Sparsest X'],tablefmt = 'fancy_grid'))

        # %%
        print('QUESTION 1 part (f) \n')

        print(f"The least norm solution to the system is \n {np.linalg.pinv(A) @ b}")

                ##question 1 code goes here

    elif question == '2' :
        # %%
        print('QUESTION 2 part (a) \n')

        # Given probability ranges :
        prob_range = np.arange(0, 1.00, 0.001)
        language = [binom.pmf(k = 103, n = 869 , p = prob) for prob in prob_range]
        not_language = [binom.pmf(k = 199, n = 2353, p = prob) for prob in prob_range]

        def plot_likelihood(likelihood : list or np.ndarray,
                            xticks : tuple or np.ndarray = (0, 0.05, 0.1, 0.15, 0.2),
                            xtick1 : np.ndarray = np.arange(0, 201, step=50),
                            color  : str = 'orange',
                            xlim         = None,
                            xlabel : str = 'Probability Range',
                            ylabel : str = 'Likehoods',
                            title  : str = 'Likelihood function of tasks involving language') -> None:

            """
            Given the likelihood array or list of float, plots the likelihood function w.r.t.
            given probability range.

                Parameters:
                    - likelihood (list[float] or np.ndarray) : Likelihood function to be plotted
                    - xticks (tuple[float] or np.ndarray)    : set the current tick locations and labels of the x-axis
                    - color (str)                            : Color of the figure
                    - xlim (int)                             : The limit of the x label
                    - xlabel (str)                           : The text of x label
                    - ylabel (str)                           : The text of y label
                    - title (str) :                          : The title of the figure

                Returns:
                    - None

            """

            plt.figure(figsize = (6,6))
            plt.bar(np.arange(len(likelihood)), likelihood, color = color)

            if xlim is not None:
                plt.xlim(0, xlim)

            plt.xticks(xtick1, xticks )
            plt.xlabel(xlabel)
            plt.ylabel(ylabel)
            plt.title(title)
            plt.show(block=False)

        plot_likelihood(language,
                        xlim = 200)
        plot_likelihood(not_language,
                        color = 'green',
                        title = 'Likelihood function of tasks not involving language',
                        xlim = 200)

        # %%
        print('QUESTION 2 part (b) \n')

        table = [ [task, prob_range[np.argmax(likelihood)], max(likelihood)] for likelihood,task in zip([language,not_language], ['Language Involving Tasks', 'Language Not Involving Tasks'])]

        #print(tabulate(table,headers = ['Tasks','Probability that maximixes','Maximum value'],tablefmt = 'fancy_grid'))
        print(table)

        # %%
        print('QUESTION 2 part (c) \n')

        def bayes_theorem(likelihood : np.ndarray, prior : float) -> np.ndarray:
            """
            Given the likelihood function and prior distribution,
            computes and returns the posterior distribution by Bayes' Rule

                Parameters:
                    - likelihood (np.ndarray) : likelihood function (e.g., language or not_language)
                    - prior (float)           : prior distribution as a probability value

                Returns:
                    - Posterior probability (np.ndarray) with normalization

            """

            # Normalizing all likelihood values
            normalization_constant = np.sum(likelihood * prior)

            # Computing posterior distribution
            posterior = likelihood * prior

            return posterior/normalization_constant

        uniform_prior = 1 / len(prob_range)

        posterior_language = bayes_theorem(np.array(language),uniform_prior)
        plot_likelihood(likelihood = posterior_language,
                        color = 'b',
                        title = 'Posterior distribution for language involving tasks',
                        xlim = 200)

        posterior_not_language = bayes_theorem(np.array(not_language),uniform_prior)
        plot_likelihood(likelihood = posterior_not_language,
                        color = 'purple',
                        title = 'Posterior distribution for not language involving tasks',
                        xlim = 200)

        # %%
        # Calculating CDF of language involving tasks and plottings:
        posterior_language_cdf = [np.sum(posterior_language[:until]) for until in range(1, len(prob_range) + 1)]

        plot_likelihood(likelihood = posterior_language_cdf,
                        xticks     = np.around(np.arange(0, 1.001, 0.1), 2),
                        xtick1     = np.arange(0, 1001, 100),
                        title      = 'Cumulative distribution of tasks involving language',
                        ylabel     = 'Cumulative Distribution Function (CDF)',
                        color      = 'm')

        # Calculating CDF of not language involving tasks and plottings:
        posterior_not_language_cdf = [np.sum(posterior_not_language[:until]) for until in range(1, len(prob_range) + 1)]

        plot_likelihood(likelihood = posterior_not_language_cdf,
                        xticks     = np.around(np.arange(0, 1.001, 0.1), 2),
                        xtick1     = np.arange(0, 1001, 100),
                        title      = 'Cumulative distribution of tasks not involving language',
                        ylabel     = 'Cumulative Distribution Function (CDF)',
                        color      = 'red')

        # %%
        # Efficients calculations of CDF:
        cdf_language = np.empty(len(posterior_language))
        for i in range(len(posterior_language)):
            if i == 0:
                cdf_language[i] = posterior_language[i]
            else:
                cdf_language[i] = cdf_language[i-1] + posterior_language[i]

        plot_likelihood(likelihood = cdf_language,
                        xticks     = np.around(np.arange(0, 1.001, 0.1), 2),
                        xtick1     = np.arange(0, 1001, 100),
                        title      = 'Cumulative distribution of tasks involving language',
                        ylabel     = 'Cumulative Distribution Function (CDF)',
                        color      = 'm')

        # Efficients calculations of CDF:
        posterior_not_language_cdf = np.empty(len(posterior_not_language))
        for i in range(1, len(posterior_not_language)):
            if i == 0:
                posterior_not_language_cdf[i] = posterior_not_language[i]
            else:
                posterior_not_language_cdf[i] = posterior_not_language_cdf[i-1] + posterior_not_language[i]

        plot_likelihood(likelihood = posterior_not_language_cdf,
                        xticks     = np.around(np.arange(0, 1.001, 0.1), 2),
                        xtick1     = np.arange(0, 1001, 100),
                        title      = 'Cumulative distribution of tasks not involving language',
                        ylabel     = 'Cumulative Distribution Function (CDF)',
                        color      = 'g')

        # %%
        lower_bound = 0.025
        upper_bound = 0.975
        flags = [True] * 4

        i = 0
        while any(flags) and i < len(prob_range):

            if cdf_language[i] >= lower_bound and flags[0]:
                lower_confidence_interval_l = prob_range[i]
                flags[0] = False

            if cdf_language[i] >= upper_bound and flags[1]:
                higher_confidence_interval_l = prob_range[i]
                flags[1] = False

            if posterior_not_language_cdf[i] >= lower_bound and flags[2]:
                lower_confidence_interval_nl = prob_range[i]
                flags[2] = False

            if posterior_not_language_cdf[i] >= upper_bound and flags[3]:
                higher_confidence_interval_nl = prob_range[i]
                flags[3] = False

            i += 1

        print(f"Lower 95% confidence for language involving tasks likelihood CDF {lower_confidence_interval_l} ")

        print(f"Higher 95% confidence for language involving tasks likelihood CDF {higher_confidence_interval_l} ")

        print(f"Lower 95% confidence for language not involving tasks likelihood CDF {lower_confidence_interval_nl} ")

        print(f"Higher 95% confidence for language not involving tasks likelihood CDF {higher_confidence_interval_nl} ")

        # %%
        print('QUESTION 2 part (d) \n')
        plt.figure()
        joint = np.outer(posterior_language.T,posterior_not_language)
        plt.imshow(joint)
        plt.colorbar()
        plt.title('The joint posterior distribution')
        plt.xlabel('Language Involving RV (X_l)')
        plt.ylabel('Language Not Involving RV (X_nl)')
        plt.xticks(np.arange(len(posterior_language), step=100),
                np.round(np.arange(0.1,1.1,0.1),3))
        plt.yticks(np.arange(len(posterior_language), step=100),
                np.round(np.arange(0.1,1.1,0.1),3))
        plt.show(block=False)

        # %%
        # computing the P(X_l > X_nl | data) and P(X_nl >= X_l | data)

        assert len(posterior_language) == len(posterior_not_language)

        lower_tri_sum = 0
        upper_and_diag_tri_sum = 0
        for i in range(len(posterior_language)):
            for j in range(len(posterior_not_language)):
                if i > j:
                    lower_tri_sum += joint[i,j]
                else:
                    upper_and_diag_tri_sum += joint[i,j]

        print(f"Sum of entries of lower triangle of joint distribution (i.e., P(X_l > X_nl | data)) = {lower_tri_sum} \n")

        print(f"Sum of entries of upper triangle and diagonal of joint distribution (i.e., P(X_nl >= X_l | data) )  = {upper_and_diag_tri_sum}")

        # %%
        print('QUESTION 2 part (e) \n')
        # Here, let's recall and recompute the conditional probabilities :
        max_prop_l  = prob_range[np.argmax(language)]
        max_prop_nl = prob_range[np.argmax(not_language)]

        # P(Language) = 0.5 is given :
        p_language = .5

        # Here is the Bayes' Rule for inferensing:
        p_language_given_activation = max_prop_l * p_language / ( ( max_prop_l * p_language) + (max_prop_nl * (1-p_language)))

        print(f"P(language|activation) = {p_language_given_activation} ")

    else:
        print('Wrong Number question, please give 1 or 2')

Can_Kocagil_21602218_Hw1(question)


import sympy
import time

## From Bent: 2000_1 Computer Bonus
## Computer Bonus
##
## Find the smallest integer greater than one which has the property that
## the sum of all the integral divisors of its square equals a perfect
## cube. One and N are both divisors of N.
##
## -- Pierre de Fermat (circa 1650)
##
## Smallest solution (other than 1) = 43098
## This is also the only solution below 1e7
##

max_n = 1_000_000
solutions = []

t1=time.time()
for n in range(1,max_n+1):
    x=sympy.factorint(n*n)
    s=1
    for k in x:
        t=1
        for m in range(x[k]):
            t=k*t+1
        s=s*t
    # s = sum of the factors of the square of n
    # Check to see if it is a perfect cube
    c=round(s**(1/3))
    if c*c*c == s:
        print("Solution = ",n,"   Time = ",time.time()-t1)
        solutions.append(n)
t2=time.time()
print("Total elapsed time = ",t2-t1)
print("Largest value tested = ",max_n)
print("Number of solutions = ",len(solutions))
print("Solution set:")
print(solutions)


import matplotlib.pyplot as plt
import numpy as np

u = np.linspace(-1, 1, 200)
v = np.linspace(0, 3, 360)
uu, vv = np.meshgrid(u, v)
z0 = uu + 1j * vv
z = (1j * z0 + 1) / (z0 + 1j)

T = np.arctan2(uu, vv)

plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
plt.scatter(uu, vv, c=T, s=10, lw=0, cmap='hsv')
plt.title('real points')
plt.xlabel('Re(z)')
plt.ylabel('Im(z)')
plt.axis('equal')
plt.grid(True)

plt.subplot(1, 2, 2)
plt.scatter(np.real(z), np.imag(z), c=T, s=10, lw=0, cmap='hsv')
plt.title('poincare disk')
plt.xlabel('Re(z)')
plt.ylabel('Im(z)')
plt.axis('equal')
plt.grid(True)
plt.show()


import math
import sympy
import datetime

class RSA_key_gen:
    exponent_choices = [29, 23, 19, 17, 13, 7, 5, 3]

    n = 0
    d = 0
    e = 0

    test_ints = [0, 1, 2, 4, 3, 6, 7, 9, 16, 23, 512, 1024, 2056, 513, 112, 111, 100, 233]

    def __init__(self, min_prime_size=1, max_prime_size=100000000, *args, **kwargs):
        self.prime1 = sympy.randprime(min_prime_size, max_prime_size)
        self.prime2 = sympy.randprime(min_prime_size, max_prime_size)
        duplicates = 0
        while(self.prime2 == self.prime1):
            self.prime2 = sympy.randprime(min_prime_size, max_prime_size)
            duplicates += 1
            if duplicates > 4:
                print('Range invalid.')
                exit()

    def rsa_pair_is_valid(self):
        valid = True
        for test_int in self.test_ints:
            if test_int < self.n:
                cyper_text = pow(test_int, self.e, mod=self.n)
                plain_text = pow(cyper_text, self.d, mod=self.n)
                if test_int != plain_text:
                    valid = False
                    break
        return valid

    def calculate_d(phi, e):
        multiple = 1
        while (phi * multiple + 1) % e != 0:
            multiple += 1
        return (multiple * phi + 1) / e

    def create_pair(self):
        print(f'For primes: {self.prime1} {self.prime2}')
        self.n = self.prime1 * self.prime2
        phi = (self.prime1 - 1) * (self.prime2 - 1)
        self.e = 3
        for option in self.exponent_choices:
            if option < phi and math.gcd(option, phi) == 1:
                self.e = option
        self.d = RSA_key_gen.calculate_d(phi, self.e)
        print(f'The smallest corresponding RSA key pair is: n={self.n}, e={self.e}, phi={phi}, d={self.d}')
        self.d = int(self.d)
        print(f'Key is valid: {self.rsa_pair_is_valid()}')

    def break_pair(self):
        print(f'For public key: n={self.n} and e={self.e}')
        digits_to_cut = int((math.log10(self.n) + 1) / 2) - 1
        divisor = pow(10, digits_to_cut)
        max_min = int(self.n / divisor) + 1
        if max_min % 2 == 0:
            max_min -= 1
        current_guess = max_min

        start_search = datetime.datetime.now()
        prime_numbers = sympy.primerange(max_min)
        for prime in prime_numbers:
            if pow(self.n, 1, mod=prime) == 0:
                current_guess = prime
                break
        if current_guess == max_min:
            print('Failed to find base primes.')
            exit()
        print(f'Broke the primes in: {datetime.datetime.now() - start_search}')

        other_prime = self.n / current_guess
        phi = (current_guess - 1) * (other_prime - 1)
        private_key = RSA_key_gen.calculate_d(phi, self.e)
        print(f'The private key is: {private_key}. With base primes: {current_guess} and {other_prime}.')

# if len(sys.argv) < 2:
#     print('Supply at minimum an action argument.')
#     print('Options are "create", "break d e", "create_and_break".')
#     exit()
# action = sys.argv[1]

# key_gen = RSA_key_gen()

# if action == 'create':
#     print('Creating new RSA pair')
#     key_gen.create_pair()
# elif action == 'break':
#     print('Breaking inputted RSA pair')
#     key_gen.n = int(sys.argv[2])
#     key_gen.e = int(sys.argv[3])
#     key_gen.break_pair()
# elif action == 'create_and_break':
#     print('Creating and breaking RSA pair')
#     key_gen.create_pair()
#     key_gen.break_pair()
# else:
#     print('Invalid action command.')
#     print('Options are "create", "break d e", "create_and_break".')
#     exit()


import numpy as np

def mse_loss(y_true, y_pred):
  # y_true and y_pred are numpy arrays of the same length.
  return ((y_true - y_pred) ** 2).mean()

y_true = np.array([1, 0, 0, 1])
y_pred = np.array([0, 0, 0, 0])

print(mse_loss(y_true, y_pred)) # 0.5


import numpy as np
from coptim.function import Function

class Brown(Function):

    def eval(self, x):
        assert len(x) == 2, '2 dimensional input only.'
        return (np.power(x[0] - 1e6, 2) +
                np.power((x[1] - (2 * 1e-6)), 2) +
                np.power((x[0] * x[1] - 2), 2))

    def gradient(self, x):
        assert len(x) == 2, '2 dimensional input only.'
        return np.array([
            2 * x[0] * (x[1] ** 2 + 1) - 4 * (x[1] + 500000),
            2 * (x[0] * (x[0] * x[1] - 2) + x[1] - (1 / 500000))
        ])

    def hessian(self, x):
        assert len(x) == 2, '2 dimensional input only.'
        dfdx1 = 2 + (2 * x[1] ** 2)
        dfdx1dx2 = 4 * x[0] * x[1] - 4
        dfdx2dx1 = 4 * x[0] * x[1] - 4
        dfdx2 = 2 + (2 * x[0] ** 2)

        return np.array([[dfdx1, dfdx1dx2], [dfdx2dx1, dfdx2]])


"""This module attempts to get two pieces of data to be as similar as possible
using only orthogonal transformations. Many analyses are unique only up to
orthonormal transformations

This module is specifically referring to matrices of shape
(in_features, out_features).

Restated, the goal is to minimize the frobenius distance between two matrices
using only an orthonormal matrix.

Restated, let A and B be matrices. First find

 R = argmin | NA - B |F
       N
    where N is orthonormal

And the result is (NA, B)

This problem is known as the Orthogonal Procrustes Problem and has a simple
solution using SVD: https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem

It's straightforward to convert to minimizing (A - BN) by noting that forbenius
norms are invariant to transposes and multiplications by units (+/- 1)
"""

import numpy as np
import scipy.linalg
import pytypeutils as tus
import pca3dvis.snapshot as snapshot

def match(a: np.ndarray, b: np.ndarray) -> np.ndarray:
    """Finds the matrix R that minimizes the frobenius norm of RA - B, where
    R is orthonormal.

    Args:
        a (np.ndarray[samples, features]): the first matrix to match
        b (np.ndarray[samples, features]): the second matrix to match

    Returns:
        np.ndarray: the orthonormal matching matrix R
    """
    tus.check_ndarrays(
        a=(a, ('samples', 'features'), ('float32', 'float64')),
        b=(b, (('samples', a.shape[0]), ('features', a.shape[1])), a.dtype)
    )
    m = b @ a.T
    u, _, vh = scipy.linalg.svd(m)
    return np.real(u @ vh)

def match_snaps(
        reference: snapshot.ProjectedSnapshot,
        tomatch: snapshot.ProjectedSnapshot) -> snapshot.ProjectedSnapshot:
    """Returns a new snapshot that is a transformation the tomatch snapshot
    to match the reference snapshot. Neither argument is modified.

    Reason why this works:

    Let A be reference projected samples.
    Let B be tomatch projected samples.

    Then |A - BN| = |(A-BN)'| = |A' - N'B'| = |N'B' - A'|
    """

    transform = match(tomatch.projected_samples.T, reference.projected_samples.T)
    return snapshot.ProjectedSnapshot(
        tomatch.projection_vectors @ transform.T,
        tomatch.projected_samples @ transform.T,
        tomatch.projected_sample_labels
    )


import numpy as np
import matplotlib.pyplot as plt

import jetFunctions as jet

dataPath = '11-jet/data/'
plotsPath = '11-jet/plots/'

########################################
#   Pressure calibration
########################################

p00, steps, count = jet.read(dataPath + 'calibration/00 Pa.txt')
p70, steps, count = jet.read(dataPath + 'calibration/70 Pa.txt')

pressure = [0, 70] # Pa
adc = [int(p00.mean()), int(p70.mean())]

linearFit = np.polyfit(adc, pressure, 1)

adcFit = np.linspace(int(p00.mean()), int(p70.mean()), 100)
pressureFit = np.polyval(linearFit, adcFit)

# Plot

plt.figure('Калибровка давления', figsize=(8, 6), dpi=200)

plt.title('Калибровочный график\nзависимости показаний АЦП от давления')
plt.xlabel('Далвение [Па]')
plt.ylabel('Отсчёты АЦП')

plt.plot(pressure, adc, '*', label='Измерения')
plt.plot(pressureFit, adcFit, label='P = {:.3f} * N - {:.2f} [Па]'.format(linearFit[0], -linearFit[1]))
plt.legend()

plt.grid(which='major')
plt.grid(which='minor', linestyle='--')
plt.minorticks_on()

plt.savefig(plotsPath + 'pressure-calibration.png')

########################################
#   Distance calibration
########################################

xTotalDistance = 50 / 1000 # m
xTotalSteps = 900 # steps
xStep = xTotalDistance / xTotalSteps # m / step

step = [0, xTotalSteps]
distance = np.array([0, xTotalDistance])

stepsLinearFit = np.polyfit(step, distance, 1)

stepFit = np.linspace(0, xTotalSteps, 100)
distanceFit = np.polyval(stepsLinearFit, stepFit)

# Plot

plt.figure('Калибровка расстояния', figsize=(8, 6), dpi=200)

plt.title('Калибровочный график\nзависимости перемещения трубки Пито от шага двигателя')
plt.xlabel('Количество шагов')
plt.ylabel('Перемещение трубки Пито [см]')

plt.plot(step, distance * 100, '*', label='Измерения')
plt.plot(stepFit, distanceFit * 100, label='X = {:.1e} * step [м]'.format(xStep))
plt.legend()

plt.grid(which='major')
plt.grid(which='minor', linestyle='--')
plt.minorticks_on()

plt.savefig(plotsPath + 'distance-calibration.png')

########################################
#   Processing
########################################

measuresDirectory = '100 measures/'

airDensity = 1.2 # kg / m^3

def calculateVelocity(filename, xCorrection):
    data, steps, count = jet.read(filename)

    p = np.polyval(linearFit, data) # Pa
    p[p < 3] = 0
    u = np.sqrt(2 * abs(p) / airDensity) # m / s

    dx = xStep * steps
    x = np.linspace(0, count * dx, count) - xCorrection / 1000

    return x, u, dx

def calculateOutgo(x, u, dx):
    dq = 2 * np.pi * airDensity * u * abs(x) * dx # kg / s
    q = sum(dq) / 2 # kg / s

    return q

x00, u00, dx = calculateVelocity(dataPath + measuresDirectory + '00 mm.txt', 27.9)
x10, u10, dx = calculateVelocity(dataPath + measuresDirectory + '10 mm.txt', 27.9)
x20, u20, dx = calculateVelocity(dataPath + measuresDirectory + '20 mm.txt', 27.9)
x30, u30, dx = calculateVelocity(dataPath + measuresDirectory + '30 mm.txt', 27.4)
x40, u40, dx = calculateVelocity(dataPath + measuresDirectory + '40 mm.txt', 28.5)
x50, u50, dx = calculateVelocity(dataPath + measuresDirectory + '50 mm.txt', 26.9)
x60, u60, dx = calculateVelocity(dataPath + measuresDirectory + '60 mm.txt', 26.5)
x70, u70, dx = calculateVelocity(dataPath + measuresDirectory + '70 mm.txt', 27.5)
x80, u80, dx = calculateVelocity(dataPath + measuresDirectory + '80 mm.txt', 28.9)
x90, u90, dx = calculateVelocity(dataPath + measuresDirectory + '90 mm.txt', 31.2)

q00 = calculateOutgo(x00, u00, dx)
q10 = calculateOutgo(x10, u10, dx)
q20 = calculateOutgo(x20, u20, dx)
q30 = calculateOutgo(x30, u30, dx)
q40 = calculateOutgo(x40, u40, dx)
q50 = calculateOutgo(x50, u50, dx)
q60 = calculateOutgo(x60, u60, dx)
q70 = calculateOutgo(x70, u70, dx)
q80 = calculateOutgo(x80, u80, dx)
q90 = calculateOutgo(x90, u80, dx)

# Plot

plt.figure('Скорость и расход', figsize=(10, 7), dpi=200)

plt.title('Скорость потока воздуха\nв сечении затопленной струи')
plt.xlabel('Положение трубки Пито относительно центра струи [мм]')
plt.ylabel('Скоровть воздуха [м/с]')

plt.plot(x00 * 1000, u00, label='Q (00 мм) = {:.2f} [г/с]'.format(q00 * 1000))
plt.plot(x10 * 1000, u10, label='Q (10 мм) = {:.2f} [г/с]'.format(q10 * 1000))
plt.plot(x20 * 1000, u20, label='Q (20 мм) = {:.2f} [г/с]'.format(q20 * 1000))
plt.plot(x30 * 1000, u30, label='Q (30 мм) = {:.2f} [г/с]'.format(q30 * 1000))
plt.plot(x40 * 1000, u40, label='Q (40 мм) = {:.2f} [г/с]'.format(q40 * 1000))
plt.plot(x50 * 1000, u50, label='Q (50 мм) = {:.2f} [г/с]'.format(q50 * 1000))
plt.plot(x60 * 1000, u60, label='Q (60 мм) = {:.2f} [г/с]'.format(q60 * 1000))
plt.plot(x70 * 1000, u70, label='Q (70 мм) = {:.2f} [г/с]'.format(q70 * 1000))
plt.plot(x80 * 1000, u80, label='Q (80 мм) = {:.2f} [г/с]'.format(q80 * 1000))
plt.plot(x90 * 1000, u90, label='Q (90 мм) = {:.2f} [г/с]'.format(q90 * 1000))
plt.legend()

plt.grid(which='major')
plt.grid(which='minor', linestyle='--')
plt.minorticks_on()

plt.savefig(plotsPath + 'velocity-outgo.png')


import pandas as pd
import altair as alt
from scipy.signal import savgol_filter
from math import sqrt
def averageUncertainty(X,ΔX):
    n = len(X)
    Average = sum(X)/n
    Uncertainty = sqrt(sum([(Xk-Average)**2 for Xk in X])/n/(n-1) +  ΔX**2/3)
    return (Average,Uncertainty)

def averageUncertaintyPrint(X,ΔX):
    print("The average is %s, and the unvertainty is %s"%averageUncertainty(X,ΔX))

def linearRegression(X,Y):
    n = len(X)
    x = sum(X) / n
    y = sum(Y) / n
    Sxx = sum([(xi - x)**2 for xi in X])
    Sxy = sum([(xi - x)*(yi - y) for (xi,yi) in zip(X,Y)])
    Syy = sum([(yi - y)**2 for yi in Y])
    k = Sxy/Sxx #斜率k
    b = y - k * x #截距b
    Uk = sqrt((Syy/Sxx - k**2)/(n-2))
    Ub = Uk*sqrt(sum([xi**2 for xi in X])/n)
    r = Sxy/sqrt(Sxx*Syy)#相关系数r
    return (k,b,Uk,Ub,r)
def linearRegressionPrint(X,Y):
    print("k = %s, b = %s, Uk = %s, Ub = %s, r = %s"%linearRegression(X,Y))

def pointGraph(X,Y,Xname="X",Yname="Y",color="#000000"):
    df = pd.DataFrame({
    Xname:X,
    Yname:Y,
    })
    Graph = alt.Chart(df).mark_circle(color=color).encode(
        alt.X(Xname),
        alt.Y(Yname),
        tooltip=[Xname,Yname],
    ).interactive()
    return Graph
def lineGraph(X,Y,Xname="X",Yname="Y",color="#66ccff"):
    df = pd.DataFrame({
        Xname:X,
        Yname:Y,
    })
    Graph = alt.Chart(df).mark_line(color=color).encode(
        alt.X(Xname),
        alt.Y(Yname),
        tooltip = [Xname,Yname]
    ).interactive()
    return Graph
def curveGraph(X,Y,Xname="X",Yname="Y",color="#66ccff",part=3,polynomialOrder=2):
    newY = savgol_filter(Y, int(len(Y)/part), polynomialOrder)
    return lineGraph(X,newY,Xname=Xname,Yname=Yname,color=color)
def linearRegressionGraph(X,Y,Xname="X",Yname="Y",color="#66ccff"):
    (k,b,Uk,Ub,r)=linearRegression(X,Y)
    f = lambda x:k*x+b
    X=sorted(X)
    left = X[0] + X[0] - X[1]
    right = X[-1] + X[-1] - X[-2]
    newX = [left,right]
    newY = [f(left),f(right)]
    return lineGraph(newX,newY,Xname=Xname,Yname=Yname,color=color)

aU = averageUncertainty
aUP = averageUncertaintyPrint
lR = linearRegression
lRP = linearRegressionPrint
pG = pointGraph
lG = lineGraph
cG = curveGraph
lRG = linearRegressionGraph


# Code by Desh Iyer

# ----------------------------------------------------------------

# PROBLEM STATEMENT

# 1) Generate a random sample with mean = 5, std. dev. = 2. 2) Plot the distribution. 3) Give the summary statistics

# ----------------------------------------------------------------

# TODO

# [X] - Generate a random sample with mean = 5, std. dev. = 2.
# [X] - Plot the distribution.
# [X] - Give the summary statistics

# ----------------------------------------------------------------

# SOLUTION

# Importing libraries
import numpy as np
import matplotlib.pyplot as plt

# Accept number of elements to generate thru' user input
numberSample = int(input("Number of samples: "))

# Generate the array using the numpy.random.normal(mean, std, numberOfElts) method
sampleArray = np.random.normal(5, 2, numberSample)

# Calculate mean and std. deviation of the generated array
sampleArrayMean = np.mean(sampleArray)
sampleArrayStd = np.std(sampleArray)
sampleArrayVar = np.var(sampleArray)

# Give the summary statistics
print("\nGenerated Sample List with {} elements:\n\n{}\n\n------SUMMARY------\n\nCalculated Mean = {:.3f}\nCalculated Standard Deviation = {:.3f}\nCalculated Variance = {:.3f}\n".format(
    numberSample, sampleArray, sampleArrayMean, sampleArrayStd, sampleArrayVar
))

# Plotting the graph
# Setting a grid
plt.grid()

# Setting graph title
plt.title("Normal distribution w/ Mean: 5 & Std. Deviation: 2")

# Setting axes labels
plt.xlabel('x')
plt.ylabel('Normal Distribution')

# Plotting and displaying graph
plt.hist(sampleArray)
plt.show()


import numpy as np

def pol2cart(angle, r):
    """
    Conversion from polar coordinates to cartesians
    :param angle:  Degrees of the speaker from [1, 0] vector in degree
    :param r: radius of the arena
    :return: [x, y] of the speaker, in cm relative to the center of the arena = [0, 0]
    """

    theta = np.deg2rad(angle)
    x = r * np.cos(theta)
    y = r * np.sin(theta)
    return x, y

def calc_d_angle(body_angle, head_angle):
    """
    Function that computes the relative angle between the head and the body, in range [-180, 180]
    :param body_angle: (N x 1 ndarray) - Angles in [0, 360]
    :param head_angle:  (N x 1 ndarray) - Angles in [0, 360]
    :return: dAngle - (N x 1 ndarray) - Relative angle in degrees, in range [-180, 180].
    """
    dAngle = np.mod(head_angle - body_angle, 360)
    if np.isscalar(dAngle):
        if np.abs(dAngle) > 180:
            dAngle -= 360
    else:
        rescale_at_inds = np.abs(dAngle) > 180
        dAngle[rescale_at_inds] -= 360
    return dAngle

def cart2pol(loc):
    """
    Converts the cartesian coordinates into polar ones
    :param loc:
    :return:
        r - distance from [0, 0]
        deg - angle to the location of the rat from the vector [1, 0], in degrees
    """

    theta = np.arctan2(loc[1], loc[0])
    rho = np.hypot(loc[0], loc[1])
    ang_wrapped = np.round(np.mod(np.rad2deg(theta), 360), 7)
    # Round -> Fix numerical instabilities
    # mod() -> wrap to 360
    return rho, ang_wrapped

def average_angle(direc_mat):
    """
    Averages the angle vectors along samples. The input matrix (NxM) is parsed as M vectors,
    each with N samples. Each M-tuple is averages into a single angle, and a vector of size
    N is returned.
    :param direc_mat: (NxM array) - M direction vectors of N samples each, [0, 360]. Averaging M samples.
    :return: (Nx1 array) - The average direction vector
    """
    n_direcs, n_vecs = direc_mat.shape
    xs = np.zeros(direc_mat[:, 0].shape)
    ys = np.zeros(direc_mat[:, 0].shape)
    rs = np.ones(direc_mat[:, 0].shape)

    for i in range(n_vecs):
        loc = pol2cart(direc_mat[:, i], rs)
        xs += loc[0]
        ys += loc[1]

    xs /= n_vecs
    ys /= n_vecs
    r, av_direcs = cart2pol((xs, ys))
    return av_direcs

def smooth_angles(direcs, kernel_size):
    """
    Smooth angle vector, with consideration of angle circularity: 1 deg is close to 359 deg
    :param direcs: (Nx1 vector) - Direction vector, [0, 360]
    :param kernel_size: (scalar) - The size of the smoothing kernel to be used. Should be even.
    :return: (1xN vector) - Smoothed direction vector
    """
    rs = np.ones(direcs.shape)
    loc = pol2cart(direcs, rs)
    [r, angle] = cart2pol([smooth(loc[0], kernel_size), smooth(loc[1], kernel_size)])
    return angle

def smooth(vec, window):
    """
    Function that smooths the input vector by a sliding window - analog to matlab's smooth().
    :param vec: (1xN vector) - The input signal
    :param window: (scalar) - The size of the smoothing kernel to be used. Should be even.
    :return: (1xN vector) - Smoothed signal
    """

    if (window % 2) == 0:
        window += 1
    vec_flat = vec.flatten()
    filt_sig = np.convolve(vec_flat, np.ones(window, dtype=int), 'valid') / window
    r = np.arange(1, window - 1, 2)
    ramp_up = np.cumsum(vec_flat[:window - 1])[::2] / r
    ramp_down = (np.cumsum(vec_flat[:-window:-1])[::2] / r)[::-1]
    return np.concatenate((ramp_up, filt_sig, ramp_down))

if __name__ == '__main__':
    pass

    # === Test angle averaging ===
    # ang = (np.random.randn(1000, 1) + 2) % 360
    # angs_smoothed = smooth_angles(ang, 9)

    # === Test angle averaging ===
    # direcs = np.arange(8)*45
    # n_vects = 1000
    # direc_mat = np.zeros([8, n_vects])
    # for i in range(n_vects):
    #     direc_mat[:, [i]] = np.reshape(direcs, [8, 1]) + np.random.randn(8, 1)
    # av_direcs = average_angle(direc_mat)
    # print(np.round(av_direcs, 2))

    # === Test direction conversion - vectors ===
    # direcs = np.arange(8)*45
    # rs = np.zeros(8)+1
    #
    # loc = pol2cart(direcs, rs)
    # [r, angle] = cart2pol(loc)

    # === Test direction conversion - scalars ===
    # for i in range(8):
    #     loc = pol2cart(45*i, 1)
    #     [r, angle] = cart2pol(loc)
    #     print(str(45*i) + ' ' + str(1) + ' -> ' + str(angle) + ' ' + str(r))


import math
from typing import Union
import numpy as np
import torch

def fourier_model_mat(datetimes: np.ndarray,
                      K: int,
                      period: Union[np.timedelta64, str],
                      output_fmt: str = 'float64') -> np.ndarray:
    """
    :param datetimes: An array of datetimes.
    :param K: The expansion integer.
    :param period: Either a np.timedelta64, or one of {'weekly','yearly','daily'}
    :param start_datetime: A np.datetime64 on which to consider the season-start; useful for aligning (e.g) weekly
    seasons to start on Monday, or daily seasons to start on a particular hour. Default is first monday after epoch.
    :param output_fmt: A numpy dtype, or 'dataframe' to output a dataframe.
    :return: A numpy array (or dataframe) with the expanded fourier series.
    """
    # parse period:
    name = 'fourier'
    if isinstance(period, str):
        name = period
        if period == 'weekly':
            period = np.timedelta64(7, 'D')
        elif period == 'yearly':
            period = np.timedelta64(int(365.25 * 24), 'h')
        elif period == 'daily':
            period = np.timedelta64(24, 'h')
        else:
            raise ValueError("Unrecognized `period`.")

    if not isinstance(datetimes, np.ndarray) and isinstance(getattr(datetimes, 'values', None), np.ndarray):
        datetimes = datetimes.values
    period_int = int(period / np.timedelta64(1, 'ns'))
    time_int = (datetimes.astype("datetime64[ns]") - np.datetime64(0, 'ns')).astype('int64')

    output_dataframe = (output_fmt.lower() == 'dataframe')
    if output_dataframe:
        output_fmt = 'float64'

    # fourier matrix:
    out_shape = tuple(datetimes.shape) + (K * 2,)
    out = np.empty(out_shape, dtype=output_fmt)
    columns = []
    for idx in range(K):
        k = idx + 1
        for is_cos in range(2):
            val = 2. * np.pi * k * time_int / period_int
            out[..., idx * 2 + is_cos] = np.sin(val) if is_cos == 0 else np.cos(val)
            columns.append(f"{name}_K{k}_{'cos' if is_cos else 'sin'}")

    if output_dataframe:
        if len(out_shape) > 2:
            raise ValueError("Cannot output dataframe when input is 2+D array.")
        from pandas import DataFrame
        out = DataFrame(out, columns=columns)

    return out

def fourier_tensor(time: torch.Tensor, seasonal_period: float, K: int) -> torch.Tensor:
    """
    Given an N-dimensional tensor, create an N+2 dimensional tensor with the 2nd to last dimension corresponding to the
    Ks and the last dimension corresponding to sin/cos.
    """
    out = torch.empty(time.shape + (K, 2))
    for idx in range(K):
        k = idx + 1
        for sincos in range(2):
            val = 2. * math.pi * k * time / seasonal_period
            out[..., idx, sincos] = torch.sin(val) if sincos == 0 else torch.cos(val)
    return out


import math
from typing import Any, Optional, Tuple

import numpy as np
from numpy import ndarray

def cylinder_area(r: float, h: float) -> float:
    """Obliczenie pola powierzchni walca.
    Szczegółowy opis w zadaniu 1.

    Parameters:
    r (float): promień podstawy walca
    h (float): wysokosć walca

    Returns:
    float: pole powierzchni walca
    """
    nan = float('nan')
    if r > 0 and h > 0:
        p = 2 * math.pi * r * r + 2 * math.pi * r * h
        return p
    else:
        return nan

def fib(n: int) -> np.ndarray or None:
    """Obliczenie pierwszych n wyrazów ciągu Fibonnaciego.
    Szczegółowy opis w zadaniu 3.

    Parameters:
    n (int): liczba określająca ilość wyrazów ciągu do obliczenia

    Returns:
    np.ndarray: wektor n pierwszych wyrazów ciągu Fibonnaciego.
    """
    if type(n) is int:
        vector_of_fib_numbers = np.empty(0, int)
        a_1 = 1
        a_2 = 1
        a_wyn = 0

        if n > 1:
            vector_of_fib_numbers = np.append(vector_of_fib_numbers, [a_1, a_2])
            for i in range(1, n):
                a_wyn = a_2 + a_1

                a_1 = a_2
                a_2 = a_wyn

                vector_of_fib_numbers = np.append(vector_of_fib_numbers, a_2)

            return vector_of_fib_numbers
        elif n == 1:
            vector_of_fib_numbers = np.append(vector_of_fib_numbers, a_1)
            return vector_of_fib_numbers
        else:
            return None

    else:
        return None

def matrix_calculations(a: float) -> Tuple[Any]:
    """Funkcja zwraca wartości obliczeń na macierzy stworzonej
    na podstawie parametru a.
    Szczegółowy opis w zadaniu 4.

    Parameters:
    a (float): wartość liczbowa

    Returns:
    touple: krotka zawierająca wyniki obliczeń
    (Minv, Mt, Mdet) - opis parametrów w zadaniu 4.
    """
    matrix = np.array([[a, 1, -a],
                       [0, 1, 1],
                       [-a, a, 1]])
    matrix_det = np.linalg.det(matrix)
    matrix_transponsed = np.transpose(matrix)

    if matrix_det != 0:
        matrix_inv = np.linalg.inv(matrix)
        t = matrix_inv, matrix_transponsed, matrix_det
    else:
        t = float('nan'), matrix_transponsed, matrix_det

    print(t)
    return t

def custom_matrix(m: int, n: int) -> Optional[ndarray]:
    """Funkcja zwraca macierz o wymiarze mxn zgodnie
    z opisem zadania 7.

    Parameters:
    m (int): ilość wierszy macierzy
    n (int): ilość kolumn macierzy

    Returns:
    np.ndarray: macierz zgodna z opisem z zadania 7.
    """
    if m > 0 and n > 0 and type(m) is int and type(n) is int:
        matrix = np.zeros((m, n))
        for i in range(m):
            for j in range(n):
                if i > j:
                    matrix[i, j] = i
                else:
                    matrix[i, j] = j

        return matrix
    else:
        return None

Metody numeryczne 2021/Laboratorium 1/main.py


import numpy as np

def transiteCartesianToPolar(vector):
    return np.arctan2(vector[1], vector[0])

def transitePolarToCartesian(angle):
    return np.array([np.cos(angle), np.sin(angle)])

def computeAngleBetweenVectors(vector1, vector2):
    vectoriseInnerProduct = np.dot(vector1, vector2.T)
    if np.ndim(vectoriseInnerProduct) > 0:
        innerProduct = vectoriseInnerProduct.diagonal()
    else:
        innerProduct = vectoriseInnerProduct
    angle = np.arccos(innerProduct/(computeVectorNorm(vector1) * computeVectorNorm(vector2)))
    return angle

def computeVectorNorm(vector):
    return np.power(np.power(vector, 2).sum(axis = 0), 0.5)

def getSymmetricVector(symmetricAxis, originalVector):
    orthogonalVector = symmetricAxis.dot(originalVector) * symmetricAxis / np.power(computeVectorNorm(symmetricAxis),2) - originalVector
    symmetricVector = originalVector + 2*orthogonalVector
    return symmetricVector

def calculateCrossEntropy(prediction, target, episilon = 1e-12):
    ce = -1 * sum([target[index] * np.log(prediction[index]+episilon) for index in range(len(prediction))])
    return ce


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

print("This script takes data.csv as default data")

alpha=float(input("Enter the value of alpha: "))
iterations=int(input("Enter number of iterations: "))
x_label=input("Enter x label: ")
y_label=input("Enter y label: ")

data=pd.read_csv("data.csv", header=None)

m=len(data)                                         # total data
x=np.hstack((data.iloc[:, 0])).reshape(m,1)         # creates a vector of (m,1) from 1st index
Y=np.hstack((data.iloc[:, 1])).reshape(m,1)         # creates a vector of (m,1) from 2nd index

ones= np.ones((m,1))                                # creates a vector of (m,1) of ones for x0
X=np.hstack((ones,x))                               # merge x and ones to X

theta= np.zeros((2,1))                              # theta vector of (2,1): theta0, theta1

def costfn(theta):                                  # calculates cost function
    temp=X.dot(theta)-Y
    return np.sum(np.power(temp,2))/(2*m)

def gradient_descent(theta):                                # calculates gradient descent
    for _ in range(iterations):
        temp=X.dot(theta)-Y
        temp2=X.transpose()
        temp=temp2.dot(temp)
        theta= theta - (alpha/m)*temp
    return theta

theta=gradient_descent(theta)
cost_value=costfn(theta)

print("Value of theta and minimum error")
print(theta)
print(cost_value)

plt.scatter(x, Y)                                   # scatter plot
plt.plot(x, X.dot(theta))                           # plot regression line
plt.xlabel(x_label)
plt.ylabel(y_label)
plt.show()

min_theta=int(np.amin(theta)-5)                     # creates a range for theta as input
max_theta=int(np.amax(theta)+5)

theta0=np.arange(min_theta,max_theta,0.1)           # creates theta0 from the range
theta1=np.arange(min_theta,max_theta,0.1)
j_vals= np.zeros((len(theta0),len(theta1)))         # to store the cost function value of theta0, theta1

t=np.ones((2,1))                                    # temporary theta

for i in range(len(theta0)):                        # storing the costfun values in j_vals
    for j in range(len(theta1)):
        t[0][0]=theta0[i]
        t[1][0]=theta1[j]
        j_vals[i][j]=costfn(t)

plt.contourf(theta0,theta1, j_vals)                 # plot contour
plt.show()


import numpy as np
import matplotlib.pyplot as pypl

'''
일반적인 미분. 기울기 = (f(x+h)-f(x-h))/2*h
'''
def numericalDiff(f, x) :
    h=1e-4
    return (f(x+h)-f(x-h))/(2*h)

def sample_func1(x) :
    return 0.01*x**2+0.1*x

'''
편미분 : 두개 이상의 독립변수가 존재하는 함수에서 하나의 독립변수에 대한 편미분은
나머지 독립변수들을 상수로 취급하여 미분하는 것. 두개 이상의 독립변수를 가진 함수의 기울기는
모든 독립변수에 대한 편미분을 벡터로 정리한 것을 말한다.
'''

def numericalGradient(f, x) :   # 입력 두개에 한정된 함수. 일반화가 필요함.
    h=1e-4
    grad=np.zeros(x.shape)
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x_copy=np.array(x)

            x_copy[i,j]=x[i,j]+h
            fh1=f(x_copy)

            x_copy[i,j]=x[i,j]-h
            fh2=f(x_copy)

            grad[i,j]=(fh1-fh2)/(2*h)

    return grad

def sample_func2(x) :
    return x[0]**2 + x[1]**2

# x=np.arange(0, 20, 0.1)
# y=sample_func1(x)
# pypl.xlabel('x')
# pypl.ylabel('f(x)')
# pypl.plot(x,y)
# pypl.show()
#
# print(numericalDiff(sample_func1,5))
# print(numericalDiff(sample_func1,10))

# print(numericalGradient(sample_func2,np.array([3.0, 4.0])))
# print(numericalGradient(sample_func2,np.array([0.0, 2.0])))
# print(numericalGradient(sample_func2,np.array([3.0, 0.0])))


import numpy as np
import matplotlib.pyplot as plt
import pylab
import math

def find_y_in_x(X,Y,x):
    m=  len(X)
    y = Y.copy()
    for k in range(1,m):
        y[0:m-k] = ((x-X[k:m])*y[0:m-k] + (X[0:m-k]-x)*y[1:m-k+1])/(X[0:m-k]-X[k:m])
    return y[0]

def main():
    X = np.array([0,math.pi/6,math.pi/4,math.pi/3,math.pi/2])
    Y = np.sin(X)**2
    x_in = math.pi/5
    y_true = np.sin(x_in)**2
    xnew = np.linspace(np.min(X),np.max(X),100)
    ynew = [find_y_in_x(X,Y,i) for i in xnew]
    plt.plot(X,Y,'o',xnew,ynew)
    plt.grid(True)
    y_in = find_y_in_x(X,Y,x_in)
    print(y_in-y_true)
    pylab.show()
main()


import numpy as np

# Write a function that takes as input a list of numbers, and returns
# the list of values given by the softmax function.
def softmax(L):
    softmaxList = []
    for i in np.exp(L):
        softmaxList.append(i*1.0/sum(np.exp(L)))
    return softmaxList

Deep Learning/Neural Networks/Intro to Neural Networks/softmax.py


import numpy as np

def lr(z):
    a = 1 / (1 + np.exp(-z))
    return a

### Random values Generation
p = np.random.normal(0, 1, 500) / 10000  # y = 1
q = np.random.normal(0, 4, 500) / 10000  # y = -1
sample = [p, q]
sample_min = np.min(sample)
sample = np.array(sample)
w = 2
eta = 0.1
update_sum = 0

updateValueList = [w]
### Gradient descent implementation of Linear Regression
for i in range(2):
    for j in range(500):
        if i == 0:
            update_sum += (1 - sample[i, j] * w) * (-sample[i, j])
        if i == 1:
            update_sum += (-1 - sample[i, j] * w) * (-sample[i, j])
w = w - 2 * eta * update_sum
updateValueList.append(w)
while np.abs(update_sum) > 0.001:
    for i in range(2):
        for j in range(500):
            if i == 0:
                update_sum += (1 - sample[i, j] * w) * (-sample[i, j])
            if i == 1:
                update_sum += (-1 - sample[i, j] * w) * (-sample[i, j])
    w = w - 2 * eta * update_sum
    updateValueList.append(w)

# print(updateValueList)
import matplotlib.pyplot as plt
# lenofupdatevalues = len(updateValueList)
# print(lenofupdatevalues)
# xx = np.linspace(0,1,lenofupdatevalues)
# print(len(xx))
# plt.scatter(xx, updateValueList, label= "Decision Boundary",linestyle='solid',color = "k")
print('Global Minima by Linear Regression we got', update_sum)
# plt.show()

### Gradient Descent implementation of Logistic Regression
update_sum = 0
w = 2
updateValueList = [w]
for i in range(2):
    for j in range(500):
        if i == 0:
            update_sum += (1 - lr(sample[i, j] * w)) * (-sample[i, j]) * (lr(sample[i, j] * w)) ** 2
        if i == 1:
            update_sum += (-1 - lr(sample[i, j] * w)) * (-sample[i, j]) * (lr(sample[i, j] * w)) ** 2
w = w - 2 * eta * update_sum
updateValueList.append(w)

while update_sum > 0.001:
    for i in range(2):
        for j in range(500):
            if i == 0:
                update_sum += (1 - lr(sample[i, j] * w)) * (-sample[i, j]) * (lr(sample[i, j] * w)) ** 2
            if i == 1:
                update_sum += (-1 - lr(sample[i, j] * w)) * (-sample[i, j]) * (lr(sample[i, j] * w)) ** 2
    w = w - 2 * eta * update_sum
    updateValueList.append(w)

# lenofupdatevalues = len(updateValueList)
# print(lenofupdatevalues)
# xx = np.linspace(0,1,lenofupdatevalues)
# print(len(xx))
# plt.scatter(xx, updateValueList, label= "Decision Boundary",linestyle='solid',color = "k")
print('Global Minima by Logistic Regression we got', update_sum)
# plt.show()


from typing import List, Tuple

import numpy as np

from .quantifier import ProblemType, UncertaintyQuantifier
from .variation_ratio import VariationRatio

def entropy(data: np.ndarray, axis: int) -> np.ndarray:
    """
    A utility method to compute the entropy. May also be used by other quantifiers which internally rely on entropy.

    Following standard convention, the logarithm used in the entropy calculation is on base 2.

    :param data: The values on which the entropy should be computed.
    :param axis: Entropy will be taken along this axis.
    :return: An array containing the entropies. It is one dimension smaller than the passed data (the specified axis was removed).
    """
    # Remove zeros from nn_outputs (to allow to take logs)
    # Note that the actual increment (1e-20) does not matter, as it is multiplied by 0 below
    increments = np.zeros(shape=data.shape, dtype=np.float32)
    indexes_of_zeros = data == 0
    increments[indexes_of_zeros] = 1e-20
    nonzero_data = data + increments

    # These arrays can be quite large and are not used anymore - we free the space for the operations below
    del increments, indexes_of_zeros

    # Calculate and return the entropy
    return -np.sum(data * np.log2(nonzero_data), axis=axis)

class PredictiveEntropy(UncertaintyQuantifier):
    """
    A predictor & uncertainty quantifier, based on multiple samples (e.g. nn outputs) in a classification problem

    The prediction is made using a plurality vote, i.e., the class with the highest value in most samples is selected.
    In the case of a tie, the class with the lowest index is selected.

    The uncertainty is quantified using the predictive entropy;
    the entropy (base 2) of the per-class means of the sampled predictions.
    """

    # docstr-coverage:inherited
    @classmethod
    def aliases(cls) -> List[str]:
        return ["predictive_entropy", "pred_entropy"]

    # docstr-coverage:inherited
    @classmethod
    def takes_samples(cls) -> bool:
        return True

    # docstr-coverage:inherited
    @classmethod
    def problem_type(cls) -> ProblemType:
        return ProblemType.CLASSIFICATION

    # docstr-coverage:inherited
    @classmethod
    def calculate(cls, nn_outputs: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        # For simplicity, we let the predictions be calculated by the Variation Ratio Code
        #    accepting a slight overhead from also calculating the actual variation ratio
        predictions, _ = VariationRatio.calculate(nn_outputs)

        # Take means over the samples
        means = np.mean(nn_outputs, axis=1)

        # The predictive entropy is the entropy of the means
        predictive_entropy = entropy(means, axis=1)

        return predictions, predictive_entropy


"""ЛР 3.5, Ларькин Владимир, М8О-303Б-18"""

import fire  # CLI
import numpy as np
from sympy import integrate

from utilities import str2fun

def get_points(x0, x, step):
    return [i for i in np.arange(x0, x + step, step)]

def get_values(points, f):
    return [f(x) for x in points]

def rectangle_method(x, h, f):
    return h * sum([f((x[i] + x[i + 1]) / 2) for i in range(len(x) - 1)])

def trapeze_method(y, h):
    n = len(y) - 1
    return h * (y[0] / 2 + sum([y[i] for i in range(1, n - 1)]) + y[n] / 2)

def simpson_method(y, h):
    n = len(y) - 1
    return h / 3 * (y[0] + sum([4 * y[i] for i in range(1, n, 2)]) +
                    sum([2 * y[i] for i in range(2, n - 1, 2)]) + y[n])

def runge_romberg_method(res, anal_val):
    k = res[0]['h'] / res[1]['h']
    err_rec = [abs(res[0]['Rectangle'] - res[1]['Rectangle']) / (k ** 2 - 1), abs(res[0]['Rectangle'] - anal_val) / (k ** 2 - 1)]
    err_trap = [abs(res[0]['Trapeze'] - res[1]['Trapeze']) / (k ** 2 - 1), abs(res[0]['Trapeze'] - anal_val) / (k ** 2 - 1)]
    err_sim = [abs(res[0]['Simpson'] - res[1]['Simpson']) / (k ** 4 - 1), abs(res[0]['Simpson'] - anal_val) / (k ** 4 - 1)]
    return {'Rectangle': err_rec, 'Trapeze': err_trap, 'Simpson': err_sim}

def main():
    """Численное интегрирование методами прямоугольников, трапеций и Симпсона"""

    init_dict = {
        "x": (-1, 1),
        "h": [0.5, 0.25],
        "f": "x / ((2 * x + 7) * (3 * x + 4))"
    }

    a, b = init_dict["x"]
    h_list = init_dict["h"]
    f_str = init_dict["f"]

    anal_val = float(integrate(f_str, ("x", a, b)))
    f = str2fun(f_str)

    save_res = []
    for h in h_list:
        points = get_points(a, b, h)
        values = get_values(points, f)

        res_rec = rectangle_method(points, h, f)
        res_trap = trapeze_method(values, h)
        res_sim = simpson_method(values, h)

        save_res.append({
            "h": h,
            "Rectangle": res_rec,
            "Trapeze": res_trap,
            "Simpson": res_sim
        })

    errors = runge_romberg_method(save_res, anal_val)

    print("Интеграл f(x) =", f_str, "от", a, "до", b)
    print("Аналитическое решение:", anal_val)
    print(f"-------------------------------------")
    print(f"|  h  | Прямоуг.|  Трап.  | Симпсон |")
    print(f"-------------------------------------")
    for res in save_res:
        h = res["h"]
        rec = res["Rectangle"]
        trap = res["Trapeze"]
        simp = res["Simpson"]
        print(f"|{h:5.2f}|{rec:9.6f}|{trap:9.6f}|{simp:9.6f}|")
    print(f"-------------------------------------")
    print(f"|   Погрешности (Рунге-Ромберг):    |")
    print(f"-------------------------------------")

    for i in range(len(errors["Rectangle"])):
        print(f"|{h_list[i]:5.2f}|{errors['Rectangle'][i]:9.6f}|{errors['Trapeze'][i]:9.6f}|{errors['Simpson'][i]:9.6f}|")
    print(f"-------------------------------------")

if __name__ == "__main__":
    fire.Fire(main)


# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.4.2
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# %% jupyter={"outputs_hidden": false}
from sympy import *
init_printing(pretty_print=True,use_latex=True)
# %matplotlib inline
import matplotlib.pyplot as plt
from numpy import arange

# %% jupyter={"outputs_hidden": false}
f,t,T,G = symbols('f t T G')

# %% jupyter={"outputs_hidden": false}
f = G*t
print ('f =', f)

# %% jupyter={"outputs_hidden": false}
p = plot((f.subs(G,25),(t,0,1)),((f-25).subs(G,25),(t,1,2)),((f-50).subs(G,25),(t,2,3)), \
         ylabel = '$f = 25 t$ [V]', xlabel = '$t$ [sec]', xlim=(0,3), figsize=(10,8))

# %% jupyter={"outputs_hidden": false}
c0 = integrate(f,(t,0,T))*(1/T)
print(c0)
print (c0.subs([(G,25),(T,1)]))

# %% jupyter={"outputs_hidden": false}
a1 = (2/T)*integrate(f*sin(2*pi*t),(t,0,T))

# %% jupyter={"outputs_hidden": false}
print(a1)
print( a1.subs([(T,1),(pi, 3.14),(G,25)]))

# %% jupyter={"outputs_hidden": false}
a,b = [],[]
for n in range(10):
    a.append((2/T)*integrate(f*sin(n*pi*t),(t,0,T)))
    b.append((2/T)*integrate(f*cos(n*pi*t),(t,0,T)))

# %% jupyter={"outputs_hidden": false}
a[9], b[9]

# %% jupyter={"outputs_hidden": false}

c = []
for n in range(1,10):
    print( a[n])
    expr = sqrt(a[n]**2 + b[n]**2)
    expr = expr.subs([(T,1),(pi, 3.14),(G,25)])
    # print expr
    # val = lambdify(t,expr,'numpy')
    c.append(expr)

# %% jupyter={"outputs_hidden": false}
fig,ax = plt.subplots(figsize=(10,8))
ax.bar(-.5+arange(1,10),c,alpha=.5)
ax.set_xticks([1,2,3,4,5,6,7,8,9,10])
ax.set_xlabel('$n$',fontsize=16)
ax.set_ylabel('Fourier coefficients $a_n^2 + b_n^2$',fontsize=16)

# %% jupyter={"outputs_hidden": false}


###_______________________ FelixBaumgartner Jump ___________________________###
###_____________________ Ordinary Differential Eqs _________________________###
# EDO: (ecuación que gobierna la caída de Felix es:)
    # $$\displaystyle m \frac{d^2 y}{d t^2} = -m g + D$$ #
# With:
    # $$D = \frac{1}{2} \rho v^2 C_D A$$
# Where:
    # * $m$ es la masa de Félix y la tomaremos $m = 80~\text{kg}$,
    # * $\rho$ es la densidad del aire **y depende de la altura**,
    # * $v = |\dot{y}|$ es la velocidad,
    # * $C_D$ es el coeficiente de rozamiento, que tomaremos* $C_D = 0.4$, y
    # * $A$ es un área de referencia y tomaremos $A = 1~\text{m}^2$.
# Source: http://fisicadepelicula.blogspot.com.es/2012/10/la-fisica-del-salto-baumgartner.html
# Además, necesitaremos la altura inicial $h_0 = 39000 m$.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
# from scipy.integrate import solve_ivp # Different solver
# from numpy.testing import assert_almost_equal

##_________________________ Temp ISA ___________________________##
# $$T(h) = \begin{cases} T_0 + \lambda h & 0 <= h <= 11000 \\ T(11000) & 11000 < h \end{cases}
# \\ ~\\ T_0 = 288.16 K \\
# \lambda = -6.5 \cdot 10^{-3}~\text{K/m}$$

# **¡Importante!** Si utilizas condicionales para comprobar las capas de la atmósfera,
# seguramente tus funciones fallarán si las quieres representar utilizando un `linspace`.
# Para estos casos es mejor utilizar la función `np.select`:
def T_ISA(h):
    """Temperature as a function of altitude using the ISA.

    Arguments
    ----------
    h : Altitude in metres.

    Returns
    --------
    T : Temperature in Kelvin.

    """
    # Con esta línea convertimos la entrada a un array
    h = np.asarray(h)
    # Initial values
    T0 = 288.16  # K
    ll = -6.5e-3 # K/m
    ## One way to do it (without h=np.asarray(h)) ##
    # if 0 <= h <= 11000:
    #     T = T0 + ll*h
    # elif 11000 < h:
    #     T = T0 + ll*11000
    ## Another way to do it with h as array ##
    # 0 <= h <= 110000 no funciona para arrays
    T1 = T0 + ll * h
    T2 = T0 + ll * 11000
    T = np.select([(0 <= h) & (h <= 11000), 11000 < h], [T1, T2])
    return T
# Si quieres comprobar que tus funciones hacen lo que deben, puedes ejecutar estos tests:
# print(assert_almost_equal(T_ISA(0), 288.16))
# print(assert_almost_equal(T_ISA(11000), 216.66))

##_________________________ Rho ISA ___________________________##
# $$ \rho(h) = \begin{cases} \rho_0 \left( \frac{T}{T_0} \right)^{-\frac{g}{\lambda R} - 1}
# & 0 <= h <= 11000 \\ \rho(11000)~e^{\frac{-g(z - 11000)}{R T}} & 11000 < h <= 20000
# \end{cases} $$
# $$\rho_0 = 1.225~\text{[SI]} \\
# R = 287~\text{[SI]}$$
def rho_ISA(h):
    """Density (rho) as a function of altitude using the ISA.

    Arguments
    ----------
    h : Altitude in metres.

    Returns
    --------
    rho : Density in [SI].

    """
    h = np.asarray(h)
    # Initial values
    T0 = 288.16  # K
    ll = -6.5e-3 # K/m
    rho0 = 1.225 # kg / m3
    g = 9.81     # m / s2
    R = 287      # [SI]
    rho1 = rho0 * (T_ISA(h) / T0) ** (-g / (ll * R) - 1)
    rho2 = rho0 * (T_ISA(11000) / T0) ** (-g / (ll * R) - 1) * np.exp(-g * (h - 11000) / (R * T_ISA(h)))
    rho = np.select([(0 <= h) & (h <= 11000), 11000 < h], [rho1, rho2])
    return rho

##_________________________ EDO ___________________________##
# System Equation
def f(y, t):
    g = 9.8  # m / s2
    C_D = 0.4
    A = 1.0  # m^2
    m = 80  # kg
    return np.array([
        y[1],
        -g + rho_ISA(y[0]) * y[1] ** 2 * C_D * A / (2 * m)
    ])
# Initial Conditions
y0 = np.array([39000, 0])
# Time vector
t = np.linspace(0, 250)
# Integrate the equation
sol = odeint(f, y0, t)
pos = sol[:, 0]  # Primera columna: posición
vel = sol[:, 1]  # Segunda columna: velocidad
# Representing the solution
with plt.style.context('seaborn-notebook'):
    fig, axes = plt.subplots(3, 1, sharex=True, figsize=(6, 6))
    line, = axes[0].plot(t, pos / 1e3, label="Position $y$")
    axes[0].set_ylabel("Position [km]")
    axes[1].plot(t, vel, '--', color=line.get_color(), label="Velocity $\dot{y}$")
    axes[1].set_ylabel("Velocity [m/s]")
    # axes[1].set_xlabel("Time (s)")
    axes[0].legend()
    axes[1].legend()
    axes[0].set_title("Felix Baumgartner free fall")
    axes[0].grid()
    axes[1].grid()
    fig.tight_layout()

##_________________________ Sound Barrier _________________________##
# La velocidad del sonido en el aire variará también, y lo hará de esta forma:
    # $$M = \frac{v}{c}$$
# where: $$c = \sqrt{\gamma R T}$$

# Como paso final, representa 𝑀 en función de 𝑡 y en la misma gráfica incluye una
# línea horizontal de trazo discontinuo donde 𝑀=1
gamma = 1.4
R = 287.0  # [SI]
c = np.sqrt(gamma * R * T_ISA(pos))

M = np.abs(vel) / c

with plt.style.context('seaborn-notebook'):
    plt.plot(t, M, 'r', label="Mach Number $\mathrm{M}$")
    plt.plot(t, np.ones_like(t), 'k--')
    plt.ylabel('Mach number')
    plt.xlabel('Time [s]')
    # plt.title("Mach number")
    plt.legend()
    plt.grid()


'''
If you have a story, you can simulate it!
100xp
Sometimes, the story describing our probability distribution does not have a named
distribution to go along with it. In these cases, fear not! You can always simulate
it. We'll do that in this and the next exercise.

In earlier exercises, we looked at the rare event of no-hitters in Major League Baseball.
Hitting the cycle is another rare baseball event. When a batter hits the cycle, he gets
all four kinds of hits, a single, double, triple, and home run, in a single game. Like
no-hitters, this can be modeled as a Poisson process, so the time between hits of the
cycle are also Exponentially distributed.

How long must we wait to see both a no-hitter and a batter hit the cycle? The idea is
that we have to wait some time for the no-hitter, and then after the no-hitter, we have
to wait for hitting the cycle. Stated another way, what is the total waiting time for
the arrival of two different Poisson processes? The total waiting time is the time waited
for the no-hitter, plus the time waited for the hitting the cycle.

Now, you will write a function to sample out of the distribution described by this story.

Instructions
-Define a function with call signature successive_poisson(tau1, tau2, size=1) that samples
the waiting time for a no-hitter and a hit of the cycle.
    -Draw waiting times tau1 (size number of samples) for the no-hitter out of an exponential
    distribution and assign to t1.
    -Draw waiting times tau2 (size number of samples) for hitting the cycle out of an exponential
    distribution and assign to t2.
    -The function returns the sum of the waiting times for the two events.
'''
import numpy as np

def successive_poisson(tau1, tau2, size=1):
    # Draw samples out of first exponential distribution: t1
    t1 = np.random.exponential(tau1, size)

    # Draw samples out of second exponential distribution: t2
    t2 = np.random.exponential(tau2, size)

    return t1 + t2


#!/usr/bin/env python
#
#----------------------------------------------------------------------
# Reference: https://epubs.siam.org/doi/pdf/10.1137/S0036144502417715

# In[1]:

import numpy as np
import matplotlib.pyplot as plt

from scipy.interpolate import lagrange

# mgena
#from pylab import *

print ("")
print ("01 ----------------------------")

#----------------------------------------------------------------------

# In[2]:

def f(t):
    return t ** 10 - 1 * t ** 9

ts = np.linspace(0, 1, 6)
fs = f(ts)
print(ts)

print ("02 ----------------------------")

# In[3]:

p4  = lagrange (ts[1:-1], fs[1:-1])
p51 = lagrange (ts[:-1],  fs[:-1])
p52 = lagrange (ts[1:],   fs[1:])
p6  = lagrange (ts, fs)
ts_cont = np.linspace(0, 1, 100)

# In[4]:

plt.plot (ts_cont, f(ts_cont), ts_cont, p6(ts_cont))

# In[5]:
#----------------------------------------------------------------------

class LagrangeBary:
    def __init__(self, ts, fs):
        self.ts = np.copy(ts)
        self.fs = np.copy(fs)
        self.compute_ws()

    def compute_ws_j(self, j):
        wsj = 1
        for k in range(len(self.ts)):
            if k == j:
                continue
            wsj /= self.ts[j] - self.ts[k]
        return wsj

    def compute_ws(self):
        n = len(self.ts)
        ws = np.ones(n)
        for j in range(n):
            ws[j] = self.compute_ws_j(j)
        self.ws = ws

    def remove_left(self):
        t = self.ts[0]
        for i in range(len(self.ws)):
            self.ws[i] *= self.ts[i] - t
        self.ts = np.delete(self.ts, 0)
        self.fs = np.delete(self.fs, 0)
        self.ws = np.delete(self.ws, 0)

    def add_right(self, ti, fi):
        self.ts = np.append(self.ts, ti)
        self.fs = np.append(self.fs, fi)

        for i in range(len(self.ws)):
            self.ws[i] /= self.ts[i] - ti

        w_new = self.compute_ws_j(len(self.ts) - 1)
        self.ws = np.append(self.ws, w_new)

    def __call__(self, t):
        for i in range(len(self.ts)):
            if self.ts[i] == t:
                return self.fs[i]
        n = len(self.ts)
        nom = sum(self.ws[j] * self.fs[j] / (t - self.ts[j]) for j in range(n))
        denom = sum(self.ws[j] / (t - self.ts[j]) for j in range(n))
        return nom / denom

#----------------------------------------------------------------------

# In[6]:

p_bary = LagrangeBary (ts[:-1], fs[:-1])

# In[7]:

plt.plot (ts_cont, p51(ts_cont), ts_cont, [p_bary(t) for t in ts_cont])

print ("03 ----------------------------")

# In[8]:

p_bary.remove_left()
plt.plot (ts_cont, p4(ts_cont), ts_cont, [p_bary(t) for t in ts_cont])

# In[9]:

p_bary.add_right(ts[-1], f(ts[-1]))
plt.plot (ts_cont, p52(ts_cont), ts_cont, [p_bary(t) for t in ts_cont])

print ("04 ----------------------------")

print ("")

#----------------------------------------------------------------------


"""
  Question 5: Poisson 2D with Conjugate Gradient
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pathlib
#%%
# Functions to use
p = lambda x, y: np.cos(x + y) * np.sin(x - y) # Analytic solution
f = lambda x, y: -4 * p(x, y) # RHS

# Conjugate gradient implementation
def ConjugateGradient(A, b, x, conv=1e-6):
  r = b - np.dot(A, x)
  p = r
  while np.linalg.norm(r) > conv: # Convergence criteria
    a = np.dot(p.T, r) / (np.dot(p.T, np.dot(A, r)))
    x = x + a * p
    r = r - a * np.dot(A, p)
    b = - np.dot(p.T, np.dot(A, r)) / np.dot(np.dot(A, p).T, p)
    p = r + b * p

  return x

# Create A and b
def createSystem(Nx, Ny, h, F, P):
  # Create A matrix
  A = np.zeros(((Nx-1)*(Ny-1), (Nx-1)*(Ny-1)))
  I = np.eye(Nx-1, Ny-1) # Identity matrix
  D = np.zeros((Nx-1, Ny-1)) # For 5 points stencil matrix
  np.fill_diagonal(D[1:], 1) # Lower diagonal
  D += D.T # Upper diagonal
  np.fill_diagonal(D, -4) # Main diagonal
  # Fill A matrix
  # Upper block
  A[:Nx-1,:Ny-1] = D
  A[:Nx-1,Ny-1:2*(Ny-1)] = I
  # Lower block
  A[-(Nx-1):,-(Ny-1):] = D
  A[-(Nx-1):,-2*(Ny-1):-(Ny-1)] = I
  # Inner blocks
  for i in range(1, Nx-2):
    A[i*(Nx-1):(i+1)*(Nx-1),i*(Ny-1):(i+1)*(Ny-1)] = D
    A[i*(Nx-1):(i+1)*(Nx-1),(i-1)*(Ny-1):i*(Ny-1)] = I
    A[i*(Nx-1):(i+1)*(Nx-1),(i+1)*(Ny-1):(i+2)*(Ny-1)] = I

  # Create b vector
  B = h**2 * F[1:-1,1:-1] # Inside
  B[0] += P[0,1:-1] # South boundary
  B[-1] += P[-1,1:-1] # North boundary
  B[:,0] += P[1:-1,0] # West boundary
  B[:,-1] += P[1:-1,-1] # East boundary

  b = B.flatten('F') # Flatten column-major order (vectorization)

  return A, b

# Experiment
def experiment(h):
  Nx, Ny = int(1/h), int(1/h)

  # Grid definition
  x = np.linspace(0, 1, Nx + 1)
  y = np.linspace(0, 1, Ny + 1)

  # Grid to evaluate
  X, Y = np.meshgrid(x, y)

  F = f(X, Y) # RHS evaluation
  P = p(X, Y) # For boundary conditions

  # Build A and b
  A, b = createSystem(Nx, Ny, h, F, P)
  x0 = np.zeros(len(b)) # Initial guess

  # Solve
  u = ConjugateGradient(A, b, x0)

  # Numerical solution
  U = np.copy(P) # Boundary
  U[1:-1,1:-1] = u.reshape((Nx-1), (Ny-1)) # Approximation of interior points

  return X, Y, U, P

# 2D plot
def plot2D(x, y, u):
  plt.contourf(x, y, u)
  plt.colorbar()
  plt.show()

#%% Experiments
X1, Y1, U1, P1 = experiment(0.1)
X2, Y2, U2, P2 = experiment(0.05)
X3, Y3, U3, P3 = experiment(0.025)
#%% Plots
print("h=0.1")
plot2D(X1, Y1, U1)
plot2D(X1, Y1, P1)
plot2D(X1, Y1, np.abs(U1-P1)) # "Error"

#%%
print("h=0.05")
plot2D(X2, Y2, U2)
plot2D(X2, Y2, P2)
plot2D(X2, Y2, np.abs(U2 - P2))

#%%
print("h=0.025")
plot2D(X3, Y3, U3)
plot2D(X3, Y3, P3)
plot2D(X3, Y3, np.abs(U3-P3))

#%% Error
e1 = np.linalg.norm((U1-P1).flatten(), np.inf)
e2 = np.linalg.norm((U2-P2).flatten(), np.inf)
e3 = np.linalg.norm((U3-P3).flatten(), np.inf)

print("Error for each h")
print(e1, e2, e3)

#%% Structures to save
M1, N1 = U1.shape
M2, N2 = U2.shape
M3, N3 = U3.shape

U1f = np.zeros((M1 * N1, 3)); U1f[:,0] = X1.flatten(); U1f[:,1] = Y1.flatten(); U1f[:,2] = U1.flatten()
U2f = np.zeros((M2 * N2, 3)); U2f[:,0] = X2.flatten(); U2f[:,1] = Y2.flatten(); U2f[:,2] = U2.flatten()
U3f = np.zeros((M3 * N3, 3)); U3f[:,0] = X3.flatten(); U3f[:,1] = Y3.flatten(); U3f[:,2] = U3.flatten()
E1f = np.zeros((M1 * N1, 3)); E1f[:,0] = X1.flatten(); E1f[:,1] = Y1.flatten(); E1f[:,2] = np.abs(U1-P1).flatten()
E2f = np.zeros((M2 * N2, 3)); E2f[:,0] = X2.flatten(); E2f[:,1] = Y2.flatten(); E2f[:,2] = np.abs(U2-P2).flatten()
E3f = np.zeros((M3 * N3, 3)); E3f[:,0] = X3.flatten(); E3f[:,1] = Y3.flatten(); E3f[:,2] = np.abs(U3-P3).flatten()

#%% Save data
DIR = 'data/5/b/'
pathlib.Path(DIR).mkdir(parents=True, exist_ok=True) # Create Folder
# Write files
np.savetxt(DIR + 'U1.csv', U1f, fmt='%.8f', delimiter=' ')
np.savetxt(DIR + 'U2.csv', U2f, fmt='%.8f', delimiter=' ')
np.savetxt(DIR + 'U3.csv', U3f, fmt='%.8f', delimiter=' ')
np.savetxt(DIR + 'E1.csv', E1f, fmt='%.8f', delimiter=' ')
np.savetxt(DIR + 'E2.csv', E2f, fmt='%.8f', delimiter=' ')
np.savetxt(DIR + 'E3.csv', E3f, fmt='%.8f', delimiter=' ')


"""
Computing pi with parallel quadrature formula
"""
import numpy
from mpi4py import MPI
import sys
import random

comm = MPI.COMM_WORLD
mynode = comm.Get_rank()
totalnodes = comm.Get_size()

truepi = 3.141592653589793238462643
dest = 0
pi = numpy.zeros(1)

# Initialize value of n only if this is rank 0
# we are not checking the input, if the user does something that
# has no meaning we are going to fail badly!
if mynode == 0:
    if len(sys.argv) == 1:
        n = numpy.full(1, 20, dtype=int) # default value
    else:
        n = numpy.full(1,int(sys.argv[1]),dtype=int)
else:
    n = numpy.zeros(1, dtype=int)

# Broadcast n to all processes
comm.Bcast(n, root=0)

# Compute local quantities
h  = 2.0 / totalnodes
x1 = -1.0 + mynode * h
x2 = x1 + h
y1 = -1.0
y2 = 1.0
my_SqPoints  = numpy.zeros(1, dtype=int)
my_CiPoints  = numpy.zeros(1, dtype=int)

for i in numpy.arange(1,n+1,totalnodes):
    x = random.random()
    x = x1 + x*(x2-x1)
    y = random.random()
    y = y1 + y*(y2-y1)
    my_SqPoints = my_SqPoints + 1
    if ( x**2 + y**2 <= 1.0 ):
        my_CiPoints = my_CiPoints + 1

SqPoints = numpy.zeros(1, dtype=int)
CiPoints = numpy.zeros(1, dtype=int)

# Send back to the root process the data
comm.Reduce(my_SqPoints, SqPoints, MPI.SUM, dest)
comm.Reduce(my_CiPoints, CiPoints, MPI.SUM, dest)

# Only print the result in process 0
if mynode == 0:
    pi = 4.0*float(CiPoints)/float(SqPoints)
    print('The Integral Sum =', pi," The Error is ",numpy.abs(pi-truepi))


# -*- coding: utf-8 -*-
"""K-Means Clustering.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1jSTGlZ2TwwgaV7muv9JYsbAFBIa9dnsi

*Some contents of this notebook is from the [Python Data Science Handbook](http://shop.oreilly.com/product/0636920034919.do) by Jake VanderPlas*

# K-Means Clustering

Clustering algorithms seek to learn, from the properties of the data, an optimal division or discrete labeling of groups of points.

Many clustering algorithms are available in Scikit-Learn and elsewhere, but perhaps the simplest to understand is an algorithm known as *k-means clustering*, which is implemented in ``sklearn.cluster.KMeans``.
"""

# Commented out IPython magic to ensure Python compatibility.
# %matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()  # for plot styling
import numpy as np

"""## Understanding k-Means

The *k*-means algorithm searches for a pre-determined number of clusters within an unlabeled multidimensional dataset.
It accomplishes this using a simple conception of what the optimal clustering looks like:

- The "cluster center" is the arithmetic mean of all the points belonging to the cluster.
- Each point is closer to its own cluster center than to other cluster centers.

Those two assumptions are the basis of the *k*-means model.
We will soon dive into exactly *how* the algorithm reaches this solution, but for now let's take a look at a simple dataset and see the *k*-means result.

First, let's generate a two-dimensional dataset containing four distinct blobs.
To emphasize that this is an unsupervised algorithm, we will leave the labels out of the visualization
"""

from sklearn.datasets.samples_generator import make_blobs
X, y_true = make_blobs(n_samples=300, centers=4,
                       cluster_std=0.60, random_state=0)
plt.scatter(X[:, 0], X[:, 1], s=50);

X[:5]

"""Scalling the features"""

from sklearn.preprocessing import StandardScaler

data_scaler = StandardScaler()
X = data_scaler.fit_transform(X)
X[:5]

"""By eye, it is relatively easy to pick out the four clusters.
The *k*-means algorithm does this automatically, and in Scikit-Learn uses the typical estimator API:
"""

from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=4)
kmeans.fit(X)

y_kmeans = kmeans.predict(X)

# The lowest Sum Squared Error (SSE) value
print(kmeans.inertia_)

# The number of iterations required to converge
kmeans.n_iter_

"""Let's visualize the results by plotting the data colored by these labels.
We will also plot the cluster centers as determined by the *k*-means estimator:
"""

plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')

# plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='black', s=200, alpha=0.5);

plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='black', s=200, alpha=0.5);

"""## k-Means Algorithm: Expectation–Maximization

Expectation–Maximization (E–M) is a powerful algorithm that comes up in a variety of contexts within data science.
*k*-means is a particularly simple and easy-to-understand application of the algorithm, and we will walk through it briefly here.
In short, the expectation–maximization approach here consists of the following procedure:

1. Guess some cluster centers
2. Repeat until converged
   1. *E-Step*: assign points to the nearest cluster center
   2. *M-Step*: set the cluster centers to the mean

Here the "E-step" or "Expectation step" is so-named because it involves updating our expectation of which cluster each point belongs to.
The "M-step" or "Maximization step" is so-named because it involves maximizing some fitness function that defines the location of the cluster centers—in this case, that maximization is accomplished by taking a simple mean of the data in each cluster.

The literature about this algorithm is vast, but can be summarized as follows: under typical circumstances, each repetition of the E-step and M-step will always result in a better estimate of the cluster characteristics.

We can visualize the algorithm as shown in the following figure.
For the particular initialization shown here, the clusters converge in just three iterations.
For an interactive version of this figure, refer to the code in [the Appendix](06.00-Figure-Code.ipynb#Interactive-K-Means).

![(run code in Appendix to generate image)](https://github.com/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/figures/05.11-expectation-maximization.png?raw=1)
[figure source in Appendix](06.00-Figure-Code.ipynb#Expectation-Maximization)

The *k*-Means algorithm is simple enough that we can write it in a few lines of code.
The following is a very basic implementation:
"""

from sklearn.metrics import pairwise_distances_argmin

def find_clusters(X, n_clusters, rseed=2):
    # 1. Randomly choose clusters
    rng = np.random.RandomState(rseed)
    i = rng.permutation(X.shape[0])[:n_clusters]
    centers = X[i]

    while True:
        # 2a. Assign labels based on closest center
        labels = pairwise_distances_argmin(X, centers)

        # 2b. Find new centers from means of points
        new_centers = np.array([X[labels == i].mean(0)
                                for i in range(n_clusters)])

        # 2c. Check for convergence
        if np.all(centers == new_centers):
            break
        centers = new_centers

    return centers, labels

centers, labels = find_clusters(X, 4)
plt.scatter(X[:, 0], X[:, 1], c=labels,
            s=50, cmap='viridis');

"""### Caveats of expectation–maximization

There are a few drawbacks to be aware of k-means algorithm.

#### The globally optimal result may not be achieved
First, although the E–M procedure is guaranteed to improve the result in each step, there is no assurance that it will lead to the *global* best solution.
For example, if we use a different random seed in our simple procedure, the particular starting guesses lead to poor results:
"""

centers, labels = find_clusters(X, 4, rseed=0)
plt.scatter(X[:, 0], X[:, 1], c=labels, s=50, cmap='viridis');
plt.scatter(centers[:, 0], centers[:, 1], c='black', s=200, alpha=0.5);

"""Here the E–M approach has converged, but has not converged to a globally optimal configuration. For this reason, it is common for the algorithm to be run for multiple starting guesses, as indeed Scikit-Learn does by default (set by the ``n_init`` parameter, which defaults to 10).

#### The number of clusters must be selected beforehand
Another common challenge with *k*-means is that you must tell it how many clusters you expect: it cannot learn the number of clusters from the data.
For example, if we ask the algorithm to identify six clusters, it will happily proceed and find the best six clusters:
"""

labels = KMeans(6, random_state=0).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=labels,
            s=50, cmap='viridis');

"""#### Choosing the Appropriate Number of Clusters
In this section, you’ll look at two methods that are commonly used to evaluate the appropriate number of clusters:

* The elbow method
* The silhouette coefficient

These are often used as complementary evaluation techniques rather than one being preferred over the other. To perform the elbow method, run several k-means, increment k with each iteration, and record the SSE:

**Elbow method**
"""

kmeans_kwargs = {
    "init": "random",
    "n_init": 10,
    "max_iter": 30,
    "random_state": 42,
}

# A list holds the SSE values for each k
sse = []
for k in range(1, 11):
    kmeans = KMeans(n_clusters=k, **kmeans_kwargs)
    kmeans.fit(X)
    sse.append(kmeans.inertia_)

plt.style.use("fivethirtyeight")
plt.plot(range(1, 11), sse)
plt.xticks(range(1, 11))
plt.xlabel("Number of Clusters")
plt.ylabel("SSE")
plt.show()

"""##### Silhouette Coefficient
The silhouette coefficient is a measure of cluster cohesion and separation. It quantifies how well a data point fits into its assigned cluster based on two factors:

* How close the data point is to other points in the cluster
* How far away the data point is from points in other clusters

Silhouette coefficient values range between -1 and 1. Larger numbers indicate that samples are closer to their clusters than they are to other clusters.
"""

from sklearn.metrics import silhouette_score

# A list holds the silhouette coefficients for each k
silhouette_coefficients = []

# Notice you start at 2 clusters for silhouette coefficient
for k in range(2, 11):
    kmeans = KMeans(n_clusters=k, **kmeans_kwargs)
    kmeans.fit(X)
    score = silhouette_score(X, kmeans.labels_)
    silhouette_coefficients.append(score)

plt.style.use("fivethirtyeight")
plt.plot(range(2, 11), silhouette_coefficients)
plt.xticks(range(2, 11))
plt.xlabel("Number of Clusters")
plt.ylabel("Silhouette Coefficient")
plt.show()

"""#### k-means is limited to linear cluster boundaries
The fundamental model assumptions of *k*-means (points will be closer to their own cluster center than to others) means that the algorithm will often be ineffective if the clusters have complicated geometries.

In particular, the boundaries between *k*-means clusters will always be linear, which means that it will fail for more complicated boundaries.
Consider the following data, along with the cluster labels found by the typical *k*-means approach:
"""

from sklearn.datasets import make_moons
X, y = make_moons(200, noise=.05, random_state=0)
plt.scatter(X[:, 0], X[:, 1], s=50);

labels = KMeans(2, random_state=0).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=labels,
            s=50, cmap='viridis');

"""#### Density-Based Spatial Clustering of Applications with Noise (DBSCAN)
DBSCAN works based on the idea that a cluster in data space is a contiguous region of high point density, separated from other such clusters by contiguous regions of low point density.

The DBSCAN algorithm uses two parameters:

* min_samples: The minimum number of points (a threshold) clustered together for a region to be considered dense.
* eps (ε): A distance measure that will be used to locate the points in the neighborhood of any point.
"""

from sklearn.cluster import DBSCAN

kmeans = KMeans(n_clusters=2)
dbscan = DBSCAN(eps=0.3, min_samples=3)

kmeans.fit(X)
dbscan.fit(X)

# Compute the silhouette scores for each algorithm
kmeans_silhouette = silhouette_score(
    X, kmeans.labels_
).round(2)

dbscan_silhouette = silhouette_score(
   X, dbscan.labels_
).round(2)

print(kmeans_silhouette, dbscan_silhouette)

# Plot the data and cluster silhouette comparison
fig, (ax1, ax2) = plt.subplots(
    1, 2, figsize=(8, 6), sharex=True, sharey=True
)
fig.suptitle(f"Clustering Algorithm Comparison: Crescents", fontsize=16)
fte_colors = {
    0: "#008fd5",
    1: "#fc4f30",
}
# The k-means plot
# km_colors = [fte_colors[label] for label in kmeans.labels_]
ax1.scatter(X[:, 0], X[:, 1], c=kmeans.labels_, s=50, cmap='viridis')
ax1.set_title(
    f"k-means\nSilhouette: {kmeans_silhouette}", fontdict={"fontsize": 12}
)

# The dbscan plot,
# db_colors = [fte_colors[label] for label in dbscan.labels_]
ax2.scatter(X[:, 0], X[:, 1], c=dbscan.labels_, s=50, cmap='viridis')
ax2.set_title(
    f"DBSCAN\nSilhouette: {dbscan_silhouette}", fontdict={"fontsize": 12}
)
plt.show()

"""##### Evaluating Clusters
Since the ground truth labels are known, it’s possible to use a clustering metric that considers labels in its evaluation. You can use the scikit-learn implementation of a common metric called the adjusted rand index (ARI). Unlike the silhouette coefficient, the ARI uses true cluster assignments to measure the similarity between true and predicted labels.
"""

from sklearn.metrics import adjusted_rand_score

ari_kmeans = adjusted_rand_score(y, kmeans.labels_)
ari_dbscan = adjusted_rand_score(y, dbscan.labels_)
print(ari_kmeans, ari_dbscan)

"""#### k-means can be slow for large numbers of samples
Because each iteration of *k*-means must access every point in the dataset, the algorithm can be relatively slow as the number of samples grows.

## Example

Being careful about these limitations of the algorithm, we can use *k*-means to our advantage in a wide variety of situations.
We'll now take a look at a couple examples.

### Example: k-means on digits

To start, let's take a look at applying *k*-means on the same simple digits data.
Here we will attempt to use *k*-means to try to identify similar digits *without using the original label information*.

We will start by loading the digits and then finding the ``KMeans`` clusters.
"digits" dataset consist of 1,797 samples with 64 features, where each of the 64 features is the brightness of one pixel in an 8×8 image:
"""

from sklearn.datasets import load_digits
digits = load_digits()
digits.data.shape

"""The clustering can be performed as we did before:"""

kmeans = KMeans(n_clusters=10, random_state=0)
clusters = kmeans.fit_predict(digits.data)
print(kmeans.cluster_centers_.shape)
kmeans.cluster_centers_[0]

"""The result is 10 clusters in 64 dimensions.
Notice that the cluster centers themselves are 64-dimensional points, and can themselves be interpreted as the "typical" digit within the cluster.
Let's see what these cluster centers look like:
"""

fig, ax = plt.subplots(2, 5, figsize=(8, 3))
centers = kmeans.cluster_centers_.reshape(10, 8, 8)
for axi, center in zip(ax.flat, centers):
    axi.set(xticks=[], yticks=[])
    axi.imshow(center, interpolation='nearest', cmap=plt.cm.binary)

"""We see that *even without the labels*, ``KMeans`` is able to find clusters whose centers are recognizable digits, with perhaps the exception of 1 and 8.

Because *k*-means knows nothing about the identity of the cluster, the 0–9 labels may be permuted.
We can fix this by matching each learned cluster label with the true labels found in them:
"""

from scipy.stats import mode

labels = np.zeros_like(clusters)
for i in range(10):
    mask = (clusters == i)
    labels[mask] = mode(digits.target[mask])[0]

"""Now we can check how accurate our unsupervised clustering was in finding similar digits within the data:"""

from sklearn.metrics import accuracy_score
accuracy_score(digits.target, labels)

"""With just a simple *k*-means algorithm, we discovered the correct grouping for 80% of the input digits!
Let's check the confusion matrix for this:
"""

from sklearn.metrics import confusion_matrix
mat = confusion_matrix(digits.target, labels)
sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False,
            xticklabels=digits.target_names,
            yticklabels=digits.target_names)
plt.xlabel('true label')
plt.ylabel('predicted label');

"""As we might expect from the cluster centers we visualized before, the main point of confusion is between the eights and ones.
But this still shows that using *k*-means, we can essentially build a digit classifier *without reference to any known labels*!

t-SNE is a nonlinear embedding algorithm that is particularly adept at preserving points within clusters.
Let's see how it does:
"""

from sklearn.manifold import TSNE

# Project the data: this step will take several seconds
tsne = TSNE(n_components=2, init='random', random_state=0)
digits_proj = tsne.fit_transform(digits.data)

# Compute the clusters
kmeans = KMeans(n_clusters=10, random_state=0)
clusters = kmeans.fit_predict(digits_proj)

# Permute the labels
labels = np.zeros_like(clusters)
for i in range(10):
    mask = (clusters == i)
    labels[mask] = mode(digits.target[mask])[0]

# Compute the accuracy
accuracy_score(digits.target, labels)

"""That's nearly 93+% classification accuracy *without using the labels*."""


import matplotlib
import matplotlib.pyplot as plt

import numpy as np

from skimage import io
import os

import math

image = io.imread(os.getcwd() + '/Ejercicio-4/Img/ruido04.jpg', as_gray=True)

print(image.shape)

transform = np.fft.rfft2(image)

transformM = np.abs(transform).astype('float64')

m = 1/(transformM.min() - transformM.max())
b = 1-m*transformM.min()

for i in range(0, transformM.shape[0]):
    for j in range(0, transformM.shape[1]):
        transformM[i,j] *= m
        transformM[i,j] += b
# transformP = np.angle(transform)

def lowPass2D(x, y, w0):
    return w0/(w0 + 1j*(x**2 + y**2)**0.5)

def highPass2D(x, y, w0):
    return -(1j*(x**2 + y**2)**0.5)/(w0 + 1j*(x**2 + y**2)**0.5)

shape = transform.shape

transform1 = transform.copy()
for i in range(0, shape[0]):
    for j in range(0, shape[1]):
        transform1[i,j] *= lowPass2D(i,j,20)

filterImg1 = np.fft.irfft2(transform1)

transform2 = transform.copy()
for i in range(0, shape[0]):
    for j in range(0, shape[1]):
        transform2[i,j] *= highPass2D(i,j,1)

filterImg2 = np.fft.irfft2(transform2)

transform3 = transform.copy()
for i in range(0, shape[0]):
    for j in range(0, shape[1]):
        transform3[i,j] *= lowPass2D(i,0,20)

filterImg3 = np.fft.irfft2(transform3)

transform4 = transform.copy()
for i in range(0, shape[0]):
    for j in range(0, shape[1]):
        transform4[i,j] *= highPass2D(0,j,1)

filterImg4 = np.fft.irfft2(transform4)

fig, ax = plt.subplots(1, 1)
ax.imshow(image, cmap=plt.cm.gray)
ax.set_title('Original')
ax.axis('off')

fig1, ax1 = plt.subplots(1, 1)
ax1.imshow(transformM, cmap=plt.cm.gray)
ax1.set_title('Transformada de Fourier')
plt.xlim(0,50)
plt.ylim(0,50)

fig2, ax2 = plt.subplots(1, 1)
ax2.imshow(filterImg1, cmap=plt.cm.gray)
ax2.set_title('Filtro Pasa Bajas')
ax2.axis('off')

fig3, ax3 = plt.subplots(1, 1)
ax3.imshow(filterImg2, cmap=plt.cm.gray)
ax3.set_title('Filtro Pasa Altas')
ax3.axis('off')

fig4, ax4 = plt.subplots(1, 1)
ax4.imshow(filterImg3, cmap=plt.cm.gray)
ax4.set_title('Filtro Pasa Bajas Vertical')
ax4.axis('off')

fig5, ax5 = plt.subplots(1, 1)
ax5.imshow(filterImg4, cmap=plt.cm.gray)
ax5.set_title('Filtro Pasa Altas Horizontal')
ax5.axis('off')

fig1, ax1 = plt.subplots(1, 1)
ax1.hist(image.ravel(), bins=256, histtype='step', color='black', density=True)
ax1.set_title('Histograma Original')

plt.show()


import numpy as np
import math
from timeit import default_timer as timer

#the function image
def f(x,y):
    return 100*(y-x**2)**2+(1-x)**2

#The gradient of the function
def gradf(x,y):
	gx=400*x**3+x*(2-400*y)-2
	gy=200*(y-x**2)
	return np.array([gx,gy])

#hessian of the function
def hessian(x,y):
	fxx=-400*(y-x**2)+800*x**2+2
	fyy=200
	fxy=-400*x
	H=np.array([[fxx,fxy],[fxy,fyy]])
	return H

#lambda times identity function
def landaI(l,dim):
	I=np.zeros((dim,dim))
	for i in range(dim):
		for j in range(dim):
			if i==j:
				I[i,j]=l
	return I

#inverse of a 2x2 matrix
def inverse(A):
	copy=np.zeros((2,2))
	posd=1
	negd=1
	for i in range(0,2):
		for j in range(0,2):
			if i!=j:
				copy[i,j]=-A[i,j]
				negd=negd*A[i,j]
			if i==j:
				copy[i,j]=A[i-1,j-1]
				posd=posd*A[i,j]
	detA=posd-negd
	return 1/(detA)*copy

#product of a matrix times a vector
def prod(A,v):
	result=np.zeros(2)
	for i in range(0,2):
		for j in range(0,2):
			result[i]=result[i]+(A[i,j]*v[j])
	return result

#step function of LM method
def s(x,y,la):
	H=hessian(x,y)
	lambdaI=landaI(la,2)
	grad=gradf(x,y)
	suma=H+lambdaI
	inv=inverse(suma)
	return -prod(inv,grad)

#main function or Levenberg-Marquardt algorithm
def LVMD(xin,yin,e,Lambda):
	#setting the initial conditions
	start = timer()
	finish=False
	maxiter=int(1e4)
	points=np.zeros((maxiter+2,2))
	posx=xin
	posy=yin
	i=0

	while finish==False:
		#saving points in a point array
		points[i,0]=posx
		points[i,1]=posy

		#step
		sx,sy=s(posx,posy,Lambda)
		xnew=posx+sx
		ynew=posy+sy

		#extra calculations for the termination condition
		newgrad=gradf(xnew,ynew)
		diff=np.sqrt((newgrad[0])**2+(newgrad[1])**2)
		fnew=f(xnew,ynew)
		fold=f(posx,posy)

		#landa updating
		if fnew<fold:
			Lambda=Lambda/2
		elif fold<fnew:
			Lambda=2*Lambda

		if diff<e or i>maxiter:
			finish=True

		#updating variables
		i=i+1
		posx=xnew
		posy=ynew

	#some performance calculations
	end = timer()
	t=end-start
	print("Algorithm finished with",i,"iterations and a time of ",t,"seconds", "\nthe final point is (",posx,posy,")")
	return i,t,points

a,ti,pointv=LVMD(-1.5,-1,1e-9,0.001)
#uncomment this last two lines to get the points and a csv with the points of the algorithm
print(pointv)
np.savetxt("pointsLVMD.csv",pointv,delimiter=",")


import math
from typing import Optional

import numpy as np

# modified from https://stackoverflow.com/questions/34372480/
def rotate_points(px, py, origin, angle):
    """
    Rotate points counterclockwise by a given angle around a given origin.

    The angle should be given in degrees.
    """
    angle = math.radians(angle)
    ox, oy = origin

    qx = ox + np.cos(angle) * (px - ox) - np.sin(angle) * (py - oy)
    qy = oy + np.sin(angle) * (px - ox) + np.cos(angle) * (py - oy)
    return qx, qy

def normalize(arr: np.ndarray,
              vmin: Optional[float] = None,
              vmax:  Optional[float] = None,
              ):
    if (vmin is None) & (vmax is None):
        return arr

    amin = np.amin(arr)
    amax = np.amax(arr)
    narr = (arr - amin) / (amax - amin)
    vmin = 0 if vmin is None else vmin
    vmax = 1 if vmax is None else vmax
    return narr * (vmax - vmin) + vmin


# variance
"""
Variance is another number that indicates how spread out the values are.
In fact, if you take the square root of the variance,
you get the standard deviation.
Or the other way around, if you multiply the standard deviation by itself, you get the variance.
To calculate the variance, you have to do as follows.
1. Find the mean.
2. For each value: find the difference from the mean.
3. For each difference: find the square value.
4. The variance is the average number of these squared differences.
"""
# but you can user var() of numpy to find the variance.

import numpy as np

speed = np.array([32,111,138,28,59,77,97])

x = np.var(speed)

print("The variance: ", x)


import scipy.constants as s_const
from math import pi
#In condensed matter physics, the Madelung constant gives the total electric potential felt by an atom in a solid.
#In this program, we will use the sodium chloride wich is arranged in a cubic lattice. The vertexes of the cube alternate
#positive to negative with the same charge e. If we give each vertex three coordinates, then the sodium atoms will fall
# where j+k+i is even. The chloride atoms will fall where i+j+k is odd.

#We also know that the Madelung constant is equal to the summation of of the potential energy of all the atoms in the
#lattice divided by  (4*pi*epsilon_0)*the spacing of the lattice.
#The potential enery of a charge is Q/4*pi*epsilon*r with r being the distance between the atom and the origin.
#The summation domain is from one end of the cube to the other excluding the origin.
e = s_const.elementary_charge #absolute value of electron charge
epsilon_0 = s_const.epsilon_0 # permittivity of vacuum
L = 600 #1/2 the width of the cube
a =1 #spacing of the atoms
V_tot = 0 #initial potential energy
# This works but the O^3 iteration is a bummer. I think if I let it sit at L= 1000 my computer will die
for i in range(-L,L+1):
    for j in range(-L,L+1):
        for k in range(-L,L+1):
            if (i == 0 or j==0 or k==0):
                V_tot +=0
            elif ((i+j+k)%2 == 0):
                V_tot += e/(4*pi*epsilon_0*a*((i**2)+(j**2)+(k**2))**0.5)
            else:
                V_tot -= e/(4 * pi * epsilon_0 * a * ((i ** 2) + (j ** 2) + (k ** 2))**0.5)
print((V_tot*(4 * pi * epsilon_0 * a ))/e)


from statistics import mean
import numpy as np

class LSR:

    def __init__(self):
        self.slope = 0 #This represents the slope of our regression
        self.intercept = 0 #This represents the intercept of our regression
        self.r_squared = 0 #This represents the r^2 squared of our regression

    def Model(self, x, y):
        temp_x = np.array(x, dtype=np.float64) #numpy array of our x values
        temp_y = np.array(y, dtype=np.float64) #numpy array of our y values
        if(temp_x.shape == temp_y.shape):
            self.slope = ( ((mean(temp_x) * mean(temp_y)) - mean(temp_x * temp_y))/((mean(temp_x)*mean(temp_x)) - mean(temp_x*temp_x)) )
            self.intercept = mean(temp_y) - (self.slope * mean(temp_x))
            temp_y_approx = self.slope*temp_x + self.intercept
            self.r_squared = 1 - ( (np.sum((temp_y - temp_y_approx)**2))/(np.sum((temp_y - mean(temp_y))**2)) )
            print("Slope: ", self.slope)
            print("Offset: ", self.intercept)
            print("R^2 Squared: ", self.r_squared)
            del temp_x, temp_y, temp_y_approx
        else:
            print("X and Y are not the same shape")


import numpy as np

# axis 0 = selecting elements in vertical direction
# axis 1 = selecting elements in horizontal direction

a = np.linspace(1, 5, 4)          # gives 4 values between 1 to 5 with equal distance
print(a)

b = np.empty(shape=(2, 4))        # gives empty 2 arrays with 4 elements in each with random numbers in it
print(b)

c = np.empty_like(a)             # copying shape of other array
print(c)

d = np.identity(3)                # create 3*3 shape array
print(d)

e = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(e.T)                    # transverse elements from row to column and column to row

f = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(f.flatten())                            # gives iterator for iteration of array

g = np.array([1, 2, 3, 4, 6, 7, 35, 2, 2, 45, 34, 2, 43, 46])
print(g.argmin())                            # to find index of minimum values from array
print(g.argmax())                            # to find index of maximum values from array
print(g.argsort())                           # find indexes to sort the array

h = np.array([1, 2, 3, 4, 5])
i = np.array([1, 4, 9, 16, 25])
print(h + i)                       # also can other arithmetic operations like divide, subtract, addition
print(np.square(h))                # to square elements of array
print(np.sqrt(i))                  # for square root of elements in array

Python/Numpy/numpy 44 - code with harray.py