Detecting and Resolving Infeasibility

Hong Chen

2026-03-18

Introduction

Infeasibility in automatic test assembly commonly arises from two sources.

This vignette demonstrates a structured workflow for:

Examples: Introduce Infeasibility

A simulated item pool is used for analysis and can be accessed via data("mixe_format_pool"). Specifications for each panel is the same as that in Formulation for Multiple Objectives vignette. Minimax strategy (mode = “one_dev”) is used to formulate multiple objectives (minimize the largest deviation between the realized TIF values and the target value of 5 at \(\theta = -1,0,1\) for S1R module).

Specifications for MST 1-2-2 with discrete and stimulus-based items
Specification Description Num of Constraints
Structure requirements
MST structure MST 1-2-2, each module contains 12 items. RDP: 0 9
Panel-level requirements
Item exposure control Unique items are used across modules in the panel. 1000
Pathway-level requirements
TEI Each pathway has at most 2 TEI items. 4
Response time The total response time per pathway should be between 20 and 40 minutes. 8
TIF threshold Minimum TIF values for the easier pathways (S1R-S2E-S3E and S1R-S2H-S3E): TIF values must be at least 10 at \(\theta\) = -1.15, -0.67 and -0.32 6
Minimum TIF values for the harder pathways (S1R-S2E-S3H and S1R-S2H-S3H): TIF values must be at least 10 at \(\theta\) = 0.32, 0.67 and 1.15 6
Module-level requirements
Stimulus num and type S1R module does not include any stimuli 2
S2E and S2H each contain 1 text-based stimulus. 2
S3E and S3H each contain 1 graphic-based stimulus. 2
Content Min and Max number of items (4 content areas) per module: (2,4) 40
DOK Min and Max number of items for DOK 1–3 per module: (3,5) 30
Mean difficulty S2E: (-0.55,-0.45), S2H: (0.45,0.55), S3E: (-1.05,-0.95), S3H: (0.95,1.05) 8
Itemset-level requirements
Item count in a selected stimulus S1R module does not have items associated with any stimulus. 30
At least 4 items conditional on the selection of a stimulus in S2E, S2H, S3E and S3H. 240
TEI item count in a selected stimulus At most 1 TEI item conditional on a selected stimulus in S2E, S2H, S3E and S3H. 120
No enemy items Items from the same enemy group cannot appear in the same pathway. 40
Stimulus-level requirements
Stimulus complexity score The complexity score from selected stimulus should be between 4.5 and 8.5. 240
Relative Objective: Maximize the TIFs at -1, 0 and 1
capped maximin \(\min\; d \quad \text{s.t. }\; \lvert y_k - 5 \rvert \le d\) 6

Constructing mstATA_model for one panel

There are 1793 linear constraints for an MST panel.

data("mixed_format_pool")
# step 1: prepare item pool
low_abilities<-c(-1.15,-0.67,-0.32)
high_abilities<-c(0.32,0.67,1.15)
target_abilities<-c(-1,0,1)
theta_points<-c(low_abilities,high_abilities,target_abilities)
theta_information<-compute_iif(mixed_format_pool,
                               item_par_cols = list("2PL"=c("discrimination","difficulty")),
                               theta = theta_points,
                               model_col = "model",D = 1)
mixed_format_pool[,paste0("iif(theta=",theta_points,")")]<-theta_information
enemyitem_set<-create_enemy_sets(mixed_format_pool$item_id,
                                 mixed_format_pool$enemyitem,
                                 sep_pattern = ",")
pivot_stim_map<-create_pivot_stimulus_map(mixed_format_pool,
                                          item_id_col = "item_id",
                                          stimulus = "stim",
                                          pivot_item = "pivot")
# step 2: specify mst structure
mst122<-mst_design(itempool = mixed_format_pool,item_id_col = "item_id",
                   design = "1-2-2",rdps = list(c(0),c(0)),
                   module_length = rep(12,5),
                   enemyitem_set = enemyitem_set,
                   pivot_stim_map = pivot_stim_map)
# step 3: identify hierarchical requirements

# step 4: translate to linear model
mst_structure<-mst_structure_con(x = mst122,info_tol = 0.1)
mst_noreuse<-panel_itemreuse_con(x = mst122,overlap = FALSE)
mst_tei<-test_itemcat_con(x = mst122,attribute = "itemtype",cat_levels = "TEI",
                          operator = "<=",target_num = 2,which_pathway = 1:4)
mst_time<-test_itemquant_range_con(x = mst122,attribute = "time",
                                   min = 20*60,max = 40*60,
                                   which_pathway = 1:4)
mst_tif_low1<-test_itemquant_con(x = mst122,
                                 attribute = "iif(theta=-1.15)",
                                 operator = ">=",
                                 target_value = 10,
                                 which_pathway = 1:2)
mst_tif_low2<-test_itemquant_con(x = mst122,
                                 attribute = "iif(theta=-0.67)",
                                 operator = ">=",
                                 target_value = 10,
                                 which_pathway = 1:2)
mst_tif_low3<-test_itemquant_con(x = mst122,
                                 attribute = "iif(theta=-0.32)",
                                 operator = ">=",
                                 target_value = 10,
                                 which_pathway = 1:2)
mst_tif_high1<-test_itemquant_con(x = mst122,
                                  attribute = "iif(theta=0.32)",
                                  operator = ">=",
                                  target_value = 10,
                                  which_pathway = 3:4)
mst_tif_high2<-test_itemquant_con(x = mst122,
                                  attribute = "iif(theta=0.67)",
                                  operator = ">=",
                                  target_value = 10,
                                  which_pathway = 3:4)
mst_tif_high3<-test_itemquant_con(x = mst122,
                                  attribute = "iif(theta=1.15)",
                                  operator = ">=",
                                  target_value = 10,
                                  which_pathway = 3:4)
mst_stimtype_s1<-test_stimcat_con(x = mst122,
                                  attribute = "stimtype",
                                  cat_levels = c("text-based","graphic-based"),
                                  operator = "=",
                                  target_num = 0,which_module = 1)
mst_stimtype_s2<-test_stimcat_con(x = mst122,
                                  attribute = "stimtype",
                                  cat_levels = "text-based",
                                  operator = "=",
                                  target_num = 1,which_module = 2:3)
mst_stimtype_s3<-test_stimcat_con(x = mst122,attribute = "stimtype",
                                  cat_levels = "graphic-based",
                                  operator = "=",
                                  target_num = 1,which_module = 4:5)
mst_content<-test_itemcat_range_con(x = mst122,
                                    attribute = "content",
                                    cat_levels = paste0("content ",1:4),
                                    target = 3,deviation = 1,
                                    which_module = 1:5)
mst_dok<-test_itemcat_range_con(x = mst122,
                                attribute = "dok",
                                cat_levels = paste0("dok ",1:3),
                                min = 3,max = 5,
                                which_module = 1:5)
mst_meandiff<-test_itemquant_range_con(x = mst122,
                                       attribute = "difficulty",
                                       target = c(-0.5,0.5,-1,1)*12,
                                       deviation = 0.05*12,
                                       which_module = 2:5)
mst_stimitem_s1<-stim_itemcount_con(x = mst122,
                                    min = 0,max = 0,
                                    which_module = 1)
mst_stimitem_s2_s3<-stim_itemcount_con(x = mst122,
                                       min = 4,max = NULL,
                                       which_module = 2:5)
mst_stimitemtype<-stim_itemcat_con(x = mst122,
                                   attribute = "itemtype",cat_levels = "TEI",
                                   operator = "<=",target_num = 1,
                                   which_module = 2:5)
mst_noenemy<-enemyitem_exclu_con(x = mst122)
mst_stimcomplexity<-stimquant_con(x = mst122,
                                  attribute = "stimcomplexity",
                                  min = 4.5,max = 8.5,
                                  which_module = 2:5)
constraint_list<-list(mst_structure,
                      mst_noreuse,
                      mst_tei,mst_time,
                      mst_tif_low1,mst_tif_low2,mst_tif_low3,
                      mst_tif_high1,mst_tif_high2,mst_tif_high3,
                      mst_stimtype_s1,mst_stimtype_s2,mst_stimtype_s3,
                      mst_content,mst_dok,mst_meandiff,
                      mst_stimitem_s1,mst_stimitem_s2_s3,
                      mst_stimitemtype,
                      mst_noenemy,
                      mst_stimcomplexity)
abs_obj1<-objective_term(x = mst122,attribute = "iif(theta=-1)",
                         applied_level = "Module-level",
                         which_module = 1,sense = "min",
                         goal = 5)
abs_obj2<-objective_term(x = mst122,attribute = "iif(theta=0)",
                         applied_level = "Module-level",
                         which_module = 1,sense = "min",
                         goal = 5)
abs_obj3<-objective_term(x = mst122,attribute = "iif(theta=1)",
                         applied_level = "Module-level",
                         which_module = 1,sense = "min",
                         goal = 5)
unary_minimax<-minimax_obj(x = mst122,
                           multiple_terms = list(abs_obj1,abs_obj2,abs_obj3),
                           strategy_args = list(mode = "one_dev"))

Case 1:

In addition to the existing requirement limiting TEI items to at most two per pathway, a new specification requiring at least one TEI item per module is introduced, which leads to infeasibility.

mst_tei_new<-test_itemcat_con(x = mst122,attribute = "itemtype",cat_levels = "TEI",
                              operator = ">=",target_num = 1,which_module = 1:5)
new_constraint_list<-constraint_list
new_constraint_list[[length(constraint_list)+1]]<-mst_tei_new
case1_model<-onepanel_spec(x = mst122,
                           constraints = new_constraint_list,
                           objective = unary_minimax)
# \dontrun{
# ### Step 5: Execute assembly via solver
# case1_result<-solve_model(model_spec = case1_model,
#                           solver = "HiGHS",check_feasibility = FALSE,
#                           time_limit = 5*60)
# ### Step 6: Diagnose infeasible model
# 
# case1_singleblock<-check_singleblock_feasibility(model_spec = case1_model,
#                                                  solver = "HiGHS",
#                                                  time_limit = 60)
# case1_TEIcomblock<-check_comblock_feasibility(model_spec = case1_model,
#                                               con_blocks = c(5,28),
#                                               solver = "HiGHS",
#                                               time_limit = 60)
# case1_resolve<-solve_with_slack(model_spec = case1_model,
#                                 cat_penalty = 100,quant_penalty = 10,
#                                 solver = "HiGHS",time_limit = 5*60)
# }
Case 1: Infeasibility check for each specification
Requirement Attribute Type Application_Level Operator Num_Constraints Source Status
Item count from TEI itemtype Categorical Pathway-level (max) 4 Constraint OPTIMAL
Sum of time time Quantitative Pathway-level (min) 4 Constraint OPTIMAL
Sum of time time Quantitative Pathway-level (max) 4 Constraint OPTIMAL
Sum of iif(theta=-1.15) iif(theta=-1.15) Quantitative Pathway-level (min) 2 Constraint OPTIMAL
Sum of iif(theta=-0.67) iif(theta=-0.67) Quantitative Pathway-level (min) 2 Constraint OPTIMAL
Sum of iif(theta=-0.32) iif(theta=-0.32) Quantitative Pathway-level (min) 2 Constraint OPTIMAL
Sum of iif(theta=0.32) iif(theta=0.32) Quantitative Pathway-level (min) 2 Constraint OPTIMAL
Sum of iif(theta=0.67) iif(theta=0.67) Quantitative Pathway-level (min) 2 Constraint OPTIMAL
Sum of iif(theta=1.15) iif(theta=1.15) Quantitative Pathway-level (min) 2 Constraint OPTIMAL
Stimulus count from text-based/graphic-based stimtype Categorical Module-level (exact) 2 Constraint OPTIMAL
Stimulus count from text-based stimtype Categorical Module-level (exact) 2 Constraint OPTIMAL
Stimulus count from graphic-based stimtype Categorical Module-level (exact) 2 Constraint OPTIMAL
Item count from content 1/content 2/content 3/content 4 content Categorical Module-level (min) 20 Constraint OPTIMAL
Item count from content 1/content 2/content 3/content 4 content Categorical Module-level (max) 20 Constraint OPTIMAL
Item count from dok 1/dok 2/dok 3 dok Categorical Module-level (min) 15 Constraint OPTIMAL
Item count from dok 1/dok 2/dok 3 dok Categorical Module-level (max) 15 Constraint OPTIMAL
Sum of difficulty difficulty Quantitative Module-level (min) 4 Constraint OPTIMAL
Sum of difficulty difficulty Quantitative Module-level (max) 4 Constraint OPTIMAL
Within-stimulus item count Stimulus item membership Logical Module-level (exact) 30 Constraint OPTIMAL
Within-stimulus item count Stimulus item membership Logical Module-level (range) 240 Constraint OPTIMAL
Within-stimulus item count from TEI itemtype Logical Module-level (max) 120 Constraint OPTIMAL
Enemy item exclusion Enemy items membership Logical Pathway-level (max) 40 Constraint OPTIMAL
Quantitative attribute per stimulus stimcomplexity Quantitative Module-level (range) 240 Constraint OPTIMAL
Item count from TEI itemtype Categorical Module-level (min) 5 Constraint OPTIMAL
Core set: (MST structure) Number of items in each module Item_id Categorical Module-level (exact) 5 Constraint OPTIMAL
Core set: (MST structure) RDP IIF Quantitative Module-level (range) 2 Constraint OPTIMAL
Core set: (MST structure) RDP IIF Quantitative Module-level (range) 2 Constraint OPTIMAL
Core set: Item exposure control within a panel Item_id (item-itself set) Logical Panel-level (max) 1000 Constraint OPTIMAL
Core set: Sum of iif(theta=-1) iif(theta=-1) Quantitative Module-level Absolute objective 2 Objective OPTIMAL
Core set: Sum of iif(theta=0) iif(theta=0) Quantitative Module-level Absolute objective 2 Objective OPTIMAL
Core set: Sum of iif(theta=1) iif(theta=1) Quantitative Module-level Absolute objective 2 Objective OPTIMAL

The feasibility check conducted via check_comblock_feasibility() indicates that the combination of “Item count from TEI (pathway-level)” and “Item count from TEI (module-level)” constraints is infeasible, as the two specifications impose incompatible requirements on TEI item allocation.

By applying solve_with_slack() with cat_penalty = 100 and quant_penalty = 10, the previously infeasible model becomes solvable under a relaxed formulation. Using HiGHS, the optimal solution to the relaxed model was obtained in 6.71 seconds. The results indicate that the “Item count from TEI (module-level)” constraint was relaxed to restore feasibility: no TEI items were assigned to the S2E, S2H, S3E, or S3H modules.

Case 2:

The existing requirements include: the TIF values must be at least 10 at \(\theta = (-1.15,-0.67,-0.32)\) for the easier pathways (S1R–S2E–S3E and S1R–S2H–S3E) and the TIF values must be at least 10 at \(\theta = (0.32,0.67,1.15)\) for the harder pathways (S1R–S2E–S3H and S1R–S2H–S3H). If the required minimum TIF value is increased from 10 to 30, the model becomes infeasible. This infeasibility arises because the specified target TIF levels exceed what can be achieved given the characteristics and information capacity of the available item pool.

new_mst_tif_low1<-test_itemquant_con(x = mst122,
                                     attribute = "iif(theta=-1.15)",
                                     operator = ">=",
                                     target_value = 30,
                                     which_pathway = 1:2)
new_mst_tif_low2<-test_itemquant_con(x = mst122,
                                     attribute = "iif(theta=-0.67)",
                                     operator = ">=",
                                     target_value = 30,
                                     which_pathway = 1:2)
new_mst_tif_low3<-test_itemquant_con(x = mst122,
                                     attribute = "iif(theta=-0.32)",
                                     operator = ">=",
                                     target_value = 30,
                                     which_pathway = 1:2)
new_mst_tif_high1<-test_itemquant_con(x = mst122,
                                      attribute = "iif(theta=0.32)",
                                      operator = ">=",
                                      target_value = 30,
                                      which_pathway = 3:4)
new_mst_tif_high2<-test_itemquant_con(x = mst122,
                                      attribute = "iif(theta=0.67)",
                                      operator = ">=",
                                      target_value = 30,
                                      which_pathway = 3:4)
new_mst_tif_high3<-test_itemquant_con(x = mst122,
                                      attribute = "iif(theta=1.15)",
                                      operator = ">=",
                                      target_value = 30,
                                      which_pathway = 3:4)

new_constraint_list<-list(mst_structure,
                          mst_noreuse,
                          mst_tei,mst_time,
                          new_mst_tif_low1,new_mst_tif_low2,new_mst_tif_low3,
                          new_mst_tif_high1,new_mst_tif_high2,new_mst_tif_high3,
                          mst_stimtype_s1,mst_stimtype_s2,mst_stimtype_s3,
                          mst_content,mst_dok,mst_meandiff,
                          mst_stimitem_s1,mst_stimitem_s2_s3,
                          mst_stimitemtype,
                          mst_noenemy,
                          mst_stimcomplexity)
case2_model<-onepanel_spec(x = mst122,
                           constraints = new_constraint_list,
                           objective = unary_minimax)
# \dontrun{
# ### Step 5: Execute assembly via solver
# case2_result<-solve_model(model_spec = case2_model,
#                           solver = "HiGHS",check_feasibility = FALSE,
#                           time_limit = 5*60)
# ### Step 6: Diagnose infeasible model
# case2_singleblock<-check_singleblock_feasibility(model_spec = case2_model,
#                                                  solver = "HiGHS",
#                                                  time_limit = 60)
#}
Case 2: Infeasibility check for each specification
Requirement Attribute Type Application_Level Operator Num_Constraints Source Status
Item count from TEI itemtype Categorical Pathway-level (max) 4 Constraint OPTIMAL
Sum of time time Quantitative Pathway-level (min) 4 Constraint OPTIMAL
Sum of time time Quantitative Pathway-level (max) 4 Constraint OPTIMAL
Sum of iif(theta=-1.15) iif(theta=-1.15) Quantitative Pathway-level (min) 2 Constraint INFEASIBLE
Sum of iif(theta=-0.67) iif(theta=-0.67) Quantitative Pathway-level (min) 2 Constraint INFEASIBLE
Sum of iif(theta=-0.32) iif(theta=-0.32) Quantitative Pathway-level (min) 2 Constraint INFEASIBLE
Sum of iif(theta=0.32) iif(theta=0.32) Quantitative Pathway-level (min) 2 Constraint INFEASIBLE
Sum of iif(theta=0.67) iif(theta=0.67) Quantitative Pathway-level (min) 2 Constraint INFEASIBLE
Sum of iif(theta=1.15) iif(theta=1.15) Quantitative Pathway-level (min) 2 Constraint INFEASIBLE
Stimulus count from text-based/graphic-based stimtype Categorical Module-level (exact) 2 Constraint OPTIMAL
Stimulus count from text-based stimtype Categorical Module-level (exact) 2 Constraint OPTIMAL
Stimulus count from graphic-based stimtype Categorical Module-level (exact) 2 Constraint OPTIMAL
Item count from content 1/content 2/content 3/content 4 content Categorical Module-level (min) 20 Constraint OPTIMAL
Item count from content 1/content 2/content 3/content 4 content Categorical Module-level (max) 20 Constraint OPTIMAL
Item count from dok 1/dok 2/dok 3 dok Categorical Module-level (min) 15 Constraint OPTIMAL
Item count from dok 1/dok 2/dok 3 dok Categorical Module-level (max) 15 Constraint OPTIMAL
Sum of difficulty difficulty Quantitative Module-level (min) 4 Constraint OPTIMAL
Sum of difficulty difficulty Quantitative Module-level (max) 4 Constraint OPTIMAL
Within-stimulus item count Stimulus item membership Logical Module-level (exact) 30 Constraint OPTIMAL
Within-stimulus item count Stimulus item membership Logical Module-level (range) 240 Constraint OPTIMAL
Within-stimulus item count from TEI itemtype Logical Module-level (max) 120 Constraint OPTIMAL
Enemy item exclusion Enemy items membership Logical Pathway-level (max) 40 Constraint OPTIMAL
Quantitative attribute per stimulus stimcomplexity Quantitative Module-level (range) 240 Constraint OPTIMAL
Core set: (MST structure) Number of items in each module Item_id Categorical Module-level (exact) 5 Constraint OPTIMAL
Core set: (MST structure) RDP IIF Quantitative Module-level (range) 2 Constraint OPTIMAL
Core set: (MST structure) RDP IIF Quantitative Module-level (range) 2 Constraint OPTIMAL
Core set: Item exposure control within a panel Item_id (item-itself set) Logical Panel-level (max) 1000 Constraint OPTIMAL
Core set: Sum of iif(theta=-1) iif(theta=-1) Quantitative Module-level Absolute objective 2 Objective OPTIMAL
Core set: Sum of iif(theta=0) iif(theta=0) Quantitative Module-level Absolute objective 2 Objective OPTIMAL
Core set: Sum of iif(theta=1) iif(theta=1) Quantitative Module-level Absolute objective 2 Objective OPTIMAL

By applying solve_with_slack() with cat_penalty = 100 and quant_penalty = 10, the previously infeasible model becomes solvable under a relaxed formulation. Using HiGHS, the optimal solution to the relaxed model was obtained in 3.46 seconds. The results show that the minimum TIF requirement of 30 cannot be satisfied. The realized TIF values range from 13.5 to 20.8, indicating that the information constraints are binding in a way that prevents simultaneous satisfaction of other requirements. In addition, the absolute deviation between the realized mean difficulty levels for S2E and S3E and the target mean difficulty level (-0.5) exceeds 0.05.

If the mean difficulty requirement must be strictly enforced (i.e., not relaxed), test developers may consider lowering the minimum TIF threshold to a value between 10 (for which the original model yields an optimal solution) and 13 (the lowest realized TIF under the relaxed solution). The appropriate adjustment can be systematically determined using the check data frame returned by solve_model() or solve_with_slack(),which provides detailed diagnostic information about constraint satisfaction and deviations.

When the minimum TIF threshold is set to 13.5, the model yields an optimal solution (42.01 seconds using HiGHS). However, obtaining an optimal solution does not necessarily guarantee adequate measurement precision. Therefore, after assembling panels—whether optimal or merely feasible—it is recommended to conduct additional evaluation, such as analytical precision assessment or simulation studies, to verify the measurement performance of the assembled panels.

Case 3

Five parallel MST panels are assembled using the same set of specifications for each panel. Two solution-level requirements are imposed: (1) stimuli must be unique across panels, and (2) each item may be selected at most twice. Under these specifications, the model becomes infeasible (HiGHS returns Infeasible in 0.39 seconds).

onepanel<-onepanel_spec(x = mst122,
                        constraints = constraint_list,
                        objective = unary_minimax)
num_panels<-5
mst_unique_stim<-solution_stimcount_con(x = mst122,operator = "=",
                                        target_num = 4*num_panels)
case3_model<-multipanel_spec(x = mst122,panel_model = onepanel,
                             solution_con = list(mst_unique_stim),
                             num_panels = num_panels,global_max_use = 2)
# \dontrun{
# ### Step 5: Execute assembly via solver
# case3_result<-solve_model(model_spec = case3_model,
#                           solver = "HiGHS",check_feasibility = FALSE,
#                           time_limit = 60*5)
#}

(1) Adjusting the number of panels

Keeping all other specifications unchanged, the number of parallel panels is reduced from five to four. Under this revised setting, feasible solutions are obtained within four minutes. Constraint satisfaction is verified using the check data frame returned by solve_model(), which shows that all constraints are satisfied. The most binding constraint corresponds to the absolute objective at \(\theta = 1\) for the routing modules.

(2) Possible approaches to restore feasibility

Suggestions

When an mstATA_model is infeasible, several diagnostic and remedial strategies can be employed.

Assess Item Pool Sufficiency

Evaluate whether the item pool contains adequate items with the required content, statistical, or categorical attributes. Examine attribute distributions to detect shortages or strong correlations that may make certain specifications impossible to satisfy. When deficiencies are identified, the item pool should be replenished or the maximum usage of rare items increased. The more closely the test specifications align with the structure of the item bank, the higher the likelihood of feasibility.

Check Residual Variables for A Simplified MST Design

Reducing the number of panels or modules per panel lowers the model’s complexity and increases the chance of feasibility—especially when item resources are limited. In the mstATA package, feasible models can produce residual variables that quantify deviations between achieved and required constraint values. Solving a simplified problem and inspecting the residual values helps identify which constraints are most restrictive, guiding targeted adjustments to specifications or constraint bounds.

Replace Equality Constraints with Small Ranges

Constraints expressed as strict equalities (e.g., exact averages or totals) drastically reduce the feasible region and often cause infeasibility. Wherever possible, replace equality constraints with narrow inequality bounds (e.g., \(\pm 1\) or \(\pm 2\) items). The test length constraint is usually the only exception that must remain exact.

Check for Correctness and Consistency

Ensure that each constraint is correctly formulated and defines a feasible region. In the mstATA package, the check_singleblock_feasibility() function is used to check the feasibility for each specification. Specifically, a core set of constraints (objective-related constraints and essential MST structure constraints) is always included. Each remaining constraint block generated by each specification is then tested individually by solving a reduced model that contains the core constraints plus exactly one additional constraint block. If the model is feasible, examine the residual variables to ensure that each constraint functions as intended and that constraints imposed at different hierarchical levels—module, pathway, panel, and solution—are logically consistent; specifically, for the same specification, the realized constraint value at a lower level should not exceed that at a higher level.

Perform Incremental Constraint Testing

Once individual specifications have been verified, the next step is to identify which specific combinations of specifications lead to infeasibility. This process begins by establishing a priority ordering of specifications based on their relative importance, as determined by subject matter experts and test development teams. Two complementary diagnostic strategies—additive and subtractive methods [@spaccapanico2020automated]—can then be applied to isolate problematic constraints.

In the additive approach (ADD), the analysis starts with an unconstrained model that includes only a core set of requirements, such as objective-related constraints and essential MST structural constraints. Additional constraints are then introduced sequentially, in order of decreasing importance, until infeasibility is encountered. In the subtractive approach (SUB), the process begins with the full model containing all constraints, and constraints are removed one at a time, starting with the least important, until feasibility is restored.

In the mstATA package, both approaches can be implemented via check_comblock_feasibility() function, which evaluates the feasibility of models constructed from multiple specifications.

Reformulate Using Goal Programming and Slack Variables

The solve_with_slack function is designed to recover feasibility for an infeasible mstATA_model by selectively relaxing non-core constraint blocks through the introduction of slack variables with user-specified penalties. Core constraint blocks include objective-related constraints, essential MST structure constraints (e.g., module- and pathway-level item count constraints, optional TIF constraints for routing decision points), logical constraints (such as enemy relationships, item–stimulus grouping relationships, and within-panel item exposure control), as well as equality constraints (i.e., constraints with = operators). All other constraint blocks are treated as non-core and are eligible for relaxation.

For each relaxable constraint block, a single continuous slack variable is introduced to permit controlled violations of the corresponding specifications. Slack variables for categorical constraints receive higher penalties than those for quantitative constraints, reflecting their greater importance in the test assembly process. When an mstATA_model assembles multiple panels and security is the top priority, the penalty for reusing items across panels should be increased accordingly. In all other cases, the penalty hierarchy should assign the highest weight to categorical requirements, followed by quantitative requirements, with the lowest penalty applied to across panel item reuse.

The reformulated objective function consists of two components: (a) the original objective function and (b) a weighted penalty term that captures the magnitude of violations in non-core constraint blocks. When the original objective is a maximization problem, it is multiplied by −1 so that the reformulated objective remains a minimization problem. The new objective direction is to minimize the reformulated objective function.

This slack-based formulation enables the model to satisfy all core specifications while restoring feasibility by allowing limited violations of lower-priority requirements. As discussed in the Translate to Linear Model section, the number of constraint rows generated by a specification may depend on factors such as the number of specified categories and the number of modules or pathways involved. Accordingly, users may control the size and granularity of constraint blocks by invoking the same constraint-generating function multiple times to construct a specification.