Flat White Vitriol - An incremental solver protocol for combinatorial solving using shared objects

refactor: use virtual values so no allocation is ever required

dekker.one 0bce1096 b79aa7c9

verified
+444 -300
+12 -2
c/ipdos_solver_template.c
··· 26 26 // 27 27 // Note that this is only valid to be called with options named by 28 28 // `ipdos_option_list`. 29 - IpdosValue ipdos_solver_option_get(const IpdosSolver *solver, const char *ident); 29 + IpdosValueRef ipdos_solver_option_get(const IpdosSolver *solver, const char *ident); 30 30 31 31 // Set the current value of an option for the solver. 32 32 // 33 33 // Note that this is only valid to be called with options named by 34 34 // `ipdos_option_list`, and the value must be of the correct type. 35 - bool ipdos_solver_option_set(IpdosSolver *solver, const char *ident, IpdosValue value); 35 + bool ipdos_solver_option_set(IpdosSolver *solver, const char *ident, IpdosValueRef value); 36 36 37 37 // Read an error message from the solver. 38 38 // ··· 42 42 void ipdos_solver_read_error(IpdosSolver *solver, 43 43 void *context, 44 44 void (*read_error)(void *context, const char *error)); 45 + 46 + // Run the solver with the given model 47 + IpdosStatus ipdos_solver_run(IpdosSolver *solver, 48 + IpdosModelRef model, 49 + void *context, 50 + void (*on_solution)(void *context, IpdosSolutionRef solution)); 51 + 52 + // Returns the list of available statistical information that can be requested 53 + // from the solver and its solutions. 54 + IpdosStatisticList ipdos_statistic_list(void);
+173 -79
c/ipdos_types.h
··· 1 1 #ifndef ipdos_types_h 2 2 #define ipdos_types_h 3 3 4 - #include <stdbool> 5 - #include <stdint> 4 + #include <stdbool.h> 5 + #include <stdint.h> 6 6 7 7 // The status returned by `ipdos_solver_run`, indicating whether the solver 8 8 // completed its search. ··· 33 33 } IpdosTypeBase; 34 34 35 35 // Enumerated type used to mark the kind of [`IpdosValue`]. This is used to 36 - // determine which field in the [`IpdosValueContent`] union to access. 36 + // determine which "get" method in [`IpdosValueMethods`] is safe to call. 37 37 typedef enum IpdosValueKind { 38 - // No value is stored. 38 + // No value is available. 39 39 IpdosValueAbsent, 40 - // The value is stored in `decision_index`. 40 + // The value is available using [`IpdosValueMethods::get_decision`]. 41 41 IpdosValueDecision, 42 - // The value is stored in `boolean_value`. 43 - IpdosValueBoolean, 44 - // The value is stored in `integer_value`. 45 - IpdosValueInteger, 46 - // The value is stored in `float_value`. 42 + // The value is available using [`IpdosValueMethods::get_bool`]. 43 + IpdosValueBool, 44 + // The value is available using [`IpdosValueMethods::get_int`]. 45 + IpdosValueInt, 46 + // The value is available using [`IpdosValueMethods::get_float`]. 47 47 IpdosValueFloat, 48 - // The value is stored in `string_value`. 48 + // The value is available using [`IpdosValueMethods::get_string`]. 49 49 IpdosValueString, 50 - // The value is stored in `list_value`. 50 + // Sets of integers are represented using a range list. The number of 51 + // ranges is available using [`IpdosValueMethods::len`], and the ranges can 52 + // be accessed using [`IpdosValueMethods::get_range_int`]. 53 + IpdosValueSetInt, 54 + // Sets of floats are represented using a range list. The number of 55 + // ranges is available using [`IpdosValueMethods::len`], and the ranges can 56 + // be accessed using [`IpdosValueMethods::get_range_float`]. 57 + IpdosValueSetFloat, 58 + // The length of the list can be accessed using 59 + // [`IpdosValueMethods::len`], and elements in the list can be 60 + // accessed using [`IpdosValueMethods::get_element`] 51 61 IpdosValueList, 52 62 } IpdosValueKind; 53 63 ··· 108 118 // cast the pointer back to the original type, e.g. `(MyModel*) ipdos_model`. 109 119 typedef void IpdosModel; 110 120 111 - // The storage of the value content of a [`IpdosValue`]. 112 - typedef union IpdosValueContent { 113 - // Decision index storage 114 - uint64_t decision_index; 115 - // Boolean value storage 116 - bool bool_value; 117 - // Integer value storage 118 - int64_t integer_value; 119 - // Float value storage 120 - double float_value; 121 - // Set of integers value storage 121 + // The type of a value used as an argument or solution assignment. 122 + // 123 + // This type is opaque to the user. Only pointers of this type are ever used. 124 + // 125 + // In implementation of the IPDOS interface, a pointer is generally cast to 126 + // this type, e.g. `(IpdosValue*) my_value`. A similar cast can be used to cast 127 + // the pointer back to the original type, e.g. `(MyValue*) ipdos_value`. 128 + typedef void IpdosValue; 129 + 130 + // A struct containing the function pointers to interact with a 131 + // [`IpdosValue`] 132 + typedef struct IpdosValueMethods { 133 + // Function callback that returns the kind of the value. 134 + enum IpdosValueKind (*kind)(const IpdosValue*); 135 + // Function callback that returns the length of the value. 122 136 // 123 - // The set of integers is represented using a range list. The array of 124 - // `i64` values should be interpreted as a list of inclusive ranges, 125 - // where each range is represented by two values. 137 + // In case [`IpdosValueMethods::kind`] returns 138 + // [`IpdosValueKind::IpdosValueList`], the length is the number of elements 139 + // in the list, accessible using [`IpdosValueMethods::get_element`]. 126 140 // 127 - // Note that the number of `i64` values is stored in the `len` field. Since 128 - // each range is represented by two values, it can be assumed that `len mod 129 - // 2 == 0`. 130 - const int64_t *set_of_int_value; 131 - // Set of floating point value storage 141 + // In case [`IpdosValueMethods::kind`] returns 142 + // [`IpdosValueKind::IpdosValueSetInt`] or 143 + // [`IpdosValueKind::IpdosValueSetFloat`], the length is the number of 144 + // ranges in the set, accessible using 145 + // [`IpdosValueMethods::get_range_int`] or 146 + // [`IpdosValueMethods::get_range_float`]. 132 147 // 133 - // The set of floating point values is represented using a range list. The 134 - // array of `f64` values should be interpreted as a list of inclusive 135 - // ranges, where each range is represented by two values. 148 + // # Panics 136 149 // 137 - // Note that the number of `f64` values is stored in the `len` field. Since 138 - // each range is represented by two values, it can be assumed that `len mod 139 - // 2 == 0`. 140 - const int64_t *set_of_float_value; 141 - // String value storage 150 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 151 + // return [`IpdosValueKind::IpdosValueList`], 152 + // [`IpdosValueKind::IpdosValueSetInt`], or 153 + // [`IpdosValueKind::IpdosValueSetFloat`]. 154 + uint64_t (*len)(const IpdosValue*); 155 + // Function callback that returns the decision variable index contained in 156 + // the value. 142 157 // 143 - // Note that the `string_value` field is intended to be interpreted as a 144 - // C-string. It should be \0 terminated, use UTF-8 encoding, and be valid 145 - // for the lifetime of the value. 146 - const char *string_value; 147 - // List value storage 158 + // # Panics 148 159 // 149 - // Note that the the number of elements in the list is stored in the `len` 150 - // field. 151 - const struct IpdosValue *list_value; 152 - } IpdosValueContent; 153 - 154 - // The value representation used for values assigned to decision variables in 155 - // solution, as constraint/annotation arguments, and option parameters. 156 - typedef struct IpdosValue { 157 - // The kind of the value 160 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 161 + // return [`IpdosValueKind::IpdosValueDecision`]. 162 + uint64_t (*get_decision)(const IpdosValue*); 163 + // Function callback that returns the integer value contained in the value. 158 164 // 159 - // This field is used to determine what field in the `value` union is 160 - // allowed to be accessed. 161 - enum IpdosValueKind kind; 162 - // A field containing the size of the value. 165 + // # Panics 163 166 // 164 - // This field is used when the value is a list, set of float/int, and 165 - // string, to determine the number of elements in the C array type. 166 - uint32_t len; 167 - // The storage of the actual data of the value. 168 - union IpdosValueContent content; 169 - } IpdosValue; 167 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 168 + // return [`IpdosValueKind::IpdosValueInt`]. 169 + int64_t (*get_int)(const IpdosValue*); 170 + // Function callback that returns the floating point value contained in the 171 + // value. 172 + // 173 + // # Panics 174 + // 175 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 176 + // return [`IpdosValueKind::IpdosValueFloat`]. 177 + double (*get_float)(const IpdosValue*); 178 + // Function callback that returns the string pointer value contained in the 179 + // value. 180 + // 181 + // # Panics 182 + // 183 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 184 + // return [`IpdosValueKind::IpdosValueString`]. 185 + const char *(*get_string)(const IpdosValue*); 186 + // Function callback that returns the Boolean value contained in the value. 187 + // 188 + // # Panics 189 + // 190 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 191 + // return [`IpdosValueKind::IpdosValueBool`]. 192 + bool (*get_bool)(const IpdosValue*); 193 + // Function callback that returns a range from a range list representing 194 + // the integer set contained in the value. 195 + // 196 + // # Panics 197 + // 198 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 199 + // return [`IpdosValueKind::IpdosValueSetInt`]. 200 + int64_t ((*get_range_int)(const IpdosValue*, uint64_t index))[2]; 201 + // Function callback that returns a range from a range list representing 202 + // the floating point set contained in the value. 203 + // 204 + // # Panics 205 + // 206 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 207 + // return [`IpdosValueKind::IpdosValueSetFloat`]. 208 + double ((*get_range_float)(const IpdosValue*, uint64_t index))[2]; 209 + // Function callback that returns an element from the list contained in the 210 + // value. 211 + // 212 + // # Panics 213 + // 214 + // This function callback may panic if [`IpdosValueMethods::kind`] does not 215 + // return [`IpdosValueKind::IpdosValueList`]. 216 + struct IpdosValueRef (*get_element)(const IpdosValue*, uint64_t index); 217 + } IpdosValueMethods; 170 218 171 - // A struct containing the function pointers to interact with theIpdosModel` 219 + // Handle for a value 220 + // 221 + // The caller can use the `get_value` function to retrieve the value of the 222 + // used decision variables. 223 + typedef struct IpdosValueRef { 224 + // The data pointer to be the first argument of `get_value`. 225 + const IpdosValue *data; 226 + // Reference to the structure containing the function callbacks used to 227 + // interact with the value. 228 + const struct IpdosValueMethods *methods; 229 + } IpdosValueRef; 230 + 231 + // A struct containing the function pointers to interact with the 232 + // [`IpdosModel`] 172 233 typedef struct IpdosModelMethods { 173 234 // Returns the current number of model layers currently contained in the 174 235 // model. ··· 216 277 // 217 278 // Note that the the value might have [`IpdosValueKind::IpdosValueAbsent`] 218 279 // if the decision variable does not have an explicit domain. 219 - struct IpdosValue (*decision_domain)(const IpdosModel *model, uint64_t decision); 280 + struct IpdosValueRef (*decision_domain)(const IpdosModel *model, uint64_t decision); 220 281 // Retrieve the name of a decision variable, if it exists. 221 282 // 222 283 // Note that names are only available for debugging purposes. Decisions are ··· 252 313 // 253 314 // The `index` argument must be less than the value returned by 254 315 // `constraint_argument_len` for the given constraint. 255 - struct IpdosValue (*constraint_argument)(const IpdosModel *model, 256 - uint64_t constraint, 257 - uint64_t index); 316 + struct IpdosValueRef (*constraint_argument)(const IpdosModel *model, 317 + uint64_t constraint, 318 + uint64_t index); 258 319 // Check whether the decision variable is functionally defined by a 259 320 // constraint. 260 321 // 261 322 // This function returns a [`IpdosValue`] that is either 262 323 // [`IpdosValueKind::IpdosValueDecision`] if it defined a decision variable 263 324 // or [`IpdosValueKind::IpdosValueAbsent`] otherwise. 264 - struct IpdosValue (*constraint_defines)(const IpdosModel *model, uint64_t decision); 325 + struct IpdosValueRef (*constraint_defines)(const IpdosModel *model, uint64_t decision); 265 326 // Retrieve the number of annotations on a constraint. 266 327 uint64_t (*constraint_annotation_len)(const IpdosModel *model, uint64_t constraint); 267 328 // Retrieve the annotation on a constraint at the given index. ··· 278 339 // have an objective strategy. 279 340 const char *(*objective_ident)(const IpdosModel *model); 280 341 // Retrieve the argument of the objective strategy. 281 - struct IpdosValue (*objective_arg)(const IpdosModel *model); 342 + struct IpdosValueRef (*objective_arg)(const IpdosModel *model); 282 343 // Retrieve the number of annotations on an objective 283 344 uint64_t (*objective_annotation_len)(const IpdosModel *model); 284 345 // Retrieve the annotation on the objective at the given index. ··· 297 358 // 298 359 // The `index` argument must be less than the value returned by 299 360 // `annotation_argument_len` for the given annotation. 300 - struct IpdosValue (*annotation_argument)(const IpdosAnnotation *ann, uint64_t index); 361 + struct IpdosValueRef (*annotation_argument)(const IpdosAnnotation *ann, uint64_t index); 301 362 } IpdosModelMethods; 302 363 303 364 // An interface to a model instance used to communicate with the solver. ··· 337 398 // option. 338 399 const char *ident; 339 400 // The type of value that is expected for this option. 340 - struct IpdosValue arg_ty; 401 + struct IpdosValueRef arg_ty; 341 402 // The default value for this option. 342 403 struct IpdosType arg_def; 343 404 } IpdosOption; ··· 362 423 // ipdos_solution`. 363 424 typedef void IpdosSolution; 364 425 365 - // Structure used to represent a solution emitted by the solver. 366 - // 367 - // The caller can use the `get_value` function to retrieve the value of the 368 - // used decision variables. 426 + // A struct containing the function pointers to interact with a 427 + // [`IpdosSolution`] 428 + typedef struct IpdosSolutionMethods { 429 + // Function callback to retrieve the value assigned to a decision variable 430 + // in the solution. 431 + struct IpdosValueRef (*get_value)(const IpdosSolution *data, uint64_t decision_index); 432 + // Function callback to retrieve the statistical information made available 433 + // by the solver about the search process so far. 434 + struct IpdosValueRef (*get_statistic)(const IpdosSolution *data, const char *ident); 435 + } IpdosSolutionMethods; 436 + 437 + // Handle for a solution emitted by the solver. 369 438 typedef struct IpdosSolutionRef { 370 439 // The data pointer to be the first argument of `get_value`. 371 440 const IpdosSolution *data; 372 - // Function callback to retrieve the value assigned to a decision variable 373 - // in the solution. 374 - struct IpdosValue (*get_value)(const IpdosSolution *data, uint64_t decision_index); 441 + // Reference to the structure containing the function callbacks used to 442 + // interact with the solution. 443 + const struct IpdosSolutionMethods *methods; 375 444 } IpdosSolutionRef; 376 445 377 446 // The handle to a solver instance. ··· 383 452 // cast the pointer back to the original type, e.g. `(MySolverType*) 384 453 // ipdos_solver`. 385 454 typedef void IpdosSolver; 455 + 456 + // The definition of statistical information that is made available by the 457 + // solver. 458 + typedef struct IpdosStatistic { 459 + // The identifier used to retrieve the statistical information from the 460 + // solver or a solution. 461 + const char *ident; 462 + // The type of value that is expected for this option. 463 + struct IpdosValueRef ty; 464 + // Whether the statistical information is available as part of solutions. 465 + bool solution; 466 + // Whether the statistical information is generally available from the 467 + // solver instance. 468 + bool solver; 469 + } IpdosStatistic; 470 + 471 + // A list of [`IpdosStatistic`]s. 472 + // 473 + // This type is, for example, used to return from [`ipdos_statistic_list`]. 474 + typedef struct IpdosStatisticList { 475 + // The number of elements in the `stats` array. 476 + uint64_t len; 477 + // An array of statistical information definitions. 478 + const struct IpdosStatistic *stats; 479 + } IpdosStatisticList; 386 480 387 481 // A list of [`IpdosType`]s. 388 482 //
+8 -4
cbindgen_template.toml
··· 12 12 [export] 13 13 exclude = [ 14 14 "IpdosAnnotation", 15 - "IpdosConstraintType", 16 15 "IpdosConstraintList", 17 - "IpdosModelRef", 16 + "IpdosConstraintType", 18 17 "IpdosModel", 19 18 "IpdosModelMethods", 19 + "IpdosModelRef", 20 20 "IpdosObjective", 21 21 "IpdosObjectiveList", 22 22 "IpdosOption", 23 23 "IpdosOptionList", 24 - "IpdosSolutionRef", 25 24 "IpdosSolution", 25 + "IpdosSolutionMethods", 26 + "IpdosSolutionRef", 26 27 "IpdosSolver", 28 + "IpdosStatistic", 29 + "IpdosStatisticList", 27 30 "IpdosStatus", 28 31 "IpdosType", 29 32 "IpdosTypeBase", 30 33 "IpdosTypeList", 31 34 "IpdosValue", 32 - "IpdosValueContent", 33 35 "IpdosValueKind", 36 + "IpdosValueMethods", 37 + "IpdosValueRef", 34 38 ]
+9 -5
cbindgen_types.toml
··· 3 3 # Wrapping header contents 4 4 include_guard = "ipdos_types_h" 5 5 documentation_style = "c99" 6 - sys_includes = ["stdbool", "stdint"] 6 + sys_includes = ["stdbool.h", "stdint.h"] 7 7 no_includes = true 8 8 9 9 [export] 10 10 include = [ 11 11 "IpdosAnnotation", 12 - "IpdosConstraintType", 13 12 "IpdosConstraintList", 14 - "IpdosModelRef", 13 + "IpdosConstraintType", 15 14 "IpdosModel", 16 15 "IpdosModelMethods", 16 + "IpdosModelRef", 17 17 "IpdosObjective", 18 18 "IpdosObjectiveList", 19 19 "IpdosOption", 20 20 "IpdosOptionList", 21 - "IpdosSolutionRef", 22 21 "IpdosSolution", 22 + "IpdosSolutionMethods", 23 + "IpdosSolutionRef", 23 24 "IpdosSolver", 25 + "IpdosStatistic", 26 + "IpdosStatisticList", 24 27 "IpdosStatus", 25 28 "IpdosType", 26 29 "IpdosTypeBase", 27 30 "IpdosTypeList", 28 31 "IpdosValue", 29 - "IpdosValueContent", 30 32 "IpdosValueKind", 33 + "IpdosValueMethods", 34 + "IpdosValueRef", 31 35 ]
+14 -9
rust/ipdos-solver-template/src/lib.rs
··· 9 9 use core::ffi; 10 10 11 11 use ipdos_types::{ 12 - IpdosConstraintList, IpdosModelRef, IpdosObjectiveList, IpdosOptionList, IpdosSolution, 13 - IpdosSolver, IpdosStatus, IpdosTypeList, IpdosValue, 12 + IpdosConstraintList, IpdosModelRef, IpdosObjectiveList, IpdosOptionList, IpdosSolutionRef, 13 + IpdosSolver, IpdosStatisticList, IpdosStatus, IpdosTypeList, IpdosValueRef, 14 14 }; 15 15 16 16 #[unsafe(no_mangle)] ··· 40 40 41 41 #[unsafe(no_mangle)] 42 42 /// Create a new solver instance 43 - pub extern "C" fn ipdos_solver_create() -> Box<IpdosSolver> { 43 + pub extern "C" fn ipdos_solver_create() -> *mut IpdosSolver { 44 44 unimplemented!() 45 45 } 46 46 ··· 49 49 /// The pointer to the solver instance will be invalid after this function has 50 50 /// been called. 51 51 #[unsafe(no_mangle)] 52 - pub extern "C" fn ipdos_solver_free(solver: Box<IpdosSolver>) { 52 + pub extern "C" fn ipdos_solver_free(solver: *mut IpdosSolver) { 53 53 let _ = solver; 54 54 unimplemented!() 55 55 } ··· 62 62 pub extern "C" fn ipdos_solver_option_get( 63 63 solver: &IpdosSolver, 64 64 ident: *const ffi::c_char, 65 - ) -> IpdosValue<'_> { 65 + ) -> IpdosValueRef<'_> { 66 66 let _ = solver; 67 67 let _ = ident; 68 68 unimplemented!() ··· 76 76 pub extern "C" fn ipdos_solver_option_set( 77 77 solver: &mut IpdosSolver, 78 78 ident: *const ffi::c_char, 79 - value: IpdosValue<'_>, 79 + value: IpdosValueRef<'_>, 80 80 ) -> bool { 81 81 let _ = solver; 82 82 let _ = ident; ··· 103 103 104 104 #[unsafe(no_mangle)] 105 105 /// Run the solver with the given model 106 - /// 107 - /// cbindgen:no-export 108 106 pub extern "C" fn ipdos_solver_run( 109 107 solver: &mut IpdosSolver, 110 108 model: IpdosModelRef, 111 109 context: &mut ffi::c_void, 112 - on_solution: extern "C" fn(context: &mut ffi::c_void, solution: &IpdosSolution), 110 + on_solution: extern "C" fn(context: &mut ffi::c_void, solution: IpdosSolutionRef), 113 111 ) -> IpdosStatus { 114 112 let _ = solver; 115 113 let _ = model; ··· 117 115 let _ = on_solution; 118 116 unimplemented!() 119 117 } 118 + 119 + #[unsafe(no_mangle)] 120 + /// Returns the list of available statistical information that can be requested 121 + /// from the solver and its solutions. 122 + pub extern "C" fn ipdos_statistic_list() -> IpdosStatisticList<'static> { 123 + unimplemented!() 124 + }
+228 -201
rust/ipdos-types/src/lib.rs
··· 6 6 //! unified way, and to allow the dynamic loading of solver libraries (as DLLs). 7 7 8 8 #![no_std] 9 - use core::{ffi, fmt, marker::PhantomData, ptr::slice_from_raw_parts}; 9 + 10 + use core::{ffi, marker::PhantomData}; 10 11 11 12 #[repr(transparent)] 12 13 #[derive(Debug)] ··· 22 23 23 24 #[repr(C)] 24 25 #[derive(Clone, Debug)] 25 - /// Representation of a type of constraint, discerned by its identifier and the 26 - /// types of its arguments. 27 - pub struct IpdosConstraintType<'a> { 28 - /// The identifier of the constraint type. 29 - pub ident: *const ffi::c_char, 30 - /// The number of expected arguments for the constraint type. 31 - pub arg_len: u64, 32 - /// The types of the expected arguments for the constraint type. 33 - pub arg_types: *const IpdosType, 34 - /// The lifetime for which the `ident` and `arg_types` attributes are 35 - /// allocated. 36 - pub lifetime: PhantomData<&'a ()>, 37 - } 38 - 39 - #[repr(C)] 40 - #[derive(Clone, Debug)] 41 26 /// A list of [`IpdosConstraintType`]s. 42 27 /// 43 28 /// This type is for example used to return from [`ipdos_constraint_list`]. ··· 51 36 } 52 37 53 38 #[repr(C)] 54 - #[derive(Clone, Copy, Debug)] 55 - /// An interface to a model instance used to communicate with the solver. 56 - /// 57 - /// The solver can use the included function callbacks to interact with the 58 - /// model. 59 - pub struct IpdosModelRef<'a> { 60 - /// The handle to the data of the model instance. 61 - data: &'a IpdosModel, 62 - /// Reference to the structure containing the function callbacks used to 63 - /// interact with the model. 64 - methods: &'static IpdosModelMethods, 39 + #[derive(Clone, Debug)] 40 + /// Representation of a type of constraint, discerned by its identifier and the 41 + /// types of its arguments. 42 + pub struct IpdosConstraintType<'a> { 43 + /// The identifier of the constraint type. 44 + pub ident: *const ffi::c_char, 45 + /// The number of expected arguments for the constraint type. 46 + pub arg_len: u64, 47 + /// The types of the expected arguments for the constraint type. 48 + pub arg_types: *const IpdosType, 49 + /// The lifetime for which the `ident` and `arg_types` attributes are 50 + /// allocated. 51 + pub lifetime: PhantomData<&'a ()>, 65 52 } 66 53 67 54 #[repr(transparent)] ··· 77 64 78 65 #[repr(C)] 79 66 #[derive(Debug)] 80 - /// A struct containing the function pointers to interact with theIpdosModel` 67 + /// A struct containing the function pointers to interact with the 68 + /// [`IpdosModel`] 81 69 pub struct IpdosModelMethods { 82 70 /// Returns the current number of model layers currently contained in the 83 71 /// model. ··· 126 114 /// 127 115 /// Note that the the value might have [`IpdosValueKind::IpdosValueAbsent`] 128 116 /// if the decision variable does not have an explicit domain. 129 - decision_domain: extern "C" fn(model: &IpdosModel, decision: u64) -> IpdosValue<'_>, 117 + decision_domain: extern "C" fn(model: &IpdosModel, decision: u64) -> IpdosValueRef<'_>, 130 118 /// Retrieve the name of a decision variable, if it exists. 131 119 /// 132 120 /// Note that names are only available for debugging purposes. Decisions are ··· 163 151 /// The `index` argument must be less than the value returned by 164 152 /// `constraint_argument_len` for the given constraint. 165 153 constraint_argument: 166 - extern "C" fn(model: &IpdosModel, constraint: u64, index: u64) -> IpdosValue<'_>, 154 + extern "C" fn(model: &IpdosModel, constraint: u64, index: u64) -> IpdosValueRef<'_>, 167 155 /// Check whether the decision variable is functionally defined by a 168 156 /// constraint. 169 157 /// 170 158 /// This function returns a [`IpdosValue`] that is either 171 159 /// [`IpdosValueKind::IpdosValueDecision`] if it defined a decision variable 172 160 /// or [`IpdosValueKind::IpdosValueAbsent`] otherwise. 173 - constraint_defines: extern "C" fn(model: &IpdosModel, decision: u64) -> IpdosValue<'_>, 161 + constraint_defines: extern "C" fn(model: &IpdosModel, decision: u64) -> IpdosValueRef<'_>, 174 162 /// Retrieve the number of annotations on a constraint. 175 163 constraint_annotation_len: extern "C" fn(model: &IpdosModel, constraint: u64) -> u64, 176 164 /// Retrieve the annotation on a constraint at the given index. ··· 187 175 /// have an objective strategy. 188 176 objective_ident: extern "C" fn(model: &IpdosModel) -> *const ffi::c_char, 189 177 /// Retrieve the argument of the objective strategy. 190 - objective_arg: extern "C" fn(model: &IpdosModel) -> IpdosValue<'_>, 178 + objective_arg: extern "C" fn(model: &IpdosModel) -> IpdosValueRef<'_>, 191 179 /// Retrieve the number of annotations on an objective 192 180 objective_annotation_len: extern "C" fn(model: &IpdosModel) -> u64, 193 181 /// Retrieve the annotation on the objective at the given index. ··· 207 195 /// 208 196 /// The `index` argument must be less than the value returned by 209 197 /// `annotation_argument_len` for the given annotation. 210 - annotation_argument: extern "C" fn(ann: &IpdosAnnotation, index: u64) -> IpdosValue<'_>, 198 + annotation_argument: extern "C" fn(ann: &IpdosAnnotation, index: u64) -> IpdosValueRef<'_>, 199 + } 200 + 201 + #[repr(C)] 202 + #[derive(Clone, Copy, Debug)] 203 + /// An interface to a model instance used to communicate with the solver. 204 + /// 205 + /// The solver can use the included function callbacks to interact with the 206 + /// model. 207 + pub struct IpdosModelRef<'a> { 208 + /// The handle to the data of the model instance. 209 + data: &'a IpdosModel, 210 + /// Reference to the structure containing the function callbacks used to 211 + /// interact with the model. 212 + methods: &'static IpdosModelMethods, 211 213 } 212 214 213 215 #[repr(C)] ··· 245 247 /// option. 246 248 pub ident: *const ffi::c_char, 247 249 /// The type of value that is expected for this option. 248 - pub arg_ty: IpdosValue<'a>, 250 + pub arg_ty: IpdosValueRef<'a>, 249 251 /// The default value for this option. 250 252 pub arg_def: IpdosType, 251 253 /// The lifetime of the `ident` attribute. ··· 266 268 pub lifetime: PhantomData<&'a ()>, 267 269 } 268 270 269 - #[repr(C)] 270 - #[derive(Clone, Debug)] 271 - /// Structure used to represent a solution emitted by the solver. 272 - /// 273 - /// The caller can use the `get_value` function to retrieve the value of the 274 - /// used decision variables. 275 - pub struct IpdosSolutionRef<'a> { 276 - /// The data pointer to be the first argument of `get_value`. 277 - data: &'a IpdosSolution, 278 - /// Function callback to retrieve the value assigned to a decision variable 279 - /// in the solution. 280 - get_value: extern "C" fn(data: &IpdosSolution, decision_index: u64) -> IpdosValue<'_>, 281 - } 282 - 283 271 #[repr(transparent)] 284 272 #[derive(Debug)] 285 273 /// The handle to a the data of a solution instance. ··· 292 280 /// ipdos_solution`. 293 281 pub struct IpdosSolution(ffi::c_void); 294 282 283 + #[repr(C)] 284 + #[derive(Clone, Debug)] 285 + /// A struct containing the function pointers to interact with a 286 + /// [`IpdosSolution`] 287 + pub struct IpdosSolutionMethods { 288 + /// Function callback to retrieve the value assigned to a decision variable 289 + /// in the solution. 290 + get_value: extern "C" fn(data: &IpdosSolution, decision_index: u64) -> IpdosValueRef<'_>, 291 + /// Function callback to retrieve the statistical information made available 292 + /// by the solver about the search process so far. 293 + get_statistic: 294 + extern "C" fn(data: &IpdosSolution, ident: *const ffi::c_char) -> IpdosValueRef<'_>, 295 + } 296 + 297 + #[repr(C)] 298 + #[derive(Clone, Debug)] 299 + /// Handle for a solution emitted by the solver. 300 + pub struct IpdosSolutionRef<'a> { 301 + /// The data pointer to be the first argument of `get_value`. 302 + data: &'a IpdosSolution, 303 + /// Reference to the structure containing the function callbacks used to 304 + /// interact with the solution. 305 + methods: &'static IpdosSolutionMethods, 306 + } 307 + 295 308 #[repr(transparent)] 296 309 #[derive(Debug)] 297 310 /// The handle to a solver instance. ··· 303 316 /// cast the pointer back to the original type, e.g. `(MySolverType*) 304 317 /// ipdos_solver`. 305 318 pub struct IpdosSolver(ffi::c_void); 319 + 320 + #[repr(C)] 321 + #[derive(Clone, Debug)] 322 + /// The definition of statistical information that is made available by the 323 + /// solver. 324 + pub struct IpdosStatistic<'a> { 325 + /// The identifier used to retrieve the statistical information from the 326 + /// solver or a solution. 327 + pub ident: *const ffi::c_char, 328 + /// The type of value that is expected for this option. 329 + pub ty: IpdosValueRef<'a>, 330 + /// Whether the statistical information is available as part of solutions. 331 + pub solution: bool, 332 + /// Whether the statistical information is generally available from the 333 + /// solver instance. 334 + pub solver: bool, 335 + /// The lifetime of the `ident` attribute. 336 + pub lifetime: PhantomData<&'a ()>, 337 + } 338 + 339 + #[repr(C)] 340 + #[derive(Clone, Debug)] 341 + /// A list of [`IpdosStatistic`]s. 342 + /// 343 + /// This type is, for example, used to return from [`ipdos_statistic_list`]. 344 + pub struct IpdosStatisticList<'a> { 345 + /// The number of elements in the `stats` array. 346 + pub len: u64, 347 + /// An array of statistical information definitions. 348 + pub stats: *const IpdosStatistic<'a>, 349 + /// The lifetime of the `stats` attribute. 350 + pub lifetime: PhantomData<&'a ()>, 351 + } 306 352 307 353 #[repr(C)] 308 354 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] ··· 369 415 pub lifetime: PhantomData<&'a ()>, 370 416 } 371 417 372 - #[repr(C)] 373 - #[derive(Clone, Copy)] 374 - /// The value representation used for values assigned to decision variables in 375 - /// solution, as constraint/annotation arguments, and option parameters. 376 - pub struct IpdosValue<'a> { 377 - /// The kind of the value 378 - /// 379 - /// This field is used to determine what field in the `value` union is 380 - /// allowed to be accessed. 381 - pub kind: IpdosValueKind, 382 - /// A field containing the size of the value. 383 - /// 384 - /// This field is used when the value is a list, set of float/int, and 385 - /// string, to determine the number of elements in the C array type. 386 - pub len: u32, 387 - /// The storage of the actual data of the value. 388 - pub content: IpdosValueContent<'a>, 389 - /// Lifetime of the value (only relevant in Rust). 390 - pub lifetime: PhantomData<&'a ()>, 391 - } 392 - 393 - #[repr(C)] 394 - #[derive(Clone, Copy)] 395 - /// The storage of the value content of a [`IpdosValue`]. 396 - pub union IpdosValueContent<'a> { 397 - /// Decision index storage 398 - pub decision_index: u64, 399 - /// Boolean value storage 400 - pub bool_value: bool, 401 - /// Integer value storage 402 - pub integer_value: i64, 403 - /// Float value storage 404 - pub float_value: f64, 405 - /// Set of integers value storage 406 - /// 407 - /// The set of integers is represented using a range list. The array of 408 - /// `i64` values should be interpreted as a list of inclusive ranges, 409 - /// where each range is represented by two values. 410 - /// 411 - /// Note that the number of `i64` values is stored in the `len` field. Since 412 - /// each range is represented by two values, it can be assumed that `len mod 413 - /// 2 == 0`. 414 - pub set_of_int_value: *const i64, 415 - /// Set of floating point value storage 416 - /// 417 - /// The set of floating point values is represented using a range list. The 418 - /// array of `f64` values should be interpreted as a list of inclusive 419 - /// ranges, where each range is represented by two values. 420 - /// 421 - /// Note that the number of `f64` values is stored in the `len` field. Since 422 - /// each range is represented by two values, it can be assumed that `len mod 423 - /// 2 == 0`. 424 - pub set_of_float_value: *const i64, 425 - /// String value storage 426 - /// 427 - /// Note that the `string_value` field is intended to be interpreted as a 428 - /// C-string. It should be \0 terminated, use UTF-8 encoding, and be valid 429 - /// for the lifetime of the value. 430 - pub string_value: *const ffi::c_char, 431 - /// List value storage 432 - /// 433 - /// Note that the the number of elements in the list is stored in the `len` 434 - /// field. 435 - pub list_value: *const IpdosValue<'a>, 436 - } 418 + #[repr(transparent)] 419 + #[derive(Debug)] 420 + /// The type of a value used as an argument or solution assignment. 421 + /// 422 + /// This type is opaque to the user. Only pointers of this type are ever used. 423 + /// 424 + /// In implementation of the IPDOS interface, a pointer is generally cast to 425 + /// this type, e.g. `(IpdosValue*) my_value`. A similar cast can be used to cast 426 + /// the pointer back to the original type, e.g. `(MyValue*) ipdos_value`. 427 + pub struct IpdosValue(ffi::c_void); 437 428 438 429 #[repr(C)] 439 430 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 440 431 /// Enumerated type used to mark the kind of [`IpdosValue`]. This is used to 441 - /// determine which field in the [`IpdosValueContent`] union to access. 432 + /// determine which "get" method in [`IpdosValueMethods`] is safe to call. 442 433 pub enum IpdosValueKind { 443 - /// No value is stored. 434 + /// No value is available. 444 435 IpdosValueAbsent, 445 - /// The value is stored in `decision_index`. 436 + /// The value is available using [`IpdosValueMethods::get_decision`]. 446 437 IpdosValueDecision, 447 - /// The value is stored in `boolean_value`. 448 - IpdosValueBoolean, 449 - /// The value is stored in `integer_value`. 450 - IpdosValueInteger, 451 - /// The value is stored in `float_value`. 438 + /// The value is available using [`IpdosValueMethods::get_bool`]. 439 + IpdosValueBool, 440 + /// The value is available using [`IpdosValueMethods::get_int`]. 441 + IpdosValueInt, 442 + /// The value is available using [`IpdosValueMethods::get_float`]. 452 443 IpdosValueFloat, 453 - /// The value is stored in `string_value`. 444 + /// The value is available using [`IpdosValueMethods::get_string`]. 454 445 IpdosValueString, 455 - /// The value is stored in `list_value`. 446 + /// Sets of integers are represented using a range list. The number of 447 + /// ranges is available using [`IpdosValueMethods::len`], and the ranges can 448 + /// be accessed using [`IpdosValueMethods::get_range_int`]. 449 + IpdosValueSetInt, 450 + /// Sets of floats are represented using a range list. The number of 451 + /// ranges is available using [`IpdosValueMethods::len`], and the ranges can 452 + /// be accessed using [`IpdosValueMethods::get_range_float`]. 453 + IpdosValueSetFloat, 454 + /// The length of the list can be accessed using 455 + /// [`IpdosValueMethods::len`], and elements in the list can be 456 + /// accessed using [`IpdosValueMethods::get_element`] 456 457 IpdosValueList, 457 458 } 458 459 459 - impl fmt::Debug for IpdosValue<'_> { 460 - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 461 - f.debug_struct("IpdosValue") 462 - .field("kind", &self.kind) 463 - .field("len", &self.len) 464 - // Safety: Access `self.content` according to the type information 465 - // provided explicitly by `self.kind`. 466 - .field("content", unsafe { 467 - match self.kind { 468 - IpdosValueKind::IpdosValueAbsent => &(), 469 - IpdosValueKind::IpdosValueDecision => &self.content.decision_index, 470 - IpdosValueKind::IpdosValueBoolean => &self.content.bool_value, 471 - IpdosValueKind::IpdosValueInteger => &self.content.integer_value, 472 - IpdosValueKind::IpdosValueFloat => &self.content.float_value, 473 - IpdosValueKind::IpdosValueString => &self.content.string_value, 474 - IpdosValueKind::IpdosValueList => &self.content.list_value, 475 - } 476 - }) 477 - .field("lifetime", &self.lifetime) 478 - .finish() 479 - } 480 - } 481 - 482 - impl PartialEq for IpdosValue<'_> { 483 - fn eq(&self, other: &Self) -> bool { 484 - self.kind == other.kind 485 - // Safety: Access `self.content` and `other.content` according to the type 486 - // information provided explicitly by `self.kind` and `other.kind`. 487 - && unsafe { 488 - match self.kind { 489 - IpdosValueKind::IpdosValueAbsent => true, 490 - IpdosValueKind::IpdosValueDecision => { 491 - self.content.decision_index == other.content.decision_index 492 - } 493 - IpdosValueKind::IpdosValueBoolean => { 494 - self.content.bool_value == other.content.bool_value 495 - } 496 - IpdosValueKind::IpdosValueInteger => { 497 - self.content.integer_value == other.content.integer_value 498 - } 499 - IpdosValueKind::IpdosValueFloat => { 500 - self.content.float_value == other.content.float_value 501 - } 502 - IpdosValueKind::IpdosValueString => { 503 - if self.len != other.len { 504 - return false; 505 - } 506 - if self.len == 0 { 507 - return true; 508 - } 509 - let self_str = 510 - &*slice_from_raw_parts(self.content.string_value, self.len as usize); 511 - let other_str = 512 - &*slice_from_raw_parts(other.content.string_value, other.len as usize); 513 - self_str == other_str 514 - } 515 - IpdosValueKind::IpdosValueList => { 516 - if self.len != other.len { 517 - return false; 518 - } 519 - let self_li = 520 - &*slice_from_raw_parts(self.content.list_value, self.len as usize); 521 - let other_li = 522 - &*slice_from_raw_parts(other.content.list_value, other.len as usize); 523 - self_li == other_li 524 - } 525 - } 526 - } 527 - } 460 + #[repr(C)] 461 + #[derive(Clone, Debug)] 462 + /// A struct containing the function pointers to interact with a 463 + /// [`IpdosValue`] 464 + pub struct IpdosValueMethods { 465 + /// Function callback that returns the kind of the value. 466 + kind: fn(&IpdosValue) -> IpdosValueKind, 467 + /// Function callback that returns the length of the value. 468 + /// 469 + /// In case [`IpdosValueMethods::kind`] returns 470 + /// [`IpdosValueKind::IpdosValueList`], the length is the number of elements 471 + /// in the list, accessible using [`IpdosValueMethods::get_element`]. 472 + /// 473 + /// In case [`IpdosValueMethods::kind`] returns 474 + /// [`IpdosValueKind::IpdosValueSetInt`] or 475 + /// [`IpdosValueKind::IpdosValueSetFloat`], the length is the number of 476 + /// ranges in the set, accessible using 477 + /// [`IpdosValueMethods::get_range_int`] or 478 + /// [`IpdosValueMethods::get_range_float`]. 479 + /// 480 + /// # Panics 481 + /// 482 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 483 + /// return [`IpdosValueKind::IpdosValueList`], 484 + /// [`IpdosValueKind::IpdosValueSetInt`], or 485 + /// [`IpdosValueKind::IpdosValueSetFloat`]. 486 + len: fn(&IpdosValue) -> u64, 487 + /// Function callback that returns the decision variable index contained in 488 + /// the value. 489 + /// 490 + /// # Panics 491 + /// 492 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 493 + /// return [`IpdosValueKind::IpdosValueDecision`]. 494 + get_decision: unsafe extern "C" fn(&IpdosValue) -> u64, 495 + /// Function callback that returns the integer value contained in the value. 496 + /// 497 + /// # Panics 498 + /// 499 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 500 + /// return [`IpdosValueKind::IpdosValueInt`]. 501 + get_int: unsafe extern "C" fn(&IpdosValue) -> i64, 502 + /// Function callback that returns the floating point value contained in the 503 + /// value. 504 + /// 505 + /// # Panics 506 + /// 507 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 508 + /// return [`IpdosValueKind::IpdosValueFloat`]. 509 + get_float: unsafe extern "C" fn(&IpdosValue) -> f64, 510 + /// Function callback that returns the string pointer value contained in the 511 + /// value. 512 + /// 513 + /// # Panics 514 + /// 515 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 516 + /// return [`IpdosValueKind::IpdosValueString`]. 517 + get_string: unsafe extern "C" fn(&IpdosValue) -> *const ffi::c_char, 518 + /// Function callback that returns the Boolean value contained in the value. 519 + /// 520 + /// # Panics 521 + /// 522 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 523 + /// return [`IpdosValueKind::IpdosValueBool`]. 524 + get_bool: unsafe extern "C" fn(&IpdosValue) -> bool, 525 + /// Function callback that returns a range from a range list representing 526 + /// the integer set contained in the value. 527 + /// 528 + /// # Panics 529 + /// 530 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 531 + /// return [`IpdosValueKind::IpdosValueSetInt`]. 532 + get_range_int: unsafe extern "C" fn(&IpdosValue, index: u64) -> [i64; 2], 533 + /// Function callback that returns a range from a range list representing 534 + /// the floating point set contained in the value. 535 + /// 536 + /// # Panics 537 + /// 538 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 539 + /// return [`IpdosValueKind::IpdosValueSetFloat`]. 540 + get_range_float: unsafe extern "C" fn(&IpdosValue, index: u64) -> [f64; 2], 541 + /// Function callback that returns an element from the list contained in the 542 + /// value. 543 + /// 544 + /// # Panics 545 + /// 546 + /// This function callback may panic if [`IpdosValueMethods::kind`] does not 547 + /// return [`IpdosValueKind::IpdosValueList`]. 548 + get_element: unsafe extern "C" fn(&IpdosValue, index: u64) -> IpdosValueRef<'_>, 528 549 } 529 550 530 - impl fmt::Debug for IpdosValueContent<'_> { 531 - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 532 - // Safety: interprets the storage as raw 64 bits, no type information 533 - // necessary. 534 - write!(f, "{:#X}", unsafe { self.decision_index }) 535 - } 551 + #[repr(C)] 552 + #[derive(Clone, Debug)] 553 + /// Handle for a value 554 + /// 555 + /// The caller can use the `get_value` function to retrieve the value of the 556 + /// used decision variables. 557 + pub struct IpdosValueRef<'a> { 558 + /// The data pointer to be the first argument of `get_value`. 559 + data: &'a IpdosValue, 560 + /// Reference to the structure containing the function callbacks used to 561 + /// interact with the value. 562 + methods: &'static IpdosValueMethods, 536 563 } 537 564 538 565 #[cfg(test)] 539 566 mod tests { 540 - use crate::{IpdosModelRef, IpdosSolutionRef, IpdosType, IpdosValue}; 567 + use crate::{IpdosModelRef, IpdosSolutionRef, IpdosType, IpdosValueRef}; 541 568 542 569 #[test] 543 570 fn memory() { 544 571 assert_eq!(size_of::<IpdosModelRef>(), size_of::<u128>()); 545 572 assert_eq!(size_of::<IpdosSolutionRef>(), size_of::<u128>()); 546 573 assert_eq!(size_of::<IpdosType>(), size_of::<u64>()); 547 - assert_eq!(size_of::<IpdosValue>(), size_of::<u128>()); 574 + assert_eq!(size_of::<IpdosValueRef>(), size_of::<u128>()); 548 575 } 549 576 }