Пример #1
0
def climb_ineff(seq, N, phase_unity=2, num_elem=None, min_fn=second_max):
    """
        Function to find the optimal neighbor in the one-neighborhood of a polyphase code with values drawn from
        the roots of unity of order phase_unity.

        :param seq: the code we are considering
        :type seq: array
        :param N: the doppler width
        :type N: int
        :param phase_unity: the order of the roots of unity
        :type phase_unity: int
        :param num_elem: number of elements that min_fn is being evaluated on
        :type num_elem: int
        :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
        :type min_fn: function
        :return: an updated sequence, the optimal neighbor of seq
    """
    val = fast_autocorrelations(seq, N, num_elem,
                                min_fn)  # initial peak sidelobe value
    current_seq = np.copy(seq)
    min_val = val
    for i in range(len(seq)):  # loop over all possible coordinates to change
        for j in range(1,
                       phase_unity):  # loop over all possible phase additions
            seq_new = np.copy(seq)
            phase_add = random.randint(1, phase_unity - 1)
            seq_new[i] = seq_new[i] * cmath.exp(
                phase_add * 2 * cmath.pi * imag / phase_unity)
            new_val = fast_autocorrelations(seq_new, N, num_elem, min_fn)
            if new_val < min_val:
                min_val = new_val
                current_seq = seq_new

    return current_seq
Пример #2
0
def conv_ineff(left, right, N, length):
    """
    Inefficient convolution of the finished code from our peak sidelobe exhaustive algorithm, which outputs the peak sidelobe
    of the finished code.  If the length of the code in question is odd, it tries both possible values for the middle entry.

    :param left: left-hand side of the code built up
    :type left: array
    :param right: right-hand side of the code
    :type right: array
    :param N: doppler width
    :type N: int
    :param length: length of the codes we are considering
    :type length: int
    :return: peak sidelobe of possible codes from combining left and right
    """
    if length % 2 == 0:  # if it is even, the only possible code is exactly their concatenation
        l_copy = left[:]
        r_copy = right[:]
        r_copy.reverse()
        signal = l_copy + r_copy
        convert(signal)  # convert to -1, 1 form
        return fast_autocorrelations(signal, N)
    else:  # otherwise, the middle entry can be 1 or -1; we check both
        l_copy = left[:]
        l_copy.append(1)
        r_copy = right[:]
        r_copy.reverse()
        signal = l_copy + r_copy
        convert(signal)
        corr_1 = fast_autocorrelations(signal, N)
        l_copy[-1] = 0
        signal = l_copy + r_copy
        convert(signal)
        corr_2 = fast_autocorrelations(signal, N)
        return min(corr_1, corr_2)
Пример #3
0
def select_parent(population,
                  N=1,
                  phase_unity=2,
                  num_elem=None,
                  min_fn=second_max):
    """
    A tournament selection algorithm to select a parent, with k = 2.

    :param population: array of the codes at our current generation
    :type population: array
    :param N: doppler width of our ambiguity function
    :type N: int
    :param phase_unity: order of the roots of unity filling our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: a parent for the next generation with the maximal fitness among 2 randomly selected

    """
    poss_parents = random.sample(range(0, len(population)), 2)
    if fast_autocorrelations(population[poss_parents[0]], N, num_elem,
                             min_fn) < fast_autocorrelations(
                                 population[poss_parents[1]], N, num_elem,
                                 min_fn):
        return poss_parents[0]
    else:
        return poss_parents[1]
Пример #4
0
def ts_local_search(child,
                    N=1,
                    phase_unity=2,
                    num_elem=None,
                    min_fn=second_max):
    """
    Tabu local search algorithm; works by climbing to better neighbors, excluding those with coordinates in a tabu,
    for a number of iterations.

    :param child: initial sequence
    :type child: array
    :param N: doppler width of our ambiguity function
    :type N: int
    :param phase_unity: order of the roots of unity filling our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: locally near-optimal value near child, found by tabu local search, which allows for more potential movement
    away from a very close local minima
    """
    length = len(child)
    tabu_table = [0 for x in range(length)
                  ]  # table to store most recently changed coordinates
    max_iters = 20
    min_iters = 2
    extra_iters = 1
    best_code = np.copy(child)
    best_val = fast_autocorrelations(child, N, num_elem, min_fn)
    for k in range(max_iters):
        code_this_gen = np.array([])
        val_this_gen = float('inf')
        which_i = None
        for i in range(length):  # loop over all coordinates
            for j in range(
                    1, phase_unity):  # loop over all possible phase additions
                seq_new = np.copy(child)
                if phase_unity == 2:
                    seq_new[i] = -seq_new[i]
                else:
                    seq_new[i] = seq_new[i] * cmath.exp(
                        j * 2 * cmath.pi * imag / phase_unity)
                val = fast_autocorrelations(seq_new, N, num_elem, min_fn)
                if k >= tabu_table[
                        i] or val < best_val:  # if it improves on the best value or it hasn't beeen changed in a while, move to that code.
                    if val < val_this_gen:
                        val_this_gen = val
                        code_this_gen = seq_new
                        which_i = i
        if code_this_gen.any():
            child = np.copy(code_this_gen)
        if which_i != None:  # increase value of tabu_table for the coordinate that changed
            tabu_table[which_i] = k + min_iters + random.randint(
                0, extra_iters)
        if val_this_gen < best_val:
            best_code = np.copy(code_this_gen)
            best_val = val_this_gen
    return best_code
Пример #5
0
def threshold(code, N, threshold_schedule, phase_unity, num_elem = None, min_fn = second_max):
    """
    The core threshold accepting algorithm, which starts with a random polyphase code (with generating phase described
    by phase_unity) and moves around with gen_neighbor, excluding movements for which the new code has a min_fn value
    that exceeds our threshold.

    :param code: the code in question
    :type code: array
    :param N: doppler width of our ambiguity function
    :type N: int
    :param threshold_schedule: function that describes how the threshold decreases from generation to generation
    :type threshold_schedule: function
    :param phase_unity: order of the roots of unity that fill the codes we will be considering
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: a tuple of the best code and its corresponding min_fn value found by the algorithm, given the particular starting point
    """
    psl = fast_autocorrelations(code, N, num_elem, min_fn)  # initial peak sidelobe value
    curr_threshold = int(psl * 2)
    count = 0
    kappa = 0.2
    num_gen = 30 * len(code)  # number of generations at a fixed threshold
    best_found = code
    best_psl = psl
    stuck_count = 0  # number of iterations we have been stuck at a given code
    upp_bound = 2 * len(code) ** (kappa)
    num_threshold_gens = int(len(code)/2)
    for j in range(1, num_threshold_gens):
        for i in range(num_gen):  # loop at a given threshold of movements from neighbor to neighbor
            new_code = gen_neighbor(code, phase_unity, 3)  # new_code found by gen_neighbor

            new_psl = fast_autocorrelations(new_code, N, num_elem, min_fn)
            if new_psl < curr_threshold:  # move to the new code if it is better than the threshold
                code = new_code
                stuck_count = 0
                if new_psl < best_psl:  # update best_psl as well if it is improved on
                    best_found = new_code
                    best_psl = new_psl
            else:
                stuck_count+=1

            if stuck_count > upp_bound:  # if we have been stuck for a while, break out of the loop
                count += 1
                break
        if stuck_count > upp_bound:
            break


        curr_threshold = semiexponential(curr_threshold, j) # update the threshold

    return (best_found, best_psl)
Пример #6
0
def k_select_parent(population,
                    k,
                    N=1,
                    phase_unity=2,
                    num_elem=None,
                    min_fn=second_max):
    """
        A tournament selection algorithm to select a parent, for arbitrary k.

        :param population: array of the codes at our current generation
        :type population: array
        :param k: number of members from which we are choosing
        :type k: int
        :param N: doppler width of our ambiguity function
        :type N: int
        :param phase_unity: order of the roots of unity filling our codes
        :type phase_unity: int
        :param num_elem: number of elements that min_fn is being evaluated on
        :type num_elem: int
        :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
        :type min_fn: function
        :return: a parent for the next generation with the maximal fitness among k randomly selected

    """
    poss_parents = random.sample(range(0, len(population)), k)
    poss_parents_psl = np.array([
        fast_autocorrelations(population[x], N, num_elem, min_fn)
        for x in poss_parents
    ])
    max_index = np.argmax(
        poss_parents_psl)  # utility of using np; argmax exists
    return poss_parents[max_index]
Пример #7
0
def select_parents_random(population,
                          retain,
                          random_select,
                          k,
                          N=1,
                          phase_unity=2,
                          num_elem=None,
                          min_fn=second_max):
    """
    Function to select the parents from the population, via a random weighted selection algorithm.

    :param population: list of arrays comprising our population
    :type population: array
    :param retain: fraction of population that we retain as is, unchanged
    :type retain: float
    :param random_select: unused variable
    :param k: unused variable
    :param N: doppler width
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: the population after the selection process, in the array parents
    """
    # sort the population by min_fn value
    psl_values = [(fast_autocorrelations(x, N, num_elem, min_fn), x)
                  for x in population]
    pop_length = len(psl_values)
    psl_values.sort(key=lambda x: x[0])
    seqs_sorted_by_psl = [x[1] for x in psl_values]
    num_retain = int(pop_length * retain)  # number of parents we will retain
    min_fitness = psl_values[0][0]
    parents = []
    count = 0
    while count < num_retain:  # until we reach num_retain members, select a random parent, add it with probability weighted by its min_fn value.
        select_point = random.randint(0, pop_length - 1)
        poss_parent = seqs_sorted_by_psl[select_point]
        prob = min_fitness / fast_autocorrelations(poss_parent, N, num_elem,
                                                   min_fn)
        if prob > random.random():
            count += 1
            parents.append(poss_parent)

    return parents
Пример #8
0
def hill_climbing_iterative(length,
                            climb_type,
                            N=1,
                            phase_unity=2,
                            min_fn=second_max,
                            num_elem=None):
    """
    An iterative, partial-restart based hill climbing algorithm with flexibility for doppler width, polyphase codes,
    and different types of local search.  This algorithm is used to find codes with the minimal min_fn value.

    :param length: length of the codes in question
    :type length: int
    :param climb_type: local search function
    :type climb_type: function
    :param N: doppler width of the ambiguity function
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: the minimal min_fn value found by our algorithm, as well as the corresponding polyphase code
    """
    f = open(
        'best_known_sidelobes.txt')  # best known sidelobes from the literature
    lines = f.readlines()
    best_code = None
    best_val = float('inf')
    cutoff_val = int(lines[length - 1])  # lowest possible value we can find
    time_limit = 0
    if length <= 30:
        time_limit = 300
    else:
        time_limit = (300 + 60 * (length - 30))
    start_time = time.time()
    while time.time() - start_time < time_limit and (best_val != cutoff_val
                                                     or phase_unity != 2):
        x = gen_rand_seq(
            length, phase_unity
        )  # generate a random sequence of the given specifications
        x = climb_type(
            x, N, phase_unity, num_elem,
            min_fn)  # climb using the given climb_type to a nearby minima
        val = fast_autocorrelations(
            x, N, num_elem, min_fn
        )  # compute the psl/min_fn value, update if it improves on the previous best
        if val < best_val:
            best_code = x
            best_val = val

    return (best_code, best_val)
Пример #9
0
def stochastic_climb(seq, N, phase_unity, num_elem=None, min_fn=second_max):
    """
    An algorithm to climb to a neighbor of a given sequence, where each step taken is always taken if it improves the
    min_fn value, and sometimes taken if it does not, probabilistically according to the difference between the new and old min_fn values.

    :param seq: initial sequence
    :type seq: array
    :param N: doppler width of the ambiguity function over which we are minimizing
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: a neighbor of seq
    """
    val = fast_autocorrelations(seq, N, num_elem, min_fn)
    current_seq = np.copy(seq)
    min_val = val
    for i in range(len(seq)):  # loop over all possible coordinates to change
        for j in range(1,
                       phase_unity):  # loop over all possible phase additions
            seq_new = np.copy(seq)
            phase_add = random.randint(1, phase_unity - 1)
            seq_new[i] = seq_new[i] * cmath.exp(
                phase_add * 2 * cmath.pi * imag /
                phase_unity)  # multiply by the given root of unity
            new_val = fast_autocorrelations(seq_new, N, num_elem, min_fn)
            if new_val < min_val:
                min_val = new_val
                current_seq = seq_new
                return current_seq
            else:
                if prob_of_acceptance(min_val, new_val) > random.random(
                ):  # if the min_fn value is worse, move with some probability.
                    current_seq = seq_new
                    return current_seq
    return current_seq
Пример #10
0
def simple_climb(seq, N, phase_unity, num_elem=None, min_fn=second_max):
    """
    An algorithm to climb to a local minima from a given sequence, where each step taken is the first that improves
    the current sequence.

    :param seq: initial sequence
    :type seq: array
    :param N: doppler width of the ambiguity function over which we are minimizing
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: a code neighboring seq with an improved min_fn value, as well as whether climbing there required at least one change to seq.
    """
    val = fast_autocorrelations(seq, N, num_elem, min_fn)
    current_seq = np.copy(seq)
    min_val = val
    changed = False
    for i in range(len(seq)):  # loop over all possible coordinates to change
        for j in range(1,
                       phase_unity):  # loop over all possible phase additions
            seq_new = np.copy(seq)
            seq_new[i] = seq_new[i] * cmath.exp(
                j * 2 * cmath.pi * imag /
                phase_unity)  # multiply by the given root of unity
            new_val = fast_autocorrelations(seq_new, N, num_elem, min_fn)
            if new_val < min_val:
                min_val = new_val
                current_seq = seq_new
                changed = True
                return current_seq, changed

    return current_seq, changed
Пример #11
0
def stochastic_local_search(
    seq,
    N,
    phase_unity,
    num_elem=None,
    min_fn=second_max
):  # note: for future, would be good to collapse this with sdls, simple into one algor
    """
    Handler for our simple climb algorithm, where we keep climbing for a fixed number of steps using stochastic_climb.

    :param seq: initial sequence
    :type seq: array
    :param N: doppler width over which we are minimizing
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: probabilistically, a locally minimal code near seq
    """
    max_iters = 40  # perhaps fewer iterations, if want to optimize
    to_return = seq
    best_found = seq
    best_val = fast_autocorrelations(seq, N, num_elem, min_fn)
    for i in range(max_iters):
        to_return = stochastic_climb(to_return, N, phase_unity, num_elem,
                                     min_fn)  # climb stochastically to a code
        new_val = fast_autocorrelations(
            to_return, N, num_elem, min_fn
        )  # compute min_fn value of the ambiguity function, update best_val if it improves.
        if new_val < best_val:
            best_val = new_val
            best_found = np.copy(to_return)
    return best_found
Пример #12
0
def select_parents_part_random(population,
                               retain,
                               random_select,
                               k,
                               N=1,
                               phase_unity=2,
                               num_elem=None,
                               min_fn=second_max):
    """
    Function to select the parents from the population, via a partially random, partially fitness-based selection process.

    :param population: list of arrays comprising our population
    :type population: array
    :param retain: fraction of population that we retain as is, unchanged
    :type retain: float
    :param random_select: probability for selecting additional parents, in addition to the most fit
    :type random_select: float
    :param k: unused variable
    :param N: doppler width
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: the population after the selection process, in the array parents
    """
    # sort the population by min_fn value
    psl_values = [(fast_autocorrelations(x, N, num_elem, min_fn), x)
                  for x in population]
    psl_values.sort(key=lambda x: x[0])
    seqs_sorted_by_psl = [x[1] for x in psl_values]
    num_retain = int(len(seqs_sorted_by_psl) * retain)
    parents = seqs_sorted_by_psl[:
                                 num_retain]  # add the num_retain most fit parents

    for individual in seqs_sorted_by_psl[
            num_retain:]:  # add a randomly selected additional set of parents from the population
        if random_select > random.random():
            parents.append(individual)

    return parents
Пример #13
0
def fast_autocorrelations_phases(code, N=1, num_elem=None, min_fn=second_max):
    """
    Calculate the min_fn value of the corresponding polyphase code to a given phase sequence.
    :param code: The phase sequence in question
    :type code: array
    :param N: doppler width of our ambiguity function
    :type N: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: the min_fn value of the polyphase code corresponding to the phase sequence.
    """
    act_code = []
    for i in range(
            len(code)
    ):  # construct the actual polyphase code corresponding to a sequence of phases
        act_code.append(cmath.exp(code[i] * imag))
    return fast_autocorrelations(act_code, N, num_elem, min_fn)
Пример #14
0
def climb(seq, num_elem=None, min_fn=second_max):
    """
    Efficient algorithm to climb, in nondoppler, binary space, to the highest neighbor.  IMPORTANT NOTE: is outdated,
    does not work with updated fast_autocorrelation because the list there was cut in half.  To fix, need to change inner
    if statements below in some way.

    :param seq: initial code in question
    :type seq: array
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: the best neighbor, and the corresponding min_fn value
    """
    lst = fast_autocorrelations(seq, 1)
    current_seq = seq
    min_val = min_fn(lst, num_elem)
    for i in range(len(seq)):  # looping over all possible coordinates
        lst_new = list(lst)
        length = len(lst_new)
        for j in range(length):  # looping over length of autocorrelation
            if j < ((length - 1) / 2):
                if j >= i:
                    lst_new[j] = lst_new[j] - 2 * seq[i] * seq[
                        len(seq) - 1 - j +
                        i]  # exploiting singled changed element on left side
                if i + j + 1 >= len(seq):
                    lst_new[j] -= 2 * seq[i] * seq[i + j + 1 - len(
                        seq)]  # exploiting single changed element on right side
            elif j > ((length - 1) / 2) and j >= i:
                lst_new[j] = lst_new[length - 1 - j]
        min_pos_new = min_fn(lst_new, num_elem)
        if min_pos_new < min_val:
            min_val = min_pos_new
            seq_2 = np.copy(seq)
            seq_2[i] = -seq_2[i]
            current_seq = seq_2

    return (current_seq, min_val)
Пример #15
0
def peak_sidelobes_general(length,
                           num_elem,
                           N,
                           gen_codes,
                           op_on_list=second_max,
                           optional_limit=0):
    """

    :param length: length of the code in question
    :type length: int
    :param num_elem: number of elements that concern our minimization function
    :type num_elem: int
    :param N: doppler window for which we are calculating the ambiguity function
    :type N: int
    :param op_on_list: the operation we are minimizing of the ambiguity function
    :type op_on_list: fn
    :param gen_codes: function to generate the codes we are considering
    :type gen_codes: fn
    :param optional_limit: optimal limit for gen_codes, if we are dealing with arbitrary amplitude codes
    :type optional_limit: int
    :return: the code with the minimum value of op_on_list on its ambiguity function, as well as that value.

    See also: peak_sidelobes_single, peak_sidelobes_efficient
    """
    all_codes = gen_codes(
        length,
        optional_limit)  # generate the codes over which we are minimizing
    min_sl = float('inf')
    min_code = None
    for code in all_codes:
        sidelobe_size = fast_autocorrelations(
            code, N, num_elem,
            op_on_list)  # calculate the value of op_on_list on this code
        if sidelobe_size < min_sl:  # update the value if it improves
            min_sl = sidelobe_size
            min_code = code
        if min_sl == 1:  # break if we reach 1, since we can never do better.
            break
    return (min_code, min_sl)
Пример #16
0
def peak_sidelobes_single(length):
    """
    A function to return the binary code of a given length with the minimum peak sidelobe

    :param length: length of the binary codes in question
    :type length: int
    :return: the binary code with minimal peak sidelobe, as well as that peak sidelobe value.

    See also: peak_sidelobes_general, peak_sidelobes_efficient
    """
    codes = gen_binary_codes(length, None)

    min_sl = float("inf")
    min_code = None
    for code in codes:
        val = fast_autocorrelations(code, 1)  # calculate the peak sidelobe
        if val < min_sl:  # update if it improves on the previous value
            min_sl = val
            min_code = code
        if min_sl == 1:
            break

    return (min_code, min_sl)
Пример #17
0
def run_evolution_memet(length,
                        local_search_type,
                        amount_retain=0.1,
                        N=1,
                        phase_unity=2,
                        num_elem=None,
                        min_fn=second_max):
    """
    Handler for memetic algorithm.

    :param length: length of the codes in question
    :type length: int
    :param local_search_type: the local search function we are using to minimize min_fn
    :type local_search_type: function
    :param amount_retain: the fraction of parents we will retain for the next generation (give or take mutations)
    :type amount_retain: float
    :param N: doppler width of our ambiguity function
    :type N: int
    :param phase_unity: order of the roots of unity filling our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: (best_code, best_val): a tuple of a complex value and numpy array representing the minimum min_fn code found
    """
    f = open(
        'best_known_sidelobes.txt')  # best known sidelobes in the literature
    lines = f.readlines()
    best_code = None
    best_val = float('inf')
    cutoff_val = int(lines[length -
                           1])  # best min_fun value our algorithm could find
    time_limit = 0
    if length <= 30:
        time_limit = 1.5 * 300
    else:
        time_limit = 1.5 * (300 + 60 * (length - 30))
    start_time = time.time()
    while time.time() - start_time < time_limit:
        partial_restart = 8
        num_members = 100  # number of members of our population
        num_generations = 30
        mut_prob = 1. / length
        offspring = 500  # number of offspring created at each step
        pop = gen_population(num_members, length,
                             phase_unity)  # initial population
        amount_restart = int(1.5 * length)
        for i in range(num_generations):
            if i % partial_restart == 0:
                pop_add = gen_population(amount_restart, length, phase_unity)
                pop = np.concatenate((pop, pop_add), 0)
            pop = evolve_mem(
                pop, mut_prob, offspring, local_search_type, amount_retain, N,
                phase_unity, num_elem, min_fn
            )  # main step in the code: evolving the population, giving an offspring size, initial pop, and mutation probability
            psl_values = [(fast_autocorrelations(x, N, num_elem, min_fn), x)
                          for x in pop]
            psl_values.sort(key=lambda x: x[0])
            seqs_sorted_by_psl = [x[1] for x in psl_values]
            new_val = fast_autocorrelations(seqs_sorted_by_psl[0], N, num_elem,
                                            min_fn)
            if new_val < best_val:  # keep track of the best value found so far, in case we lose it.
                best_code = seqs_sorted_by_psl[0]
                best_val = new_val
            #print((seqs_sorted_by_psl[0], con_psl(seqs_sorted_by_psl[0])))
            if best_val == cutoff_val and phase_unity == 2:
                break
        if best_val == cutoff_val and phase_unity == 2:
            break
    print(time.time() - start_time)
    return (best_code, best_val)
Пример #18
0
def anneal_with_local_search(code,
                             N,
                             cooling_schedule,
                             phase_unity,
                             num_elem=None,
                             min_fn=second_max):
    """
    Our fundamental annealing algorithm, which starts with a random polyphase code (phase described by phase_unity)
    and moves around, returning the best code and min_fn value found.  The premise of the algorithm is simple:
    start with a high temperature, which allows more movement around the current code, even if it increases the min_fn value
    and over time lower the temperature.  This is one altered version of the algorithm, where we perform a local search
    after generating each new code.

    Note: I do not provide additional comments here, outside of comments on the new sections of the code.

    :param code: the initial code
    :type code: array
    :param N: doppler width of our ambiguity function
    :type N: int
    :param cooling_schedule: function that describes how the temperature decreases from generation to generation
    :type cooling_schedule: function
    :param phase_unity: order of the roots of unity that fill the codes we will be considering
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: a tuple of the best code and its corresponding min_fn value found by the algorithm

    See also: anneal, anneal_with_tabu
    """
    init_temperature = 2 * len(code)
    temperature = init_temperature
    psl = fast_autocorrelations(code, N, num_elem)
    count = 0
    kappa = 0.2
    num_gen = 3 * len(code)
    best_found = code
    best_psl = psl
    stuck_count = 0
    num_temp_gens = int(1.2 * init_temperature)
    upp_bound = 2 * len(code)**(kappa)
    for j in range(1, num_temp_gens):
        for i in range(num_gen):
            new_code = gen_neighbor(code, phase_unity, min(len(code) - 1, 3))
            new_code = simple_local_search(
                new_code, N, phase_unity, num_elem, min_fn
            )  # locally search and find the best point around this new neighbor

            new_psl = fast_autocorrelations(new_code, N, num_elem, min_fn)

            if new_psl < psl:
                code = new_code
                stuck_count = 0
                if new_psl < best_psl:
                    best_found = new_code
                    best_psl = new_psl
            else:
                if acceptance_probability(psl, new_psl,
                                          temperature) > random.random():
                    code = new_code
                    stuck_count = 0
                else:
                    stuck_count += 1
            if stuck_count > upp_bound:
                count += 1
                break
        if stuck_count > upp_bound:
            break
        temperature = cooling_schedule(temperature, init_temperature, 0.25,
                                       num_temp_gens, j)

    return (best_found, best_psl)
Пример #19
0
def evolut_algo(length, N=1, phase_unity=2, num_elem=None, min_fn=second_max):
    """
    Our most in-depth evolutionary algorithm, incorporating a neighborhood selection step, a climb step, but no crossover.

    :param length: length of the codes we are considering
    :type length: int
    :param N: doppler width
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: the best code found, along with its minimum min_fn value.
    """
    f = open('best_known_sidelobes.txt'
             )  # file with the best known sidelobes in the literature
    lines = f.readlines()
    best_code = None
    best_val = float('inf')
    cutoff_val = int(lines[length -
                           1])  # best possible value our algorithm can find
    time_limit = 0
    if length <= 30:
        time_limit = 2 * 300
    else:
        time_limit = 2 * (300 + 60 * (length - 30))
    start_time = time.time()
    while time.time() - start_time < time_limit and (best_val != cutoff_val
                                                     or N != 1
                                                     or phase_unity != 2):
        time_to_restart = 8
        num_generations = 25
        num_members = 100
        num_offspring = 500
        pop = gen_population(num_members, length, phase_unity)
        restart_members = 15
        for i in range(len(pop)):
            pop[i] = climb_ineff(
                pop[i], N, phase_unity, num_elem,
                min_fn)  # first move the population to its best state
        for i in range(1, num_generations + 1):
            # partial restart step; for the sake of introducing variance, we add some new members to the population every partial_restart steps
            if i % time_to_restart == 0:
                pop_add = gen_population(restart_members, length, phase_unity)
                for i in range(len(pop_add)):
                    pop_add[i] = climb_ineff(pop_add[i], N, phase_unity,
                                             num_elem, min_fn)
                pop = np.concatenate((pop, pop_add), 0)
            children = []
            while len(
                    children
            ) < num_offspring:  # mutate and climb until we have sufficient offspring
                father = random.randint(0, len(pop) - 1)
                father_mut = gen_neighbor(pop[father], phase_unity,
                                          min(length - 1, 3))
                father_mut_best = climb_ineff(father_mut, N, phase_unity,
                                              num_elem, min_fn)
                children.append(father_mut_best)
            pop = np.concatenate((pop, children), axis=0)
            psl_values = [(fast_autocorrelations(x, N, num_elem, min_fn), x)
                          for x in pop]
            psl_values.sort(key=lambda x: x[
                0])  # can make more efficient, only need top num_members
            seqs_sorted_by_psl = [x[1] for x in psl_values]
            if psl_values[0][0] < best_val:
                best_val = psl_values[0][0]
                best_code = psl_values[0][1]
            pop = seqs_sorted_by_psl[:num_members]

            if best_val == cutoff_val:
                break

    return (best_code, best_val)
Пример #20
0
def evolut_algo_naive(length,
                      N=1,
                      phase_unity=2,
                      num_elem=None,
                      min_fn=second_max):
    """
    Our basic evolutionary algorithm, incorporating only crossover and fitness-based selection.

    :param length: length of the codes we are considering
    :type length: int
    :param N: doppler width
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: the best code found, along with its minimum min_fn value.
    """
    f = open('best_known_sidelobes.txt'
             )  # file with the best known sidelobes in the literature
    lines = f.readlines()
    best_code = None
    best_val = float('inf')
    cutoff_val = int(lines[length -
                           1])  # best possible value our algorithm can find
    time_limit = 0
    if length <= 30:
        time_limit = 2 * 300
    else:
        time_limit = 2 * (300 + 60 * (length - 30))
    start_time = time.time()
    while time.time() - start_time < time_limit and (best_val != cutoff_val
                                                     or N != 1
                                                     or phase_unity != 2):
        num_members = 100
        num_generations = 25
        num_offspring = 500
        pop = gen_population(num_members, length, phase_unity)
        for i in range(num_generations):
            children = []
            while len(children) < num_offspring:
                father = random.randint(0, len(pop) - 1)
                mother = random.randint(0, len(pop) - 1)
                if father != mother:
                    father = pop[father]
                    mother = pop[mother]
                    splice_point = random.randint(
                        0,
                        len(father) - 1)  # analogue of crossover_randpoint.
                    child = np.append(father[:splice_point],
                                      mother[splice_point:])
                    children.append(child)
            pop = np.concatenate((pop, children), axis=0)
            psl_values = [(fast_autocorrelations(x, N, num_elem, min_fn), x)
                          for x in pop]
            psl_values.sort(key=lambda x: x[0])
            seqs_sorted_by_psl = [x[1] for x in psl_values]
            if psl_values[0][0] < best_val:
                best_val = psl_values[0][0]
                best_code = psl_values[0][1]
            pop = seqs_sorted_by_psl[:num_members]

            if best_val == cutoff_val:
                break
    return (best_code, best_val)
Пример #21
0
def anneal_with_tabu(code,
                     N,
                     cooling_schedule,
                     phase_unity,
                     num_elem=None,
                     min_fn=second_max):
    """
    Our fundamental annealing algorithm, which starts with a random polyphase code (phase described by phase_unity)
    and moves around, returning the best code and min_fn value found.  The premise of the algorithm is simple:
    start with a high temperature, which allows more movement around the current code, even if it increases the min_fn value
    and over time lower the temperature.  This is one altered version of the algorithm, where we store recent codes
    in a tabu and do not proceed with any normal steps when a considered code is in this tabu.

    Note: I do not provide additional comments here, outside of comments on the new sections of the code.

    :param code: the initial code
    :type code: array
    :param N: doppler width of our ambiguity function
    :type N: int
    :param cooling_schedule: function that describes how the temperature decreases from generation to generation
    :type cooling_schedule: function
    :param phase_unity: order of the roots of unity that fill the codes we will be considering
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: a tuple of the best code and its corresponding min_fn value found by the algorithm

    See also: anneal, anneal_with_local_search
    """
    tabu = deque()  # initialize an empty queue
    init_temperature = 2 * len(code)
    temperature = init_temperature
    psl = fast_autocorrelations(code, N, num_elem, min_fn)
    count = 0
    kappa = 0.2
    num_gen = 25 * len(code)
    best_found = code
    best_psl = psl
    stuck_count = 0
    num_temp_gens = int(2.9 * init_temperature)
    upp_bound = 2 * len(code)**(kappa)
    for j in range(1, num_temp_gens):
        for i in range(num_gen):
            new_code = gen_neighbor(code, phase_unity, min(len(code) - 1, 3))
            new_psl = fast_autocorrelations(new_code, N, num_elem, min_fn)
            list_v = new_code.tolist()
            if list_v not in tabu:  # if the new_code is not in the tabu, we can proceed in the normal steps
                if new_psl < psl:
                    code = new_code
                    stuck_count = 0
                    if new_psl < best_psl:
                        best_found = new_code
                        best_psl = new_psl
                else:
                    if acceptance_probability(psl, new_psl,
                                              temperature) > random.random():
                        code = new_code
                        stuck_count = 0
                    else:
                        stuck_count += 1
                tabu.append(
                    list_v
                )  # we add this code to the tabu so we do not consider it again
                if len(
                        tabu
                ) == 101:  # if the tabu is full, we pop off the front of the queue
                    tabu.popleft()
            if stuck_count > upp_bound:
                count += 1
                break
        if stuck_count > upp_bound:
            break

        temperature = cooling_schedule(temperature, init_temperature, 0.25,
                                       num_temp_gens, j)
    return (best_found, best_psl)
Пример #22
0
def run_evolution(length,
                  num_members=100,
                  num_gens=25,
                  parent_func=select_parents_part_random,
                  mut_func=mutate_all,
                  crossover_func=crossover_random,
                  retain=0.2,
                  random_select=0.05,
                  mutate=0.02,
                  k=2,
                  N=1,
                  phase_unity=2,
                  num_elem=None,
                  min_fn=second_max):
    """
    Handler for running evolution; for a fixed number of number of generations, generates a population, evolves it, and returns the best code found.

    :param length: length of the codes in question
    :type length: int
    :param num_members: number of members to put in the population
    :type num_members: int
    :param parent_func: function to select the parents of the next generation
    :type parent_func: function
    :param mut_func: function to mutate the members of a population
    :type mut_func: function
    :param crossover_func: function to crossover two parents to create a child
    :type crossover_func: function
    :param retain: fraction of parents to retain
    :type retain: float
    :param random_select: fraction of parents to randomly select, if necessary
    :type random_select: float
    :param mutate: fraction of population to mutate
    :type mutate: float
    :param k: number of members for tournament selection
    :type k: int
    :param N: doppler width
    :type N: int
    :param phase_unity: order of the roots of unity in our codes
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: (best_code, best_val): a tuple of a complex value and numpy array representing the minimum min_fn code found
    """

    f = open(
        'best_known_sidelobes.txt')  # best known sidelobes in the literature
    lines = f.readlines()
    best_code = None
    best_val = float('inf')
    cutoff_val = int(lines[length -
                           1])  # best possible value our algorithm can find
    time_limit = 0
    if length <= 30:
        time_limit = 0.5 * 300
    else:
        time_limit = 0.5 * (300 + 60 * (length - 30))
    start_time = time.time()
    while time.time() - start_time < time_limit and (best_val != cutoff_val
                                                     or N != 1
                                                     or phase_unity != 2):
        pop = gen_population(num_members, length,
                             phase_unity)  # generate a population of codes
        for i in range(num_gens):
            pop = evolve(pop, parent_func, mut_func, crossover_func, retain,
                         random_select, mutate, k)  # evolve that population
            # sort the population by min_fn, if best fitness improves on the previous best, update best_val.
            psl_values = [(fast_autocorrelations(x, N, num_elem, min_fn), x)
                          for x in pop]
            psl_values.sort(key=lambda x: x[0])
            seqs_sorted_by_psl = [x[1] for x in psl_values]
            new_val = fast_autocorrelations(seqs_sorted_by_psl[0], N, num_elem,
                                            min_fn)
            if new_val < best_val:  # keep track of the best value found so far, in case we lose it.
                best_code = seqs_sorted_by_psl[0]
                best_val = new_val

            if best_val == cutoff_val:
                break

    return (best_code, best_val)
Пример #23
0
def anneal(code,
           N,
           cooling_schedule,
           phase_unity,
           num_elem=None,
           min_fn=second_max):
    """
    Our fundamental annealing algorithm, which starts with a random polyphase code (phase described by phase_unity)
    and moves around, returning the best code and min_fn value found.  The premise of the algorithm is simple:
    start with a high temperature, which allows more movement around the current code, even if it increases the min_fn
    and over time lower the temperature.  This is the bare-bones algorithm, with no extra features besides that.

    :param code: the initial code
    :type code: array
    :param N: doppler width of our ambiguity function
    :type N: int
    :param cooling_schedule: function that describes how the temperature decreases from generation to generation
    :type cooling_schedule: function
    :param phase_unity: order of the roots of unity that fill the codes we will be considering
    :type phase_unity: int
    :param num_elem: number of elements that min_fn is being evaluated on
    :type num_elem: int
    :param min_fn: function of the ambiguity function/autocorrelation we are minimizing
    :type min_fn: function
    :return: a tuple of the best code and its corresponding min_fn value found by the algorithm

    See also: anneal_with_local_search, anneal_with_tabu
    """
    init_temperature = 2 * len(code)
    temperature = init_temperature
    psl = fast_autocorrelations(code, N, num_elem,
                                min_fn)  # initial peak sidelobe value
    count = 0
    kappa = 0.2
    num_gen = 25 * len(code)  # number of generations at a fixed temperature
    best_found = code
    best_psl = psl
    stuck_count = 0  # number of iterations we have been stuck at a given code
    num_temp_gens = int(2.9 * init_temperature)
    upp_bound = 2 * len(code)**(kappa)
    for j in range(
            1, num_temp_gens):  # loop from high temperature to low temperature
        for i in range(
                num_gen
        ):  # loop at a given temperature of movements from neighbor to neighbor
            new_code = gen_neighbor(code, phase_unity,
                                    min(len(code) - 1,
                                        3))  # new_code found by gen_neighbor
            new_psl = fast_autocorrelations(new_code, N, num_elem, min_fn)
            if new_psl < psl:  # move to the new code if the peak sidelobe value improves
                code = new_code
                stuck_count = 0
                if new_psl < best_psl:  # update best_psl as well if it is improved on
                    best_found = new_code
                    best_psl = new_psl
            else:
                if acceptance_probability(
                        psl, new_psl, temperature
                ) > random.random(
                ):  # otherwise, only move to the code if a randomly generated number is greater than acceptance_probability
                    code = new_code
                    stuck_count = 0
                else:
                    stuck_count += 1

            if stuck_count > upp_bound:  # if we have been stuck for a while, break out of the loop
                count += 1
                break
        if stuck_count > upp_bound:
            break

        temperature = cooling_schedule(temperature, init_temperature, 0.25,
                                       num_temp_gens, j)

    return (best_found, best_psl)