예제 #1
0
def full_hill_climb(mtx, R=2, multithread=False):
    NodesA, NodesB = mtx.shape
    old_nodf = -100.0
    count = 0
    while (old_nodf < tb.nodf(mtx)):
        count = count + 1
        old_nodf = tb.nodf(mtx)
        if (multithread):
            mtx = hill_climb_step(mtx, R=R)
        else:
            mtx = hill_climb_step_st(mtx, R=R)
    return mtx
예제 #2
0
def hill_climb(mtx, depth=2):
    NodesA, NodesB = mtx.shape
    old_nodf = -100.0
    new_nodf = tb.nodf(mtx)
    old_mtx = np.copy(mtx)
    counter = 0
    while (new_nodf > old_nodf and counter < 5):
        counter = counter + 1
        old_mtx = np.copy(mtx)
        old_nodf = new_nodf
        for i in range(depth):
            mtx = greedy_remove_link(mtx)
        for i in range(depth):
            mtx = greedy_add_link(mtx)
        new_nodf = tb.nodf(mtx)
    return old_mtx
예제 #3
0
def hill_climb_step_st(mtx, R):
    NodesA, NodesB = mtx.shape
    oPosList = tb.get_valid_ones(mtx)
    # initialise nodf computations
    opt_mtx = np.copy(mtx)
    opt_nodf = tb.nodf(mtx)

    # List positions to test:
    posSwapList = []

    for oidx in range(len(oPosList)):
        oPos = oPosList[oidx]
        xpos, ypos = oPos
        for xshift, yshift in zip(range(-R, R + 1), range(-R, R + 1)):
            # Test if new position is valid:
            xnew = xpos + xshift
            ynew = ypos + yshift
            if (xnew >= 0 and xnew < NodesA and ynew >= 0 and ynew < NodesB):
                # Test if there is a 0
                if (mtx[xnew, ynew] == 0.0):
                    # perform the swap:
                    zPos = [xnew, ynew]
                    posSwapList.append([oPos, zPos])
    opt_mtx, opt_nodf = hill_climb_test_positions(mtx, posSwapList)
    mtx = np.copy(opt_mtx)
    return mtx
예제 #4
0
def sim_anneal_opt(mtx, alpha = 0.99, iters = 32, init_temp = 0.01, min_temp = 10**(-4)):
    """
    A Simulated annealing algorithm. The default values are copied from my
    supervisors implementation. Given these defaults the user only needs to
    supply a initial solution [mtx_0] to get a well performing simulated
    annealing algorithm.
    Other parameters:
        alpha: cooling factor
        iters: the number of iterations at each temp level
        bar: show a progress bar
    Output will be the optimal solution encountered in the whole algorithm.

    Rest is clear.
    """
    NodesA, NodesB = mtx.shape
    tot_steps = int(np.ceil(np.log(min_temp / init_temp) / np.log(alpha))) * iters
    cool_steps = int(np.ceil(np.log(min_temp / init_temp) / np.log(alpha)))

    # Initialise optimal parameters:
    opt_mtx = np.copy(mtx)
    opt_nodf = tb.nodf(opt_mtx)
    opt_cost = cost(opt_nodf)
    temp = init_temp
    for i in tqdm(range(cool_steps)):
        # Use multithreading to speed this step up.
        [new_mtx, new_cost, sums] = sim_anneal_step(mtx, temp, iters)
        # print(1.0 - opt_cost, temp, tb.nodf(mtx))
        if(new_cost < opt_cost):
            # Found a new optimal matrix.
            opt_mtx = np.copy(new_mtx)
            # Try hillclimbing to improve it!
            opt_mtx = hill_climb.full_hill_climb(opt_mtx, multithread = True)
            # Update optimal parameters
            opt_nodf = tb.nodf(opt_mtx)
            opt_cost = cost(opt_nodf)
        # Test to see if we should go back to the opt. solution
        acc_prob = tb.acceptProb(opt_cost, new_cost, temp)
        if(np.random.random() > acc_prob):
            # Go back to the optimal solution:
            mtx = np.copy(opt_mtx)
        temp = temp * alpha
    return opt_mtx
예제 #5
0
def hill_climb_test_positions(mtx, posSwapList):
    MT, F, DM, ND, S = tb.init_nodf(mtx)
    opt_mtx = np.copy(mtx)
    opt_nodf = tb.nodf(mtx)
    for oPos, zPos in posSwapList:
        n1, S = tb.nodf_one_link_removed(mtx, MT, F, DM, ND, S, oPos)
        n1, S = tb.nodf_one_link_added(mtx, MT, F, DM, ND, S, zPos)
        if (n1 > opt_nodf):
            # Update the optimal matrix and params
            opt_mtx = np.copy(mtx)
            opt_nodf = n1
        n1, S = tb.nodf_one_link_removed(mtx, MT, F, DM, ND, S, zPos)
        a, S = tb.nodf_one_link_added(mtx, MT, F, DM, ND, S, oPos)
    return opt_mtx, opt_nodf
예제 #6
0
def hill_climb_step(mtx, R):
    NodesA, NodesB = mtx.shape
    oPosList = tb.get_valid_ones(mtx)
    # initialise nodf computations
    opt_mtx = np.copy(mtx)
    opt_nodf = tb.nodf(mtx)

    # List positions to test:
    posSwapList = []

    for oidx in range(len(oPosList)):
        oPos = oPosList[oidx]
        xpos, ypos = oPos
        for xshift, yshift in zip(range(-R, R + 1), range(-R, R + 1)):
            # Test if new position is valid:
            xnew = xpos + xshift
            ynew = ypos + yshift
            if (xnew >= 0 and xnew < NodesA and ynew >= 0 and ynew < NodesB):
                # Test if there is a 0
                if (mtx[xnew, ynew] == 0.0):
                    # perform the swap:
                    zPos = [xnew, ynew]
                    posSwapList.append([oPos, zPos])
    processes = multiprocessing.cpu_count()
    posLists = [[] for i in range(processes)]
    for i in range(len(posSwapList)):
        j = i % processes
        posLists[j].append(posSwapList[i])
    argList = []
    for i in range(processes):
        argList.append([np.copy(mtx), posLists[i]])

    with Pool(processes) as pool:
        resList = pool.starmap(hill_climb_test_positions,
                               argList,
                               chunksize=32)

    for new_mtx, new_nodf in resList:
        if (new_nodf > opt_nodf):
            opt_nodf = new_nodf
            opt_mtx = np.copy(new_mtx)

    mtx = np.copy(opt_mtx)
    return mtx
예제 #7
0
def sim_anneal_step(mtx, temp, iters):
    """
    One step of the simmulated annealing algorithm.
    Supply a initial solution and nodf meta-data.
    Output will be the optimal matrix found in this process
    along with it's cost
    """
    # Do the reqired dice rolls for this algorithm vectorised and in advance:
    decision = np.random.rand(iters)
    MT, F, DM, ND, S = tb.init_nodf(mtx)

    opt_mtx = np.copy(mtx)
    opt_cost = cost(tb.nodf(mtx))
    old_cost = opt_cost
    ones = tb.get_valid_ones(mtx)
    zeros = tb.get_promising_zeros(mtx)
    opt_time = -1
    for i in range(iters):
        o_idx = np.random.randint(len(ones))
        z_idx = np.random.randint(len(zeros))

        o_pos = ones[o_idx]
        z_pos = zeros[z_idx]
        # compute the new
        new_nodf, S = neighbor_nodf(mtx, MT, F, DM, ND, S, o_pos, z_pos)
        new_cost = cost(new_nodf)
        if(new_cost < opt_cost):
            opt_cost = new_cost
            opt_mtx = np.copy(mtx)
            opt_time = i
        acc_prob = tb.acceptProb(old_cost, new_cost, temp)
        if(decision[i] <= acc_prob):
            # accept the new solution by not changing it back
            old_cost = new_cost
            # modify the zero and ones list accordingly:
            # ones = tb.get_valid_ones(mtx)
            ones = tb.get_valid_ones(mtx)
            zeros[z_idx] = o_pos
        else:
            # reject the new solution by reverting back
            a, S = neighbor_nodf(mtx, MT, F, DM, ND, S, z_pos, o_pos)
            # old_cost will not be updated!
    return [opt_mtx, opt_cost, S]
예제 #8
0
    opt_mtx, opt_nodf = hill_climb_test_positions(mtx, posSwapList)
    mtx = np.copy(opt_mtx)
    return mtx


def full_hill_climb(mtx, R=2, multithread=False):
    NodesA, NodesB = mtx.shape
    old_nodf = -100.0
    count = 0
    while (old_nodf < tb.nodf(mtx)):
        count = count + 1
        old_nodf = tb.nodf(mtx)
        if (multithread):
            mtx = hill_climb_step(mtx, R=R)
        else:
            mtx = hill_climb_step_st(mtx, R=R)
    return mtx


if (__name__ == "__main__"):
    NodesA = 14
    NodesB = 13
    Edges = 52
    mtx1 = greedySolver2.greedySolve(NodesA, NodesB, Edges)
    mtx2 = np.copy(mtx1)
    mtx1 = full_hill_climb(mtx1, R=2)
    mtx2 = full_hill_climb(mtx2, R=3)
    nodf1 = tb.nodf(mtx1)
    nodf2 = tb.nodf(mtx2)
    print("NODF: {} -> {}".format(nodf1, nodf2))
예제 #9
0
    for i in tqdm(range(cool_steps)):
        # Use multithreading to speed this step up.
        [new_mtx, new_cost, sums] = sim_anneal_step(mtx, temp, iters)
        # print(1.0 - opt_cost, temp, tb.nodf(mtx))
        if(new_cost < opt_cost):
            # Found a new optimal matrix.
            opt_mtx = np.copy(new_mtx)
            # Try hillclimbing to improve it!
            opt_mtx = hill_climb.full_hill_climb(opt_mtx, multithread = True)
            # Update optimal parameters
            opt_nodf = tb.nodf(opt_mtx)
            opt_cost = cost(opt_nodf)
        # Test to see if we should go back to the opt. solution
        acc_prob = tb.acceptProb(opt_cost, new_cost, temp)
        if(np.random.random() > acc_prob):
            # Go back to the optimal solution:
            mtx = np.copy(opt_mtx)
        temp = temp * alpha
    return opt_mtx

if(__name__ == "__main__"):
    NodesA = 131
    NodesB = 666
    Edges = 2933
    mtx = greedySolver2.greedySolve(NodesA, NodesB, Edges)
    mtx = hill_climb.full_hill_climb(mtx)
    mtx = sim_anneal_opt(mtx)
    nodf = tb.nodf(mtx)
    print(mtx.sum())
    print("NODF = {}".format(nodf))
예제 #10
0
import pandas as pd
import algSimulatedAnneal
import toolbox as tb

# This script solves optimises the NODF-metric for the
# network considered in the paper. This purpose of this
# script is to make the algorithm accessible and reproduction
# of our results easy.

if __name__ == "__main__":
    # Read in the list of the network configurations considered.
    fname = "webs.csv"
    DF = pd.read_csv(fname)
    for index, row in DF.iterrows():
        rows = row['rows']
        cols = row['columns']
        links = row['links']
        NODFsong = row['song']
        print("Simulating web with config ({}, {}, {}).".format(
            rows, cols, links))
        mtx = algSimulatedAnneal.optimise(rows, cols, links)
        NODFSa = tb.nodf(mtx)
        print("NODF_song = {}\t, NODF_SA = {}.".format(NODFsong, NODFSa))
예제 #11
0
import greedySolver2
import simulatedAnnealing
import time
import toolbox

# This script contains an algorithm to optimise the NODF
# metric by computing a first estimate for the marginal totals
# and then applying a greedy hill climb approach to improve
# the result.


def optimise(NodesA, NodesB, Edges, verbose=True):
    mtx = greedySolver2.greedySolve(NodesA, NodesB, Edges)
    bestMTX = simulatedAnnealing.sim_anneal_opt(mtx)
    return bestMTX


if __name__ == "__main__":
    NodesA = 131
    NodesB = 666
    Edges = 2933
    t1 = time.time()
    mtx = optimise(NodesA, NodesB, Edges)
    t2 = time.time()
    print(toolbox.nodf(mtx), t2 - t1)
예제 #12
0
def greedy_gen(params):
    """
    Generate an initial graph with the intention of using it later in a
    more sophisticated algorithm like simmulated annealing or a genetic
    algorithm. This function is not deterministic. It rather tests all
    potential positions for a new entry and then adds one using the nodf
    values to create a probability distribution.
    """
    NodesA, NodesB, Edges = params
    #Initial fill
    mtx = np.zeros((NodesA, NodesB))
    mtx[0,:] = 1.0
    mtx[:,0] = 1.0
    mtx[1,1] = 1.0
    edges_left = Edges - NodesA - NodesB

    MT, F, DM, ND, S = tb.init_nodf(mtx)
    for i in range(edges_left):
        #greedy add a link
        greedyAddLink(mtx, MT, F, DM, ND, S, edges_left - i)
    return mtx

if(__name__ == "__main__"):
    NodesA = 131
    NodesB = 666
    Edges = 2933
    mtx = greedySolve(NodesA, NodesB, Edges, verbose = False)
    print(mtx.sum())
    print(tb.nodf(mtx))