Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 115 additions & 50 deletions src/libpapilo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,45 @@ check_run( Func func, const char* message )
std::terminate();
}

// Helper function to run parameter operations with proper exception handling
template <typename Func>
libpapilo_param_result_t
run_param_op( Func func )
{
try
{
func();
return LIBPAPILO_PARAM_OK;
}
catch( const std::invalid_argument& e )
{
// ParameterSet uses std::invalid_argument for both missing keys and
// parse errors. Distinguish based on exception message.
const std::string msg( e.what() );
if( msg.find( "could not parse" ) != std::string::npos )
return LIBPAPILO_PARAM_INVALID_VALUE;
return LIBPAPILO_PARAM_NOT_FOUND;
}
catch( const std::domain_error& )
{
return LIBPAPILO_PARAM_WRONG_TYPE;
}
catch( const std::out_of_range& )
{
return LIBPAPILO_PARAM_INVALID_VALUE;
}
catch( const std::exception& )
{
// Catch any other standard exceptions to prevent crossing C boundary
return LIBPAPILO_PARAM_INVALID_VALUE;
}
catch( ... )
{
// Catch-all for non-standard exceptions
return LIBPAPILO_PARAM_INVALID_VALUE;
}
}

// Helper function to convert PresolveStatus to libpapilo_presolve_status_t
static libpapilo_presolve_status_t
convert_presolve_status( PresolveStatus status )
Expand Down Expand Up @@ -1320,61 +1359,75 @@ extern "C"
presolve->presolve.getPresolveOptions() = options->options;
}

libpapilo_presolve_status_t
libpapilo_presolve_apply_simple( libpapilo_presolve_t* presolve,
libpapilo_problem_t* problem )
libpapilo_param_result_t
libpapilo_presolve_set_param_bool( libpapilo_presolve_t* presolve,
const char* key, int value )
{
check_presolve_ptr( presolve );
check_problem_ptr( problem );
custom_assert( key != nullptr, "key pointer is null" );

return check_run(
return run_param_op(
[&]()
{
PresolveResult<double> result =
presolve->presolve.apply( problem->problem );
return convert_presolve_status( result.status );
},
"Failed to apply presolve" );
ParameterSet paramSet = presolve->presolve.getParameters();
paramSet.setParameter( key, static_cast<bool>( value ) );
} );
}

void
libpapilo_presolve_apply_reductions( libpapilo_presolve_t* presolve,
int round,
libpapilo_reductions_t* reductions,
libpapilo_problem_update_t* update,
int* num_rounds, int* num_changes )
libpapilo_param_result_t
libpapilo_presolve_set_param_int( libpapilo_presolve_t* presolve,
const char* key, int value )
{
check_presolve_ptr( presolve );
check_reductions_ptr( reductions );
check_problem_update_ptr( update );
custom_assert( num_rounds != nullptr, "num_rounds pointer is null" );
custom_assert( num_changes != nullptr, "num_changes pointer is null" );
custom_assert( key != nullptr, "key pointer is null" );

check_run(
return run_param_op(
[&]()
{
std::pair<int, int> result = presolve->presolve.applyReductions(
round, reductions->reductions, update->update );
*num_rounds = result.first;
*num_changes = result.second;
},
"Failed to apply reductions" );
ParameterSet paramSet = presolve->presolve.getParameters();
paramSet.setParameter( key, value );
} );
}

libpapilo_param_result_t
libpapilo_presolve_set_param_double( libpapilo_presolve_t* presolve,
const char* key, double value )
{
check_presolve_ptr( presolve );
custom_assert( key != nullptr, "key pointer is null" );

return run_param_op(
[&]()
{
ParameterSet paramSet = presolve->presolve.getParameters();
paramSet.setParameter( key, value );
} );
}

libpapilo_param_result_t
libpapilo_presolve_parse_param( libpapilo_presolve_t* presolve,
const char* key, const char* value )
{
check_presolve_ptr( presolve );
custom_assert( key != nullptr, "key pointer is null" );
custom_assert( value != nullptr, "value pointer is null" );

return run_param_op(
[&]()
{
ParameterSet paramSet = presolve->presolve.getParameters();
paramSet.parseParameter( key, value );
} );
}

/* High-level presolve function for backward compatibility */
libpapilo_presolve_status_t
libpapilo_presolve_apply( libpapilo_problem_t* problem,
const libpapilo_presolve_options_t* options,
const libpapilo_message_t* message,
libpapilo_reductions_t** reductions_out,
libpapilo_postsolve_storage_t** postsolve_out,
libpapilo_statistics_t** statistics_out )
libpapilo_presolve_apply_full( libpapilo_presolve_t* presolve,
libpapilo_problem_t* problem,
libpapilo_postsolve_storage_t** postsolve_out,
libpapilo_statistics_t** statistics_out )
{
check_presolve_ptr( presolve );
check_problem_ptr( problem );
check_presolve_options_ptr( options );
check_message_ptr( message );
custom_assert( reductions_out != nullptr,
"reductions_out pointer is null" );
custom_assert( postsolve_out != nullptr,
"postsolve_out pointer is null" );
custom_assert( statistics_out != nullptr,
Expand All @@ -1383,19 +1436,13 @@ extern "C"
return check_run(
[&]()
{
// Create presolve object
auto* presolve = libpapilo_presolve_create( message );
libpapilo_presolve_add_default_presolvers( presolve );
libpapilo_presolve_set_options( presolve, options );

// Execute presolve
PresolveResult<double> result =
presolve->presolve.apply( problem->problem );

// Create output objects
auto* postsolve_storage = new libpapilo_postsolve_storage_t(
std::move( result.postsolve ) );
auto* reductions = new libpapilo_reductions_t();
auto* stats = new libpapilo_statistics_t();

// Copy overall statistics
Expand All @@ -1421,20 +1468,38 @@ extern "C"
stats->presolver_stats.push_back( stat );
}

// Set output parameters
*reductions_out = reductions;
*postsolve_out = postsolve_storage;
*statistics_out = stats;

// Clean up presolve object
libpapilo_presolve_free( presolve );

// Convert status
return convert_presolve_status( result.status );
},
"Failed to apply presolve" );
}

void
libpapilo_presolve_apply_reductions( libpapilo_presolve_t* presolve,
int round,
libpapilo_reductions_t* reductions,
libpapilo_problem_update_t* update,
int* num_rounds, int* num_changes )
{
check_presolve_ptr( presolve );
check_reductions_ptr( reductions );
check_problem_update_ptr( update );
custom_assert( num_rounds != nullptr, "num_rounds pointer is null" );
custom_assert( num_changes != nullptr, "num_changes pointer is null" );

check_run(
[&]()
{
std::pair<int, int> result = presolve->presolve.applyReductions(
round, reductions->reductions, update->update );
*num_rounds = result.first;
*num_changes = result.second;
},
"Failed to apply reductions" );
}

libpapilo_reductions_t*
libpapilo_reductions_create()
{
Expand Down
108 changes: 97 additions & 11 deletions src/libpapilo.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,21 @@ extern "C"
LIBPAPILO_ROW_REDUCTION_PARALLEL_ROW = -16
} libpapilo_row_reduction_t;

/**
* Result of parameter setting operations.
*/
typedef enum
{
/** Parameter was successfully set */
LIBPAPILO_PARAM_OK = 0,
/** Parameter key was not found */
LIBPAPILO_PARAM_NOT_FOUND = 1,
/** Value type does not match parameter type */
LIBPAPILO_PARAM_WRONG_TYPE = 2,
/** Value is out of valid range or could not be parsed */
LIBPAPILO_PARAM_INVALID_VALUE = 3
} libpapilo_param_result_t;

/** Reduction info structure */
typedef struct
{
Expand Down Expand Up @@ -578,9 +593,89 @@ extern "C"
libpapilo_presolve_t* presolve,
const libpapilo_presolve_options_t* options );

/**
* Set a boolean parameter on the presolve object.
*
* This function allows configuring presolvers and presolve options using
* parameter keys. Common parameter keys include:
* - "parallelcols.enabled": Enable/disable parallel column detection
* - "parallelrows.enabled": Enable/disable parallel row detection
* - "probing.enabled": Enable/disable probing
* - "propagation.enabled": Enable/disable propagation
* - etc.
*
* Note: Presolvers must be added (via add_default_presolvers) before their
* parameters can be set.
*
* @param presolve The presolve object
* @param key The parameter key (e.g., "parallelcols.enabled")
* @param value The boolean value to set
* @return LIBPAPILO_PARAM_OK on success, or an error code
*/
LIBPAPILO_EXPORT libpapilo_param_result_t
libpapilo_presolve_set_param_bool( libpapilo_presolve_t* presolve,
const char* key, int value );

/**
* Set an integer parameter on the presolve object.
*
* @param presolve The presolve object
* @param key The parameter key
* @param value The integer value to set
* @return LIBPAPILO_PARAM_OK on success, or an error code
*/
LIBPAPILO_EXPORT libpapilo_param_result_t
libpapilo_presolve_set_param_int( libpapilo_presolve_t* presolve,
const char* key, int value );

/**
* Set a double parameter on the presolve object.
*
* @param presolve The presolve object
* @param key The parameter key
* @param value The double value to set
* @return LIBPAPILO_PARAM_OK on success, or an error code
*/
LIBPAPILO_EXPORT libpapilo_param_result_t
libpapilo_presolve_set_param_double( libpapilo_presolve_t* presolve,
const char* key, double value );

/**
* Set a parameter by parsing a string value.
*
* This function parses the string value according to the parameter's type.
* For boolean parameters, the value must be "0" or "1". Other values will
* cause a parse error and return LIBPAPILO_PARAM_INVALID_VALUE.
*
* @param presolve The presolve object
* @param key The parameter key
* @param value The string value to parse
* @return LIBPAPILO_PARAM_OK on success, LIBPAPILO_PARAM_NOT_FOUND if
* the key does not exist, or LIBPAPILO_PARAM_INVALID_VALUE if
* the value could not be parsed
*/
LIBPAPILO_EXPORT libpapilo_param_result_t
libpapilo_presolve_parse_param( libpapilo_presolve_t* presolve,
const char* key, const char* value );

/**
* Apply presolve using the given presolve object with full output.
*
* This is the main presolve function. Create a presolve object, add
* presolvers, optionally configure parameters, then call this function.
*
* @param presolve The configured presolve object (must have presolvers
* added)
* @param problem The problem to presolve (will be modified in-place)
* @param postsolve_out Output: postsolve storage for solution recovery
* @param statistics_out Output: statistics about the presolve process
* @return Presolve status indicating the result
*/
LIBPAPILO_EXPORT libpapilo_presolve_status_t
libpapilo_presolve_apply_simple( libpapilo_presolve_t* presolve,
libpapilo_problem_t* problem );
libpapilo_presolve_apply_full( libpapilo_presolve_t* presolve,
libpapilo_problem_t* problem,
libpapilo_postsolve_storage_t** postsolve_out,
libpapilo_statistics_t** statistics_out );

LIBPAPILO_EXPORT void
libpapilo_presolve_apply_reductions( libpapilo_presolve_t* presolve,
Expand Down Expand Up @@ -633,15 +728,6 @@ extern "C"
libpapilo_presolve_options_get_randomseed(
const libpapilo_presolve_options_t* options );

/* Main presolve function */
LIBPAPILO_EXPORT libpapilo_presolve_status_t
libpapilo_presolve_apply( libpapilo_problem_t* problem,
const libpapilo_presolve_options_t* options,
const libpapilo_message_t* message,
libpapilo_reductions_t** reductions,
libpapilo_postsolve_storage_t** postsolve,
libpapilo_statistics_t** statistics );

/* Reductions access API */
LIBPAPILO_EXPORT libpapilo_reductions_t*
libpapilo_reductions_create();
Expand Down
11 changes: 11 additions & 0 deletions test/libpapilo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(LIBPAPILO_TESTS
GetterAPIsTest.cpp
PresolverStatsTest.cpp
ParallelColDetectionTest.cpp
ParameterTest.cpp
# Add more test files as needed
)

Expand Down Expand Up @@ -100,6 +101,16 @@ set(LIBPAPILO_TEST_NAMES
"parallel_col_detection_int_merge_failed_hole"
"parallel_col_detection_obj_not_parallel"
"parallel_col_detection_multiple_parallel_columns"

# ParameterTest.cpp
"set-param-bool-disables-presolver"
"set-param-bool-returns-not-found-for-unknown-key"
"set-param-int-sets-integer-parameter"
"set-param-double-sets-double-parameter"
"parse-param-parses-string-value"
"parse-param-returns-invalid-value-for-parse-error"
"set-param-bool-returns-not-found-before-presolvers-added"
"disabling-parallelcols-prevents-parallel-column-detection"
)

# Register test targets for each test file
Expand Down
Loading
Loading