def is_violation(model_data, config, solve_data):

    nom_value = model_data.master_nominal_scenario_value
    denom = float(max(1, abs(nom_value)))
    tol = config.robust_feasibility_tolerance
    active_objective = next(
        model_data.separation_model.component_data_objects(Objective,
                                                           active=True))

    if value(active_objective) / denom > tol:

        violating_param_realization = list(p.value for p in list(
            model_data.separation_model.util.uncertain_param_vars.values()))
        list_of_violations = get_all_sep_objective_values(
            model_data=model_data, config=config)
        solve_data.violating_param_realization = violating_param_realization
        solve_data.list_of_scaled_violations = [
            l / denom for l in list_of_violations
        ]
        solve_data.found_violation = True
        return True
    else:
        violating_param_realization = list(p.value for p in list(
            model_data.separation_model.util.uncertain_param_vars.values()))
        list_of_violations = get_all_sep_objective_values(
            model_data=model_data, config=config)
        solve_data.violating_param_realization = violating_param_realization
        solve_data.list_of_scaled_violations = [
            l / denom for l in list_of_violations
        ]
        solve_data.found_violation = False
        return False
def get_all_sep_objective_values(model_data, config):
    """
    Returns all violations from separation
    """
    list_of_violations_across_objectives = []
    for o in model_data.separation_model.util.separation_objectives:
        try:
            list_of_violations_across_objectives.append(value(o.expr))
        except:
            for v in model_data.separation_model.util.first_stage_variables:
                config.progress_logger.info(v.name + " " + str(v.value))
            for v in model_data.separation_model.util.second_stage_variables:
                config.progress_logger.info(v.name + " " + str(v.value))
            raise ArithmeticError(
                "Objective function " + str(o) +
                " led to a math domain error. "
                "Does this objective (meaning, its parent performance constraint) "
                "contain log(x)  or 1/x functions or others with tricky domains?"
            )
    return list_of_violations_across_objectives
def solve_separation_problem(model_data, config):

    # Timing variables
    global_solve_time = 0
    local_solve_time = 0

    # List of objective functions
    objectives_map = model_data.separation_model.util.map_obj_to_constr
    constraint_map_to_master = model_data.separation_model.util.map_new_constraint_list_to_original_con

    # Add additional or remaining separation objectives to the dict
    # (those either not assigned an explicit priority or those added by Pyros for ssv bounds)
    config_sep_priority_dict = config.separation_priority_order
    actual_sep_priority_dict = ComponentMap()
    for perf_con in model_data.separation_model.util.performance_constraints:
        actual_sep_priority_dict[perf_con] = config_sep_priority_dict.get(
            perf_con.name, 0)

    # "Bin" the objectives based on priorities
    sorted_unique_priorities = sorted(list(
        set(actual_sep_priority_dict.values())),
                                      reverse=True)
    set_of_deterministic_constraints = model_data.separation_model.util.deterministic_constraints
    if hasattr(model_data.separation_model, "epigraph_constr"):
        set_of_deterministic_constraints.add(
            model_data.separation_model.epigraph_constr)
    for is_global in (False, True):
        solver = config.global_solver if \
            (is_global or config.bypass_local_separation) else config.local_solver
        solve_data_list = []

        for val in sorted_unique_priorities:
            # Descending ordered by value
            # The list of performance constraints with this priority
            perf_constraints = [
                constr_name
                for constr_name, priority in actual_sep_priority_dict.items()
                if priority == val
            ]
            for perf_con in perf_constraints:
                #config.progress_logger.info("Separating constraint " + str(perf_con))
                try:
                    separation_obj = objectives_map[perf_con]
                except:
                    raise ValueError(
                        "Error in mapping separation objective to its master constraint form."
                    )
                separation_obj.activate()

                if perf_con in set_of_deterministic_constraints:
                    nom_constraint = perf_con
                else:
                    nom_constraint = constraint_map_to_master[perf_con]

                try:
                    model_data.master_nominal_scenario_value = value(
                        model_data.master_nominal_scenario.find_component(
                            nom_constraint))
                except:
                    raise ValueError(
                        "Unable to access nominal scenario value for the constraint "
                        + str(nom_constraint))

                if config.uncertainty_set.geometry == Geometry.DISCRETE_SCENARIOS:
                    solve_data_list.append(
                        discrete_solve(model_data=model_data,
                                       config=config,
                                       solver=solver,
                                       is_global=is_global))
                    if all(s.termination_condition in globally_acceptable for
                           sep_soln_list in solve_data_list for s in sep_soln_list) or \
                            (is_global == False and all(s.termination_condition in locally_acceptable for
                                                        sep_soln_list in solve_data_list for s in sep_soln_list)):
                        exit_separation_loop = False
                    else:
                        exit_separation_loop = True
                else:
                    solve_data = SeparationResult()
                    exit_separation_loop = solver_call_separation(
                        model_data=model_data,
                        config=config,
                        solver=solver,
                        solve_data=solve_data,
                        is_global=is_global)
                    solve_data_list.append([solve_data])

                # === Keep track of total solve times
                if is_global or config.bypass_local_separation:
                    if config.uncertainty_set.geometry == Geometry.DISCRETE_SCENARIOS:
                        for sublist in solve_data_list:
                            for s in sublist:
                                global_solve_time += get_time_from_solver(
                                    s.results)
                    else:
                        global_solve_time += get_time_from_solver(
                            solve_data.results)
                else:
                    if config.uncertainty_set.geometry == Geometry.DISCRETE_SCENARIOS:
                        for sublist in solve_data_list:
                            for s in sublist:
                                local_solve_time += get_time_from_solver(
                                    s.results)
                    else:
                        local_solve_time += get_time_from_solver(
                            solve_data.results)

                # === Terminate for timing
                if exit_separation_loop:
                    return solve_data_list, [], [], is_global, local_solve_time, global_solve_time
                separation_obj.deactivate()

        # Do we return?
        # If their are multiple violations in this bucket, pick the worst-case
        idx_i, idx_j = get_index_of_max_violation(
            model_data=model_data,
            config=config,
            solve_data_list=solve_data_list)

        violating_realizations = [
            v
            for v in solve_data_list[idx_i][idx_j].violating_param_realization
        ]
        violations = solve_data_list[idx_i][idx_j].list_of_scaled_violations

        if any(s.found_violation for solve_list in solve_data_list
               for s in solve_list):
            #config.progress_logger.info(
            #	"Violation found in constraint %s with realization %s" % (
            #	list(objectives_map.keys())[idx_i], violating_realizations))
            return solve_data_list, violating_realizations, violations, is_global, local_solve_time, global_solve_time

    return solve_data_list, [], [], is_global, local_solve_time, global_solve_time