Skip to content

CadetCareerProblem – Solution Handling Methods

measure_solution(approximate=False, printing=None)

Evaluate a solution using the VFT objective hierarchy

Source code in afccp/main.py
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
def measure_solution(self, approximate=False, printing=None):
    """
    Evaluate a solution using the VFT objective hierarchy
    """
    # Error checking, solution setting
    if self.solution is None or self.value_parameters is None:
        raise ValueError("Error. Solution and value parameters needed to evaluate solution.")

    # Print statement
    if printing is None:
        printing = self.printing

    # Copy weight on GUO solution (relative to CASTLE) from "mdl_p" to "parameters"
    self.parameters['w^G'] = self.mdl_p['w^G']

    # Calculate solution metrics
    self.solution = afccp.solutions.handling.evaluate_solution(
        self.solution, self.parameters, self.value_parameters, approximate=approximate, printing=printing,
        re_calculate_x=self.mdl_p['re-calculate x'])

measure_fitness(printing=None)

This is the fitness function method (could be slightly different depending on how the constraints are handled)

Returns:

Type Description

fitness score

Source code in afccp/main.py
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
def measure_fitness(self, printing=None):
    """
    This is the fitness function method (could be slightly different depending on how the constraints are handled)
    :return: fitness score
    """
    # Error checking, solution setting
    if self.solution is None or self.value_parameters is None:
        raise ValueError("Error. Solution and value parameters needed to evaluate solution.")

    # Printing statement
    if printing is None:
        printing = self.printing

    # Get the solution metrics if necessary
    if "z" not in self.solution:
        self.solution = self.measure_solution(printing=False)

    # Calculate fitness value
    z = afccp.solutions.handling.fitness_function(self.solution['j_array'], self.parameters,
                                                  self.value_parameters, self.mdl_p,
                                                  con_fail_dict=self.solution['con_fail_dict'])

    # Print and return fitness value
    if printing:
        print("Fitness value calculated to be", round(z, 4))
    return z

set_solution(solution_name=None, printing=None)

Set the current instance object's solution to a solution from the dictionary

Source code in afccp/main.py
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
def set_solution(self, solution_name=None, printing=None):
    """
    Set the current instance object's solution to a solution from the dictionary
    """
    if printing is None:
        printing = self.printing

    if self.solutions is None:
        raise ValueError('No solution dictionary initialized')
    else:
        if solution_name is None:
            solution_name = list(self.solutions.keys())[0]
        else:
            if solution_name not in self.solutions:
                raise ValueError('Solution ' + solution_name + ' not in solution dictionary')

        self.solution = self.solutions[solution_name]
        self.solution_name = solution_name
        if self.value_parameters is not None:
            self.measure_solution(printing=printing)

add_solution(j_array: np.ndarray = None, afsc_array: np.ndarray = None, method: str = None)

Takes a numpy array of AFSCs and adds this new solution into the solution dictionary

Source code in afccp/main.py
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
def add_solution(self, j_array: np.ndarray = None, afsc_array: np.ndarray = None, method: str = None):
    """
    Takes a numpy array of AFSCs and adds this new solution into the solution dictionary
    """

    # Determine AFSC solution information
    if j_array is not None:
        afsc_array = np.array([self.parameters['afscs'][j] for j in j_array])
    elif afsc_array is not None:
        j_array = np.array([np.where(self.parameters['afscs'] == afsc)[0][0] for afsc in afsc_array])
    else:
        raise ValueError(f'Error. No AFSC solution array specified')
    if method is None:
        method = 'Added'

    # Create solution dictionary
    solution = {'j_array': j_array, 'method': method, 'afsc_array': afsc_array}

    # Determine what to do with the solution
    self._solution_handling(solution)

    # Return the solution
    return solution

incorporate_rated_algorithm_results(p_dict={}, printing=None)

Takes the two sets of Rated Matches and Reserves and adds that into the parameters (J^Fixed and J^Reserved)

Source code in afccp/main.py
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
def incorporate_rated_algorithm_results(self, p_dict={}, printing=None):
    """
    Takes the two sets of Rated Matches and Reserves and adds that into the parameters (J^Fixed and J^Reserved)
    """
    if printing is None:
        printing = self.printing

    # Reset instance model parameters
    self._reset_functional_parameters(p_dict)

    self.parameters = afccp.solutions.handling.incorporate_rated_results_in_parameters(
        self, printing=printing)

    # Shorthand
    p = self.parameters

    # Temporary stuff
    if self.mdl_p['create_new_rated_solutions']:

        name_dict = {'Rated Matches': 'J^Fixed', 'Rated Reserves': 'J^Reserved',
                     'Rated Alternates (Hard)': 'J^Alternates (Hard)',
                     'Rated Alternates (Soft)': 'J^Alternates (Soft)'}
        new_solutions = {}
        for s_name, s_param in name_dict.items():
            new_solutions[s_name] = {'method': s_name,
                                     'j_array': np.array([p['M'] for _ in p['I']]),
                                     'afsc_array': np.array(['*' for _ in p['I']])}

            # Create this solution array
            for i in p['I']:
                if i in p[s_param]:
                    if s_param == 'J^Reserved':
                        j = p['J^Reserved'][i][len(p['J^Reserved'][i]) - 1]
                    else:
                        j = p[s_param][i]
                    new_solutions[s_name]['j_array'][i] = j
                    new_solutions[s_name]['afsc_array'][i] = p['afscs'][j]

            # Integrate this solution
            self._solution_handling(new_solutions[s_name], printing=False)

find_ineligible_cadets(solution_name=None, fix_it=True)

Prints out the ID's of ineligible pairs of cadets/AFSCs

Source code in afccp/main.py
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
def find_ineligible_cadets(self, solution_name=None, fix_it=True):
    """
    Prints out the ID's of ineligible pairs of cadets/AFSCs
    """

    if solution_name is None:
        if self.solution is None:
            raise ValueError("No solution activated.")
        else:
            solution = self.solution['j_array']
    else:
        solution = self.solutions[solution_name]['j_array']

    # Loop through each cadet to see if they're ineligible for the AFSC they're assigned to
    total_ineligible = 0
    for i, j in enumerate(solution):
        cadet, afsc = self.parameters['cadets'][i], self.parameters['afscs'][j]

        # Unmatched AFSC
        if j == self.parameters['M']:
            continue

        # Cadet is not in the set of eligible cadets for this AFSC
        if i not in self.parameters['I^E'][j]:
            total_ineligible += 1

            # Do we do anything about it yet?
            if fix_it:
                print('Cadet', cadet, 'assigned to AFSC', afsc, 'but is ineligible for it. Adjusting qual matrix to'
                                                                ' allow this exception.')

                # Add exception in different parameters
                self.parameters['qual'][i, j] = "E" + str(self.parameters['t_count'][j])
                self.parameters['ineligible'][i, j] = 0
                self.parameters['eligible'][i, j] = 1

            else:
                print('Cadet', cadet, 'assigned to AFSC', afsc, 'but is ineligible for it.')

    # Printing statement
    if total_ineligible == 0:
        print("No cadets assigned to AFSCs that they're ineligible for in the current solution.")
    else:
        print(total_ineligible, "total cadets assigned to AFSCs that they're ineligible for in the current solution.")

    # Adjust sets and subsets of cadets to reflect change
    if fix_it and total_ineligible > 0:
        self.parameters = afccp.data.adjustments.parameter_sets_additions(self.parameters)