from itertools import combinations
from operator  import attrgetter, itemgetter

import bitvec
import numpy as np
from kneed             import KneeLocator
from scipy.stats       import wasserstein_distance
from sklearn.neighbors import NearestNeighbors

#
# Methods for analyzing annealing results
#

def energy_arrays(result, qubo):
    arr_reported = []
    arr_true = []
    for sample, reported_energy, occ, *_ in sorted(result.aggregate().record, key=itemgetter(1)):
        true_energy = sample @ qubo @ sample
        arr_true.extend([true_energy]*occ)
        arr_reported.extend([reported_energy]*occ)
    return np.vstack((
        np.asarray(arr_reported, dtype=np.float64),
        np.asarray(arr_true, dtype=np.float64)))

def wasserstein(result, qubo):
    """Calculates the Wasserstein distance between the energy distribution reported
       by the sampler and the actual energy values of the original QUBO instance"""
    sampler_energies = []
    true_energies    = []
    weights = []
    for sample in sorted(result.aggregate().data(), key=attrgetter('energy')):
        sampler_energies.append(sample.energy)
        x = np.fromiter(sample.sample.values(), dtype=np.float64)
        true_energies.append(x @ qubo @ x)
        weights.append(sample.num_occurrences)
    return wasserstein_distance(sampler_energies, true_energies, weights, weights)

#
# Methods for analyzing QUBO instances
#

def dynamic_range(qubo, bits=False):
    params = np.r_[qubo[np.triu_indices_from(qubo, 1)], 0]
    dmin, dmax = minmax(abs(u-v) for u, v in combinations(params, r=2) if not np.isclose(u, v))
    return np.log2(dmax/dmin) if bits else 20*np.log10(dmax/dmin)

def epsilon_for_dbscan(qubo, k=5):
    params = qubo[np.triu_indices_from(qubo)][:, np.newaxis]
    knn = NearestNeighbors(n_neighbors=k)
    fit = knn.fit(params)
    distances, _ = fit.kneighbors(params)
    distances = np.sort(distances[:, 1:].mean(1))
    kneeloc = KneeLocator(np.arange(distances.size), distances, curve='convex', direction='increasing')
    return kneeloc.knee_y

def induced_ordering(qubo):
    n = qubo.shape[0]
    values = [ x @ qubo @ x for x in bitvec.all(n) ]
    return np.argsort(values)

#
# Random miscellaneous methods
#

def minmax(it):
    xmin = float('inf')
    xmax = float('-inf')
    for x in it:
        if x < xmin:
            xmin = x
        if x > xmax:
            xmax = x
    return xmin, xmax

if __name__ == '__main__':
    from qubo import random_subset_sum

    qubo, _ = random_subset_sum(64)
    res = epsilon_for_dbscan(qubo)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""ssl.py unit tests
"""
import argparse
from contextlib import contextmanager
import os
import pytest
import shutil
import tempfile

import csrtool

DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
SSL_CNF = os.path.join(DATA_DIR, "ssl.cnf")

# params {{{1
ALTNAME_PARAMS = (
    (('fqdn1', 'fqdn2'), ('ip1', ), "DNS.1:fqdn1,DNS.2:fqdn2,IP.1:ip1"),
    (('host1', 'host2'), (), "DNS.1:host1,DNS.2:host2"),
)

# helpers {{{1
class Aggregator(object):
    msgs = []

    def append(self, *args, **kwargs):
        self.msgs.append([args, kwargs])

def noop(*args, **kwargs):
    assert args or kwargs or True

@contextmanager
def temp_dir():
    tmpdir = tempfile.mkdtemp()
    yield tmpdir
    shutil.rmtree(tmpdir)

@pytest.fixture(scope='function')
def agg():
    return Aggregator()

@pytest.fixture(scope='function')
def fake_ssl_conf():
    with open(SSL_CNF, "r") as fh:
        return fh.read()

# generate_new_ssl_conf {{{1
def test_generate_new_ssl_conf(fake_ssl_conf):
    options = argparse.Namespace()
    options.ca_dir = "CA_DIR"
    config = csrtool.generate_new_ssl_conf(options, fake_ssl_conf)
    assert config.get(' default ', 'HOME') == '.'
    assert config.get(' v3_ca ', 'subjectAltName') == r'$ENV::ALTNAME'
    assert config.get(' CA_default ', 'dir') == options.ca_dir

def test_generate_new_ssl_conf_ca(fake_ssl_conf):
    options = argparse.Namespace()
    options.ca_dir = "CA_DIR"
    config = csrtool.generate_new_ssl_conf(options, fake_ssl_conf, ca=True)
    assert config.get(' default ', 'HOME') == '.'
    assert config.has_option(' v3_ca ', 'subjectAltName') is False
    assert config.get(' CA_default ', 'dir') == options.ca_dir

# read_orig_ssl_conf {{{1
def test_read_orig_ssl_conf(fake_ssl_conf):
    val = csrtool.read_orig_ssl_conf(SSL_CNF, [])
    assert val == fake_ssl_conf

def test_missing_orig_ssl_conf(fake_ssl_conf):
    with pytest.raises(Exception):
        csrtool.read_orig_ssl_conf(None, ["x"])

# runner {{{1
def test_runner(agg):
    cmd = ['asdf', 'blah', 'secret']
    silence = {'secret': 'notsecret'}
    csrtool.runner(cmd, silence=silence, log_fn=agg.append, call=noop)
    assert "['asdf', 'blah', 'notsecret']" in agg.msgs[0][0][0]
    assert "asdf blah notsecret" in agg.msgs[1][0][0]

# build_altname {{{1
@pytest.mark.parametrize("params", ALTNAME_PARAMS)
def test_build_altname(params):
    assert csrtool.build_altname(params[0], params[1]) == params[2]

# create_ca_files {{{1
def test_create_ca_files():
    options = argparse.Namespace()
    with temp_dir() as tmp:
        options.ca_dir = tmp
        csrtool.create_ca_files(options)
        assert os.path.exists(os.path.join(tmp, "ca.db.index"))
        assert os.path.isdir(os.path.join(tmp, "ca.db.certs"))
        with open(os.path.join(tmp, "ca.db.serial"), "r") as fh:
            serial = fh.read().rstrip()
        assert serial == "01"

# parse_args {{{1
def test_parse_args():
    args = ['gen_ca', 'sign_csr', '--fqdn', 'fqdn']
    options = csrtool.parse_args(args)
    assert options.actions == ['gen_ca', 'sign_csr']
    assert options.fqdn == ['fqdn']

def test_parse_bad_args():
    args = ['gen_ca', 'sign_csr']
    with pytest.raises(SystemExit):
        csrtool.parse_args(args)


import base.amtop as amtop
import threading

thred = threading.Thread(target=amtop.server())
thred.start()

print("""+ for addition, - for subtraction, * for multiplication, / for division""")
input1 = input("enter a opration to perform: ")
print("enter the numbers to perform the operation on")
input2 = input("enter the first number: ")
input3 = input("enter the second number: ")

Problem = {
    "data": {
        "num1": input2,
        "num2": input3,
        "do": input1
    }
}

paylode = amtop.messagemaker("Compute", Problem)
print(paylode)


"""Valid mp_uuid generator."""

from random import choice

_ENCODER = "0123456789ABCDEF"
_DASHLIST = [8, 13, 18, 23]
_MPID = 'mp_uuid'
_INNER = 'iform_section'
_ELEMS = 'iformFieldsArray'

def give_id():
	"""Generates an mp_uuid.
	Please only bestow an id if the object DOES NOT HAVE ONE."""
	newid = u''
	for i in range(0, 36):
		if i in _DASHLIST:
			newid += '-'
		else:
			newid += choice(_ENCODER)
	return newid

def id_wrap(id_func):
	"""Wrapper for the uuid creating function."""
	def wrapper():
		"""The actual workhorse!
		This keeps track of which ids were produced, on the
		very, very, very, very slim chance two are alike."""
		newid = id_func()
		while newid in all_ids:
			newid = id_func()
		all_ids.append(newid)
		return newid
	all_ids = []
	return wrapper

def fake_id(pyform):
	"""Assigns new mp_uuids to form, section, and elements.
	Checks to ensure mp_uuid is not present before assigning.

	On the extreeeeeme outside chance that it generates the same
	mp_uuid twice for one form, holds them in an array and makes sure
	all newly generated IDs are unique until the form is done."""
	if not pyform.get(_MPID, False):
		pyform[_MPID] = give_id()
	for section in pyform.get('iformSectionTiesArray', []):
		if not section.get(_MPID, False):
			section[_MPID] = give_id()
		for element in section.get(_INNER, {}).get(_ELEMS, []):
			if not element.get(_MPID, False):
				element[_MPID] = give_id()

give_id = id_wrap(give_id)		##wraps give_id


"""
Created on Thu Mar  5 17:55:58 2020.

@author: Engelpracht
"""

# %% Import relevant libraries.
#
from json import dumps
from os import listdir
from os.path import isfile, join
from typing import Union

from pandas import read_csv, DataFrame, Series

# %% Define global constants used to read *.csv-files and to create JSON-file.
#
# Definition of names of columns of data frames: Columns are different for refrigerants, adsorption
# or absorption isotherms; and for equations or experimental data
#
# a) Names that are required for several entries
#
COLUMNS_DF_WPAIR = ['sorbent', 'refrigerant', 'sorbent-subtype', 'type']
COLUMNS_DF_LITERATURE = ['comment', 'literature']

COLUMNS_DF_ADS_PROPERTIES = ['prop-diameter-crystal', 'prop-diameter-pore',
                             'prop-diameter-pellet', 'prop-diameter-extrudate',
                             'prop-length-extrudate', 'prop-area-surface',
                             'prop-volume-pore', 'props-porosity-pellet',
                             'props-density-bulk', 'props-density-pellet',
                             'props-density-solid']

# b) Names that are required for entries of equation coefficients
#
COLUMNS_DF_ERROR = ['error-are', 'error-rmse']

COLUMNS_DF_ABS_VALIDITY = ['validity-temperature-min', 'validity-temperature-max']
COLUMNS_DF_ADS_VALIDITY = ['validity-pressure-min', 'validity-pressure-max',
                           'validity-temperature-min', 'validity-temperature-max',
                           'validity-loading-min', 'validity-loading-max']
COLUMNS_DF_REF_VALIDITY = ['validity-temperature-min', 'validity-temperature-max']

# c) Names that are required for entries of experimental data
#
COLUMNS_DF_MEASUREMENT = ['measurements-procedure', 'measurements-type']

COLUMNS_DF_ADS_UNCERTAINITY = ['uncertainty-data-source',
                               'uncertainty-pressure-abs',
                               'uncertainty-pressure-rel',
                               'uncertainty-temperature-abs',
                               'uncertainty-temperature-rel',
                               'uncertainty-loading-abs',
                               'uncertainty-loading-rel',
                               'uncertainty-adsorptionPotential-abs',
                               'uncertainty-adsorptionPotential-rel',
                               'uncertainty-volumetricLoading-abs',
                               'uncertainty-volumetricLoading-rel']

COLUMNS_DF_ABS_UNCERTAINITY = ['uncertainty-data-source',
                               'uncertainty-pressure-abs',
                               'uncertainty-pressure-rel',
                               'uncertainty-temperature-abs',
                               'uncertainty-temperature-rel',
                               'uncertainty-loading-abs',
                               'uncertainty-loading-rel']

# Definition of names of keys of dicts
#
# a) Keys that are more general
#
KEYS_DICT_BASE = ['k', 'v']

KEYS_DICT_K = ['_as_', '_rf_', '_st_', '_tp_']
KEYS_DICT_V = ['_ep_', '_ed_', '_r_', '_s_', '_t_']

KEYS_DICT_ABS_EQUATIONS = ['_c_', '_n_', '_e_', '_va_', '_p_']
KEYS_DICT_ADS_EQUATIONS = ['_c_', '_n_', '_e_', '_pr_', '_va_', '_er_', '_p_']
KEYS_DICT_REF_EQUATIONS = ['_c_', '_n_', '_e_', '_va_', '_p_']

KEYS_DICT_ADS_EXPDATA = ['_c_', '_n_', '_pr_', '_m_', '_u_', '_d_']
KEYS_DICT_ABS_EXPDATA = ['_c_', '_n_', '_m_', '_u_', '_d_']

KEYS_DICT_ADS_PROPERTIES = ['diameter-crystal', 'diameter-pore', 'diameter-pellet',
                            'diameter-extrudate', 'length-extrudate', 'area-surface',
                            'volume-pore', 'porosity-pellet', 'density-bulk',
                            'density-pellet', 'density-solid']

# b) Keys that are required for entries of equation coefficients
#
KEYS_DICT_ERROR = ['are', 'rmse']

KEYS_DICT_ABS_VALIDITY = ['temperature-min', 'temperature-max']
KEYS_DICT_ADS_VALIDITY = ['pressure-min', 'pressure-max',
                          'temperature-min', 'temperature-max',
                          'loading-min', 'loading-max']
KEYS_DICT_REF_VALIDITY = ['temperature-min', 'temperature-max']

# c) Keys that are required for entries of experimental data
#
KEYS_DICT_MEASUREMENT = ['procedure', 'type']

KEYS_DICT_ADS_UNCERTAINITY = ['data-source',
                              'pressure-abs', 'pressure-rel', 'temperature-abs',
                              'temperature-rel', 'loading-abs', 'loading-rel',
                              'adsorptionPotential-abs', 'adsorptionPotential-rel',
                              'volumetricLoading-abs', 'volumetricLoading-rel']

KEYS_DICT_ABS_UNCERTAINITY = ['data-source',
                              'pressure-abs', 'pressure-rel', 'temperature-abs',
                              'temperature-rel', 'loading-abs', 'loading-rel',
                              'adsorptionPotential-abs', 'adsorptionPotential-rel',
                              'volumetricLoading-abs', 'volumetricLoading-rel']

# %% Definition of sub-functions.
#
def read_csv_to_list(path: str) -> Union[list, list]:
    r"""Input a path to folder with *.csv-files to read files into panda data frames.

    This function reads all *.csv-files that are located in the directory
    specified by the variable 'path'. This function returns a list containing
    all names of the *.csv-files and another list containing all *.csv-files
    saved as data frames.

    Parameters
    ----------
    path : str
        Path that specifies folder in which *.csv-files are located.

    Returns
    -------
    names : list
        Contains all names of files without their type extensions (i.e. *.csv).
    files : list
        Contains all files saved as type 'pandas.core.frame.DataFrame'.

    Notes
    -----
    This function does not check if files have typ .csv.

    Examples
    --------
    >>> names, files = read_csv_to_list('C:\\example\\')
    >>> print(names)
    ['dss', 'langmuir']
    >>> print(type(files[0]))
    <class 'pandas.core.frame.DataFrame'>

    History
    -------
    03/07/2020, by Mirko Engelpracht:
        First implementation.
    """
    # Read names of files
    #
    names = [file for file in listdir(path) if isfile(join(path, file))]

    # Import all files as 'pandas.core.frame.DataFrame'
    # Drop second line of all files since it only contains units of values
    #
    files = []
    for _, name_file in enumerate(names):
        tmp_df = read_csv(join(path, name_file),
                          sep=';',
                          encoding='utf_8',
                          keep_default_na=False)
        files.append(tmp_df.drop(0).reset_index(drop=True))

    # Cut file-extensions of file names and return lists
    #
    return [name_file.split('.')[0] for name_file in names], files

def search_df(df_search: DataFrame, columns: list, values: list) -> DataFrame:
    r"""Search data frame for values in some columns.

    This function searches the data frame 'df_search' for the specified 'values'
    in the specified 'columns' and returns the maching data frame.

    Parameters
    ----------
    df_search : pandas.core.frame.DataFrame
        Data frame that that is being searched for.
    columns : list
        List containing names of the columns to be searched.
    values : list
        List containing values to be searched for.

    Returns
    -------
    pandas.core.frame.DataFrame
        Data frame that only contains searched values.

    Notes
    -----
    Parameters 'columns' and 'values' need to have the same length.

    Examples
    --------
    >>> search_result = search_df(df, ['Column 1'], ['Random value'])
    >>> print(type(search_result))
    <class 'pandas.core.frame.DataFrame'>

    History
    -------
    03/07/2020, by Mirko Engelpracht:
        First implementation.
    """
    # Search for specified values by sequential application of the search
    # criteria
    #
    for no_criterion, value_criterion in enumerate(values):
        df_search = df_search.loc[df_search[columns[no_criterion]] == value_criterion]

    # Return search result
    #
    return df_search

def search_wpair(dfs: list, columns: list) -> DataFrame:
    r"""Search for all available working pais.

    This function counts the number of unique working pairs, which are
    specfied by the 'columns' of the data frame, and returns a data frame that
    only contains the unique working pairs.

    Parameters
    ----------
    dfs : list
        List containing data frames that are searched for.
    columns : list
        List containing names of the columns to be searched.

    Returns
    -------
    pandas.core.frame.DataFrame
        Data frame that only unique working pairs according to 'columns'.

    History
    -------
    03/07/2020, by Mirko Engelpracht:
        First implementation.
    """
    # Create empty data frame and add all working pairs to it
    #
    df_unique = DataFrame(columns=columns)
    for _, val in enumerate(dfs):
        df_unique = df_unique.append(val[columns], ignore_index=True)

    # Drop duplicates and return data frame cointaing all working pairs
    #
    return df_unique.drop_duplicates().reset_index(drop=True)

def create_dict(series: Series, keys: list, convert: bool = False) -> dict:
    r"""Create a dict for a JSON file.

    This function creates a dict. Therefore, it uses the given 'keys' and takes
    values from the given 'series'. If necessary, the funtion convert values to
    floats when a conversion is possible.

    Parameters
    ----------
    series : pandas.core.series.Series
        Series containing values for the dict.
    keys : list
        Lists containing names of the keys of the dict.
    convert : bool
        Boolean indicating if values shall be converted to type float.

    Returns
    -------
    dict
        Data containing values from series.

    History
    -------
    03/07/2020, by Mirko Engelpracht:
        First implementation.
    """
    # Get series, read values of indices, and convert values if necessary
    #
    names_series = series.index
    values_series = []
    for _, val in enumerate(names_series):
        if convert:
            # Convert values to type float
            #
            if series[val] == '':
                values_series.append(series[val])
            else:
                values_series.append(float(series[val]))

        else:
            # Do not convert values to type float
            #
            values_series.append(series[val])

    # Create dict and return it
    #
    return dict(zip(keys, values_series))

# %% Definition of main-function.
#
def create_json_file(path_coef: str, path_exp: list, path_json: str) -> Union[list, str, str]:
    r"""Create JSON file.

    This function creates the JSON file. Therefore, it needs the path to the
    coefficients of the equations (i.e., 'path_coef'), the paths to the
    experimental data (i.e., 'path_exp'), and the path to the location where
    the JSON files shall be saved (i.e., 'path_json').

    Parameters
    ----------
    path_coef : str
        Path to *.csv-files that contain coefficients of equations.
    path_exp : list
        List containing paths to *.csv-files that contain experimental data points.
    path_exp : str
        Path to to folder where json-files shall be safed.

    Returns
    -------
    list_json : list
        List with content of JSON file.
    json_compact : str
        String of JSON file as one line.
    json_formatted : str
        String of JSON file formatted in multiple lines.

    History
    -------
    03/07/2020, by Mirko Engelpracht:
        First implementation.
    """
    # 0) Load all *.csv-files that contain equation coefficients or experimental
    #    data of refrigerants, adsorption isotherms, and absorption isotherms
    #
    names_equations, list_df_equations = read_csv_to_list(path_coef)

    tmp_list_df_experimental_data = []
    list_df_experimental_data = [None] * len(path_exp)

    for ind_exp, path_exp_i in enumerate(path_exp):
        _, list_df_experimental_data[ind_exp] = read_csv_to_list(path_exp_i)
        tmp_list_df_experimental_data = list_df_experimental_data[ind_exp] if ind_exp == 0 else \
            tmp_list_df_experimental_data + list_df_experimental_data[ind_exp]

    # 1) Get names of all unique refrigerants and working pairs:
    #
    df_wpair_unique = search_wpair(list_df_equations + tmp_list_df_experimental_data,
                                   COLUMNS_DF_WPAIR)

    # 2) Create dict 'k' that is used to save general information of all
    #    refrigerants and working pairs:
    #
    #       - Iterate over all unique refrigerants and working pairs
    #       - Save general information as dict
    #
    dict_k = [dict(zip(KEYS_DICT_K, row)) for _, row in df_wpair_unique.iterrows()]

    # 3) Create dict 'v' to save coefficients of equations and experimental
    #    data:
    #
    #       - Iterate over all unique refrigerants and working pairs
    #       - Create dicts and save dummy values for keys that will be later
    #         used for coefficients of equations and for experimental data
    #
    dict_v = [dict(zip(KEYS_DICT_V, [[], [],
                                     row[COLUMNS_DF_WPAIR[1]],
                                     row[COLUMNS_DF_WPAIR[0]],
                                     row[COLUMNS_DF_WPAIR[2]]]))
              for _, row in df_wpair_unique.iterrows()]

    # 4) Create dict '_ep_' to save coefficients of equations:
    #
    #       - Create dict that contains names of all equations as keys and empty
    #         lists as valeus
    #       - Create list for all unique refrigerants and working pairs with former
    #         created dict as attribute
    #       - For each refrigerant and working pair, count how often an equation
    #         occurs by iterating over refrigerants and working pairs first and
    #         iterating over equations second
    #       - For each equation, iterate over available sets of coefficients and
    #         save coresponding coefficients as dict
    #
    list_equations = []
    for _ in range(len(df_wpair_unique)):
        # Create empty dicts for each unique refrigerant and working pair, in which
        # the keys corespond the names of all existing equations
        #
        dict_equations = dict()
        for _, val in enumerate(names_equations):
            dict_equations.update(zip([val], [[]]))
        list_equations.append(dict_equations)

    for ind_ref_wpair, ref_wpair in df_wpair_unique.iterrows():
        for ind_equ, name_equ in enumerate(dict_equations):
            # Search current working pair within current equation data frame
            #
            df_equ = list_df_equations[ind_equ]
            cur_ref_wpair = ref_wpair.tolist()
            df_result = search_df(df_equ, COLUMNS_DF_WPAIR, cur_ref_wpair)

            # Save current equation data frame into coresponding position of
            # 'list_equations' when working pair exists
            #
            df_result = df_result.reset_index(drop=True)
            df_result = [] if df_result.empty else df_result
            list_equations[ind_ref_wpair].update(dict(zip([name_equ],
                                                          [df_result])))

    for ind_ref_wpair, _ in enumerate(dict_v):
        # Count and save names of different equation approaches for
        # current working pair
        #
        list_name_equ = []
        for _, name_equ in enumerate(list_equations[ind_ref_wpair]):
            if not isinstance(list_equations[ind_ref_wpair][name_equ], list):
                list_name_equ.append(name_equ)

        # Create dummy dict to save coefficient sets for each identified
        # equations of current working pair
        #
        list_equ = [[] for i in range(len(list_name_equ))]
        dict_equ = dict(zip(list_name_equ, list_equ))

        # For each equation, iterate over available sets of coefficients
        # For each set of coefficient, save coefficients as dict
        #
        for _, name_equ in enumerate(dict_equ):
            list_equ = []
            for _, equ_set in list_equations[ind_ref_wpair][name_equ].iterrows():
                # Create dummy dict for each set of coefficients depending on
                # kind of data (e.g., refrigerant or adsorption isotherm)
                #
                if dict_k[ind_ref_wpair]['_tp_']=='refrig':
                    list_equ_set = [[] for i in range(len(KEYS_DICT_REF_EQUATIONS))]
                    dict_equ_set = dict(zip(KEYS_DICT_REF_EQUATIONS, list_equ_set))

                elif dict_k[ind_ref_wpair]['_tp_']=='ads':
                    list_equ_set = [[] for i in range(len(KEYS_DICT_ADS_EQUATIONS))]
                    dict_equ_set = dict(zip(KEYS_DICT_ADS_EQUATIONS, list_equ_set))

                else:
                    list_equ_set = [[] for i in range(len(KEYS_DICT_ABS_EQUATIONS))]
                    dict_equ_set = dict(zip(KEYS_DICT_ABS_EQUATIONS, list_equ_set))

                # Update dummy dict depending on kind of data (e.g., refrigerant or
                # adsorption isotherm)
                #
                if dict_k[ind_ref_wpair]['_tp_']=='refrig':
                    # Update dummy dict: Literature
                    #
                    dict_equ_set[KEYS_DICT_REF_EQUATIONS[0]] = \
                        equ_set[COLUMNS_DF_LITERATURE[1]]

                    # Update dummy dict: Note
                    #
                    dict_equ_set[KEYS_DICT_REF_EQUATIONS[1]] = \
                        equ_set[COLUMNS_DF_LITERATURE[0]]

                    # Update dummy dict: Name of equation
                    #
                    dict_equ_set[KEYS_DICT_REF_EQUATIONS[2]] = \
                        name_equ.capitalize()

                    # Update dummy dict: Valid range
                    #
                    dict_equ_set[KEYS_DICT_REF_EQUATIONS[3]] = \
                        create_dict(equ_set[COLUMNS_DF_REF_VALIDITY],
                                    KEYS_DICT_REF_VALIDITY, True)

                    # Update dummy dict: Coefficients
                    #
                    equ_set = equ_set.drop(COLUMNS_DF_WPAIR +
                                           COLUMNS_DF_LITERATURE +
                                           COLUMNS_DF_REF_VALIDITY)

                    name_par = equ_set.index
                    list_par_name = []
                    list_par_value = []
                    for _, val in enumerate(name_par):
                        list_par_name.append(val)
                        list_par_value.append(float(equ_set[val]))

                    dict_equ_set[KEYS_DICT_REF_EQUATIONS[4]] = dict(zip(list_par_name,
                                                                    list_par_value))

                elif dict_k[ind_ref_wpair]['_tp_']=='ads':
                    # Update dummy dict: Literature
                    #
                    dict_equ_set[KEYS_DICT_ADS_EQUATIONS[0]] = \
                        equ_set[COLUMNS_DF_LITERATURE[1]]

                    # Update dummy dict: Note
                    #
                    dict_equ_set[KEYS_DICT_ADS_EQUATIONS[1]] = \
                        equ_set[COLUMNS_DF_LITERATURE[0]]

                    # Update dummy dict: Name of equation
                    #
                    dict_equ_set[KEYS_DICT_ADS_EQUATIONS[2]] = \
                        name_equ.capitalize()

                    # Update dummy dict: Further properties of sorbent
                    #
                    dict_equ_set[KEYS_DICT_ADS_EQUATIONS[3]] = \
                        create_dict(equ_set[COLUMNS_DF_ADS_PROPERTIES],
                                    KEYS_DICT_ADS_PROPERTIES)

                    # Update dummy dict: Valid range
                    #
                    dict_equ_set[KEYS_DICT_ADS_EQUATIONS[4]] = \
                        create_dict(equ_set[COLUMNS_DF_ADS_VALIDITY],
                                    KEYS_DICT_ADS_VALIDITY, True)

                    # Update dummy dict: Error
                    #
                    dict_equ_set[KEYS_DICT_ADS_EQUATIONS[5]] = \
                        create_dict(equ_set[COLUMNS_DF_ERROR],
                                    KEYS_DICT_ERROR, True)

                    # Update dummy dict: Coefficients
                    #
                    equ_set = equ_set.drop(COLUMNS_DF_WPAIR +
                                           COLUMNS_DF_LITERATURE +
                                           COLUMNS_DF_ADS_PROPERTIES +
                                           COLUMNS_DF_ADS_VALIDITY +
                                           COLUMNS_DF_ERROR)

                    name_par = equ_set.index
                    list_par_name = []
                    list_par_value = []
                    for _, val in enumerate(name_par):
                        list_par_name.append(val)
                        list_par_value.append(float(equ_set[val]))

                    dict_equ_set[KEYS_DICT_ADS_EQUATIONS[6]] = dict(zip(list_par_name,
                                                                    list_par_value))

                else:
                    # Update dummy dict: Literature
                    #
                    dict_equ_set[KEYS_DICT_ABS_EQUATIONS[0]] = \
                        equ_set[COLUMNS_DF_LITERATURE[1]]

                    # Update dummy dict: Note
                    #
                    dict_equ_set[KEYS_DICT_ABS_EQUATIONS[1]] = \
                        equ_set[COLUMNS_DF_LITERATURE[0]]

                    # Update dummy dict: Name of equation
                    #
                    dict_equ_set[KEYS_DICT_ABS_EQUATIONS[2]] = name_equ.capitalize()

                    # Update dummy dict: Valid range
                    #
                    dict_equ_set[KEYS_DICT_ABS_EQUATIONS[3]] = \
                        create_dict(equ_set[COLUMNS_DF_ABS_VALIDITY],
                                    KEYS_DICT_ABS_VALIDITY, True)

                    # Update dummy dict: Coefficients
                    #
                    equ_set = equ_set.drop(COLUMNS_DF_WPAIR +
                                           COLUMNS_DF_LITERATURE +
                                           COLUMNS_DF_ABS_VALIDITY)

                    name_par = equ_set.index
                    list_par_name = []
                    list_par_value = []
                    for _, val in enumerate(name_par):
                        list_par_name.append(val)
                        list_par_value.append(float(equ_set[val]))

                    dict_equ_set[KEYS_DICT_ABS_EQUATIONS[4]] = dict(zip(list_par_name,
                                                                    list_par_value))

                # Update list that contains all sets of coefficients
                #
                list_equ.append(dict_equ_set)

            # Update dict that contains different equations
            #
            dict_equ[name_equ] = list_equ

        # Update dict 'v' by adding coefficients of equations
        #
        dict_v[ind_ref_wpair][KEYS_DICT_V[0]] = dict_equ

    # 5) Create dict '_ed_' to save experimental data. Thereby, check type of data:
    #
    #       - Iterate over all refrigerants and working pairs
    #       - Check if experimental data exist
    #       - Iterate over all refrigerants and working pairs and save
    #         experimental data as dict
    #
    # Iterate over all experimental data specified by list of paths
    #
    for ind_type_exp, list_df_experimental_data_i in enumerate(list_df_experimental_data):
        df_exp_data = search_wpair(list_df_experimental_data_i, COLUMNS_DF_WPAIR)

        # Iterate over all refrigerants and working pairs
        #
        for ind_ref_wpair, _ in enumerate(dict_v):
            list_exp_data = []

            # Check if experimental data exists for current working pair
            # Thus, save time and skip iterations that are not necessary
            #
            df_cur_ref_wpair = df_wpair_unique.loc[ind_ref_wpair, :]
            name_cur_ref_wpair = df_cur_ref_wpair.tolist()
            df_result = search_df(df_exp_data, COLUMNS_DF_WPAIR,
                                  name_cur_ref_wpair)

            # If experimental data exist, save it into dict '_ed_'
            #
            if not df_result.empty:
                # Iterate over all data frames that contain experimental data
                #
                for _, df_exp in enumerate(list_df_experimental_data_i):
                    # Search experimental data within data frame for current working
                    # pair
                    # Note that experimental data only exists within one data frame!
                    #
                    df_exp = search_df(df_exp, COLUMNS_DF_WPAIR,
                                        name_cur_ref_wpair)

                    if not df_exp.empty:
                        # Check type of experimental data
                        #
                        if df_result['type'].all() == 'ads':
                            # Iterate over all available experimental data for the
                            # current working pair
                            #
                            for _, exp_set in df_exp.iterrows():
                                # Create dummy dict to save current experimental data
                                #
                                list_cur_exp_data = [[] for i in range(len(KEYS_DICT_ADS_EXPDATA))]
                                dict_exp_data = dict(zip(KEYS_DICT_ADS_EXPDATA,
                                                          list_cur_exp_data))

                                # Update dummy dict: Literature
                                #
                                dict_exp_data[KEYS_DICT_ADS_EXPDATA[0]] = \
                                    exp_set[COLUMNS_DF_LITERATURE[1]]

                                # Update dummy dict: Note
                                #
                                dict_exp_data[KEYS_DICT_ADS_EXPDATA[1]] = \
                                    exp_set[COLUMNS_DF_LITERATURE[0]]

                                # Update dummy dict: Sorbent properties
                                #
                                dict_exp_data[KEYS_DICT_ADS_EXPDATA[2]] = \
                                    create_dict(exp_set[COLUMNS_DF_ADS_PROPERTIES],
                                                KEYS_DICT_ADS_PROPERTIES)

                                # Update dummy dict: Measurement information
                                #
                                dict_exp_data[KEYS_DICT_ADS_EXPDATA[3]] = \
                                    create_dict(exp_set[COLUMNS_DF_MEASUREMENT],
                                                KEYS_DICT_MEASUREMENT)

                                # Update dummy dict: Uncertainty information
                                #
                                dict_exp_data[KEYS_DICT_ADS_EXPDATA[4]] = \
                                    create_dict(exp_set[COLUMNS_DF_ADS_UNCERTAINITY],
                                                KEYS_DICT_ADS_UNCERTAINITY)

                                # Update dummy dict: Measurements
                                #
                                mea_set = exp_set.drop(COLUMNS_DF_WPAIR +
                                                        COLUMNS_DF_LITERATURE +
                                                        COLUMNS_DF_ADS_PROPERTIES +
                                                        COLUMNS_DF_MEASUREMENT +
                                                        COLUMNS_DF_ADS_UNCERTAINITY)

                                name_par = mea_set.index
                                list_par_value = []
                                for _, val in enumerate(name_par):
                                    # Split string and ensure empty lists
                                    #
                                    measurments = mea_set[val].split(',')
                                    measurments = [] if (measurments[0] == '') else \
                                        [float(item) for item in measurments]

                                    list_par_value.append(measurments)

                                dict_exp_data[KEYS_DICT_ADS_EXPDATA[5]] = \
                                    dict(zip(name_par.to_list(), list_par_value))

                                # Update list containing all experiment data of current
                                # working pair
                                #
                                list_exp_data.append(dict_exp_data)

                        elif df_result['type'].all() == 'abs':
                            # Iterate over all available experimental data for the
                            # current working pair
                            #
                            for _, exp_set in df_exp.iterrows():
                                # Create dummy dict to save current experimental data
                                #
                                list_cur_exp_data = [[] for i in range(len(KEYS_DICT_ABS_EXPDATA))]
                                dict_exp_data = dict(zip(KEYS_DICT_ABS_EXPDATA,
                                                          list_cur_exp_data))

                                # Update dummy dict: Literature
                                #
                                dict_exp_data[KEYS_DICT_ABS_EXPDATA[0]] = \
                                    exp_set[COLUMNS_DF_LITERATURE[1]]

                                # Update dummy dict: Note
                                #
                                dict_exp_data[KEYS_DICT_ABS_EXPDATA[1]] = \
                                    exp_set[COLUMNS_DF_LITERATURE[0]]

                                # Update dummy dict: Measurement information
                                #
                                dict_exp_data[KEYS_DICT_ABS_EXPDATA[2]] = \
                                    create_dict(exp_set[COLUMNS_DF_MEASUREMENT],
                                                KEYS_DICT_MEASUREMENT)

                                # Update dummy dict: Uncertainty information
                                #
                                dict_exp_data[KEYS_DICT_ABS_EXPDATA[3]] = \
                                    create_dict(exp_set[COLUMNS_DF_ABS_UNCERTAINITY],
                                                KEYS_DICT_ABS_UNCERTAINITY)

                                # Update dummy dict: Measurements
                                #
                                mea_set = exp_set.drop(COLUMNS_DF_WPAIR +
                                                        COLUMNS_DF_LITERATURE +
                                                        COLUMNS_DF_MEASUREMENT +
                                                        COLUMNS_DF_ABS_UNCERTAINITY)

                                name_par = mea_set.index
                                list_par_value = []
                                for _, val in enumerate(name_par):
                                    # Split string and ensure empty lists
                                    #
                                    measurments = mea_set[val].split(',')
                                    measurments = [] if (measurments[0] == '') else \
                                        [float(item) for item in measurments]

                                    list_par_value.append(measurments)

                                dict_exp_data[KEYS_DICT_ABS_EXPDATA[4]] = \
                                    dict(zip(name_par.to_list(), list_par_value))

                                # Update list containing all experiment data of current
                                # working pair
                                #
                                list_exp_data.append(dict_exp_data)

                        # Update dict 'v' by adding experimental data
                        #
                        dict_v[ind_ref_wpair][KEYS_DICT_V[1]] = list_exp_data

    # 6) Create base dict that containts dicts 'k' and 'v':
    #
    #       - Iterate over all unique refrigerants and working pairs
    #       - Create dicts by zip-function
    #
    list_json = [dict(zip(KEYS_DICT_BASE, [val, dict_v[ind]])) for ind, val in
                 enumerate(dict_k)]

    # 7) Save JSON-files
    #
    with open(join(path_json, "sorpproplib.json"), "w") as text_file:
        json_compact = dumps(list_json)
        text_file.write(json_compact)

    with open(join(path_json, "sorpproplib_formatted.json"), "w") as text_file:
        json_formatted = dumps(list_json, indent=4)
        text_file.write(json_formatted)

    # Return data
    #
    return list_json, json_compact, json_formatted

# %% Execute main-function.
#
if __name__ == "__main__":
    # Defines paths
    #
    PATH_COEFFICIENTS = 'JSON\\equation_coefficients\\'
    PATH_EXPERIMENTS = ['JSON\\experimental_data\\ads\\']
    PATH_JSON = 'JSON\\'

    # Create JSON files
    #
    JSON_FILE, JSON_STR_COMPLETE, JSON_STR_FORMATTED = \
        create_json_file(PATH_COEFFICIENTS, PATH_EXPERIMENTS, PATH_JSON)


# In the last assignment, your code might have looked something like this:

# guido.make_deposit(100)
# guido.make_deposit(200)
# guido.make_deposit(300)
# guido.make_withdrawal(50)
# guido.display_user_balance()
#
# This takes up a lot of space and we're repeating our call to guido many times. There is a way to call on guido just once and keep attaching
# new method calls to the end of the previous one, like so:

# guido.make_deposit(100).make_deposit(200).make_deposit(300).make_withdrawal(50).display_user_balance()
#
# This is called chaining. In order for this to work, each method must return self. By returning self, if we recall how functions work, each method
# call will now be equal to the instance that called it.

# For example if guido.make_deposit(100) returns its own instance (guido), then we can call one of that instance's methods after that call,
# like guido.make_deposit(100).make_withdrawal(50).

# class User:
#     def make_deposit(self, amount):
#         # your code goes here...
#         return self
# copy
# The practice of having OOP return its own instance is pretty common and is done in other programming languages,
# though the variable name in some languages is not self, but instead this.

# Update your previous assignment so that each instance's methods are chained

class User:		# here's what we have so far
    def __init__(self, name, email):
        self.name = name
        self.email = email
        self.account_balance = 0
    # adding the deposit method
    def make_deposit(self, amount):	# takes an argument that is the amount of the deposit
        self.account_balance += amount	# the specific user's account increases by the amount of the value received
        return self
    # adding the withdrawal method
    def make_withdrawal(self, amount):  # takes an argument that is the amount of the withdrawal
        self.account_balance -= amount  # the specific user's account increases by the amount of the value received
        return self
    # adding the display_user_balance method
    def display_user_balance(self):
        print(self.name, "has $" + str(self.account_balance))
        return self
    # adding the transfer_money method
    def transer_money(self, other_user, amount): # takes two argument: amount of the transfer and user to transfer to
        self.account_balance -= amount # the transferring user's account decreases by the transfer amount
        other_user.account_balance += amount # the other user's account increases by the transfer amount
        return self

lydia = User("Lydia", "lydia@fofo.com")
fayez = User("Fayez", "Fayez@bcs.com")
karim = User("Karim", "Karim@Apeak.com")

lydia.make_deposit(100).make_deposit(500).make_deposit(50).make_withdrawal(250).display_user_balance()
fayez.make_deposit(1000).make_deposit(250).make_withdrawal(600).make_withdrawal(600).display_user_balance()
karim.make_deposit(200).make_withdrawal(20).make_withdrawal(75).make_withdrawal(13).display_user_balance()
lydia.transer_money(karim, 300).display_user_balance()
karim.display_user_balance()


import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
import warnings

warnings.filterwarnings("ignore")

# load data
df = pd.read_csv("nba_elo_games_merged-4.csv")

# select relevant columns
columns_to_keep = ['elo1_pre', 'elo2_pre', 'carm-elo1_pre', 'carm-elo2_pre', 'raptor1_pre', 'raptor2_pre', 'score1', 'score2', 'ortg', 'drtg', 'total_opp', 'home_opp', 'won']
df = df[columns_to_keep]

# df = df.dropna(subset=['carm-elo1_pre', 'carm-elo2_pre', 'raptor1_pre', 'raptor2_pre', 'score1', 'score2', 'total_rating'],how='any')
df = df.dropna(subset=columns_to_keep)

scaler = StandardScaler()

# split data into training and test sets
train_df, test_df = train_test_split(df, test_size=0.4, random_state=42)

# define X and y for training and test sets
X_train = train_df.drop(columns=['won'])
y_train = train_df['won']
X_test = test_df.drop(columns=['won'])
y_test = test_df['won']

X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# train logistic regression model
lr = LogisticRegression()
lr.fit(X_train, y_train)

# make predictions on test set
y_pred = lr.predict(X_test)

# calculate accuracy
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy: ", accuracy)

# Now you can use this model to predict the outcome of future games
# by providing the appropriate input to the predict() function

future_games = pd.read_csv("future_games.csv")
future_games_X = future_games[columns_to_keep]

future_games_X= future_games_X.drop(columns=['won'])

future_games_predictions = lr.predict(future_games_X)

print(future_games_predictions)


# Import date from datetime
from datetime import date

# Create a date object
hurricane_andrew = date(1992, 8, 24)

# Which day of the week is the date?
print(hurricane_andrew.weekday())

# Counter for how many before June 1
early_hurricanes = 0

# We loop over the dates
for hurricane in florida_hurricane_dates:
  # Check if the month is before June (month number 6)
  if hurricane.month < 6:
    early_hurricanes = early_hurricanes + 1

print(early_hurricanes)


from __future__ import absolute_import, division, print_function
import shutil
from np_transformation import SEs2ses
from datasets.kitti_dataset import KITTIOdomDataset
from rotation_conversions import axis_angle_to_matrix, matrix_to_axis_angle
from multilossmanager import MultiLossManager
from posemanager import PoseManager
from networks.PPnet import PPnet
from operator import itemgetter
from tqdm import tqdm

import numpy as np
import time
import sys

import torch
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from tensorboardX import SummaryWriter

import json

from utils import *
from kitti_utils import *
from layers import *

import datasets
import networks
from IPython import embed

class Trainer:
    def __init__(self, options):
        self.opt = options
        self.log_path = os.path.join(self.opt.log_dir, self.opt.model_name)

        # checking height and width are multiples of 32
        assert self.opt.height % 32 == 0, "'height' must be a multiple of 32"
        assert self.opt.width % 32 == 0, "'width' must be a multiple of 32"

        self.best_val = float('inf')
        self.models = {}
        self.parameters_to_train = []

        self.device = torch.device("cpu" if self.opt.no_cuda else "cuda")

        self.num_scales = len(self.opt.scales)
        self.num_input_frames = len(self.opt.frame_ids)
        self.num_pose_frames = 2 if self.opt.pose_model_input == "pairs" else self.num_input_frames

        assert self.opt.frame_ids[0] == 0, "frame_ids must start with 0"

        self.use_pose_net = not (self.opt.use_stereo and self.opt.frame_ids == [0])

        if self.opt.use_stereo:
            self.opt.frame_ids.append("s")

        self.models["encoder"] = networks.ResnetEncoder(
            self.opt.num_layers, self.opt.weights_init == "pretrained")
        self.models["encoder"].to(self.device)
        self.parameters_to_train += list(self.models["encoder"].parameters())

        self.models["depth"] = networks.DepthDecoder(
            self.models["encoder"].num_ch_enc, self.opt.scales)
        self.models["depth"].to(self.device)
        self.parameters_to_train += list(self.models["depth"].parameters())

        ## MotionHint: PPnet
        self.models["ppnet"] = networks.PPnet().to(self.device)
        if self.opt.ppnet:
            self.models["ppnet"].load_state_dict(torch.load(self.opt.ppnet))
        else:
            print("You must provide the path of PPnet.")
            sys.exit(1)
        # end

        if self.use_pose_net:
            if self.opt.pose_model_type == "separate_resnet":
                self.models["pose_encoder"] = networks.ResnetEncoder(
                    self.opt.num_layers,
                    self.opt.weights_init == "pretrained",
                    num_input_images=self.num_pose_frames)

                self.models["pose_encoder"].to(self.device)
                self.parameters_to_train += list(self.models["pose_encoder"].parameters())

                self.models["pose"] = networks.PoseDecoder(
                    self.models["pose_encoder"].num_ch_enc,
                    num_input_features=1,
                    num_frames_to_predict_for=2)

            elif self.opt.pose_model_type == "shared":
                self.models["pose"] = networks.PoseDecoder(
                    self.models["encoder"].num_ch_enc, self.num_pose_frames)

            elif self.opt.pose_model_type == "posecnn":
                self.models["pose"] = networks.PoseCNN(
                    self.num_input_frames if self.opt.pose_model_input == "all" else 2)

            self.models["pose"].to(self.device)
            self.parameters_to_train += list(self.models["pose"].parameters())

        if self.opt.predictive_mask:
            assert self.opt.disable_automasking, \
                "When using predictive_mask, please disable automasking with --disable_automasking"

            # Our implementation of the predictive masking baseline has the the same architecture
            # as our depth decoder. We predict a separate mask for each source frame.
            self.models["predictive_mask"] = networks.DepthDecoder(
                self.models["encoder"].num_ch_enc, self.opt.scales,
                num_output_channels=(len(self.opt.frame_ids) - 1))
            self.models["predictive_mask"].to(self.device)
            self.parameters_to_train += list(self.models["predictive_mask"].parameters())

        self.model_optimizer = optim.Adam(self.parameters_to_train, self.opt.learning_rate)
        self.model_lr_scheduler = optim.lr_scheduler.StepLR(
            self.model_optimizer, self.opt.scheduler_step_size, 0.1)

        fpath = os.path.join(os.path.dirname(__file__), "splits", self.opt.split, "{}_files.txt")

        train_filenames = readlines(fpath.format("train"))
        val_filenames = readlines(fpath.format("val"))

        ## MotionHint: build Pose Manager
        self.pseudo_poses_initialized = False
        self.pseudo_poses = {}
        for lines in train_filenames:
            seq, frame_id, side = lines.split()
            frame_id = int(frame_id)
            if (seq, side) not in self.pseudo_poses:
                self.pseudo_poses[(seq, side)] = frame_id
            else:
                self.pseudo_poses[(seq, side)] = max(self.pseudo_poses[(seq, side)], frame_id)

        for key in self.pseudo_poses:
            max_num = self.pseudo_poses[key] + 2
            self.pseudo_poses[key] = PoseManager(torch.zeros(max_num, 2, 6).to(self.device))
            self.pseudo_poses[key][:, 1, 0] = float('inf')
        # end

        if self.opt.load_weights_folder is not None:
            self.load_model()

        ## MotionHint: convient to set learning rate
        for param_group in self.model_optimizer.param_groups:
            param_group["lr"] = self.opt.learning_rate
        # end

        print("Training model named:\n  ", self.opt.model_name)
        print("Models and tensorboard events files are saved to:\n  ", self.opt.log_dir)
        print("Training is using:\n  ", self.device)

        print(f"Current Learning Rate: {self.model_optimizer.param_groups[0]['lr']}")

        # data
        datasets_dict = {"kitti": datasets.KITTIRAWDataset,
                         "kitti_odom": datasets.KITTIOdomDataset}
        self.dataset = datasets_dict[self.opt.dataset]

        img_ext = '.png' if self.opt.png else '.jpg'

        num_train_samples = len(train_filenames)
        self.num_total_steps = num_train_samples // self.opt.batch_size * self.opt.num_epochs

        train_dataset = self.dataset(
            self.opt.data_path, train_filenames, self.opt.height, self.opt.width,
            self.opt.frame_ids, 4, is_train=True, img_ext=img_ext)
        self.train_loader = DataLoader(
            train_dataset, self.opt.batch_size, True,
            num_workers=self.opt.num_workers, pin_memory=True, drop_last=True)
        val_dataset = self.dataset(
            self.opt.data_path, val_filenames, self.opt.height, self.opt.width,
            self.opt.frame_ids, 4, is_train=False, img_ext=img_ext)
        self.val_loader = DataLoader(
            val_dataset, self.opt.batch_size, True,
            num_workers=self.opt.num_workers, pin_memory=True, drop_last=True)
        self.val_iter = iter(self.val_loader)

        ## MotionHint: Using 'Multi Loss Rebalancing' for weights initialization, just update once
        self.num_losses = 2
        self.mlm = MultiLossManager(self.opt.batch_size, self.num_losses, self.opt.num_for_rebalance, self.opt.update_once)
        # end

        self.writers = {}
        for mode in ["train", "val"]:
            self.writers[mode] = SummaryWriter(os.path.join(self.log_path, mode))

        if not self.opt.no_ssim:
            self.ssim = SSIM()
            self.ssim.to(self.device)

        self.backproject_depth = {}
        self.project_3d = {}
        for scale in self.opt.scales:
            h = self.opt.height // (2 ** scale)
            w = self.opt.width // (2 ** scale)

            self.backproject_depth[scale] = BackprojectDepth(self.opt.batch_size, h, w)
            self.backproject_depth[scale].to(self.device)

            self.project_3d[scale] = Project3D(self.opt.batch_size, h, w)
            self.project_3d[scale].to(self.device)

        self.depth_metric_names = [
            "de/abs_rel", "de/sq_rel", "de/rms", "de/log_rms", "da/a1", "da/a2", "da/a3"]

        print("Using split:\n  ", self.opt.split)
        print("There are {:d} training items and {:d} validation items\n".format(
            len(train_dataset), len(val_dataset)))

        self.save_opts()

    def set_train(self):
        """Convert all models to training mode
        """
        for m in self.models.values():
            m.train()

    def set_eval(self):
        """Convert all models to testing/evaluation mode
        """
        for m in self.models.values():
            m.eval()

    ## MotionHint
    def initialize(self):
        """Generate prior poses using the original SSM-VO
        """
        print('=> Initialize the pseudo poses')
        self.set_eval()

        initialize_bar = tqdm(range(len(self.train_loader)))
        for inputs in self.train_loader:
            _, _ = self.process_batch(inputs)
            initialize_bar.update()

        save_path = os.path.join(self.log_path, 'pseudo_poses.pth')
        torch.save(self.pseudo_poses, save_path)
        print(f'=> The pseudo poses have been saved in {save_path}')
    # end

    def train(self):
        """Run the entire training pipeline
        """
        ## MotionHint:
        ## If the Pose Manager has not been initialized, call 'initialize' function
        if not self.pseudo_poses_initialized:
            with torch.no_grad():
                self.initialize()
        # end

        self.epoch = 0
        self.step = 0
        self.start_time = time.time()
        for self.epoch in range(self.opt.num_epochs):
            ## MotionHint: Param lambda of multi loss rebalancing
            self.current_lambda = max(self.opt.lambda_start + self.epoch * self.opt.lambda_slope, self.opt.lambda_end)
            # end
            self.run_epoch()
            self.val()

    def run_epoch(self):
        """Run a single epoch of training and validation
        """
        self.model_lr_scheduler.step()

        print("Training")
        self.set_train()

        epoch_loss = 0
        origin_loss = 0
        motion_loss = 0
        sample_num = 0
        for batch_idx, inputs in enumerate(self.train_loader):

            before_op_time = time.time()

            outputs, losses = self.process_batch(inputs)

            ## MotionHint: Combining the origin loss and the motion loss using Multi Loss Rebalancing
            with torch.no_grad():
                motion_index = losses['motion_loss'] > 0
                num_motion = motion_index.sum()
            if num_motion:
                loss, cur_ptr = self.mlm.get_total_loss([losses['origin_loss'][motion_index], losses['motion_loss'][motion_index]], num_motion, True)

                self.model_optimizer.zero_grad()
                loss.backward()
                self.model_optimizer.step()

                if cur_ptr >= self.opt.num_for_rebalance:
                    self.mlm.rebalancing(self.current_lambda, self.epoch, os.path.join(self.log_path, 'weights.txt'))

                epoch_loss += loss.cpu().data * num_motion
                origin_loss += losses["origin_loss"][motion_index].sum().data
                motion_loss += losses["motion_loss"][motion_index].sum().data
                sample_num += num_motion
            else:
                loss, cur_ptr = self.mlm.get_total_loss([losses['origin_loss'], losses['motion_loss']], self.opt.batch_size, False)

                # not update parameters, just clear calculation graph
                self.model_optimizer.zero_grad()
                loss.backward()
            # end

            duration = time.time() - before_op_time

            # log less frequently after the first 2000 steps to save time & disk space
            early_phase = batch_idx % self.opt.log_frequency == 0 and self.step < 2000
            late_phase = self.step % 2000 == 0

            if early_phase or late_phase:
                self.log_time(batch_idx, duration, loss.cpu().data)

            self.step += 1

        if sample_num > 0:
            epoch_loss /= sample_num
            origin_loss /= sample_num
            motion_loss /= sample_num

        print(f'Total Loss: {epoch_loss}, Origin Loss: {origin_loss}, Motion Loss: {motion_loss}')
        with open(f'{self.log_path}/log.txt', 'a') as f:
            f.write(f'Total Loss: {epoch_loss}, Origin Loss: {origin_loss}, Motion Loss: {motion_loss} \n')

    def process_batch(self, inputs, is_training=True):
        """Pass a minibatch through the network and generate images and losses
        """
        # skip 'filename' items
        for key, ipt in inputs.items():
            if isinstance(ipt, list):
                continue
            inputs[key] = ipt.to(self.device)

        if self.opt.pose_model_type == "shared":
            # If we are using a shared encoder for both depth and pose (as advocated
            # in monodepthv1), then all images are fed separately through the depth encoder.
            all_color_aug = torch.cat([inputs[("color_aug", i, 0)] for i in self.opt.frame_ids])
            all_features = self.models["encoder"](all_color_aug)
            all_features = [torch.split(f, self.opt.batch_size) for f in all_features]

            features = {}
            for i, k in enumerate(self.opt.frame_ids):
                features[k] = [f[i] for f in all_features]

            outputs = self.models["depth"](features[0])
        else:
            # Otherwise, we only feed the image with frame_id 0 through the depth encoder
            features = self.models["encoder"](inputs["color_aug", 0, 0])
            outputs = self.models["depth"](features)

        if self.opt.predictive_mask:
            outputs["predictive_mask"] = self.models["predictive_mask"](features)

        if self.use_pose_net:
            outputs.update(self.predict_poses(inputs, features))

        self.generate_images_pred(inputs, outputs)
        losses = self.compute_losses(inputs, outputs, is_training)

        ## MotionHint:
        ## Update prior poses in Pose Manager, if new pose have lower loss value.
        with torch.no_grad():
            if is_training:
                self.update_pseudo_poses(inputs, outputs, losses['origin_loss'].data)
        # end

        return outputs, losses

    def update_pseudo_poses(self, inputs, outputs, judge_losses):
        """Update poses in Pose Manager according to loss
        """
        for i in range(self.opt.batch_size):
            seq, frame_id, side = inputs['filename'][i].split()
            frame_id = int(frame_id)

            T_10 = outputs['cam_T_cam', 0, 1][i].data
            T_n10 = outputs['cam_T_cam', 0, -1][i].data
            judge_loss = judge_losses[i]

            loss0 = self.pseudo_poses[(seq, side)][frame_id, 1, 0]
            loss1 = self.pseudo_poses[(seq, side)][frame_id + 1, 1, 0]
            if judge_loss < loss0:
                self.pseudo_poses[(seq, side)].update(frame_id, self.SE2se(T_n10.inverse()), judge_loss)

            if judge_loss < loss1:
                self.pseudo_poses[(seq, side)].update(frame_id+1, self.SE2se(T_10), judge_loss)

    def predict_poses(self, inputs, features):
        """Predict poses between input frames for monocular sequences.
        """
        outputs = {}
        if self.num_pose_frames == 2:
            # In this setting, we compute the pose to each source frame via a
            # separate forward pass through the pose network.

            # select what features the pose network takes as input
            if self.opt.pose_model_type == "shared":
                pose_feats = {f_i: features[f_i] for f_i in self.opt.frame_ids}
            else:
                pose_feats = {f_i: inputs["color_aug", f_i, 0] for f_i in self.opt.frame_ids}

            for f_i in self.opt.frame_ids[1:]:
                if f_i != "s":
                    # To maintain ordering we always pass frames in temporal order
                    if f_i < 0:
                        pose_inputs = [pose_feats[f_i], pose_feats[0]]
                    else:
                        pose_inputs = [pose_feats[0], pose_feats[f_i]]

                    if self.opt.pose_model_type == "separate_resnet":
                        pose_inputs = [self.models["pose_encoder"](torch.cat(pose_inputs, 1))]
                    elif self.opt.pose_model_type == "posecnn":
                        pose_inputs = torch.cat(pose_inputs, 1)

                    axisangle, translation = self.models["pose"](pose_inputs)
                    outputs[("axisangle", 0, f_i)] = axisangle
                    outputs[("translation", 0, f_i)] = translation

                    # Invert the matrix if the frame id is negative
                    outputs[("cam_T_cam", 0, f_i)] = transformation_from_parameters(
                        axisangle[:, 0], translation[:, 0], invert=(f_i < 0))

        else:
            # Here we input all frames to the pose net (and predict all poses) together
            if self.opt.pose_model_type in ["separate_resnet", "posecnn"]:
                pose_inputs = torch.cat(
                    [inputs[("color_aug", i, 0)] for i in self.opt.frame_ids if i != "s"], 1)

                if self.opt.pose_model_type == "separate_resnet":
                    pose_inputs = [self.models["pose_encoder"](pose_inputs)]

            elif self.opt.pose_model_type == "shared":
                pose_inputs = [features[i] for i in self.opt.frame_ids if i != "s"]

            axisangle, translation = self.models["pose"](pose_inputs)

            for i, f_i in enumerate(self.opt.frame_ids[1:]):
                if f_i != "s":
                    outputs[("axisangle", 0, f_i)] = axisangle
                    outputs[("translation", 0, f_i)] = translation
                    outputs[("cam_T_cam", 0, f_i)] = transformation_from_parameters(
                        axisangle[:, i], translation[:, i])

        return outputs

    # def val(self):
    #     """Validate the model on a single minibatch
    #     """
    #     self.set_eval()

    #     eval_list = [0, 1, 2, 3, 4, 5, 6, 7, 8]
    #     img_ext = '.png' if self.opt.png else '.jpg'

    #     ates = []
    #     for seq_id in eval_list:
    #         print(f'=> Eval seq {seq_id} ...')
    #         filenames = readlines(os.path.join(os.path.dirname(__file__), "splits", "odom",
    #                  "test_files_{:02d}.txt".format(seq_id)))
    #         dataset = KITTIOdomDataset(self.opt.data_path, filenames, self.opt.height, self.opt.width,
    #                                 [0, 1], 4, is_train=False, img_ext=img_ext)
    #         dataloader = DataLoader(dataset, self.opt.batch_size, shuffle=False,
    #                     num_workers=self.opt.num_workers, pin_memory=True, drop_last=False)

    #         bar = tqdm(range(len(dataloader)))
    #         pred_local_mat = []
    #         with torch.no_grad():
    #             for inputs in dataloader:
    #                 for key, ipt in inputs.items():
    #                     if isinstance(ipt, list):
    #                         continue
    #                     inputs[key] = ipt.cuda()

    #                 all_color_aug = torch.cat([inputs[("color_aug", i, 0)] for i in [0, 1]], 1)

    #                 features = [self.models['pose_encoder'](all_color_aug)]
    #                 axisangle, translation = self.models['pose'](features)

    #                 pred_local_mat.append(transformation_from_parameters(axisangle[:, 0], translation[:, 0]).cpu().numpy())
    #                 bar.update()

    #         pred_local_mat = np.concatenate(pred_local_mat)
    #         pred_global_mat = relative2absolute(pred_local_mat)

    #         gt_poses_path = os.path.join(self.opt.data_path, "poses", "{:02d}.txt".format(seq_id))
    #         gt_global_mat = np.loadtxt(gt_poses_path).reshape(-1, 3, 4)
    #         gt_global_mat = np.concatenate(
    #             (gt_global_mat, np.zeros((gt_global_mat.shape[0], 1, 4))), 1)
    #         gt_global_mat[:, 3, 3] = 1

    #         pred_global_poses = SEs2ses(pred_global_mat)
    #         gt_global_poses = SEs2ses(gt_global_mat)

    #         r, t, scale = umeyama_alignment(pred_global_poses[:, :3].transpose(1, 0), gt_global_poses[:, :3].transpose(1, 0), True)

    #         align_transformation = np.eye(4)
    #         align_transformation[:3:, :3] = r
    #         align_transformation[:3, 3] = t

    #         N = pred_global_mat.shape[0]
    #         for i in range(N):
    #             pred_global_mat[i][:3, 3] *= scale
    #             pred_global_mat[i] = align_transformation @ pred_global_mat[i]

    #         errors = []
    #         for i in range(N):
    #             gt_xyz = gt_global_mat[i][:3, 3]
    #             pred_xyz = pred_global_mat[i][:3, 3]

    #             align_err = gt_xyz - pred_xyz

    #             errors.append(np.sqrt(np.sum(align_err ** 2)))
    #         ates.append(np.sqrt(np.mean(np.asarray(errors) ** 2)))

    #     mean_ate = np.mean(ates)
    #     with open(f'{self.log_path}/log.txt', 'a') as f:
    #         f.write('ATE: [')
    #         for ate in ates:
    #             f.write(f' {ate},')
    #         f.write(']\n')
    #         f.write(f'Mean ATE: {mean_ate}\n')

    #     self.set_train()

    def val(self):
        """Validate the model
        """
        self.set_eval()
        val_loss = 0
        vbar = tqdm(range(len(self.val_loader)))
        while True:
            try:
                inputs = self.val_iter.next()
            except StopIteration:
                self.val_iter = iter(self.val_loader)
                break

            with torch.no_grad():
                outputs, losses = self.process_batch(inputs, False)

                val_loss += losses['origin_loss'].mean().cpu().data
            vbar.update()

        val_loss /= len(self.val_loader)
        with open(f'{self.log_path}/log.txt', 'a') as f:
            f.write(f'Validate Loss: {val_loss}\n')

        if (self.epoch + 1) % self.opt.save_frequency == 0:
            is_best = val_loss <= self.best_val
            if is_best:
                self.best_val = val_loss
            self.save_model(is_best)

        self.set_train()

    def generate_images_pred(self, inputs, outputs):
        """Generate the warped (reprojected) color images for a minibatch.
        Generated images are saved into the `outputs` dictionary.
        """
        for scale in self.opt.scales:
            disp = outputs[("disp", scale)]
            if self.opt.v1_multiscale:
                source_scale = scale
            else:
                disp = F.interpolate(
                    disp, [self.opt.height, self.opt.width], mode="bilinear", align_corners=False)
                source_scale = 0

            _, depth = disp_to_depth(disp, self.opt.min_depth, self.opt.max_depth)

            outputs[("depth", 0, scale)] = depth

            for i, frame_id in enumerate(self.opt.frame_ids[1:]):

                if frame_id == "s":
                    T = inputs["stereo_T"]
                else:
                    T = outputs[("cam_T_cam", 0, frame_id)]

                # from the authors of https://arxiv.org/abs/1712.00175
                if self.opt.pose_model_type == "posecnn":

                    axisangle = outputs[("axisangle", 0, frame_id)]
                    translation = outputs[("translation", 0, frame_id)]

                    inv_depth = 1 / depth
                    mean_inv_depth = inv_depth.mean(3, True).mean(2, True)

                    T = transformation_from_parameters(
                        axisangle[:, 0], translation[:, 0] * mean_inv_depth[:, 0], frame_id < 0)

                cam_points = self.backproject_depth[source_scale](
                    depth, inputs[("inv_K", source_scale)])
                pix_coords = self.project_3d[source_scale](
                    cam_points, inputs[("K", source_scale)], T)

                outputs[("sample", frame_id, scale)] = pix_coords

                outputs[("color", frame_id, scale)] = F.grid_sample(
                    inputs[("color", frame_id, source_scale)],
                    outputs[("sample", frame_id, scale)],
                    padding_mode="border")

                if not self.opt.disable_automasking:
                    outputs[("color_identity", frame_id, scale)] = \
                        inputs[("color", frame_id, source_scale)]

    def compute_reprojection_loss(self, pred, target):
        """Computes reprojection loss between a batch of predicted and target images
        """
        abs_diff = torch.abs(target - pred)
        l1_loss = abs_diff.mean(1, True)

        if self.opt.no_ssim:
            reprojection_loss = l1_loss
        else:
            ssim_loss = self.ssim(pred, target).mean(1, True)
            reprojection_loss = 0.85 * ssim_loss + 0.15 * l1_loss

        return reprojection_loss

    def use_weak_supervised(self, inputs):
        """Check if can use motion supervision. (if there is enough consecutive prior poses to predict the pseudo pose)
        """
        judge_mat = torch.zeros((self.opt.batch_size, 20)).to(self.device)
        judge_mat[:] = float('inf')

        for i in range(self.opt.batch_size):
            seq, frame_id, side = inputs['filename'][i].split()
            frame_id = int(frame_id)

            if frame_id < 20:
                continue

            judge_mat[i] = self.pseudo_poses[(seq, side)][frame_id-19:frame_id+1, 1, 0]

        use_weak_supervision = (judge_mat == float('inf')).sum(1) == 0
        return use_weak_supervision

    def se2SE(self, se_data):
        SE = torch.eye(4).to(self.device)
        SE[:3, :3] = axis_angle_to_matrix(se_data[3:])
        SE[:3, 3] = se_data[:3]
        return SE

    def ses2SEs(self, se_datas):
        N = se_datas.shape[0]

        SEs = torch.zeros(N, 4, 4).to(self.device)
        for i in range(N):
            SEs[i] = self.se2SE(se_datas[i])
        return SEs

    def SE2se(self, SE_data):
        se = torch.zeros(6).to(self.device)
        se[:3] = SE_data[:3, 3]
        se[3:] = matrix_to_axis_angle(SE_data[:3, :3])
        return se

    def SEs2ses(self, SE_datas):
        N = SE_datas.shape[0]

        ses = torch.zeros(N, 6).to(self.device)
        for i in range(N):
            ses[i] = self.SE2se(SE_datas[i])
        return ses

    # pos_seqs: [BS, 19, 6], relative poses
    def translate_poses(self, pos_seqs, mode='middle'):
        b, num, dim = pos_seqs.shape

        I_idx = (num+1) // 2
        if mode != 'middle':
            print('You need implemente other modes by yourself !')
            return None

        pos_seqs_mat = self.ses2SEs(pos_seqs.reshape(-1, 6)).reshape(b, -1, 4, 4)       # [BS, 19, 4, 4]
        translated_pos_mat = torch.zeros(b, num+1, 4, 4).to(self.device)                # [BS, 20, 4, 4]

        translated_pos_mat[:, I_idx] = torch.eye(4)
        for i in range(I_idx-1, -1, -1):
            translated_pos_mat[:, i] = translated_pos_mat[:, i+1] @ pos_seqs_mat[:, i]
        for i in range(I_idx+1, num+1):
            translated_pos_mat[:, i] = translated_pos_mat[:, i-1] @ pos_seqs_mat[:, i-1].inverse()

        translated_poses = self.SEs2ses(translated_pos_mat.reshape(-1, 4, 4)).reshape(b, -1, 6)

        return translated_poses

    def get_pseudo_poses(self, keys, frame_ids, use_weak_supervision):
        """Obtain the pseudo label

        1. predict the pseudo pose using PPnet
        2. compute the pseudo label
        """
        poselist = itemgetter(*keys)(self.pseudo_poses)

        # Note that the poses saved in Pose Manager are relative poses
        poses = torch.zeros(self.opt.batch_size, 19, 6).to(self.device)
        for i in range(self.opt.batch_size):
            if use_weak_supervision[i]:
                target_frame_id = frame_ids[i]
                poses[i] = poselist[i][target_frame_id-19:target_frame_id, 0]

        # Convert relative poses to pose-centralized poses
        translated_poses = self.translate_poses(poses)      # [BS, 20, 6]

        # 1. predict the pseudo pose using PPnet
        mean, log_variance = self.models['ppnet'](translated_poses)

        # 2. compute the pseudo label
        relative_mat_label = self.ses2SEs(mean).inverse() @ self.ses2SEs(translated_poses[:, -1])

        variance = torch.exp(log_variance)

        return relative_mat_label, variance

    def get_confidence(self, uncertainty, coff=1e3):
        """Convert the predicted variance to supervision confidence
        """
        base = torch.tensor(coff).to(self.device)
        return torch.pow(base, -uncertainty*coff)

    def get_weak_supervision(self, inputs, outputs, side_id, use_weak_supervision):
        """Compute the motion supervision

        1. predict the pseudo pose using PPnet
        2. compute the pseudo label of ego-motion
        3. compute the weighted difference as the motion supervision
        """
        lines = [line.split() for line in inputs['filename']]

        keys = [(line[0], line[2]) for line in lines]
        frame_ids = [int(line[1]) for line in lines]

        if side_id > 0:
            frame_ids = [int(line[1]) + 1 for line in lines]

        with torch.no_grad():
            # 1. predict the pseudo pose using PPnet
            # 2. compute the pseudo label of ego-motion
            pseudo_mat, uncertainties = self.get_pseudo_poses(keys, frame_ids, use_weak_supervision)

            if side_id < 0:
                pseudo_mat = pseudo_mat.inverse()

        pred_mat = outputs['cam_T_cam', 0, side_id]

        pred_poses = self.SEs2ses(pred_mat)
        pseudo_poses = self.SEs2ses(pseudo_mat)

        confidence = self.get_confidence(uncertainties.sum(1))
        confidence_filter = confidence * (confidence > self.opt.conf_thres)

        # 3. compute the weighted difference as the motion supervision
        return confidence_filter * torch.norm(pseudo_poses - pred_poses, dim=1)

    def coff_expand(self, coff, pattern):
        res = torch.zeros_like(pattern).to(self.device)
        for i in range(pattern.shape[0]):
            res[i] = coff[i]
        return res

    def compute_losses(self, inputs, outputs, is_training):
        """Compute the reprojection and smoothness losses for a minibatch
        """
        losses = {}
        origin_loss = 0

        ## MotionHint:
        ## While training
        ## 1. check if can use motion supervision. (At some points, there is not enough prior poses to predicted the pseudo pose)
        ## 2. predict the pseudo pose using PPnet and compute the pseudo label
        if is_training:
            motion_loss = 0
            with torch.no_grad():
                use_weak_supervision = self.use_weak_supervised(inputs)
            if use_weak_supervision.sum():
                weak_supervision = {}
                for frame_id in self.opt.frame_ids[1:]:
                    weak_supervision[frame_id] = self.get_weak_supervision(inputs, outputs, frame_id, use_weak_supervision) * use_weak_supervision
            else:
                weak_supervision = {}
                for frame_id in self.opt.frame_ids[1:]:
                    weak_supervision[frame_id] = torch.zeros(self.opt.batch_size)
        # end

        for scale in self.opt.scales:
            loss = 0
            reprojection_losses = []

            if self.opt.v1_multiscale:
                source_scale = scale
            else:
                source_scale = 0

            disp = outputs[("disp", scale)]
            color = inputs[("color", 0, scale)]
            target = inputs[("color", 0, source_scale)]

            for frame_id in self.opt.frame_ids[1:]:
                pred = outputs[("color", frame_id, scale)]
                reprojection_losses.append(self.compute_reprojection_loss(pred, target))

            reprojection_losses = torch.cat(reprojection_losses, 1)

            if not self.opt.disable_automasking:
                identity_reprojection_losses = []
                for frame_id in self.opt.frame_ids[1:]:
                    pred = inputs[("color", frame_id, source_scale)]
                    identity_reprojection_losses.append(
                        self.compute_reprojection_loss(pred, target))

                identity_reprojection_losses = torch.cat(identity_reprojection_losses, 1)

                if self.opt.avg_reprojection:
                    identity_reprojection_loss = identity_reprojection_losses.mean(1, keepdim=True)
                else:
                    # save both images, and do min all at once below
                    identity_reprojection_loss = identity_reprojection_losses

            elif self.opt.predictive_mask:
                # use the predicted mask
                mask = outputs["predictive_mask"]["disp", scale]
                if not self.opt.v1_multiscale:
                    mask = F.interpolate(
                        mask, [self.opt.height, self.opt.width],
                        mode="bilinear", align_corners=False)

                reprojection_losses *= mask

                # add a loss pushing mask to 1 (using nn.BCELoss for stability)
                weighting_loss = 0.2 * nn.BCELoss()(mask, torch.ones(mask.shape).cuda())
                loss += weighting_loss.mean()

            if self.opt.avg_reprojection:
                reprojection_loss = reprojection_losses.mean(1, keepdim=True)
            else:
                reprojection_loss = reprojection_losses

            if not self.opt.disable_automasking:
                # add random numbers to break ties
                identity_reprojection_loss += torch.randn(
                    identity_reprojection_loss.shape).cuda() * 0.00001

                combined = torch.cat((identity_reprojection_loss, reprojection_loss), dim=1)
            else:
                combined = reprojection_loss

            if combined.shape[1] == 1:
                to_optimise = combined
            else:
                to_optimise, idxs = torch.min(combined, dim=1)
                ## MotionHint: build motion supervision
                ## note: 'idxs' determines which side is used to supervise
                if is_training:
                    motion_error = torch.zeros_like(to_optimise).to(self.device)
                    search_n1 = (idxs == 0) | (idxs == 2)
                    search_p1 = (idxs == 1) | (idxs == 3)
                    motion_error[search_n1] = self.coff_expand(weak_supervision[-1], motion_error)[search_n1]
                    motion_error[search_p1] = self.coff_expand(weak_supervision[1], motion_error)[search_p1]
                # end

            if not self.opt.disable_automasking:
                outputs["identity_selection/{}".format(scale)] = (
                    idxs > identity_reprojection_loss.shape[1] - 1).float()

            loss += to_optimise.mean(-1).mean(-1)
            if is_training:
                motion_loss += motion_error.mean(-1).mean(-1)

            mean_disp = disp.mean(2, True).mean(3, True)
            norm_disp = disp / (mean_disp + 1e-7)
            smooth_loss = get_smooth_loss(norm_disp, color)

            loss += self.opt.disparity_smoothness * smooth_loss / (2 ** scale)
            origin_loss += loss
            losses["loss/{}".format(scale)] = loss

        origin_loss /= self.num_scales
        losses["origin_loss"] = origin_loss

        if is_training:
            motion_loss /= self.num_scales
            losses["motion_loss"] = motion_loss
        return losses

    def compute_depth_losses(self, inputs, outputs, losses):
        """Compute depth metrics, to allow monitoring during training

        This isn't particularly accurate as it averages over the entire batch,
        so is only used to give an indication of validation performance
        """
        depth_pred = outputs[("depth", 0, 0)]
        depth_pred = torch.clamp(F.interpolate(
            depth_pred, [375, 1242], mode="bilinear", align_corners=False), 1e-3, 80)
        depth_pred = depth_pred.detach()

        depth_gt = inputs["depth_gt"]
        mask = depth_gt > 0

        # garg/eigen crop
        crop_mask = torch.zeros_like(mask)
        crop_mask[:, :, 153:371, 44:1197] = 1
        mask = mask * crop_mask

        depth_gt = depth_gt[mask]
        depth_pred = depth_pred[mask]
        depth_pred *= torch.median(depth_gt) / torch.median(depth_pred)

        depth_pred = torch.clamp(depth_pred, min=1e-3, max=80)

        depth_errors = compute_depth_errors(depth_gt, depth_pred)

        for i, metric in enumerate(self.depth_metric_names):
            losses[metric] = np.array(depth_errors[i].cpu())

    def log_time(self, batch_idx, duration, loss):
        """Print a logging statement to the terminal
        """
        samples_per_sec = self.opt.batch_size / duration
        time_sofar = time.time() - self.start_time
        training_time_left = (
            self.num_total_steps / self.step - 1.0) * time_sofar if self.step > 0 else 0
        print_string = "epoch {:>3} | batch {:>6} | examples/s: {:5.1f}" + \
            " | loss: {:.5f} | time elapsed: {} | time left: {}"
        print(print_string.format(self.epoch, batch_idx, samples_per_sec, loss,
                                  sec_to_hm_str(time_sofar), sec_to_hm_str(training_time_left)))
        with open(f'{self.log_path}/log.txt', 'a') as f:
            f.write(print_string.format(self.epoch, batch_idx, samples_per_sec, loss, sec_to_hm_str(time_sofar), sec_to_hm_str(training_time_left)))
            f.write('\n')

    def log(self, mode, inputs, outputs, losses):
        """Write an event to the tensorboard events file
        """
        writer = self.writers[mode]
        for l, v in losses.items():
            writer.add_scalar("{}".format(l), v, self.step)

        for j in range(min(4, self.opt.batch_size)):  # write a maxmimum of four images
            for s in self.opt.scales:
                for frame_id in self.opt.frame_ids:
                    writer.add_image(
                        "color_{}_{}/{}".format(frame_id, s, j),
                        inputs[("color", frame_id, s)][j].data, self.step)
                    if s == 0 and frame_id != 0:
                        writer.add_image(
                            "color_pred_{}_{}/{}".format(frame_id, s, j),
                            outputs[("color", frame_id, s)][j].data, self.step)

                writer.add_image(
                    "disp_{}/{}".format(s, j),
                    normalize_image(outputs[("disp", s)][j]), self.step)

                if self.opt.predictive_mask:
                    for f_idx, frame_id in enumerate(self.opt.frame_ids[1:]):
                        writer.add_image(
                            "predictive_mask_{}_{}/{}".format(frame_id, s, j),
                            outputs["predictive_mask"][("disp", s)][j, f_idx][None, ...],
                            self.step)

                elif not self.opt.disable_automasking:
                    writer.add_image(
                        "automask_{}/{}".format(s, j),
                        outputs["identity_selection/{}".format(s)][j][None, ...], self.step)

    def save_opts(self):
        """Save options to disk so we know what we ran this experiment with
        """
        models_dir = os.path.join(self.log_path, "models")
        if not os.path.exists(models_dir):
            os.makedirs(models_dir)
        to_save = self.opt.__dict__.copy()

        with open(os.path.join(models_dir, 'opt.json'), 'w') as f:
            json.dump(to_save, f, indent=2)

    def save_model(self, is_best):
        """Save model weights to disk
        """
        save_folder = os.path.join(self.log_path, "models", "weights_{}".format(self.epoch))
        if not os.path.exists(save_folder):
            os.makedirs(save_folder)

        for model_name, model in self.models.items():
            save_path = os.path.join(save_folder, "{}.pth".format(model_name))
            to_save = model.state_dict()
            if model_name == 'encoder':
                # save the sizes - these are needed at prediction time
                to_save['height'] = self.opt.height
                to_save['width'] = self.opt.width
                to_save['use_stereo'] = self.opt.use_stereo
            torch.save(to_save, save_path)

        save_path = os.path.join(save_folder, "{}.pth".format("adam"))
        torch.save(self.model_optimizer.state_dict(), save_path)

        if is_best:
            tgt_folder = os.path.join(self.log_path, "models", "best")
            if not os.path.exists(tgt_folder):
                os.makedirs(tgt_folder)

            for file in os.listdir(save_folder):
                src_file = os.path.join(save_folder, file)
                tgt_file = os.path.join(tgt_folder, file)
                if os.path.isfile(src_file):
                    shutil.copyfile(src_file, tgt_file)

    def load_model(self):
        """Load model(s) from disk
        """
        self.opt.load_weights_folder = os.path.expanduser(self.opt.load_weights_folder)

        assert os.path.isdir(self.opt.load_weights_folder), \
            "Cannot find folder {}".format(self.opt.load_weights_folder)
        print("loading model from folder {}".format(self.opt.load_weights_folder))

        for n in self.opt.models_to_load:
            print("Loading {} weights...".format(n))
            path = os.path.join(self.opt.load_weights_folder, "{}.pth".format(n))
            model_dict = self.models[n].state_dict()
            pretrained_dict = torch.load(path)
            pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
            model_dict.update(pretrained_dict)
            self.models[n].load_state_dict(model_dict)

        # loading adam state
        optimizer_load_path = os.path.join(self.opt.load_weights_folder, "adam.pth")
        if os.path.isfile(optimizer_load_path):
            print("Loading Adam weights")
            optimizer_dict = torch.load(optimizer_load_path)
            self.model_optimizer.load_state_dict(optimizer_dict)
        else:
            print("Cannot find Adam weights so Adam is randomly initialized")

        # loading pseudo poses
        pseudo_poses_path = os.path.join(self.opt.load_weights_folder, "pseudo_poses.pth")
        if os.path.isfile(pseudo_poses_path):
            print("Loading Pseudo Poses")
            self.pseudo_poses = torch.load(pseudo_poses_path)
            self.pseudo_poses_initialized = True
        else:
            print("Cannot find Pseudo Poses so execute initializing")


def control(choice):
    namelist = []
    attendance = []
    for line in file:
        record = line.rstrip().split(";")
        if record[-1] == choice:
            namelist.append(record[1:3])
    for i in namelist:
        name = i[0] + " " + i[1]
        present = input(f"{name}:")
        if present.upper() == "N":
            attendance.append(i + ["NOT"])
        else:
            attendance.append(i + ["OK"])
    return(attendance)

def classlist():
    classes = set()
    for line in file:
        record = line.rstrip().split(";")
        classes.add(record[3])
    return(classes)

with open("Files for Chapter 12\classlist.csv") as file:
    classes = classlist()
    print("Pick one of these classes:")
    for i in classes:
        print(i, end=" | ")
    print("")
with open("Files for Chapter 12\classlist.csv") as file:
    choice = input("enter a classroom: ")
    attendance = control(choice)
    print(attendance)
if len(attendance) > 0:
    file = open(f"{choice}.txt", "w")
    for i in attendance:
        file.write(f"{i[0]};{i[1]};{i[2]}\n")
else:
    print("This class doesn't exist")

pythonProject/Desktop exercises/Files for Chapter 12/exercise2.py