"""
We use the Full Whiskas Model Python Formulation for the PuLP Modeller
We make the cost of different ingredients vary and observe the corresponding optimal quantity for ingredients
Authors: Antony Phillips, Dr Stuart Mitchell  2007

Modifications: Bertrand Travacca 2017
"""

# Import PuLP modeler functions
from pulp import *
import random as rd
import numpy as np

# Creates a list of the Ingredients
Ingredients = ['CHICKEN', 'BEEF', 'MUTTON', 'RICE', 'WHEAT', 'GEL']

# A dictionary of the costs of each of the Ingredients is created
reference_costs = {'CHICKEN': 0.013,
         'BEEF': 0.008,
         'MUTTON': 0.010,
         'RICE': 0.002,
         'WHEAT': 0.005,
         'GEL': 0.001}

# A dictionary of the protein percent in each of the Ingredients is created
proteinPercent = {'CHICKEN': 0.100,
                  'BEEF': 0.200,
                  'MUTTON': 0.150,
                  'RICE': 0.000,
                  'WHEAT': 0.040,
                  'GEL': 0.000}

# A dictionary of the fat percent in each of the Ingredients is created
fatPercent = {'CHICKEN': 0.080,
              'BEEF': 0.100,
              'MUTTON': 0.110,
              'RICE': 0.010,
              'WHEAT': 0.010,
              'GEL': 0.000}

# A dictionary of the fibre percent in each of the Ingredients is created
fibrePercent = {'CHICKEN': 0.001,
                'BEEF': 0.005,
                'MUTTON': 0.003,
                'RICE': 0.100,
                'WHEAT': 0.150,
                'GEL': 0.000}

# A dictionary of the salt percent in each of the Ingredients is created
saltPercent = {'CHICKEN': 0.002,
               'BEEF': 0.005,
               'MUTTON': 0.007,
               'RICE': 0.002,
               'WHEAT': 0.008,
               'GEL': 0.000}
N_data=1000
optimum_sol=np.zeros((6, N_data))
costs_values=np.zeros((6, N_data))

for k in range(N_data):
    # A dictionary of the costs of each of the Ingredients is created, it takes the reference cost and draws u.a.r
    # independently for each ingredient a cost within 50% of the reference

    costs_values[0,k]=0.013*(0.5+rd.random())
    costs_values[1,k]=0.008*(0.5+rd.random())
    costs_values[2,k]=0.010*(0.5+rd.random())
    costs_values[3,k]=0.002*(0.5+rd.random())
    costs_values[4,k]=0.005*(0.5+rd.random())
    costs_values[5,k]=0.001*(0.5+rd.random())

    costs = {'CHICKEN': costs_values[0,k],
             'BEEF':costs_values[1,k] ,
             'MUTTON': costs_values[2,k],
             'RICE':costs_values[3,k] ,
             'WHEAT': costs_values[4,k],
             'GEL': costs_values[5,k]}

    # Create the 'prob' variable to contain the problem data
    prob = LpProblem("The Whiskas Problem", LpMinimize)

    # A dictionary called 'ingredient_vars' is created to contain the referenced Variables
    ingredient_vars = LpVariable.dicts("Ingr",Ingredients,0)

    # The objective function is added to 'prob' first
    prob += lpSum([costs[i]*ingredient_vars[i] for i in Ingredients]), "Total Cost of Ingredients per can"

    # The five constraints are added to 'prob'
    prob += lpSum([ingredient_vars[i] for i in Ingredients]) == 100, "PercentagesSum"
    prob += lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0, "ProteinRequirement"
    prob += lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0, "FatRequirement"
    prob += lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0, "FibreRequirement"
    prob += lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4, "SaltRequirement"

    # The problem data is written to an .lp file
    prob.writeLP("WhiskasModel2.lp")

    # The problem is solved using PuLP's choice of Solver
    prob.solve()

    # The status of the solution is printed to the screen
    #print("Status:", LpStatus[prob.status])

    # Each of the variables is printed with it's resolved optimum value
    l=0
    for v in prob.variables():
        optimum_sol[l,k]=v.varValue
        l=l+1


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
pp = PdfPages('custom.pdf')
print("Setup Complete")

# Create a figure of size 8x6 inches, 80 dots per inch
plt.figure(figsize=(8, 6), dpi=80)
# Create a new subplot from a grid of 1x1
plt.subplot(1, 1, 1)
X = np.linspace(-np.pi, np.pi, 256)
C, S = np.cos(X), np.sin(X)
plt.plot(X, C, color="blue", linewidth=2.5, linestyle="-",label="sine")
plt.plot(X, S, color="green", linewidth=2.5, linestyle="-",label="cosine")
plt.legend(loc='upper left')
# Set x limits
plt.xlim(X.min() * 1.1, X.max() * 1.1)
# Set x ticks
plt.xticks([-np.pi, -np.pi/2, np.pi/2, np.pi],
[r'$-\pi$', r'$-\pi/2$',  r'$+\pi/2$', r'$+\pi$'])
# Set y limits
plt.ylim(C.min() * 1.1, C.max() * 1.1)
# Set y ticks
plt.yticks([-1, +1],
[r'$-1$', r'$+1$'])
# move spine
# gca stands for 'get current axis'
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))

#Annotate
t = 2 * np.pi / 3
plt.plot([t, t], [0, np.cos(t)], color='blue', linewidth=2.5, linestyle="--")
plt.scatter([t, ], [np.cos(t), ], 50, color='blue')
plt.annotate(r'$cos(\frac{2\pi}{3} )=-\frac{1} {2} $',
xy=(t, np.cos(t)), xycoords='data',
xytext=(-90, -50), textcoords='offset points', fontsize=16,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
plt.plot([t, t],[0, np.sin(t)], color='red', linewidth=2.5, linestyle="--")
plt.scatter([t, ],[np.sin(t), ], 50, color='red')
plt.annotate(r'$sin(\frac{2\pi}{3} )=\frac{\sqrt{3} }{2} $',
xy=(t, np.sin(t)), xycoords='data',
xytext=(+10, +30), textcoords='offset points', fontsize=16,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))

#resize ticks
for label in ax.get_xticklabels() + ax.get_yticklabels():
    label.set_fontsize(16)
    label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65))

# Save figure using 72 dots per inch
plt.savefig("custom.png", dpi=72)
# Show result on screen
plt.show()
pp.savefig()
pp.close()


################################################################################
# Peach - Computational Intelligence for Python
# Jose Alexandre Nalon
#
# This file: tutorial/fuzzy-c-means.py
# Basic example of Fuzzy C-Means
################################################################################

# We import numpy for arrays and peach for the library. Actually, peach also
# imports the numpy module, but we want numpy in a separate namespace:
from numpy import *
import peach as p

# We create the example list (the training set) and the corresponding membership
# values for each example. There are 15 two-dimensional examples, and 15 pairs
# of membership values. This means that each example will be classified in two
# classes, with the corresponding membership values. This means that the vector
# [ 0, 0 ] will be classified with membership values 0.7 in the first class and
# membership values 0.3 in the second class and so on. Notice that, with this
# values, we expect the two centers, after the clustering, to be located at
# [ 1., 1. ] and [ 6., 6. ].

x = array( [
    [ 0., 0. ], [ 0., 1. ], [ 0., 2. ], [ 1., 0. ], [ 1., 1. ], [ 1., 2. ],
    [ 2., 0. ], [ 2., 1. ], [ 2., 2. ], [ 5., 5. ], [ 5., 6. ], [ 5., 7. ],
    [ 6., 5. ], [ 6., 6. ], [ 6., 7. ], [ 7., 5. ], [ 7., 6. ], [ 7., 7. ] ] )

mu = array( [
    [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ],
    [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.3, 0.7 ],
    [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ],
    [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ] ] )

# Notice that the starting values for the memberships could be randomly choosen,
# at least for simple cases like this. You could try the lines below to
# initialize the membership array:
#
# from numpy.random import random
# mu = random((18, 1))
# mu = hstack((mu, 1.-mu))

# This parameter measures the smoothness of convergence
m = 2.0

# We create the algorithm. We must pass, in this order, the example set, the
# corresponding membership values, and the parameter `m`. This parameter is
# optional, though, and if not given, will default to 2.
fcm = p.FuzzyCMeans(x, mu, m)

# The __call__ interface runs the algorithm till completion. It returns the
# center of the classification. If we want to check the membership values for
# the vectors, the .mu instance variable can be checked. Notice that we pass the
# parameter emax = 0 to the algorithm. This is the maximum error accepted. In
# general, fuzzy c-means will converge very fastly and with little error. A
# imax parameter -- the maximum number of iterations, can also be given. If it
# isn't given, 20 iterations will be assumed.
print "After 20 iterations, the algorithm converged to the centers:"
print fcm(emax=0)
print
print "The membership values for the examples are given below:"
print fcm.mu
print


"""This file contains code used in "Think Stats",
by Allen B. Downey, available from greenteapress.com

Copyright 2010 Allen B. Downey
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
"""

from __future__ import print_function, division

import numpy as np

import nsfg
import first

import thinkstats2
import thinkplot

def PercentileRank(scores, your_score):
    """Computes the percentile rank relative to a sample of scores."""
    count = 0
    for score in scores:
        if score <= your_score:
            count += 1

    percentile_rank = 100.0 * count / len(scores)
    return percentile_rank

scores = [55, 66, 77, 88, 99]
your_score = 88

print('score, percentile rank')
for score in scores:
    print(score, PercentileRank(scores, score))
print()

def Percentile(scores, percentile_rank):
    """Computes the value that corresponds to a given percentile rank. """
    scores.sort()
    for score in scores:
        if PercentileRank(scores, score) >= percentile_rank:
            return score

def Percentile2(scores, percentile_rank):
    """Computes the value that corresponds to a given percentile rank.

    Slightly more efficient.
    """
    scores.sort()
    index = percentile_rank * (len(scores)-1) // 100
    return scores[index]

print('prank, score, score')
for percentile_rank in [0, 20, 25, 40, 50, 60, 75, 80, 100]:
    print(percentile_rank,
          Percentile(scores, percentile_rank),
          Percentile2(scores, percentile_rank))

def EvalCdf(sample, x):
    """Computes CDF(x) in a sample.

    sample: sequence
    x: value

    returns: cumulative probability
    """
    count = 0.0
    for value in sample:
        if value <= x:
            count += 1.0

    prob = count / len(sample)
    return prob

sample = [1, 2, 2, 3, 5]

print('x', 'CDF(x)')
for x in range(0, 7):
    print(x, EvalCdf(sample, x))

def PositionToPercentile(position, field_size):
    """Converts from position in the field to percentile.

    position: int
    field_size: int
    """
    beat = field_size - position + 1
    percentile = 100.0 * beat / field_size
    return percentile

def PercentileToPosition(percentile, field_size):
    """Converts from percentile to hypothetical position in the field.

    percentile: 0-100
    field_size: int
    """
    beat = percentile * field_size / 100.0
    position = field_size - beat + 1
    return position

# my time 42:44
print('Percentile rank in field', PositionToPercentile(97, 1633))
print('Percentile rank in age group', PositionToPercentile(26, 256))

percentile = PositionToPercentile(26, 256)
print('Equivalent position in M50-59', PercentileToPosition(percentile, 171))
# 17th place = 46:05
print('Equivalent position in F20-29', PercentileToPosition(percentile, 448))
# 48:28

def MakeExample():
    """Makes a simple example CDF."""
    t = [2, 1, 3, 2, 5]
    cdf = thinkstats2.Cdf(t)
    thinkplot.Clf()
    thinkplot.Cdf(cdf)
    thinkplot.Save(root='cumulative_example_cdf',
                   xlabel='x',
                   ylabel='CDF',
                   axis=[0, 6, 0, 1],
                   legend=False)

def MakeFigures(live, firsts, others):
    """Creates several figures for the book.

    live: DataFrame
    firsts: DataFrame
    others: DataFrame
    """

    first_wgt = firsts.totalwgt_lb
    first_wgt_dropna = first_wgt.dropna()
    print('Firsts', len(first_wgt), len(first_wgt_dropna))
    #assert len(first_wgt_dropna) == 4381

    other_wgt = others.totalwgt_lb
    other_wgt_dropna = other_wgt.dropna()
    print('Others', len(other_wgt), len(other_wgt_dropna))
    #assert len(other_wgt_dropna) == 4706

    first_pmf = thinkstats2.Pmf(first_wgt_dropna, label='first')
    other_pmf = thinkstats2.Pmf(other_wgt_dropna, label='other')

    width = 0.4 / 16

    # plot PMFs of birth weights for first babies and others
    thinkplot.PrePlot(2)
    thinkplot.Hist(first_pmf, align='right', width=width)
    thinkplot.Hist(other_pmf, align='left', width=width)
    thinkplot.Save(root='cumulative_birthwgt_pmf',
                   title='Birth weight',
                   xlabel='weight (pounds)',
                   ylabel='PMF')

    # plot CDFs of birth weights for first babies and others
    first_cdf = thinkstats2.Cdf(firsts.totalwgt_lb, label='first')
    other_cdf = thinkstats2.Cdf(others.totalwgt_lb, label='other')

    thinkplot.PrePlot(2)
    thinkplot.Cdfs([first_cdf, other_cdf])
    thinkplot.Save(root='cumulative_birthwgt_cdf',
                   title='Birth weight',
                   xlabel='weight (pounds)',
                   ylabel='CDF',
                   axis=[0, 12.5, 0, 1]
                   )

def MakeCdf(live):
    """Plot the CDF of pregnancy lengths for live births.

    live: DataFrame for live births
    """
    cdf = thinkstats2.Cdf(live.prglngth, label='prglngth')
    thinkplot.Cdf(cdf)
    thinkplot.Save('cumulative_prglngth_cdf',
                   title='Pregnancy length',
                   xlabel='weeks',
                   ylabel='CDF')

def RandomFigure(live):
    weights = live.totalwgt_lb
    cdf = thinkstats2.Cdf(weights, label='totalwgt_lb')

    sample = np.random.choice(weights, 100, replace=True)
    ranks = [cdf.PercentileRank(x) for x in sample]

    rank_cdf = thinkstats2.Cdf(ranks, label='percentile ranks')
    thinkplot.Cdf(rank_cdf)
    thinkplot.Save(root='cumulative_random',
                   xlabel='percentile rank',
                   ylabel='CDF')

def TestSample(live):
    """Plots the distribution of weights against a random sample.

    live: DataFrame for live births
    """
    weights = live.totalwgt_lb
    cdf = thinkstats2.Cdf(weights, label='totalwgt_lb')

    sample = cdf.Sample(1000)
    sample_cdf = thinkstats2.Cdf(sample, label='sample')

    thinkplot.PrePlot(2)
    thinkplot.Cdfs([cdf, sample_cdf])
    thinkplot.Save(root='cumulative_sample',
                   xlabel='weight (pounds)',
                   ylabel='CDF')

def main(name, data_dir=''):
    thinkstats2.RandomSeed(17)

    MakeExample()
    live, firsts, others = first.MakeFrames()
    RandomFigure(live)
    TestSample(live)
    MakeCdf(live)
    MakeFigures(live, firsts, others)

if __name__ == '__main__':
    import sys
    main(*sys.argv)

DSC 530 - Data Exploration and Analysis/ThinkStats2/code/cumulative.py


import numpy as np

def median(input_list: list) -> float:
    """
    中位数
    href: https://baike.baidu.com/item/%E4%B8%AD%E4%BD%8D%E5%80%BC/9501969?fr=aladdin
    input_list: list[int] 数值型数据列表
    returns: float: 中位值
    """
    if not input_list:
        raise ValueError("你输入的是个空列表")

    input_list.sort()
    num = len(input_list)
    if num % 2 == 1:
        return input_list[num // 2]
    else:
        return sum(input_list[num // 2 - 1: num // 2 + 1]) / 2

def median2(input_list: list) -> float:
    return np.median(input_list)

if __name__ == "__main__":
    # print(mean([]))
    print(median([60]))
    print(median([60, 10, 20, 30, 40, 50, 70, 80, 90]))
    print(median2([60, 10, 20, 30, 40, 50, 70, 80, 90]))


#!/usr/bin/env python
# coding: utf-8

# In[ ]:

###########################
### The puZZle in a Mug ###
###########################

# importing the libraries
import scipy.stats as stats

# Adding the data
y = [1.90642, 2.22488, 2.10288, 1.69742, 1.52229, 3.15435, 2.61826, 1.98492, 1.42738, 1.99568]

# Calculating the Shapiro-Wilk test
shapiro_stat, shapiro_p_valor = stats.shapiro(y)

language = "portugues"
language = "english"

# Conclusion
if language == "english":
    print("The calculated value for the Shapiro-Wilk test is = " + str(shapiro_stat))
    print("The calculated p-value of the Shapiro-Wilk test is = " + str(shapiro_p_valor))
    # Conclusion
    if shapiro_p_valor >= 0.05:
        print("At 95% confidence level, we have NO evidence to reject the hypothesis of data normality, according to the Shapiro-Wilk test")
    else:
        print("At 95% confidence level, we have evidence to reject the hypothesis of data normality, according to the Shapiro-Wilk test")

elif language == "portugues":
    print("O valor calculado do teste de Shapiro-Wilk eh de = " + str(shapiro_stat))
    print("O p-valor calculado para o teste de Shapiro-Wilk eh de = " + str(shapiro_p_valor))
    # Conclusion
    if shapiro_p_valor >= 0.05:
        print("Com 95% de confianca, não temos evidências para rejeitar a hipótese de normalidade dos dados, segundo o teste de Shapiro-Wilk")
    else:
        print("Com 95% de confianca, temos evidências para rejeitar a hipótese de normalidade dos dados, segundo o teste de Shapiro-Wilk")
else:
    print("Unsupported language")


# Lab 08, Question 03
# AB Satyaprakash, 180123062

# imports
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from math import pow, pi, exp

# functions
# define these as per requirement!

def P(x):
    return -1

def Q(x):
    return 0

def R(x):
    return 1

def A(i):
    return 1-h*P(x[i])/2

def B(i):
    return -2+(pow(h, 2)*Q(x[i]))

def C(i):
    return 1+h*P(x[i])/2

def y(x):
    return 2*exp(x) - x-1

def L(n):
    LArray = np.zeros((n-1, n-1))
    for i in range(n-1):
        if i == 0:
            LArray[0][0], LArray[0][1] = B(1), C(1)
        elif i == n-2:
            LArray[n-2][n-3], LArray[n-2][n-2] = A(n-1), B(n-1)
        else:
            LArray[i][i-1], LArray[i][i], LArray[i][i +
                                                    1] = A(i+1), B(i+1), C(i+1)
    return LArray

def F(n):
    FArray = []
    for i in range(n-1):
        if i == 0:
            FArray.append((pow(h, 2)*R(x[1]))-(alpha*(1-h*P(x[1])/2)))
        elif i == n-2:
            FArray.append((pow(h, 2)*R(x[n-1]))-(beta*(1+h*P(x[n-1])/2)))
        else:
            FArray.append(pow(h, 2)*R(x[i+1]))
    return FArray

# program body
# BVP : y" = y+1. Thus P = -1, Q = 0, R = 1

a, b = 0, 1
alpha, beta = 1, 2*(exp(1)-1)
sol, err = [], []
x = []
h = 1/3
x = np.arange(a, b+h/2, h)
n = len(x)-1
LArray, FArray = L(n), F(n)
UArray = np.linalg.inv(LArray).dot(FArray)
Y = [alpha]
for u in UArray:
    Y.append(u)
Y.append(beta)
Act = []
for xi in x:
    Act.append(y(xi))

df = pd.DataFrame()
df['Nodal points'] = pd.Series(['0', '1/3', '2/3', '1'])
df['Actual'] = pd.Series(Act)
df['Approx'] = pd.Series(Y)
df['Error'] = pd.Series(np.array(Act)-np.array(Y))
print()
print(df)
print()

Semester 6/MA 322 (Scientific Computing Theory)/Labs/Lab 8/Code/q3.py


# note: modules are imported in each function to reduce overhead when using these utilities. This way, only the modules
# that are needed for the functions you use will be imported, rather than the modules needed for all utilities.
import numpy as np
import os

def reduced_chi_sq(model, data, errors, num_parameters):
    """ Does a reduced chi squared calculation

    .. math::
        \\chi^2 = \\sum_{k=1}^{n} \\left( \\frac{\\text{model}_k - \\text{data}_k}{\\text{error}_k} \\right) ^2

        \\chi^2_{\\text{red}} = \\frac{\\chi^2}{n - n_param - 1}

    where :math:`n` is the number of data points.

    :param model: list of values that describe a possible fit to the data
    :param data: list of values that are the data do be fitted
    :param errors: list of errors on the data
    :param num_parameters: Number of tunable pararamters in the model. This is
                           required for the reduced chi-squared calculation.
    :return: value for the reduced chi squared value of the fit of the model to the data
    """
    if not len(model) == len(data) == len(errors):
        raise ValueError("The length of the model, data, and errors need to be the same.")

    # convert to numpy arrays to vectorize computation
    model = np.array(model)
    data = np.array(data)
    errors = np.array(errors)

    chi_squared_values = ((model - data)/errors) ** 2
    chi_squared = sum(chi_squared_values)

    return chi_squared/(len(data) - num_parameters - 1)

def mag_to_flux(mag, zeropoint):
    """Convert a magnitude into a flux.

    We get the conversion by starting with the definition of the magnitude scale.

    .. math::
        m = -2.5 \\log_{10}(F) + C

        2.5 \\log_{10}(F) = C - m

        F = 10^{\\frac{C-m}{2.5}}

    :param mag: magnitdue to be converted into a flux.
    :param zeropoint: zeropoint (in mags) of the magnitude system being used
    :return: flux that corresponds to the given magnitude
    """

    return 10**((zeropoint - mag)/2.5)

def flux_to_mag(flux, zeropoint, bad_data=-99):
    """Convert flux to magnitude with the given zeropoint.

    .. math::
        m = -2.5 \\log_{10} (F) + C

    :param flux: flux in whatever units. Choose your zeropoint correctly to make this work with the units flux is in.
    :param zeropoint: zeropoint of the system (in mags)
    :param bad_data: Value to give data with negative flux. Defaults to -99.
    :return: magnitude that corresponds to the given flux
    """

    if flux > 0:
        return -2.5 * np.log10(flux) + zeropoint  # This is just the definition of magnitude
    else:
        return bad_data

def mag_errors_to_percent_flux_errors(mag_error):
    """Converts a magnitude error into a percent flux error.

    .. math::
        m = -2.5 \\log_{10} (F) + C

        dm = \\frac{-2.5}{\ln(10)} \\frac{dF}{F}

        \\frac{dF}{F} = \\frac{\\ln(10)}{2.5} dm

    The minus sign just tells us that increasing flux gives decreasing magnitudes, so we can safely ignore it.

    :param mag_error: magnitude error
    :return: percentage flux error corresponding to this magnitude error.
    """

    return mag_error * (np.log(10) / 2.5)  # math.log takes natural log unless specified.

def percent_flux_errors_to_mag_errors(percent_flux_error):
    """Converts a percentage flux error into a magnitude error.

    .. math::
        m = -2.5 \\log_{10} (F) + C

        dm = \\frac{-2.5}{\ln(10)} \\frac{dF}{F}

    :param percent_flux_error: percentage flux error
    :return: magnitude error corresponding to the percentage flux error.
    """

    return (2.5 / np.log(10)) * percent_flux_error

def empty_data(datatype):
    """
    Makes an empty data of a given datatype.

    This is useful for filling tables that have missing values.

    Here is what the various datatypes return:

    Float: np.nan

    Integer: -999999999999

    String: Empty string.

    :param datatype: data type, obtained by using `.dtype` on some numpy object.
    """

    if "f" == datatype.kind:
        return np.nan
    elif "i" == datatype.kind:
        return -999999999999
    elif "S" == datatype.kind:
        return ""

def check_if_file(possible_location):
    """
    Check if a file already exists at a given location.

    :param possible_location: File to check. Can be a path to a file, too.
    :type possible_location: str
    :return: bool representing whether or not a file already exists there.
    """

    # have to do separate cases for files in current directory and those
    # elsewhere. Those with paths has os.sep in their location.
    if os.sep in possible_location:
        # get all but the last part, which is the directory
        directory = os.sep.join(possible_location.split(os.sep)[:-1])
        # then the filename is what's left
        filename = possible_location.split(os.sep)[-1]
    else:  # it's in the current directory
        directory = "."
        filename = possible_location

    # Then check if the given file exists
    if filename in os.listdir(directory):
        return True
    else:
        return False

def gaussian(x, mean, sigma, amplitude):
    """
    The Gaussian density at the given value.

    The Gaussian density is defined as

    .. math::
        f(x) = A e ^ {- \\frac{(x - \\mu)^2}{2 \\sigma^2}}

    Note that if you want a normalized Gaussian (so that the total area
    under the curve is 1), then use the `normed_gaussian()` function.

    :param x: location to get the Gaussian density at.
    :type x: float
    :param mean: Mean of the Gaussian.
    :type mean: float
    :param sigma: Standard deviation of the Gaussian. Should be positive
    :type sigma: float
    :param amplitude: Height of the highest point of the Gaussian.
    :return: Gaussain density of the given gaussian at the given x value.

    """

    return amplitude * np.e ** (-1 * ((x - mean) / (np.sqrt(2) * sigma))**2)

def normed_gaussian(x, mean, sigma):
    """
    The Gaussian density at the given value.

    The Gaussian density is defined as

    .. math::
        f(x) = \\frac{1}{\\sigma \\sqrt{2 \\pi}} e ^ {- \\frac{(x - \\mu)^2}{2 \\sigma^2}}

    Noe that if you want a Gaussian that is not normalized (ie where you
    can set the amplitude), then use the `gaussian()` function.

    :param x: location to get the Gaussian density at.
    :type x: float
    :param mean: Mean of the Gaussian.
    :type mean: float
    :param sigma: Standard deviation of the Gaussian. Should be positive
    :type sigma: float
    :return: Gaussain density of the given gaussian at the given x value.

    """

    # first see if amplitude is defined, and if not, normalize it.
    amplitude = 1.0 / (sigma * np.sqrt(2 * np.pi))
    return amplitude * np.e ** (-1 * ((x - mean) / (np.sqrt(2) * sigma))**2)

def flux_conv(flux_zeropont, counts_zeropoint):
    """
    Calculate the conversion factor from counts to flux.

    This is done by using the zeropoints of the magnitudes in flux and counts.
    The magnitude should be the same no matter how we calculate it, which is
    how we can derive this:

    .. math::
        \\text{mag}_\\text{flux} = \\text{mag}_\\text{counts}

        -2.5 \\log_{10}(F) + Z_F = -2.5 \\log_{10}(C) + Z_C

        \\text{where $F = $ Flux, $C = $ counts, and $Z_F$ and $Z_C$ represent the respective zeropoints when using flux or counts}

        -2.5(\\log_{10}(F) - \\log_{10}(C)) = Z_C - Z_F

        \\log_{10} \\left( \\frac{F}{C} \\right) = \\frac{Z_F - Z_C}{2.5}

        \\frac{F}{C} = 10^{\\frac{Z_F - Z_C}{2.5}}

        F = C * 10^{\\frac{Z_F - Z_C}{2.5}}

        F = C * \\text{Flux Conversion}

        \\text{where Flux Conversion $= 10^{\\frac{Z_F - Z_C}{2.5}}$}

    This flux conversion is what is calculated here.

    NOTE: Make sure the magnitudes really are the same (ie AB/Vega).

    :param flux_zeropoint: zero point for calculating magnitudes when using
                           flux, such that mag = -2.5 log(flux) +
                           `flux_zeropoint`. The actual value will depend
                           on what flux units you want. If you want
                           microJanskys, use 23.9, for example.
    :type flux_zeropoint: float
    :param counts_zeropoint: zero point for calculating magnitues when using
                             counts, such that mag = -2.5 log(counts) +
                             `counts_zeropoint`. This is often found in the
                             header of the image in question.
    :type counts_zeropoing: float
    :returns: float containing the conversion from counts to flux, such that
              flux = counts * flux_conv. See above for derivation.

    """

    return 10 ** ((flux_zeropont - counts_zeropoint) / 2.5)

# Store the vega to AB conversions. They must all be of the form
# m_AB = m_Vega + conversion
vega_to_ab_conversions = {"w1": 2.683,
                          "w2": 3.319,
                          "w3": 5.242,
                          "w4": 6.604,
                          "ch1": 2.787,
                          "ch2": 3.260,
                          "sloan_u": 0.904,
                          "sloan_g": -0.098,
                          "sloan_r": 0.146,
                          "sloan_i": 0.357,
                          "sloan_z": 0.521}

def vega_to_ab(vega_mag, band):
    """
    Converts a Vega mag to AB.

    This basically contains a bunch of AB-Vega conversions, so I don't have
    to look them up every time I want to do anything.

    :param vega_mag: magnitude in the Vega system to be converted to AB.
    :type vega_mag: float
    :param band: Band the magnitude is in. Can be one of the following: "w1",
                 "w2", "w3", "w4".
    :type band: str
    :returns: float containing the ab magnitude
    """
    # use the dictionary I defined above.
    try:
        return vega_mag + vega_to_ab_conversions[band]
    except KeyError:
        print("{} has not yet been implemented. Find the conversion "
              "and add it, please. ".format(band))

def ab_to_vega(ab_mag, band):
    """
    Converts a AB mag to Vega.

    This basically contains a bunch of AB-Vega conversions, so I don't have
    to look them up every time I want to do anything.

    :param ab_mag: magnitude in the AB system to be converted to Vega.
    :type ab_mag: float
    :param band: Band the magnitude is in. Can be one of the following: "w1",
                 "w2", "w3", "w4".
    :type band: str
    :returns: float containing the Vega magnitude
    """

    # use the dictionary I defined above.
    try:
        return ab_mag - vega_to_ab_conversions[band]
    except KeyError:
        print("{} has not yet been implemented. Find the conversion "
              "and add it, please. ".format(band))

# Note: these all need to be tested.


# Comparisons, Masks, and Boolean Logic:

# Masking is useful when you want to extract, modify, count, or just
# manipulate values in an array based on some arbitrary criteria.

import numpy as np
import pandas as pd

# %matplotlib inline
import matplotlib.pyplot as plt
import seaborn
seaborn.set()

# Count rainy days for Seattle in 2014.
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values
inches = rainfall / 254.0   # 1/10mm to inches conversion
inches.shape

plt.hist(inches, 40)
# The histogram plot shows the general shape of data, masking reveals more.

# Masking with Comparison Operator uFuncs:
# <, >, <=, >=, !=, ==

# The returned result is always an array with boolean data type, where
# True == 1, False == 0.

x = np.array([1, 2, 3, 4, 5])
x < 3       # equivalent to np.less(x, 3)
x > 3       # np.greater(x, 3)
x <= 3      # np.less_equal(x, 3)
x >= 3      # np.greater_equal(x, 3)
x != 3      # np.notequal(x, 3)
x == 3      # np.equal(x, 3)

# As before, these work with multi-dimensional arrays.
rng = np.random.RandomState(0)
x = rng.randint(10, size=(3, 4))
x < 6

# Both of these calls count total True entries on x < 6.
np.count_nonzero(x < 6)
np.sum(x < 6)

# using sum, we can break up along axes as well
np.sum(x < 6, axis=1)

np.any(x > 8)   # any vals > 8, returns bool (True)
np.all(x < 0)   # any vals < 0, returns bool (False)
np.all(x < 10)
np.all(x == 6)
# can also break these up along axes:
np.all(x < 8, axis=1)

# Masking with Bitwise Operator uFuncs:
# &, |, ^, ~

# &     np.bitwise_and
# |     np.bitwise_or
# ^     np.bitwise_xor
# ~     np.bitwise_not

# Recall bitwise operator precedence - parens required.
np.sum((inches > .5) & (inches < 1))
np.sum(inches == 0)
np.sum(inches != 0)
np.sum(inches > 0.5)
np.sum((inches > 0) & (inches < .2))

# Boolean Arrays as Masks:

# In prior section, used aggregates computed directly on Boolean arrays.
# Can also use Boolean arrays as masks in order to select subsets of data

# "Get x such that x < 5."
x[x < 5]

# Make rainy the mask of all rainy days.
# Then make summer the mask of all summer days (June 21st is 172nd day).
rainy = (inches > 0)
days = np.arange(365)
summer = (days > 172) & (days < 262)

np.median(inches[rainy])
np.median(inches[summer])
np.max(inches[summer])
np.median(inches[rainy & ~summer])

# Aside: using keywords "and/or" vs Operators "&/|"

# The keywords 'and/or' operate on the entire object
# The symbols  ' & / |' operate on the bits of the inner int/char/etc.

# using "and" or "or" asks Python to treat the object act a single Bool entity

# NOTE: when dealing specifically with numpy arrays,
# ALWAYS use bitwise operators.


from abc import ABC, abstractmethod
import numpy as np

class Activation(ABC):
    'This is an abstract class for activation functions'
    def __init__(self, name):
        self.name = name

    @abstractmethod
    def apply(self, Z):
        return

    @abstractmethod
    def derivative(self, Z, **kwargs):
        return

class Identity(Activation):
    'Applies identity'
    def __init__(self, name='Identity'):
        super().__init__(name)

    def apply(self, Z):
        return Z

    def derivative(self, Z, **kwargs):
        return np.full(Z.shape, 1)

class Sigmoid(Activation):
    'Applies sigmoid'
    def __init__(self, name='Identity'):
        super().__init__(name)

    def apply(self, Z):
        return 1 / (1 + np.exp(-Z))

    def derivative(self, Z, A):
        return A * (1 - A)

class Softmax(Activation):
    'Applies softmax'
    def __init__(self, name='Softmax'):
        super().__init__(name)

    def apply(self, Z):
        ''' Normalizes to probability '''
        # e_Z = np.exp(Z - np.max(Z))
        # return e_Z / e_Z.sum(axis=0)
        assert len(Z.shape) == 2
        s = np.max(Z, axis=0) #.reshape(1, -1)
        e_Z = np.exp(Z - s)
        div = np.sum(e_Z, axis=0) #.reshape(1, -1)
        return e_Z / div

    def derivative(self, Z, **kwargs):
        '''WARNING: The behavior of this function is abnormal due to the math of softmax

        Z ---softmax---> A ---CategoricalCrossEntropy---> Loss

        Turns out that dZ = Yhat - Y, therefore (Yhat - Y) is passed in backpropagation

        Input:
            dA = Yhat - Y
        '''

        return 1

class Relu(Activation):
    'Applies Rectified Linear Unit'
    def __init__(self, name='Relu'):
        super().__init__(name)

    def apply(self, Z):
        return Z.clip(min=0)

    def derivative(self, Z, **kwargs):
        return np.greater(Z, 0).astype(int)

class LeakyRelu(Activation):
    'Applies Leaky Relu'
    def __init__(self, name='Leaky Relu', leak=0.01):
        super().__init__(name)
        self.leak = 0.01

    def apply(self, Z):
        return np.where(Z > 0, Z, Z * self.leak)

    def derivative(self, Z, **kwargs):
        return np.where(Z > 0, 1, self.leak)

def test_softmax():
    soft = Softmax()

    x1 = np.array([[1, 2, 3, 6]]).T

    print(x1)

    print(soft.apply(x1))

if __name__ == '__main__':
    # Testing
    test_softmax()


import numpy as np

def softmax(predictions):
    '''
    Computes probabilities from scores

    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output

    Returns:
      probs, np array of the same shape as predictions -
        probability for every class, 0..1
    '''
    # Implement softmax
    # Your final implementation shouldn't have any loops

    if predictions.ndim == 1:
      predictions -= np.max(predictions)
      predictions_exp = np.exp(predictions)
      probs = predictions_exp/np.sum(predictions_exp)
      return probs

    batch_max = np.max(predictions, axis=1)[:, np.newaxis]
    predictions -= batch_max
    predictions_exp = np.exp(predictions)
    batch_sum = np.sum(predictions_exp, axis=1)
    probs = predictions_exp/batch_sum[:, np.newaxis]

    return probs

def cross_entropy_loss(probs, target_index):
    '''
    Computes cross-entropy loss

    Arguments:
      probs, np array, shape is either (N) or (batch_size, N) -
        probabilities for every class
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss: single value
    '''
    # Implement cross-entropy
    # Your final implementation shouldn't have any loops
    if probs.ndim == 1:
      loss = - np.log(probs[target_index])
      return loss

    batch_index = np.arange(probs.shape[0])
    batch_size = batch_index.shape[0]
    loss = - np.sum(np.log(probs[batch_index, target_index]))/batch_size

    return loss

def l2_regularization(W, reg_strength):
    '''
    Computes L2 regularization loss on weights and its gradient

    Arguments:
      W, np array - weights
      reg_strength - float value

    Returns:
      loss, single value - l2 regularization loss
      gradient, np.array same shape as W - gradient of weight by l2 loss
    '''
    # Copy from previous assignment
    grad = reg_strength*2*W
    loss = reg_strength*np.sum(W**2)

    return loss, grad

def softmax_with_cross_entropy(predictions, target_index):
    '''
    Computes softmax and cross-entropy loss for model predictions,
    including the gradient

    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss, single value - cross-entropy loss
      dprediction, np array same shape as predictions - gradient of predictions by loss value
    '''
    # Copy from the previous assignment
    probs = softmax(predictions.copy())
    loss = cross_entropy_loss(probs, target_index)
    dprediction = probs.copy()

    if predictions.ndim == 1:
      dprediction[target_index] -= 1

      return loss, dprediction

    batch_size = target_index.shape[0]
    batch_index = np.arange(batch_size)
    dprediction[batch_index, target_index] -= 1
    dprediction /= batch_size

    return loss, dprediction

class Param:
    '''
    Trainable parameter of the model
    Captures both parameter value and the gradient
    '''
    def __init__(self, value):
        self.value = value
        self.grad = np.zeros_like(value)

class ReLULayer:
    def __init__(self):
        pass

    def forward(self, X):
        # Copy from the previous assignment
        result = np.maximum(X, 0)
        self.X = X

        return result

    def backward(self, d_out):
        # Copy from the previous assignment
        d_result = d_out
        d_result[self.X < 0] = 0

        return d_result

    def params(self):
        return {}

class FullyConnectedLayer:
    def __init__(self, n_input, n_output):
        self.W = Param(0.001 * np.random.randn(n_input, n_output))
        self.B = Param(0.001 * np.random.randn(1, n_output))
        self.X = None

    def forward(self, X):
        # Copy from the previous assignment
        self.X = X
        result = X @ self.W.value + self.B.value

        return result

    def backward(self, d_out):
        # Copy from the previous assignment

        self.W.grad += self.X.T @ d_out
        self.B.grad += np.mean(d_out, axis=0) * d_out.shape[0]

        d_input = d_out @ self.W.value.T

        return d_input

    def params(self):
        return { 'W': self.W, 'B': self.B }

class ConvolutionalLayer:
    def __init__(self, in_channels, out_channels,
                 filter_size, padding):
        '''
        Initializes the layer

        Arguments:
        in_channels, int - number of input channels
        out_channels, int - number of output channels
        filter_size, int - size of the conv filter
        padding, int - number of 'pixels' to pad on each side
        '''

        self.filter_size = filter_size
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.W = Param(
            np.random.randn(filter_size, filter_size,
                            in_channels, out_channels)
        )

        self.B = Param(np.zeros(out_channels))

        self.padding = padding
        self.X = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        out_height = height - self.filter_size + 2*self.padding + 1
        out_width = width - self.filter_size + 2*self.padding + 1
        result = np.empty((batch_size, out_height, out_width, self.out_channels))
        self.X = X

        X_padded = np.zeros((batch_size, height+2*self.padding, width+2*self.padding, channels))
        X_padded[:, self.padding:self.padding + height, self.padding:self.padding + width, :] = X
        self.X_padded = X_padded

        # Implement forward pass
        # Hint: setup variables that hold the result
        # and one x/y location at a time in the loop below

        # It's ok to use loops for going over width and height
        # but try to avoid having any other loops
        for y in range(out_height):
            for x in range(out_width):
                # Implement forward pass for specific location
                receptive_field = X_padded[:, y:y+self.filter_size, x:x+self.filter_size]
                receptive_field_col = receptive_field.reshape(batch_size, -1)
                W = self.W.value.reshape(-1, self.out_channels)
                result[:, y, x, :] = receptive_field_col @ W + self.B.value

        return result

    def backward(self, d_out):
        # Hint: Forward pass was reduced to matrix multiply
        # You already know how to backprop through that
        # when you implemented FullyConnectedLayer
        # Just do it the same number of times and accumulate gradients

        batch_size, height, width, channels = self.X.shape
        _, out_height, out_width, out_channels = d_out.shape

        d_input =  np.zeros_like(self.X_padded)

        # Implement backward pass
        # Same as forward, setup variables of the right shape that
        # aggregate input gradient and fill them for every location
        # of the output

        # Try to avoid having any other loops here too
        for y in range(out_height):
            for x in range(out_width):
                # Implement backward pass for specific location
                # Aggregate gradients for both the input and
                # the parameters (W and B)

                X_expanded = self.X_padded[:, y:y+self.filter_size, x:x+self.filter_size, :, np.newaxis]
                d_out_expanded = d_out[:, y, x, np.newaxis, np.newaxis, np.newaxis, :]
                self.W.grad += np.sum(d_out_expanded * X_expanded, axis=0)

                self.B.grad += np.sum(d_out[:, y, x], axis=0).reshape(-1)

                W = self.W.value.T.reshape(out_channels, -1)
                d_field_column = d_out[:, y, x] @ W
                d_field_inverse = d_field_column.reshape(batch_size, channels,
                                                         self.filter_size, self.filter_size)
                d_field = np.moveaxis(d_field_inverse.T, 3, 0)
                d_input[:, y:y+self.filter_size, x:x+self.filter_size] += d_field

        return d_input[:, self.padding:self.padding+height, self.padding:self.padding+width, :]

    def params(self):
        return { 'W': self.W, 'B': self.B }

class MaxPoolingLayer:
    def __init__(self, pool_size, stride):
        '''
        Initializes the max pool

        Arguments:
        pool_size, int - area to pool
        stride, int - step size between pooling windows
        '''
        self.pool_size = pool_size
        self.stride = stride
        self.X = None
        self.M = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        # Implement maxpool forward pass
        # Hint: Similarly to Conv layer, loop on
        # output x/y dimension

        X = np.moveaxis(X, 3, 1)
        self.M = np.zeros_like(X)
        out_height = int((height - self.pool_size)/self.stride + 1)
        out_width = int((width - self.pool_size)/self.stride + 1)
        result = np.empty((batch_size, out_height, out_width, channels))

        for y in range(out_height):
            for x in range(out_width):
                region = X[:,
                           :,
                           y*self.stride:y*self.stride+self.pool_size,
                           x*self.stride:x*self.stride+self.pool_size]
                region_col = region.reshape(batch_size, channels, -1)
                max_items = region_col.max(axis=2)

                result[:, y, x, :] = max_items

                maximum_region = np.zeros_like(region)
                indices = (region_col == max_items[:,:, np.newaxis]).reshape(region.shape)
                maximum_region[indices] = 1.0
                self.M[:, :, y*self.stride:y*self.stride+self.pool_size, x*self.stride:x*self.stride+self.pool_size] = maximum_region

        self.M = np.moveaxis(self.M, 1, 3)

        return result

    def backward(self, d_out):
        # Implement maxpool backward pass
        _, out_height, out_width, out_channels = d_out.shape

        for y in range(out_height):
            for x in range(out_width):
                region = self.M[:, y*self.stride:y*self.stride+self.pool_size,
                                x*self.stride:x * self.stride+self.pool_size, :]
                d_region = d_out[:, np.newaxis, np.newaxis, y, x, :]
                region *= d_region

        return self.M

    def params(self):
        return {}

class Flattener:
    def __init__(self):
        self.X_shape = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        self.X_shape = X.shape

        # Implement forward pass
        # Layer should return array with dimensions
        # [batch_size, hight*width*channels]

        return X.reshape(batch_size, height*width*channels)

    def backward(self, d_out):
        # Implement backward pass

        return d_out.reshape(self.X_shape)

    def params(self):
        # No params!
        return {}


import matplotlib.pyplot as plt
import numpy as np
import scipy.io as scio
import linearRegCostFunction as lrcf
import trainLinearReg as tlr
import learningCurve as lc
import polyFeatures as pf
import featureNormalize as fn
import plotFit as plotft
import validationCurve as vc

plt.ion()
np.set_printoptions(formatter={'float': '{: 0.6f}'.format})

# ===================== Part 1: Loading and Visualizing Data =====================
# We start the exercise by first loading and visualizing the dataset.
# The following code will load the dataset into your environment and pot
# the data.
#

# Load Training data
print('Loading and Visualizing data ...')

# Load from ex5data1:
data = scio.loadmat('ex5data1.mat')
X = data['X']
y = data['y'].flatten()
Xval = data['Xval']
yval = data['yval'].flatten()
Xtest = data['Xtest']
ytest = data['ytest'].flatten()

m = y.size

# Plot training data
plt.figure()
plt.scatter(X, y, c='r', marker="x")
plt.xlabel('Change in water level (x)')
plt.ylabel('Water folowing out of the dam (y)')

input('Program paused. Press ENTER to continue')

# ===================== Part 2: Regularized Linear Regression Cost =====================
# You should now implement the cost function for regularized linear regression
#

theta = np.ones(2)
cost, _ = lrcf.linear_reg_cost_function(theta, np.c_[np.ones(m), X], y, 1)

print('Cost at theta = [1  1]: {:0.6f}\n(this value should be about 303.993192'.format(cost))

input('Program paused. Press ENTER to continue')

# ===================== Part 3: Regularized Linear Regression Gradient =====================
# You should now implement the gradient for regularized linear regression
#

theta = np.ones(2)
cost, grad = lrcf.linear_reg_cost_function(theta, np.c_[np.ones(m), X], y, 1)

print('Gradient at theta = [1  1]: {}\n(this value should be about [-15.303016  598.250744]'.format(grad))

input('Program paused. Press ENTER to continue')

# ===================== Part 4: Train Linear Regression =====================
# Once you have implemented the cost and gradient correctly, the
# train_linear_reg function will use your cost function to train regularzized linear regression.
#
# Write Up Note : The data is non-linear, so this will not give a great fit.
#

# Train linear regression with lambda = 0
lmd = 0

theta = tlr.train_linear_reg(np.c_[np.ones(m), X], y, lmd)

# Plot fit over the data
plt.plot(X, np.dot(np.c_[np.ones(m), X], theta))

input('Program paused. Press ENTER to continue')

# ===================== Part 5: Learning Curve for Linear Regression =====================
# Next, you should implement the learning_curve function.
#
# Write up note : Since the model is underfitting the data, we expect to
#                 see a graph with "high bias" -- Figure 3 in ex5.pdf
#

lmd = 0
error_train, error_val = lc.learning_curve(np.c_[np.ones(m), X], y, np.c_[np.ones(Xval.shape[0]), Xval], yval, lmd)

plt.figure()
plt.plot(np.arange(m), error_train, np.arange(m), error_val)
plt.title('Learning Curve for Linear Regression')
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('Number of Training Examples')
plt.ylabel('Error')
plt.axis([0, 13, 0, 150])

input('Program paused. Press ENTER to continue')

# ===================== Part 6 : Feature Mapping for Polynomial Regression =====================
# One solution to this is to use polynomial regression. You should now
# complete polyFeatures to map each example into its powers
#

p = 5

# Map X onto Polynomial Features and Normalize
X_poly = pf.poly_features(X, p)
X_poly, mu, sigma = fn.feature_normalize(X_poly)
X_poly = np.c_[np.ones(m), X_poly]

# Map X_poly_test and normalize (using mu and sigma)
X_poly_test = pf.poly_features(Xtest, p)
X_poly_test -= mu
X_poly_test /= sigma
X_poly_test = np.c_[np.ones(X_poly_test.shape[0]), X_poly_test]

# Map X_poly_val and normalize (using mu and sigma)
X_poly_val = pf.poly_features(Xval, p)
X_poly_val -= mu
X_poly_val /= sigma
X_poly_val = np.c_[np.ones(X_poly_val.shape[0]), X_poly_val]

print('Normalized Training Example 1 : \n{}'.format(X_poly[0]))

input('Program paused. Press ENTER to continue')

# ===================== Part 7 : Learning Curve for Polynomial Regression =====================
# Now, you will get to experiment with polynomial regression with multiple
# values of lambda. The code below runs polynomial regression with
# lambda = 0. You should try running the code with different values of
# lambda to see how the fit and learning curve change.
#

lmd = 0
theta = tlr.train_linear_reg(X_poly, y, lmd)

# Plot trainint data and fit
plt.figure()
plt.scatter(X, y, c='r', marker="x")
plotft.plot_fit(np.min(X), np.max(X), mu, sigma, theta, p)
plt.xlabel('Change in water level (x)')
plt.ylabel('Water folowing out of the dam (y)')
plt.ylim([0, 60])
plt.title('Polynomial Regression Fit (lambda = {})'.format(lmd))

error_train, error_val = lc.learning_curve(X_poly, y, X_poly_val, yval, lmd)
plt.figure()
plt.plot(np.arange(m), error_train, np.arange(m), error_val)
plt.title('Polynomial Regression Learning Curve (lambda = {})'.format(lmd))
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('Number of Training Examples')
plt.ylabel('Error')
plt.axis([0, 13, 0, 150])

print('Polynomial Regression (lambda = {})'.format(lmd))
print('# Training Examples\tTrain Error\t\tCross Validation Error')
for i in range(m):
    print('  \t{}\t\t{}\t{}'.format(i, error_train[i], error_val[i]))

input('Program paused. Press ENTER to continue')

# ===================== Part 8 : Validation for Selecting Lambda =====================
# You will now implement validationCurve to test various values of
# lambda on a validation set. You will then use this to select the
# 'best' lambda value.

lambda_vec, error_train, error_val = vc.validation_curve(X_poly, y, X_poly_val, yval)

plt.figure()
plt.plot(lambda_vec, error_train, lambda_vec, error_val)
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('lambda')
plt.ylabel('Error')

input('ex5 Finished. Press ENTER to exit')


"""
From Similarity Preserving Graph Convolutional Networks (SimPGCN).
`Node Similarity Preserving Graph Convolutional Networks
<https://arxiv.org/abs/2011.09643>`

Original implementation: <https://github.com/ChandlerBang/SimP-GCN>
and <https://github.com/DSE-MSU/DeepRobust>
"""

import numpy as np
import scipy.sparse as sp
from sklearn.metrics.pairwise import cosine_similarity
from itertools import product

def knn_graph(x, k=20):
    X = np.zeros_like(x)
    X[x != 0] = 1
    sims = cosine_similarity(X)
    sims[np.diag_indices(len(sims))] = 0.

    for i, sim in enumerate(sims):
        indices_argsort = np.argsort(sim)
        sims[i, indices_argsort[: -k]] = 0.

    adj_knn = sp.csr_matrix(sims)
    return adj_knn

def attr_sim(x, k=5):
    X = np.zeros_like(x)
    X[x != 0] = 1.

    sims = cosine_similarity(X)
    indices_sorted = sims.argsort(1)
    selected = np.hstack((indices_sorted[:, :k],
                          indices_sorted[:, - k - 1:]))

    selected_set = set()
    for i in range(len(sims)):
        for pair in product([i], selected[i]):
            if pair[0] > pair[1]:
                pair = (pair[1], pair[0])
            if pair[0] == pair[1]:
                continue
            selected_set.add(pair)

    sampled = np.transpose(list(selected_set))
#     print('number of sampled:', len(sampled[0]))
    node_pairs = (sampled[0], sampled[1])
    return sims[node_pairs], node_pairs


import numpy as np
import math

def quantify_confusion(N, f=lambda x: x * (x - 1) * 0.5, exact=False):
    if len(N)<=0: return 0
    f = np.vectorize(f, otypes=[np.float])
    b2 = np.ones((N.shape[1], 1), dtype='float')
    b1 = np.ones((1, N.shape[0]), dtype='float')
    m1 = b1.dot(N)
    m2 = N.dot(b2)
    m = b1.dot(N).dot(b2)
    s1 = (b1.dot(f(m2)))
    s2 = (f(m1).dot(b2))
    n = f(m)
    I = (b1.dot(f(N)).dot(b2))
    if exact:
        E = (b1.dot(f(m2).dot(f(m1)) / f(m))).dot(b2)
    else:
        E = (b1.dot(f(m2.dot(m1) / m))).dot(b2)
    AG = float((s1 + s2 - 2 * I) / ((s1 + s2) - 2 * E))
    return 1 - AG

def nmi(e): return quantify_confusion(e, f=lambda x: x * math.log(x) if x > 0 else 0)
def kappa(e): return quantify_confusion(e, f=lambda x: x * x )
def ari(e): return quantify_confusion(e, f=lambda x: x * (x - 1), exact=True)
def ari2(e): return quantify_confusion(e, f=lambda x: x * (x - 1))

'''
def test():
    ct = np.array([[3, 1], [0, 3], [3, 0]])
    nct = ct*1.0/np.sum(ct)

    print nct
    print "NMI: " , nmi(ct), " == ", nmi(nct)
    print "kappa: " , kappa(ct), " == ", kappa(nct)

    # more variations, you can play with function f to get new measures
    print "other variations: ", ari(ct), ari2(ct)

if __name__ == '__main__':
    test()
    '''


# -*- coding: utf-8 -*-
"""
Created on Fri Apr 26 12:26:13 2019

@author: Marcezar
"""

import numpy as np
import numpy.linalg as npl

def A_norm(x,A):
    return np.sqrt(np.vdot(x,A@x))

def cg(A,b,x0=None,tol=1e-5,maxiter=None,M1=None,M3=None,x_exact=None):
    n = A.shape[0]
    if x0 is None:
        x = np.zeros(n)
    else:
        x = x0
    if maxiter is None:
        maxiter = 3*n
    r = b-A@x
    res_arr = np.zeros(maxiter)
    res_arr[0] = npl.norm(r)
    if x_exact is not None:
        err_arr = np.zeros(maxiter)
        err_arr[0] = A_norm(x-x_exact,A)
    i = 0

    # unpreconditioned CG-method
    if M1 is None  and M3 is None:
        p = r

        while res_arr[i] > tol*res_arr[0] and i < maxiter-1:
            i = i+1
            w       = A@p
            aph     = np.vdot(r,r)/np.vdot(p,w)
            x       = x + aph*p
            rnew    = r - aph*w
            bta     = np.vdot(rnew,rnew)/np.vdot(r,r)
            p       = rnew + bta*p
            r       = rnew
            res_arr[i] = npl.norm(r)
            if x_exact is not None:
                err_arr[i] = A_norm(x-x_exact,A)

    # CG method for using a preconditioner
    elif M1 is not None and M3 is None:
        y = p = M1@r
        while res_arr[i] > tol*res_arr[0] and i < maxiter-1:
            i = i+1
            w       = A@p
            aph     = np.vdot(y,r)/np.vdot(p,w)
            x       = x + aph*p
            rnew    = r - aph*w
            ynew    = M1@rnew
            bta     = np.vdot(ynew,rnew)/np.vdot(r,y)
            p       = ynew + bta*p
            r       = rnew
            y       = ynew
            res_arr[i] = npl.norm(r)
            if x_exact is not None:
                err_arr[i] = A_norm(x-x_exact,A)

    # CG method for using the P_DEF-Operator
    elif M1 is not None and M3 is not None:
        y = p = M1@r
        while res_arr[i] > tol*res_arr[0] and i < maxiter-1:
            i = i+1
            w       = M3@(A@p)
            aph     = np.vdot(y,r)/np.vdot(p,w)
            x       = x + aph*p
            rnew    = r - aph*w
            ynew    = M1@rnew
            bta     = np.vdot(ynew,rnew)/np.vdot(r,y)
            p       = ynew + bta*p
            r       = rnew
            y       = ynew
            res_arr[i] = npl.norm(r)
            if x_exact is not None:
                err_arr[i] = A_norm(x-x_exact,A)
        Qb = np.conj(M3.U.T)@b
        Qb = np.linalg.solve(M3.E,Qb)
        Qb = M3.U@Qb
        x = Qb + M3.H@x

    else:
        raise NotImplementedError("no 'M1' given")

    res_arr = np.delete(res_arr,range(i+1,maxiter))

    if x_exact is not None:
        err_arr = np.delete(err_arr,range(i+1,maxiter))
        return (x,res_arr,err_arr)
    else:
        return (x,res_arr)


#!python3

"""
A function to pick a random number according to the power-law distribution,
  with PDF: f(z) = minValue/z^2 for z>=zmin.

Note that it is different than numpy.random.power and scipy.random.powerlaw.

@author Erel Segal-Halevi
@since  2017-10
"""

import numpy as np

def random_powerlaw(minValue, power=2, size=1):
	"""
	Draws a random value according to the power-law distribution, with:
	* CDF: F(z) = 1-(minValue/z)^(power-1) for z>=zmin
	Uses inverse random sampling: https://en.wikipedia.org/wiki/Inverse_transform_sampling

	minValue: smallest value from which the PDF is nonzero.
	size:     number of random values to return (in a numpy array)
	power:    the power of the law.
	"""

	u = np.random.uniform(0,1,size)
	# The inverse CDF is:  F^{-1}(u) = minValue/(1-u)^(1/(k-1))
	return minValue/(1-u)**(1/(power-1))

if __name__ == "__main__":
	import matplotlib.pyplot as plt
	minValue = 0.001
	values=random_powerlaw(minValue=minValue,size=10000,power=2.5)
	plt.hist(values) # All except 20 samples are in [0,500]
	maxValue = max(values)
	xValues = np.linspace(minValue,maxValue,10000)
	plt.plot(xValues, minValue/xValues**2)  # Plot visible only by zooming to [0, 0.1]
	plt.show()


import matplotlib.pyplot as plt
import numpy as np
from functools import partial
from scipy import stats

GAMMA_LIST = np.logspace(0, .8, num=4)
BETA_PRODUCT = 1
NUM = 20_000_000
BIN_NUMBER = 200

LOG_FUDGE_FACTOR = .2

def color_num(gamma):
    return ((np.log(gamma) - np.log(min(GAMMA_LIST)))
    / (
        np.log(max(GAMMA_LIST))
      - np.log(min(GAMMA_LIST))
      + LOG_FUDGE_FACTOR)
    )

def beta(gamma):
    return np.sqrt(1 - 1 / gamma)

def boost_angle_com_to_lab(theta, gamma, beta_product):
    return np.arctan(
        np.sin(theta)
        / gamma
        / (
            np.cos(theta)
            + beta(gamma) / beta_product
        )
    ) % (np.pi)

class SineRandomVariable(stats.rv_continuous):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.a = 0.
        self.b = np.pi

    def _pdf(self, theta):
        return (np.sin(theta) / 2)

    def _ppf(self, x):
        return(np.arccos(1 - 2*x))

def angular_distribution_shift():

    angles_com = SineRandomVariable().rvs(size=NUM)

    colormap = plt.get_cmap('plasma')
    def color(gamma):
        return colormap(color_num(gamma))

    fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})

    for gamma in GAMMA_LIST:
        angles_lab = boost_angle_com_to_lab(angles_com, gamma=gamma, beta_product=BETA_PRODUCT)

        vals, bins_with_edges = np.histogram(angles_lab, bins=BIN_NUMBER)
        bins = (
            bins_with_edges[1:]+
            bins_with_edges[:-1]
            ) / 2
        vals = vals / max(vals)

        ax.plot(bins, vals, label=f'$\gamma=$ {gamma:.1f}', color=color(gamma))
        # ax.plot(2 * np.pi - bins, vals, color=color(gamma))

        ax.axvline(1 / gamma, color=color(gamma), ls=':')
        # ax.axvline(-1 / gamma, color=color(gamma), ls=':')

        # fraction_below_gamma_inv = (
        #     sum(1 for ang in angles_lab if ang < 1/ gamma)
        #     / len(angles_lab)
        # )

        # print(f'Fraction sent below $1/\\gamma$: {100*fraction_below_gamma_inv:.1f}%')

    # ax.set_theta_zero_location("N")
    ax.set_thetamin(0)
    ax.set_thetamax(180)
    # ax.set_xlabel('$\\theta$')
    # ax.set_ylabel('$p(\\theta)$')
    ax.legend()
    ax.set_yticklabels([])
    plt.tight_layout()

if __name__ == "__main__":

    from make_all_figures import plot_and_save
    plot_and_save(angular_distribution_shift)


import numpy as np

def integrateedo(x0, N, h, method):

    if method == "runge-kutta-2":
        xk = x0
        tk = f(0, x0)
        c = 0
        while c < N:
            K1 = f(tk, xk)
            K2 = f(tk + h, xk + h*K1)
            xk1 = xk + (h/2.0)*(K1+K2)
            tk = (c+1)*h
            xk = xk1
            c += 1
        return xk
    elif method == "runge-kutta-4":
        xk = x0
        tk = f(0, x0)
        c = 0
        while c < N:
            K1 = f(tk, xk)
            K2 = f(tk + h/2, xk + (h/2)*K1)
            K3 = f(tk + h/2, xk + (h/2)*K2)
            K4 = f(tk + h, xk + h*K3)
            xk1 = xk + (h/6)*(K1+2*K2+2*K3+K4)
            tk = (c+1)*h
            xk = xk1
            c += 1
        return xk
    elif method == "euler":
        xk = x0
        tk = f(0, x0)
        c = 0
        while c < N:
            K1 = f(tk, xk)
            xk1 = xk + K1*h
            tk = (c+1)*h
            xk = xk1
            c += 1
        return xk
    return 0

def f(t, x):
    return t + x

x0 = 0.0
N = 3
method = "euler"
h=0.1

result = integrateedo(x0, N, h, method)
print(result)


import numpy as np

#Questions on NumPy Linear Algebra
#Find a matrix or vector norm using NumPy
#norm Return Matrix or vector norm.
np.linalg.norm(np.arange(9) - 4)
np.linalg.norm((np.arange(9) - 4).reshape(3,3))

#Calculate the QR decomposition of a given matrix using NumPy
#qr Compute the qr factorization of a matrix.
np.linalg.qr(np.arange(10).reshape(2, 5))

#Compute the condition number of a given matrix using NumPy
#cond Compute the condition number of a matrix.
np.linalg.cond(np.arange(1,10).reshape(3, 3))

#Compute the eigenvalues and right eigenvectors of a given square array using NumPy?
#eig Compute the eigenvalues and right eigenvectors of a square array.
np.linalg.eig(np.arange(1,10).reshape(3, 3))

#Calculate the Euclidean distance using NumPy
np.linalg.norm(np.array([1, 2, 3]) - np.array([3, 2, 1]))

NumpyTutorial/Questions on NumPy Linear Algebra.py


import numpy as np
import gd

X = np.array([[2, 0], [0, 1], [0, 0]])
y = np.array([[3], [2], [2]])

def fp(w):
    return 2 * X.T @ (X @ w - y)

stepsize = 0.1
maxiter = 1000000
w0 = np.array([[0.0], [0.0]])
w_traces = gd.gd_const_ss(fp, w0, stepsize=stepsize, maxiter=maxiter)

print(
    f'stepsize={stepsize}, number of iterations={len(w_traces)-1}')
print(f'optimal solution:\n{w_traces[-1]}')

sol = np.linalg.solve(X.T @ X, X.T @ y)
print(f'solution from np.linalg.solve:\n{sol}')


import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
    return (np.sqrt(x**4-2*x**2*y**2+y**4+4)/(x+y))+x+y

def grad_x(x, y):
    return((x+y)**2*np.sqrt(x**4 - 2*x**2*y**2 + y**4 + 4) + x**4 + 2*x**3*y - 2*x*y**3 - y**4 - 4)/((x+y)**2*np.sqrt(x**4 - 2*x**2*y**2 + y**4 + 4))

def grad_y(x, y):
    return((x+y)**2*np.sqrt(x**4 - 2*x**2*y**2 + y**4 + 4) - x**4 - 2*x**3*y + 2*x*y**3 + y**4 - 4)/((x+y)**2*np.sqrt(x**4 - 2*x**2*y**2 + y**4 + 4))

x_min = 0.707106
y_min = x_min

x = [2.25]
y = [3]
pkt = [f(x[-1], y[-1])]
# x = np.array([2.25])
# y = np.array([3])
# pkt = np.array([f(x[-1], y[-1])])

krok = 1
dok = 1e-5
licz = 0
jeszcze = True
while jeszcze:
    licz += 1

    grx = grad_x(x[-1], y[-1])
    gry = grad_y(x[-1], y[-1])
    nx = x[-1] - grx * krok
    ny = y[-1] - gry * krok
    npkt = f(nx, ny)

    if npkt < pkt[-1]:
        x.append(nx)
        y.append(ny)
        pkt.append(npkt)
        # np.append(x, [nx])
        # np.append(y, [ny])
        # np.append(pkt, [npkt])
    else:
        x.append(x[-1])
        y.append(y[-1])
        pkt.append(pkt[-1])
        # np.append(x, [x[-1]])
        # np.append(y, [y[-1]])
        # np.append(pkt, [pkt[-1]])
        krok /= 2
        bl = npkt/(np.sqrt(grx**2+gry**2))

    if krok < dok :
        jeszcze = False

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, '-k')
ax.plot(x_min, y_min, 'xb')
ax.plot(x[-1], y[-1], '+k')
ax.axis('equal')
x0 = 0.1
xk = 3
Nx = 500
x = np.linspace(x0, xk,Nx)
y0=0.1
yk= 2.5
Ny = 500
y = np.linspace(y0, yk, Ny)
X,Y = np.meshgrid(x,y)
Z= f(X,Y)
ax.contour(X,Y,Z,15)
plt.show()


# BSM Model

from math import sqrt

import numpy as np
from scipy.stats import norm

def N(x):
    return norm.cdf(x)

def d1(S0, K, r, sigma, T):
    return (np.log(S0 / K) + (r + sigma * sigma / 2) * T) / (sigma * sqrt(T))

def d2(S0, K, r, sigma, T):
    return d1(S0, K, r, sigma, T) - sigma * sqrt(T)

def bsm_call_price(S0, K, r=0.01, sigma=0.1, T=1):
    return S0 * N(d1(S0, K, r, sigma, T)) - K * np.exp(-r * T) * N(d2(S0, K, r, sigma, T))

def bsm_put_price(S0, K, r=0.01, sigma=0.1, T=1):
    return -S0 * N(-d1(S0, K, r, sigma, T)) + K * np.exp(-r * T) * N(-d2(S0, K, r, sigma, T))

def bsm_price(S0, K, r=0.01, sigma=0.1, T=1, is_call=True):
    if is_call:
        return bsm_call_price(S0, K, r, sigma, T)
    else:
        return bsm_put_price(S0, K, r, sigma, T)


from math import fabs
from typing import Union

import numpy as np
from numpy.lib.stride_tricks import sliding_window_view

from jesse.helpers import get_candle_source, slice_candles, same_length

def fwma(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
    float, np.ndarray]:
    """
    Fibonacci's Weighted Moving Average (FWMA)

    :param candles: np.ndarray
    :param period: int - default: 5
    :param source_type: str - default: "close"
    :param sequential: bool - default: False

    :return: float | np.ndarray
    """

    # Accept normal array too.
    if len(candles.shape) == 1:
        source = candles
    else:
        candles = slice_candles(candles, sequential)
        source = get_candle_source(candles, source_type=source_type)

    fibs = fibonacci(n=period)
    swv = sliding_window_view(source, window_shape=period)
    res = np.average(swv, weights=fibs, axis=-1)

    return same_length(candles, res) if sequential else res[-1]

def fibonacci(n: int = 2) -> np.ndarray:
    """Fibonacci Sequence as a numpy array"""
    n = (int(fabs(n)) if n >= 0 else 2) - 1
    a, b = 1, 1

    result = np.array([a])

    for _ in range(n):
        a, b = b, a + b
        result = np.append(result, a)

    fib_sum = np.sum(result)
    return result / fib_sum if fib_sum > 0 else result


import matplotlib.pyplot as plt
import numpy as np

def logistic_map(x0, r, n):
    xs = [x0]
    x = x0
    for i in range(n):
        x = r * x * (1 - x)
        xs.append(x)
    return xs

def bifurcation_diagram(x0, r_min, r_max, r_step, n, k):
    fig, ax = plt.subplots()
    r_range = np.arange(r_min, r_max + r_step, r_step)
    cycles = {}
    for r in r_range:
        xs = logistic_map(x0=x0, r=r, n=n)[k:]
        xs = np.unique(xs)
        cycles[r] = len(xs)
        ax.scatter(np.full_like(xs, r), xs, c='b', s=r_step)
    return cycles

if __name__ == '__main__':
    # bifurcation_diagram(x0=0.2, r_min=2.8, r_max=3.9, r_step=0.01, n=1000, k=50)
    bifurcation_diagram(x0=0.5, r_min=3.84, r_max=3.8571, r_step=0.001, n=1000, k=50)
    plt.show()


    import numpy as np

    def find_distance(row_A, row_B):
        """
        Returns Euclidean distance between 2 rows of data.

            Parameters:
                    row_A (list): vector of numerical data
                    row_B (list): vector of numerical data

            Returns:
                 Euclidean Distance (float)
        """
        # row_A = np.array(row_A)
        # row_B = np.array(row_B)

        # set distance to start at 0
        dist = 0.0
        # iterate through row_A
        for i in range(len(row_A)):
            # subtract the rows element wise, square the difference, then add
            #. it to the distance
            dist += (row_A[i] - row_B[i])**2

        # return the square root of the total distance
        return np.sqrt(dist)


# We will learn how to write our own code for the Ks-neighbor algorithm
# starting with an introduction on the Euclidean distance
"""
Define:
Euclidean Distance - Practical Machine Learning

"""
from math import sqrt
import numpy as np
import warnings                     # to warn the user when they using a wrong number for k
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')
from collections import Counter     # to create the vote basically

# Simple example on programming the Euclidean distance
point1 = [1,3]
point2 = [2,5]
def euclidean_distance(point1,point2):
    return sqrt((point1[0]-point2[0])**2+(point1[1]-point2[1])**2)
ed1 = euclidean_distance(point1,point2)
print('euclidean_distance = ', ed1)

Machine_Learning_Old_Files/[15] Writing our own Ks-neighbor algorithm.py


# -*- coding: utf-8 -*-
"""
helper function: convert Hz to Mel scale

  Args:
    fInHz: The frequency to be converted, can be scalar or vector
    cModel: The name of the model ('Fant' [default], 'Shaughnessy', 'Umesh')

  Returns:
    Mel values of the input dimension
"""

import numpy as np
import math

def ToolFreq2Mel(fInHz, cModel = 'Fant'):
    def acaFant_scalar(f):
        return 1000 * math.log2(1 + f/1000)
    def acaShaughnessy_scalar(f):
        return 2595 * math.log10(1 + f/700)
    def acaUmesh_scalar(f):
        return f/(2.4e-4*f + 0.741)

    f = np.asarray(fInHz)
    if f.ndim == 0:
        if cModel == 'Shaughnessy':
            return acaShaughnessy_scalar(f)
        elif cModel == 'Umesh':
            return acaUmesh_scalar(f)
        else:
            return acaFant_scalar(f)

    fMel = np.zeros(f.shape)
    if cModel == 'Shaughnessy':
        for k,fi in enumerate(f):
            fMel[k] =  acaShaughnessy_scalar(fi)
    elif cModel == 'Umesh':
        for k,fi in enumerate(f):
            fMel[k] =  acaUmesh_scalar(fi)
    else:
        for k,fi in enumerate(f):
            fMel[k] =  acaFant_scalar(fi)

    return (fMel)


# Implementation of gaussInferParamsMean1d
# Author: Animesh Gupta

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
import pyprobml_utils as pml

priorVar = [1, 5]
Sigma = 1 # Assumed to be known
X = 3
post_mu = 0
post_Sigma = 0

for i in priorVar:

    prior_Sigma = i
    prior_mu    = 0
    xbar = np.mean(X)
    n    = np.size(X)

    lik_Sigma = Sigma
    lik_mu    = xbar

    S0    = prior_Sigma
    S0inv = 1./S0
    mu0   = prior_mu
    S     = Sigma
    Sinv  = 1./S
    Sn    = 1./(S0inv + n*Sinv)

    post_mu    = Sn*(n*Sinv*xbar + S0inv*mu0)
    post_Sigma = Sn

    x = np.arange(-5,5.25, 0.25)

    fig = plt.figure(figsize = (4, 4))
    plt.ylim(0,0.6)
    plt.xlim(-5,5)
    plt.xticks([-5,0,5])
    plt.plot(x, multivariate_normal.pdf(x, mean = prior_mu, cov = prior_Sigma), color = "blue",
             label = "prior")
    plt.plot(x, multivariate_normal.pdf(x, mean = lik_mu, cov = lik_Sigma), color = "red",
             label = "lik",linestyle='dotted')
    plt.plot(x, multivariate_normal.pdf(x, mean = post_mu, cov = post_Sigma), color = "black",
             label = "post",linestyle='dashdot')
    plt.title(f"prior variance of {i}")
    plt.legend(loc="upper left")
    pml.savefig(f"gauss_infer_1d_{i}.pdf")


# Resolving sets of congruent equations using Chinese remainder theorem
# Rafał Lal 03/06/2021
import sys
from math import gcd, lcm, floor
from itertools import product
from functools import reduce
import numpy

nums = []
mods = []

def check_req():
    if numpy.prod(mods) != reduce(lcm, mods):
        return False
    prod = [x for x in product(mods, mods) if x[0] != x[1]]
    for i in range(len(prod)):
        if gcd(prod[i][0], prod[i][1]) != 1:
            return False
    return True

def extended_euclid(x, y):
    x0, x1, y0, y1 = 1, 0, 0, 1
    while y > 0:
        q, x, y = floor(x / y), y, x % y
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return q, x0, y0

def inversed_mod(a, m):
    g, x, y = extended_euclid(a, m)
    return x % m

def chinese_remainder_theorem():
    n = numpy.prod(mods)
    ni, mi, res = [], [], []
    for i in range(len(mods)):
        ni.append(int(n / mods[i]))
        mi.append(int(inversed_mod(ni[i], mods[i])))
        res.append(int(nums[i] * ni[i] * mi[i]))
    try:
        res = sum(res) % n
    except ArithmeticError:
        res = sum(res)
    return n, ni, mi, res

def main():
    print('Insert congruent equation (q to break): ')
    while True:
        char = input('x {} '.format(chr(0x2261)))
        if char == 'q':
            break
        nums.append(int(char))
        char = int(input('mod '))
        mods.append(int(char))

    if not check_req():
        print('Equation does not fulfil requirments')
        sys.exit()

    n, ni, mi, result, = chinese_remainder_theorem()
    print('\nResults:\nn =', n, sep=' ')
    print('Ni =', *ni, sep=' ')
    print('Mi =', *mi, sep=' ')
    print('x {} {} (mod {})'.format(chr(0x2261), result, n))

    return

if __name__ == '__main__':
    main()


"""
    MÉTODO PUNTO MEDIO O EULER MEJORADO
    __ funciones__
    complete_integrate: devuelve soluciones de nodos y soluciones
    succ_integrate: dado un nodo devuelve la siguiente solución

    __ significado variables __
    X,Y     = integrate(F,x0,y0,xfinal,N).
    {y}'    = {F(x,{y})}, donde
    {y}     = {y[0],y[1],...y[N-1]}.
    x0,y0   = condiciones iniciales
    xfinal  = valor final de la variable x
    h       = incremento de x usado para la integración
    F       = función suplida por el usuario que devuelve
            el array F(x,y) = {y'[0],y'[1],...,y'[N-1]}.
"""
import numpy as np
def succ_euler(F, x,y,h):
    '''método de euler explícito
    F (x,y) función derivada
    x, y valores de los que se parte
    incremento
    Salida : y_{n+1}
    '''
    return y + h* F(x,y)

def succ_integrate(F,x,y,h):
    '''PUNTO MEDIO siguiente solución
    '''
    return y + h*F(x+h/2, succ_euler(F,x,y,h/2))

def complete_integrate(F,x0, y0,xfinal, N ):
    X = np.linspace(x0,xfinal,N+1)
    Y = [y0]
    h = (xfinal - x0)/N

    for n in range(N):
        Y.append(succ_integrate(F,X[n],Y[n],h))
    return X,Y


from sympy import *
from quaternion_R_utils import *
from plucker_line_utils import *

fx,fy,cx,cy = symbols('fx fy cx cy');
px, py, pz = symbols('px py pz')
q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
mx_w, my_w, mz_w, lx_w, ly_w, lz_w = symbols('mx_w my_w mz_w lx_w ly_w lz_w')
u_s,v_s = symbols('u_s v_s')
u_e,v_e = symbols('u_e v_e')

position_symbols = [px, py, pz]
quaternion_symbols = [q0, q1, q2, q3]
plucker_line_symbols = [mx_w, my_w, mz_w]
all_symbols = position_symbols + quaternion_symbols + plucker_line_symbols

RT_wc = matrix44FromQuaternion(px, py, pz, q0, q1, q2, q3)
K=plucker_line_K(fx, fy, cx, cy)
mm_cw = plucker_line_motion_matrix_cw(RT_wc)
l_w = Matrix([[mx_w],[my_w],[mz_w],[lx_w],[ly_w],[lz_w]])
l_c = mm_cw*l_w
m_c=Matrix([[l_c[0,0]],[l_c[1,0]],[l_c[2,0]]])
l=K*m_c

x_s=Matrix([[u_s,v_s,1]])
x_e=Matrix([[u_e,v_e,1]])
xs_delta = Matrix([[0]]) - (x_s*l)/sqrt(l[0,0]*l[0,0] + l[1,0]*l[1,0])
xe_delta = Matrix([[0]]) - (x_e*l)/sqrt(l[0,0]*l[0,0] + l[1,0]*l[1,0])

obs_eq = Matrix([xs_delta, xe_delta]).vec()
obs_eq_jacobian = obs_eq.jacobian(all_symbols)

print(obs_eq)
print(obs_eq_jacobian)

with open("perspective_camera_plucker_line_quaternion_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void observation_equation_perspective_camera_plucker_line_quaternion_wc(Eigen::Matrix<double, 2, 1> &delta, double fx, double fy, double cx, double cy, double px, double py, double pz, double q0, double q1, double q2, double q3, double mx_w, double my_w, double mz_w, double lx_w, double ly_w, double lz_w, double u_s, double v_s, double u_e, double v_e)\n")
    f_cpp.write("{")
    f_cpp.write("delta.coeffRef(0,0) = %s;\n"%(ccode(obs_eq[0,0])))
    f_cpp.write("delta.coeffRef(1,0) = %s;\n"%(ccode(obs_eq[1,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void observation_equation_perspective_camera_plucker_line_quaternion_wc_jacobian(Eigen::Matrix<double, 2, 7, Eigen::RowMajor> &j, double fx, double fy, double cx, double cy, double px, double py, double pz, double q0, double q1, double q2, double q3, double mx_w, double my_w, double mz_w, double lx_w, double ly_w, double lz_w, double u_s, double v_s, double u_e, double v_e)\n")
    f_cpp.write("{")
    for i in range (2):
        for j in range (7):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(obs_eq_jacobian[i,j])))
    f_cpp.write("}")


import numpy as np
# B/B,B/W,W/W,B/B

# inspired by https://github.com/cavaunpeu/statistical-rethinking/blob/master/chapter-2/homework.R
#[b/b, b/w, w/w,b/b]
# in which ways can they produce 1st black (cause there are 2x b/b they can produce 2*2 first black)
ways_producing_black = np.array([4.,1.,0.])

priors = ways_producing_black / ways_producing_black.sum()
print(priors)

#[b/b,b/w,w/w]
# and can produce second black
ways_can_produce_black_on_other_side = np.array([4., 0., 0.])
posterior = ways_can_produce_black_on_other_side*priors/ways_can_produce_black_on_other_side.sum()
print(posterior)
prob_black_other_side = posterior[0]
print('prob that other side is black = ' + str(prob_black_other_side))

# inspired by https://rpubs.com/andersgs/my_solutions_chapter2_statrethink
# b1/b2, b1/w1, w1/w2, b3/b4
#
# card |pick|other side
#
# b1/b2| 1 | 1
# b2/b1| 1 | 1
# b1/w1| 1 | 0
# w1/b1| 0 | 0
# w1/w2| 0 | 0
# w2/w1| 0 | 0
# b3/b4| 1 | 1
# b4/b3| 1 | 1
#
# to produce black on other side 4
# from to produce black on the first side 5
# 4/5 ~ 0.8

#bayes theorem
# probability that the second side is black given that the first side is black
# P(2black|1black) = P(1black|2black)*P(2black)/P(1black)
# probability that a card has black on both sides is 2 card from 4 cards
# P(2black) = 2/4 = 1/2
# probability that a card has 1 black side from 8 producing ways of card settings
# is 5, so 5/8 = 5/8
# P(1black) = 5/8
# probability that 1st side is black given the second side is black, e.g.
# that both sides are black is 1
# P(1black|2black) = 1 (hm)
# 1*1/2 / 5/8 ~ 0.8


import pandas as pd
from matplotlib import pyplot as plt
import scipy.stats as stats
from pandas import DataFrame, Series
import seaborn as sns
import pymc3 as pm

true_prob_a = 0.05
n_a = 1500
true_prob_b = 0.04
n_b = 750

obs_a_data = Series(stats.bernoulli.rvs(true_prob_a, size=n_a), index=range(0, n_a))
obs_b_data = Series(stats.bernoulli.rvs(true_prob_b, size=n_b), index=range(0, n_b))

# set prior on a
# note: can take samples from this
with pm.Model() as model:
    p_a = pm.Uniform('p_a', lower=0, upper=1)
    p_b = pm.Uniform('p_b', lower=0, upper=1)

# now update with actual obs
# CANNOT take samples from this
with model:
    # what does it mean that pm.Bernoulli takes p_a?
    # i guess that we're chaining these things together
    obs_a = pm.Bernoulli('obs_a', p_a, observed=obs_a_data)
    obs_b = pm.Bernoulli('obs_b', p_b, observed=obs_b_data)

# now run model (explained later)
# note: not referring obs_a or obs_b anywhere in here
# fits whole system
with model:
    step = pm.Metropolis()
    trace = pm.sample(18000, step=step)
    burned_trace = trace[1000:]

# now this is a bunch of samples from the posterior
ps_a = Series(burned_trace['p_a'])
ps_b = Series(burned_trace['p_b'])
diff = ps_a - ps_b

ps = pd.concat([
    ps_a.to_frame('p').assign(test = 'a'),
    ps_b.to_frame('p').assign(test = 'b'),
    diff.to_frame('p').assign(test = 'diff')], ignore_index=True)

# now plot
# note: should re-anki FacetGrid thing, didn't remember
plt.clf()
g = (sns.FacetGrid(ps, hue='test', col='test')
     .map(sns.distplot, 'p'))
g.add_legend()


#!/usr/bin/env python3

import numpy as np
from tqdm import tqdm

def step(x):
    """step function:
    outputs 1 if x > 0
    and -1 otherwise"""
    xout = np.ones_like(x)
    xout[x<0] = -1
    return xout

def sigmoid(x):
    """sigmoid function"""
    return 1.0/(1.0 + np.exp(-x))

class cross_entropy_loss:
    def __init__(self):
        self.name = 'cross-entropy'

    def loss(self, y, y_pred, zerotol=1e-10):
        """the cross-entropy loss for each
        data instance

        Args:
            y (array): labels for each instance (0 or 1)
            y_pred (array): predicted probabilty that
                each instance belongs to class 1
        """
        loss = -(y * np.log(y_pred + zerotol) + (1 - y) * np.log(1 - y_pred + zerotol))
        return loss

    def derivative_loss(self, y, y_pred):
        """the derivative of the cross-entropy loss w.r.t.
        to sigmoid activation function (we will get to
        this later)

        Args:
            y (array): labels for each instance (0 or 1)
            y_pred (array): predicted probabilty that
                each instance belongs to class 1
        """
        return y_pred - y

class Perceptron:

    def __init__(self, n_in, activation=sigmoid, loss=cross_entropy_loss, b=None):
        """A simple Perceptron implementation;
        This implementation also contains the
        gradient descent algorithm (see the
        gradient_descent_step and train
        functions).

        Args:
            n_in (int): number of input features
            activation (function): activation function of the Perceptron;
                should only take x as input
            loss (function): the used loss function;
                This should be the cross_entropy
                loss for a sigmoid activation
            b (float): bias term; if a value is specified, the
                bias term is fixed at this value. if not,
                the bias will be estimated during training.
        """
        self.n_in = n_in
        self.w = np.random.uniform(-1,1,n_in)
        if b is None:
            self.b = np.random.uniform(-1,1,1)
            self.fit_b = True
        else:
            self.b = b
            self.fit_b = False
        self.activation = activation
        self.loss = loss().loss
        self.derivative_loss = loss().derivative_loss

    def predict(self, x):
        """Predict probability that each
        instance of x (with shape n_instances x n_features)
        belongs to class 1

        Args:
            x (ndarray): input data (n_instances x n_features)

        Returns:
            predicted probability for each instance
        """
        self.Z = np.dot(x, self.w) + self.b
        self.A = self.activation(self.Z)
        return self.A

    def gradient_descent_step(self, x, y, learning_rate):
        """A single gradient descent step.

        Args:
            x (ndarray): input data (n_instances x n_features)
            y (array): label of each instance (0 or 1)
            learning_rate (float): learning rate of the
                gradient descent algorithm
        """
        # compute derivative of loss wrt Z
        dZ = self.derivative_loss(y, self.predict(x))
        dW = np.dot(dZ, x)
        # subtract average derivative from weights
        self.w -= learning_rate * 1.0/dW.shape[0] * dW
        if self.fit_b:
            self.b -= learning_rate * (1.0/x.shape[0] * np.sum(dZ))

    def train(self, X, y, batch_size=8, learning_rate=1, n_steps=100):
        """Apply gradient descent algorithm.
        At each iteration, the algorithm will draw
        a random sample from x and perform a weight
        update with the partial derivatives that
        are computed by the use of this sample.

        Args:
            x (ndarray): input data (n_instances x n_features)
            y (array): label of each instance (indicated by 0 and 1)
            learning_rate (float): learning rate of the
                gradient descent algorithm
            n_steps (int): number of gradient descent
                iterations to perform during training
        """
        self.training_w = np.zeros((n_steps, self.n_in+1))
        self.training_loss = np.zeros(n_steps)
        for s in tqdm(range(n_steps)):
            # draw a random batch
            batch_idx = np.random.choice(X.shape[0], batch_size, replace=False)
            # compute and store mean loss
            self.training_loss[s] = np.mean(self.loss(y[batch_idx], self.predict(X[batch_idx])))
            # store current weights
            self.training_w[s,:self.n_in] = self.w
            self.training_w[s,-1] = self.b
            # perform a gradient descent step
            self.gradient_descent_step(X[batch_idx], y[batch_idx], learning_rate)


'''
calc_performance_metrics_for_proba_predictions.py

Provides implementation of common metrics for assessing a binary classifier's
*probabilistic* predictions against true binary labels, including:

* binary cross entropy from probabilities
* binary cross entropy from scores (real-values, to be fed into sigmoid)
'''

import numpy as np

from scipy.special import logsumexp as scipy_logsumexp
from scipy.special import expit as sigmoid

def calc_mean_binary_cross_entropy_from_probas(ytrue_N, yproba1_N):
    ''' Compute average cross entropy for given binary classifier's predictions

    Consumes probabilities ("probas"), values between 0.0 and 1.0.

    Computing BCE uses *base-2* logarithms, so the resulting number is a valid
    upper bound of the zero-one loss (aka error rate) when we threshold at 0.5.

    Notes
    -----
    Given a binary label $y_n \in \{0, 1}$ and a probability $p_n \in (0,1)$,
    we define binary cross entropy as:
    $$
        BCE(y_n, p_n) = - y_n \log_2 p_n - (1-y_n) \log_2 (1-p_n)
    $$
    Given $N$ labels and their predicted probas, we define the mean BCE as:
    $$
        mean_BCE(y, p) = \frac{1}{N} \sum_{n=1}^N BCE(y_n, p_n)
    $$

    Args
    ----
    ytrue_N : 1D array, shape (n_examples,) = (N,)
        All values must be either 0 or 1. Will be cast to int dtype.
        Each entry represents the binary 'true' label of one example
        One entry per example in current dataset
    yproba1_N : 1D array, shape (n_examples,) = (N,)
        All values must be within the interval 0.0 - 1.0, inclusive.
        Will be truncated to (eps, 1 - eps) to keep log values from extremes,
        with small value eps equal to 10^{-14}.
        Each entry is probability that that example should have positive label.
        One entry per example in current dataset.
        Needs to be same size as ytrue_N.

    Returns
    -------
    bce : float234erdf
        Binary cross entropy, averaged over all N provided examples

    Examples
    --------
    >>> N = 8
    >>> ytrue_N = np.asarray([0., 0., 0., 0., 1., 1., 1., 1.])

    # Try perfect predictions
    >>> perfect_proba1_N = np.asarray([0., 0., 0., 0., 1., 1., 1., 1.])
    >>> perfect_bce = calc_mean_binary_cross_entropy_from_probas(
    ...     ytrue_N, perfect_proba1_N)
    >>> print("%.4f" % perfect_bce)
    0.0000

    # Try some confident scores
    >>> good_proba1_N = np.asarray([0.01, 0.05, 0.1, 0.1, 0.9, 0.9, 0.95, 0.97])
    >>> good_bce = calc_mean_binary_cross_entropy_from_probas(
    ...     ytrue_N, good_proba1_N)
    >>> print("%.4f" % good_bce)
    0.1018

    # Try some decent but underconfident scores
    >>> ok_proba1_N = np.asarray([0.3, 0.4, 0.46, 0.47, 0.5, 0.6, 0.7, 0.71])
    >>> ok_bce = calc_mean_binary_cross_entropy_from_probas(
    ...     ytrue_N, ok_proba1_N)
    >>> print("%.4f" % ok_bce)
    0.7253

    # Try some mistakes that are way over confident
    >>> bad_pr1_N  = np.asarray([0., 0., 1., 0., 1., 1., 0., 0.])
    >>> bad = calc_mean_binary_cross_entropy_from_probas(ytrue_N, bad_pr1_N)
    >>> print("%.4f" % bad)
    17.4403

    # Try empty
    >>> empty_bce = calc_mean_binary_cross_entropy_from_probas([], [])
    >>> np.allclose(0.0, empty_bce)
    True
    '''

    if(len(ytrue_N) < 1):
        return 0
    if(len(yproba1_N) < 1):
        return 0
    # Cast labels to integer just to be sure we're getting what's expected
    ytrue_N = np.asarray(ytrue_N, dtype=np.int32)
    N = int(ytrue_N.size)
    # Cast probas to float and be sure we're between zero and one
    yproba1_N = np.asarray(yproba1_N, dtype=np.float64)           # dont touch
    yproba1_N = np.maximum(1e-14, np.minimum(1-1e-14, yproba1_N)) # dont touch

    return np.sum(- ytrue_N * np.log2(yproba1_N) - (1-ytrue_N) * np.log2(1-yproba1_N))/ytrue_N.shape[0]

def calc_mean_binary_cross_entropy_from_scores(ytrue_N, scores_N):
    ''' Compute average cross entropy for given binary classifier's predictions

    Consumes "scores", real values between (-np.inf, np.inf),
    and then conceptually produces probabilities by feeding into sigmoid,
    and then into the BCE function to get the result.

    In practice, we compute BCE directly from scores using an implementation
    that avoids the numerical issues of the sigmoid (saturation, if underflows
    to zero would become negative infinity). This clever implementation uses
    the "logsumexp" trick.

    Computing BCE uses *base-2* logarithms, so the resulting number is a valid
    upper bound of the zero-one loss (aka error rate) when we threshold at 0.5.

    Args
    ----
    ytrue_N : 1D array, shape (n_examples,) = (N,)
        All values must be either 0 or 1. Will be cast to int dtype.
        Each entry represents the binary 'true' label of one example
        One entry per example in current dataset
    scores_N : 1D array, shape (n_examples,) = (N,)
        One entry per example in current dataset.
        Each entry is a real value, could be between -infinity and +infinity.
        Large negative values indicate strong probability of label y=0.
        Zero values indicate probability of 0.5.
        Large positive values indicate strong probability of label y=1.
        Needs to be same size as ytrue_N.

    Returns
    -------
    bce : float
        Binary cross entropy, averaged over all N provided examples

    Examples
    --------
    >>> N = 8
    >>> ytrue_N = np.asarray([0., 0., 0., 0., 1., 1., 1., 1.])

    # Try some good scores for these 8 points.
    >>> good_scores_N = np.asarray([-4., -3., -2., -1., 1, 2., 3., 4.])
    >>> good_bce = calc_mean_binary_cross_entropy_from_scores(
    ...     ytrue_N, good_scores_N)
    >>> print("%.6f" % (good_bce))
    0.182835

    # Try some near-perfect scores for these 8 points.
    >>> perfect_scores_N = np.asarray([-9., -9., -9,  -9., 9., 9., 9., 9.])
    >>> perfect_bce = calc_mean_binary_cross_entropy_from_scores(
    ...     ytrue_N, perfect_scores_N)
    >>> print("%.6f" % (perfect_bce))
    0.000178

    # Check same computation with "probas". Should match exactly.
    >>> perfect_bce2 = calc_mean_binary_cross_entropy_from_probas(
    ...     ytrue_N, sigmoid(perfect_scores_N))
    >>> print("%.6f" % (perfect_bce2))
    0.000178

    # Try some extreme scores (should get worse BCE with more extreme scores)
    >>> ans = calc_mean_binary_cross_entropy_from_scores([0], [+99.])
    >>> print("%.6f" % (ans))
    142.826809
    >>> ans = calc_mean_binary_cross_entropy_from_scores([0], [+999.])
    >>> print("%.6f" % (ans))
    1441.252346
    >>> ans = calc_mean_binary_cross_entropy_from_scores([0], [+9999.])
    >>> print("%.6f" % (ans))
    14425.507714

    # Try with "probas": sigmoid saturates! using scores is *better*
    >>> a = calc_mean_binary_cross_entropy_from_probas([0], sigmoid([+999.]))
    >>> print("%.6f" % (a))
    46.508147
    >>> a = calc_mean_binary_cross_entropy_from_probas([0], sigmoid([+9999.]))
    >>> print("%.6f" % (a))
    46.508147

    # Try empty
    >>> empty_bce = calc_mean_binary_cross_entropy_from_scores([], [])
    >>> np.allclose(0.0, empty_bce)
    True
    '''
    if(len(ytrue_N) < 1):
        return 0.0
    if(len(scores_N) < 1):
        return 0.0

    # Cast labels to integer just to be sure we're getting what's expected
    ytrue_N = np.asarray(ytrue_N, dtype=np.int32)
    N = int(ytrue_N.size)
    if N == 0:
        return 0.0 # special case: empty array should be 0

    # Convert binary y values so 0 becomes +1 and 1 becomes -1
    # See HW2 instructions on website for the math

    yflippedsign_N = -1 * np.sign(ytrue_N-0.001)

    sum = 0

    for i in range(len(scores_N)):
        sum += scipy_logsumexp(a=np.append(yflippedsign_N[i] * scores_N[i], 0))/np.log(2)
    # TODO fixme
    # Be sure to use scipy_logsumexp were needed
    return sum/len(scores_N)

# N = 8
# ytrue_N = np.asarray([0., 0., 0., 0., 1., 1., 1., 1.])
#
# # Try perfect predictions
# perfect_proba1_N = np.asarray([0., 0., 0., 0., 1., 1., 1., 1.])
# perfect_bce = calc_mean_binary_cross_entropy_from_probas(ytrue_N, perfect_proba1_N)
# print("%.4f" % perfect_bce)
# # 0.0000
#
# # Try some confident scores
# good_proba1_N = np.asarray([0.01, 0.05, 0.1, 0.1, 0.9, 0.9, 0.95, 0.97])
# good_bce = calc_mean_binary_cross_entropy_from_probas(ytrue_N, good_proba1_N)
# print("%.4f" % good_bce)
# # 0.1018
#
# # Try some decent but underconfident scores
# ok_proba1_N = np.asarray([0.3, 0.4, 0.46, 0.47, 0.5, 0.6, 0.7, 0.71])
# ok_bce = calc_mean_binary_cross_entropy_from_probas(ytrue_N, ok_proba1_N)
# print("%.4f" % ok_bce)
# # 0.7253
#
# # Try some mistakes that are way over confident
# bad_pr1_N  = np.asarray([0., 0., 1., 0., 1., 1., 0., 0.])
# bad = calc_mean_binary_cross_entropy_from_probas(ytrue_N, bad_pr1_N)
# print("%.4f" % bad)
# # 17.4403
#
# # Try empty
# empty_bce = calc_mean_binary_cross_entropy_from_probas([], [])
# print(np.allclose(0.0, empty_bce))
# # True

# N = 8
# ytrue_N = np.asarray([0., 0., 0., 0., 1., 1., 1., 1.])
#
# # Try some good scores for these 8 points.
# good_scores_N = np.asarray([-4., -3., -2., -1., 1, 2., 3., 4.])
# good_bce = calc_mean_binary_cross_entropy_from_scores(ytrue_N, good_scores_N)
# print("%.6f" % (good_bce))
# # 0.182835
#
# # Try some near-perfect scores for these 8 points.
# perfect_scores_N = np.asarray([-9., -9., -9,  -9., 9., 9., 9., 9.])
# perfect_bce = calc_mean_binary_cross_entropy_from_scores(ytrue_N, perfect_scores_N)
# print("%.6f" % (perfect_bce))
# # 0.000178
#
# # Check same computation with "probas". Should match exactly.
# perfect_bce2 = calc_mean_binary_cross_entropy_from_probas(ytrue_N, sigmoid(perfect_scores_N))
# print("%.6f" % (perfect_bce2))
# # 0.000178
#
# # Try some extreme scores (should get worse BCE with more extreme scores)
# ans = calc_mean_binary_cross_entropy_from_scores([0], [+99.])
# print("%.6f" % (ans))
# # 142.826809
# ans = calc_mean_binary_cross_entropy_from_scores([0], [+999.])
# print("%.6f" % (ans))
# # 1441.252346
# ans = calc_mean_binary_cross_entropy_from_scores([0], [+9999.])
# print("%.6f" % (ans))
# # 14425.507714
#
# # Try with "probas": sigmoid saturates! using scores is *better*
# a = calc_mean_binary_cross_entropy_from_probas([0], sigmoid([+999.]))
# print("%.6f" % (a))
# # 46.508147
# a = calc_mean_binary_cross_entropy_from_probas([0], sigmoid([+9999.]))
# print("%.6f" % (a))
# # 46.508147
#
# # Try empty
# empty_bce = calc_mean_binary_cross_entropy_from_scores([], [])
# np.allclose(0.0, empty_bce)
# True


#!/usr/bin/env python3
"""
Clustering Module
"""
import numpy as np

def initialize(X, k):
    """
    Initializes cluster centroids for K-means:

    X is a numpy.ndarray of shape (n, d) containing the dataset that will be
    used for K-means clustering
        n is the number of data points
        d is the number of dimensions for each data point
    k is a positive integer containing the number of clusters
    The cluster centroids should be initialized with a multivariate uniform
    distribution along each dimension in d:
        The minimum values for the distribution should be the minimum values
        of X along each dimension in d
        The maximum values for the distribution should be the maximum values
        of X along each dimension in d
        You should use numpy.random.uniform exactly once
    You are not allowed to use any loops

    Returns: a numpy.ndarray of shape (k, d) containing the initialized
    centroids for each cluster, or None on failure
    """
    if not isinstance(X, np.ndarray) or len(X.shape) != 2:
        return None
    if type(k) != int or k <= 0 or k >= X.shape[0]:
        return None

    max = np.max(X, axis=0)
    min = np.min(X, axis=0)
    return np.random.uniform(min, max, size=(k, X.shape[1]))


"""
Concider the MIQCQP problem
0.5 * (x1^2 + 2x2^2 + 3x3^2) + 15x1 + 8x2 + 80x3 -> min        (1)
subjected to
x1 + 2x2 + 3x3 <= 150            (2)
8x1 +  15x2 +  80x3 <= 800    (3)
x2 - x3 = 25.5                           (4)
x1 <= 15                                  (5)
x1^2 + 2.5 x2^2 + 3 x3^2 + 0.1 x1 + 0.2 x2 + 0.3 x3 - 1000 <= 0 (6)
2 x1^2 + x2^2 + 3 x3^2 + 0.1 x1 + 0.5 x2 + 0.3 x3  <= 1000 (7)
x1, x3 are integers
"""

from numpy import diag, matrix, inf
from openopt import QP

H = diag([1.0, 2.0,3.0])
f = [15,8,80]
A = matrix('1 2 3; 8 15 80')
b = [150, 800]

# QC should be list or tuple of triples (P, q, s): 0.5 x^T P x + q x + s <= 0
QC = ((diag([1.0, 2.5, 3.0]), [0.1, 0.2, 0.3], -1000), (diag([2.0, 1.0, 3.0]), [0.1, 0.5, 0.3], -1000))

p = QP(H, f, A = A, b = b, Aeq = [0, 1, -1], beq = 25.5, ub = [15,inf,inf], QC = QC, name='OpenOpt QCQP example 1')
# or p = QP(H=diag([1,2,3]), f=[15,8,80], ...)

r = p.solve('cplex', iprint = 0, plot=1)
f_opt, x_opt = r.ff, r.xf
# x_opt = array([ -2.99999999,   9.5       , -16.        ])
# f_opt = -770.24999989134858