def test_get_node_pos(self):
     num_rows = 3
     num_cols = 3
     network_config = NetworkConfig(num_rows, num_cols)
     assert network_config.get_node_pos(0) == (0, 1)
     assert network_config.get_node_pos(1) == (1, 0)
     assert network_config.get_node_pos(2) == (1, 1)
     assert network_config.get_node_pos(3) == (1, 2)
     assert network_config.get_node_pos(4) == (2, 1)
Example #2
0
    def get_attacker_observation(
            self,
            network_config: NetworkConfig,
            local_view=False,
            reconnaissance=False,
            reconnaissance_bool_features=False) -> np.ndarray:
        """
        Converts the state of the dynamical system into an observation for the attacker. As the environment
        is a partially observed markov decision process, the attacker observation is only a subset of the game state

        :param network_config: the network configuration of the game
        :param local_view: boolean flag indicating whether observations are provided in a local view or not
        :param reconnaissance: boolean flag indicating whether reconnaissance states should be included
        :param reconnaissance_bool_features: boolean flag whether to include boolean features that indicate if
                                             reconnaissance have been done for a certain defense type
        :return: An observation of the environment
        """
        if not reconnaissance:
            # +1 to have an extra feature that indicates if this is the node that the attacker is currently in
            attack_observation = np.zeros((len(network_config.node_list),
                                           self.attack_values.shape[1] + 1))
        elif reconnaissance and not reconnaissance_bool_features:
            # +1 to have an extra feature that indicates if this is the node that the attacker is currently in
            attack_observation = np.zeros(
                (len(network_config.node_list),
                 (self.attack_values.shape[1] * 2 + 1)))
        else:
            # +2 to have an extra feature that indicates if this is the node that the attacker is currently in and reconnaissance bool feature
            attack_observation = np.zeros(
                (len(network_config.node_list),
                 (self.attack_values.shape[1] * 2 + 2)))

        current_pos = self.attacker_pos
        current_node_id = network_config.get_node_id(current_pos)
        current_row, current_col = current_pos
        current_adjacency_matrix_id = network_config.get_adjacency_matrix_id(
            current_row, current_col)

        if local_view:
            neighbors = []

        for node_id in range(len(network_config.node_list)):
            pos = network_config.get_node_pos(node_id)
            node_row, node_col = pos
            node_adjacency_matrix_id = network_config.get_adjacency_matrix_id(
                node_row, node_col)
            if local_view:
                if network_config.adjacency_matrix[current_adjacency_matrix_id][node_adjacency_matrix_id] \
                        and node_id != current_node_id:
                    if not reconnaissance:
                        neighbor_data = np.append(self.attack_values[node_id],
                                                  node_id)
                    elif reconnaissance and not reconnaissance_bool_features:
                        neighbor_data = np.append(
                            np.append(self.attack_values[node_id], node_id),
                            self.reconnaissance_state[node_id]),
                    else:
                        reconaissance_bool = [0]
                        if node_id in self.reconnaissance_actions:
                            reconaissance_bool = [1]
                        neighbor_data = np.append(
                            np.append(
                                np.append(self.attack_values[node_id],
                                          node_id),
                                self.reconnaissance_state[node_id]),
                            reconaissance_bool)
                    neighbor_row, neighbor_col = network_config.get_node_pos(
                        node_id)
                    neighbors.append(
                        (neighbor_row, neighbor_col, neighbor_data))
            else:
                if node_id == current_node_id:
                    if not reconnaissance:
                        attack_observation[node_id] = np.append(
                            self.attack_values[node_id], 1)
                    elif reconnaissance and not reconnaissance_bool_features:
                        attack_observation[node_id] = np.append(
                            np.append(self.attack_values[node_id], 1),
                            self.reconnaissance_state[node_id])
                    else:
                        reconaissance_bool = [0]
                        if node_id in self.reconnaissance_actions:
                            reconaissance_bool = [1]
                        attack_observation[node_id] = np.append(
                            np.append(
                                np.append(self.attack_values[node_id], 1),
                                self.reconnaissance_state[node_id]),
                            reconaissance_bool)
                elif network_config.fully_observed:
                    attack_observation[node_id] = np.append(
                        self.attack_values[node_id], 0)
                elif network_config.adjacency_matrix[
                        current_adjacency_matrix_id][node_adjacency_matrix_id]:
                    if not reconnaissance:
                        attack_observation[node_id] = np.append(
                            self.attack_values[node_id], 0)
                    elif reconnaissance and not reconnaissance_bool_features:
                        attack_observation[node_id] = np.append(
                            np.append(self.attack_values[node_id], 0),
                            self.reconnaissance_state[node_id])
                    else:
                        reconaissance_bool = [0]
                        if node_id in self.reconnaissance_actions:
                            reconaissance_bool = [1]
                        attack_observation[node_id] = np.append(
                            np.append(
                                np.append(self.attack_values[node_id], 0),
                                self.reconnaissance_state[node_id]),
                            reconaissance_bool)
                elif reconnaissance:
                    if not reconnaissance_bool_features:
                        attack_values = np.zeros((self.attack_values.shape[1]))
                        attack_observation[node_id] = np.append(
                            np.append(attack_values, 0),
                            self.reconnaissance_state[node_id])
                    else:
                        reconaissance_bool = [0]
                        if node_id in self.reconnaissance_actions:
                            reconaissance_bool = [1]
                        attack_values = np.zeros((self.attack_values.shape[1]))
                        attack_observation[node_id] = np.append(
                            np.append(np.append(attack_values, 0),
                                      self.reconnaissance_state[node_id]),
                            reconaissance_bool)

        if local_view:
            # sort by row then col
            sorted_neighbors = sorted(neighbors, key=lambda x: (x[0], x[1]))
            neighbor_data = np.array(
                list(map(lambda x: x[2], sorted_neighbors)))
            neighbor_ids = neighbor_data[:, self.attack_values.shape[1]]
            if not reconnaissance:
                local_view_obs = np.full((network_config.max_neighbors,
                                          self.attack_values.shape[1] + 1), -1)
            elif reconnaissance and not reconnaissance_bool_features:
                local_view_obs = np.full((network_config.max_neighbors,
                                          self.attack_values.shape[1] * 2 + 1),
                                         -1)
            else:
                local_view_obs = np.full((network_config.max_neighbors,
                                          self.attack_values.shape[1] * 2 + 2),
                                         -1)
            for n in range(network_config.max_neighbors):
                rel_neighbor_pos = network_config.relative_neighbor_positions[
                    n]
                neighbor_pos = (current_row + rel_neighbor_pos[0],
                                current_col + rel_neighbor_pos[1])
                for i in range(len(neighbor_ids)):
                    node_id = neighbor_ids[i]
                    node_pos = network_config.get_node_pos(node_id)
                    if node_pos == neighbor_pos and node_pos[0] <= current_row:
                        local_view_obs[n] = neighbor_data[i]
            attack_observation = np.array(local_view_obs)
        return attack_observation
Example #3
0
    def set_state(self,
                  node_list: List,
                  num_attack_types: int,
                  defense_val: int = 2,
                  attack_val: int = 0,
                  num_vulnerabilities_per_node: int = 1,
                  det_val: int = 2,
                  vulnerability_val: int = 0,
                  num_vulnerabilities_per_layer: int = 1,
                  network_config: NetworkConfig = None,
                  randomize_state: bool = False,
                  randomize_visibility: bool = False,
                  visibility_p: float = 0.5):
        """
        Sets the state

        :param node_list: list of nodes
        :param num_attack_types:  number of attack types
        :param defense_val: defense value for defense types that are not vulnerable
        :param attack_val: attack value for attack types
        :param num_vulnerabilities_per_node: number of vulnerabilities per node
        :param det_val: detection value per node
        :param vulnerability_val: defense value for defense types that are vulnerable
        :param num_vulnerabilities_per_layer: number of vulnerabilities per layer
        :param network_config: network configuration
        :param randomize_state: boolean flag whether to create the state randomly
        :param randomize_visibility: boolean flag whether to randomize visibility for partially observed envs
        :return: None
        """
        num_nodes = len(node_list)
        attack_values = np.zeros((num_nodes, num_attack_types))
        defense_values = np.zeros((num_nodes, num_attack_types))
        det_values = np.zeros(num_nodes)
        reconnaissance_state = np.full(
            (num_nodes, num_attack_types),
            constants.GAME_CONFIG.INITIAL_RECONNAISSANCE_STATE)

        d_val = defense_val
        a_val = attack_val
        de_val = det_val
        v_val = vulnerability_val

        vulnerabilities_per_layer = np.zeros(
            (network_config.num_rows, network_config.num_cols))
        for row in range(1, network_config.num_rows - 1):
            vulnerabilities = np.random.choice(
                network_config.num_cols,
                size=num_vulnerabilities_per_layer,
                replace=False)
            vulnerabilities_per_layer[row][vulnerabilities] = 1

        for node_id in range(num_nodes):
            row, col = network_config.get_node_pos(node_id)
            num_vuln = min(num_vulnerabilities_per_node, num_attack_types)
            vulnerabilities = []
            if vulnerabilities_per_layer[row][col] == 1 or node_list[
                    node_id] == NodeType.DATA.value:
                vulnerabilities = np.random.choice(
                    num_attack_types,
                    size=num_vuln)  # random vulnerability per node
            if node_list[node_id] == NodeType.DATA.value or node_list[
                    node_id] == NodeType.SERVER.value:
                d_vals = []
                a_vals = []
                for at in range(num_attack_types):
                    if randomize_state:
                        d_val = max(
                            self.min_random_d_val,
                            np.random.choice(
                                list(
                                    range(self.min_random_d_val,
                                          defense_val + 1))))
                        a_val = max(
                            self.min_random_a_val,
                            np.random.choice(
                                list(
                                    range(self.min_random_a_val,
                                          attack_val + 1))))
                        de_val = max(
                            self.min_random_det_val,
                            np.random.choice(
                                list(
                                    range(self.min_random_det_val,
                                          det_val + 1))))
                        v_val = max(
                            vulnerability_val,
                            np.random.choice(
                                list(
                                    range(vulnerability_val,
                                          self.max_random_v_val + 1))))
                    d_vals.append(d_val)
                    a_vals.append(a_val)
                defense_values[node_id] = d_vals
                det_values[node_id] = de_val
                attack_values[node_id] = a_vals
                for vuln_id in vulnerabilities:
                    defense_values[node_id][
                        vuln_id] = v_val  # vulnerability (lower defense)

        if randomize_visibility:
            for node_id in range(len(reconnaissance_state)):
                if np.random.rand() < visibility_p:
                    reconnaissance_state[node_id] = defense_values[node_id]
                    self.reconnaissance_actions.append(node_id)
        self.attack_values = attack_values.astype(np.int32)
        self.defense_values = defense_values.astype(np.int32)
        self.defense_det = det_values.astype(np.int32)
        self.reconnaissance_state = reconnaissance_state.astype(np.int32)