import numpy as np

def affine_forward(x, w, b):
  """
  Computes the forward pass for an affine (fully-connected) layer.

  The input x has shape (N, d_1, ..., d_k) and contains a minibatch of N
  examples, where each example x[i] has shape (d_1, ..., d_k). We will
  reshape each input into a vector of dimension D = d_1 * ... * d_k, and
  then transform it to an output vector of dimension M.

  Inputs:
  - x: A numpy array containing input data, of shape (N, d_1, ..., d_k)
  - w: A numpy array of weights, of shape (D, M)
  - b: A numpy array of biases, of shape (M,)

  Returns a tuple of:
  - out: output, of shape (N, M)
  - cache: (x, w, b)
  """
  out = None
  #############################################################################
  # Done: Implement the affine forward pass. Store the result in out. You     #
  # will need to reshape the input into rows.                                 #
  #############################################################################
  x_row = np.reshape(x, (len(x), -1))
  out = np.dot(x_row, w) + b
  cache = (x, w, b)

  return out, cache

def affine_backward(dout, cache):
  """
  Computes the backward pass for an affine layer.

  Inputs:
  - dout: Upstream derivative, of shape (N, M)
  - cache: Tuple of:
    - x: Input data, of shape (N, d_1, ... d_k)
    - w: Weights, of shape (D, M)

  Returns a tuple of:
  - dx: Gradient with respect to x, of shape (N, d1, ..., d_k)
  - dw: Gradient with respect to w, of shape (D, M)
  - db: Gradient with respect to b, of shape (M,)
  """
  x, w, b = cache
  dx, dw, db = None, None, None
  #############################################################################
  # Done: Implement the affine backward pass.                                 #
  #############################################################################
  dx = np.dot(dout, w.T).reshape(x.shape)
  dw = np.dot(x.reshape(x.shape[0], np.prod(x.shape[1:])).T, dout)
  db = np.sum(dout, axis=0)

  return dx, dw, db

def relu_forward(x):
  """
  Computes the forward pass for a layer of rectified linear units (ReLUs).

  Input:
  - x: Inputs, of any shape

  Returns a tuple of:
  - out: Output, of the same shape as x
  - cache: x
  """
  out = None
  #############################################################################
  # Done: Implement the ReLU forward pass.                                    #
  #############################################################################
  out = np.maximum(0, x)
  cache = x

  return out, cache

def relu_backward(dout, cache):
  """
  Computes the backward pass for a layer of rectified linear units (ReLUs).

  Input:
  - dout: Upstream derivatives, of any shape
  - cache: Input x, of same shape as dout

  Returns:
  - dx: Gradient with respect to x
  """
  dx, x = None, cache
  #############################################################################
  # Done: Implement the ReLU backward pass.                                   #
  #############################################################################
  dx = dout * (x >= 0)

  return dx

def batchnorm_forward(x, gamma, beta, bn_param):
  """
  Forward pass for batch normalization.

  During training the sample mean and (uncorrected) sample variance are
  computed from minibatch statistics and used to normalize the incoming data.
  During training we also keep an exponentially decaying running mean of the mean
  and variance of each feature, and these averages are used to normalize data
  at test-time.

  At each timestep we update the running averages for mean and variance using
  an exponential decay based on the momentum parameter:

  running_mean = momentum * running_mean + (1 - momentum) * sample_mean
  running_var = momentum * running_var + (1 - momentum) * sample_var

  Note that the batch normalization paper suggests a different test-time
  behavior: they compute sample mean and variance for each feature using a
  large number of training images rather than using a running average. For
  this implementation we have chosen to use running averages instead since
  they do not require an additional estimation step; the torch7 implementation
  of batch normalization also uses running averages.

  Input:
  - x: Data of shape (N, D)
  - gamma: Scale parameter of shape (D,)
  - beta: Shift paremeter of shape (D,)
  - bn_param: Dictionary with the following keys:
    - mode: 'train' or 'test'; required
    - eps: Constant for numeric stability
    - momentum: Constant for running mean / variance.
    - running_mean: Array of shape (D,) giving running mean of features
    - running_var Array of shape (D,) giving running variance of features

  Returns a tuple of:
  - out: of shape (N, D)
  - cache: A tuple of values needed in the backward pass
  """
  mode = bn_param['mode']
  eps = bn_param.get('eps', 1e-5)
  momentum = bn_param.get('momentum', 0.9)

  N, D = x.shape
  running_mean = bn_param.get('running_mean', np.zeros(D, dtype=x.dtype))
  running_var = bn_param.get('running_var', np.zeros(D, dtype=x.dtype))

  out, cache = None, None
  if mode == 'train':
    #############################################################################
    # Done: Implement the training-time forward pass for batch normalization.   #
    # Use minibatch statistics to compute the mean and variance, use these      #
    # statistics to normalize the incoming data, and scale and shift the        #
    # normalized data using gamma and beta.                                     #
    #                                                                           #
    # You should store the output in the variable out. Any intermediates that   #
    # you need for the backward pass should be stored in the cache variable.    #
    #                                                                           #
    # You should also use your computed sample mean and variance together with  #
    # the momentum variable to update the running mean and running variance,    #
    # storing your result in the running_mean and running_var variables.        #
    #############################################################################

    # 1) compute mean
    mu = 1.0/N * np.sum(x, axis = 0)

    # 2) helper to compute the variance
    x_mu = x - mu

    # 3) helper to compute the variance
    x_mu_square = x_mu ** 2

    # 4) compute the variance
    var = 1.0/N * np.sum(x_mu_square, axis = 0)

    # 5) compute the square root of the variance
    sqrt_var = np.sqrt(var + eps)

    # 6) compute its inverse (easier to compute the derivatives this way)
    sqrt_var_inv = 1.0/sqrt_var

    # 7) do the normalization
    x_hat = x_mu * sqrt_var_inv

    # 8) do the scaling
    x_scaled = gamma * x_hat

    # 9) do the shifting (which is also the final output)
    out = x_scaled + beta

    # cache everything
    cache = (x_scaled, x_hat, sqrt_var_inv, sqrt_var, var, x_mu_square, \
        x_mu, mu, x, gamma, beta, bn_param)

    # compute the running mean and the running variance
    running_mean = momentum * running_mean + (1.0 - momentum) * mu
    running_var = momentum * running_var + (1.0 - momentum) * var

  elif mode == 'test':
    #############################################################################
    # Done: Implement the test-time forward pass for batch normalization. Use   #
    # the running mean and variance to normalize the incoming data, then scale  #
    # and shift the normalized data using gamma and beta. Store the result in   #
    # the out variable.                                                         #
    #############################################################################
    mu = running_mean
    var = running_var
    x_hat = (x - mu) / np.sqrt(var + eps)
    out = gamma * x_hat + beta
    cache = (mu, var, gamma, beta, bn_param)

  else:
    raise ValueError('Invalid forward batchnorm mode "%s"' % mode)

  # Store the updated running means back into bn_param
  bn_param['running_mean'] = running_mean
  bn_param['running_var'] = running_var

  return out, cache

def batchnorm_backward(dout, cache):
  """
  Backward pass for batch normalization.

  For this implementation, you should write out a computation graph for
  batch normalization on paper and propagate gradients backward through
  intermediate nodes.

  Inputs:
  - dout: Upstream derivatives, of shape (N, D)
  - cache: Variable of intermediates from batchnorm_forward.

  Returns a tuple of:
  - dx: Gradient with respect to inputs x, of shape (N, D)
  - dgamma: Gradient with respect to scale parameter gamma, of shape (D,)
  - dbeta: Gradient with respect to shift parameter beta, of shape (D,)
  """
  dx, dgamma, dbeta = None, None, None
  #############################################################################
  # Done: Implement the backward pass for batch normalization. Store the      #
  # results in the dx, dgamma, and dbeta variables.                           #
  #############################################################################
  x_scaled, x_hat, sqrt_var_inv, sqrt_var, var, x_mu_square, \
        x_mu, mu, x, gamma, beta, bn_param = cache

  N, D = dout.shape
  eps = bn_param.get('eps', 1e-5)

  # back-prop (9)
  dx_scaled = dout
  dbeta = np.sum(dout, axis = 0)

  # back-prop (8)
  dx_hat = gamma * dx_scaled
  dgamma = np.sum(x_hat * dx_scaled, axis = 0)

  # back-prop (7)
  dx_mu = sqrt_var_inv * dx_hat
  dsqrt_var_inv = np.sum(x_mu * dx_hat, axis = 0)

  # back-prop (6)
  dsqrt_var = -1.0/(sqrt_var ** 2) * dsqrt_var_inv

  # back-prop (5)
  dvar = 0.5 * (var + eps) ** (-0.5) * dsqrt_var

  # back-prop (4)
  dx_mu_square = 1.0/N * np.ones((x_mu_square.shape)) * dvar

  # back-prop (3)
  dx_mu += 2 * x_mu * dx_mu_square

  # back-prop (2)
  dx = dx_mu
  dmu = -np.sum(dx_mu, axis = 0)

  # back-prop (1)
  dx += 1.0/N * np.ones((dmu.shape)) * dmu
  #############################################################################
  #                             END OF YOUR CODE                              #
  #############################################################################

  return dx, dgamma, dbeta

def batchnorm_backward_alt(dout, cache):
  """
  Alternative backward pass for batch normalization.

  For this implementation you should work out the derivatives for the batch
  normalizaton backward pass on paper and simplify as much as possible. You
  should be able to derive a simple expression for the backward pass.

  Note: This implementation should expect to receive the same cache variable
  as batchnorm_backward, but might not use all of the values in the cache.

  Inputs / outputs: Same as batchnorm_backward
  """
  dx, dgamma, dbeta = None, None, None
  x_scaled, x_hat, sqrt_var_inv, sqrt_var, var, x_mu_square, \
        x_mu, mu, x, gamma, beta, bn_param = cache

  N, D = dout.shape
  eps = bn_param.get('eps', 1e-5)
  #############################################################################
  # Done: Implement the backward pass for batch normalization. Store the      #
  # results in the dx, dgamma, and dbeta variables.                           #
  #                                                                           #
  # After computing the gradient with respect to the centered inputs, you     #
  # should be able to compute gradients with respect to the inputs in a       #
  # single statement; our implementation fits on a single 80-character line.  #
  #############################################################################
  dbeta = np.sum(dout, axis=0)
  dgamma = np.sum((x - mu) * (var + eps)**(-1. / 2.) * dout, axis=0)
  dx = (1. / N) * gamma * (var + eps)**(-1. / 2.) * \
       (N * dout - np.sum(dout, axis=0) - (x - mu) * \
       (var + eps)**(-1.0) * np.sum(dout * (x - mu), axis=0))
  #############################################################################
  #                             END OF YOUR CODE                              #
  #############################################################################

  return dx, dgamma, dbeta

def dropout_forward(x, dropout_param):
  """
  Performs the forward pass for (inverted) dropout.

  Inputs:
  - x: Input data, of any shape
  - dropout_param: A dictionary with the following keys:
    - p: Dropout parameter. We drop each neuron output with probability p.
    - mode: 'test' or 'train'. If the mode is train, then perform dropout;
      if the mode is test, then just return the input.
    - seed: Seed for the random number generator. Passing seed makes this
      function deterministic, which is needed for gradient checking but not in
      real networks.

  Outputs:
  - out: Array of the same shape as x.
  - cache: A tuple (dropout_param, mask). In training mode, mask is the dropout
    mask that was used to multiply the input; in test mode, mask is None.
  """
  p, mode = dropout_param['p'], dropout_param['mode']
  if 'seed' in dropout_param:
    np.random.seed(dropout_param['seed'])

  mask = None
  out = None

  if mode == 'train':
    ###########################################################################
    # Done: Implement the training phase forward pass for inverted dropout.   #
    # Store the dropout mask in the mask variable.                            #
    ###########################################################################
    mask = (np.random.rand(*x.shape) < p) / p
    out = x * mask

  elif mode == 'test':
    ###########################################################################
    # Done: Implement the test phase forward pass for inverted dropout.       #
    ###########################################################################
    out = x

  cache = (dropout_param, mask)
  out = out.astype(x.dtype, copy=False)

  return out, cache

def dropout_backward(dout, cache):
  """
  Perform the backward pass for (inverted) dropout.

  Inputs:
  - dout: Upstream derivatives, of any shape
  - cache: (dropout_param, mask) from dropout_forward.
  """
  dropout_param, mask = cache
  mode = dropout_param['mode']

  dx = None
  if mode == 'train':
    ###########################################################################
    # Done: Implement the training phase backward pass for inverted dropout.  #
    ###########################################################################
    dx = dout * mask

  elif mode == 'test':
    dx = dout
  return dx

def conv_forward_naive(x, w, b, conv_param):
  """
  A naive implementation of the forward pass for a convolutional layer.

  The input consists of N data points, each with C channels, height H and width
  W. We convolve each input with F different filters, where each filter spans
  all C channels and has height HH and width HH.

  Input:
  - x: Input data of shape (N, C, H, W)
  - w: Filter weights of shape (F, C, HH, WW)
  - b: Biases, of shape (F,)
  - conv_param: A dictionary with the following keys:
    - 'stride': The number of pixels between adjacent receptive fields in the
      horizontal and vertical directions.
    - 'pad': The number of pixels that will be used to zero-pad the input.

  Returns a tuple of:
  - out: Output data, of shape (N, F, H', W') where H' and W' are given by
    H' = 1 + (H + 2 * pad - HH) / stride
    W' = 1 + (W + 2 * pad - WW) / stride
  - cache: (x, w, b, conv_param)
  """
  out = None
  #############################################################################
  # TODO: Implement the convolutional forward pass.                           #
  # Hint: you can use the function np.pad for padding.                        #
  #############################################################################

  # extract the dimensions of the data structures
  N, C, H, W = x.shape
  F, C, HH, WW = w.shape
  stride = conv_param['stride']
  pad = conv_param['pad']

  # add padding
  x_pad = np.pad(x, [(0,0), (0,0), (pad,pad), (pad,pad)], 'constant')

  # calculate the dimensions for the next layer
  H_prime = 1 + (H + 2 * pad - HH)/stride
  W_prime = 1 + (W + 2 * pad - WW)/stride

  # build the new data structure which will contain the info after
  # convolution happens
  out = np.zeros((N, F, H_prime, W_prime))

  # looooooop :S
  for i in range(N): #iterate over all the images
    for j in range(F): # iterate over all filters
      for k in range(H_prime): # iterate over all possibilities in height
        # see how much we have to jump each time in horizontal direction
        window_horizontal = k * stride
        for l in range(W_prime): # iterate over all possibilities in width
          # see how much we have to jump each time in vertical direction
          window_vertical = l * stride
          # build the window
          window = x_pad[i, :, window_horizontal:window_horizontal + HH, \
            window_vertical:window_vertical + WW]
          # do the sot product
          out[i, j, k, l] = np.sum(window * w[j]) + b[j]

  cache = (x, w, b, conv_param)
  return out, cache

def conv_backward_naive(dout, cache):
  """
  A naive implementation of the backward pass for a convolutional layer.

  Inputs:
  - dout: Upstream derivatives.
  - cache: A tuple of (x, w, b, conv_param) as in conv_forward_naive

  Returns a tuple of:
  - dx: Gradient with respect to x
  - dw: Gradient with respect to w
  - db: Gradient with respect to b
  """
  dx, dw, db = None, None, None
  x, w, b, conv_param = cache # extract the data structures

  # extract the dimensions of the data structures and initialize the arrays
  # which will contain the derivatives of them
  N, C, H, W = x.shape
  dx = np.zeros((N, C, H, W))
  F, C, HH, WW = w.shape
  dw = np.zeros((F, C, HH, WW))
  db = np.zeros(F)
  stride = conv_param['stride']
  pad = conv_param['pad']

  # calculate the dimensions for the next layer
  H_prime = 1 + (H + 2 * pad - HH)/stride
  W_prime = 1 + (W + 2 * pad - WW)/stride

  # do the padding for both x and dx
  x_pad = np.pad(x, [(0,0), (0,0), (pad,pad), (pad,pad)], 'constant')
  dx_pad = np.pad(dx, [(0,0), (0,0), (pad,pad), (pad,pad)], 'constant')

  # backprop and loop a lot
  for i in xrange(N): #iterate over all the images
    for j in xrange(F): # iterate over all filters
      for k in xrange(H_prime): # iterate over all possibilities in height
        # see how much we have to jump each time in horizontal direction
        window_horizontal = k * stride
        for l in xrange(W_prime): # iterate over all possibilities in width
          # see how much we have to jump each time in vertical direction
          window_vertical = l * stride
          # build the window
          window = x_pad[i, :, window_horizontal:window_horizontal + HH, \
            window_vertical:window_vertical + WW]
          # compute the gradients
          db[j] += dout[i, j, k, l]
          dw[j] += window * dout[i, j, k, l]
          dx_pad[i, :, window_horizontal:window_horizontal + HH, \
            window_vertical:window_vertical + WW] += w[j] * dout[i, j, k, l]

  # unpad
  dx = dx_pad[:, :, pad:pad+H, pad:pad+W]

  return dx, dw, db

def max_pool_forward_naive(x, pool_param):
  """
  A naive implementation of the forward pass for a max pooling layer.

  Inputs:
  - x: Input data, of shape (N, C, H, W)
  - pool_param: dictionary with the following keys:
    - 'pool_height': The height of each pooling region
    - 'pool_width': The width of each pooling region
    - 'stride': The distance between adjacent pooling regions

  Returns a tuple of:
  - out: Output data
  - cache: (x, pool_param)
  """

  # extract the dimensions of the data structures
  N, C, H, W = x.shape
  HH = pool_param['pool_height']
  WW = pool_param['pool_width']
  stride = pool_param['stride']

  # calculate the dimensions for the next layer
  H_prime = 1 + (H - HH)/stride
  W_prime = 1 + (W - WW)/stride

  # build the new data structure which will contain the info after
  # convolution happens
  out = np.zeros((N, C, H_prime, W_prime))

  # loooooooop
  for i in xrange(N): # iterate over each image
    for j in xrange(C): # iterate ober each channel
      for k in range(H_prime): # iterate over all possibilities in height
        # see how much we have to jump each time in horizontal direction
        window_horizontal = k * stride
        for l in range(W_prime): # iterate over all possibilities in width
          # see how much we have to jump each time in vertical direction
          window_vertical = l * stride
          # build the window
          window = x[i, j, window_horizontal:window_horizontal + HH, \
            window_vertical:window_vertical + WW]
          out[i, j, k, l] = np.max(window)

  cache = (x, pool_param)
  return out, cache

def max_pool_backward_naive(dout, cache):
  """
  A naive implementation of the backward pass for a max pooling layer.

  Inputs:
  - dout: Upstream derivatives
  - cache: A tuple of (x, pool_param) as in the forward pass.

  Returns:
  - dx: Gradient with respect to x
  """
  dx = None
  x, pool_param = cache # extract the data structures

  # extract the dimensions of the data structures and initialize the arrays
  # which will contain the derivatives of them
  N, C, H, W = x.shape
  HH = pool_param['pool_height']
  WW = pool_param['pool_width']
  stride = pool_param['stride']

  # calculate the dimensions for the next layer
  H_prime = 1 + (H - HH)/stride
  W_prime = 1 + (W - WW)/stride

  dx = np.zeros((N, C, H, W))

  # loooooooop
  for i in xrange(N): # iterate over each image
    for j in xrange(C): # iterate ober each channel
      for k in range(H_prime): # iterate over all possibilities in height
        # see how much we have to jump each time in horizontal direction
        window_horizontal = k * stride
        for l in range(W_prime): # iterate over all possibilities in width
          # see how much we have to jump each time in vertical direction
          window_vertical = l * stride
          # build the window
          window = x[i, j, window_horizontal:window_horizontal + HH, \
            window_vertical:window_vertical + WW]
          # get the maximum value in the window
          m = np.max(window)
          # compute the derivative (all values which are equal to max multiply by the previous derivatives)
          dx[i, j, window_horizontal:window_horizontal + HH, \
            window_vertical:window_vertical + WW] += (window == m) * dout[i, j, k, l]

  return dx

def spatial_batchnorm_forward(x, gamma, beta, bn_param):
  """
  Computes the forward pass for spatial batch normalization.

  Inputs:
  - x: Input data of shape (N, C, H, W)
  - gamma: Scale parameter, of shape (C,)
  - beta: Shift parameter, of shape (C,)
  - bn_param: Dictionary with the following keys:
    - mode: 'train' or 'test'; required
    - eps: Constant for numeric stability
    - momentum: Constant for running mean / variance. momentum=0 means that
      old information is discarded completely at every time step, while
      momentum=1 means that new information is never incorporated. The
      default of momentum=0.9 should work well in most situations.
    - running_mean: Array of shape (D,) giving running mean of features
    - running_var Array of shape (D,) giving running variance of features

  Returns a tuple of:
  - out: Output data, of shape (N, C, H, W)
  - cache: Values needed for the backward pass
  """
  N, C, H, W = x.shape
  mode = bn_param['mode']
  eps = bn_param.get('eps', 1e-5)
  momentum = bn_param.get('momentum', 0.9)

  running_mean = bn_param.get('running_mean', np.zeros(C, dtype=x.dtype))
  running_var = bn_param.get('running_var', np.zeros(C, dtype=x.dtype))

  if mode == 'train':
  # Step 1 , calcul the average for each channel
    mu = (1. / (N * H * W) * np.sum(x, axis=(0, 2, 3))).reshape(1, C, 1, 1)
    var = (1. / (N * H * W) * np.sum((x - mu)**2,
                                         axis=(0, 2, 3))).reshape(1, C, 1, 1)
    xhat = (x - mu) / (np.sqrt(eps + var))
    out = gamma.reshape(1, C, 1, 1) * xhat + beta.reshape(1, C, 1, 1)

    running_mean = momentum * running_mean + (1.0 - momentum) * np.squeeze(mu)
    running_var = momentum * running_var + (1.0 - momentum) * np.squeeze(var)

    cache = (mu, var, x, xhat, gamma, beta, bn_param)

    # Store the updated running means back into bn_param
    bn_param['running_mean'] = running_mean
    bn_param['running_var'] = running_var

  elif mode == 'test':
    mu = running_mean.reshape(1, C, 1, 1)
    var = running_var.reshape(1, C, 1, 1)

    xhat = (x - mu) / (np.sqrt(eps + var))
    out = gamma.reshape(1, C, 1, 1) * xhat + beta.reshape(1, C, 1, 1)
    cache = (mu, var, x, xhat, gamma, beta, bn_param)

  else:
    raise ValueError('Invalid forward batchnorm mode "%s"' % mode)

  return out, cache

def spatial_batchnorm_backward(dout, cache):
  """
  Computes the backward pass for spatial batch normalization.
  Inputs:
  - dout: Upstream derivatives, of shape (N, C, H, W)
  - cache: Values from the forward pass
  Returns a tuple of:
  - dx: Gradient with respect to inputs, of shape (N, C, H, W)
  - dgamma: Gradient with respect to scale parameter, of shape (C,)
  - dbeta: Gradient with respect to shift parameter, of shape (C,)
  """
  dx, dgamma, dbeta = None, None, None

  mu, var, x, xhat, gamma, beta, bn_param = cache
  N, C, H, W = x.shape
  mode = bn_param['mode']
  eps = bn_param.get('eps', 1e-5)

  gamma = gamma.reshape(1, C, 1, 1)
  beta = beta.reshape(1, C, 1, 1)

  dbeta = np.sum(dout, axis=(0, 2, 3))
  dgamma = np.sum(dout * xhat, axis=(0, 2, 3))

  Nt = N * H * W
  dx = (1. / Nt) * gamma * (var + eps)**(-1. / 2.) * (
        Nt * dout - np.sum(dout, axis=(0, 2, 3)).reshape(1, C, 1, 1)
        - (x - mu) * (var + eps)**(-1.0) * np.sum(dout * (x - mu), axis=(0, 2, 3)).reshape(1, C, 1, 1))

  return dx, dgamma, dbeta

def svm_loss(x, y):
  """
  Computes the loss and gradient using for multiclass SVM classification.

  Inputs:
  - x: Input data, of shape (N, C) where x[i, j] is the score for the jth class
    for the ith input.
  - y: Vector of labels, of shape (N,) where y[i] is the label for x[i] and
    0 <= y[i] < C

  Returns a tuple of:
  - loss: Scalar giving the loss
  - dx: Gradient of the loss with respect to x
  """
  N = x.shape[0]
  correct_class_scores = x[np.arange(N), y]
  margins = np.maximum(0, x - correct_class_scores[:, np.newaxis] + 1.0)
  margins[np.arange(N), y] = 0
  loss = np.sum(margins) / N
  num_pos = np.sum(margins > 0, axis=1)
  dx = np.zeros_like(x)
  dx[margins > 0] = 1
  dx[np.arange(N), y] -= num_pos
  dx /= N
  return loss, dx

def softmax_loss(x, y):
  """
  Computes the loss and gradient for softmax classification.

  Inputs:
  - x: Input data, of shape (N, C) where x[i, j] is the score for the jth class
    for the ith input.
  - y: Vector of labels, of shape (N,) where y[i] is the label for x[i] and
    0 <= y[i] < C

  Returns a tuple of:
  - loss: Scalar giving the loss
  - dx: Gradient of the loss with respect to x
  """
  probs = np.exp(x - np.max(x, axis=1, keepdims=True))
  probs /= np.sum(probs, axis=1, keepdims=True)
  N = x.shape[0]
  loss = -np.sum(np.log(probs[np.arange(N), y])) / N
  dx = probs.copy()
  dx[np.arange(N), y] -= 1
  dx /= N
  return loss, dx


import numpy as np

# data array, Num_Item x Num_Dim
data_m = np.array([[-1, -2], [-1, 0], [0, 0], [2, 1], [0, 1]])

item_num = data_m.shape[0]

# mean data_arr
mean = np.mean(data_m, axis=0)
data_m = data_m - np.mean(data_m, axis=0)

# covariance matrix
covar_m = np.matmul(data_m.T, data_m) / item_num

# eigen value & vector
values, vectors = np.linalg.eig(covar_m)
print(vectors)
print(values)

pca_base = vectors.T
print(np.matmul(np.matmul(pca_base, covar_m), pca_base.T))

test = np.array([[-1, -2]])
coefficent = np.matmul(test - mean, vectors)
print('co:\n', coefficent)
print('base:\n', vectors)
print(np.matmul(coefficent, vectors.T) + mean)
print(mean)


import math
import numpy as np

def calculate_distance_matrix(coordinates):
    number_of_lines = len(coordinates)
    distances = np.zeros((number_of_lines, number_of_lines))

    for i in range(number_of_lines):
        x1,y1 = coordinates[i]
        for j in range(i, number_of_lines):
            x2,y2 = coordinates[j]
            distances[i][j] = distances[j][i] = math.sqrt( (x1-x2)**2 + (y1-y2)**2 )

    return distances


# idexação de arrays
'''
Relembrando :
Os índices ao longo de uma dimensão variam de 0 an-1,
onde o n é o numero de elementos da dimensão.

'''
# os indices no python vão de 0 a -n,
# on n é o tamanho da dimensão.
import numpy as np

x = np.linspace(start=10, stop=100, num=10) # vai pegar 10 numeros aleatorioas entre 10 e 100.
print('\nx: ', x)
print('shape: ', x.shape)

# extração de elementos
print('\n***EXTRAÇÃO DE ELEMENTOS***\n')
print('x: ', x)
print("primeiro elemento: ",x[0])
print('Segundo elemento: ',x[1])
print("penultimo elemento: ",x[-2])
print("ultimo elemento: ",x[-1])

# slicing: extração de subarrays:
print('\n***SLICING: EXTRAÇÃO DE SUBARRAYS***\n')
print("x: ",x)
print("dois primeiros elementos: ", x[0:2])
print("dois primeiros elementos: ", x[:2])
print("dois ultimos elementos: ", x[-2:])

# slicing em arrays 2D(matrizes)
print('\n***SLICING EM ARRAYS 2D***\n')
x = x.reshape(2, 5)  # reshape de x para 2 linhas e 5 colunas
print("x:\n", x)

# extração de elementos (matriz)
# vai acessar os elementos criados no array 2d
print('\n***EXTRAÇÃO DE ELEMENTOS***\n')
print('primeira linha , segunda coluna: ', x[0, 1])
print('segunda linha, penultima coluna: ', x[1, -2])
print('ultima linha, ultima coluna: ', x[1, 4])
print('ultima linha, ultima coluna: ',x[-1, -1])

# slicing: extração de subarrays em 2d
print('\n***SLICING EXTRAÇÃO DE SUBARRAYS EM 2D***\n')
print("x: \n",x)
print('\nprimeira linha inteira: ',x[0, :])
print('primeira linha, segunda a quarta coluna: ',x[0, 1:4])
print('ultima coluna inteira:\n ', x[:, [-1]])

# atenção com compartilhamento de memória em subarrays!!
x = np.array([1, 2, 3])
print("x antes: ", x)
y = x[:2]
y[0] = -100 # aletração do valor em y altera o valor de x
print('depois: ',x)

# atenção com compartilhamento de memória em subarrays!!
x = np.array([1, 2, 3])
print("x antes: ", x)
y = x[:2].copy()
y[0] = -100 # aletração do valor em y altera o valor de x
print('depois: ',x)


from sympy import symbols, oo, sympify, Heaviside, sqrt, acos, cos, pi
x = symbols('x')

def U(a, side):
    '''The unit function: 0 when x<a and 1 when x>a.
    The value at x=a is determined by the 'side' argument.
        side = 'left'   -> 0 at x=a
        side = 'right'  -> 1 at x=a
    '''
    # Using the definition of Heaviside where it's 1 at x=0
    # uncomment these if you want it when it's 0 at x=0
    # H0 = Heaviside(x, 0)    # heaviside, 0 at x=0
    # H1 = 1-H0.subs(x,-x)    # heaviside, 1 at x=0
    H1 = Heaviside(x, 1)
    H0 = 1-H1.subs(x,-x)
    if side == 'left':
        return H0.subs(x, x-a)
    elif side == 'right':
        return H1.subs(x, x-a)
    else:
        raise ValueError("side was not left or right (%s)" % (side))

def Boxcar(x1, x2, left=True, right=True):
    '''The boxcar function: 1 when x1 < x < x2, 0 otherwise.
    x1 and x2 can also be set to -oo and oo respectively for no boundaries.

    The 'left' and 'right' arguments determine whether the endpoints
    are included in the region or not.
    (default: the left and right endpoints are included)
    '''
    assert x1 <= x2

    if x1 == -oo:   fleft = 1
    else:   fleft = U(x1, 'right' if left else 'left')
    if x2 == oo:    fright = 0
    else:   fright = U(x2, 'left' if right else 'right')

    return fleft - fright

def Chop(expr, x1, x2, left=True, right=True):
    '''
            { 0         if x < x1
    f(x) =  { expr(x)   if x1 <= x <= x2
            { 0         if x > x2

    x1,x2 can also be set to -oo and oo respectively, to have
    the function go off to infinity instead.
    '''
    return expr * Boxcar(x1, x2, left, right)

def Undefined(x1, x2, c=0):
    '''Return a function which is undefined within (x1, x2) and c otherwise'''
    if x1 == -oo and x2 == oo:
        x_function = sqrt(-1)
    elif x1 == -oo:
        x_function = sqrt(x-x2)
    elif x2 == oo:
        x_function = sqrt(-(x-x1))
    else:
        x_function = sqrt((x-x1)*(x-x2))

    return Chop(x_function, x1, x2) + c

def Oscillate(f, src, dest):
    '''
    Create a function which oscillates from f(x0) to f(x1), from f(x1) to f(x0), ...
    src = (x0, x1) is the slice of the function to oscillate over
    dest = (x2, x3) is where the slice from (x0, x1) gets mapped in the returned function
    '''
    #create a function which oscillates between [0,1] over [x0,x1]
    x0,x1 = src
    x2,x3 = dest
    x_function = (x-x2)/(x3-x2)
    # use acos here because the math lines up nicer
    oscillator = acos(cos(x_function*pi))/pi   #[x2,x3] -> [0,1]
    return f.subs(x, oscillator*(x1-x0) + x0)  #[x2,x3] -> [0,1] -> [x0,x1]

# ----------------------------------
#  Piecewise function generators
# ----------------------------------
def Piecewise(parts):
    '''
    parts is a list of tuples in the format
        (func1, left x bound, right x bound),
        (func2, left x bound, right x bound),
        etc ...

    Assumes that the piecewise function is continuous
    (each part connects with each other).
    Might do funny things if they're not.
    '''
    return sum(map(lambda x: Chop(*x), parts))

def Glued(parts, start_point=(0,0), left=None, right=None):
    '''
    Cuts out regions from given functions and glues them together
    to form a continuous piecewise function.

    Returns a list of function parts you can pass into Piecewise().

    parts - a list of function regions, in the format [(f, x1, x2), ...]
    start_point - an (x,y) coordinate specifying where the first function part should start
    left - a function region to use off to -oo (e.g. [x**2, -oo, 0])
    right - a function region to use off to oo (e.g. [x**2, 0, oo])
    '''

    def translate(f, src, dest):
        '''translate f so that the point `src` moves to the point `dest`'''
        dx = dest[0]-src[0]
        dy = dest[1]-src[1]
        return f.subs(x, x-dx) + dy

    new_parts = []
    if left:
        f,x1,x2 = left
        assert x1 == -oo, "left bound of `left` argument must be -oo"
        moved_f = translate(f, (x2, f.subs(x, x2)), start_point)
        new_parts.append((moved_f, -oo, start_point[0]))

    for f,x1,x2 in parts:
        length = x2 - x1
        delta = f.subs(x, x2) - f.subs(x, x1)
        moved_f = translate(f, (x1, f.subs(x, x1)), start_point)
        new_parts.append((moved_f, start_point[0], start_point[0]+length))
        start_point = (start_point[0]+length, start_point[1]+delta)

    if right:
        f,x1,x2 = right
        assert x2 == oo, "right bound of `right` argument must be oo"
        moved_f = translate(f, (x1, f.subs(x, x1)), start_point)
        new_parts.append((moved_f, start_point[0], oo))

    return new_parts

def Glue(*args, **kwargs):
    '''
    Cuts out regions from given functions and glues them together
    to form a continuous piecewise function.
    Alias for Piecewise(Glued([arguments])).
    '''
    return Piecewise(Glued(*args, **kwargs))


#! /usr/bin/env python3
import numpy as np

def sigmoid_(x):
    return 1 / (1 + np.exp(-x))

if __name__ == "__main__":
    x = -4
    print(sigmoid_(x))
    x = 2
    print(sigmoid_(x))
    x = np.array([-4, 2, 0])
    print(sigmoid_(x))


import numpy as np
import sys
sys.path.append('../../software/models/')
from scipy.fftpack import fft
import matplotlib.pyplot as plt
from scipy.signal import get_window
from dftModel import dftAnal
"""
A3-part-5: FFT size and zero-padding (Optional)

Write a function that takes in an input signal, computes three different FFTs on the input and returns
the first 80 samples of the positive half of the FFT magnitude spectrum (in dB) in each case.

This part is a walk-through example to provide some insights into the effects of the length of signal
segment, the FFT size, and zero-padding on the FFT of a sinusoid. The input to the function is x, which
is 512 samples of a real sinusoid of frequency 110 Hz and the sampling frequency fs = 1000 Hz. You will
first extract the first 256 samples of the input signal and store it as a separate variable xseg. You
will then generate two 'hamming' windows w1 and w2 of size 256 and 512 samples, respectively (code given
below). The windows are used to smooth the input signal. Use dftAnal to obtain the positive half of the
FFT magnitude spectrum (in dB) for the following cases:
Case-1: Input signal xseg (256 samples), window w1 (256 samples), and FFT size of 256
Case-2: Input signal x (512 samples), window w2 (512 samples), and FFT size of 512
Case-3: Input signal xseg (256 samples), window w1 (256 samples), and FFT size of 512 (Implicitly does a
        zero-padding of xseg by 256 samples)
Return the first 80 samples of the positive half of the FFT magnitude spectrum output by dftAnal.

To understand better, plot the output of dftAnal for each case on a common frequency axis. Let mX1, mX2,
mX3 represent the outputs of dftAnal in each of the Cases 1, 2, and 3 respectively. You will see that
mX3 is the interpolated version of mX1 (zero-padding leads to interpolation of the DFT). You will also
observe that the 'mainlobe' of the magnitude spectrum in mX2 will be much smaller than that in mX1 and
mX3. This shows that choosing a longer segment of signal for analysis leads to a narrower mainlobe with
better frequency resolution and less spreading of the energy of the sinusoid.

If we were to estimate the frequency of the sinusoid using its DFT, a first principles approach is to
choose the frequency value of the bin corresponding to the maximum in the DFT magnitude spectrum.
Some food for thought: if you were to take this approach, which of the Cases 1, 2, or 3 will give you
a better estimate of the frequency of the sinusoid ? Comment and discuss on the forums!

Test case 1: The input signal is x (of length 512 samples), the output is a tuple with three elements:
(mX1_80, mX2_80, mX3_80) where mX1_80, mX2_80, mX3_80 are the first 80 samples of the magnitude spectrum
output by dftAnal in cases 1, 2, and 3, respectively.

"""
def zpFFTsizeExpt(x, fs):
    """
    Inputs:
        x (numpy array) = input signal (2*M = 512 samples long)
        fs (float) = sampling frequency in Hz
    Output:
        The function should return a tuple (mX1_80, mX2_80, mX3_80)
        mX1_80 (numpy array): The first 80 samples of the magnitude spectrum output of dftAnal for Case-1
        mX2_80 (numpy array): The first 80 samples of the magnitude spectrum output of dftAnal for Case-2
        mX3_80 (numpy array): The first 80 samples of the magnitude spectrum output of dftAnal for Case-3

    The first few lines of the code to generate xseg and the windows have been written for you,
    please use it and do not modify it.
    """

    M = len(x)/2
    xseg = x[:M]
    w1 = get_window('hamming',M)
    w2 = get_window('hamming',2*M)
    ## Your code here


# https://www.cnblogs.com/ssyfj/p/12913015.html

# https://www.jianshu.com/p/42b0b6ffcf97

import numpy as np
# 1-D array
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print('result1 = a*b', a*b)
print('result2 = a@b' ,a@b)
# result1 = a@b [ 4 10 18]
# result2 = b@a 32

a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([[1, 2], [3, 4], [5, 6]])
print("result3 = a@b",a@b)
# result3 = a@b [[22 28]
#   [49 64]]

a = np.array([1, 2, 3, 4, 5, 6])
b = np.array([1, 2, 3,4])

outer_result_1 =  np.outer(a,b)
print('outer_result_1:\n %s' %(outer_result_1))
# outer_result_1:
#  [[ 1  2  3  4]
#  [ 2  4  6  8]
#  [ 3  6  9 12]
#  [ 4  8 12 16]
#  [ 5 10 15 20]
#  [ 6 12 18 24]]

a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([[1, 2], [3,4]])

outer_result_2 =  np.outer(a,b)
print('outer_result_2:\n %s' %(outer_result_2))
# outer_result_2:
#  [[ 1  2  3  4]
#  [ 2  4  6  8]
#  [ 3  6  9 12]
#  [ 4  8 12 16]
#  [ 5 10 15 20]
#  [ 6 12 18 24]]


import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

# Pomocna funkcija koja od niza trening primera pravi feature matricu (m X n).
def create_feature_matrix(x, nb_features):
	tmp_features = []
	for deg in range(1, nb_features + 1):
		tmp_features.append(np.power(x, deg))
	return np.column_stack(tmp_features)

def polynomial_regression(input_data, nb_samples, nb_features, color):
	# Restartuj graf
	tf.reset_default_graph()

	# Kreiranje feature matrice.
	data = input_data.copy()
	data['x'] = create_feature_matrix(data['x'], nb_features)

	# Korak 2: Model.
	X = tf.placeholder(shape=(None, nb_features), dtype=tf.float32)
	Y = tf.placeholder(shape=None, dtype=tf.float32)
	w = tf.Variable(tf.zeros(nb_features))
	bias = tf.Variable(0.0)

	w_col = tf.reshape(w, (nb_features, 1))
	hyp = tf.add(tf.matmul(X, w_col), bias)

	# Korak 3: Funkcija troška i optimizacija.
	Y_col = tf.reshape(Y, (-1, 1))
	loss = tf.reduce_mean(tf.square(hyp - Y_col))

	# Prelazimo na AdamOptimizer jer se prost GradientDescent lose snalazi sa
	# slozenijim funkcijama.
	opt_op = tf.train.AdamOptimizer().minimize(loss)

	# Korak 4: Trening.
	with tf.Session() as sess:
		sess.run(tf.global_variables_initializer())

		# Izvršavamo 200 epoha treninga.
		nb_epochs = 200
		for epoch in range(nb_epochs):

			# Stochastic Gradient Descent.
			epoch_loss = 0
			for sample in range(nb_samples):
				feed = {X: data['x'][sample].reshape((1, nb_features)),
						Y: data['y'][sample]}
				_, curr_loss = sess.run([opt_op, loss], feed_dict=feed)
				epoch_loss += curr_loss

			# U svakoj desetoj epohi ispisujemo prosečan loss.
			epoch_loss /= nb_samples
			if (epoch + 1) % 10 == 0:
				print('Epoch: {}/{} | Avg loss: {:.5f}'.format(epoch + 1, nb_epochs, epoch_loss))

		# Ispisujemo i plotujemo finalnu vrednost parametara.
		w_val = sess.run(w)
		bias_val = sess.run(bias)
		print('w = ', w_val, 'bias = ', bias_val)
		xs = create_feature_matrix(np.linspace(-2, 4, 100), nb_features)
		hyp_val = sess.run(hyp, feed_dict={X: xs})  # Bez Y jer nije potrebno.
		plt.plot(xs[:, 0].tolist(), hyp_val.tolist(), color=color)
		plt.xlim([-2, 2])
		plt.ylim([-3, 4])

		return sess.run(loss, feed_dict={X: data['x'], Y: data['y']})

def main():
	# Izbegavamo scientific notaciju i zaokruzujemo na 5 decimala.
	np.set_printoptions(suppress=True, precision=5)

	# Korak 1: Učitavanje i obrada podataka.
	filename = 'data/funky.csv'
	all_data = np.loadtxt(filename, delimiter=',', skiprows=0, usecols=(0, 1))
	data = dict()
	data['x'] = all_data[:, 0]
	data['y'] = all_data[:, 1]

	# Nasumično mešanje.
	nb_samples = data['x'].shape[0]
	indices = np.random.permutation(nb_samples)
	data['x'] = data['x'][indices]
	data['y'] = data['y'][indices]

	# Normalizacija (obratiti pažnju na axis=0).
	data['x'] = (data['x'] - np.mean(data['x'], axis=0)) / np.std(data['x'], axis=0)
	data['y'] = (data['y'] - np.mean(data['y'])) / np.std(data['y'])

	# Iscrtavanje podataka
	plt.scatter(data['x'], data['y'])
	plt.xlabel('X value')
	plt.ylabel('Y value')

	colors = ['g', 'm', 'b', 'r', 'k', 'y']
	losses = []

	for degree in range(1, 7):
		print('Polynomial regression for degree = ', degree)
		loss = polynomial_regression(data, nb_samples, degree, colors[degree - 1])
		losses.append(loss)
		print('loss = {:.5f}'.format(loss))
		print('----------------------------')

	plt.show()

	plt.plot(range(1, 7), losses, color='g')
	plt.xlabel('Degree')
	plt.ylabel('Loss')
	plt.show()

if __name__ == '__main__':
	main()

# Mozemo primetiti da polinomi stepena 3, 4, 5 i 6 dobro aproksimiraju skup podataka i imaju loss blizu 0.1.
# Polinom stepena 3 ima najmanji loss (jedini ispod 0.11).

# Polinomi stepena 1 i 2 su losija aproksimacija i imaju loss oko 0.3
# Polinom stepena 1 definise pravu koja nije dobra aproksimacija podataka.


import numpy as np

def max_average(data, window):
    '''
    args:
        data (1d array): An array to be averaged.
        window (int): Number of frames over thich the average
        is computed.
    return:
        The maximum averaged value over <window> size window.
    '''
    if window > len(data):
        raise Exception('Window length is bigger than array')
    window = int(window)
    max_mean = 0
    for i in range(len(data) - window + 1):
        mean = sum(data[i:i + window]) * 1.0 / window
        if mean > max_mean:
            max_mean = mean
    return max_mean

def find_intersections(x1, x2, tolerance):
    '''
    Find the indices on which the difference between two variables are
    smaller than a tolerance.

    Args:
        x1, x2 (numpy array): Two curves.
    Return:
        Indices (int) of intersections.

    '''
    mask = abs(x1 - x2) < tolerance
    return np.argwhere(np.diff(mask) != 0).reshape(-1)

def expansions(lpos, fpos, lvel, fvel, relative, w):
    '''
    return the rate of (relative) optical expansion or contraction
    of the leader in the eye of the follower

    Args:
        lpos, fpos (2-d np array of float): Leader and follower positions
        in meter.
        lvel, fvel (2-d np array of float): Leader and follower velocities
        in meter / second.
        relative (boolean): Whether compute relative rate of expansion.
        w (float): The size of the leader in meters. Height or width.
    Return:
        e (1-d np array of float): An array of optical expansion.
    '''
    axis_val = 1 if len(lpos.shape) == 2 else None
    dspd = np.linalg.norm(fvel - lvel, axis=axis_val)
    dist = np.linalg.norm(lpos - fpos, axis=axis_val)
    e = w * dspd / (dist ** 2 + w ** 2 / 4)
    if relative:
        e /= 2 * np.arctan(w / (2 * dist))
    return e

def running_average(data):
    '''
    Args:
        data (2-d np array of float): With size (steps, number of variables).
    Return:
        Array(s) of float representing the running average of all variables
        in data.
    '''
    if len(data.shape) == 2:
        ns = np.expand_dims(np.arange(1, len(data) + 1), axis=1)
    elif len(data.shape) == 1:
        ns = np.arange(1, len(data) + 1)
    else:
        raise Exception('Data dimension larger than 2')
    return np.cumsum(data, axis=0) / ns

def time_to_contact(lpos, fpos, lspd, fspd):
    '''
    Calculate the time to contact in 1-d case, assuming constant speed.

    Args:
        lpos, fpos (1-d np array of float): Leader and follower positions
        in meter.
        lspd, fspd (1-d np array of float): Leader and follower speeds
        in meter / second.
    Return:
        1-d np array of float, representing array of float Time to contact
        at each moment.
    '''
    return (lpos - fpos) / (fspd - lspd)


## load dependency - third party
import numpy as np

## calculate box plot statistics
def box_plot_stats(

    ## arguments / inputs
    x,          ## input array of values
    coef = 1.5  ## positive real number
                ## (determines how far the whiskers extend from the iqr)
    ):

    """
    calculates box plot five-number summary: the lower whisker extreme, the
    lower ‘hinge’ (observed value), the median, the upper ‘hinge’, and upper
    whisker extreme (observed value)

    returns a results dictionary containing 2 items: "stats" and "xtrms"
    1) the "stats" item contains the box plot five-number summary as an array
    2) the "xtrms" item contains values which lie beyond the box plot extremes

    functions much the same as R's 'boxplot.stats()' function for which this
    Python implementation was predicated

    ref:

    The R Project for Statistical Computing. (2019). Box Plot Statistics.
    http://finzi.psych.upenn.edu/R/library/grDevices/html/boxplot.stats.html.

    Tukey, J. W. (1977). Exploratory Data Analysis. Section 2C.

    McGill, R., Tukey, J.W. and Larsen, W.A. (1978). Variations of Box Plots.
    The American Statistician, 32:12-16. http://dx.doi.org/10.2307/2683468.

    Velleman, P.F. and Hoaglin, D.C. (1981). Applications, Basics and
    Computing of Exploratory Data Analysis. Duxbury Press.

    Emerson, J.D. and Strenio, J. (1983). Boxplots and Batch Comparison.
    Chapter 3 of Understanding Robust and Exploratory Data Analysis,
    eds. D.C. Hoaglin, F. Mosteller and J.W. Tukey. Wiley.

    Chambers, J.M., Cleveland, W.S., Kleiner, B. and Tukey, P.A. (1983).
    Graphical Methods for Data Analysis. Wadsworth & Brooks/Cole.
    """

    ## quality check for coef
    if coef <= 0:
        raise ValueError("cannot proceed: coef must be greater than zero")

    ## convert input to numpy array
    x = np.array(x)

    ## determine median, lower ‘hinge’, upper ‘hinge’
    median = np.quantile(a = x, q = 0.50, interpolation = "midpoint")
    first_quart = np.quantile(a = x, q = 0.25, interpolation = "midpoint")
    third_quart = np.quantile(a = x, q = 0.75, interpolation = "midpoint")

    ## calculate inter quartile range
    intr_quart_rng = third_quart - first_quart

    ## calculate extreme of the lower whisker (observed, not interpolated)
    lower = first_quart - (coef * intr_quart_rng)
    lower_whisk = np.compress(x >= lower, x)
    lower_whisk_obs = np.min(lower_whisk)

    ## calculate extreme of the upper whisker (observed, not interpolated)
    upper = third_quart + (coef * intr_quart_rng)
    upper_whisk = np.compress(x <= upper, x)
    upper_whisk_obs = np.max(upper_whisk)

    ## store box plot results dictionary
    boxplot_stats = {}
    boxplot_stats["stats"] = np.array([lower_whisk_obs,
                                       first_quart,
                                       median,
                                       third_quart,
                                       upper_whisk_obs])

    ## store observations beyond the box plot extremes
    boxplot_stats["xtrms"] = np.array(x[(x < lower_whisk_obs) |
                                        (x > upper_whisk_obs)])

    ## return dictionary
    return boxplot_stats


import numpy as np
#from random import shuffle

def softmax_loss_naive(W, X, y, reg):
  """
  Softmax loss function, naive implementation (with loops)

  Inputs have dimension D, there are C classes, and we operate on minibatches
  of N examples.

  Inputs:
  - W: A numpy array of shape (D, C) containing weights.
  - X: A numpy array of shape (N, D) containing a minibatch of data.
  - y: A numpy array of shape (N,) containing training labels; y[i] = c means
    that X[i] has label c, where 0 <= c < C.
  - reg: (float) regularization strength

  Returns a tuple of:
  - loss as single float
  - gradient with respect to weights W; an array of same shape as W
  """
  # Initialize the loss and gradient to zero.
  loss = 0.0
  dW = np.zeros_like(W)

  #############################################################################
  # TODO: Compute the softmax loss and its gradient using explicit loops.     #
  # Store the loss in loss and the gradient in dW. If you are not careful     #
  # here, it is easy to run into numeric instability. Don't forget the        #
  # regularization!                                                           #
  #############################################################################
  pass
  N, C = X.shape[0], W.shape[1]
  for i in range(N):
      f = np.dot(X[i], W)
      f -= np.max(f) # f.shape = C
      loss = loss + np.log(np.sum(np.exp(f))) - f[y[i]]
      dW[:, y[i]] -= X[i]
      s = np.exp(f).sum()
      for j in range(C):
          dW[:, j] += np.exp(f[j]) / s * X[i]
  loss = loss / N + 0.5 * reg * np.sum(W * W)
  dW = dW / N + reg * W
  #############################################################################
  #                          END OF YOUR CODE                                 #
  #############################################################################

  return loss, dW

def softmax_loss_vectorized(W, X, y, reg):
  """
  Softmax loss function, vectorized version.

  Inputs and outputs are the same as softmax_loss_naive.
  """
  # Initialize the loss and gradient to zero.
  loss = 0.0
  dW = np.zeros_like(W)

  #############################################################################
  # TODO: Compute the softmax loss and its gradient using no explicit loops.  #
  # Store the loss in loss and the gradient in dW. If you are not careful     #
  # here, it is easy to run into numeric instability. Don't forget the        #
  # regularization!                                                           #
  #############################################################################
  pass
  N = X.shape[0]
  f = np.dot(X, W) # f.shape = N, C
  f -= f.max(axis = 1).reshape(N, 1)
  s = np.exp(f).sum(axis = 1)
  loss = np.log(s).sum() - f[range(N), y].sum()

  counts = np.exp(f) / s.reshape(N, 1)
  counts[range(N), y] -= 1
  dW = np.dot(X.T, counts)

  loss = loss / N + 0.5 * reg * np.sum(W * W)
  dW = dW / N + reg * W
  #############################################################################
  #                          END OF YOUR CODE                                 #
  #############################################################################

  return loss, dW


import numpy as np
from scipy.constants import mu_0, epsilon_0

# useful params
def omega(freq):
    """Angular frequency, omega"""
    return 2.0 * np.pi * freq

def k(freq, sigma, mu=mu_0, eps=epsilon_0):
    """ Eq 1.47 - 1.49 in Ward and Hohmann """
    w = omega(freq)
    alp = w * np.sqrt(mu * eps / 2 * (np.sqrt(1.0 + (sigma / (eps * w)) ** 2) + 1))
    beta = w * np.sqrt(mu * eps / 2 * (np.sqrt(1.0 + (sigma / (eps * w)) ** 2) - 1))
    return alp - 1j * beta

def TriangleFun(time, ta, tb):
    """
    Triangular Waveform
    * time: 1D array for time
    * ta: time at peak
    * tb: time at step-off
    """
    out = np.zeros(time.size)
    out[time <= ta] = 1 / ta * time[time <= ta]
    out[(time > ta) & (time < tb)] = (
        -1 / (tb - ta) * (time[(time > ta) & (time < tb)] - tb)
    )
    return out

def TriangleFunDeriv(time, ta, tb):
    """
    Derivative of Triangular Waveform
    """
    out = np.zeros(time.size)
    out[time <= ta] = 1 / ta
    out[(time > ta) & (time < tb)] = -1 / (tb - ta)
    return out

def SineFun(time, ta):
    """
    Sine Waveform
    * time: 1D array for time
    * ta: Pulse Period
    """
    out = np.zeros(time.size)
    out[time <= ta] = np.sin(1.0 / ta * np.pi * time[time <= ta])

    return out

def SineFunDeriv(time, ta):
    """
    Derivative of Sine Waveform
    """
    out = np.zeros(time.size)
    out[time <= ta] = 1.0 / ta * np.pi * np.cos(1.0 / ta * np.pi * time[time <= ta])
    return out

def VTEMFun(time, ta, tb, a):
    """
    VTEM Waveform
    * time: 1D array for time
    * ta: time at peak of exponential part
    * tb: time at step-off
    """
    out = np.zeros(time.size)
    out[time <= ta] = (1 - np.exp(-a * time[time <= ta] / ta)) / (1 - np.exp(-a))
    out[(time > ta) & (time < tb)] = (
        -1 / (tb - ta) * (time[(time > ta) & (time < tb)] - tb)
    )
    return out


# Program 17d: Plot tau curve, D_q curve and f(alpha) curve.
# See Figure 17.16.

import numpy as np
import matplotlib.pyplot as plt
import scipy.misc

plt.subplots_adjust(hspace=1)
plt.figure(1)

# The tau curve
x = np.linspace(-20, 20, 1000)
y = (np.log((1/9)**x + (8/9)**x) / np.log(3))

plt.subplot(3, 1, 1)
plt.plot(x, y)
plt.xlabel('$q$', fontsize=15)
plt.ylabel(r'$\tau(q)$', fontsize=15)
plt.tick_params(labelsize=15)

# The D_q curve
x1 = np.linspace(-20, 0.99, 100)
x2 = np.linspace(0.99, 20, 100)
Dq1 = (np.log((1/9)**x1 + (8/9)**x1) / (np.log(3) * (1 - x1)))
Dq2 = (np.log((1/9)**x2 + (8/9)**x2) / (np.log(3) * (1 - x2)))
plt.subplot(3, 1, 2)
plt.plot(x1, Dq1, x2, Dq2)
plt.xlabel('q', fontsize=15)
plt.ylabel('$D_q$', fontsize=15)
plt.tick_params(labelsize=15)

# The f(alpha) curve
p1, p2 = 1/9, 8/9
k = 500
s = np.arange(500)
x = (s*np.log(p1) + (k-s) * np.log(p2)) / (k*np.log(1/3))
f = -(np.log(scipy.misc.comb(k, s))) / (k*np.log(1/3))

plt.subplot(3, 1, 3)
plt.plot(x, f)
plt.xlabel(r'$\alpha$', fontsize=15)
plt.ylabel(r'$f(\alpha)$', fontsize=15)
plt.tick_params(labelsize=15)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Mon May  4 12:48:05 2020

@author: sven
"""
import numpy as np

def nearfield(f,c,theta):
	"""
	Compute the nearfield

	Parameters
	----------
	f : numeric
		Transducer Frequency in kHz [kHz].
	c : numeric
		Ambient sound speed [m/s].
	theta : numeric
		3dB angle or beam width in degrees [degrees].

	Returns
	-------
	Rnf : numeric
		Range of the nearfield for the given conditions in meters [m].

	"""
	lmbd = c/ ( f * 1000)
	k = 2* np.pi / lmbd
	a = 1.6 / (k * np.sin((theta * np.pi/180) / 2))
	Rnf = (2*a)**2 / lmbd
	return Rnf

def eba(f,c,theta):
	"""
	Compute the equivalent beam angle for a circular transducer.

	Parameters
	----------
	f : numeric
		Transducer Frequency in kHz [kHz].
	c : numeric
		Ambient sound speed [m/s].
	theta : numeric
		3dB angle or beam width in degrees [degrees].

	Returns
	-------
	EBA : numeric
		equivalent beam angle in dB [dB].

	"""
	lmbd = c/ ( f * 1000)
	k = 2* np.pi / lmbd
	a = 1.6  / (k * np.sin((theta * np.pi/180) / 2))
	EBA = 10 * np.log10( 5.78 / ( ( k * a ) ** 2))#equivalent beam angle in steradians
	return EBA

def vol_samp(f,c,theta,tau,R,start=0):
	f = f*1000
	Rtot = R+start
	Vtot = 10**(eba(f,c,theta)/10) * Rtot**2 * c * tau / 2
	V0 = 10**(eba(f,c,theta)/10) * start**2 * c * tau / 2
	V = Vtot - V0
	return V
def footprint_radius(theta,R):
	return R * np.tan(theta * np.pi / 180 / 2)

def footprint_area(theta, R):
	return np.pi * footprint_radius(theta,R)**2

'''
vol_samp(f=200,c=1450,theta=9.8,tau=6/1000,R=10)
vol_samp(f=1000,c=1450,theta=4,tau=6/1000,R=10)
#Zonar
nearfield(200,1480,9.8)

nearfield(1000,1480,4)

c=1450;f=200000
0.045**2/(c/f)

c=1450;f=1000000
0.022**2/(c/f)
'''


# 神经网络
# 输入层 输出层 隐藏层
# y=h(b+w1*x1+w2*x2)
# 激活函数 activation function ： 将输入信号的总和转换为输出信号（加权输入信号和偏置）
# 阶跃函数：激活函数以阈值为界，一旦输入超过阈值，就切换输入
# 感知机中，使用了阶跃函数作为激活函数

import numpy as np
import matplotlib.pyplot as plt

# 阶跃函数的图形
def step_function(x):
    return np.array(x > 0, dtype=np.int)

x = np.arange(-5.0, 5.0, 0.1)  # 在-5.0到5.0的范围内，以0.1为单位，生成numpy数组
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.title("step_function")
plt.show()

# sigmoid function
# h(x) = 1/1+exp(-x)    e是Napier常数2.7182
# sigmoid函数是神经网络常用函数之一
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

s = np.array([-1.0, 1.0, 2.0])
t = np.array([1.0, 2.0, 3.0])
print(1.0 + t)
print(1.0 / t)
print(sigmoid(s))

p = np.arange(-5.0, 5.0, 0.1)
q = sigmoid(p)
plt.plot(p, q)
plt.ylim(-0.1, 1.1)
plt.title("sigmoid")
plt.show()

# 阶跃函数和sigmoid函数都是非线性函数
# 激活函数必须使用非线性函数

# ReLU函数 rectified linear unit
# ReLU在输入大雨0时，直接输出该值；在输入小于0时，输出0
def relu(x):
    return np.maximum(0, x)

r = np.arange(-5.0, 5.0, 0.1)
u = relu(r)
plt.plot(r, u)
plt.title("ReLU")
plt.show()

# 多维数组
A = np.array([1, 2, 3, 4])
print(A)
np.ndim(A)  # 数组的维数
print(A.shape)
print(A.shape[0])
B = np.array([[1, 2], [3, 4], [5, 6]])
print(B)
print(np.ndim(B))
print(B.shape)
print(B.shape[0])

# 矩阵乘法
C = np.array([[1, 2], [3, 4]])
D = np.array([[5, 6], [7, 8]])
E = np.dot(C, D)  # np.dot()接受NumPy数组作为参数，并返回数组的乘积
print(E)

# 2*3 和 3*2 的矩阵相乘, 结果不同
# 并且左侧数组的列数要和右侧数组的行数一致
# 拓展这个概念左侧数组的第一维要和右侧数组的第0维元素个数一致
# 3*2 dot 2*4 = 3*4
# 3*2 dot 2 = 3
# 2*3 dot 2*2 error
F = np.array([[1, 2], [3, 4], [5, 6]])
G = np.array([[1, 2, 3], [4, 5, 6]])
H = np.dot(F, G)
I = np.dot(G, F)
print(H)
print(I)

# 2*3 dot 2*2 error
try:
    J = np.dot(C, F)
except ValueError as e:
    print('error :', e)
finally:
    print('END')
# Traceback (most recent call last):
#   File "<input>", line 2, in <module>
# ValueError: shapes (2,2) and (3,2) not aligned: 2 (dim 1) != 3 (dim 0)
K = np.dot(F, C)
print(K)
# 3*2 dot 2*4 = 3*4
L = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
M = np.dot(F, L)
print(M)

# 神经网络的内积
# X   W  =  Y
# 2  2*3    3
_x = np.array([1, 2])
_w = np.array([[1, 3, 5], [2, 4, 6]])
_y = np.dot(_x, _w)
print(_y)  # [ 5 11 17]

# 3层神经网络的实现
# 神经网络的运算可以作为矩阵运算打包进行
# 第一层
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.2, 0.3], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
print(W1.shape)
print(X.shape)
print(B1.shape)
print(A1)
print(Z1)
# 第二层
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
print(Z1.shape)
print(W2.shape)
print(B2.shape)
print(A2)
print(Z2)

# 恒等函数 作为输出层的激活函数
# 一般滴，回归问题用恒等函数，二元分类问题可以使用sigmoid函数，多元分类问题用softmax函数
def identify_function(x):
    return x

W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identify_function(A3)

#
#
# 按照惯例整理下3层神经网络的实现
#
#
def init_network():
    network = {}
    network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    network['b1'] = np.array([0.1, 0.2, 0.3])
    network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
    network['b2'] = np.array([0.1, 0.2])
    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
    network['b3'] = np.array([0.1, 0.2])
    return network

def forward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = identify_function(a3)
    return y

network = init_network()
x = np.array([1.5, 0.5])
y = forward(network, x)
print('y', y)

# 输出层的设计
# 分类问题中的softmax函数 yk = exp(ak) / sum(exp(ai)  (i:1~n)
# softmax 函数的实现
def softmax(a):
    c = np.max(a)  # 溢出对策
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

a = np.array([1010, 1000, 990])
y = softmax(a)
print(y)  # softmax函数输出的值是0.0到1.0之间的实数
print(np.sum(y))  # 并且softmax函数的输出值的总和是1
# 这样我们才把softmax函数的输出解释为概率

# 实现神经网络的推理处理，也叫神经网络的前向传播 forward propagation
# 手写数字的识别
# MNIST数据集


from numba import jit

def Normal_solver_MO(data_set, vlambda, flag = "n"):
    """
        This function takes as input a data_set and a set of lambdas
        and evaluates the Multi Objective Normal Equation as
        the equation reported on the article

        Inputs:
            data_set: data container
            vlambda: hyperparameter for regularization
        Returns:
            evaluation of the ridge regression
    """

    # QM Reference data
    y    = data_set.y_train
    H    = data_set.x_train

    values  = []

    N_func = np.size(H,1)
    I      = np.eye(N_func, N_func)

    if isinstance(vlambda, list):
        for l in vlambda:
            values.append(solve_normal_eq(H,l,I,y))  # Normal Equation Multi

    elif isinstance(vlambda, np.ndarray):
        for l in vlambda:
            l = np.asscalar(l)
            values.append(solve_normal_eq(H,l,I,y))  # Normal Equation Multi Objective
    else:
        l = vlambda
        values.append(solve_normal_eq(H,l,I,y))  # Normal Equation Multi

    values = np.array(values)
    dim    = np.shape(values)
    values = np.resize(values,(dim[0],dim[-1]))
    if flag == "n":
        return values
    else:
        return values, data_set.ip['sigma']

@jit(nopython=True,parallel = True,fastmath = True)
def solve_normal_eq(H,l,I,y):
    """
        Evaluation of the Normal Equation

        Inputs
            H:  training set
            l:  hyperparameter for the regularization
            I:  Identity matrix
            y:  reference

        Returns
            Estimate C of the ridge regression
    """
    return np.dot(np.dot(np.linalg.inv(np.dot(H.T,H) + l*I), H.T),y)


# -*- coding: utf-8 -*-
"""
Created on Sun Mar 31 19:41:38 2019

@author: mhossa12
"""

import numpy as np

# You need to install numpy in order to import it
# Numpy transpose returns similar result when
# applied on 1D matrix

matrix_M = [[4,1,5],[3,6,2]]
print(matrix_M)

print("\n")
print(np.transpose(matrix_M))

matrix_a = [[8,4,2],[6,1,5]]
matrix_b = [[3,10,4],[5,6,1]]

matrix_c = np.matrix(matrix_a) + np.matrix(matrix_b)

print(matrix_c)

matrix_a = [[8,4,2],[6,1,5]]
matrix_b = [[3,10,4],[5,6,1]]

matrix_d = np.matrix(matrix_a) - np.matrix(matrix_b)

print(matrix_d)

matrix_a = [[8,4,2],[6,1,5]]

matrix_e = 2 * np.matrix(matrix_a)
print(matrix_e)

matrix_a = [[8,4,2],[6,1,5]]
matrix_b = [[3,10],[5,6], [1, 2]]

matrix_f = np.matrix(matrix_a) * np.matrix(matrix_b)

print(matrix_f)

matrix_m = [[5,1,6],[0, 8, -2]]
matrix_n = [[2],[3],[4]]

matrix_g = np.matrix(matrix_m) * np.matrix(matrix_n)

print(matrix_g)

matrix_1 = [[4, 7], [2, 6]]
matrix1_inv = np.linalg.inv(matrix_1)
print(matrix1_inv)

matrixE = [[1, 0, 0], [-5, 1, 0], [0, 0, 1]]
matrixE_inv = np.linalg.inv(matrixE)
print(matrixE_inv)

matrixM = [[1, 0, 5], [2, 1, 6], [3, 4, 0]]
matrixM_inv = np.linalg.inv(matrixM)
print(matrixM_inv)

Matrix and Vector Operations/BasicMatrixOperations.py


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import random

style.use('fivethirtyeight')

#xs = np.array([1,2,3,4,5,6], dtype = np.float64)
#ys = np.array([5,4,6,5,6,7], dtype = np.float64)

def create_dataset(hm, variance, step=2, correlation=False):
     val= 1
     ys= []
     for i in range(hm):
         y = val + random.randrange(-variance, variance)
         ys.append(y)
         if correlation and correlation =='pos':
             val+=step
         elif correlation and correlation == 'neg':
              val-=step
     xs=[i for i in range(len(ys))]

     return np.array(xs, dtype = np.float64), np.array(ys, dtype = np.float64)

def best_fit_slope_and_intercept(xs,ys):
    m =(  ((mean(xs)* mean(ys)) - mean(xs*ys))/ ((mean(xs)* mean(xs))- mean(xs*xs)))
    b= mean(ys) - m*mean(xs)
    return m,b

def squared_error(ys_orig, ys_line):
    return sum((ys_line -ys_orig)**2)

def coefficient_of_determination(ys_orig,ys_line):
    y_mean_line =[mean(ys_orig) for y in ys_orig]
    squared_error_regr=squared_error(ys_orig,ys_line)
    squared_error_y_mean= squared_error(ys_orig,y_mean_line)
    return 1-(squared_error_regr /squared_error_y_mean)

xs, ys = create_dataset(40,10,2, correlation = 'pos')

m,b = best_fit_slope_and_intercept(xs,ys)
print(m,b)

regression_line = [(m*x)+b for x in xs]

predict_x =8
predict_y=(m*predict_x)+b

r_squared= coefficient_of_determination(ys,regression_line)
print(r_squared)

plt.scatter(xs,ys)
plt.scatter(predict_x,predict_y, s=100, color='g')
plt.plot(xs,regression_line)
plt.show()


import numpy as np

def gaussian(x, p):
    '''
    gaussian(arr,p): p[0] = norm, p[1] = mean, p[2]=sigma
    '''
    return p[0] * np.exp(-1 * (x - p[1]) ** 2 / (2 * p[2] ** 2))

def double_gaussian(x, p):
    '''
    gaussian(arr,p): p[0] = norm1, p[1] = mean1, p[2]=sigma1
                     p[3] = norm2, p[4] = mean2, p[5]=sigma2
    '''
    return gaussian(x, p[:3]) + gaussian(x, p[3:])

def mp_double_gauss(p, fjac=None, x=None, y=None, err=None):
    '''
    double gaussian for mpfit
    '''
    model = double_gaussian(x, p)
    status = 0
    return [status, (y - model) / err]

def mp_gauss(p, fjac=None, x=None, y=None, err=None):
    '''
    double gaussian for mpfit
    '''
    model = gaussian(x, p)
    status = 0
    return [status, (y - model) / err]

def diff_gaussian(x, p):
    '''
    gaussian(arr,p): p[0] = norm1, p[1] = mean1, p[2]=sigma1
                     p[3] = norm2, p[4] = mean2, p[5]=sigma2
    '''
    return gaussian(x, p[:3]) - gaussian(x, p[3:])

#del np


"""Module responsible for the kernels"""
from __future__ import absolute_import, division, print_function, unicode_literals
import numpy as np

def poly_kernel(x: np.ndarray, y: np.ndarray, degree: int=1, gamma: float=1.0, coef0: float=0.0):
    """
    Compute the polynomial kernel between x and y::
        K(x, y) = (gamma * <x, y> + coef0)^degree

    :param x: ndarray of shape (n_samples_1, n_features)
    :param y: ndarray of shape (n_samples_2, n_features)
    :param degree: see formula
    :param gamma: see formula
    :param coef0: see formula
    :return: Gram matrix : array of shape (n_samples_1, n_samples_2)
    """

    K = np.dot(x, y.T)
    K *= gamma
    K += coef0
    K **= degree
    return K

def rbf(a: np.ndarray, b: np.ndarray, length: float, sigma_f: float):
    """
    Radial basis kernel: Gaussian distance kernel

    :param a: design matrix of shape (nexamples, nfeatures)
    :param b: design matrix of shape (nexamples, nfeatures)
    :param length: Kernel parameter: vertical length
    :param sigma_f: Kernel parameter: horizontal length
    :return: Gram Matrix of shape (nexamples, nexamples)
    """
    sqdist = np.sum(a**2, 1).reshape(-1, 1) + np.sum(b**2, 1) - 2 * np.dot(a, b.T)
    return np.exp(-0.5 * (1 / length) * sqdist) * sigma_f


import tools
import log
import copy

import numpy as np

def gauss(B, c, use_np = True):
    if use_np:
        return np.linalg.solve(B, c)
    A = copy.copy(B)
    b = copy.copy(c)

    log.debug(f'Gauss started {tools.print_matrix(A)}')
    assert A.shape[0] == A.shape[1] == len(b)
    n = len(b)
    assert n > 0

    current_line = np.array([i for i in range(n)]).reshape(n)
    line_max = np.array([max([abs(A[i][j]) for j in range(n)]) for i in range(n)]).reshape(n)

    for i in range(n - 1):

        index = i + np.argmax([abs(A[current_line[j]][i]) / line_max[current_line[j]] for j in range(i, n)])
        current_line[i], current_line[index] = current_line[index], current_line[i]

        for j in range(i + 1, n):
            factor = A[current_line[j]][i] / A[current_line[i]][i]
            b[current_line[j]] = b[current_line[j]] - factor * b[current_line[i]]
            for k in range(i, n):
                A[current_line[j]][k] = A[current_line[j]][k] - factor * A[current_line[i]][k]

    x = np.zeros(n)
    x[n - 1] = b[current_line[n - 1]] / A[current_line[n - 1]][n - 1]

    for i in range(n - 2, -1, -1):
        summa = sum([A[current_line[i]][j] * x[j] for j in range(i + 1, n)])
        x[i] = (b[current_line[i]] - summa) / A[current_line[i]][i]

    log.debug('Gauss ended')
    return x


import numpy as _np

__all__=['calc_phase',
        'linear_frequency',
        'symetric_linear_frequency',
        'poly_frequency',
        'sin_frequency',
        'sin_quart_frequency',
        'sin_half_frequency',
        'sin_phase_frequency'
        ]

#-------------------------------
def calc_phase(frequencies, init_phases, length = None, fs=None):
    '''
    Utilite to calculte the phase to time relation
      based on frequency to time relation.
      ..math::
      phi(n) = 2pi*integral(*f(n)*n/fs)

    Paramters
    -----------
    frequencies: float or 1d ndarray,
      could be either carier frequency (float),
      or frequency to time relation (frequency modulation).
    init_phases: float or 1d ndarray,
      could be either initial phase (float),
      or additional phase to time relation (phase modulation).
    length: int or None,
      length or required signal,
      if None, length = max of lengths
      between frequencies, amplitudes, init_phases
    fs: float or None,
      sampling frequency, if None, fs = length

    Returns
    --------------
    full_phase: 1d ndarray.

    Note
    ----------
    If some of the parameters (freq, amp, phase) have a size,
      smaller than the required size, it will be extended,
      with the last value. In the opposite case,
      it will be cut up to the required size.

    '''
    frequencies = _np.asarray(frequencies, dtype = float)
    init_phases = _np.asarray(init_phases, dtype = float)

    if length is None:
        length = max(frequencies.size,
                     init_phases.size)

    frequencies = _check_size(frequencies, length)
    init_phases = _check_size(init_phases, length)

    if fs is None:
        fs = length

    dt = 1/fs

    return 2*_np.pi*_np.cumsum(frequencies)*dt+init_phases

#-------------------------------------
def _check_size(array, size=None):

    array = _np.asarray(array, dtype = float)

    if size is None:
        return array

    size = int(size)

    if array.size==1:
        return _np.ones(size, dtype=float)*array
    else:
        if array.size>=size:
            return array[:size]

        else:
            last  = _np.ones(size-array.size,
                            dtype=float)*array[-1]
            return _np.concatenate((array,last))

#-------------------------------
def linear_frequency(f0, delta_f, length, fs):
    '''
    Linear frequency to time relation.
      ..math::
      f(n) = f0 + delta_f*(n/fs)/Tm,
      Tm = N/fs, n=0,...,N-1

    Paramteres
    ------------
    f0: float,
      carried frequency.
    delta_f: float,
      frequency bandwidth.
    length: int,
      length of relation.
    fs: float,
      sampling frequency.

    Return
    ----------
    freq: 1d ndarray.
    '''
    length = int(length)
    t = _np.arange(length)/fs
    return f0+delta_f*t/((length/fs))
#-------------------------------
def symetric_linear_frequency(f0, delta_f, length, fs):
    '''
    Linear frequency to time relation.
      ..math::
      f(n) = [f0 + delta_f*(n/fs)/Tm,
      f0+delta_f - delta_f*(n/fs)/Tm]
      Tm = N/2fs, n=0,...,N-1

    Paramteres
    ------------
    f0: float,
      carried frequency.
    delta_f: float,
      frequency bandwidth.
    length: int,
      length of relation.
    fs: float,
      sampling frequency.

    Return
    ----------
    freq: 1d ndarray.
    '''
    length = int(length)
    t = _np.arange(length)/(2*fs)
    tm = length/fs/2
    return _np.concatenate((f0+delta_f*t/tm,
                           f0+delta_f-delta_f*t/tm))
#-------------------------------
def poly_frequency(f0, delta_f, degree, length, fs):
    '''
    Polynomial frequency to time relation.
      ..math::
      f(n) = f0 + delta_f*(fm/max(fm)),
      fm = (n/fs)^d/Tm
      Tm = N/fs, n=0,...,N-1

    Paramteres
    ------------
    f0: float,
      carried frequency.
    delta_f: float,
      frequency bandwidth.
    degree: float,
      degree of Polynomial relation.
    length: int,
      length of relation.
    fs: float,
      sampling frequency.

    Return
    ----------
    freq: 1d ndarray.
    '''
    t = _np.arange(length)/fs
    freq = _np.power(t,degree)/((length/fs))
    return f0+delta_f*freq/_np.max(freq)
#-------------------------------
def sin_frequency(f0, delta_f, length, fs):
    '''
    Sinus frequency to time relation.
      ..math::
      f(n) = f0+delta_f/2+delta_f*sin(2pi*n/(Tmfs))/2,
      Tm = N/fs, n=0,...,N-1

    Paramteres
    ------------
    f0: float,
      carried frequency.
    delta_f: float,
      frequency bandwidth.
    length: int,
      length of relation.
    fs: float,
      sampling frequency.

    Return
    ----------
    freq: 1d ndarray.
    '''
    t = _np.arange(length)/fs
    tm = length/fs
    return (f0+delta_f/2)+delta_f*_np.sin(2*_np.pi*t/tm)/2
#-------------------------------
def sin_quart_frequency(f0, delta_f, length, fs):
    '''
    Sinus quarter (first quadrant) frequency to time relation.
      ..math::
      f(n) = f0+delta_f*sin(2pi*n/(4Tmfs)),
      Tm = N/fs, n=0,...,N-1

    Paramteres
    ------------
    f0: float,
      carried frequency.
    delta_f: float,
      frequency bandwidth.
    length: int,
      length of relation.
    fs: float,
      sampling frequency.

    Return
    ----------
    freq: 1d ndarray.
    '''
    t = _np.arange(length)/fs
    tm = length/fs
    return (f0)+delta_f*_np.sin((2*_np.pi*t/tm)/4)

#-------------------------------
def sin_half_frequency(f0, delta_f, length, fs):
    '''
    Sinus half (first 2 quadrants) frequency to time relation.
      ..math::
      f(n) = f0+delta_f*sin(2pi*n/(2Tmfs)),
      Tm = N/fs, n=0,...,N-1

    Paramteres
    ------------
    f0: float,
      carried frequency.
    delta_f: float,
      frequency bandwidth.
    length: int,
      length of relation.
    fs: float,
      sampling frequency.

    Return
    ----------
    freq: 1d ndarray.
    '''
    t = _np.arange(length)/fs
    tm = length/fs
    return (f0)+delta_f*_np.sin((2*_np.pi*t/tm)/2)

#-------------------------------
def sin_phase_frequency(f0, delta_f, length, fs, phi0=0, phiN=_np.pi/4):
    '''
    Arbitarry sinus frequency to time relation.
      ..math::
      f(n) = f0+delta_f*sin(2pi*n/(Tm*fs*pi/phiN)+phi0),
      Tm = N/fs, n=0,...,N-1

    Paramteres
    ------------
    f0: float,
      carried frequency.
    delta_f: float,
      frequency bandwidth.
    length: int,
      length of relation.
    fs: float,
      sampling frequency.

    Return
    ----------
    freq: 1d ndarray.
    '''
    t = _np.arange(length)/fs
    tm = length/fs
    divider = _np.pi/phiN
    freqm = _np.sin(2*_np.pi*t/(tm*divider)+phi0)
    return (f0)+delta_f*(freqm-min(freqm))/(max(freqm)-min(freqm))

#---------------------------------------
def to_beatsignal(sig_ref, sig_delayed,
                  reduced_ratio=None, hilbert = False):
    '''
    Mixing reference and delayed (received)
        signals to create the beat one.

    Parameters
    -------------
    sig_ref: 1d ndarray,
        reference signal
    sig_delayed: 1d ndarray,
        dealyed signal
    reduced_ratio: float,
        pooling (or downsampling) ratio as fs_old/fs_new,
        where fs is the sampling frequency,
    hilbert: bool,
        If ture, hilbert transform will be carried out additionally.

    Returns
    -------------
    sig_beat: 1d ndarray,
        beat signal.
    '''
    sig_beat = sig_ref*_np.conj(sig_delayed);
    if reduced_ratio is not None:

        len_reduce = int(len(sig_ref)/reduced_ratio)

        sp = _np.fft.fft(sig_beat)

        if hilbert is False:
            sp = _np.concatenate((sp[:len_reduce//2],
                                 [sp[len(sp)//2]],
                                 sp[len(sp) - len_reduce//2 +1:]))
        else:
            sp = _np.concatenate((sp[:len_reduce//2],
                                 [sp[len(sp)//2]],
                                 _np.zeros(len_reduce//2 +1,
                                          dtype=complex)))

        sig_beat = _np.fft.ifft(sp)
    else:
        if hilbert:
            sp = _np.fft.fft(sig_beat)
            sp[len(sp)//2:] = 0
            sig_beat = _np.fft.ifft(sp)

    #TODO: add pooling
    return sig_beat


import numpy as np
import math

def FitLinearGaussianParameters(X, U):
    '''
    :param X:
    :param U:
    :return:
    '''

    M = U.shape[0]
    N = U.shape[1]
    assert(N == 3)
    assert(M == X.shape[0])

    Beta = np.zeros((N+1, ))
    sigma = 1

    A = np.ones((N + 1, N + 1))
    mean_U = np.mean(U, axis=0)
    last_col = np.concatenate(([1], mean_U))
    first_row = np.concatenate((mean_U, [1]))
    A[0, :] = first_row
    A[:, -1] = last_col

    for i in range(N):
        for j in range(N):
            prod_ij = np.dot(U[:, i], U[:, j])
            E_ij = prod_ij / M
            A[i + 1, j] = E_ij

    B = np.ones((N + 1, ))
    first_row_B = np.mean(X)
    B[0] = first_row_B
    for k in range(N):
        prod_X_U_k = np.dot(X, U[:, k])
        E_X_U_k = prod_X_U_k / M
        B[k + 1] = E_X_U_k

    Beta = np.linalg.solve(A, B)

    cov_X_X = np.dot(X, X) / M - (np.mean(X)) ** 2
    cov_U = np.cov(U, rowvar=False, bias=True)

    beta_vec = Beta[0 : -1]
    beta_mat = np.matmul(np.matmul(beta_vec, cov_U), beta_vec.transpose())

    first_term = cov_X_X
    second_term = np.matmul(np.matmul(beta_vec, cov_U), beta_vec.transpose())
    sigma = math.sqrt(first_term - second_term)

    return Beta, sigma


# -*- coding: utf-8 -*-

import numpy, math

def gain(X_partition, y_partition):
    """Calcula medida de ganho de informação."""
    info_partition = info(y_partition)
    info_attr, partitions, values = info_attribute(X_partition, y_partition)
    return (info_partition - info_attr), partitions, values

def info(y_partition):
    """Calcula medida de informação para o conjunto de dados."""
    total = 0
    partition_size = y_partition.size
    values, indices = numpy.unique(y_partition, return_inverse=True)
    counts = numpy.bincount(indices)
    if len(values) > 1:
        for value, count in zip(values, counts):
            prob = count / float(partition_size)
            total -= prob * math.log(prob, len(values))
    return total

def info_attribute(X_partition, y_partition):
    """Calcula medida de informação para um atributo do conjunto de dados."""
    total = 0
    partition_size = y_partition.size
    values, indices = numpy.unique(X_partition, return_inverse=True)
    counts = numpy.bincount(indices)
    partitions = []
    for value, count in zip(values, counts):
        prob = count / float(partition_size)
        partition = numpy.where(X_partition==value)
        partitions.append(partition)
        partition_info = info(y_partition[partition])
        total += prob * partition_info
    return total, partitions, values


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 19 2020

@author: daniel
"""

# swish function
# https://en.wikipedia.org/wiki/Swish_function
from numpy import arange
def swish(x, beta):
    return x/(1+exp(-beta*x))

x_sample_space = arange(-5, 5.01, 0.01)
beta = 1 # 0.5
y = swish(x_sample_space, beta)
plt.plot(x_sample_space, y)
plt.show()


from math import floor
from numpy import arange, exp, pi, flipud, append, conj, real
from numpy.fft import ifft
from numpy.random import randn, rand

def powernoise(alpha, N, randpower=False, normalize=False):
	"""
	Generate samples of power law noise. The power spectrum
	of the signal scales as f^(-alpha).

	Usage:
		x = powernoise(alpha, N)
		x = powernoise(alpha, N, randpower=True, normalize=True)

	Inputs:
		alpha - power law scaling exponent
		N     - number of samples to generate

	Output:
		x     - N x 1 vector of power law samples

	By default, the power spectrum is deterministic, and the phases are
	uniformly distributed in the range -pi to +pi. The power law extends
	all the way down to 0Hz (DC) component. By specifying randpower=True
	the power spectrum will be stochastic with Chi-square distribution.
	With normalize=True the output is scaled to the range [-1, 1], and
	consequently the power law will not necessarily extend right down to 0Hz.

	Original Matlab code by Max Little:
	Little MA et al. (2007), "Exploiting nonlinear recurrence and fractal
	scaling properties for voice disorder detection", Biomed Eng Online, 6:23
	See http://www.maxlittle.net/software/
	"""

	N2 = floor(N/2)-1
	f = arange(2, N2+2)
	A2 = 1./(f**(alpha/2))

	if (randpower):
		p2 = (rand(N2)-0.5)*2.*pi
		d2 = A2 * exp(1j*p2)
	else:
		# 20080323
		p2 = randn(N2) + 1j * randn(N2)
		d2 = A2 * p2

	d = append( append( append([1.], d2), [1/((N2+2)**alpha)] ), flipud(conj(d2)) )
	x = real(ifft(d))

	if (normalize):
		x = ((x - min(x))/(max(x) - min(x)) - 0.5) * 2

	return x


from scipy.integrate import odeint
import matplotlib.pyplot as plt

class SIR:
    def __init__(self, N, I0, beta, gamma, days):
        """
        N - Total population
        I0 - Initial number of infected individuals,
        S0 - Everyone else is susceptible to infection initially
        Contact rate, beta, and mean recovery rate, gamma, (in 1/days)
        """
        self.N = N
        self.I0 = I0
        self.beta = beta
        self.gamma = gamma
        self.S0 = N - I0
        self.days = days

    @staticmethod
    def _deriv(y, t, N, beta, gamma):
        """
        The SIR model differential equations
        """
        S, I, R = y
        #print(t)
        dSdt = -beta[int(t)] * S * I / N
        dIdt = beta[int(t)] * S * I / N - gamma * I
        dRdt = gamma * I
        return dSdt, dIdt, dRdt

    def run(self):
        # Initial conditions vector
        y0 = self.S0, self.I0, 0
        # Integrating the SIR equations over the time grid, t
        t = list(range(0, self.days))
        # Getting results
        result = odeint(self._deriv, y0, t, args=(self.N, self.beta, self.gamma))
        S, I, R = result.T
        return S, I, R

    @staticmethod
    def plot_results(S, I, R):
        fig = plt.figure(facecolor='w')
        ax = fig.add_subplot(111, axisbelow=True)
        ax.plot(S/1000, 'b', alpha=0.5, lw=2, label='Susceptible')
        ax.plot(I/1000, 'r', alpha=0.5, lw=2, label='Infected')
        ax.plot(R/1000, 'g', alpha=0.5, lw=2, label='Recovered')
        ax.set_xlabel('Time /days')
        ax.set_ylabel('Number (1000s)')
        #ax.set_ylim(0,1.2)
        ax.yaxis.set_tick_params(length=0)
        ax.xaxis.set_tick_params(length=0)
        ax.grid(b=True, which='major', c='w', lw=2, ls='-')
        legend = ax.legend()
        legend.get_frame().set_alpha(0.5)
        for spine in ('top', 'right', 'bottom', 'left'):
            ax.spines[spine].set_visible(False)
        plt.show()


import numpy as np

def mse(y,t):
    result = 0.5 * np.sum((y-t)**2)    #예측값 - 참값
    print(result)
    return result

#출력층 활성함수에 의해 출력된 결과  (소프트맥스)   -----> 예측값
y3 = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]  # 1일 확률이 0.1, 2일 확률이 0.05 등..
#원핫 인코딩   ----->  참값
t3 = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]  #np.argmax(y3)을 통해서 가장 확률이 높은 값을 표현하는 원핫인코딩

# 분류가 3일 확률이 0.6으로 가장 높은 경우 실제 3라는 결과의 손실 함수 값
mse(np.array(y3), np.array(t3)) #0.0975

# 분류가 7일 확률이 가장 높으나 실제 3이라는 결과의 손실 함수 값

y7 = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
mse(np.array(y7), np.array(t3)) #0.5975

#코스트가 줄지 않았다..?
#그러니까, 모든 케이스를 봤을 때, 손실함수 값이 가장 작았던 값을 첫 추정결과로 판단하게 되는 근거가 된다.
#그게 바로 MSE! 손실함수! cost !!

def cross_entropy_error(y,t):  # y와 t는 넘파이 배열이다.
    delta = 1e-7
    result = -np.sum(t* np.log(y+delta)) #np.log계산할때 아주 작은 값인 delta를 더했다.
                                          #0을 함수에 입력하면 -inf가 되버리기 때문에, 작은 값을 더해 무한대가 발생하지 않도록 하는 것.
    print(result)
    return result

cross_entropy_error(np.array(y3), np.array(t3))  #0.510825457099
cross_entropy_error(np.array(y7), np.array(t3))  #2.30258409299


from scipy.integrate import odeint, simps
import numpy as np
import matplotlib.pyplot as plt

"""
def pend(y, t, b, c):
    theta, omega = y
    dydt = [omega, -b*omega - c*np.sin(theta)]
    return dydt

b = 0.025
c = 5.0
y0 = [np.pi - 0.1, 0.0]
t = np.linspace(0, 100, 1001)
sol = odeint(pend, y0, t, args=(b, c))

x, p = sol.T[0], sol.T[1]
plt.plot(x, p, ".", markersize=4)
plt.pause(3)
plt.savefig('plot_xp.png', dpi=60)
plt.close()

plt.plot(t, sol[:, 0], 'b', label='theta(t)')
plt.xlabel('t')
plt.grid()
plt.legend(loc='best')
plt.pause(3)
plt.plot(t, sol[:, 1], 'g', label='omega(t)')
plt.legend(loc='best')
plt.pause(3)
plt.savefig('plot_thomegavst.png', dpi=60)
plt.close()
"""

def dualPendulum(y, t, L, omegaJ,M,M1):
    theta1, omega1,theta2, omega2 = y
    dydt = [omega1,
            (-omegaJ*(np.sin(theta1)+M*np.sin(theta1-2*theta2))-2*np.sin(theta1-theta2)*M*(omega2*omega2*L+omega1*omega1*np.cos(theta1-theta2)))/(1*(1-M*np.cos(2*(theta1-theta2)))),
            omega2,
            (2*np.sin(theta1-theta2)*(omega1*omega1*M1+omegaJ*M1*np.cos(theta1)+omega2*omega2*L*M1*np.cos(theta1-theta2)))/(L*(1 - M*np.cos(2*(theta1-theta2))))]
    return dydt

def doublePendulum(y, t, L, omegaJ,M):
    theta1, omega1,theta2, omega2 = y
    theta12=theta1 -theta2
    dydt = [omega1,
            (omegaJ*(-np.sin(theta1)+M*np.cos(theta12)*np.sin(theta2))-M*(omega1*omega1*np.cos(theta12)+L*omega2*omega2)*np.sin(theta12))/(1-M*np.cos(theta12)*np.cos(theta12)),
            omega2,
            (omegaJ*(-np.sin(theta2)+np.cos(theta12)*np.sin(theta1))+(M*L*omega2*omega2*np.cos(theta12)+omega1*omega1)*np.sin(theta12))/(L-M*L*np.cos(theta12)*np.cos(theta12))]
    return dydt

def doublePendulum2(y, t, L, omegaJ,M,M1):
    theta1, omega1,theta2, omega2 = y
    theta12=theta1 -theta2
    dydt = [omega1,
            (omegaJ*(-M1*np.sin(theta1)+M*np.cos(theta12)*np.sin(theta2))-M*(2*omega1*omega1*np.cos(theta12)+L*omega2*omega2)*np.sin(theta12))/(1-2*M*np.cos(theta12)*np.cos(theta12)),
            omega2,
            (omegaJ*(-np.sin(theta2)+2*M1*np.cos(theta12)*np.sin(theta1))+(2*M*L*omega2*omega2*np.cos(theta12)+2*omega1*omega1)*np.sin(theta12))/(L-2*M*L*np.cos(theta12)*np.cos(theta12))]
    return dydt

L1=7
L2=1
g=9.8
m1=100
m2=20
y01=0.5
for i in range(0,10,1):
    y01=(2.1115*0.0001)*np.exp(-i)  #3.14*np.exp(i)/np.exp(10)
    M=m2/(2*m1+m2)
    M1=(m1+m2)/(2*m1+m2)
    omegaJ=g/L1
    L=L2/L1
    y0 = [y01, 0.0,0, 0.0]
    t = np.linspace(0, 1000, 4001)
    sol = odeint(dualPendulum, y0, t, args=(L, omegaJ,M,M1))

    x1, p1,x2,p2 = sol.T[0],sol.T[1], sol.T[2], sol.T[3]
    plt.plot(x1, p1, ".", markersize=4)
    plt.pause(3)
    plt.savefig('plot_xp1dual_L1{}L2{}m1{}m2{}y01{}.png'.format(L1, L2,m1,m2,y01), dpi=180)
    plt.close()
    plt.plot(x2, p2, ".", markersize=4)
    plt.pause(3)
    plt.savefig('plot_xp2dual_L1{}L2{}m1{}m2{}y01{}.png'.format(L1, L2,m1,m2,y01),dpi=180)
    plt.close()
    """
    plt.plot(t, sol[:, 0], 'b', label='theta1(t)')
    plt.xlabel('t')
    plt.xlim(0,100)
    #plt.ylim(-0.5,0.5)
    plt.grid()
    plt.legend(loc='best')
    plt.pause(3)
    plt.plot(t, sol[:, 2], 'g', label='theta2(t)')
    plt.legend(loc='best')
    plt.pause(3)
    plt.savefig('plot_theta2vstdual_L1{}L2{}m1{}m2{}y01{}.png'.format(L1, L2,m1,m2,y01), dpi=180)
    plt.close()

    plt.plot(t, sol[:, 1], 'b', label='omega1(t)')
    plt.xlabel('t')
    plt.xlim(0,100)
    #plt.ylim(-1,1)
    plt.grid()
    plt.legend(loc='best')
    plt.pause(3)
    plt.plot(t, sol[:, 3], 'g', label='omega2(t)')
    plt.legend(loc='best')
    plt.pause(3)
    plt.savefig('plot_omega2vstdual_L1{}L2{}m1{}m2{}y01{}.png'.format(L1, L2,m1,m2,y01), dpi=180)
    plt.close()
    """

    M=m2/(m1+m2)
    L=L2/L1
    omegaJ=g/L1
    #y01=0.0020
    y0 = [y01, 0.0,0, 0.0]
    t = np.linspace(0, 1000, 4001)
    #sol = odeint(dualPendulum, y0, t, args=(L1, L2,g,m1,m2))
    #sol = odeint(doublePendulum, y0, t, args=(L1, L2,g,m1,m2))
    sol = odeint(doublePendulum, y0, t, args=(L, omegaJ,M))

    x1, p1,x2,p2 = sol.T[0],sol.T[1], sol.T[2], sol.T[3]
    plt.plot(x1, p1, ".", markersize=4)
    plt.pause(3)
    #plt.savefig('plot_xp1pen_L{}M{}omegaJ{}y01{}n.png'.format(L, omegaJ,M,y01), dpi=180)
    plt.savefig('plot_xp1pen_L1{}L2{}m1{}m2{}y01{}n.png'.format(L1, L2,m1,m2,y01), dpi=180)
    plt.close()
    plt.plot(x2, p2, ".", markersize=4)
    plt.pause(3)
    plt.savefig('plot_xp2pen_L{}M{}omegaJ{}y01{}n.png'.format(L, omegaJ,M,y01),dpi=180)
    plt.close()
    """
    plt.plot(t, sol[:, 0], 'b', label='theta1(t)')
    plt.xlabel('t')
    plt.xlim(0,100)
    #plt.ylim(-0.5,0.5)
    plt.grid()
    plt.legend(loc='best')
    plt.pause(3)
    plt.plot(t, sol[:, 2], 'g', label='theta2(t)')
    plt.legend(loc='best')
    plt.pause(3)
    plt.savefig('plot_theta2vstpen_L1{}L2{}m1{}m2{}y01{}n.png'.format(L1, L2,m1,m2,y01), dpi=180)
    plt.close()

    plt.plot(t, sol[:, 1], 'b', label='omega1(t)')
    plt.xlabel('t')
    plt.xlim(0,100)
    #plt.ylim(-1,1)
    plt.grid()
    plt.legend(loc='best')
    plt.pause(3)
    plt.plot(t, sol[:, 3], 'g', label='omega2(t)')
    plt.legend(loc='best')
    plt.pause(3)
    plt.savefig('plot_omega2vstpen_L1{}L2{}m1{}m2{}y01{}n.png'.format(L1, L2,m1,m2,y01), dpi=180)
    plt.close()
    """

    L1=L1
    L2=L2
    g=9.8
    m1=m1
    m2=m2
    M=2*m2/(m1+4*m2)
    M1=(m1+2*m2)/(m1+4*m2)
    L=L2/L1
    omegaJ=g/L1
    y01=y01
    y0 = [y01, 0.0,0, 0.0]
    t = np.linspace(0, 1000, 4001)
    #sol = odeint(dualPendulum, y0, t, args=(L1, L2,g,m1,m2))
    #sol = odeint(doublePendulum, y0, t, args=(L1, L2,g,m1,m2))
    sol = odeint(doublePendulum2, y0, t, args=(L, omegaJ,M,M1))

    x1, p1,x2,p2 = sol.T[0],sol.T[1], sol.T[2], sol.T[3]
    plt.plot(x1, p1, ".", markersize=4)
    plt.pause(3)
    #plt.savefig('plot_xp1Pen2_L{}M{}omegaJ{}y01{}n.png'.format(L, omegaJ,M,M1,y01), dpi=180)
    plt.savefig('plot_xp1Pen2_L1{}L2{}m1{}m2{}y01{}n.png'.format(L1, L2,m1,m2,y01), dpi=180)
    plt.close()
    plt.plot(x2, p2, ".", markersize=4)
    plt.pause(3)
    plt.savefig('plot_xp2Pen2_L{}M{}omegaJ{}y01{}n.png'.format(L, omegaJ,M,y01),dpi=180)
    plt.close()
    """
    plt.plot(t, sol[:, 0], 'b', label='theta1(t)')
    plt.xlabel('t')
    plt.xlim(0,100)
    #plt.ylim(-0.5,0.5)
    plt.grid()
    plt.legend(loc='best')
    plt.pause(3)
    plt.plot(t, sol[:, 2], 'g', label='theta2(t)')
    plt.legend(loc='best')
    plt.pause(3)
    plt.savefig('plot_theta2vstPen2_L{}M{}omegaJ{}y01{}n.png'.format(L, omegaJ,M,y01), dpi=180)
    plt.close()

    plt.plot(t, sol[:, 1], 'b', label='omega1(t)')
    plt.xlabel('t')
    plt.xlim(0,100)
    #plt.ylim(-1,1)
    plt.grid()
    plt.legend(loc='best')
    plt.pause(3)
    plt.plot(t, sol[:, 3], 'g', label='omega2(t)')
    plt.legend(loc='best')
    plt.pause(3)
    plt.savefig('plot_omega2vstPen2_L{}M{}omegaJ{}y01{}n.png'.format(L, omegaJ,M,y01), dpi=180)
    plt.close()
    """


# 2D Newton's method
import numpy as np

def f(x):
    x0,x1 = x[0,0],x[1,0]
    return np.array([[4*x0-2*x0*x1],[2*x1+x0*x1-2*x1**2]])

def JI(x):
    x0,x1 = x[0,0],x[1,0]
    d = (4-2*x1)*(2-x0-4*x1)+2*x0*x1
    return (1/d)*np.array([[2-x0-4*x1,2*x0],[-x1,4-2*x0]])

x0 = float(input("x0: "))
x1 = float(input("x1: "))
x = np.array([[x0],[x1]])

N = 20
for i in range(N):
    x = x - JI(x) @ f(x)
    if (i > (N-10)):
        print("%4d: (%0.8f, %0.8f)" % (i, x[0,0],x[1,0]))


import numpy as np

def convert_meter_to_latlon_deg(lat_deg):
    # Scaling factor is 1 / 111.32 km, hence approximating a spherical Earth
    # See https://en.wikipedia.org/wiki/Decimal_degrees#Precision
    m_to_deg_lat = 1 / 111320.
    m_to_deg_lon = 1 / (111320. * np.cos(np.deg2rad(lat_deg)))
    return m_to_deg_lat, m_to_deg_lon

def convert_resolution_from_meters_to_deg(position_lat, gsd_metric):
    m_to_deg_lat, m_to_deg_lon = convert_meter_to_latlon_deg(position_lat)
    gsd_deg_lat = gsd_metric * m_to_deg_lat
    gsd_deg_lon = gsd_metric * m_to_deg_lon
    return gsd_deg_lon, gsd_deg_lat


from math import sqrt
from pprint import pprint
import numpy as np

#Cholesky
def cholesky(A):
    rows = len(A)
    columns = len(A[0])
    Z = np.zeros((rows,1), float)
    X = np.zeros((rows, 1), float)
    #X = np.array([[1],[1],[1],[1]])
    #Z = np.array([[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1]])
    L = np.zeros((rows,columns), float)
    U = np.zeros((rows,columns), float)

    b = np.array([[1],[1],[1],[1]])

    #L = np.zeros((rows,columns), float)
    #print(L)

    for k in range(0, rows):
        suma = 0.0
        for p in range(0, k):
            suma = suma + L[k][p]*U[p][k]
        L[k][k] = sqrt(A[k][k] - suma)
        U[k][k] = L[k][k]

        for i in range(k, rows):
            suma = 0
            for p in range(0, k):
                suma = suma + L[i][p]*U[p][k]
            L[i][k] = (A[i][k] - suma)/L[k][k]

        for j in range(k, rows):
            suma = 0
            for p in range(0, k):
                suma = suma + L[k][p]*U[p][j]
            U[k][j] = (A[k][j] - suma)/L[k][k]

    Lb = np.concatenate([L, b], axis=1)

    for i in range(0, rows):
        suma = 0
        for p in range(0, i):
            suma = suma + Lb[i][p]*Z[p][0]
        Z[i][0] = (Lb[i][rows] - suma)/Lb[i][i]

    Lu = np.concatenate([U, Z], axis=1)

    for i in range(rows-1,-1, -1):
        suma = 0
        for p in range(i, rows):
            suma = suma + Lu[i][p]*X[p]
        X[i] = (Lu[i][rows] - suma)/Lu[i][i]

    return L, U, Z, X

if __name__ == "__main__":
    A = [[4, -1, 0, 3], [1, 15.5, 3, 8], [0, -1.3, -4, 1.1],[14, 5, -2, 30]]
    C = [[5,4,3,2], [2,4,1,1], [3,4,5,1],[2,3,3,7]]
    #[4, -1, 0, 3; 1, 15.5, 3, 8; 0, -1.3, -4, 1.1; 14, 5, -2, 30]
    if((np.all(np.linalg.eigvals(C) > 0) == True)):
        L,U, Z, X = cholesky(C)
        print ("A:")
        pprint(C)

        print ("L:")
        pprint(L)
        print("U: ")
        pprint(U)
        print("X: ")
        print(X)
        print("Z: ")
        print(Z)
    else:
        print("La matriz no se puede operar, pues no esta definida positivamente.")


import numpy as np

def sigmoid(x):
  return 1 / (1 + np.exp(-x))

def derivated_sigmoid(x):
    return x*(1-x)

x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0, 1, 1, 0]]).T

assert np.shape(x)[0] == np.shape(y)[0]

w = 2*np.random.random((np.shape(x)[1], 1))-1
b =1
lr = 0.0001
epoc = 100000
for i in range(epoc):
    #forward propogation
    layer_1 = x
    y_hat = sigmoid((np.dot(layer_1, w) + b))

    #backpropogation
    error = y - y_hat
    weight_updates = lr*error*derivated_sigmoid(y_hat)
    w += np.dot(x.T, weight_updates)
    b += np.dot(lr,error)

print("--Output--")
print(y_hat)


import numpy as np
from scipy.stats import chi2

def chi_square(Ns, Ni):
    """ Return $\chi^2_{\text stat}$ value and the associate p-value"""

    NsNi = np.ndarray((len(Ns), 2))
    NsNi[:, 0] = Ns
    NsNi[:, 1] = Ni

    length = len(Ns)
    Ns = sum(Ns)
    Ni = sum(Ni)

    X2 = 0.
    for Nsj, Nij in NsNi:
        X2 += (Nsj*Ni - Nij*Ns)**2 / (Nsj + Nij)

    X2 *= 1.0/(Ns*Ni)
    rv = chi2(length - 1)
    return 1.0 - rv.cdf(X2)

def calculate_central_age(Ns, Ni, zeta, seZeta, rhod, Nd, sigma=0.15):
    """Function to calculate central age."""

    Ns = np.array(Ns)
    Ni = np.array(Ni)

    # We just replace 0 counts with a low value, the age will be rounded to
    # 2 decimals. That should take care of the zero count issue.
    Ns = np.where(Ns == 0, 1e-10, Ns) # Do this to account for 0 track counts
    Ni = np.where(Ni == 0, 1e-10, Ni) # Do this to account for 0 track counts

    # Calculate mj
    LAMBDA = 1.55125e-4
    G = 0.5
    m = Ns + Ni
    p = Ns / m

    theta = np.sum(Ns) / np.sum(m)

    for i in range(0, 30):
        w = m / (theta * (1 - theta) + (m - 1) * theta**2 * (1 - theta)**2 * sigma**2)
        sigma = sigma * np.sqrt(np.sum(w**2 * (p - theta)**2) / np.sum(w))
        theta = np.sum(w * p) / np.sum(w)

    t = (1.0 / LAMBDA) * np.log( 1.0 + G * LAMBDA * zeta * rhod * (theta) / (1.0 - theta))
    se = np.sqrt(1 / (theta**2 * (1.0 - theta)**2 * np.sum(w)) + 1.0 / Nd + (seZeta / zeta)**2) * t

    return {"Central": np.round(t, 2), "se": np.round(se, 2), "sigma": np.round(sigma, 2)}

def calculate_pooled_age(Ns, Ni, zeta, seZeta, rhod, Nd):

    # We just replace 0 counts with a low value, the age will be rounded to
    # 2 decimals. That should take care of the zero count issue.
    Ns = np.where(Ns == 0, 1e-10, Ns) # Do this to account for 0 track counts
    Ni = np.where(Ni == 0, 1e-10, Ni) # Do this to account for 0 track counts

    Ns = np.sum(Ns)
    Ni = np.sum(Ni)

    LAMBDA = 1.55125e-4
    G = 0.5
    t = 1.0 / LAMBDA * np.log(1.0 + G * LAMBDA * zeta * rhod * Ns / Ni)
    se = t * (1.0 / Ns + 1.0 / Ni + 1 / Nd + seZeta**2)**0.5

    return {"Pooled Age": np.round(t, 2), "se": np.round(se, 2)}

def calculate_ages(Ns, Ni, zeta, seZeta, rhod, Nd):

    # We just replace 0 counts with a low value, the age will be rounded to
    # 2 decimals. That should take care of the zero count issue.
    Ns = np.where(Ns == 0, 1e-10, Ns) # Do this to account for 0 track counts
    Ni = np.where(Ni == 0, 1e-10, Ni) # Do this to account for 0 track counts

    Ns = np.array(Ns)
    Ni = np.array(Ni)
    Nd = np.array(Nd)

    def true_divide(val):
        with np.errstate(divide='ignore', invalid='ignore'):
            val = np.true_divide(1.0, val)
            val[val == np.inf] = 0
            return np.nan_to_num(val)

    # Calculate mj
    LAMBDA = 1.55125e-4
    G = 0.5
    t = 1.0 / LAMBDA * np.log(1.0 + G * LAMBDA * zeta * rhod * Ns / Ni)

    se = (true_divide(Ns) + true_divide(Ni) + 1.0 / Nd + seZeta**2)**0.5
    se *= t

    return {"Age(s)": np.round(t, 2), "se(s)": np.round(se, 2)}