longw010
1/10/2018 - 4:01 PM

Apply gradient descent to optimize the objective function of model2

Apply gradient descent to optimize the objective function of model2

import time
from utils import *
import argparse

# set a random seed to make experiments reproducible
np.random.seed(10)


# create parser
def create_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument('--input_file', default=None, help='Input data file')
    parser.add_argument('--epochs', default=None, help='The number of epochs during training')
    parser.add_argument('--lr', default=None, help='Learning rate')
    return parser


# initialize the coordinates of beads
def init(n, r=1.5):
    node_posi = np.zeros((n, 3), dtype=float)
    for i in range(n):
        node_posi[i, :] = np.random.uniform(-r, r, 3)
    return node_posi


# set initial weights
def weights():
    # weights setting for chr1
    W = np.zeros(4, dtype=float)
    W[0], W[1], W[2], W[3] = 1.0, 0.4, 0.4, 0.4
    return W

# set initial distance parameters
def dist_const():
    # distant setting; note that all are in square.
    dist = dict()
    dist['max'] = 30 ** 2
    dist['min'] = 0.2**2
    dist['dc'] = 6 ** 2
    dist['damax'] = 1.5 ** 2
    return dist


# load data from file
def load_data(file_name):
    ## load data
    with open(file_name, 'r') as f:
        # get the number of nodes
        num_node = int(f.readline().strip())-19

        # get the value of totalIF
        #totalIF = float(f.readline().strip())
        totalIF=1000

        # get the value of IFmax
        IFmax = float(f.readline().strip())

        # initialize F
        F = np.zeros((num_node, num_node), dtype=float) - 1

        for line in f:
            submatrix = line.strip().split('\t')
            idx1 = int(submatrix[0])-1
            idx2 = int(submatrix[1])-1
            if idx1 > 121:
                idx1 -= 19
            if idx2 > 121:
                idx2 -= 19
            F[idx1][idx2] = float(submatrix[2])
            F[idx2][idx1] = float(submatrix[2])
        return num_node, totalIF, IFmax, F


# update coordinate thru each epochs
def solver(filename, dconst, W, epochs=10, lr=0.00001):
    ## load data
    num_node, totalIF, IFmax, F = load_data(filename)

    ## initialize vars
    node_posi = init(num_node)
    loss = []

    ## go thru epoch
    for epoch in range(epochs):
        # calculate pairwise distance
        print('--epoch- ' + str(epoch))
        node_dist = pairwise_dist(node_posi)
        # calculate loss
        current_loss = cal_loss(node_dist, F)
        print(current_loss)
        loss += [current_loss]

        # calculate gradient
        for i in range(num_node):
            df_cache = cal_df_cache(node_posi, node_dist, dconst, W, IFmax,
                                    totalIF, F)
            dx, dy, dz = df_cache[i][0], df_cache[i][1], df_cache[i][2]
            #print(df_cache[i])
            # print (dx, dy, dz)
            node_posi[i][0] += lr * dx
            node_posi[i][1] += lr * dy
            node_posi[i][2] += lr * dz
        #simple_visulization(node_posi)
    return loss, node_posi


# backpropagation
def cal_df_cache(node_posi, node_dist, dconst, W, IFmax, totalIF, F):
    num_node = len(node_dist)
    Fn = np.zeros((num_node, 3), dtype=float)
    for i in range(num_node):
        # it is symmetic
        #temp = 0.0
        num_outlink = 0
        for j in range(num_node):
            # check whether it has valid F[i][j]
            if F[i][j] < 0 or F[j][i] < 0 or i == j:
                continue

            i, j = min(i, j), max(i, j)
            num_outlink += 1
            current_dist = node_dist[i][j] ** 2
            # when |i-j| = 1
            if j == i + 1:
                temp = - W[0] * IFmax * sech2(current_dist - dconst['damax']) + W[1] * sech2(current_dist - dconst['min'])
            else:
                # when it is in contact
                if current_dist < dconst['dc']:
                    temp = - W[0] * sech2(current_dist - dconst['dc']) * F[i][j] * 2 * node_dist[i][j] + W[1] * sech2(
                        current_dist - dconst['min'])
                # when it is not in contact
                else:
                    temp = - W[2] * sech2(current_dist - dconst['max']) + W[3] * sech2(current_dist - dconst['dc'])
            # *2 is from gradient
            temp *= 2
            # divide const from d{d_ij} / d{x}
            for coordinate in range(3):
                Fn[i][coordinate] += temp * (node_posi[i][coordinate] - node_posi[j][coordinate])

        # normalize the gradient add
        Fn[i, :] /= num_outlink
    return Fn

# calculate loss at each epoch
def cal_loss_obj(node_dist, dconst, W, IFmax, totalIF, F):
    num_node = len(node_dist)
    Fn = 0.0
    count = 0
    for i in range(num_node):
        # it is symmetic
        for j in range(i + 1, num_node):
            # check whether it has valid F[i][j]
            if F[i][j] < 0:
                continue

            current_dist = node_dist[i][j] ** 2
            # print(current_dist)
            # when |i-j| = 1
            if j == i + 1:
                temp = W[0] * IFmax * np.tanh(dconst['damax'] - current_dist) + W[1] * np.tanh(current_dist - dconst['min'])
                # print('1', temp)
            else:
                # when it is in contact
                if current_dist < dconst['dc']:
                    temp = W[0] * np.tanh(dconst['dc'] - current_dist) * F[i][j] + W[1] * np.tanh(current_dist - dconst['min'])
                    # print('2', temp)
                # when it is not in contact
                else:
                    temp = W[2] * np.tanh(dconst['max'] - current_dist) + W[3] * np.tanh(current_dist - dconst['dc'])
                    # print('3', temp)
            Fn += temp
            count += 1
    return Fn * 2


# calculate pairwise distance
def pairwise_dist(node_posi):
    # init variables
    num_node = len(node_posi)
    dist = np.zeros((num_node, num_node), dtype=float)

    for i in range(num_node):
        for j in range(i + 1, num_node):
            dist[i][j] = get_dist(node_posi[i, :], node_posi[j, :])
    return dist

# print the results
def print_result(node_posi):
    file = open('output.txt', 'w')
    for element in node_posi:
        file.write('\t'.join(map(str, element)) + '\n')
    file.close()

# main
def main():
    start = time.time()
    W = weights()
    dconst = dist_const()
    _, node_posi = solver(argparse.input_file, dconst, W, lr=argparse.lr, epochs=argparse.epochs)
    print(time.time() - start)


if __name__ == "__main__":
    main()
import numpy as np
import math

# calculate the square of sech function
def sech2(x):
    """
    sech(x)
    Uses numpy's cosh(x).
    """
    return np.power(1. / np.cosh(x), 2)

# calculate euclidean distance between two nodes
def get_dist(x, y):
    return np.linalg.norm(x-y)


# calculate loss from model1
def cal_loss(node_posi, real_dist):
    num = len(node_posi)
    loss = 0.0
    count = 0
    for i in range(num):
        for j in range(i+1, num):
            if real_dist[i][j] > 0:
                loss += (node_posi[i][j] - real_dist[i][j])**2 / real_dist[i][j]**2
                count += 1
    return 2 * loss / count


# 3D visulation
def simple_visulization(node_posi):
    import matplotlib as mpl
    import matplotlib.pyplot as plt

    mpl.rcParams['legend.fontsize'] = 10

    fig = plt.figure()
    ax = fig.gca(projection='3d')
    N = len(node_posi)

    ax.plot(node_posi[:, 0], node_posi[:, 1], node_posi[:, 2],
            label='parametric curve')
    ax.legend()
    fig.savefig('test.pdf')  # save the figure to file