1

I am trying to run a replication study, and I have run the code in question multiple times without error. I took a little time off to work on some other projects, and it suddenly doesn't work, I have run into the following error (with different key numbers) every time I have tried to run the code subsequently.

<class 'KeyError'> Traceback (most recent call last): File "main.py", line 145, in total_requests_served = run_epoch(envt, oracle, central_agent, value_function, day, is_training=True) File "main.py", line 58, in run_epoch scored_final_actions = central_agent.choose_actions(scored_actions_all_agents, is_training=is_training, epoch_num=envt.num_days_trained) File "C:\Users\src\CentralAgent.py", line 33, in choose_actions return self._choose(agent_action_choices, is_training, epoch_num) File "C:\Users\src\CentralAgent.py", line 53, in _additive_noise final_actions = self._choose_actions_ILP(agent_action_choices, get_noise=get_noise) File "C:\Users\src\CentralAgent.py", line 132, in _choose_actions_ILP assigned_action_id = assigned_actions[agent_idx] KeyError: 579

I have tried different imports from docplex, I have tried to use dict.get with a pass statement if False, I have tried dict.get with a break statement if False. All either crash the code or in the case of the break statement make the resulting output useless. I have also tried uninstalling and reinstalling python packages, uninstalling and reinstalling anaconda altogether.

Here is the code that seems to cause the error, which was taken from the github for the paper.

def _choose_actions_ILP(self, agent_action_choices: List[List[Tuple[Action, float]]], get_noise: Callable[[Var], float]=lambda x: 0) -> List[Tuple[Action, float]]:
    # Model as ILP
    model = Model()

    # For converting Action -> action_id and back
    action_to_id: Dict[Action, int] = {}
    id_to_action: Dict[int, Action] = {}
    current_action_id = 0

    # For constraint 2
    requests: Set[Request] = set()

    # Create decision variables and their coefficients in the objective
    # There is a decision variable for each (Action, Agent).
    # The coefficient is the value associated with the decision variable
    decision_variables: Dict[int, Dict[int, Tuple[Any, float]]] = {}
    for agent_idx, scored_actions in enumerate(agent_action_choices):
        for action, value in scored_actions:
            # Convert action -> id if it hasn't already been done
            if action not in action_to_id:
                action_to_id[action] = current_action_id
                id_to_action[current_action_id] = action
                current_action_id += 1

                action_id = current_action_id - 1
                decision_variables[action_id] = {}
            else:
                action_id = action_to_id[action]

            # Update set of requests in actions
            for request in action.requests:
                if request not in requests:
                    requests.add(request)

            # Create variable for (action_id, agent_id)
            variable = model.binary_var(name='x{},{}'.format(action_id, agent_idx))

            # Save to decision_variable data structure
            decision_variables[action_id][agent_idx] = (variable, value)

    # Create Constraint 1: Only one action per Agent
    for agent_idx in range(len(agent_action_choices)):
        agent_specific_variables: List[Any] = []
        for action_dict in decision_variables.values():
            if agent_idx in action_dict:
                agent_specific_variables.append(action_dict[agent_idx])
        model.add_constraint(model.sum(variable for variable, _ in agent_specific_variables) == 1)

    # Create Constraint 2: Only one action per Request
    for request in requests:
        relevent_action_dicts: List[Dict[int, Tuple[Any, float]]] = []
        for action_id in decision_variables:
            if (request in id_to_action[action_id].requests):
                relevent_action_dicts.append(decision_variables[action_id])
        model.add_constraint(model.sum(variable for action_dict in relevent_action_dicts for variable, _ in action_dict.values()) <= 1)

    # Create Objective
    score = model.sum((value + get_noise(variable)) * variable for action_dict in decision_variables.values() for (variable, value) in action_dict.values())
    model.maximize(score)

    # Solve ILP
    solution = model.solve()
    assert solution  # making sure that the model doesn't fail

    # Get vehicle specific actions from ILP solution
    assigned_actions: Dict[int, int] = {}
    for action_id, action_dict in decision_variables.items():
        for agent_idx, (variable, _) in action_dict.items():
            if (solution.get_value(variable) == 1):
                assigned_actions[agent_idx] = action_id


    final_actions: List[Tuple[Action, float]] = []
    for agent_idx in range(len(agent_action_choices)):

        assigned_action_id = assigned_actions[agent_idx]
        assigned_action = id_to_action[assigned_action_id]

        scored_final_action = None
        for action, score in agent_action_choices[agent_idx]:
            if (action == assigned_action):
                scored_final_action = (action, score)
                break

        assert scored_final_action is not None
        final_actions.append(scored_final_action)

    return final_actions

This is my first time working with docplex so I'm not really even sure if this is the problem or not. Any advice would be incredibly helpful, thanks.

Sean
  • 11
  • 1
  • if you get key error for `assigned_actions[agent_idx]` then first check what keys you have in `assigned_actions.keys()`. Maybe this part of code works correctly and you made mistake in other part and you use wrong value in `agent_idx`. So first check if you get correct `data` before you start searching problem in code. – furas Apr 18 '21 at 00:59
  • Thank you for the feedback, I will take a closer look at all the imported data. The assigned_actions dictionary populates with useable keys the vast majority of the time. Do you have any clue why it would suddenly stop working without any changes to the code. – Sean Apr 18 '21 at 01:08
  • if code doesn't change then maybe data changed - and now code need changes to work correctly with new data. – furas Apr 18 '21 at 01:40

1 Answers1

0

It's not simple to guess what's wrong without running the code. Still, one line looks suspicious to me, this test:

        if (solution.get_value(variable) == 1):

is not robust: CPLEX works with floating point numbers, and integer/binary variables yield integer values within a (small) tolerance. The deviation might change with versions and platforms. IN other terms, a binary variable might end up with 0.9999991 as value in a solution, which is consistent with the tolerance but would not pass the "==1" test. A robust version of this test would be:

        if ( abs(solution[variable] -1 ) <= 1e-6): 

1e-6 is the default integrality tolerance in CPLEX.

Philippe Couronne
  • 826
  • 1
  • 5
  • 6