<filename>tests/opt_matrix_test.py
"""
Test of the basic optimization functionality by basic matrix problems.

Matrices are the simplest tensors.  Here we have simple matrix examples that are
very easy to think about.  All the core optimization strategies should first be
tested here.

"""

import pytest
from drudge import Range, Drudge
from sympy import symbols, IndexedBase

from gristmill import optimize, verify_eval_seq, get_flop_cost

@pytest.fixture
def three_ranges(spark_ctx):
    """Fixture with three ranges.

    This drudge has three ranges, named M, N, L with sizes m, n, and l,
    respectively.  It also has a substitution dictionary setting n = 2m and l
    = 3m.

    """

    dr = Drudge(spark_ctx)

    # The sizes.
    m, n, l = symbols('m n l')

    # The ranges.
    m_range = Range('M', 0, m)
    n_range = Range('N', 0, n)
    l_range = Range('L', 0, l)

    dr.set_dumms(m_range, symbols('a b c d e f g'))
    dr.set_dumms(n_range, symbols('i j k l m n'))
    dr.set_dumms(l_range, symbols('p q r'))
    dr.add_resolver_for_dumms()
    dr.set_name(m, n, l)

    dr.substs = {
        n: m * 2,
        l: m * 3
    }

    return dr

#
# Test of core functionality
# --------------------------
#

def test_matrix_chain(three_ranges):
    """Test a basic matrix chain multiplication problem.

    Here a very simple matrix chain multiplication problem with three
    matrices are used to test the factorization facilities.  In this simple
    test, we will have three matrices :math:`x`, :math:`y`, and :math:`z`,
    which are of shapes :math:`m\\times n`, :math:`n \\times l`, and :math:`l
    \\times m` respectively. In the factorization, we are going to set
    :math:`n = 2 m` and :math:`l = 3 m`.

    """

    dr = three_ranges
    p = dr.names
    m, n, l = p.m, p.n, p.l

    # The indexed bases.
    x = IndexedBase('x', shape=(m, n))
    y = IndexedBase('y', shape=(n, l))
    z = IndexedBase('z', shape=(l, m))

    target_base = IndexedBase('t')
    target = dr.define_einst(
        target_base[p.a, p.b],
        x[p.a, p.i] * y[p.i, p.p] * z[p.p, p.b]
    )

    # Perform the factorization.
    targets = [target]
    stats = {}
    eval_seq = optimize(targets, substs=dr.substs, stats=stats)
    assert stats['Number of nodes'] < 2 ** 3
    assert len(eval_seq) == 2

    # Check the correctness.
    assert verify_eval_seq(eval_seq, targets)

    # Check the cost.
    cost = get_flop_cost(eval_seq)
    leading_cost = get_flop_cost(eval_seq, leading=True)
    expected_cost = 2 * l * m * n + 2 * m ** 2 * n
    assert cost == expected_cost
    assert leading_cost == expected_cost

@pytest.mark.parametrize('rand_constr', [True, False])
def test_shallow_matrix_factorization(three_ranges, rand_constr):
    """Test a shallow matrix multiplication factorization problem.

    In this test, there are four matrices involved, X, Y, U, and V.  The final
    expression to optimize is mathematically

    .. math::

        (2 X - Y) * (2 U + V)

    Here, the expression is to be given in its expanded form originally, and
    we test if it can be factorized into something similar to what we have
    above. Here we have the signs and coefficients to have better code
    coverage for these cases.  This test case more concentrates on the
    horizontal complexity in the input.

    """

    #
    # Basic context setting-up.
    #

    dr = three_ranges
    p = dr.names

    m = p.m
    a, b, c, d = p.a, p.b, p.c, p.d

    # The indexed bases.
    x = IndexedBase('X')
    y = IndexedBase('Y')
    u = IndexedBase('U')
    v = IndexedBase('V')
    t = IndexedBase('T')

    # The target.
    target = dr.define_einst(
        t[a, b],
        4 * x[a, c] * u[c, b] + 2 * x[a, c] * v[c, b]
        - 2 * y[a, c] * u[c, b] - y[a, c] * v[c, b]
    )
    targets = [target]

    # The actual optimization.
    res = optimize(targets, rand_constr=rand_constr)
    assert len(res) == 3

    # Test the correctness.
    assert verify_eval_seq(res, targets, simplify=False)

    # Test the cost.
    cost = get_flop_cost(res)
    leading_cost = get_flop_cost(res, leading=True)
    assert cost == 2 * m ** 3 + 2 * m ** 2
    assert leading_cost == 2 * m ** 3
    cost = get_flop_cost(res, ignore_consts=False)
    assert cost == 2 * m ** 3 + 4 * m ** 2

def test_deep_matrix_factorization(three_ranges):
    """Test a basic matrix multiplication factorization problem.

    Similar to the shallow factorization test, the final expression to optimize
    is mathematically

    .. math::

        (X - 2 Y) * U * V

    Different from the shallow test case, here we concentrate more on the
    treatment of depth complexity in the input.  The sum intermediate needs to
    be factored again.

    """

    #
    # Basic context setting-up.
    #

    dr = three_ranges
    p = dr.names

    m = p.m
    a, b, c, d = p.a, p.b, p.c, p.d

    # The indexed bases.
    x = IndexedBase('X')
    y = IndexedBase('Y')
    u = IndexedBase('U')
    v = IndexedBase('V')
    t = IndexedBase('T')

    # The target.
    target = dr.define_einst(
        t[a, b], x[a, c] * u[c, d] * v[d, b] - 2 * y[a, c] * u[c, d] * v[d, b]
    )
    targets = [target]

    # The actual optimization.
    res = optimize(targets)
    assert len(res) == 3

    # Test the correctness.
    assert verify_eval_seq(res, targets, simplify=True)

    # Test the cost.
    cost = get_flop_cost(res)
    leading_cost = get_flop_cost(res, leading=True)
    assert cost == 4 * m ** 3 + m ** 2
    assert leading_cost == 4 * m ** 3
    cost = get_flop_cost(res, ignore_consts=False)
    assert cost == 4 * m ** 3 + 2 * m ** 2

    # Test disabling summation optimization.
    res = optimize(targets, opt_sum=False)
    assert verify_eval_seq(res, targets, simplify=True)
    new_cost = get_flop_cost(res, ignore_consts=False)
    assert new_cost - cost != 0

def test_factorization_of_two_products(three_ranges):
    """Test a sum where we have two disjoint products.

    The final expression to optimize is

    .. math::

        2 X (3 U + 5 V) - 7 Y (11 U + 13 V) + 17 T

    In this test case, we concentrate on the handling of multiple disjoint
    possible factorization inside a single sum.

    """

    #
    # Basic context setting-up.
    #

    dr = three_ranges
    p = dr.names

    m = p.m
    a, b, c = p.a, p.b, p.c

    # The indexed bases.
    x = IndexedBase('X')
    y = IndexedBase('Y')
    u = IndexedBase('U')
    v = IndexedBase('V')
    t = IndexedBase('T')

    # The target.
    target = dr.define_einst(
        IndexedBase('r')[a, b],
        6 * x[a, c] * u[c, b] + 10 * x[a, c] * v[c, b]
        - 77 * y[a, c] * u[c, b] - 91 * y[a, c] * v[c, b]
        + 17 * t[a, b]
    )
    targets = [target]

    # The actual optimization.
    res = optimize(targets)
    assert len(res) == 3
    assert res[-1].n_terms == 3

    # Test the correctness.
    assert verify_eval_seq(res, targets, simplify=True)

    # Test the cost.
    cost = get_flop_cost(res)
    assert cost == 4 * m ** 3 + 4 * m ** 2

def test_general_matrix_problem(three_ranges):
    """Test optimization of a very general matrix computation.

    This is a very general problem trying to test and illustrate many different
    aspects of the optimization, parenthesization, recursion to newly-formed
    factors, and sum of disjoint factorizations.  The target to evaluate reads

    .. math::

        (A + 2B) (3C + 5D) (7E + 13F) + (17P + 19Q) (23X + 29Y)

    where

    - A, B, P, Q is over ranges M, L
    - C, D is over M, N
    - E, F is over N, L
    - X, Y is over L, N

    """

    dr = three_ranges
    p = dr.names

    m, n, l = p.m, p.n, p.l
    a, b = p.a, p.b
    i = p.i
    p = p.p

    f1 = IndexedBase('A')[a, i] + 2 * IndexedBase('B')[a, i]
    f2 = 3 * IndexedBase('C')[i, p] + 5 * IndexedBase('D')[i, p]
    f3 = 7 * IndexedBase('E')[p, b] + 13 * IndexedBase('F')[p, b]
    f4 = 17 * IndexedBase('P')[a, i] + 19 * IndexedBase('Q')[a, i]
    f5 = 23 * IndexedBase('X')[i, b] + 29 * IndexedBase('Y')[i, b]

    target = dr.define_einst(
        IndexedBase('R')[a, b],
        (f1 * f2 * f3 + f4 * f5).expand()
    )
    targets = [target]
    assert target.n_terms == 12
    assert get_flop_cost(targets).subs(dr.substs) == (
            144 * m ** 4 + 16 * m ** 3 + 11 * m ** 2
    )

    eval_seq = optimize(targets, substs=dr.substs)

    # Check the correctness.
    assert verify_eval_seq(eval_seq, targets)
    assert len(eval_seq) == 7
    cost = get_flop_cost(eval_seq)
    assert cost.subs(dr.substs) == 20 * m ** 3 + 16 * m ** 2

#
# Test of special cases
# ---------------------
#

@pytest.mark.xfail(reason='Flaky until the following test is fixed')
def test_disconnected_outer_product_factorization(three_ranges):
    """Test optimization of expressions with disconnected outer products.
    """

    dr = three_ranges
    p = dr.names

    m = p.m
    a, b, c, d, e = p.a, p.b, p.c, p.d, p.e

    # The indexed bases.
    u = IndexedBase('U')
    x = IndexedBase('X')
    y = IndexedBase('Y')
    z = IndexedBase('Z')
    t = IndexedBase('T')

    # The target.
    target = dr.define_einst(
        t[a, b],
        u[a, b] * z[c, e] * x[e, c] + u[a, b] * z[c, e] * y[e, c]
    )
    targets = [target]

    # The actual optimization.
    res = optimize(targets)
    assert len(res) == 3

    # Test the correctness.
    assert verify_eval_seq(res, targets, simplify=False)

    # Test the cost.
    cost = get_flop_cost(res)
    leading_cost = get_flop_cost(res, leading=True)
    assert cost == 4 * m ** 2
    assert leading_cost == 4 * m ** 2

@pytest.mark.xfail(reason='TODO: Needs investigation')
def test_factorization_needing_canonicalization(three_ranges):
    """Test a simple factorization needing canonicalization.

    The inability of gristmill to fully optimize this test is the ultimate
    reason why the above test is flaky.
    """

    dr = three_ranges
    p = dr.names

    m = p.m
    a, b = p.a, p.b

    x = IndexedBase('X')
    y = IndexedBase('Y')
    z = IndexedBase('Z')
    t = Symbol('T')

    # The target.
    target = dr.define_einst(
        t, x[b, a] * z[a, b] + y[a, b] * z[b, a]
    )
    targets = [target]

    # The actual optimization.
    res = optimize(targets)
    assert len(res) == 2

    # Test the correctness.
    assert verify_eval_seq(res, targets, simplify=False)

def test_optimization_of_common_terms(three_ranges):
    """Test optimization of common terms in summations.

    In this test, there are just two matrices involved, X, Y.  The target reads

    .. math::

        T[a, b] = X[a, b] - X[b, a] + 2 Y[a, b] - 2 Y[b, a]

    Ideally, it should be evaluated as,

    .. math::

        I[a, b] = X[a, b] + 2 Y[a, b]
        T[a, b] = I[a, b] - I[b, a]

    or,

    .. math::

        I[a, b] = X[a, b] - 2 Y[b, a]
        T[a, b] = I[a, b] - I[b, a]

    """

    #
    # Basic context setting-up.
    #
    dr = three_ranges
    p = dr.names

    a, b, c, d = p.a, p.b, p.c, p.d

    # The indexed bases.
    x = IndexedBase('x')
    y = IndexedBase('y')
    t = dr.define_einst(
        IndexedBase('t')[a, b],
        x[a, b] - x[b, a] + 2 * y[a, b] - 2 * y[b, a]
    )

    targets = [t]
    eval_seq = optimize(targets)
    assert len(eval_seq) == 2
    verify_eval_seq(eval_seq, targets)

    # Check the result when the common symmetrization optimization is disabled.
    eval_seq = optimize(targets, opt_symm=False)
    assert len(eval_seq) == 1
    verify_eval_seq(eval_seq, targets)

def test_eval_compression(three_ranges):
    """Test compression of optimized evaluations.

    Here we have two targets,

    .. math::

        U X V + U Y V

    and

    .. math::

        U X W + U Y W

    and it has been deliberately made such that the multiplication with U
    should be carried out first.  Then after the factorization of U, we have
    an intermediate U (X + Y), which is a sum of a single product
    intermediate.  This test succeeds when we have two intermediates only,
    without the unnecessary addition of a single product.

    """

    # Basic context setting-up.
    dr = three_ranges
    p = dr.names

    a = p.a  # Small range
    i, j, k = p.i, p.j, p.k  # Big range

    # The indexed bases.
    u = IndexedBase('U')
    v = IndexedBase('V')
    w = IndexedBase('W')
    x = IndexedBase('X')
    y = IndexedBase('Y')

    s = IndexedBase('S')
    t1 = IndexedBase('T1')
    t2 = IndexedBase('T2')

    # The target.
    s_def = dr.define_einst(
        s[i, j],
        u[i, k] * x[k, j] + u[i, k] * y[k, j]
    )
    targets = [dr.define_einst(
        t1[i, j],
        s_def[i, a] * v[a, j]
    ), dr.define_einst(
        t2[i, j],
        s_def[i, a] * w[a, j]
    )]

    # The actual optimization.
    res = optimize(targets, substs=dr.substs)
    assert len(res) == 4

    # Test the correctness.
    assert verify_eval_seq(res, targets, simplify=False)

@pytest.mark.parametrize('res_at_end', [True, False])
def test_interleaving_res_interm(three_ranges, res_at_end):
    r"""Test the interleaving of results and intermediates.

    Here we have intermediate,

    .. math::

        I = X Y

    and result

    .. math::
        R1 = I * 2

    and result

    .. math::

        R2 = I * tr(R1)

    """

    dr = three_ranges
    p = dr.names
    a, b, c, d, e = p.a, p.b, p.c, p.d, p.e

    x = IndexedBase('X')
    y = IndexedBase('Y')
    r1 = IndexedBase('R1')
    r2 = IndexedBase('R2')

    r1_def = dr.define_einst(r1[a, b], x[a, c] * y[c, b] * 2)
    r2_def = dr.define_einst(r2[a, b], x[a, c] * y[c, b] * x[d, e] * y[e, d])

    origs = [r1_def, r2_def]
    eval_seq = optimize(origs, res_at_end=res_at_end)

    assert verify_eval_seq(eval_seq, origs)

    assert len(eval_seq) == 4
    if res_at_end:
        assert eval_seq[2].base == r1
    else:
        assert eval_seq[1].base == r1
    assert eval_seq[3].base == r2

    for i in eval_seq:
        assert i.if_interm == (not (str(i.base)[0] == 'R'))
        continue


import math
from fractions import Fraction
from django.contrib import admin
from products.models import Product, Image

class ProductLabelInlineAdmin(admin.TabularInline):
    """Selection to select labels for a product."""
    model = Product.labels.through

class ImageInlineAdmin(admin.TabularInline):
    """Selection to select images for a product."""
    model = Image

class ProductAdmin(admin.ModelAdmin):
    list_display = ('id','name','brand','active','stock')
    fields = ('id','name','review_calculated','stock','price','active', 'category','brand','description','info','weight','contents', 'label_info')
    readonly_fields = ('id','info','review_calculated','label_info')
    list_filter = ('brand',)
    list_editable = ('active','stock')
    search_fields = ('name','id','brand__name')
    inlines = (ProductLabelInlineAdmin, ImageInlineAdmin)

    def has_delete_permission(self, request, obj=None):
        return False

    def info(self, obj):
        return "If the product is not cereal please leave weight & contents fields empty."

    def label_info(self, obj):
        return "Each label can only be selected once."

class ProductDiscountAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'percentage_off', 'price', 'new_price')
    list_display_links = None
    fields = ['name', 'brand', 'description', 'contents', 'weight', 'price', 'category', 'active', 'stock',
              'percentage_off', 'discounted_price']
    readonly_fields = ('id',)
    list_filter = ('brand',)
    list_editable = ('percentage_off',)
    search_fields = ('name', 'id', 'brand__name')

    def has_delete_permission(self, request, obj=None):
        return False

    def new_price(self, obj):
        """Returns and sets the discounted price for a given product object."""

        # If percentage_off exceeds 100, reset the price
        if obj.percentage_off > 100 or obj.percentage_off <= 0:
            obj.percentage_off = 0
            obj.discounted_price = None
            obj.save()

            return obj.price

        # Calculate the discounted price
        price = Fraction(obj.price)
        percentage_off = Fraction(obj.percentage_off)
        discount = (percentage_off / 100) * price
        final_price = Fraction(price - discount)
        new = float(Fraction(math.floor(final_price*100), 100))

        # Save and set the discounted price
        obj.discounted_price = new
        obj.save()

        return new

    def has_add_permission(self, request):
        return False

class ProductDiscount(Product):
    """Proxy model so we can use the Product model
    on two different admin pages."""
    class Meta:
        proxy = True

# admin.site.register(Label, LabelAdmin)
admin.site.register(Product, ProductAdmin)
admin.site.register(ProductDiscount, ProductDiscountAdmin)


#! usr/bin/env python
import numpy as np
import seaborn as sns
import scipy as sp
import functools
import numpy as np
from scipy.stats import multivariate_normal
import scipy.stats as stats
import time
import scipy as scipy
import sys
import pandas as pd
from scipy.stats import norm
from numpy import linalg as la
import pandas as pd
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
import itertools
__author__ = '<NAME>'

class IBO(object):
    """
    IBO: Intelligent Bayesian OPtimization
    A class to perform Bayesian Optimization on a 1D or 2D domain.
    Can either have an objective function to maximize or a true function
    to maximize"""

    def __init__(self, kernel = 'squared_kernel'):
        """Define the parameters for the bayesian optimization.

        The train points should be x,y coordinate that you already know about your
        function"""
        if kernel == 'squared_kernel':
            self.kernel = self.__squared_kernel__
        elif kernel == 'matern':
            self.kernel = self.__matern_kernel__

    def fit(self, train_points_x, train_points_y,
            test_domain, train_y_func, y_func_type = 'real',
            samples = 10 , test_points_x = None, test_points_y = None,
            model_train_points_x = None, model_train_points_y = None,
            covariance_noise = 5e-5, n_posteriors = 30, kernel_params = None,
            model_obj = GradientBoostingRegressor,
            verbose = True):
        """Define the parameters for the GP.
        PARAMS:
        train_points_x, - x coordinates to train on
        train_points_y,  -  resulting output from the function, either objective or
        true function
        test_domain - the domain to test
        test_points_y - If using ab objective function, this is from the
            train test split data
        test_points_x = if using an objective function, this is from the
            train test split
        model - the model to fit for use with the objective function. Currently
            works with Gradient Boosting
        y_func_type - either the real function or the objective function.
            The objective function implemented in negative MSE (since BO is
            a maximization procedure)
        verbose = Whether to print out the points Bayesian OPtimization is
            picking
        train_y_func - This can either be an objective function or a true function
        kernel_params: dictionary of {'length':value} for squaredkernel
        model_train_points: the training points for the objective function
        """

        try:
            type(train_points_x).__module__ == np.__name__
            type(train_points_y).__module__ == np.__name__
        except Exception as e:
            print(e)
            return ' You need to input numpy types'
        # Store the training points
        self.train_points_x = train_points_x
        self.train_points_y = train_points_y
        self.test_domain = test_domain

        # setup the kernel parameters
        if kernel_params != None:
            self.squared_length = kernel_params['rbf_length']
        else:
            self.squared_length = None

        # Y func can either be an objective function, or the true underlying func.
        if y_func_type == 'real':
            self.train_y_func = train_y_func
        elif y_func_type == 'objective':
            if model_obj == None:
                return ' you need to pass in a model (GradientBoostingRegressor)'

            # Only if using an objective function, from the 'test' split
            self.test_points_x = test_points_x
            self.test_points_y = test_points_y
            self.model_train_points_x  = model_train_points_x
            self.model_train_points_y = model_train_points_y
            # model to train and fit
            self.model = model_obj
            self.train_y_func = self.hyperparam_choice_function

        # store the testing parameters
        self.covariance_noise = covariance_noise
        self.n_posteriors = n_posteriors
        self.samples = samples
        self.verbose = verbose

        if self.train_points_x.shape[1] ==1: # one dimension
            self.dimensions ='one'
        elif self.train_points_x.shape[1] ==2:
            self.dimensions = 'two'
        else:
            print('Either you entered more than two dimensions, \
                  or not a numpy array.')
            print(type(self.train_points_x))
        # create the generator
        self.bo_gen = self.__sample_from_function__(verbose=self.verbose)

    def predict(self):
        """returns x_sampled_points, y_sampled_points, best_x, best_y"""

        x_sampled_points, y_sampled_points, sampled_var, \
            best_x, best_y, improvements, domain, mus = next(self.bo_gen)

        return x_sampled_points, y_sampled_points, best_x, best_y

    def maximize(self, n_steps=10, verbose = None):
        """For the n_steps defined, find the best x and y coordinate
        and return them.
        Verbose controls whether to print out the points being sampled"""
        verbose_ = self.verbose
        self.samples = n_steps
        bo_gen = self.__sample_from_function__(verbose = verbose_)
        for _ in range(self.samples):
            x_sampled_points, y_sampled_points, sampled_var, \
                best_x, best_y, improvements, domain, mus = next(self.bo_gen)

        self.best_x = best_x
        self.best_y = best_y
        # return the best PARAMS
        return best_x, best_y

    def __test_gaussian_process__(self,  return_cov = False,
                              return_sample = False):
        """Test one new point in the Gaussian process or an array of points
        Returns the mu, variance, as well as the posterior vector.
        Improvements is the expected improvement for each potential test point.
        Domain, is the domain over which you are searching.

        Return cov = True will return the full covariance matrix.

        If return_sample= True
        returns samples ( a vector) from the
        informed posterior and the uninformed prior distribution

        Covariance diagonal noise is used to help enforce positive definite matrices

        """

        # Update the covaraince matrices
        self.covariance_train_train = self.kernel(self.train_points_x,
                                self.train_points_x, train=True)
        self.covariance_test_train  = self.kernel(self.test_domain,
                                                  self.train_points_x)
        self.covariance_test_test  = self.kernel(self.test_domain,
                                                 self.test_domain)

        # Use cholskey decomposition to increase speed for calculating mean
        try :# First try,
            L_test_test = np.linalg.cholesky(self.covariance_test_test + \
                    self.covariance_noise * np.eye(len(self.covariance_test_test)))
            L_train_train = np.linalg.cholesky(self.covariance_train_train + \
                    self.covariance_noise * np.eye(len(self.covariance_train_train)))
            Lk = np.linalg.solve(L_train_train, self.covariance_test_train.T)
            mus = np.dot(Lk.T, np.linalg.solve(L_train_train,
                                              self.train_points_y)).reshape(
                                    (len(self.test_domain),))
            # Compute the standard deviation so we can plot it
            s2 = np.diag(self.covariance_test_test) - np.sum(Lk**2, axis=0)
            stdv = np.sqrt(abs(s2))

        except Exception as e:
            print(e)#LinAlgError: # In case the covariance matrix is not positive definite
            # Find the near positive definite matrix to decompose
            decompose_train_train = self.nearestPD(
                self.covariance_train_train + self.covariance_noise * np.eye(
                    len(self.train_points_x)))
            decompose_test_test = self.nearestPD(
                self.covariance_test_test + self.covariance_noise * np.eye(
                    len(self.test_domain)))

            # cholskey decomposition on the nearest PD matrix
            L_train_train = np.linalg.cholesky(decompose_train_train)
            L_test_test = np.linalg.cholesky(decompose_test_test)
            Lk = np.linalg.solve(L_train_train, self.covariance_test_train.T)
            mus = np.dot(Lk.T, np.linalg.solve(L_train_train,
                                self.train_points_y)).reshape((len(self.test_domain)),)
            # Compute the standard deviation so we can plot it
            s2 = np.diag(self.covariance_test_test) - np.sum(Lk**2, axis=0)
            stdv = np.sqrt(abs(s2))

    #         ##### FULL INVERSION ####
    #         mus = covariance_test_train  @ np.linalg.pinv(covariance_train_train) @ train_y_numbers
    #         s2 = covariance_test_test - covariance_test_train @ np.linalg.pinv(covariance_train_train ) \
    #                      @ covariance_test_train.T

        def sample_from_posterior(n_priors=3):
            """Draw samples from the prior distribution of the GP.
            len(test_x) is the number of samplese to draw.
            Resource: http://katbailey.github.io/post/gaussian-processes-for-dummies/.

            N-Posteriors / N-Priors tells the number of functions to samples from the dsitribution"""

            try: # try inside sample from posterior function
                L = np.linalg.cholesky(self.covariance_test_test +
                    self.covariance_noise * np.eye(
                        len(self.test_domain))- np.dot(Lk.T, Lk))
            except Exception as e:
                print(e)
                # Find the neareset Positive Definite Matrix
                near_decompose = self.nearestPD(self.covariance_test_test +
                    self.covariance_noise * np.eye(
                        len(self.test_domain)) - np.dot(Lk.T, Lk))
                L = np.linalg.cholesky(near_decompose.astype(float) )
                # within posterior
            # sample from the posterior
            f_post = mus.reshape(-1,1) + np.dot(L, np.random.normal(
                size=(len(self.test_domain), self.n_posteriors)))

            # Sample X sets of standard normals for our test points,
            # multiply them by the square root of the covariance matrix
            f_prior_uninformed = np.dot(L_test_test,
                    np.random.normal(size=(len(self.test_domain), n_priors)))
            # For the posterior, the columns are the vector for that function
            return (f_prior_uninformed, f_post)

        if return_cov == True:
            return y_pred_mean.ravel(), var_y_pred_diag.ravel(), var_y_pred

        if return_sample == True:
            f_prior, f_post = sample_from_posterior()
            return mus.ravel(), s2.ravel(), f_prior, f_post
        else:
            return mus.ravel(), s2.ravel()

    def __sample_from_function__(self, verbose=None):
        """Sample N times from the unknown function and for each time find the
        point that will have the highest expected improvement (find the maxima of the function).
        Verbose signifies if the function should print out the points where it is sampling

        Returns a generator of x_sampled_points, y_sampled_points, vars_, best_x, best_y, \
                    list_of_expected_improvements, testing_domain, mus
              for improvements. Mus and Vars are the mean and var for each sampled point
               in the gaussian process.

        Starts off the search for expected improvement with a coarse search and then hones in on
        the domain the the highest expected improvement.

        Note - the y-function can EITHER by the actual y-function (for evaluation
        purposes, or an objective function
        (i.e. - RMSE))"""
        verbose = self.verbose

        # for plotting the points sampled
        x_sampled_points = []
        y_sampled_points = []
        best_x = self.train_points_x[np.argmax(self.train_points_y ),:]
        best_y =self.train_points_y [np.argmax(self.train_points_y ),:]

        for i in range(self.samples):
            if i == 0:
                if self.train_points_x .shape[1]==1: ## one dimensional case
                    testing_domain = np.array([self.test_domain]).reshape(-1,1)
                else:
                    testing_domain = self.test_domain

                # find the next x-point to sample
                mus, vars_, prior, post = self.__test_gaussian_process__(
                    return_sample = True)

                sigmas_post = np.var(post,axis=1)
                mus_post = np.mean(post,axis=1)
                # get the expected values from the posterior distribution
                list_of_expected_improvements = self.expected_improvement(
                    mus_post, sigmas_post ,best_y)

                max_improv_x_idx = np.argmax(np.array(
                    list_of_expected_improvements))
                #print(max_improv_x_idx,'max_improv_x_idx')
                max_improv_x = testing_domain[max_improv_x_idx]
                # don't resample the same point
                c = 1
                while max_improv_x in x_sampled_points:
                    if c == 1:
                        if self.train_points_x .shape[1]==1:
                            sorted_points_idx = np.argsort(list(np.array(
                                list_of_expected_improvements)))
                        else:
                            sorted_points_idx = np.argsort(list(np.array(
                                list_of_expected_improvements)),axis=0)
                    c+=1
                    max_improv_x_idx = int(sorted_points_idx[c])
                    max_improv_x = testing_domain[max_improv_x_idx]
                    # only wait until we've gon through half of the list
                    if c > round(len(list_of_expected_improvements)/2):
                        max_improv_x_idx = int(
                            np.argmax(list_of_expected_improvements))
                        max_improv_x = testing_domain[max_improv_x_idx]
                        break
                if self.train_points_x.shape[1]==1:
                    max_improv_y = self.train_y_func(max_improv_x)
                else: # Two D
                    try: # see if we are passing in the actual function
                        max_improv_y = self.train_y_func(
                            max_improv_x[0], max_improv_x[1])
                    except: # we are passing the objective function in
                        max_improv_y = self.train_y_func(
                            max_improv_x[0], dimensions = 'two',
                            hyperparameter_value_two = max_improv_x[1])
                if max_improv_y > best_y: ## use to find out where to search next
                    best_y = max_improv_y
                    best_x = max_improv_x
                if verbose:
                    print(f"Bayesian Optimization just sampled point = {best_x}")
                    print(f"Best x (Bayesian Optimization) = {best_x},\
                         Best y = {best_y}")
                    # append the point to sample
                    x_sampled_points.append(max_improv_x)
                    y_sampled_points.append(max_improv_y)
                    # append our new the newly sampled point to the training data
                    self.train_points_x = np.vstack((self.train_points_x,
                                                     max_improv_x))
                    self.train_points_y = np.vstack((self.train_points_y,
                                                     max_improv_y))

                    yield x_sampled_points, y_sampled_points, vars_, best_x, best_y, \
                        list_of_expected_improvements, testing_domain, mus

                else:
                    # append the point to sample
                    x_sampled_points.append(max_improv_x)
                    y_sampled_points.append(max_improv_y)

                    # append our new the newly sampled point to the training data
                    self.train_points_x = np.vstack((self.train_points_x, max_improv_x))
                    self.train_points_y = np.vstack((self.train_points_y, max_improv_y))

                    yield x_sampled_points, y_sampled_points, vars_, best_x, best_y, \
                        list_of_expected_improvements, testing_domain, mus

            else:

                if self.train_points_x.shape[1]==1:
                    testing_domain = np.array([testing_domain]).reshape(-1,1)
                else:
                    testing_domain = self.test_domain

                mus, vars_, prior, post = self.__test_gaussian_process__(
                        return_sample = True)

                igmas_post  = np.var(post,axis=1)
                mus_post = np.mean(post,axis=1)
                # get the expected values from the posterior distribution
                list_of_expected_improvements = self.expected_improvement(
                    mus_post, sigmas_post ,best_y)
                max_improv_x_idx = np.argmax(list_of_expected_improvements)
                max_improv_x = testing_domain[max_improv_x_idx]
                # don't resample the same point
                c = 1
                while max_improv_x in x_sampled_points:
                    if c == 1:
                        if self.train_points_x .shape[1]==1:
                            sorted_points_idx = np.argsort(list(np.array(
                                list_of_expected_improvements)))
                        else:
                            sorted_points_idx = np.argsort(list(np.array(
                                list_of_expected_improvements)),axis=0)
                    c+=1
                    max_improv_x_idx = int(sorted_points_idx[c])
                    max_improv_x = testing_domain[max_improv_x_idx]
                    # only wait until we've gon through half of the list
                    if c > round(len(list_of_expected_improvements)/2):
                        max_improv_x_idx = int(
                            np.argmax(list_of_expected_improvements))
                        max_improv_x = testing_domain[max_improv_x_idx]
                        break
                if self.train_points_x .shape[1]==1:
                    max_improv_y = self.train_y_func(max_improv_x)
                else: # Two D
                    try: # see if we are passing in the actual function
                        max_improv_y = self.train_y_func(
                            max_improv_x[0], max_improv_x[1])

                    except: # we are passing the objective function in
                        max_improv_y = self.train_y_func(
                            max_improv_x[0], dimensions = 'two',
                            hyperparameter_value_two = max_improv_x[1])

                if max_improv_y > best_y: ## use to find out where to search next
                    best_y = max_improv_y
                    best_x = max_improv_x
                if verbose:
                    print(f"Bayesian Optimization just sampled point = {max_improv_x}")
                    print(f"Best x (Bayesian Optimization) = {best_x}, Best y = {best_y}")
                    # append the point to sample
                    x_sampled_points.append(max_improv_x)
                    y_sampled_points.append(max_improv_y)

                    # append our new the newly sampled point to the training data
                    self.train_points_x = np.vstack((self.train_points_x, max_improv_x))
                    self.train_points_y = np.vstack((self.train_points_y, max_improv_y))

                    yield x_sampled_points, y_sampled_points, vars_, best_x, best_y, \
                        list_of_expected_improvements, testing_domain, mus

                else:
                    # append the point to sample
                    x_sampled_points.append(max_improv_x)
                    y_sampled_points.append(max_improv_y)

                    # append our new the newly sampled point to the training data
                    self.train_points_x = np.vstack((self.train_points_x, max_improv_x))
                    self.train_points_y = np.vstack((self.train_points_y, max_improv_y))

                    yield x_sampled_points, y_sampled_points, vars_, best_x, best_y, \
                        list_of_expected_improvements, testing_domain, mus

    def hyperparam_choice_function(self, hyperparameter_value,
                               dimensions = 'one', hyperparameter_value_two = None):
        """Returns the negative MSE of the input hyperparameter for the given
         hyperparameter.
        Used with GradientBoostingRegressor estimator currently
        If dimensions = one, then search n_estimators. if dimension equal
        two then search over n_estimators and max_depth"""
        #definethe model
        model = self.model
        # define the training points
        train_points_x = self.model_train_points_x
        train_points_y = self.model_train_points_y

        if self.dimensions == 'one':
            try:
                m = model(n_estimators= int(hyperparameter_value))
            except:
                 m = model(n_estimators= hyperparameter_value)
            m.fit(train_points_x, train_points_y)
            pred = m.predict(self.test_points_x )
            n_mse = self.root_mean_squared_error(self.test_points_y , pred)
            return n_mse
        elif self.dimensions =='two':
            try:
                m = model(n_estimators = int(hyperparameter_value),
                          max_depth = int(hyperparameter_value_two))
            except:
                m = model(n_estimators = hyperparameter_value,
                          max_depth = hyperparameter_value_two)
            m.fit(train_points_x, train_points_y)
            pred = m.predict(self.test_points_x)
            n_mse = self.root_mean_squared_error(self.test_points_y , pred)
            return n_mse
        else:
            return ' We do not support this number of dimensions yet'

    def root_mean_squared_error(self, actual, predicted, negative = True):
        """MSE of actual and predicted value.
        Negative turn the MSE negative to allow for
        maximization instead of minimization"""
        if negative == True:
            return - np.sqrt(sum((actual.reshape(-1,1) - predicted.reshape(-1,1)**2))
                                 /len(actual))
        else:
            return np.sqrt(sum((actual.reshape(-1,1) - predicted.reshape(-1,1)**2))
                                 /len(actual))

    def expected_improvement(self, mean_x, sigma_squared_x,
                             y_val_for_best_hyperparameters, normal_dist=None,
                              point_est = False):
        """Finds the expected improvement of a point give the current best point.
        If point_est = False, then computes the expected value on a vector
        from the posterior distribution.
        """

        with np.errstate(divide='ignore'): # in case sigma equals zero
            # Expected val for one point
            if point_est ==True:
                sigma_x = np.sqrt(sigma_squared_x) # get the standard deviation from the variance

                Z = (mean_x - y_val_for_best_hyperparameters) / sigma_x

                if round(sigma_x,8) == 0:
                    return 0
                else:
                    return (mean_x -
                            y_val_for_best_hyperparameters)*normal_dist.cdf(Z)+\
                            sigma_x*normal_dist.pdf(Z)

            else:
                # Sample from the posterior functions
                for _ in range(len(mean_x)):
                    list_of_improvements = []
                    m_s = []
                    for m, z, s in zip(mean_x, ((mean_x -y_val_for_best_hyperparameters)\
                                         / np.std(sigma_squared_x)),np.sqrt(sigma_squared_x) ):

                        list_of_improvements.append(((m-y_val_for_best_hyperparameters)*\
                                                     norm().cdf(z)\
                                                     +s * norm().pdf(z)))
                        m_s.append(m)

                    return list_of_improvements

    def nearestPD(self, A):
        """
        #https://stackoverflow.com/questions/43238173/python-convert-matrix-to-positive-semi-definite/43244194#43244194

        Find the nearest positive-definite matrix to input

        A Python/Numpy port of <NAME>'s `nearestSPD` MATLAB code [1], which
        credits [2].

        [1] https://www.mathworks.com/matlabcentral/fileexchange/42885-nearestspd

        [2] <NAME>, "Computing a nearest symmetric positive semidefinite
        matrix" (1988): https://doi.org/10.1016/0024-3795(88)90223-6
        """
        def isPD(B):
            """Returns true when input is positive-definite, via Cholesky"""
            try:
                _ = la.cholesky(B)
                return True
            except la.LinAlgError:
                return False

        B = (A + A.T) / 2
        _, s, V = la.svd(B)

        H = np.dot(V.T, np.dot(np.diag(s), V))

        A2 = (B + H) / 2

        A3 = (A2 + A2.T) / 2

        if isPD(A3):
            return A3

        spacing = np.spacing(la.norm(A))
        # The above is different from [1]. It appears that MATLAB's `chol` Cholesky
        # decomposition will accept matrixes with exactly 0-eigenvalue, whereas
        # Numpy's will not. So where [1] uses `eps(mineig)` (where `eps` is Matlab
        # for `np.spacing`), we use the above definition. CAVEAT: our `spacing`
        # will be much larger than [1]'s `eps(mineig)`, since `mineig` is usually on
        # the order of 1e-16, and `eps(1e-16)` is on the order of 1e-34, whereas
        # `spacing` will, for Gaussian random matrixes of small dimension, be on
        # othe order of 1e-16. In practice, both ways converge, as the unit test
        # below suggests.
        I = np.eye(A.shape[0])
        k = 1
        while not self.isPD(A3):
            mineig = np.min(np.real(la.eigvals(A3)))
            A3 += I * (-mineig * k**2 + spacing)
            k += 1

        return A3

    def __squared_kernel__(self, a, b, param=2.0, train=False,
                            train_noise = 5e-3, vertical_scale=1.5):
        """Calculated the squared exponential kernel.
        Adds a noise term for the covariance of the training data
        Adjusting the param changes the difference where points will have a positive covariance
        Returns a covaraince Matrix.
        Vertical scale controls the vertical scale of the function"""
        if self.squared_length != None:
            vertical_scale = self.squared_length

        if train == False:
            # ensure a and b are numpy arrays
            a = np.array(a)
            b = np.array(b)
            sqdist = np.sum(a**2,1).reshape(-1,1) + np.sum(b**2,1) - 2*np.dot(a, b.T)
            return vertical_scale*np.exp(-.5 * (1/param) * sqdist)

        else:
            # ensure a and b are numpy arrays
            a = np.array(a)
            b = np.array(b)
            noisy_observations = train_noise*np.eye(len(a))
            sqdist = np.sum(a**2,1).reshape(-1,1) + np.sum(b**2,1) - 2*np.dot(a, b.T)
            return vertical_scale*np.exp(-.5 * (1/param) * sqdist) + noisy_observations

    def __matern_kernel__(self, a,b,C_smoothness=3/2,train=False, train_noise = 5e-2):
        """The class of Matern kernels is a generalization of the RBF and the
        absolute exponential kernel parameterized by an additional parameter
        nu. The smaller nu, the less smooth the approximated function is.
        For nu=inf, the kernel becomes equivalent to the RBF kernel and for nu=0.5
        to the absolute exponential kernel. Important intermediate values are
        nu=1.5 (once differentiable functions) and nu=2.5 (twice differentiable
        functions).

        c_smoother = inf = RBF

        The train keyword is used to add noisy observations to the matrix"""
        if C_smoothness not in [1/2,3/2]:
            return "You choose an incorrect hyparameter, please choose either 1/2 or 3/2"
        matrix_norm = np.array([np.linalg.norm(a[i] - b,axis=(1)) for i in range(len(a))])
        if C_smoothness == 1/2:
            if train == True:
                max(np.var(a),np.var(b)) * np.exp(-matrix_norm) + np.eye(len(matrix_norm))*train_noise
            else:
                return max(np.var(a),np.var(b)) * np.exp(-matrix_norm)
        elif C_smoothness == 3/2:
            if train == True:
                return max(np.var(a),np.var(b))* (1
                    + np.sqrt(3)*matrix_norm)*np.exp(-np.sqrt(3)*matrix_norm) \
                    + np.eye(len(matrix_norm))*train_noise
            else:
                return max(np.var(a),np.var(b))* (1 +np.sqrt(3) *
                                matrix_norm) * np.exp(-np.sqrt(3)*matrix_norm)


<gh_stars>1-10
"""Copyright (c) 2021 <NAME>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE."""

from scipy import integrate
import numpy as np

from percolate.framework import Port
from percolate.framework import InPort
from percolate.framework import OutPort
from percolate.framework import StreamOutput
from percolate.framework import TextOutput
from percolate.framework import StreamInput
from percolate.framework import ArrayOutput
from percolate.framework import FilePathInput
from percolate.framework import DirPathInput
from percolate.framework import MuxInput
from percolate.framework import MuxOutput
from percolate.framework import Param_input
from percolate.framework import func_Output
from percolate.framework import int_input
from percolate.framework import bool_input
from percolate.framework import choice_input
from percolate.framework import Function

# toolKit
from percolate.toolkit.step2 import step2
from percolate.toolkit.single_step import single_step
from percolate.toolkit.make_zero_array import make_zero_array
from percolate.toolkit.single_step_xanes import single_step_xanes
from percolate.toolkit.single_step_xanes import pre_edge_fit

class args_step:
    def __init__(self, parent):

        self.apply_step = parent.apply_step.default
        self.fit_function = parent.fit_function.default

        self.pre_feature_min = parent.pre_feature_min.default
        self.pre_feature_max = parent.pre_feature_max.default
        self.post_feature_min = parent.post_feature_min.default
        self.post_feature_max = parent.post_feature_max.default

class single_step_subtraction_xanes(Function):
    """TODO: Centre the step function on the peaks energy!"""

    def __init__(self):

        super().__init__("step_subtraction")

        # Input Ports
        self.input_array = StreamInput(self, "input_array")

        self.apply_step = choice_input(self, "Apply", "off", ["off", "on"])
        self.fit_function = choice_input(
            self, "fit_function", "Voight", ["Voight", "Arctan"]
        )
        self.pre_feature_min = int_input(
            self, "pre_feature_min", self.input_array, None
        )
        self.pre_feature_max = int_input(
            self, "pre_feature_max", self.input_array, None
        )
        self.post_feature_min = int_input(
            self, "post_feature_min", self.input_array, None
        )
        self.post_feature_max = int_input(
            self, "post_feature_max", self.input_array, None
        )

        # output ports
        self.stepfunction = ArrayOutput(self, "stepfunction", self.read_stepfunction)
        self.subtracted_step = ArrayOutput(
            self, "subtracted_step", self.read_subtracted_step
        )

    # evaluate method
    def evaluate(self):

        local_arguments = args_step(self)

        x = self.input_array.read()["data"][0]
        y = self.input_array.read()["data"][1]
        pre_feature_min = local_arguments.pre_feature_min
        pre_feature_max = local_arguments.pre_feature_max
        post_feature_min = local_arguments.post_feature_min
        post_feature_max = local_arguments.post_feature_max

        if local_arguments.apply_step == "off":

            x = x
            background = make_zero_array(x)
            y = y - background

        else:
            x, y, background = pre_edge_fit(
                x,
                y,
                pre_feature_min,
                pre_feature_max,
                post_feature_min,
                post_feature_max,
            )

        self.x = x
        self.y = y
        self.background = background

        self.lines = [
            pre_feature_min,
            pre_feature_max,
            post_feature_min,
            post_feature_max,
        ]

    def read_stepfunction(self):
        return {
            "data": [self.x, self.background, self.lines],
            "label": self.input_array.read()["label"],
        }
        # return self.stepfunction_a

    def read_subtracted_step(self):
        return {
            "data": [self.x, self.y, self.lines],
            "label": self.input_array.read()["label"],
        }
        # return self.post_step_p

    def calculate_fit(self, x, y, argument):
        pass


<reponame>Aaron5210/yolo_seq_nms<filename>yolo_seqnms.py
# -*- coding: utf-8 -*-
import numpy as np
import cv2
import time
import copy
#import cPickle as pickle
import os, sys
from matplotlib import pyplot as plt
from PIL import Image
import scipy.misc
import yolo_detection
import visualization_utils as vis_util
import label_map_util
from seq_nms import *

def load_image_into_numpy_array(image):
  (im_width, im_height) = image.size
  return np.array(image.getdata()).reshape(
      (im_height, im_width, 3)).astype(np.uint8)

def get_labeled_image(image_path, path_to_labels, num_classes, boxes, classes, scores):
    label_map = label_map_util.load_labelmap(path_to_labels)
    categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=num_classes,
                                                                use_display_name=True)
    category_index = label_map_util.create_category_index(categories)
    image = Image.open(image_path)
    image_np = load_image_into_numpy_array(image)
    image_process = vis_util.visualize_boxes_and_labels_on_image_array(
        image_np,
        boxes,
        classes,
        scores,
        category_index)
    return image_process

if __name__ == "__main__":
    # load image
    load_begin=time.time()
    pkllistfile=open(os.path.join('video', 'pkllist.txt'))
    pkllist=pkllistfile.readlines()
    pkllistfile.close()
    pkllist=[pkl.strip() for pkl in pkllist]
    load_end=time.time()
    print('load: {:.4f}s'.format(load_end - load_begin))

    # detection
    detect_begin=time.time()
    if len(sys.argv) > 1 and sys.argv[1]=='tiny':
        res = yolo_detection.detect_imgs(pkllist, cfg="cfg/tiny-yolo.cfg", weights="tiny-yolo.weights", nms=0, thresh=0.25)
    else:
        res = yolo_detection.detect_imgs(pkllist, nms=0, thresh=0.25)
    detect_end=time.time()
    print('total detect: {:.4f}s'.format(detect_end - detect_begin))
    print('average detect: {:.4f}s'.format((detect_end - detect_begin)/len(pkllist)))

    # nms
    nms_begin=time.time()
    if len(sys.argv) > 1 and sys.argv[1]=='only_person':
        boxes, classes, scores = dsnms(res, only_person=True)
    else:
        boxes, classes, scores = dsnms(res)
    nms_end=time.time()
    print('total nms: {:.4f}s'.format(nms_end - nms_begin))

    # save&visualization
    save_begin=time.time()
    PATH_TO_LABELS = os.path.join('data', 'mscoco_label_map.pbtxt')
    NUM_CLASSES = 80
    if not os.path.exists('video/output'):
        os.makedirs('video/output')
    for i, image_path in enumerate(pkllist):
        image_process = get_labeled_image(image_path, PATH_TO_LABELS, NUM_CLASSES, np.array(boxes[i]), np.array(classes[i]), np.array(scores[i]))
        #plt.imshow(image_process)
        #plt.show()
        scipy.misc.imsave('video/output/frame{}.jpg'.format(i), image_process)
        if i%100==0:
            print('finish writing image{}'.format(i))
    save_end=time.time()
    print('total writing images: {:.4f}s'.format(save_end - save_begin))


<filename>cnc/motor_hub/motor_hub/set_screw_drill/params.py
import scipy

# Basic layout parameters
params = {}
params['numParts'] = 5
params['partSpacing'] = 2.0
layoutLen = (params['numParts']-1)*params['partSpacing']
xPosArray = scipy.linspace(-0.5*layoutLen, 0.5*layoutLen,params['numParts'])
yPosArray = scipy.zeros(xPosArray.size)
params['xPosList'] = list(xPosArray)
params['yPosList'] = list(yPosArray)

params['xPosRelDrill'] = 0.0
params['yPosRelDrill'] = -0.1


# Created by <NAME>

from scipy.optimize import fmin_cobyla as max

def farm_optimize(acres, hours, p1, p2, h1, h2):
    #objective
    def objective(X, p1, p2):
        x,y = X
        return -(p1*x + p2*y)
    #constraint 1
    def c1(X, acres, hours, h1, h2):
        x,y = X
        return hours - h1*x - h2*y
    #constraint 2
    def c2(X, acres, hours, h1, h2):
        x,y = X
        return acres - x - y
    #constraint 3
    def c3(X, acres, hours, h1, h2):
        return X[0]
    #constraint 4
    def c4(X, acres, hours, h1, h2):
        return X[1]

    X = max(objective, x0=[acres, 0], cons=[c1,c2,c3,c4],
            args=(p1,p2), consargs=(acres, hours, h1, h2))

    print ('With input:')
    print (acres,'total acres',hours,'total labor-hours')
    print ('${}'.format(p1),'corn profit-per-acre',
           '${}'.format(p2),'oat profit-per-acre')
    print (h1,'corn labor-hours',h2,'oat labor-hours')
    print (' We should plant {0:.1f} acres of corn.'.format(X[0]))
    print (' We should plant {0:.1f} acres of oats.'.format(X[1]))
    print (' The maximum profit we can earn is ${0:.2f}.'
           .format(-objective(X, p1, p2)))
    print ()

farm_optimize(240, 320, 40, 30, 2, 1)
farm_optimize(300, 380, 70, 45, 3, 1)
farm_optimize(180, 420, 65, 55, 3, 1)


import itertools
import logging
import multiprocessing
import os
import typing

import numpy as np
import pandas as pd
import scipy.stats

from pyextremes.models.model_base import AbstractModelBaseClass

logger = logging.getLogger(__name__)

def get_fit_parameters(params) -> typing.List[tuple]:
    n, fit_function, extremes, fixed_parameters, seed = params
    size = len(extremes)
    rng_generator = np.random.default_rng(seed=seed)
    sampler = rng_generator.choice
    return [
        fit_function(
            data=sampler(a=extremes, size=size, replace=True),
            **fixed_parameters,
        )
        for _ in range(n)
    ]

class MLE(AbstractModelBaseClass):
    def __init__(
        self,
        extremes: pd.Series,
        distribution: typing.Union[str, scipy.stats.rv_continuous],
        distribution_kwargs: typing.Optional[dict] = None,
    ) -> None:
        """
        Maximum Likelihood Estimate (MLE) model.

        Built around the scipy.stats.rv_continuous.fit method.

        """
        super().__init__(
            extremes=extremes,
            distribution=distribution,
            distribution_kwargs=distribution_kwargs,
        )

        # Initialize 'fit_parameter_cache' and 'seed_cache'
        self.fit_parameter_cache: typing.List[tuple] = []
        self.seed_cache: typing.Set[int] = set()

    @property
    def name(self) -> str:
        return "MLE"

    def fit(self, **kwargs) -> None:
        if len(kwargs) != 0:
            raise TypeError(
                f"unrecognized arguments passed in: {', '.join(kwargs.keys())}"
            )
        self._fit_parameters = self.distribution.mle_parameters
        logger.debug(
            f"fit {self.distribution.name} distribution "
            f"with parameters {self.distribution.mle_parameters}"
        )

    def get_return_value(
        self, exceedance_probability, alpha: typing.Optional[float] = None, **kwargs
    ) -> tuple:
        """
        Calculate return value and confidence interval bounds.

        Parameters
        ----------
        exceedance_probability : array-like
            Exceedance probability or 1D array of exceedance probabilities.
            Each exceedance probability must be in the [0, 1) range.
        alpha : float, optional
            Width of confidence interval (0, 1).
            If None (default), return None
            for upper and lower confidence interval bounds.
        kwargs
            n_samples : int, optional
                Number of bootstrap samples used to estimate
                confidence interval bounds (default=100).

        Returns
        -------
        return_value : array-like
            Return values.
        ci_lower : array-like
            Lower confidence interval bounds.
        ci_upper : array-like
            Upper confidence interval bounds.

        """
        # Parse 'kwargs'
        n_samples = kwargs.pop("n_samples", 100)
        if not n_samples > 0:
            raise ValueError(
                f"invalid value in {n_samples} for the 'n_samples' "
                f"argument, must be positive number"
            )
        if len(kwargs) != 0:
            raise TypeError(
                f"unrecognized arguments passed in: {', '.join(kwargs.keys())}"
            )

        # Convert 'exceedance_probability' to ndarray
        exceedance_probability = np.asarray(
            a=exceedance_probability, dtype=np.float64
        ).copy()
        if exceedance_probability.ndim == 0:
            exceedance_probability = exceedance_probability[np.newaxis]
        if exceedance_probability.ndim != 1:
            raise ValueError(
                f"invalid shape in {exceedance_probability.shape} "
                f"for the 'exceedance_probability' argument, must be 1D array"
            )

        # If cache doesn't have enough values, calculate new fit parameters
        if alpha is not None:
            n_extra_fit_parameters = n_samples - len(self.fit_parameter_cache)
            if n_extra_fit_parameters > 0:
                self._extend_fit_parameter_cache(n=n_extra_fit_parameters)

        # Calculate return values
        return_value = np.full(
            shape=exceedance_probability.shape, fill_value=np.nan, dtype=np.float64
        )
        ci_lower = return_value.copy()
        ci_upper = return_value.copy()
        for i, ep in enumerate(exceedance_probability):
            key: typing.Tuple[float, typing.Optional[float], int] = (
                ep,
                alpha,
                n_samples,
            )
            try:
                # Try to fetch pre-calculated values from cache
                rv, cil, ciu = self.return_value_cache[key]
                logger.debug(
                    f"fetched return value for {key} from cache as {(rv, cil, ciu)}"
                )
            except KeyError:
                # Value not in cache - calculate new return value
                rv = self.distribution.distribution.isf(
                    q=ep,
                    **self.fit_parameters,
                    **self.distribution._fixed_parameters,
                )

                # Calculate confidence intervals
                if alpha is None:
                    cil = None
                    ciu = None
                else:
                    # Calculate confidence intervals
                    rv_sample = self.distribution.distribution.isf(
                        ep, *np.transpose(self.fit_parameter_cache[:n_samples])
                    )
                    cil, ciu = np.quantile(
                        a=rv_sample, q=[(1 - alpha) / 2, (1 + alpha) / 2]
                    )

                # Add calculated return value and intervals to cache
                self.return_value_cache[key] = (rv, cil, ciu)
                logger.debug(f"calculated return value for {key} as {(rv, cil, ciu)}")

            return_value[i] = rv
            ci_lower[i] = cil
            ci_upper[i] = ciu

        # Return results
        if len(return_value) == 1:
            return return_value[0], ci_lower[0], ci_upper[0]
        else:
            return return_value, ci_lower, ci_upper

    def _extend_fit_parameter_cache(self, n: int) -> None:
        # Prepare local variables used by fit parameter calculator
        extremes = self.extremes.values
        fit_function = self.distribution.distribution.fit
        fixed_parameters = self.distribution.fixed_parameters

        min_samples_per_core = 50
        if n <= min_samples_per_core:
            # Calculate without multiprocessing
            logger.debug("getting random seed value for fit parameter sampler")
            seed = None
            while seed is None:
                _seed = np.random.randint(low=0, high=1e6, size=None)
                if _seed not in self.seed_cache:
                    seed = _seed
                    self.seed_cache.add(_seed)

            logger.debug(f"calculating {n} additional fit parameters using single core")
            new_fit_parameters = get_fit_parameters(
                params=(
                    n,
                    fit_function,
                    extremes,
                    fixed_parameters,
                    seed,
                )
            )
        else:
            # Find number of cores
            n_cores = min(
                os.cpu_count() or 2,
                int(np.ceil(n / min_samples_per_core)),
            )

            # Calculate number of samples per core
            min_samples_per_core = int(n / n_cores)
            core_samples = [min_samples_per_core for _ in range(n_cores)]

            # Distribute remaining samples evenly across cores
            for i in range(n - sum(core_samples)):
                core_samples[i] += 1

            # Get unique random seed for each core and add it to `self.seed_cache`
            logger.debug("getting random seed values for each core")
            seeds: typing.List[int] = []
            while len(seeds) < n_cores:
                seed = np.random.randint(low=0, high=1e6, size=None)
                if seed not in self.seed_cache:
                    seeds.append(seed)
                    self.seed_cache.add(seed)

            # Calculate new fit parameters using processor pool
            logger.debug(
                f"calculating {n} additional fit parameters using {n_cores} cores "
                f"having {core_samples} samples accordingly"
            )
            with multiprocessing.Pool(processes=n_cores) as pool:
                new_fit_parameters = list(
                    itertools.chain(
                        *pool.map(
                            get_fit_parameters,
                            zip(
                                core_samples,
                                [fit_function for _ in range(n_cores)],
                                [extremes for _ in range(n_cores)],
                                [fixed_parameters for _ in range(n_cores)],
                                seeds,
                            ),
                        )
                    )
                )

        # Extend fit parameter cache
        logger.debug(f"extending fit parameter cache with {n} new entries")
        self.fit_parameter_cache.extend(new_fit_parameters)
        return None

    def __repr__(self) -> str:
        free_parameters = ", ".join(
            [
                f"{parameter}={self.fit_parameters[parameter]:.3f}"
                for parameter in self.distribution.free_parameters
            ]
        )

        fixed_parameters = ", ".join(
            [
                f"{key}={value:.3f}"
                for key, value in self.distribution.fixed_parameters.items()
            ]
        )
        if fixed_parameters == "":
            fixed_parameters = "all parameters are free"

        summary = [
            "MLE model",
            "",
            f"free parameters: {free_parameters}",
            f"fixed parameters: {fixed_parameters}",
            f"AIC: {self.AIC:.3f}",
            f"loglikelihood: {self.loglikelihood:.3f}",
            f"return value cache size: {len(self.return_value_cache):,d}",
            f"fit parameter cache size: {len(self.fit_parameter_cache):,d}",
        ]

        longest_row = max(map(len, summary))
        summary[1] = "-" * longest_row
        summary.append(summary[1])
        summary[0] = " " * ((longest_row - len(summary[0])) // 2) + summary[0]
        for i, row in enumerate(summary):
            summary[i] += " " * (longest_row - len(row))

        return "\n".join(summary)


#!c:\users\hooma\documents\github\spinesegmentation\segmentation_test\scripts\python.exe

"""
Creates the superimposition image of two label images.

Copyright (C) 2013 <NAME>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

# build-in modules
from argparse import ArgumentError
import argparse
import logging
import os

# third-party modules
import scipy

# path changes

# own modules
from medpy.io import load, save
from medpy.core import Logger

# information
__author__ = "<NAME>"
__version__ = "r0.2.1, 2011-01-04"
__email__ = "<EMAIL>"
__status__ = "Release"
__description__ = """
                  Takes two label images as input and creates their superimposition i.e.
                  all the regions borders are preserved and the resulting image contains
                  more or the same number of regions as the respective input images.

                  The resulting image has the same name as the first input image, just
                  with a '_superimp' suffix.

                  Copyright (C) 2013 <NAME>
                  This program comes with ABSOLUTELY NO WARRANTY; This is free software,
                  and you are welcome to redistribute it under certain conditions; see
                  the LICENSE file or <http://www.gnu.org/licenses/> for details.
                  """

# code
def main():
    # parse cmd arguments
    parser = getParser()
    parser.parse_args()
    args = getArguments(parser)

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # build output image name
    image_superimposition_name = args.folder + '/' + args.image1.split('/')[-1][:-4] + '_superimp'
    image_superimposition_name += args.image1.split('/')[-1][-4:]

    # check if output image exists
    if not args.force:
        if os.path.exists(image_superimposition_name):
            raise ArgumentError('The output image {} already exists. Please provide the -f/force flag, if you wish to override it.'.format(image_superimposition_name))

    # load image1 using
    logger.info('Loading image {}...'.format(args.image1))
    image1_data, image1_header = load(args.image1)

    # load image2 using
    logger.info('Loading image {}...'.format(args.image2))
    image2_data, _ = load(args.image2)

    # check input images to be valid
    logger.info('Checking input images for correctness...')
    if image1_data.shape != image2_data.shape:
        raise ArgumentError('The two input images shape do not match with 1:{} and 2:{}'.format(image1_data.shape, image2_data.shape))
    int_types = (scipy.uint, scipy.uint0, scipy.uint8, scipy.uint16, scipy.uint32, scipy.uint64, scipy.uintc, scipy.uintp,
                 scipy.int_, scipy.int0, scipy.int8, scipy.int16, scipy.int32, scipy.int64, scipy.intc, scipy.intp)
    if image1_data.dtype not in int_types:
        raise ArgumentError('Input image 1 is of type {}, an int type is required.'.format(image1_data.dtype))
    if image2_data.dtype not in int_types:
        raise ArgumentError('Input image 2 is of type {}, an int type is required.'.format(image2_data.dtype))
    if 4294967295 < abs(image1_data.min()) + image1_data.max() + abs(image2_data.min()) + image2_data.max():
        raise ArgumentError('The input images contain so many (or not consecutive) labels, that they will not fit in a uint32 range.')

    # create superimposition of the two label images
    logger.info('Creating superimposition image...')
    image_superimposition_data = scipy.zeros(image1_data.shape, dtype=scipy.uint32)
    translation = {}
    label_id_counter = 0
    for x in range(image1_data.shape[0]):
        for y in range(image1_data.shape[1]):
            for z in range(image1_data.shape[2]):
                label1 = image1_data[x,y,z]
                label2 = image2_data[x,y,z]
                if not (label1, label2) in translation:
                    translation[(label1, label2)] = label_id_counter
                    label_id_counter += 1
                image_superimposition_data[x,y,z] = translation[(label1, label2)]

    # save resulting superimposition image
    logger.info('Saving superimposition image as {} in the same format as input image...'.format(image_superimposition_name))
    save(image_superimposition_data, args.output, image1_header, args.force)

    logger.info('Successfully terminated.')

def getArguments(parser):
    "Provides additional validation of the arguments collected by argparse."
    return parser.parse_args()

def getParser():
    "Creates and returns the argparse parser object."
    parser = argparse.ArgumentParser(description=__description__)
    parser.add_argument('image1', help='The first input label image.')
    parser.add_argument('image2', help='The second input label image.')
    parser.add_argument('output', help='The output image.')
    parser.add_argument('-v', dest='verbose', action='store_true', help='Display more information.')
    parser.add_argument('-d', dest='debug', action='store_true', help='Display debug information.')
    parser.add_argument('-f', dest='force', action='store_true', help='Silently override existing output images.')

    return parser

if __name__ == "__main__":
    main()