def main():

    train_X = np.asarray([[0.2, -0.3], [0.1, -0.9], [0.3, 0.5]])
    train_Y = np.asarray([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]])

    # For comparison
    net = ParticleDipoleNetwork(cost="mse", particle_input=ParticleDipoleInput(2))
    net.append(ParticleDipole(2, 5, activation="sigmoid"))
    net.append(ParticleDipole(5, 3, activation="sigmoid"))

    print(net.predict(train_X))
    print(net.cost(train_X, train_Y))

    net2 = ParticleDipoleTreeNetwork(cost="mse", particle_input=ParticleDipoleTreeInput(2))
    net2.append(ParticleDipoleTree(2, 5, activation="sigmoid"))
    net2.append(ParticleDipoleTree(5, 3, activation="sigmoid"))
    # Make sure we have the same coordinates and charges
    net2.particle_input.copy_pos_neg_positions(net.particle_input.rx_pos, net.particle_input.ry_pos, net.particle_input.rz_pos,
                                               net.particle_input.rx_neg, net.particle_input.ry_neg, net.particle_input.rz_neg)
    for l in range(len(net.layers)):
        net2.layers[l].copy_pos_neg_positions(net.layers[l].q, net.layers[l].b,
                                              net.layers[l].rx_pos, net.layers[l].ry_pos, net.layers[l].rz_pos,
                                              net.layers[l].rx_neg, net.layers[l].ry_neg, net.layers[l].rz_neg)

    print(net2.predict(train_X))
    print(net2.cost(train_X, train_Y))
def main():

    train_X = np.asarray([[0.2, -0.3], [0.1, -0.9]])
    train_Y = np.asarray([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])

    net = ParticleDipoleNetwork(cost="mse", particle_input=ParticleDipoleInput(2))
    net.append(ParticleDipole(2, 5, activation="sigmoid", k_eq=0.1))
    net.append(ParticleDipole(5, 3, activation="sigmoid", k_eq=0.1))

    print(net.predict(train_X))
    print(net.cost(train_X, train_Y))
def main():

    train_X = np.asarray([[0.2, -0.3], [0.1, -0.9], [0.3, 0.5]])
    train_Y = np.asarray([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]])

    # For comparison
    net = ParticleDipoleNetwork(cost="mse",
                                particle_input=ParticleDipoleInput(2))
    net.append(ParticleDipole(2, 5, activation="sigmoid"))
    net.append(ParticleDipole(5, 3, activation="sigmoid"))

    print(net.predict(train_X))
    print(net.cost(train_X, train_Y))

    net2 = ParticleDipoleTreeNetwork(cost="mse",
                                     particle_input=ParticleDipoleTreeInput(2))
    net2.append(ParticleDipoleTree(2, 5, activation="sigmoid"))
    net2.append(ParticleDipoleTree(5, 3, activation="sigmoid"))
    # Make sure we have the same coordinates and charges
    net2.particle_input.copy_pos_neg_positions(net.particle_input.rx_pos,
                                               net.particle_input.ry_pos,
                                               net.particle_input.rz_pos,
                                               net.particle_input.rx_neg,
                                               net.particle_input.ry_neg,
                                               net.particle_input.rz_neg)
    for l in range(len(net.layers)):
        net2.layers[l].copy_pos_neg_positions(
            net.layers[l].q, net.layers[l].b, net.layers[l].rx_pos,
            net.layers[l].ry_pos, net.layers[l].rz_pos, net.layers[l].rx_neg,
            net.layers[l].ry_neg, net.layers[l].rz_neg)

    print(net2.predict(train_X))
    print(net2.cost(train_X, train_Y))
def main():

    train_X = np.asarray([[0.2, -0.3], [0.1, -0.9]])
    train_Y = np.asarray([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])

    net = ParticleDipoleNetwork(cost="mse",
                                particle_input=ParticleDipoleInput(2))
    net.append(ParticleDipole(2, 5, activation="sigmoid", k_eq=0.1))
    net.append(ParticleDipole(5, 3, activation="sigmoid", k_eq=0.1))

    print(net.predict(train_X))
    print(net.cost(train_X, train_Y))
def fd():
    train_X = np.asarray([[0.2, -0.3], [0.1, -0.9]])
    train_Y = np.asarray([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])

    net = ParticleDipoleNetwork(
        cost="categorical_cross_entropy",
        particle_input=ParticleDipoleInput(2),
        regularizer=ParticleDipoleRegularize(coeff_lambda=0.03))
    net.append(
        ParticleDipole(2, 5, activation="sigmoid", k_eq=0.1, k_bond=10.0))
    net.append(
        ParticleDipole(5, 6, activation="sigmoid", k_eq=0.1, k_bond=10.0))
    net.append(
        ParticleDipole(6, 3, activation="softmax", k_eq=0.1, k_bond=10.0))

    db, dq, drx_pos, dry_pos, drz_pos, drx_neg, dry_neg, drz_neg = net.cost_gradient(
        train_X, train_Y)

    h = 0.00001

    print("analytic b")
    print(db)

    fd_b = []
    for l in range(len(net.layers)):
        lb = []
        for c in range(len(net.layers[l].b)):
            for b in range(len(net.layers[l].b[c])):
                orig = net.layers[l].b[c][b]
                net.layers[l].b[c][b] += h
                fp = net.cost(train_X, train_Y)
                net.layers[l].b[c][b] -= 2 * h
                fm = net.cost(train_X, train_Y)
                lb.append((fp - fm) / (2 * h))
                net.layers[l].b[c][b] = orig
        fd_b.append(lb)
    print("numerical b")
    print(fd_b)

    print("analytic q")
    for x in dq:
        print(x)

    fd_q = []
    for l in range(len(net.layers)):
        lq = []
        for i in range(len(net.layers[l].q)):
            orig = net.layers[l].q[i]
            net.layers[l].q[i] += h
            fp = net.cost(train_X, train_Y)
            net.layers[l].q[i] -= 2 * h
            fm = net.cost(train_X, train_Y)
            lq.append((fp - fm) / (2 * h))
            net.layers[l].q[i] = orig
        fd_q.append(lq)

    print("numerical q")
    for x in fd_q:
        print(x)

    print("analytic x pos")
    for layer in drx_pos:
        print(layer)
    print("analytic y pos")
    for layer in dry_pos:
        print(layer)
    print("analytic z pos")
    for layer in drz_pos:
        print(layer)

    fd_r_x = []
    fd_r_y = []
    fd_r_z = []

    # input first
    layer = net.particle_input
    lr_x = []
    lr_y = []
    lr_z = []
    for i in range(layer.output_size):
        # x
        orig = layer.rx_pos[i]
        layer.rx_pos[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rx_pos[i] -= 2 * h
        fm = net.cost(train_X, train_Y)
        lr_x.append((fp - fm) / (2 * h))
        layer.rx_pos[i] = orig

        # y
        orig = layer.ry_pos[i]
        layer.ry_pos[i] += h
        fp = net.cost(train_X, train_Y)
        layer.ry_pos[i] -= 2 * h
        fm = net.cost(train_X, train_Y)
        lr_y.append((fp - fm) / (2 * h))
        layer.ry_pos[i] = orig

        # z
        orig = layer.rz_pos[i]
        layer.rz_pos[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rz_pos[i] -= 2 * h
        fm = net.cost(train_X, train_Y)
        lr_z.append((fp - fm) / (2 * h))
        layer.rz_pos[i] = orig

    fd_r_x.append(lr_x)
    fd_r_y.append(lr_y)
    fd_r_z.append(lr_z)

    # layers
    for layer in net.layers:
        lr_x = []
        lr_y = []
        lr_z = []
        for i in range(layer.output_size):
            # x
            orig = layer.rx_pos[i]
            layer.rx_pos[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rx_pos[i] -= 2 * h
            fm = net.cost(train_X, train_Y)
            lr_x.append((fp - fm) / (2 * h))
            layer.rx_pos[i] = orig

            # y
            orig = layer.ry_pos[i]
            layer.ry_pos[i] += h
            fp = net.cost(train_X, train_Y)
            layer.ry_pos[i] -= 2 * h
            fm = net.cost(train_X, train_Y)
            lr_y.append((fp - fm) / (2 * h))
            layer.ry_pos[i] = orig

            # z
            orig = layer.rz_pos[i]
            layer.rz_pos[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rz_pos[i] -= 2 * h
            fm = net.cost(train_X, train_Y)
            lr_z.append((fp - fm) / (2 * h))
            layer.rz_pos[i] = orig

        fd_r_x.append(lr_x)
        fd_r_y.append(lr_y)
        fd_r_z.append(lr_z)

    print("numerical r x pos")
    for f in fd_r_x:
        print(f)
    print("numerical r y pos")
    for f in fd_r_y:
        print(f)
    print("numerical r z pos")
    for f in fd_r_z:
        print(f)

    print("analytic x neg")
    for layer in drx_neg:
        print(layer)
    print("analytic y neg")
    for layer in dry_neg:
        print(layer)
    print("analytic z neg")
    for layer in drz_neg:
        print(layer)

    fd_r_x = []
    fd_r_y = []
    fd_r_z = []

    # input first
    layer = net.particle_input
    lr_x = []
    lr_y = []
    lr_z = []
    for i in range(layer.output_size):
        # x
        orig = layer.rx_neg[i]
        layer.rx_neg[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rx_neg[i] -= 2 * h
        fm = net.cost(train_X, train_Y)
        lr_x.append((fp - fm) / (2 * h))
        layer.rx_neg[i] = orig

        # y
        orig = layer.ry_neg[i]
        layer.ry_neg[i] += h
        fp = net.cost(train_X, train_Y)
        layer.ry_neg[i] -= 2 * h
        fm = net.cost(train_X, train_Y)
        lr_y.append((fp - fm) / (2 * h))
        layer.ry_neg[i] = orig

        # z
        orig = layer.rz_neg[i]
        layer.rz_neg[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rz_neg[i] -= 2 * h
        fm = net.cost(train_X, train_Y)
        lr_z.append((fp - fm) / (2 * h))
        layer.rz_neg[i] = orig

    fd_r_x.append(lr_x)
    fd_r_y.append(lr_y)
    fd_r_z.append(lr_z)

    # layers
    for layer in net.layers:
        lr_x = []
        lr_y = []
        lr_z = []
        for i in range(layer.output_size):
            # x
            orig = layer.rx_neg[i]
            layer.rx_neg[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rx_neg[i] -= 2 * h
            fm = net.cost(train_X, train_Y)
            lr_x.append((fp - fm) / (2 * h))
            layer.rx_neg[i] = orig

            # y
            orig = layer.ry_neg[i]
            layer.ry_neg[i] += h
            fp = net.cost(train_X, train_Y)
            layer.ry_neg[i] -= 2 * h
            fm = net.cost(train_X, train_Y)
            lr_y.append((fp - fm) / (2 * h))
            layer.ry_neg[i] = orig

            # z
            orig = layer.rz_neg[i]
            layer.rz_neg[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rz_neg[i] -= 2 * h
            fm = net.cost(train_X, train_Y)
            lr_z.append((fp - fm) / (2 * h))
            layer.rz_neg[i] = orig

        fd_r_x.append(lr_x)
        fd_r_y.append(lr_y)
        fd_r_z.append(lr_z)

    print("numerical r x neg")
    for f in fd_r_x:
        print(f)
    print("numerical r y neg")
    for f in fd_r_y:
        print(f)
    print("numerical r z neg")
    for f in fd_r_z:
        print(f)
def fd():
    train_X = np.asarray([[0.2, -0.3], [0.1, -0.9]])
    train_Y = np.asarray([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])

    net = ParticleDipoleNetwork(cost="categorical_cross_entropy", particle_input=ParticleDipoleInput(2),
                                regularizer=ParticleDipoleRegularize(coeff_lambda=0.03))
    net.append(ParticleDipole(2, 5, activation="sigmoid", k_eq=0.1, k_bond=10.0))
    net.append(ParticleDipole(5, 6, activation="sigmoid", k_eq=0.1, k_bond=10.0))
    net.append(ParticleDipole(6, 3, activation="softmax", k_eq=0.1, k_bond=10.0))

    db, dq, drx_pos, dry_pos, drz_pos, drx_neg, dry_neg, drz_neg = net.cost_gradient(train_X, train_Y)

    h = 0.00001

    print("analytic b")
    print(db)

    fd_b = []
    for l in range(len(net.layers)):
        lb = []
        for c in range(len(net.layers[l].b)):
            for b in range(len(net.layers[l].b[c])):
                orig = net.layers[l].b[c][b]
                net.layers[l].b[c][b] += h
                fp = net.cost(train_X, train_Y)
                net.layers[l].b[c][b] -= 2*h
                fm = net.cost(train_X, train_Y)
                lb.append((fp - fm) / (2*h))
                net.layers[l].b[c][b] = orig
        fd_b.append(lb)
    print("numerical b")
    print(fd_b)

    print("analytic q")
    for x in dq:
        print(x)

    fd_q = []
    for l in range(len(net.layers)):
        lq = []
        for i in range(len(net.layers[l].q)):
            orig = net.layers[l].q[i]
            net.layers[l].q[i] += h
            fp = net.cost(train_X, train_Y)
            net.layers[l].q[i] -= 2*h
            fm = net.cost(train_X, train_Y)
            lq.append((fp - fm) / (2*h))
            net.layers[l].q[i] = orig
        fd_q.append(lq)

    print("numerical q")
    for x in fd_q:
        print(x)

    print("analytic x pos")
    for layer in drx_pos:
        print(layer)
    print("analytic y pos")
    for layer in dry_pos:
        print(layer)
    print("analytic z pos")
    for layer in drz_pos:
        print(layer)

    fd_r_x = []
    fd_r_y = []
    fd_r_z = []

    # input first
    layer = net.particle_input
    lr_x = []
    lr_y = []
    lr_z = []
    for i in range(layer.output_size):
        # x
        orig = layer.rx_pos[i]
        layer.rx_pos[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rx_pos[i] -= 2*h
        fm = net.cost(train_X, train_Y)
        lr_x.append((fp - fm) / (2*h))
        layer.rx_pos[i] = orig

        # y
        orig = layer.ry_pos[i]
        layer.ry_pos[i] += h
        fp = net.cost(train_X, train_Y)
        layer.ry_pos[i] -= 2*h
        fm = net.cost(train_X, train_Y)
        lr_y.append((fp - fm) / (2*h))
        layer.ry_pos[i] = orig

        # z
        orig = layer.rz_pos[i]
        layer.rz_pos[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rz_pos[i] -= 2*h
        fm = net.cost(train_X, train_Y)
        lr_z.append((fp - fm) / (2*h))
        layer.rz_pos[i] = orig

    fd_r_x.append(lr_x)
    fd_r_y.append(lr_y)
    fd_r_z.append(lr_z)

    # layers
    for layer in net.layers:
        lr_x = []
        lr_y = []
        lr_z = []
        for i in range(layer.output_size):
            # x
            orig = layer.rx_pos[i]
            layer.rx_pos[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rx_pos[i] -= 2*h
            fm = net.cost(train_X, train_Y)
            lr_x.append((fp - fm) / (2*h))
            layer.rx_pos[i] = orig

            # y
            orig = layer.ry_pos[i]
            layer.ry_pos[i] += h
            fp = net.cost(train_X, train_Y)
            layer.ry_pos[i] -= 2*h
            fm = net.cost(train_X, train_Y)
            lr_y.append((fp - fm) / (2*h))
            layer.ry_pos[i] = orig

            # z
            orig = layer.rz_pos[i]
            layer.rz_pos[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rz_pos[i] -= 2*h
            fm = net.cost(train_X, train_Y)
            lr_z.append((fp - fm) / (2*h))
            layer.rz_pos[i] = orig

        fd_r_x.append(lr_x)
        fd_r_y.append(lr_y)
        fd_r_z.append(lr_z)

    print("numerical r x pos")
    for f in fd_r_x:
        print(f)
    print("numerical r y pos")
    for f in fd_r_y:
        print(f)
    print("numerical r z pos")
    for f in fd_r_z:
        print(f)

    print("analytic x neg")
    for layer in drx_neg:
        print(layer)
    print("analytic y neg")
    for layer in dry_neg:
        print(layer)
    print("analytic z neg")
    for layer in drz_neg:
        print(layer)

    fd_r_x = []
    fd_r_y = []
    fd_r_z = []

    # input first
    layer = net.particle_input
    lr_x = []
    lr_y = []
    lr_z = []
    for i in range(layer.output_size):
        # x
        orig = layer.rx_neg[i]
        layer.rx_neg[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rx_neg[i] -= 2*h
        fm = net.cost(train_X, train_Y)
        lr_x.append((fp - fm) / (2*h))
        layer.rx_neg[i] = orig

        # y
        orig = layer.ry_neg[i]
        layer.ry_neg[i] += h
        fp = net.cost(train_X, train_Y)
        layer.ry_neg[i] -= 2*h
        fm = net.cost(train_X, train_Y)
        lr_y.append((fp - fm) / (2*h))
        layer.ry_neg[i] = orig

        # z
        orig = layer.rz_neg[i]
        layer.rz_neg[i] += h
        fp = net.cost(train_X, train_Y)
        layer.rz_neg[i] -= 2*h
        fm = net.cost(train_X, train_Y)
        lr_z.append((fp - fm) / (2*h))
        layer.rz_neg[i] = orig

    fd_r_x.append(lr_x)
    fd_r_y.append(lr_y)
    fd_r_z.append(lr_z)

    # layers
    for layer in net.layers:
        lr_x = []
        lr_y = []
        lr_z = []
        for i in range(layer.output_size):
            # x
            orig = layer.rx_neg[i]
            layer.rx_neg[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rx_neg[i] -= 2*h
            fm = net.cost(train_X, train_Y)
            lr_x.append((fp - fm) / (2*h))
            layer.rx_neg[i] = orig

            # y
            orig = layer.ry_neg[i]
            layer.ry_neg[i] += h
            fp = net.cost(train_X, train_Y)
            layer.ry_neg[i] -= 2*h
            fm = net.cost(train_X, train_Y)
            lr_y.append((fp - fm) / (2*h))
            layer.ry_neg[i] = orig

            # z
            orig = layer.rz_neg[i]
            layer.rz_neg[i] += h
            fp = net.cost(train_X, train_Y)
            layer.rz_neg[i] -= 2*h
            fm = net.cost(train_X, train_Y)
            lr_z.append((fp - fm) / (2*h))
            layer.rz_neg[i] = orig

        fd_r_x.append(lr_x)
        fd_r_y.append(lr_y)
        fd_r_z.append(lr_z)

    print("numerical r x neg")
    for f in fd_r_x:
        print(f)
    print("numerical r y neg")
    for f in fd_r_y:
        print(f)
    print("numerical r z neg")
    for f in fd_r_z:
        print(f)
    Y.append(y)
Y = np.asarray(Y)

# Data subset
n_sub = 333
X_sub = X[:n_sub, :]
Y_sub = Y[:n_sub, :]

s = 4.0
cut = 3.0
max_level = 5
mac = 0.0
n = 128
n_min = 10

net = ParticleDipoleNetwork(cost="categorical_cross_entropy",
                            particle_input=ParticleDipoleInput(784, s=s))
net.append(ParticleDipole(784, n, activation="sigmoid", s=s))
net.append(ParticleDipole(n, 10, activation="softmax", s=s))

print("starting predict")
times = []
nt = 3
for _ in range(nt):
    ts = time.time()
    c = net.cost(X_sub, Y_sub)
    # c = 1.0
    # net.cost_gradient(X_sub, Y_sub)
    t = time.time() - ts
    print("Cost: {} time: {}".format(c, t))
    times.append(t)
print("Mean: " + str(sum(times) / nt))
    Y.append(y)
Y = np.asarray(Y)

# Data subset
n_sub = 333
X_sub = X[:n_sub, :]
Y_sub = Y[:n_sub, :]

s = 4.0
cut = 3.0
max_level = 5
mac = 0.0
n = 128
n_min = 10

net = ParticleDipoleNetwork(cost="categorical_cross_entropy", particle_input=ParticleDipoleInput(784, s=s))
net.append(ParticleDipole(784, n, activation="sigmoid", s=s))
net.append(ParticleDipole(n, 10, activation="softmax", s=s))

print("starting predict")
times = []
nt = 3
for _ in range(nt):
    ts = time.time()
    c = net.cost(X_sub, Y_sub)
    # c = 1.0
    # net.cost_gradient(X_sub, Y_sub)
    t = time.time() - ts
    print("Cost: {} time: {}".format(c, t))
    times.append(t)
print("Mean: " + str(sum(times) / nt))