Skip to content

afccp.main.CadetCareerProblem(data_name='Random', data_version='Default', degree_qual_type='Consistent', num_value_function_breakpoints=None, N=1600, M=32, P=6, S=10, generate_extra_components=False, generate_only_nrl=False, ctgan_model_name='CTGAN_Full', ctgan_pilot_sampling=False, printing=True)

💼 Core Class: CadetCareerProblem

The CadetCareerProblem class is the central engine behind the Air Force Cadet Career Problem (AFCCP). It enables you to load, generate, solve, and export cadet-to-AFSC matching problems with full flexibility for synthetic and historical data scenarios.

⚙️ Use Cases:

  • Load existing instances from the /instances folder (e.g. "2022b").
  • Generate new problem instances using "Random" or "CTGAN" data.
  • Solve matching problems using classical or advanced optimization methods (e.g. VFT, GUO, GP).
  • Visualize and export solution results for further analysis.

Parameters:

Name Type Description Default
data_name str

Name of the instance. Can be: - Existing instance name (e.g., "2022b") - "Random" or "CTGAN" for synthetic generation

'Random'
data_version str

Sub-version of the instance (optional).

'Default'
degree_qual_type str

Qualification structure; one of "Binary", "Tiers", "Relaxed", "Consistent".

'Consistent'
num_value_function_breakpoints int | None

Optional number of breakpoints to override existing value function definitions.

None
N int

Number of cadets to generate (synthetic only).

1600
M int

Number of AFSCs to generate.

32
P int

Number of preferences per cadet.

6
S int

Number of bases to generate.

10
generate_extra_components bool

Whether to include bases and extra data.

False
generate_only_nrl bool

Restrict AFSCs to NRLs only.

False
ctgan_model_name str

Model to use for CTGAN generation.

'CTGAN_Full'
ctgan_pilot_sampling bool

Condition sampling on pilot preferences in CTGAN.

False
printing bool ---

Whether to print status updates.

True

📦 Attributes

  • parameters (dict): Core cadet, AFSC, and preference data
  • value_parameters (dict): Active value function weights and breakpoints
  • solutions (dict): Stored solutions by name
  • solution (dict): Currently active solution
  • mdl_p (dict): Modeling parameters (for optimization)
  • gp_df (pd.DataFrame): Goal Programming dataframe (if applicable)

💡 Example

# Generate a synthetic problem instance
instance = CadetCareerProblem(data_name="Random", N=300, M=12, P=5)

# Fix generated data by adding & correcting parameters, value parameters
instance.fix_generated_data()

# Solve using VFT optimization
instance.solve_vft_pyomo_model()

# Export results to Excel
instance.export_solution_results()

Automatic Import Behavior

If data_name matches a folder in /instances, the problem will auto-load that instance's data. Otherwise, it will generate a synthetic instance.

Need Quick Test Data?

Use data_name="Random" and N=50 to generate a fast demo instance for rapid prototyping.


For a tailored guide on the use of this object, please refer to Tutorial 1.

🛠 See Also

  • solve_vft_pyomo_model, solve_guo_pyomo_model, solve_gp_pyomo_model
  • reset_functional_parameters, export_data, measure_solution
  • Module: afccp.data.processing for import/export logic
Source code in afccp/main.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
def __init__(self, data_name="Random", data_version="Default", degree_qual_type="Consistent",
             num_value_function_breakpoints=None, N=1600, M=32, P=6, S=10, generate_extra_components=False,
             generate_only_nrl=False, ctgan_model_name='CTGAN_Full', ctgan_pilot_sampling=False, printing=True):
    """
    ___
    ### 📦 Attributes

    - `parameters (dict)`: Core cadet, AFSC, and preference data
    - `value_parameters (dict)`: Active value function weights and breakpoints
    - `solutions (dict)`: Stored solutions by name
    - `solution (dict)`: Currently active solution
    - `mdl_p (dict)`: Modeling parameters (for optimization)
    - `gp_df (pd.DataFrame)`: Goal Programming dataframe (if applicable)

    ---

    ### 💡 Example

    ```python
    # Generate a synthetic problem instance
    instance = CadetCareerProblem(data_name="Random", N=300, M=12, P=5)

    # Fix generated data by adding & correcting parameters, value parameters
    instance.fix_generated_data()

    # Solve using VFT optimization
    instance.solve_vft_pyomo_model()

    # Export results to Excel
    instance.export_solution_results()
    ```

    ---

    !!! note "Automatic Import Behavior"
        If `data_name` matches a folder in `/instances`, the problem will auto-load that instance's data.
        Otherwise, it will generate a synthetic instance.

    !!! tip "Need Quick Test Data?"
        Use `data_name="Random"` and `N=50` to generate a fast demo instance for rapid prototyping.

    ---

    For a tailored guide on the use of this object, please refer to [Tutorial 1](../../../../../afccp/user-guide/tutorial_1).

    ### 🛠 See Also
    - `solve_vft_pyomo_model`, `solve_guo_pyomo_model`, `solve_gp_pyomo_model`
    - `reset_functional_parameters`, `export_data`, `measure_solution`
    - Module: `afccp.data.processing` for import/export logic
    """

    # Shorten the module name so everything fits better
    afccp_dp = afccp.data.processing

    # Data attributes
    self.data_version = data_version  # Version of instance (in parentheses of the instance sub-folders)
    self.import_paths, self.export_paths = None, None  # We initialize these attributes to 'None'
    self.printing = printing

    # The data variant helps inform how the charts should be constructed
    if len(data_name) == 1:  # "A", "B", "C", ...
        self.data_variant = "Scrubbed"
    elif data_name[:4].isdigit():  # "2016", "2017", "2018", ...
        self.data_variant = "Year"
    else:  # "Random_1", "Random_2", ...
        self.data_variant = "Generated"

    # Additional instance components (value parameters and solutions)
    self.value_parameters, self.vp_name = None, None  # Set of weight and value parameters (and the name)
    self.vp_dict = None  # Dictionary of value_parameters (set of sets)
    self.solution, self.solution_name = None, None  # Dictionary of solution elements (and the name)
    self.solutions = None  # Dictionary of solutions and their main attributes (x, j_array, afsc_array, etc.)

    # Parameters from *former* Lt Rebecca Reynold's thesis
    self.gp_parameters, self.gp_df = None, None

    # Update instances available (for importing)
    afccp.globals.instances_available = []
    for other_data_name in os.listdir(afccp.globals.paths["instances"]):
        if os.path.isdir(afccp.globals.paths["instances"] + other_data_name):
            afccp.globals.instances_available.append(other_data_name)

    # If we have an instance folder already for the specified instance (we're importing it)
    if data_name in afccp.globals.instances_available:

        # Gather information about the files we're importing and eventually exporting
        self.data_name = data_name
        self.import_paths, self.export_paths = afccp_dp.initialize_file_information(self.data_name,
                                                                                    self.data_version)

        # Print statement
        if self.printing:
            print("Importing '" + data_name + "' instance...")

        # Initialize dictionary of instance parameters (Information pertaining to cadets and AFSCs)
        self.parameters = {"Qual Type": degree_qual_type}

        # Import the "fixed" parameters (the information about cadets/AFSCs that, for the most part, doesn't change)
        import_data_functions = [afccp_dp.import_afscs_data, afccp_dp.import_cadets_data,
                                 afccp_dp.import_afsc_cadet_matrices_data, afccp_dp.import_additional_data]
        for import_function in import_data_functions:  # Here we're looping through a list of functions!
            self.parameters = import_function(self.import_paths, self.parameters)  # Python is nice like that...

        # Additional sets and subsets of cadets/AFSCs need to be loaded into the instance parameters
        self.parameters = afccp.data.adjustments.parameter_sets_additions(self.parameters)

        # Import the "Goal Programming" dataframe (from Lt Rebecca Reynold's thesis)
        if "Goal Programming" in self.import_paths:
            self.gp_df = afccp.globals.import_csv_data(self.import_paths["Goal Programming"])
        else:  # Default "GP" file
            self.gp_df = afccp.globals.import_data(
                afccp.globals.paths["files"] + "gp_parameters.xlsx")

        # Import the "Value Parameters" data dictionary
        self.vp_dict = afccp_dp.import_value_parameters_data(self.import_paths, self.parameters,
                                                             num_value_function_breakpoints)

        # Import the "Solutions" data dictionary
        self.solutions = afccp_dp.import_solutions_data(self.import_paths, self.parameters)

    # This is a new problem instance that we're generating (Should be "Random" or "CTGAN")
    else:

        # Error Handling (Must be valid data generation parameter)
        if data_name not in ["Random", "CTGAN"]:
            raise ValueError(
                "Error. Instance name '" + data_name + "' is not a valid instance name. Instances must "
                                                       "be either generated or imported. "
                                                       "(Instance not found in folder).")

        # Determine the name of this instance (Random_1, Random_2, etc.)
        for data_variant in ["Random", "CTGAN"]:
            if data_name == data_variant:

                # Determine name of data based on what instances are already in our "instances" folder
                variant_counter = 1
                name_determined = False
                while not name_determined:
                    check_name = data_variant + "_" + str(variant_counter)
                    if check_name not in afccp.globals.instances_available:
                        self.data_name = check_name
                        name_determined = True
                    else:
                        variant_counter += 1

        # Print statement
        if self.printing:
            print("Generating '" + self.data_name + "' instance...")

        # Want to generate a "valid" problem instance that meets the conditions below
        invalid = True
        while invalid:
            invalid = False  # "Innocent until proven guilty"

            # Generate a "Random" problem instance
            if data_name == 'Random':
                self.parameters = afccp.data.generation.generate_random_instance(
                    N, M, P, S, generate_only_nrl=generate_only_nrl, generate_extra=generate_extra_components)

                # Every cadet needs to be eligible for at least one AFSC
                for i in range(self.parameters['N']):
                    if np.sum(self.parameters['eligible'][i, :]) == 0:
                        invalid = True  # Guilty!
                        break

                # Every AFSC needs to have at least one cadet eligible for it
                for j in range(self.parameters['M']):
                    if np.sum(self.parameters['eligible'][:, j]) == 0:
                        invalid = True  # Guilty!
                        break

                # Too few positions or too many positions to fill
                if (np.sum(self.parameters['quota_max']) < self.parameters['N']) or \
                    (np.sum(self.parameters['quota_min']) > self.parameters['N']):
                    invalid = True  # Guilty!

            # Generate a "CTGAN" problem instance
            elif data_name == "CTGAN":
                self.parameters = afccp.data.generation.generate_ctgan_instance(
                    N, name=ctgan_model_name, pilot_condition=ctgan_pilot_sampling)

            # We don't have that type of data available to generate
            else:
                raise ValueError("Data type '" + data_name + "' currently not a valid method of generating"
                                                             " data.")

        # Additional sets and subsets of cadets/AFSCs need to be loaded into the instance parameters
        self.parameters = afccp.data.adjustments.parameter_sets_additions(self.parameters)

    # Initialize more "functional" parameters
    self.mdl_p = afccp.data.support.initialize_instance_functional_parameters(
        self.parameters["N"])
    self._reset_functional_parameters()

    if self.printing:
        print("Instance '" + self.data_name + "' initialized.")