Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
14 kB
5
Indexable
Never
from ctypes import *
from py_interface import *

import json
import os
import random
import asyncio
import numpy as np

PARTICLES_COUNT = 10
MAX_ITERATIONS = 20
DEFAULT_COGNITIVE_PARAMETER = 2.0
DEFAULT_SOCIAL_PARAMETER = 2.0
DEFAULT_INERTIA_WEIGHT = 0.8
GLOBAL_OUTPUT_PATH = os.path.dirname(__file__) + '/../../simulation_output'
PARAMETERS_INDEXES = ['ttlStart', 'ttlIncrement', 'ttlThreshold', 'rreqRetries', 'nodeTraversalTime',
                      'activeRouteTimeout', 'netDiameter', 'maxQueueLength', 'allowedHelloLoss']
PARAMETERS_BOUNDS = {
    'ttlStart': {
        'min': 1.0,
        'max': 10.0
    },
    'ttlIncrement': {
        'min': 1.0,
        'max': 10.0
    },
    'ttlThreshold': {
        'min': 1.0,
        'max': 20.0
    },
    'rreqRetries': {
        'min': 1.0,
        'max': 10.0
    },
    'nodeTraversalTime': {
        'min': 10.0,
        'max': 1000.0
    },
    'activeRouteTimeout': {
        'min': 1.0,
        'max': 30.0
    },
    'netDiameter': {
        'min': 15.0,
        'max': 100.0
    },
    'maxQueueLength': {
        'min': 1.0,
        'max': 100
    },
    'allowedHelloLoss': {
        'min': 1.0,
        'max': 10.0
    },
}

os.chdir(os.path.dirname(__file__))
global_best_positions_simulations = []
global_best_costs_simulations = []


class Env(Structure):
    _pack_ = 1
    _fields_ = [
        ('ttlStart', c_int),
        ('ttlIncrement', c_int),
        ('ttlThreshold', c_int),
        ('rreqRetries', c_int),
        ('nodeTraversalTime', c_double),
        ('activeRouteTimeout', c_int),
        ('netDiameter', c_int),
        ('maxQueueLength', c_int),
        ('allowedHelloLoss', c_int)
    ]


class Act(Structure):
    _pack_ = 1
    _fields_ = [
        ('packetDeliveryRatio', c_double)
    ]


class Particle:
    def __init__(self, params):
        self.position = np.array([params['ttlStart'], params['ttlIncrement'],
                                  params['ttlThreshold'], params['rreqRetries'],
                                  params['nodeTraversalTime'],
                                  params['activeRouteTimeout'],
                                  params['netDiameter'], params['maxQueueLength'],
                                  params['allowedHelloLoss']])
        self.velocity = np.zeros_like(self.position)
        self.best_position = self.position.copy()
        self.best_cost = 1001

    def update_velocity(self, global_best_position):
        """
        Update velocity of particle.
        
        :param global_best_position: set of parameters, that have best position in n-dimensional space
        """
        for j in range(len(self.position)):
            r1 = np.random.rand()
            r2 = np.random.rand()
            cognitive_component = DEFAULT_COGNITIVE_PARAMETER * r1 * (self.best_position[j] - self.position[j])
            social_component = DEFAULT_SOCIAL_PARAMETER * r2 * (global_best_position[j] - self.position[j])
            self.velocity[j] = DEFAULT_INERTIA_WEIGHT * self.velocity[j] + cognitive_component + social_component

    def update_position(self):
        """
        Update position of particle - set value for each parameter in set of parameters.
        """
        for j in range(len(self.position)):
            self.position[j] += self.velocity[j]
            self.position[j] = check_parameter_bounds(PARAMETERS_INDEXES[j], self.position[j])

    def update_cost(self, cost):
        """
        Update best_cost and best_position of particle if cost is lower than the previous one.
        :param cost: calculated cost from simulation
        """
        # if cost < self.best_cost:
        # self.best_cost = cost
        # self.best_position = self.position
        if cost < self.best_cost:
            self.best_cost = cost
            self.best_position = self.position


class PSO:
    def __init__(self, entry_particles):
        self.global_best_cost = 1001
        self.global_best_position = entry_particles[0].best_position
        self.particles = entry_particles

    async def fitness_function(self, iteration):
        """
        Fitness function of PSO implementation which runs 10 simulations sequentially.

        :param iteration: Number of iteration of PSO algorithm.
        """
        print(f"Running {iteration} iteration of PSO algorithm.")
        coroutines = []
        simulations_iteration = 0
        mempool_keys, memblock_keys = generate_simulations_memory_parameters()
        for simulation in convert_particles_array_to_ns3_settings_array(self.particles):
            simulation['pathOfOutput'] = os.path.dirname(__file__) + '/../../simulation_output' + str(
                simulations_iteration) + '.json'
            coroutine = run_simulation(mempool_keys[simulations_iteration], memblock_keys[simulations_iteration],
                                       simulation)
            coroutines.append(coroutine)
            simulations_iteration += 1
        sim_results = await asyncio.gather(*coroutines)

        simulation_outputs = []
        for i in range(10):
            f = open(os.path.dirname(__file__) + '/../../simulation_output' + str(i) + '.json')
            data = json.load(f)
            simulation_outputs.append(data)

        costs = calculate_costs(simulation_outputs)
        best_cost, best_set_of_parameters = find_best_cost(costs,
                                                           convert_particles_array_to_ns3_settings_array(
                                                               self.particles))

        if best_cost < self.global_best_cost:
            self.global_best_cost = best_cost
            self.global_best_position = best_set_of_parameters

        global_best_positions_simulations.append(best_set_of_parameters)
        global_best_costs_simulations.append(best_cost)

        iteration = 0
        for particle in self.particles:
            particle.update_velocity(convert_ns3_settings_to_array(self.global_best_position))
            particle.update_position()
            particle.update_cost(costs[iteration])
            iteration += 1

    async def run(self):
        """
        Runs fitness function in number of iterations set by MAX_ITERATIONS and evaluates results of implemented
        algorithm.
        """
        simulations_results = []
        for pso_iteration in range(MAX_ITERATIONS):
            simulations_results.append(await self.fitness_function(pso_iteration))
        print('Global best cost is: ' + str(self.global_best_cost))
        print('Global best position is: ' + str(self.global_best_position))
        #print('Global best costs: ' + str(global_best_costs_simulations))
        #print('Global best positions: ' + str(global_best_positions_simulations))


def generate_simulations_memory_parameters():
    """
    Generate memory parameters for simulations according to count of particles from range 1000-10000.

    :return: Generated mempool_keys and memblock_keys for ns3 simulations.
    """
    generatedNumbers = random.sample(range(1000, 10000), PARTICLES_COUNT * 2)
    mempool_keys = generatedNumbers[:10]
    memblock_keys = generatedNumbers[10:]
    return mempool_keys, memblock_keys


def check_parameter_bounds(parameter_type, value):
    """
    Check if new parameter value fits into bounds for this parameter. If not, set closest possible value.

    :param parameter_type: Name of parameter.
    :param value: New value of parameter.
    :return: Correct new value of parameter.
    """
    if value < PARAMETERS_BOUNDS[parameter_type]['min']:
        value = PARAMETERS_BOUNDS[parameter_type]['min']
    elif value > PARAMETERS_BOUNDS[parameter_type]['max']:
        value = PARAMETERS_BOUNDS[parameter_type]['max']
    return value


async def run_simulation(mempool_key, memblock_key, ns3Settings):
    """
    Run new ns3 simulation via ns3-ai class Experiment and wait till the simulation is not finished.

    :param mempool_key: Integer value for mempool_key from range 1000-10000.
    :param memblock_key: Integer value for memblock_key from range 1000-10000.
    :param ns3Settings: Set of parameters for AODV protocol.
    :return: Results of simulation.
    """
    mem_size = 4096
    exp = Experiment(mempool_key, mem_size, 'bp-pso13', '../../')
    exp.reset()
    pro = exp.run(setting=ns3Settings, show_output=True)
    pro_wait = pro.wait()
    if not type(pro_wait) is int:
        await pro.wait()
    return pro


def calculate_costs(results_of_simulations):
    """
    Calculate costs for each simulation run in one iteration of PSO algorithm.

    :param results_of_simulations: Array of results of simulations consisting from PDR, Delay and Average Throughput.
    :return: Array of calculated costs.
    """
    costs = []
    for result_of_simulation in results_of_simulations:
        pdr = result_of_simulation["PDR"]
        delay = result_of_simulation["EtE"]
        throughput = result_of_simulation["avgThroughput"]

        w_pdr = 0.4
        w_delay = 0.3
        w_throughput = 0.3

        cost = w_pdr * (1 - pdr / 100) + w_delay * delay + w_throughput * throughput
        costs.append(cost)
    return costs


def find_best_cost(costs, simulations_ns3_params):
    """
    Find best cost in array of costs.

    :param costs: Array of costs.
    :param simulations_ns3_params: Array of parameters for each simulation.
    :return: Lowest cost and the corresponding set of parameters.
    """
    lowest_cost = min(costs)
    lowest_cost_index = costs.index(lowest_cost)
    return lowest_cost, simulations_ns3_params[lowest_cost_index]


def convert_ns3_settings_to_array(params):
    """
    Convert object of simulation parameters to array form which is used for representation of particle position in
    n-dimensional space.

    :param params: Object of simulation parameters.
    :return: Particle position in n-dimensional space.
    """
    return np.array([params['ttlStart'], params['ttlIncrement'],
                     params['ttlThreshold'], params['rreqRetries'],
                     params['nodeTraversalTime'],
                     params['activeRouteTimeout'],
                     params['netDiameter'], params['maxQueueLength'],
                     params['allowedHelloLoss']])


def convert_particle_to_ns3_settings(pso_particle):
    """
    Convert array of particle position to object of simulation parameters.

    :param pso_particle: Array of particle position.
    :return: Object that represents simulation parameters.
    """
    return {
        'ttlStart': pso_particle.position[0],
        'ttlIncrement': pso_particle.position[1],
        'ttlThreshold': pso_particle.position[2],
        'rreqRetries': pso_particle.position[3],
        'nodeTraversalTime': pso_particle.position[4],
        'activeRouteTimeout': pso_particle.position[5],
        'netDiameter': pso_particle.position[6],
        'maxQueueLength': pso_particle.position[7],
        'allowedHelloLoss': pso_particle.position[8]
    }


def convert_particles_array_to_ns3_settings_array(pso_particles_array):
    """
    Convert array of particles to array of simulations parameters.

    :param pso_particles_array: Array of particles.
    :return: Array of parameters for simulations.
    """
    ns3_settings_array = []
    for pso_particle in pso_particles_array:
        ns3_settings_array.append(convert_particle_to_ns3_settings(pso_particle))
    return ns3_settings_array


def convert_ns3_settings_to_particle(ns3_settings):
    """
    Convert array of parameters for simulations to new particle.

    :param ns3_settings: Array of parameters for many simulations.
    :return: Object of particle.
    """
    return Particle(ns3_settings)


def convert_ns3_settings_array_to_particles_array(ns3_settings_array):
    """
    Convert array of set of parameters for simulations to array of particles.

    :param ns3_settings_array: Array of set of parameters for simulations.
    :return: Array of particles.
    """
    particles_array = []
    for ns3_settings in ns3_settings_array:
        particles_array.append(convert_ns3_settings_to_particle(ns3_settings['ns3Settings']))
    return particles_array


async def main():
    mempool_keys, memblock_keys = generate_simulations_memory_parameters()
    simulationsNs3Params = []

    for i in range(10):
        ns3Settings = {
            'ttlStart': random.uniform(1.0, 10.0),
            'ttlIncrement': random.uniform(1.0, 10.0),
            'ttlThreshold': random.uniform(1.0, 20.0),
            'rreqRetries': random.uniform(1.0, 10.0),
            'nodeTraversalTime': random.uniform(10.0, 1000.0),
            'activeRouteTimeout': random.uniform(1.0, 10.0),
            'netDiameter': random.uniform(10.0, 50.0),
            'maxQueueLength': random.uniform(1.0, 10.0),
            'allowedHelloLoss': random.uniform(1.0, 10.0),
            'pathOfOutput': os.path.dirname(__file__) + '/../../simulation_output' + str(i) + '.json'
        }
        simulationsNs3Params.append(
            {'mempool_key': mempool_keys[i], 'memblock_key': memblock_keys[i], 'ns3Settings': ns3Settings})

    particles = []
    for i in range(PARTICLES_COUNT):
        params = {
            'ttlStart': simulationsNs3Params[i]['ns3Settings']['ttlStart'],
            'ttlIncrement': simulationsNs3Params[i]['ns3Settings']['ttlIncrement'],
            'ttlThreshold': simulationsNs3Params[i]['ns3Settings']['ttlThreshold'],
            'rreqRetries': simulationsNs3Params[i]['ns3Settings']['rreqRetries'],
            'nodeTraversalTime': simulationsNs3Params[i]['ns3Settings']['nodeTraversalTime'],
            'activeRouteTimeout': simulationsNs3Params[i]['ns3Settings']['activeRouteTimeout'],
            'netDiameter': simulationsNs3Params[i]['ns3Settings']['netDiameter'],
            'maxQueueLength': simulationsNs3Params[i]['ns3Settings']['maxQueueLength'],
            'allowedHelloLoss': simulationsNs3Params[i]['ns3Settings']['allowedHelloLoss']
        }
        particle = Particle(params)
        particles.append(particle)
    pso = PSO(convert_ns3_settings_array_to_particles_array(simulationsNs3Params))
    await pso.run()


asyncio.run(main())