text:
# This file is designed to perform unit and integration tests on all functions
# in parabolic_pde_solver.py

# Test have been written with TDD in mind where possible
import pytest
import numpy as np
from math import pi, isclose
from unittest.mock import MagicMock
from parabolic_pde_solver import pde_solve, diags_m, create_A_CN
from parabolic_pde_solver import create_B_CN, create_A_BE, create_A_FE
from parabolic_pde_solver import solve_BE, solve_FE, solve_CN
from scipy.sparse import diags, csr_matrix

base_mx = 100     # number of gridpoints in space
base_mt = 1000   # number of gridpoints in time
base_L = 11           # length of spatial domain
base_T = 0.5         # total time to solve for
base_u_I = lambda x: np.sin(pi*x/base_L) # initial temperature distriibution

def test_unit_ensure_returns():
    # Arrange
    # Act
    res = pde_solve(base_L, base_T, base_u_I, base_mx, base_mt)
    # Assert
    assert res != None

def test_unit_throws_if_not_passed_specified_params():
    #Arrange
    thrown = False
    #Act
    try:
       res = pde_solve()
    except Exception as e:
       thrown = e
    #assert
    assert thrown != False

def test_integration_ensures_that_u_I_is_called_multiple_times():
    # Arrange
    mock = MagicMock()
    # Act
    pde_solve(base_L, base_T, mock, base_mx, base_mt)
    # Assert
    assert mock.call_count == base_mx

def test_unit_ensures_that_u_I_is_called_with_correct_args():
    # Arrange
    def fake_u_I(x):
        assert isinstance(x, float) == True
    # Act
    pde_solve(base_L, base_T, fake_u_I, base_mx, base_mt)
    # Assert

def test_int_ensures_the_bcf_func_is_called_corrrectly():
    # Arrange
    mock = MagicMock(return_value=[0,0])
    # Act
    pde_solve(base_L, base_T, base_u_I, base_mx, base_mt, bcf=mock)
    # Assert
    assert mock.call_count == base_mt

def test_unit_ensures_bcf_is_called_with_the_correct_params():
    # Arrange
    def fake_bcf(t):
        assert type(t) == np.float64
        return [0,0]
    # Act
    pde_solve(base_L, base_T, base_u_I, base_mx, base_mt, bcf=fake_bcf)
    # Assert

def test_E2E_agaist_exact_solution_to_heat_equation():
    # Arrange
    # set problem parameters/functions
    kappa = 1   # diffusion constant
    L=11         # length of spatial domain
    T=0.5        # total time to solve for

    # set numerical parameters
    mx = 100     # number of gridpoints in space
    mt = 1000   # number of gridpoints in time

    # define initial params
    def u_I(x):
         # initial temperature distribution
         y = np.sin(pi*x/L)
         return y

    # define this to compare witht the exact solution
    def u_exact(x,t):
        # the exact solution
        y = np.exp(-kappa*(pi**2/L**2)*t)*np.sin(pi*x/L)
        return y
    # Act
    # solve the heat equation
    [u_j, x, t] = pde_solve(L, T, u_I, mx, mt, f_kappa=lambda x: 1)

    # Assert
    looped = False
    # compare sol vs u_exact with error threashold
    for i in range(len(u_j)):
        exact = u_exact(x[i], T)
        assert isclose(u_j[i], exact, abs_tol=1e-3)
        looped = True

    assert looped == True

def test_E2E_agaist_heat_equation_varying_bcf():
    # Arrange
    # set problem parameters/functions
    kappa = 1   # diffusion constant
    L=11         # length of spatial domain
    T=0.5        # total time to solve for

    # set numerical parameters
    mx = 10     # number of gridpoints in space
    mt = 10   # number of gridpoints in time

    # define initial params
    def u_I(x):
         # initial temperature distribution
         y = np.sin(pi*x/L)
         return y

    def bcf(t):
        return [t, t]

    # Act
    # solve the heat equation
    [u_j, x, t] = pde_solve(L, T, u_I, mx, mt, bcf=bcf)

    # Assert
    # check solution at final value boundary conditions
    assert u_j[0] == T
    assert u_j[-1] == T

def test_E2E_agaist_heat_equation_varying_diffusion_coefficient():
    # Arrange
    # set problem parameters/functions
    L=11         # length of spatial domain
    T=0.5        # total time to solve for

    # set numerical parameters
    mx = 10     # number of gridpoints in space
    mt = 1000   # number of gridpoints in time

    # define initial params
    def u_I(x):
         # initial temperature distribution
         y = np.sin(pi*x/L)
         return y

    # Act
    # solve the heat equation
    [u_j, x, t] = pde_solve(L, T, u_I, mx, mt, f_kappa=lambda x: x)
    print("u_j {}".format(u_j))
    assert np.isclose(u_j, [0, 0.26789984, 0.40498984, 0.49342122, 0.52927611, 0.5140508,
 0.45421537, 0.36018634, 0.24470462, 0.12086154, 0]).all()

def test_E2E_agaist_heat_equation_varying_diffusion_coefficient():
    # Arrange
    # set problem parameters/functions
    L=11         # length of spatial domain
    T=0.5        # total time to solve for

    # set numerical parameters
    mx = 10     # number of gridpoints in space
    mt = 1000   # number of gridpoints in time

    # define initial params
    def u_I(x):
         # initial temperature distribution
         y = np.sin(pi*x/L)
         return y

    def heat_source(x, t):
        res = np.piecewise(x, [x < 5, x >= 5], [-1, 1])
        return res

    # Act
    # solve the heat equation
    [u_j, x, t] = pde_solve(L, T, u_I, mx, mt, heat_source=heat_source)
    print("u_j {}".format(u_j))
    assert np.isclose(u_j, [0, -863.00360932, -983.72884818, -972.80346348,-751.16896889,753.06057621,974.46691545,984.96351132,863.66058543,0]).all()

""" The below section contains test for diags_m"""

def test_unit_tri_diag_returns_a_grid_of_the_correct_size():
    # Arrange
    m = 2
    n = 1
    # Act
    M = diags_m(m, n)
    # Assert
    assert M.shape == (2,1)

def test_unit_expected_output_1():
    # Arrange
    # Act
    M  = diags_m(2, 2, [-1, 0, 1], [1, 2, 3])
    # Assert
    np.testing.assert_array_equal(M, [[2, 3], [1, 2]])

def test_unit_expected_output_2():
    # Arrange
    # Act
    M  = diags_m(2, 2, [-1, 0, 1], [5, 3, 4])
    # Assert
    np.testing.assert_array_equal(M, [[3, 4], [5, 3]])

"""The above section contains tests for diags_m"""

"""The below contains tests for create_ACN"""
def test_E2E_createACN_outputs_the_correct_matrix1():
    # Arrange
    x=np.ones(10)
    f_kappa=lambda x: 1
    deltax=1
    deltat=1
    # Act
    ACN = create_A_CN(x, f_kappa, deltat, deltax, logger=False)
    # Assert
    looped = False
    dense = ACN.todense()
    [x, y] = dense.shape
    for i in range(x):
        for j in range(y):
            if i==j:
                assert dense.item((i,j)) == 2
                looped = True
            elif abs(i-j) == 1:
                assert dense.item((i,j)) == -0.5
            else:
                assert dense.item((i,j)) == 0

    assert looped == True

def test_E2E_createACN_outputs_the_correct_matrix2():
    # Arrange
    x=np.ones(10)
    f_kappa=lambda x: 2*x
    deltax=1
    deltat=1
    # Act
    ACN = create_A_CN(x, f_kappa, deltat, deltax, logger=False)
    # Assert
    looped = False
    dense = ACN.todense()
    [x, y] = dense.shape
    for i in range(x):
        for j in range(y):
            if i==j:
                assert dense.item((i,j)) == 3
                looped = True
            elif abs(i-j) == 1:
                assert dense.item((i,j)) == -1
            else:
                assert dense.item((i,j)) == 0
    assert looped == True
"""The above contains test for create_ACN"""

"""The below contains tests for create_BCN"""
def test_E2E_create_BCN_outputs_the_correct_matrix1():
    # Arrange
    x=np.ones(10)
    f_kappa=lambda x: 1
    deltax=1
    deltat=1
    # Act
    BCN = create_B_CN(x, f_kappa, deltat, deltax, logger=False)
    # Assert
    looped = False
    dense = BCN.todense()
    [x, y] = dense.shape
    for i in range(x):
        for j in range(y):
            if i==j:
                assert dense.item((i,j)) == 0
                looped = True
            elif abs(i-j) == 1:
                assert dense.item((i,j)) == 0.5
            else:
                assert dense.item((i,j)) == 0
    assert looped == True

def test_E2E_createBCN_outputs_the_correct_matrix2():
    # Arrange
    x=np.ones(10)
    f_kappa=lambda x: 2*x
    deltax=1
    deltat=1
    # Act
    BCN = create_B_CN(x, f_kappa, deltat, deltax, logger=False)
    # Assert
    looped = False
    dense = BCN.todense()
    [x, y] = dense.shape
    for i in range(x):
        for j in range(y):
            if i==j:
                assert dense.item((i,j)) == -1
                looped = True
            elif abs(i-j) == 1:
                assert dense.item((i,j)) == 1
            else:
                assert dense.item((i,j)) == 0
    assert looped == True

"""The above contains tests for create_BCN"""

"""The below contains test for create_ABE"""
def test_E2E_create_ABE_outputs_the_correct_matrix1():
    # Arrange
    x=np.ones(10)
    f_kappa=lambda x: 1
    deltax=1
    deltat=1
    # Act
    ABE = create_A_BE(x, f_kappa, deltat, deltax, logger=False)
    # Assert
    looped = False
    dense = ABE.todense()
    [x, y] = dense.shape
    for i in range(x):
        for j in range(y):
            if i==j:
                assert dense.item((i,j)) == 3
                looped = True
            elif abs(i-j) == 1:
                assert dense.item((i,j)) == -1
            else:
                assert dense.item((i,j)) == 0
    assert looped == True
"""The above contains tests for create_ABE"""

"""The below contains tests for create AFE"""
def test_E2E_create_AFE_outputs_the_correct_matrix1():
    # Arrange
    x=np.ones(10)
    f_kappa=lambda x: 1
    deltax=1
    deltat=1
    # Act
    [AFE, lmbda_v] = create_A_FE(x, f_kappa, deltat, deltax, logger=False)
    # Assert
    looped = False
    dense = AFE.todense()
    [x, y] = dense.shape
    for i in range(x):
        assert lmbda_v[i] == 1
        for j in range(y):
            if i==j:
                assert dense.item((i,j)) == -1
                looped = True
            elif abs(i-j) == 1:
                assert dense.item((i,j)) == 1
            else:
                assert dense.item((i,j)) == 0
    assert looped == True
"""The above contains tests for create_AFE"""

"""The below contains tests for solve_CN"""
def test_E2E_solve_CN_outputs_the_correct_result():
    # Arrange
    u_j=[2, 3, 4]
    A_CN=csr_matrix([[1, -2, 0],[-2, 1, -2],[0, -2, 1]])
    B_CN=csr_matrix([[2,3,6],[5,6,9],[1,2,2]])
    heat_j=[7,1,5]
    # Act
    u_jp1 = solve_CN(u_j, A_CN, B_CN, heat_j)
    # Assert
    assert np.allclose(u_jp1, [ -4.57142857, -23.28571429, -27.57142857])

"""The above contains tests for solve_CN"""

"""The above contains tests for solve_FE"""
def test_E2E_solve_FE_outputs_the_correct_result():
    # Arrange
    u_j=[0,0,0]
    A_BE=csr_matrix([[1, -2, 0],[-2, 1, -2],[0, -2, 1]])
    heat_j=csr_matrix([0,1,0])
    lmbda=8
    bc1=6
    bc2=5
    # Act
    u_jp1 = solve_FE(u_j, A_BE, heat_j, lmbda, bc1,bc2)
    # Assert
    assert np.allclose(u_jp1, [48,  1, 40])

"""The above contains tests for solve_FE"""

"""The below contains tests for solve_BE"""
def test_E2E_solve_BE_outputs_the_correct_result():
    # Arrange
    u_j=[5,8,9]
    A_BE=csr_matrix([[1, -2, 0],[-2, 1, -2],[0, -2, 1]])
    heat_j=[0,1,0]
    # Act
    u_jp1 = solve_BE(u_j, A_BE, heat_j)
    # Assert
    assert np.allclose(u_jp1, [-5.28571429, -4.14285714, -1.28571429])
"""The above contains tests for solve_BE"""


text:
from app.chaos_game import CGR
from scipy.fftpack import fft
import cmath
import math
from app.read_data import fasta_parser

class DFT_CGR(CGR):
    def __init__(self, seq, kind="RY"):
        super().__init__(seq, kind)
        self.F = fft(self.complex)
        self.PS = []
        self.AS = []
        for x in self.F:
            self.PS.append((abs(x)) ** 2)
            self.AS.append(cmath.phase(x))

    def get_DFT(self):
        return self.PS

def T_m(seq, m):
    n = len(seq)
    if n == m:
        return seq
    seq2 = [seq[0]]
    for k in range(1, m):
        q = k * n / m
        r = math.floor(q)

        if r == 0:
            r = 1

        if q.is_integer():
            seq2.append(seq[int(q)])
        else:
            if r < n - 1:
                seq2.append(seq[r] + (q - r) * (seq[r + 1] - seq[r]))
            else:
                seq2.append(seq[r])
    return seq2

def DFT_data(kind, seq, draw_CGR):
    cgr = DFT_CGR(seq, kind)
    if draw_CGR:
        cgr.plot_CGR()
    DFT = cgr.get_DFT()
    return DFT

def DFT_from_fasta(input_file, draw_CGR=False, CGR_types=("RY", "MK", "WS")):
    items = {}
    data = fasta_parser(input_file)
    if isinstance(CGR_types, list) and len(CGR_types) > 1:
        for ele in CGR_types:
            d = {}
            for r in range(len(data[0])):
                d[data[0][r]] = DFT_data(ele, data[1][r], draw_CGR)
            items[ele] = d
    else:
        d = {}
        for r in range(len(data[0])):
            d[data[0][r]] = DFT_data(str(CGR_types), data[1][r], draw_CGR)
        items[str(CGR_types)] = d
    return items

ext: py

reason: from scipy


text:
# https://stackoverflow.com/questions/46040382/spline-interpolation-in-3d-in-python
from itertools import islice
from pprint import pformat
from scipy.interpolate import UnivariateSpline
from typing import Tuple, Optional

from hwtHls.scheduler.errors import TimeConstraintError

class Spline(UnivariateSpline):

    def __init__(self, *args, **kwargs):
        if "k" not in kwargs:
            kwargs["k"] = 1
        super(Spline, self).__init__(*args, **kwargs)

    def __repr__(self):
        knots = tuple(self.get_knots())
        coefs = tuple(self.get_coeffs())
        return f"Spline(\n            {knots},\n            {coefs})"

class ResourceSplineBundle():

    def __init__(self, *spline_for_each_possible_latency: Optional[Spline]):
        assert spline_for_each_possible_latency
        self.splines = spline_for_each_possible_latency

    def __call__(self, arg_cnt:int, arg_bit_width:int, min_latency: int, max_val:float) -> Tuple[int, float]:
        latency = min_latency - 1
        for s in islice(self.splines, min_latency, None):
            latency += 1
            if s is None:
                continue
            v = s(arg_bit_width)
            if v <= max_val:
                return (latency, v)

        raise TimeConstraintError("No operation realizations statisfying the constrain", arg_cnt, arg_bit_width, min_latency, max_val)

    def __repr__(self):
        return f"{self.__class__.__name__:s}{pformat(tuple(self.splines))}"


text:
import unittest
import tempfile
import pandas as pd
import numpy as np

import trojai.modelgen.training_statistics as tpm_ts

class TestTrainingStatistics(unittest.TestCase):
    def test_batch_statistics(self):
        batch_num = 1
        batch_train_acc = 50
        batch_train_loss = -4
        batch_statistics = tpm_ts.BatchStatistics(batch_num,
                                                  batch_train_acc, batch_train_loss)
        self.assertEqual(batch_statistics.get_batch_num(), batch_num)
        self.assertEqual(batch_statistics.get_batch_train_acc(), batch_train_acc)
        self.assertEqual(batch_statistics.get_batch_train_loss(), batch_train_loss)

        self.assertRaises(ValueError, batch_statistics.set_batch_train_acc, 150)
        self.assertRaises(ValueError, batch_statistics.set_batch_train_acc, -50)

    def test_epoch_statistics(self):
        batch1 = tpm_ts.BatchStatistics(1, 1, 1)
        batch2 = tpm_ts.BatchStatistics(2, 2, 2)
        batch3 = tpm_ts.BatchStatistics(3, 3, 3)
        batch4 = tpm_ts.BatchStatistics(4, 4, 4)

        epoch_stats = tpm_ts.EpochStatistics(1)
        epoch_stats.add_batch(batch1)
        epoch_stats.add_batch(batch2)
        epoch_stats.add_batch([batch3, batch4])

        batch_stats_list = epoch_stats.get_batch_stats()
        for batch_idx, batch_stats in enumerate(batch_stats_list):
            expected_val = batch_idx + 1
            self.assertEqual(batch_stats.get_batch_num(), expected_val)
            self.assertEqual(batch_stats.get_batch_train_acc(), expected_val)
            self.assertEqual(batch_stats.get_batch_train_loss(), expected_val)
        self.assertEqual(epoch_stats.get_epoch_num(), 1)

    def test_training_statistics(self):
        batch1 = tpm_ts.BatchStatistics(1, 1, 1)
        batch2 = tpm_ts.BatchStatistics(2, 2, 2)
        batch3 = tpm_ts.BatchStatistics(3, 3, 3)
        batch4 = tpm_ts.BatchStatistics(4, 4, 4)
        batch5 = tpm_ts.BatchStatistics(5, 5, 5)
        batch6 = tpm_ts.BatchStatistics(6, 6, 6)

        epoch1_stats = tpm_ts.EpochStatistics(1)
        epoch2_stats = tpm_ts.EpochStatistics(2)
        epoch3_stats = tpm_ts.EpochStatistics(3)
        epoch1_stats.add_batch([batch1, batch2])
        epoch2_stats.add_batch([batch3, batch4])
        epoch3_stats.add_batch([batch5, batch6])

        training_stats = tpm_ts.TrainingRunStatistics()
        training_stats.add_epoch(epoch1_stats)
        training_stats.add_epoch([epoch2_stats, epoch3_stats])
        training_stats.set_final_train_acc(1)
        training_stats.set_final_train_loss(1)
        training_stats.set_final_val_combined_acc(1)
        training_stats.set_final_val_combined_loss(1)
        training_stats.set_final_clean_data_test_acc(1)
        training_stats.set_final_triggered_data_test_acc(1)

        summary_dict = training_stats.get_summary()
        self.assertEqual(summary_dict['final_train_acc'], 1)
        self.assertEqual(summary_dict['final_train_loss'], 1)
        self.assertEqual(summary_dict['final_combined_val_acc'], 1)
        self.assertEqual(summary_dict['final_combined_val_loss'], 1)
        self.assertEqual(summary_dict['final_clean_data_test_acc'], 1)
        self.assertEqual(summary_dict['final_triggered_data_test_acc'], 1)

        self.assertRaises(ValueError, training_stats.set_final_train_acc, 150)
        self.assertRaises(ValueError, training_stats.set_final_train_acc, -50)
        self.assertRaises(ValueError, training_stats.set_final_val_combined_acc, 150)
        self.assertRaises(ValueError, training_stats.set_final_val_combined_acc, -50)
        self.assertRaises(ValueError, training_stats.set_final_clean_data_test_acc, 150)
        self.assertRaises(ValueError, training_stats.set_final_clean_data_test_acc, -50)
        self.assertRaises(ValueError, training_stats.set_final_triggered_data_test_acc, 150)
        self.assertRaises(ValueError, training_stats.set_final_triggered_data_test_acc, -50)

        # ensure data is maintained over epochs
        epoch_stats = training_stats.get_epochs_stats()
        batch_idx = 1
        for epoch_num, epoch in enumerate(epoch_stats):
            actual_epoch_num = epoch_num + 1
            self.assertEqual(epoch.get_epoch_num(), actual_epoch_num)
            batch_stats_list = epoch.get_batch_stats()
            for batch_stats in batch_stats_list:
                expected_val = batch_idx
                self.assertEqual(batch_stats.get_batch_num(), expected_val)
                self.assertEqual(batch_stats.get_batch_train_acc(), expected_val)
                self.assertEqual(batch_stats.get_batch_train_loss(), expected_val)

                batch_idx += 1

    def test_autopopulate_training_statistics(self):
        training_stats = tpm_ts.TrainingRunStatistics()
        epoch1_stats = tpm_ts.EpochStatistics(1, tpm_ts.EpochTrainStatistics(.5, .5),
                                              tpm_ts.EpochValidationStatistics(.5, .5, None, None))
        epoch2_stats = tpm_ts.EpochStatistics(2, tpm_ts.EpochTrainStatistics(.6, .6),
                                              tpm_ts.EpochValidationStatistics(.6, .6, None, None))
        epoch3_stats = tpm_ts.EpochStatistics(3, tpm_ts.EpochTrainStatistics(.7, .8),
                                              tpm_ts.EpochValidationStatistics(.9, 1.1, None, None))
        training_stats.add_epoch(epoch1_stats)
        training_stats.add_epoch([epoch2_stats, epoch3_stats])
        training_stats.add_num_epochs_trained(3)

        training_stats.autopopulate_final_summary_stats()
        training_stats.set_final_clean_data_test_acc(1)
        training_stats.set_final_triggered_data_test_acc(1)

        summary_dict = training_stats.get_summary()
        self.assertEqual(summary_dict['final_train_acc'], .7)
        self.assertEqual(summary_dict['final_train_loss'], .8)
        self.assertEqual(summary_dict['final_combined_val_acc'], .9)
        self.assertEqual(summary_dict['final_combined_val_loss'], 1.1)
        self.assertEqual(summary_dict['final_clean_data_test_acc'], 1)
        self.assertEqual(summary_dict['final_triggered_data_test_acc'], 1)

    def test_save_detailed_statistics(self):
        epoch1_stats = tpm_ts.EpochStatistics(1, tpm_ts.EpochTrainStatistics(.5, 1.5),
                                              tpm_ts.EpochValidationStatistics(2.5, 3.5, None, None))
        epoch2_stats = tpm_ts.EpochStatistics(2, tpm_ts.EpochTrainStatistics(.6, 1.6),
                                              tpm_ts.EpochValidationStatistics(2.6, 3.6, None, None))
        epoch3_stats = tpm_ts.EpochStatistics(3, tpm_ts.EpochTrainStatistics(.7, 1.7),
                                              tpm_ts.EpochValidationStatistics(2.7, 3.7, None, None))

        training_stats = tpm_ts.TrainingRunStatistics()
        training_stats.add_epoch(epoch1_stats)
        training_stats.add_epoch([epoch2_stats, epoch3_stats])

        with tempfile.NamedTemporaryFile() as output_file:
            fname = output_file.name
            training_stats.save_detailed_stats_to_disk(fname)
            # read in the file w/ pandas and ensure data consistency
            df = pd.read_csv(fname)
            self.assertTrue(np.array_equal(df['epoch_number'].values, np.asarray([1, 2, 3])))
            self.assertTrue(np.array_equal(df['train_acc'].values, np.asarray([.5, .6, .7])))
            self.assertTrue(np.array_equal(df['train_loss'].values, np.asarray([1.5, 1.6, 1.7])))
            self.assertTrue(np.array_equal(df['combined_val_acc'].values, np.asarray([2.5, 2.6, 2.7])))
            self.assertTrue(np.array_equal(df['combined_val_loss'].values, np.asarray([3.5, 3.6, 3.7])))

if __name__ == '__main__':
    unittest.main()

reason: import numpy


text:
import pytest
import numpy as np
from dmtools.colorspace import \
    gray_to_RGB, RGB_to_gray, RGB_to_XYZ, XYZ_to_RGB, RGB_to_YUV, YUV_to_RGB, \
    RGB_to_Lab, Lab_to_RGB, add_alpha

# -----------
# TEST IMAGES
# -----------

# single white pixel (single channel)
ONE_CHANNEL_WHITE_PIXEL = np.array([[1.0]])

# single white pixel (three channels)
THREE_CHANNEL_WHITE_PIXEL = np.array([[[1.0, 1.0, 1.0]]])

# single color pixel
COLOR_PIXEL = np.array([[[0.45, 0.33, 0.98]]])

# opaque pixel
OPAQUE_PIXEL = np.array([[[0.45, 0.33, 0.98, 0.50]]])

@pytest.mark.parametrize("f,source,new",[
    (gray_to_RGB, ONE_CHANNEL_WHITE_PIXEL, THREE_CHANNEL_WHITE_PIXEL),
    (RGB_to_gray, THREE_CHANNEL_WHITE_PIXEL, ONE_CHANNEL_WHITE_PIXEL)])
def test_colorspace_transformation(f, source, new):
    assert np.allclose(new, f(source), atol=1e-6)

@pytest.mark.parametrize("f,f_inv,image",[
    (gray_to_RGB, RGB_to_gray, ONE_CHANNEL_WHITE_PIXEL),
    (RGB_to_XYZ, XYZ_to_RGB, COLOR_PIXEL),
    (RGB_to_XYZ, XYZ_to_RGB, THREE_CHANNEL_WHITE_PIXEL),
    (RGB_to_XYZ, XYZ_to_RGB, OPAQUE_PIXEL),
    (RGB_to_YUV, YUV_to_RGB, COLOR_PIXEL),
    (RGB_to_YUV, YUV_to_RGB, THREE_CHANNEL_WHITE_PIXEL),
    (RGB_to_YUV, YUV_to_RGB, OPAQUE_PIXEL),
    (RGB_to_Lab, Lab_to_RGB, COLOR_PIXEL),
    (RGB_to_Lab, Lab_to_RGB, THREE_CHANNEL_WHITE_PIXEL),
    (RGB_to_Lab, Lab_to_RGB, OPAQUE_PIXEL)])
def test_colorspace_inverse(f, f_inv, image):
    print(image)
    print(f(image))
    print(f_inv(f(image)))
    assert np.allclose(image, f_inv(f(image)), atol=1e-6)

def test_add_alpha():
    assert np.allclose(COLOR_PIXEL, add_alpha(COLOR_PIXEL)[:,:,:3], atol=1e-6)

max_stars_repo_name: henryrobbins/dmtools

max_issues_repo_name: henryrobbins/dmtools

max_forks_repo_name: henryrobbins/dmtools


text:
#!/usr/bin/env python3
import sys
import numpy as np
from example import AmiciExample

class ExampleRobertson(AmiciExample):

    def __init__(self):
        AmiciExample.__init__( self )

        self.numX = 3
        self.numP = 3
        self.numK = 1

        self.modelOptions['theta'] = np.log10([0.04, 1e4, 3e7])
        self.modelOptions['kappa'] = [0.9]
        # self.modelOptions['ts'] = np.append(0, 4 * np.logspace(-6, 6))
        # logspace output from matlab slightly different:
        self.modelOptions['ts'] = [0.0, 4.0E-6, 7.030042499419172E-6, 1.2355374385909914E-5, 2.1714701757295438E-5, 3.8163819053999774E-5, 6.70733174724404E-5, 1.1788206810207238E-4, 2.071789871692485E-4, 3.641192711966091E-4, 6.39943487842423E-4, 0.0011247074791896924, 0.001976685344529533, 0.003474045495005412, 0.006105671868700933, 0.010730783181118898, 0.018859465453829577, 0.03314571091418737, 0.05825393910004978, 0.10238191690798133, 0.17993730675877775, 0.31624172843630804, 0.5557981977492555, 0.97682123781946, 1.7167737040515112, 3.017248025341849, 5.302845462360432, 9.319807242061488, 16.37966024952171, 28.787426920046055, 50.59420867421183, 88.91985930104782, 156.27759748218483, 274.6595380017199, 482.71705625573054, 848.380355168077, 1491.0374881259752, 2620.5142274381983, 4605.5815973057925, 8094.358590900622, 14225.921224892543, 25002.207701095904, 43941.64567950229, 77227.90915533014, 135728.87087581318, 238544.93266378547, 419245.253661875, 736827.9877306866, 1294983.017127056, 2275946.411607322, 4000000.0]
        self.modelOptions['pscale'] = 2

        self.solverOptions['atol'] = 1e-12
        self.solverOptions['rtol'] = 1e-8
        self.solverOptions['sens_ind'] = []
        self.solverOptions['sensi'] = 0
        self.solverOptions['sensi_meth'] = 1

def writeNoSensi(filename):
    ex = ExampleRobertson()

    ex.writeToFile(filename, '/model_robertson/nosensi/')

def writeSensiForward(filename):
    ex = ExampleRobertson()

    ex.solverOptions['sens_ind'] = np.arange(0, ex.numP)
    ex.solverOptions['sensi'] = 1

    ex.writeToFile(filename, '/model_robertson/sensiforward/')

def writeSensiForwardDense(filename):
    ex = ExampleRobertson()

    ex.solverOptions['sens_ind'] = np.arange(0, ex.numP)
    ex.solverOptions['sensi'] = 1
    ex.solverOptions['linsol'] = 1

    ex.writeToFile(filename, '/model_robertson/sensiforwarddense/')

def writeSensiForwardSPBCG(filename):
    ex = ExampleRobertson()

    ex.solverOptions['sens_ind'] = np.arange(0, ex.numP)
    ex.solverOptions['sensi'] = 1
    ex.solverOptions['linsol'] = 7
    ex.solverOptions['atol'] = 1e-14
    ex.solverOptions['rtol'] = 1e-12

    ex.writeToFile(filename, '/model_robertson/sensiforwardSPBCG/')

def main():
    if len(sys.argv) < 2:
        print("Error: Must provide output file as first and only argument.")
        sys.exit(1)
    filename = sys.argv[1]

    writeNoSensi(filename)
    writeSensiForward(filename)
    writeSensiForwardDense(filename)
    writeSensiForwardSPBCG(filename)

if __name__ == "__main__":
    main()

max_stars_repo_name: lcontento/AMICI

max_issues_repo_name: lcontento/AMICI

max_forks_repo_name: lcontento/AMICI


text:
from astropy.utils.data import get_pkg_data_filename

import numpy as np
import healpy as hp

import pytest

from .. import COLines

@pytest.mark.parametrize("include_high_galactic_latitude_clouds", [False, True])
def test_co(include_high_galactic_latitude_clouds):

    co = COLines(
        target_nside=16,
        output_units="K_CMB",
        has_polarization=True,
        line="10",
        include_high_galactic_latitude_clouds=include_high_galactic_latitude_clouds,
        polarization_fraction=0.001,
        theta_high_galactic_latitude_deg=20.0,
        random_seed=1234567,
        verbose=False,
        run_mcmole3d=False,
        coord="G",
    )

    co_map = co.signal()

    tag = "wHGL" if include_high_galactic_latitude_clouds else "noHGL"
    expected_map_filename = get_pkg_data_filename(
        "data/CO10_TQUmaps_{}_nside16_ring.fits.zip".format(tag)
    )
    expected_co_map = hp.read_map(expected_map_filename, field=(0, 1, 2))

    np.testing.assert_allclose(co_map, expected_co_map, rtol=1e-5)

reason: import numpy,from astropy


text:
""" Definition of autocorrelator interfacing commands. """
import numpy as np
import time

from mqlab.connections import Instrument

class Autocorrelator(Instrument):
    pass

class APEPulseCheck(Autocorrelator):
    """ Communication with APE Autocorrelator. Due to a bug (/"feature") of the APE software, it seems that to use the device remotely you must install (AND RUN CONTINUOUSLY) their pulseLink software and set the TCP/IP port for the device (e.g. using the one hard-coded below: 51123). It seems this is not saved when the device is power cycled and hence, must be entered each time. """

    def __init__(self):
        """ Instantiate communication, with has to be using TCP/IP ethernet protocols over the USB connection. """
        super().__init__(interface='ethernet', ip_address='127.0.0.1', port=51123, terminating_char='\r\n', timeout=5)

    def start(self):
        """ Start continuously sweeping. """
        self.send(':sta:start 1')

    def stop(self):
        """ Stop continuously sweeping. """
        self.send(':sta:start 0')

    def grab(self, raw=False, debug=False):
        """ Return delays [s] and AC intensities [a.u.] data, as shown on APE software (inc. filtering, averaging etc).

        Args:
            raw (bool): if True, grab data without any filtering / averaging etc.
        """
        # Manually request data as it seems the standard way of reading data in chunks doesn't work too well.
        if raw:
            self.send(':acf:data?')
        else:
            self.send(':acf:dacf?')
        # Short delay to allow data to be prepared for reading
        time.sleep(0.1)
        data = self.connection.socket.recv(100000)
        if debug:
            return data
        # Data is returned in little endian double float format, interlacing x and y data.
        # Thus, we must first decode and split it up.
        data_decoded = self._decode_binary_block(data, dtype='<d')
        delays = data_decoded[0::2] * 1e-12
        ys = data_decoded[1::2]
        return delays, ys

max_stars_repo_name: riwoodward/mqlab

max_issues_repo_name: riwoodward/mqlab

max_forks_repo_name: riwoodward/mqlab


text:
import json
import math
import numpy as np
from collections import OrderedDict

class Meta:

    def __init__(self, args, info):

        self.database = args.database
        self.normalize_decimal = lambda x, l, u: (x-l)/(u-l)
        self.normalize_log2 = lambda x, l, u: self.normalize_decimal(math.log2(x), math.log2(l), math.log2(u))
        self.normalize_log10 = lambda x, l, u: self.normalize_decimal(math.log10(x), math.log10(l), math.log10(u))

        if self.database == info.DATABASE_HyperRec:
            self.set_HyperRec()
            self.convert_config = self.convert_HyperRec
        elif self.database == info.DATABASE_LCBench:
            self.set_LCBench()
            self.convert_config = self.convert_LCBench

        self.set_train_test_task_ids(args, info)

    def set_train_test_task_ids(self, args, info):

        train_test_pairs = json.load(open(info.FILE_PAIRS, 'r'))
        training_tasks, testing_task = train_test_pairs[args.database][args.pair_id]

        self.training_task_ids = set([self.TASK2ID[task] for task in training_tasks.split(',')])
        self.testing_task_id = self.TASK2ID[testing_task]

    def set_HyperRec(self):

        self.NUM_PROFILE = 2 # 0: 43; 1: 12
        self.NUM_BASE_CONFIG = 100
        self.NUM_EPOCH = 75

        self.TASK2ID = {'ACTION40':0, 'AWA2':1, 'BOOKCOVER30':2, 'CALTECH256':3, 'CARS196':4,
                   'CIFAR10':5, 'CIFAR100':6, 'CUB200':7, 'FLOWER102':8, 'FOOD101':9,
                   'IP102':10, 'ISR':11, 'OIPETS':12, 'PLANT39':13, 'RESISC45':14,
                   'SCENE15':15, 'SDD':16, 'SOP':17,
                   'IMAGENET64SUB1':18, 'IMAGENET64SUB2':19, 'IMAGENET64SUB3':20,
                   'PLACE365SUB1':21, 'PLACE365SUB2':22, 'PLACE365SUB3':23,
                   'SUN397SUB1':24, 'SUN397SUB2':25, 'SUN397SUB3':26}
        self.ID2TASK = {v:k for k,v in self.TASK2ID.items()}

        self.INFO2ID = {'batchsize':0,
                   'model':1,
                   'optimizer':2, 'lr':3, 'weight_decay':4, 'betas0':5, 'betas1':6, 'momentum':7,
                   'scheduler':8, 'step_size':9, 'gamma':10, 'max_lr':11, 'step_size_up':12, 'T_0':13, 'T_mult':14, 'eta_min':15,
                   'task':16, 'epoch':17}
        self.ID2INFO = {v:k for k,v in self.INFO2ID.items()}

        self.TREE = OrderedDict({'root':['batchsize'],
                         'branches':OrderedDict({'model':OrderedDict({'resnet34':[],
                                                       'resnet50':[]}),
                                         'optimizer':OrderedDict({'Adam':['lr', 'weight_decay', 'betas0', 'betas1'],
                                                          'SGD':['lr', 'weight_decay', 'momentum']}),
                                         'scheduler':OrderedDict({'StepLR':['step_size', 'gamma'],
                                                          'ExponentialLR':['gamma'],
                                                          'CyclicLR':['max_lr', 'step_size_up', 'gamma'],
                                                          'CosineAnnealingWarmRestarts':['T_0', 'T_mult', 'eta_min']})})})

        self.CONDITION2ID = {CONDI:
                         {BRANCH: BRANCH_ID for BRANCH_ID, BRANCH in enumerate(BRANCHES)}
                      for CONDI, BRANCHES in self.TREE['branches'].items()}

        self.NESTED2ID = OrderedDict({
                      self.INFO2ID[CONDI]: OrderedDict({
                          BRANCH_ID: [self.INFO2ID[LEAF] for LEAF in LEAVES]
                      for BRANCH_ID, (BRANCH, LEAVES) in enumerate(BRANCHES.items())})
                    for CONDI, BRANCHES in self.TREE['branches'].items()})

    def convert_HyperRec(self, config):

        x = np.zeros(len(self.INFO2ID))

        x[self.INFO2ID['batchsize']] = self.normalize_decimal(config['batchsize'], 32, 128)
        x[self.INFO2ID['model']] = self.CONDITION2ID['model'][config['model']]
        x[self.INFO2ID['optimizer']] = self.CONDITION2ID['optimizer'][config['optimizer']]
        x[self.INFO2ID['scheduler']] = self.CONDITION2ID['scheduler'][config['scheduler']]

        for k, v in config['optimizer_param'].items():
            if k == 'lr':
                x[self.INFO2ID[k]] = self.normalize_log10(v, 1e-4, 1e-1)
            elif k == 'weight_decay':
                x[self.INFO2ID[k]] = self.normalize_log10(v, 1e-5, 1e-3)
            elif k == 'betas':
                x[self.INFO2ID[f'{k}0']] = self.normalize_log10(v[0], 0.5, 0.999)
                x[self.INFO2ID[f'{k}1']] = self.normalize_log10(v[1], 0.8, 0.999)
            elif k == 'momentum':
                x[self.INFO2ID[k]] = self.normalize_log10(v, 1e-3, 1)

        for k, v in config['scheduler_param'].items():
            if k == 'step_size':
                x[self.INFO2ID[k]] = self.normalize_decimal(v, 2, 20)
            elif k == 'gamma':
                if config['scheduler'] == 'StepLR':
                    x[self.INFO2ID[k]] = self.normalize_log10(v, 0.1, 0.5)
                elif config['scheduler'] == 'ExponentialLR':
                    x[self.INFO2ID[k]] = self.normalize_log10(v, 0.85, 0.999)
                elif config['scheduler'] == 'CyclicLR':
                    x[self.INFO2ID[k]] = self.normalize_log10(v, 0.1, 0.5)
            elif k == 'max_lr':
                x[self.INFO2ID[k]] = self.normalize_decimal(v, 1e-4*1.1, 1e-1*1.5)
            elif k == 'step_size_up':
                x[self.INFO2ID[k]] = self.normalize_decimal(v, 1, 10)
            elif k == 'T_0':
                x[self.INFO2ID[k]] = self.normalize_decimal(v, 2, 20)
            elif k == 'T_mult':
                x[self.INFO2ID[k]] = self.normalize_decimal(v, 1, 4)
            elif k == 'eta_min':
                x[self.INFO2ID[k]] = self.normalize_decimal(v, 1e-4*0.5, 1e-1*0.9)

        return x

    def set_LCBench(self):

        self.NUM_PROFILE = 1
        self.NUM_BASE_CONFIG = 100
        self.NUM_EPOCH = 52

        self.TASK2ID = {'APSFailure': 0, 'Amazon_employee_access': 1, 'Australian': 2, 'Fashion-MNIST': 3, 'KDDCup09_appetency': 4,
                   'MiniBooNE': 5, 'adult': 6, 'airlines': 7, 'albert': 8, 'bank-marketing': 9,
                   'blood-transfusion-service-center': 10, 'car': 11, 'christine': 12, 'cnae-9': 13, 'connect-4': 14,
                   'covertype': 15, 'credit-g': 16, 'dionis': 17, 'fabert': 18, 'helena': 19,
                   'higgs': 20, 'jannis': 21, 'jasmine': 22, 'jungle_chess_2pcs_raw_endgame_complete': 23, 'kc1': 24,
                   'kr-vs-kp': 25, 'mfeat-factors': 26, 'nomao': 27, 'numerai28.6': 28, 'phoneme': 29,
                   'segment': 30, 'shuttle': 31, 'sylvine': 32, 'vehicle': 33, 'volkert': 34}
        self.ID2TASK = {v:k for k,v in self.TASK2ID.items()}

        self.INFO2ID = {'batch_size':0, 'learning_rate':1, 'momentum':2, 'weight_decay':3,
                   'num_layers':4, 'max_units':5, 'max_dropout':6,
                   'task':7, 'epoch':8}
        self.ID2INFO = {v:k for k,v in self.INFO2ID.items()}

        self.TREE = OrderedDict({'root':['batch_size', 'learning_rate', 'momentum', 'weight_decay', 'num_layers', 'max_units', 'max_dropout'],
                         'branches':OrderedDict({})})

        self.CONDITION2ID = {CONDI:
                         {BRANCH: BRANCH_ID for BRANCH_ID, BRANCH in enumerate(BRANCHES)}
                      for CONDI, BRANCHES in self.TREE['branches'].items()}

        self.NESTED2ID = OrderedDict({
                      self.INFO2ID[CONDI]: OrderedDict({
                          BRANCH_ID: [self.INFO2ID[LEAF] for LEAF in LEAVES]
                      for BRANCH_ID, (BRANCH, LEAVES) in enumerate(BRANCHES.items())})
                    for CONDI, BRANCHES in self.TREE['branches'].items()})

    def convert_LCBench(self, config):

        x = np.zeros(len(self.INFO2ID))

        x[self.INFO2ID['batch_size']] = self.normalize_log2(config['batch_size'], 16, 512)
        x[self.INFO2ID['learning_rate']] = self.normalize_log10(config['learning_rate'], 1e-4, 1e-1)
        x[self.INFO2ID['momentum']] = self.normalize_decimal(config['momentum'], 0.1, 0.99)
        x[self.INFO2ID['weight_decay']] = self.normalize_decimal(config['weight_decay'], 1e-5, 1e-1)

        x[self.INFO2ID['num_layers']] = self.normalize_decimal(config['num_layers'], 1, 5)
        x[self.INFO2ID['max_units']] = self.normalize_log2(config['max_units'], 64, 1024)
        x[self.INFO2ID['max_dropout']] = self.normalize_decimal(config['max_dropout'], 0, 1)

        return x


text:
bl_info = {
    "name": "Drawchitecture",
    "description": "creates temporary workplanes by strokes or points for drawing in 3D with the grease pencil",
    "author": "Philipp Sommer",
    "version": (1, 1),
    "blender": (2, 80, 0),
    "location": "View3D",
    # "warning": "", # used for warning icon and text in addons panel
    # "wiki_url": ""
    # "tracker_url": "",
    "support": "TESTING",
    "category": "Paint"
}
import bpy
import math
import mathutils

from bpy.types import Panel
from math import *
from mathutils import Vector, Matrix
import numpy as np

bpy.types.Scene.gp_active = bpy.props.StringProperty(name='gp_active', description='saves last used GP',
                                                     default='empty', options={'HIDDEN'})
bpy.types.Scene.del_stroke = bpy.props.BoolProperty(name='del_stroke', description='V/H/3D: deletes last stroke',
                                                    default=False, options={'HIDDEN'})
bpy.types.Scene.expand_system = bpy.props.BoolProperty(name='expand_system', description='expands system tools',
                                                       default=True, options={'HIDDEN'})
bpy.types.Scene.expand_grid = bpy.props.BoolProperty(name='expand_grid', description='expands grid settings',
                                                     default=True, options={'HIDDEN'})
bpy.types.Scene.grid_scale = bpy.props.FloatVectorProperty(name='grid_scale',
                                                           description='saves the grid size of the workplane',
                                                           default=(1.0, 1.0, 0))
bpy.types.Scene.grid_count = bpy.props.IntVectorProperty(name='grid_count',
                                                         description='saves the grid size of the workplane',
                                                         default=(100, 100, 0))
bpy.types.Scene.plane_location = bpy.props.FloatVectorProperty(name='plane_location',
                                                               description='global memory for wp location',
                                                               default=(0.0, 0.0, 0.0))

def update_offset(self, context):
    """updates the position of the plane when the Factor in UI is change
    """
    if 'workplane_TEMPORARY' in (obj.name for obj in bpy.data.objects):
        wp = bpy.data.objects['workplane_TEMPORARY']
        # rotation in euler
        eu = wp.rotation_euler
        # offset factor in UI
        factor_offset = bpy.context.scene.plane_offset
        # Defining Vector for Translation in Z Axis and rotating it to be the normal of the plane
        vec_offset = Vector((0, 0, factor_offset))
        vec_offset.rotate(eu)

        loc = bpy.context.scene.plane_location
        vec_loc = Vector((loc[0], loc[1], loc[2]))
        wp.location = vec_loc + vec_offset

bpy.types.Scene.plane_offset = bpy.props.FloatProperty(name='plane_offset',
                                                       description='plane offset in normal-direction of plane',
                                                       default=0.0,
                                                       update=update_offset)

def cross(a, b):
    """ simple cross product formula for calculating normal vector
    """
    c = Vector((a[1] * b[2] - a[2] * b[1],
                a[2] * b[0] - a[0] * b[2],
                a[0] * b[1] - a[1] * b[0]))
    return c

def activate_gp():
    """activate last GP or create GP
    """
    if bpy.context.scene.gp_active == 'empty':
        # if gp objects exist choose random gp object if not yet initialized as active gp object
        for obj in bpy.data.objects:
            if obj.type == 'GPENCIL':
                bpy.context.scene.gp_active = obj.name
                bpy.context.view_layer.objects.active = bpy.data.objects[obj.name]
                break
        # if no gp objects exist add new gp object
        if bpy.context.scene.gp_active == 'empty':
            print('activate_gp: no gp object detected, creating new GP')
            add_GP()
            return {'GP added'}
    else:
        name_active = bpy.context.scene.gp_active
        # if there is an object with the same name as the saved GP, activate it
        if (name_active in (gp_obj.name for gp_obj in bpy.data.objects)):
            bpy.context.view_layer.objects.active = bpy.data.objects[name_active]
            return {'FINISHED'}
        else:
            print('activate_gp: gp object not found, creating new GP')
            add_GP()
            return {'GP added'}

def add4arrays():
    """ add 4x Array-modifier to active Object (Plane) to achieve grid-like-Workplane
    """
    obj = bpy.context.active_object
    step = radians(90)
    for i in range(0, 2):
        modifier = obj.modifiers.new(name='AR' + str(i), type='ARRAY')
        modifier.count = 100
        modifier.relative_offset_displace[0] = cos(step * i)
        modifier.relative_offset_displace[1] = sin(step * i)
    for i in range(2, 4):
        modifier = obj.modifiers.new(name='AR' + str(i), type='ARRAY')
        modifier.count = 2
        modifier.relative_offset_displace[0] = cos(step * i)
        modifier.relative_offset_displace[1] = sin(step * i)
    obj.modifiers['AR0'].count = bpy.context.scene.grid_count[0]
    obj.modifiers['AR1'].count = bpy.context.scene.grid_count[1]

def add_GP():
    """Create standard GP object
    """
    deselect_all()
    a = []
    b = []
    name = gpencil_obj_name()
    # list of grease_pencil object names before adding new one
    for o in bpy.data.grease_pencil:
        a.append(o.name)

    # adding new GP Object
    bpy.ops.object.gpencil_add(location=(0, 0, 0), rotation=(0, 0, 0), type='EMPTY')
    # empty Grease Pencil Object at 0,0,0 otherwise gp stroke point coordinates are offset
    # name + Number by counting all other GP Objects
    bpy.context.view_layer.objects.active.name = name
    # lock in place at 0,0,0 because point.coordinates refer to GP Origin
    bpy.context.view_layer.objects.active.lock_location = [True for x in range(3)]

    # find out the name of newly created grease_pencil object to rename it properly
    for o in bpy.data.grease_pencil:
        b.append(o.name)
    newgpname = list(set(b) - set(a))
    # name + Number same as GP Object
    bpy.data.grease_pencil[newgpname[0]].name = name
    save_active_gp()
    # name + Number by counting all other grease_pencil objects
    # bpy.data.grease_pencil[newgpname[0]].name = gpname()

def add_workplane_3p():
    """Creates Plane through 3 selected points of active GP Object (selected in Editmode)
    """
    selected_points = []

    if bpy.context.view_layer.objects.active.type == 'GPENCIL':
        # name of the active object (Type Gpencil Object)
        name_active = bpy.context.view_layer.objects.active.name
    else:
        name_active = bpy.context.scene.gp_active

    if (name_active in (gp_pen.name for gp_pen in bpy.data.grease_pencil)):
        gp_pen = bpy.data.grease_pencil[name_active]
        if gp_pen.layers.active:
            if gp_pen.layers.active.active_frame.strokes:
                for stroke in gp_pen.layers.active.active_frame.strokes:
                    for point in stroke.points:
                        if point.select:
                            selected_points.append(point.co)

    print('add_workplane_3p: selected_points:')
    print(selected_points)
    if len(selected_points) == 0:
        print('no point selected')
    elif len(selected_points) == 1:
        print('1 point selected - creating horizontal plane')
        select_p1 = selected_points[-1]
        plane_array(select_p1, (0, 0, 0), '1p')
        gpencil_paint_mode()

    elif len(selected_points) == 2:
        print('2 points selected - creating plane through 2 points')
        select_p1 = selected_points[-1]
        select_p2 = selected_points[-2]
        plane_array(select_p1, select_p2, '3d')
        gpencil_paint_mode()

    elif len(selected_points) >= 3:
        print('3 or more points selected - creating plane through 3 points')
        select_p1 = selected_points[-1]
        select_p2 = selected_points[-2]
        select_p3 = selected_points[-3]
        v1 = select_p2 - select_p1
        # print(v1)
        v2 = select_p3 - select_p1
        # print(v2)
        v_normal = Vector((cross(v1, v2)))
        # print(v_normal)
        p_normal = select_p1 + v_normal
        plane_array(select_p1, p_normal, '3p')
        gpencil_paint_mode()

    return {'FINISHED'}

def angle_between(v1, v2):
    """ Returns the angle in radians between vectors 'v1' and 'v2'
    """
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    angle = np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
    return angle

def angle_between_3d(z_dif, v_xparallel):
    """ Returns the angle in radians between vectors 'v1' and 'v2'
    v2 mostly used as vector parallel to x-y-plane for calculating x rotation
    """
    mag = np.sqrt(v_xparallel.dot(v_xparallel))
    angle_rad = atan(z_dif / mag)
    return angle_rad

def angle_between_z(v1, v2):
    """ returns the angle in radians between vectors 'v1' and 'v2' for z
    """
    v1_2d_z = Vector((v1[0], v1[1]))
    v2_2d_z = Vector((v2[0], v2[1]))
    angle_z = angle_between(v1_2d_z, v2_2d_z)
    return angle_z

def calc_location_2p(point_a, point_b):
    """ calculates midpoint of line between two input points
    """
    loc = []
    loc.append((point_a[0] + point_b[0]) / 2)
    loc.append((point_a[1] + point_b[1]) / 2)
    loc.append((point_a[2] + point_b[2]) / 2)
    return loc

def calc_rotation_2p_zh(point_a, point_b):
    """ returns rotation vector for a horizontal plane by 2 points
    """
    if point_a[0] > point_b[0]:
        v_2p = point_b - point_a
    else:
        v_2p = point_a - point_b

    v_y = Vector((0, 1, 0))
    z = angle_between_z(v_y, v_2p)

    return ((0, 0, z))

def calc_rotation_2p_zv(point_a, point_b):
    """ returns rotation vector of a Plane by 2 points
    first: set y rotation to 90°
    then: rotation in z
    """
    if point_a[0] > point_b[0]:
        v_2p = point_b - point_a
    else:
        v_2p = point_a - point_b

    v_y = Vector((0, 1, 0))
    y = 90 * math.pi / 180
    z = angle_between_z(v_y, v_2p)

    return ((0, y, z))

def calc_rotation_2p_3d(point_a, point_b):
    """returns rotation vector for plane by 2 Points
    first: rotation in z
    then: roration in x by z difference and projected distance of points"""
    if point_a[0] > point_b[0]:
        v_2p = point_b - point_a
        z_dif = point_b[2] - point_a[2]
    else:
        v_2p = point_a - point_b
        z_dif = point_a[2] - point_b[2]

    v_y = Vector((0, 1, 0))
    v_xparallel = v_2p
    v_xparallel[2] = 0
    x = angle_between_3d(z_dif, v_xparallel)
    z = angle_between_z(v_y, v_2p)

    return ((x, 0, z))

def calc_rotation_3p(point_a, point_b):
    """returns rotation vector for plane by 3 Points
    first: rotation in z
    then: rotation in x with the normal-vector of plane
    adding 90° to rotation for final plane
    """
    if point_a[0] > point_b[0]:
        v_2p = point_b - point_a
        z_dif = point_b[2] - point_a[2]
    else:
        v_2p = point_a - point_b
        z_dif = point_a[2] - point_b[2]

    v_y = Vector((0, 1, 0))
    v_xparallel = v_2p
    v_xparallel[2] = 0

    # adding 90 degrees to the x rotation because it is the normal vector
    ortho = 90 * math.pi / 180
    x = ortho + angle_between_3d(z_dif, v_xparallel)
    # print(x)
    z = angle_between_z(v_y, v_2p)
    # print(z)

    return ((x, 0, z))

def deselect_all():
    """deselects every object
    """
    if not bpy.context.mode == 'OBJECT':
        bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.select_all(action='DESELECT')

def deselect_all_gp():
    """deselects all gp vertices / strokes
    """
    if not bpy.context.mode == 'EDIT_GPENCIL':
        bpy.ops.object.mode_set(mode='EDIT_GPENCIL')
    bpy.ops.gpencil.select_all(action='DESELECT')

def find_3dview_space():
    """returns 3D_View and its screen space
    """
    area = None

    for a in bpy.data.window_managers[0].windows[0].screen.areas:
        if a.type == 'VIEW_3D':
            area = a
            break
    if area:
        space = area.spaces[0]
    else:
        space = bpy.context.space_data

    return space

def gpencil_obj_name():
    """Generates Name for new GP object based on existing GP objects
    """
    # for o in bpy.data.objects:
    #    if o.type == 'GPENCIL':
    #        num = num + 1

    # list all existing GP objects
    namelist = [gp_obj.name for gp_obj in bpy.data.objects if gp_obj.type == 'GPENCIL']

    num = 1
    name = 'Drawing ' + str(num)
    # as long as name+num is allready taken, count up num
    while name in namelist:
        num = num + 1
        name = 'Drawing ' + str(num)

    return name

def gpencil_paint_mode():
    """Gpencil has to be selected! activates DRAW mode / GPENCIL_PAINT mode, unless it's already active
    """
    if not bpy.context.mode == 'PAINT_GPENCIL':
        bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
    return {'FINISHED'}

def laststroke():
    """returns last stroke of active Greasepencil object
    returns 'No GP object active' when no GP Obj is active
    returns 'Names not equal' if data.objects GP name + data.grease_pencil object Name of active GP Object are not equal
    """
    if bpy.context.view_layer.objects.active.type == 'GPENCIL':
        # name of the active object (Type Gpencil Object)
        name_active = bpy.context.view_layer.objects.active.name

        if (name_active in (gp_pen.name for gp_pen in bpy.data.grease_pencil)):
            gp_pen = bpy.data.grease_pencil[name_active]
            if gp_pen.layers.active:
                if gp_pen.layers.active.active_frame.strokes:
                    ls = gp_pen.layers.active.active_frame.strokes[-1]
                    return ls
                else:
                    print('laststroke: active GP Obj has no strokes')
                    return {'No Strokes'}
            else:
                print('laststroke: active GP Obj has no strokes')
                return {'No Strokes'}
        else:
            print('laststroke: Names of active GP object and its bpy.data.grease_pencil equivalent must be equal')
            return {'Names not equal'}
    else:
        print('No GP object active')
        return {'GP obj inactive'}

# def offset_plane():
#
#    cube = bpy.data.objects["Cube"]
#    # one blender unit in x-direction
#    vec = mathutils.Vector((1.0, 0.0, 0.0))
#    inv = cube.rotation_euler.to_matrix()
#    # vec aligned to local axis
#    vec_rot = vec * inv
#    cube.location = cube.location + vec_rot

def plane_array(p1, p2, rotation):
    """adds an array of 1m by 1m planes at given location, parameter rotation defines way to calculate angle
    """
    # define standard scale / count
    save_active_gp()
    # delete last workplane
    if bpy.data.objects:
        deselect_all()
        # select last temporary workplane
        for o in bpy.data.objects:
            if o.name == 'workplane_TEMPORARY':
                o.select_set(state=True)
                # save settings of last workplane
                save_grid_settings()
                break
        # delete last workplane
        bpy.ops.object.delete()
        bpy.context.scene.plane_offset = 0.0
    if rotation == '1p':
        p_loc = calc_location_2p(p1, p1)
        p_rot = ((0, 0, 0))
    elif rotation == '3p':
        p_loc = calc_location_2p(p1, p1)
    else:
        p_loc = calc_location_2p(p1, p2)

    if rotation == 'v':
        p_rot = calc_rotation_2p_zv(p1, p2)
    elif rotation in ('h', 'bp'):
        p_rot = calc_rotation_2p_zh(p1, p2)
    elif rotation == '3d':
        p_rot = calc_rotation_2p_3d(p1, p2)
    elif rotation == '3p':
        p_rot = calc_rotation_3p(p1, p2)

    bpy.context.scene.plane_location = p_loc
    bpy.ops.mesh.primitive_plane_add(size=1, location=p_loc, rotation=p_rot)

    baseplane = bpy.context.active_object
    baseplane.name = 'workplane_TEMPORARY'
    add4arrays()
    baseplane.scale = bpy.context.scene.grid_scale

    # set material of plane
    # mat = bpy.data.materials['Mat_Transparent_White']
    # baseplane.active_material = mat
    baseplane.show_wire = True
    deselect_all()
    activate_gp()
    if rotation not in ('3p', 'bp'):
        if bpy.context.scene.del_stroke:
            bpy.ops.dt.delete_last_stroke()
    return {'FINISHED'}

def save_active_gp():
    """save active gp obj in global variable
    """
    if bpy.context.view_layer.objects.active:
        if (bpy.context.view_layer.objects.active.type == 'GPENCIL'):
            # name of the active object (Type Gpencil Object)
            name_active = bpy.context.view_layer.objects.active.name
            if (name_active in (gp_pen.name for gp_pen in bpy.data.grease_pencil)):
                # select data.grease_pencil object to select its strokes
                bpy.context.scene.gp_active = name_active
            else:
                bpy.context.scene.gp_active = 'empty'
        else:
            bpy.context.scene.gp_active = 'empty'
    else:
        bpy.context.scene.gp_active = 'empty'

def save_grid_settings():
    """Stores Grid settings of workplane to global Property of scene
    """
    bpy.context.scene.grid_scale = bpy.data.objects['workplane_TEMPORARY'].scale
    bpy.context.scene.grid_count = (
        bpy.data.objects['workplane_TEMPORARY'].modifiers[0].count,
        bpy.data.objects['workplane_TEMPORARY'].modifiers[1].count, 0)

def unit_vector(vector):
    """ Returns the unit vector of the input vector.
    """
    return vector / np.linalg.norm(vector)

class SetupDrawchitecture(bpy.types.Operator):  # standard plane
    """initializes the setup: colors & viewsettings
    """
    bl_idname = 'dt.setup'
    bl_label = 'SetupDrawchitecture View'

    def execute(self, context):
        # Viewport shader mode set to 'WIREFRAME' for transparent objects
        find_3dview_space().shading.type = 'WIREFRAME'
        find_3dview_space().shading.show_xray_wireframe = True

        # Disable Floor Grid + Cursor in active View3D, make Vertices in editmode visible
        find_3dview_space().overlay.show_floor = False
        find_3dview_space().overlay.show_cursor = False
        find_3dview_space().overlay.show_object_origins = False
        find_3dview_space().overlay.vertex_opacity = 1

        # Set 3d View Background color to white and Wire color to grey
        bpy.context.preferences.themes[0].view_3d.space.gradients.high_gradient = (0.8, 0.8, 0.8)
        bpy.context.preferences.themes[0].view_3d.wire = (0.5, 0.5, 0.5)

        # Set Stroke Placement in active Scene to 'Surface'
        bpy.context.window.scene.tool_settings.gpencil_stroke_placement_view3d = 'SURFACE'

        # plane_array(Vector((0, 0.5, 0)), Vector((1, 0.5, 0)), 'h')  # default workplane at 0,0,0

        # create GP object or activate last GP object
        activate_gp()
        # switch to DRAW mode
        gpencil_paint_mode()

        return {'FINISHED'}

class InitializeDrawchitecture(bpy.types.Operator):  # standard plane
    """initializes the setup: default workplane at start, activates GP mode
    """
    bl_idname = 'dt.initialize'
    bl_label = 'Create Baseplane (+ GP Object if there is none)'

    def execute(self, context):
        # default workplane at 0,0,0
        plane_array(Vector((0, 0.5, 0)), Vector((1, 0.5, 0)), 'bp')

        # create GP object if there is none
        # if not [obj for obj in bpy.data.objects if obj.type == 'GPENCIL']:
        # add_GP()
        activate_gp()
        # switch to DRAW mode
        gpencil_paint_mode()

        return {'FINISHED'}

class AddGPObject(bpy.types.Operator):
    """Adds new GP Object to Scene, locked at 0.0.0
    """
    bl_idname = 'dt.add_gp_object'
    bl_label = 'adds gp object, locked at 0.0.0'

    def execute(self, context):
        add_GP()
        gpencil_paint_mode()
        return {'FINISHED'}

class AddRotation(bpy.types.Operator):
    """Adds given rotation to the rotation vector of workplane_TEMPORARY, property sets +/- and Achsis
    only shown when workplane_Temporary exists
    """
    bl_idname = 'dt.add_rotation'
    bl_label = 'add rotation'
    axis: bpy.props.StringProperty()
    rotation: bpy.props.FloatProperty()

    # axis_index = bpy.props.IntProperty()

    def execute(self, context):
        wp = bpy.data.objects['workplane_TEMPORARY']
        rotation_old = wp.rotation_euler
        rotation_add = self.rotation * math.pi / 180

        if self.axis == 'x':
            axis_index = 0
        elif self.axis == 'y':
            axis_index = 1
        elif self.axis == 'z':
            axis_index = 2
        else:
            print('error: axis must be x / y / z')
            return {'CANCELLED'}

        bpy.data.objects['workplane_TEMPORARY'].rotation_euler[axis_index] = rotation_old[axis_index] + rotation_add
        return {'FINISHED'}

class ClearPlaneAndGP(bpy.types.Operator):
    """ Deletes the Temporary Workplane and all GP Objects
    """
    bl_idname = 'dt.clear_all_objects'
    bl_label = 'clears all Temporary Workplane + gp objects in project'

    def execute(self, context):
        if not bpy.context.mode == 'OBJECT':
            bpy.ops.object.mode_set(mode='OBJECT')
        # delete all objects
        if bpy.data.objects:
            for o in bpy.data.objects:
                if o.name == 'workplane_TEMPORARY':
                    o.select_set(state=True)
                if o.type == 'GPENCIL':
                    o.select_set(state=True)
            bpy.ops.object.delete()

            if bpy.data.grease_pencil:
                for gp in bpy.data.grease_pencil:
                    bpy.data.grease_pencil.remove(gp)
            bpy.context.scene.gp_active = 'empty'
            bpy.context.scene.plane_offset = 0.0
            bpy.ops.dt.initialize()
            return {'FINISHED'}
        else:
            bpy.context.scene.gp_active = 'empty'
            bpy.ops.dt.initialize()
            return {'FINISHED'}

class DeleteLastStroke(bpy.types.Operator):
    """For V/H/3D: deletes last drawn stroke of active GP Object
    """
    bl_idname = 'dt.delete_last_stroke'
    bl_label = 'deletes last stroke of active GP object'

    def execute(self, context):
        save_active_gp()
        activate_gp()
        gpencil_paint_mode()

        active_name = bpy.context.scene.gp_active
        if bpy.data.grease_pencil[active_name].layers.active:
            if bpy.data.grease_pencil[active_name].layers.active.active_frame.strokes:
                # deselect gp to only delete latest stroke
                deselect_all_gp()
                bpy.data.grease_pencil[active_name].layers.active.active_frame.strokes[-1].select = True
                bpy.ops.gpencil.delete(type='STROKES')
            else:
                print('DeleteLastStroke: Active Grease Pencil has no strokes to be deleted')
        else:
            print('DeleteLastStroke: Active Grease Pencil has no strokes to be deleted')

        gpencil_paint_mode()
        return {'FINISHED'}

class RemoveGPObject(bpy.types.Operator):
    """Removes the active GP Object
    """
    bl_idname = 'dt.remove_gp_object'
    bl_label = 'removes active GP Object'

    def execute(self, context):
        # make sure no other object is selected
        deselect_all()
        # activate last gp or write gp name in global variable
        activate_gp()

        # object mode must be activated to delete an object
        if not bpy.context.mode == 'OBJECT':
            bpy.ops.object.mode_set(mode='OBJECT')

        name_active = bpy.context.scene.gp_active
        # if there is an object with the same name as the saved GP, delete it
        if (name_active in (gp_obj.name for gp_obj in bpy.data.objects)):
            bpy.data.objects[name_active].select_set(state=True)
            bpy.ops.object.delete()
            # clear saved GP name to activate any other GP or create new if no GP left
            bpy.context.scene.gp_active = 'empty'

        activate_gp()
        gpencil_paint_mode()
        return {'FINISHED'}

class ResetScale(bpy.types.Operator):
    """Reset X and Y scale + count of workplane
    """
    bl_idname = 'dt.reset_scale'
    bl_label = 'reset scale + count'

    def execute(self, context):
        scale_default = (1.0, 1.0, 0)

        wp = bpy.data.objects['workplane_TEMPORARY']

        wp.scale = scale_default
        wp.modifiers[0].count = 100
        wp.modifiers[1].count = 100

        save_grid_settings()
        return {'FINISHED'}

class SelectGPobject(bpy.types.Operator):
    """Shows buttons with all GP Objects and selects them
    (Problems with hidden GP Objects)
    """
    bl_idname = 'dt.select_gp_object'
    bl_label = 'Activates Greasepencil Object by Name on Button'
    gp: bpy.props.StringProperty(default='', options={'SKIP_SAVE'})

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        deselect_all()
        gp = context.scene.objects.get(self.gp)
        bpy.context.view_layer.objects.active = gp
        context.scene.grease_pencil = gp.grease_pencil
        save_active_gp()
        gpencil_paint_mode()
        return {'FINISHED'}

class SwitchScaleAndCount(bpy.types.Operator):
    """Switches X and Y scale + count of workplane
    """
    bl_idname = 'dt.switch_scale_and_count'
    bl_label = 'switch x/y'

    def execute(self, context):
        scale = bpy.data.objects['workplane_TEMPORARY'].scale
        scale_switched = (scale[1], scale[0], scale[2])

        wp = bpy.data.objects['workplane_TEMPORARY']

        wp.scale = scale_switched

        count_x = wp.modifiers[0].count
        wp.modifiers[0].count = wp.modifiers[1].count
        wp.modifiers[1].count = count_x

        save_grid_settings()
        return {'FINISHED'}

class WPstrokeV(bpy.types.Operator):  # First+ Last Point of last Stroke create vertical plane
    """adds VERTICAL workplane at last grease pencil stroke by start + endpoint of stroke
    ! GP Object must be selected first
    ! GP Object and Grease_Pencil object need equal Names
    """
    bl_idname = 'dt.work_plane_on_stroke_2p'
    bl_label = 'add vertical workplane by stroke start end'

    def execute(self, context):
        # last greasepencil stroke
        # gp_laststroke = bpy.data.grease_pencil[-1].layers.active.active_frame.strokes[-1]
        ls = laststroke()
        if ls == {'GP obj inactive'}:
            return {'CANCELLED'}
        elif ls == {'Names not equal'}:
            return {'CANCELLED'}
        elif ls == {'No Strokes'}:
            return {'CANCELLED'}
        else:
            p1 = ls.points[0].co
            p2 = ls.points[-1].co
            plane_array(p1, p2, "v")
            # DELETE LAST GP STROKE
            # if not bpy.context.mode == 'EDIT':
            #    bpy.ops.object.mode_set(mode='EDIT')
            # gp_laststroke
            # bpy.ops.gpencil.delete(type='STROKES')
            gpencil_paint_mode()
            return {'FINISHED'}

class WPStrokeH(bpy.types.Operator):  # First+ Last Point of last Stroke create horizontal plane
    """adds HORIZONTAL workplane at last grease pencil stroke by start + endpoint of stroke
    ! GP Object must be selected first
    ! GP Object and Grease_Pencil object need equal Names
    """
    bl_idname = 'dt.work_plane_on_stroke_2p_horizontal'
    bl_label = 'add horizontal workplane by stroke start end'

    def execute(self, context):
        # last greasepencil stroke
        # gp_laststroke = bpy.data.grease_pencil[-1].layers.active.active_frame.strokes[-1]
        ls = laststroke()
        if ls == {'GP obj inactive'}:
            return {'CANCELLED'}
        elif ls == {'Names not equal'}:
            return {'CANCELLED'}
        elif ls == {'No Strokes'}:
            return {'CANCELLED'}
        else:
            p1 = ls.points[0].co
            p2 = ls.points[-1].co
            plane_array(p1, p2, "h")
            gpencil_paint_mode()
            return {'FINISHED'}

class WPstroke3D(bpy.types.Operator):  # First+ Last Point of last Stroke create horizontal plane
    """adds tilted Plane to any Stroke by Start + Endpoint of Stroke
    ! GP Object must be selected first -
    ! GP Object and Grease_Pencil object need equal Names
    """
    bl_idname = 'dt.work_plane_on_stroke_2p_3d'
    bl_label = 'align workplane to tilted 3d-strokes by start end'

    def execute(self, context):
        ls = laststroke()
        if ls == {'GP obj inactive'}:
            return {'CANCELLED'}
        elif ls == {'Names not equal'}:
            return {'CANCELLED'}
        elif ls == {'No Strokes'}:
            return {'CANCELLED'}
        else:
            p1 = ls.points[0].co
            p2 = ls.points[-1].co
            plane_array(p1, p2, '3d')
            gpencil_paint_mode()
            return {'FINISHED'}

class WPselect3P(bpy.types.Operator):  # Plane from 1/2/3 Points in Selection
    """First click: enter Stroke-Edit mode, select up to 3 points (hold SHIFT)
    Second Click: create Plane through Points
    1point: horizontal plane
    2points: 3D Plane through 2 Points
    3points: 3D Plane through 3 Points
    """
    bl_idname = 'dt.work_plane_points_3d'
    bl_label = 'Enters Editmode, or converts up to 3 Selected GP_Points to a Plane'

    def execute(self, context):
        # save gp here?
        save_active_gp()
        activate_gp()
        if not bpy.context.mode == 'EDIT_GPENCIL':
            bpy.ops.object.mode_set(mode='EDIT_GPENCIL')
            bpy.context.scene.tool_settings.gpencil_selectmode = 'POINT'
        else:
            # prevent this mode from deleting last stroke
            if bpy.context.scene.del_stroke:
                bpy.context.scene.del_stroke = False
                add_workplane_3p()
                bpy.context.scene.del_stroke = True
            else:
                add_workplane_3p()

            # reactivate gp here
            gpencil_paint_mode()
        return {'FINISHED'}

class View3DPanel:
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Drawchitecture'

class AddPanel(View3DPanel, Panel):
    """Interface
    """
    bl_label = 'Drawchitecture'

    # bl_context = 'objectmode' # without context works for all contexts
    # bl_category = 'Drawchitecture'

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True

        system_box = layout.box()
        system_box_title = system_box.row(align=True)
        system_box_title.label(text='System Tools', icon='SETTINGS')
        system_box_title_sub = system_box_title.row()
        system_box_title_sub.prop(bpy.context.scene, 'expand_system', text='', icon='THREE_DOTS', emboss=False)
        if bpy.context.scene.expand_system:
            system_box_col1 = system_box.column(align=True)
            system_box_col1.operator('dt.setup', text='Setup View', icon='PLAY')
            system_box_col1.operator('dt.clear_all_objects', text='Clear All Objects', icon='LIBRARY_DATA_BROKEN')

            bg_color = bpy.context.preferences.themes[0].view_3d.space.gradients
            color_col1 = system_box.column(align=True)
            color_col1.label(icon='COLOR', text='Colors')
            color_col1.prop(bg_color, 'high_gradient', text='background')

            wire_color = bpy.context.preferences.themes[0].view_3d
            color_col1.prop(wire_color, 'wire', text='wire lines')

        # box with Create WP options
        workplane_box = layout.box()
        workplane_box_title = workplane_box.row(align=True)
        workplane_box_title.label(text='Workplanes', icon='MESH_GRID')
        workplane_box_title.prop(bpy.context.scene, "del_stroke", text="delete Stroke")
        # Buttons
        workplane_box_row1 = workplane_box.row()
        workplane_box_row1.operator('dt.delete_last_stroke', text='Delete Last Stroke', icon='STROKE')
        workplane_box_col1 = workplane_box.column(align=True)
        workplane_box_col1.operator('dt.initialize', text='horizontal base plane', icon='AXIS_TOP')
        workplane_box_row2 = workplane_box_col1.row(align=True)
        if bpy.context.scene.del_stroke:
            workplane_box_row2.alert = True
        workplane_box_row2.operator('dt.work_plane_on_stroke_2p', text='V', icon='AXIS_FRONT')
        workplane_box_row2.operator('dt.work_plane_on_stroke_2p_horizontal', text='H', icon='AXIS_TOP')
        workplane_box_row2.operator('dt.work_plane_on_stroke_2p_3d', text='3D', icon='MOD_LATTICE')
        workplane_box_row3 = workplane_box_col1.row(align=True)
        if bpy.context.mode == 'EDIT_GPENCIL':
            workplane_box_row3.alert = True
        workplane_box_row3.operator('dt.work_plane_points_3d', text='select 1 / 2 / 3 points', icon='MOD_DATA_TRANSFER')

        if [obj for obj in bpy.data.objects if obj.name == 'workplane_TEMPORARY']:
            workplane_rotation_box = layout.box()
            workplane_rotation_box_title = workplane_rotation_box.row(align=True)
            workplane_rotation_box_title.label(text='Workplane Rotation', icon='FILE_REFRESH')
            wp_rot_box_row1 = workplane_rotation_box.row(align=True)
            wp_rot_box_row1_sub1 = wp_rot_box_row1.row()

            wp_rot_box_row1_sub1.prop(bpy.data.objects['workplane_TEMPORARY'], 'rotation_euler', text=' ')

            wp_rot_box_row1_sub2 = wp_rot_box_row1.column(align=True)
            minus_x = wp_rot_box_row1_sub2.operator('dt.add_rotation', text='- 45°')
            minus_x.axis = 'x'
            minus_x.rotation = -45
            minus_y = wp_rot_box_row1_sub2.operator('dt.add_rotation', text='- 45°')
            minus_y.axis = 'y'
            minus_y.rotation = -45
            minus_z = wp_rot_box_row1_sub2.operator('dt.add_rotation', text='- 45°')
            minus_z.axis = 'z'
            minus_z.rotation = -45

            wp_rot_box_row1_sub3 = wp_rot_box_row1.column(align=True)
            plus_x = wp_rot_box_row1_sub3.operator('dt.add_rotation', text='+ 45°')
            plus_x.axis = 'x'
            plus_x.rotation = 45
            plus_y = wp_rot_box_row1_sub3.operator('dt.add_rotation', text='+ 45°')
            plus_y.axis = 'y'
            plus_y.rotation = 45
            plus_z = wp_rot_box_row1_sub3.operator('dt.add_rotation', text='+ 45°')
            plus_z.axis = 'z'
            plus_z.rotation = 45

            wp_rot_box_row2 = workplane_rotation_box.row(align=True)
            wp_rot_box_row2.prop(bpy.context.scene, 'plane_offset')

        workplane_grid_box = layout.box()
        workplane_grid_box_title = workplane_grid_box.row(align=True)
        workplane_grid_box_title.label(text='Grid Size', icon='GRID')
        workplane_grid_box_title.prop(bpy.context.scene, 'expand_grid', text='', icon='THREE_DOTS', icon_only=True,
                                      emboss=False)
        if bpy.context.scene.expand_grid:
            if [obj for obj in bpy.data.objects if obj.name == 'workplane_TEMPORARY']:
                workplane_grid_box_row1 = workplane_grid_box.row(align=True)

                workplane_grid_box_row1_col1 = workplane_grid_box_row1.column(align=True)
                workplane_grid_box_row1_col1.label(text='scale')
                workplane_grid_box_row1_col1.prop(bpy.data.objects['workplane_TEMPORARY'], 'scale', index=0,
                                                  icon_only=True)
                workplane_grid_box_row1_col1.prop(bpy.data.objects['workplane_TEMPORARY'], 'scale', index=1,
                                                  icon_only=True)

                workplane_grid_box_row1_col2 = workplane_grid_box_row1.column(align=True)
                workplane_grid_box_row1_col2.label(text='count')
                workplane_grid_box_row1_col2.prop(bpy.data.objects['workplane_TEMPORARY'].modifiers[0], 'count',
                                                  icon_only=True)
                workplane_grid_box_row1_col2.prop(bpy.data.objects['workplane_TEMPORARY'].modifiers[1], 'count',
                                                  icon_only=True)

        workplane_grid_box_row2 = workplane_grid_box.row(align=True)
        workplane_grid_box_row2.operator('dt.switch_scale_and_count', icon='ARROW_LEFTRIGHT', text='switch')
        workplane_grid_box_row2.operator('dt.reset_scale', icon='LOOP_BACK', text='reset')

        box_gp = layout.box()
        # Show which GP Obj is active
        box_gp.label(text='Grease Pencil Objects: ' + bpy.context.scene.gp_active, icon='GREASEPENCIL')
        box_gp_row1 = box_gp.row(align=True)
        box_gp_row1.operator('dt.add_gp_object', icon='ADD', text='add new')
        box_gp_row1.operator('dt.remove_gp_object', icon='REMOVE', text='del active')

        greasepencils = [gp for gp in context.scene.objects if gp.type == 'GPENCIL']
        box_gp_col1 = box_gp.column(align=True)
        for gp in greasepencils:
            op = box_gp_col1.row()
            if gp.name == bpy.context.scene.gp_active:
                op.alert = True
            opo = op.operator('dt.select_gp_object', text=gp.name)
            opo.gp = gp.name
            # op.alert = True

# tuple of all used classes
classes = (
    SetupDrawchitecture, InitializeDrawchitecture, AddGPObject, AddRotation, ClearPlaneAndGP, DeleteLastStroke,
    RemoveGPObject, ResetScale, SelectGPobject, SwitchScaleAndCount, WPstrokeV, WPStrokeH, WPstroke3D, WPselect3P,
    AddPanel)

# registering/unregistering classes
def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)

max_stars_repo_name: Aachuma/Drawchitecture

max_issues_repo_name: Aachuma/Drawchitecture

max_forks_repo_name: Aachuma/Drawchitecture


text:
import argparse
import os

import cox.store
import numpy as np
import torch as ch
from cox import utils
from robustness import datasets, defaults, model_utils, train_eval_sample
from robustness.tools import helpers
from torch import nn
from torchvision import models

from utils import constants as cs
from utils import fine_tunify, transfer_datasets

import yaml

def str2bool(v):
    if isinstance(v, bool):
        return v
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

yaml_default_cfg_pth = "/srv/beegfs02/scratch/hl_task_prediction/data/nikola/code/robust-models-transfer/submit_jobs/debug_config.yaml"
config_parser = parser = argparse.ArgumentParser(description='Training Config', add_help=False)
parser.add_argument('--config', default=yaml_default_cfg_pth, type=str, metavar='FILE',
                    help='YAML config file specifying default arguments')

parser = argparse.ArgumentParser(description='Transfer learning via pretrained Imagenet models',
                                 conflict_handler='resolve')
parser = defaults.add_args_to_parser(defaults.CONFIG_ARGS, parser)
parser = defaults.add_args_to_parser(defaults.MODEL_LOADER_ARGS, parser)
parser = defaults.add_args_to_parser(defaults.TRAINING_ARGS, parser)
parser = defaults.add_args_to_parser(defaults.PGD_ARGS, parser)

# Custom arguments
parser.add_argument('--dataset', type=str, default='cifar',
                    help='Dataset (Overrides the one in robustness.defaults)')
parser.add_argument('--model-path', type=str, default='')
parser.add_argument('--resume', action='store_true',
                    help='Whether to resume or not (Overrides the one in robustness.defaults)')
parser.add_argument('--pytorch-pretrained', action='store_true',
                    help='If True, loads a Pytorch pretrained model.')
parser.add_argument('--cifar10-cifar10', action='store_true',
                    help='cifar10 to cifar10 transfer')
parser.add_argument('--subset', type=int, default=None,
                    help='number of training data to use from the dataset')
parser.add_argument('--no-tqdm', type=int, default=1,
                    choices=[0, 1], help='Do not use tqdm.')
parser.add_argument('--no-replace-last-layer', action='store_true',
                    help='Whether to avoid replacing the last layer')
parser.add_argument('--freeze-level', type=int, default=4,
                    help='Up to what layer to freeze in the pretrained model (assumes a resnet architectures)')
parser.add_argument('--additional-hidden', type=int, default=0,
                    help='How many hidden layers to add on top of pretrained network + classification layer')
parser.add_argument('--per-class-accuracy', action='store_true', help='Report the per-class accuracy. '
                    'Can be used only with pets, caltech101, caltech256, aircraft, and flowers.')

parser.add_argument('--act-min-slope', type=float, default=-1.0)
parser.add_argument('--act-max-slope', type=float, default=-1.0)
parser.add_argument('--act-randomness-type', type=str, default='uniform')

parser.add_argument('--optimizer', type=str, default='sgd')
parser.add_argument('--n-eval-samples', type=int, default=1)

parser.add_argument('--only-learn-slope-trf', type=str2bool, default=False)

def main(args, store):
    '''Given arguments and a cox store, trains as a model. Check out the
    argparse object in this file for argument options.
    '''
    ds, train_loader, validation_loader = get_dataset_and_loaders(args)

    if args.per_class_accuracy:
        assert args.dataset in ['pets', 'caltech101', 'caltech256', 'flowers', 'aircraft'], \
            f'Per-class accuracy not supported for the {args.dataset} dataset.'

        # VERY IMPORTANT
        # We report the per-class accuracy using the validation
        # set distribution. So ignore the training accuracy (as you will see it go
        # beyond 100. Don't freak out, it doesn't really capture anything),
        # just look at the validation accuarcy
        args.custom_accuracy = get_per_class_accuracy(args, validation_loader)

    model, checkpoint = get_model(args, ds)

    if args.eval_only:
        return train_eval_sample.eval_model(args, model, validation_loader, store=store, n_eval_samples=args.n_eval_samples)

    update_params = freeze_model(model, freeze_level=args.freeze_level)

    print(f"**********************************************************")
    print(f"Exp name: {args.exp_name}")
    print(f"Out dir: {args.out_dir}")

    print(f"Dataset: {args.dataset}")
    print(f"Data: {args.data}")
    print(f"Data aug: {args.data_aug}")

    print(f"Arch: {args.arch}")
    print(f"Model path: {args.model_path}")
    print(f"**********************************************************")
    train_eval_sample.train_model(args, model, (train_loader, validation_loader), store=store,
                      checkpoint=checkpoint, update_params=update_params, n_eval_samples=args.n_eval_samples)

def get_per_class_accuracy(args, loader):
    '''Returns the custom per_class_accuracy function. When using this custom function
    look at only the validation accuracy. Ignore trainig set accuracy.
    '''
    def _get_class_weights(args, loader):
        '''Returns the distribution of classes in a given dataset.
        '''
        if args.dataset in ['pets', 'flowers']:
            targets = loader.dataset.targets

        elif args.dataset in ['caltech101', 'caltech256']:
            targets = np.array([loader.dataset.ds.dataset.y[idx]
                                for idx in loader.dataset.ds.indices])

        elif args.dataset == 'aircraft':
            targets = [s[1] for s in loader.dataset.samples]

        counts = np.unique(targets, return_counts=True)[1]
        class_weights = counts.sum()/(counts*len(counts))
        return ch.Tensor(class_weights)

    class_weights = _get_class_weights(args, loader)

    def custom_acc(logits, labels):
        '''Returns the top1 accuracy, weighted by the class distribution.
        This is important when evaluating an unbalanced dataset.
        '''
        batch_size = labels.size(0)
        maxk = min(5, logits.shape[-1])
        prec1, _ = helpers.accuracy(
            logits, labels, topk=(1, maxk), exact=True)

        normal_prec1 = prec1.sum(0, keepdim=True).mul_(100/batch_size)
        weighted_prec1 = prec1 * class_weights[labels.cpu()].cuda()
        weighted_prec1 = weighted_prec1.sum(
            0, keepdim=True).mul_(100/batch_size)

        return weighted_prec1.item(), normal_prec1.item()

    return custom_acc

def get_dataset_and_loaders(args):
    '''Given arguments, returns a datasets object and the train and validation loaders.
    '''
    if args.dataset in ['imagenet', 'stylized_imagenet']:
        ds = datasets.ImageNet(args.data)
        train_loader, validation_loader = ds.make_loaders(
            only_val=args.eval_only, batch_size=args.batch_size, workers=8)
    elif args.cifar10_cifar10:
        ds = datasets.CIFAR('/tmp')
        train_loader, validation_loader = ds.make_loaders(
            only_val=args.eval_only, batch_size=args.batch_size, workers=8)
    else:
        ds, (train_loader, validation_loader) = transfer_datasets.make_loaders(
            args.dataset, args.batch_size, args.workers, args.subset)
        if type(ds) == int:
            new_ds = datasets.CIFAR("/tmp")
            new_ds.num_classes = ds
            new_ds.mean = ch.tensor([0., 0., 0.])
            new_ds.std = ch.tensor([1., 1., 1.])
            ds = new_ds
    return ds, train_loader, validation_loader

def resume_finetuning_from_checkpoint(args, ds, finetuned_model_path):
    '''Given arguments, dataset object and a finetuned model_path, returns a model
    with loaded weights and returns the checkpoint necessary for resuming training.
    '''
    print('[Resuming finetuning from a checkpoint...]')
    if args.dataset in list(transfer_datasets.DS_TO_FUNC.keys()) and not args.cifar10_cifar10:
        model, _ = model_utils.make_and_restore_model(
            arch=pytorch_models[args.arch](
                args.pytorch_pretrained) if args.arch in pytorch_models.keys() else args.arch,
            dataset=datasets.ImageNet(''), add_custom_forward=args.arch in pytorch_models.keys())
        while hasattr(model, 'model'):
            model = model.model
        model = fine_tunify.ft(
            args.arch, model, ds.num_classes, args.additional_hidden)
        model, checkpoint = model_utils.make_and_restore_model(arch=model, dataset=ds, resume_path=finetuned_model_path,
                                                               add_custom_forward=args.additional_hidden > 0 or args.arch in pytorch_models.keys())
    else:
        model, checkpoint = model_utils.make_and_restore_model(
            arch=args.arch, dataset=ds, resume_path=finetuned_model_path)
    return model, checkpoint

def get_model(args, ds):
    '''Given arguments and a dataset object, returns an ImageNet model (with appropriate last layer changes to
    fit the target dataset) and a checkpoint.The checkpoint is set to None if noe resuming training.
    '''
    finetuned_model_path = os.path.join(
        args.out_dir, args.exp_name, 'checkpoint.pt.latest')
    if args.resume and os.path.isfile(finetuned_model_path):
        model, checkpoint = resume_finetuning_from_checkpoint(
            args, ds, finetuned_model_path)
    else:
        if args.dataset in list(transfer_datasets.DS_TO_FUNC.keys()) and not args.cifar10_cifar10:
            model, _ = model_utils.make_and_restore_model(
                arch=pytorch_models[args.arch](
                    args.pytorch_pretrained) if args.arch in pytorch_models.keys() else args.arch,
                dataset=datasets.ImageNet(''), resume_path=args.model_path, pytorch_pretrained=args.pytorch_pretrained,
                add_custom_forward=args.arch in pytorch_models.keys())
            checkpoint = None
        else:
            model, _ = model_utils.make_and_restore_model(arch=args.arch, dataset=ds,
                                                          resume_path=args.model_path, pytorch_pretrained=args.pytorch_pretrained)
            checkpoint = None

        if not args.no_replace_last_layer and not args.eval_only:
            print(f'[Replacing the last layer with {args.additional_hidden} '
                  f'hidden layers and 1 classification layer that fits the {args.dataset} dataset.]')
            while hasattr(model, 'model'):
                model = model.model
            model = fine_tunify.ft(
                args.arch, model, ds.num_classes, args.additional_hidden)
            model, checkpoint = model_utils.make_and_restore_model(arch=model, dataset=ds,
                                                                   add_custom_forward=args.additional_hidden > 0 or args.arch in pytorch_models.keys())
        else:
            print('[NOT replacing the last layer]')
    return model, checkpoint

def freeze_model(model, freeze_level):
    '''
    Freezes up to args.freeze_level layers of the model (assumes a resnet model)
    '''
    # Freeze layers according to args.freeze-level
    update_params = None
    if freeze_level != -1:
        assert not args.only_learn_slope_trf # TODO Either use freeze_level or args.only_learn_slope_trf
        # assumes a resnet architecture
        assert len([name for name, _ in list(model.named_parameters())
                    if f"layer{freeze_level}" in name]), "unknown freeze level (only {1,2,3,4} for ResNets)"
        update_params = []
        freeze = True
        for name, param in model.named_parameters():
            print(name, param.size())

            if not freeze and f'layer{freeze_level}' not in name:
                print(f"[Appending the params of {name} to the update list]")
                update_params.append(param)
            else:
                param.requires_grad = False

            if freeze and f'layer{freeze_level}' in name:
                # if the freeze level is detected stop freezing onwards
                freeze = False
    elif args.only_learn_slope_trf:
        assert freeze_level == -1 # TODO Either use freeze_level or args.only_learn_slope_trf

        update_params = []
        #freeze = True
        for name, param in model.named_parameters():
            print(name, param.size())

            # if not freeze and f'layer{freeze_level}' not in name:
            if ('blocks' not in name) or ('slope_w' in name):
                print(f"[Appending the params of {name} to the update list]")
                update_params.append(param)
            else:
                param.requires_grad = False

    return update_params

def args_preprocess(args):
    '''
    Fill the args object with reasonable defaults, and also perform a sanity check to make sure no
    args are missing.
    '''
    if args.adv_train and eval(args.eps) == 0:
        print('[Switching to standard training since eps = 0]')
        args.adv_train = 0

    if args.pytorch_pretrained:
        assert not args.model_path, 'You can either specify pytorch_pretrained or model_path, not together.'

    # CIFAR10 to CIFAR10 assertions
    if args.cifar10_cifar10:
        assert args.dataset == 'cifar10'

    if args.data != '':
        cs.CALTECH101_PATH = cs.CALTECH256_PATH = cs.PETS_PATH = cs.CARS_PATH = args.data
        cs.FGVC_PATH = cs.FLOWERS_PATH = cs.DTD_PATH = cs.SUN_PATH = cs.FOOD_PATH = cs.BIRDS_PATH = args.data

    ALL_DS = list(transfer_datasets.DS_TO_FUNC.keys()) + \
        ['imagenet', 'breeds_living_9', 'stylized_imagenet']
    assert args.dataset in ALL_DS

    # Important for automatic job retries on the cluster in case of premptions. Avoid uuids.
    assert args.exp_name != None

    # Preprocess args
    args = defaults.check_and_fill_args(args, defaults.CONFIG_ARGS, None)
    if not args.eval_only:
        args = defaults.check_and_fill_args(args, defaults.TRAINING_ARGS, None)
    if args.adv_train or args.adv_eval:
        args = defaults.check_and_fill_args(args, defaults.PGD_ARGS, None)
    args = defaults.check_and_fill_args(args, defaults.MODEL_LOADER_ARGS, None)

    return args

if __name__ == "__main__":
    # Do we have a config file to parse?
    args_config, remaining = config_parser.parse_known_args()
    if args_config.config:
        assert os.path.isfile(args_config.config)
        with open(args_config.config, 'r') as f:
            cfg = yaml.safe_load(f)
            if cfg is not None:
                parser.set_defaults(**cfg)

    # The main arg parser parses the rest of the args, the usual
    # defaults will have been overridden if config file specified.
    args = parser.parse_args(remaining)

    # Preprocess arguments
    args = args_preprocess(args)

    pytorch_models = {
        'alexnet': models.alexnet,
        'vgg16': models.vgg16,
        'vgg16_bn': models.vgg16_bn,
        'squeezenet': models.squeezenet1_0,
        'densenet': models.densenet161,
        'shufflenet': models.shufflenet_v2_x1_0,
        'mobilenet': models.mobilenet_v2,
        'resnext50_32x4d': models.resnext50_32x4d,
        'mnasnet': models.mnasnet1_0,
    }

    # Create store and log the args
    store = cox.store.Store(args.out_dir, args.exp_name)
    if 'metadata' not in store.keys:
        args_dict = args.__dict__
        schema = cox.store.schema_from_dict(args_dict)
        store.add_table('metadata', schema)
        store['metadata'].append_row(args_dict)
    else:
        print('[Found existing metadata in store. Skipping this part.]')
    main(args, store)