+21
.gitignore
+21
.gitignore
···
1
+
# Generated by Cargo
2
+
# will have compiled files and executables
3
+
debug/
4
+
target/
5
+
6
+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7
+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8
+
Cargo.lock
9
+
10
+
# These are backup files generated by rustfmt
11
+
**/*.rs.bk
12
+
13
+
# MSVC Windows builds of rustc generate these, which store debugging information
14
+
*.pdb
15
+
16
+
# RustRover
17
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
20
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21
+
#.idea/
+59
Cargo.toml
+59
Cargo.toml
···
1
+
[workspace]
2
+
3
+
members = ["crates/ipdos_protocol"]
4
+
5
+
resolver = "2"
6
+
7
+
[workspace.package]
8
+
repository = "https://tangled.sh/@dekker.one/ipdos"
9
+
version = "0.1.0"
10
+
authors = ["Jip J. Dekker <jip@dekker.one>"]
11
+
edition = "2024"
12
+
license = ""
13
+
14
+
[workspace.lints.rust]
15
+
missing_debug_implementations = "warn"
16
+
missing_docs = "warn"
17
+
non_ascii_idents = "deny"
18
+
trivial_casts = "warn"
19
+
trivial_numeric_casts = "warn"
20
+
unit_bindings = "warn"
21
+
# unnameable_types = "warn"
22
+
unreachable_pub = "warn"
23
+
unused_crate_dependencies = "deny"
24
+
unused_import_braces = "warn"
25
+
unused_lifetimes = "warn"
26
+
unused_macro_rules = "warn"
27
+
unused_qualifications = "warn"
28
+
unused_results = "warn"
29
+
variant_size_differences = "warn"
30
+
31
+
[workspace.lints.clippy]
32
+
allow_attributes_without_reason = "deny"
33
+
# cargo_common_metadata = "warn"
34
+
clone_on_ref_ptr = "warn"
35
+
default_union_representation = "deny"
36
+
missing_docs_in_private_items = "warn"
37
+
mixed_read_write_in_expression = "deny"
38
+
# multiple_crate_versions = "warn"
39
+
negative_feature_names = "deny"
40
+
rc_buffer = "warn"
41
+
rc_mutex = "warn"
42
+
redundant_feature_names = "warn"
43
+
redundant_type_annotations = "warn"
44
+
rest_pat_in_fully_bound_structs = "warn"
45
+
# same_name_method = "warn"
46
+
semicolon_if_nothing_returned = "warn"
47
+
str_to_string = "warn"
48
+
string_add = "warn"
49
+
string_add_assign = "warn"
50
+
string_lit_chars_any = "warn"
51
+
string_to_string = "warn"
52
+
tests_outside_test_module = "warn"
53
+
try_err = "warn"
54
+
undocumented_unsafe_blocks = "warn"
55
+
unnecessary_safety_comment = "warn"
56
+
unseparated_literal_suffix = "warn"
57
+
unnecessary_safety_doc = "warn"
58
+
wildcard_dependencies = "warn"
59
+
wrong_self_convention = "warn"
+44
README.md
+44
README.md
···
1
+
# IPDOS: An Incremental Protocol for Decision and Optimization Solvers
2
+
3
+
## Introduction
4
+
5
+
## Common Functionality
6
+
7
+
Although solvers are able to define their own functionality using the IPDOS protocol, we advocate for the following common functionality to be implemented by different solvers.
8
+
This will allow for a more consistent user experience when interacting with different solvers.
9
+
Even if a solver does not implement all of these functions, we recommend that the solvers do not use the same name for different functionality.
10
+
11
+
### Common Options
12
+
13
+
A solver will expose its available options through `ipdos_option_list`.
14
+
We recommend that solvers eagerly implement the following options:
15
+
16
+
- `all_optimal` (`bool`, default: `false`): If set to `true`, the solver will after finding an optimal solution, continue to search for other solutions with the same objective value.
17
+
- `fixed_search` (`bool`, default: `false`): If set to `true`, the solver will strictly follow the search order defined by the user.
18
+
- `intermediate` (`bool`, default: `false`): If set to `true` for a problem with an objective strategy set, the solver will trigger its `on_solution` callback when it finds an intermediate solution.
19
+
Afterward, the solver will continue the search until it finds the next solution, or it proves that no better solutions exist (returning the `IpdosComplete` status).
20
+
- `threads` (`int`, default: `1`): For multithreaded solvers, this option will set the number of threads to use.
21
+
- `time_limit` (`int`, default: `-1`): If set to a positive integer, the solver will abandon the search after the specified number of milliseconds.
22
+
- `random_seed` (`opt int`, default: `<>`): If set to a positive integer, the solver will use the given value as the seed for its random number generator.
23
+
Solvers are encouraged to use a variable seed for the default value.
24
+
- `verbose` (`bool`, default: `false`): If set to `true`, the solver will print additional information about its process to `stderr`.
25
+
26
+
### Common Constraints
27
+
28
+
A solver will expose the constraints that can be used in a `IpdosModel` through `ipdos_constraint_list`.
29
+
We encourage solvers to support the constraints using the names and definitions from the [FlatZinc Builtins](https://docs.minizinc.dev/en/stable/lib-flatzinc.html).
30
+
Other MiniZinc (global) constraints are also encouraged to be implemented, using the `fzn_` prefix.
31
+
32
+
### Common Objective Strategies
33
+
34
+
A solver will expose the objective strategies that can be used in a `IpdosModel` through `ipdos_objective_list`.
35
+
We encourage solvers to support the following objective strategies if possible:
36
+
37
+
- `lex_maximize_int` (`list of var int`) | `lex_maximize_float` (`list of var float`): The solver will maximize the list of decision variables in lexicographical order, i.e., the first variable is maximized first, then the second variable, etc.
38
+
- `lex_minimize_int` (`list of var int`) | `lex_minimize_float` (`list of var float`): The solver will minimize the list of decision variables in lexicographical order, i.e., the first variable is minimized first, then the second variable, etc.
39
+
- `maximize_int` (`var int`) | `maximize_float` (`var float`): The solver will maximize the given decision variable.
40
+
- `minimize_int` (`var int`) | `minimize_float` (`var float`): The solver will minimize the given decision variable.
41
+
- `pareto_maximize_int` (`list of var int`) | `pareto_maximize_float` (`list of
42
+
var float`): The solver will output solutions where at least one decision variable is assigned a higher value than it was assigned in all previous solutions.
43
+
44
+
Note that if no objective is set, then the solver is expected to find any valid assignment of the decision variables that satisfies the constraints added to the solver.
+1
codebook.toml
+1
codebook.toml
···
1
+
words = ["ipdos"]
+1
crates/ipdos_protocol/.gitignore
+1
crates/ipdos_protocol/.gitignore
···
1
+
/target
+12
crates/ipdos_protocol/Cargo.toml
+12
crates/ipdos_protocol/Cargo.toml
+5
crates/ipdos_protocol/cbindgen.toml
+5
crates/ipdos_protocol/cbindgen.toml
+372
crates/ipdos_protocol/ipdos_protocol.h
+372
crates/ipdos_protocol/ipdos_protocol.h
···
1
+
#ifndef ipdos_protocol_h
2
+
#define ipdos_protocol_h
3
+
4
+
#include <stdarg.h>
5
+
#include <stdbool.h>
6
+
#include <stdint.h>
7
+
#include <stdlib.h>
8
+
9
+
// The status returned by `ipdos_solver_run`, indicating whether the solver
10
+
// completed its search.
11
+
typedef enum IpdosStatus {
12
+
// The solver explored the full search space and yielded all relevant
13
+
// solutions.
14
+
IpdosComplete,
15
+
// The solver did not explore the full search space due to a timeout or
16
+
// other termination condition. Additional (better) solutions might be
17
+
// possible.
18
+
IpdosIncomplete,
19
+
// An error occurred during the solver's execution.
20
+
//
21
+
// `ipdos_solver_read_error` can be used to retrieve the error message.
22
+
IpdosError,
23
+
} IpdosStatus;
24
+
25
+
// Representation of the base type of a value.
26
+
typedef enum IpdosTypeBase {
27
+
// Boolean type
28
+
IpdosTypeBaseBool,
29
+
// Integer numeric type
30
+
IpdosTypeBaseInt,
31
+
// Floating point numeric type
32
+
IpdosTypeBaseFloat,
33
+
// Character string type
34
+
IpdosTypeBaseString,
35
+
} IpdosTypeBase;
36
+
37
+
// Enumerated type used to mark the kind of [`IpdosValue`]. This is used to
38
+
// determine which field in the [`IpdosValueContent`] union to access.
39
+
typedef enum IpdosValueKind {
40
+
// No value is stored.
41
+
IpdosValueAbsent,
42
+
// The value is stored in `decision_index`.
43
+
IpdosValueDecision,
44
+
// The value is stored in `boolean_value`.
45
+
IpdosValueBoolean,
46
+
// The value is stored in `integer_value`.
47
+
IpdosValueInteger,
48
+
// The value is stored in `float_value`.
49
+
IpdosValueFloat,
50
+
// The value is stored in `string_value`.
51
+
IpdosValueString,
52
+
// The value is stored in `list_value`.
53
+
IpdosValueList,
54
+
} IpdosValueKind;
55
+
56
+
// Representation of a type to signal and check whether an argument takes the
57
+
// correct type.
58
+
typedef struct IpdosType {
59
+
// Whether the type is a list of values.
60
+
bool list_of;
61
+
// Whether the argument can be or contain decision variables (represented as
62
+
// decision indexes).
63
+
bool decision;
64
+
// Whether expected type is an set of values of the base type.
65
+
bool set_of;
66
+
// The expected base type of the argument.
67
+
enum IpdosTypeBase base;
68
+
} IpdosType;
69
+
70
+
// Representation of a type of constraint, discerned by its identifier and the
71
+
// types of its arguments.
72
+
typedef struct IpdosConstraintType {
73
+
// The identifier of the constraint type.
74
+
const char *ident;
75
+
// The number of expected arguments for the constraint type.
76
+
uintptr_t arg_len;
77
+
// The types of the expected arguments for the constraint type.
78
+
const struct IpdosType *arg_types;
79
+
} IpdosConstraintType;
80
+
81
+
// A list of [`IpdosConstraintType`]s.
82
+
//
83
+
// This type is for example used to return from [`ipdos_constraint_list`].
84
+
typedef struct IpdosConstraintList {
85
+
// The number of elements in the `constraints` array.
86
+
uintptr_t len;
87
+
// An array of constraint types.
88
+
const struct IpdosConstraintType *constraints;
89
+
} IpdosConstraintList;
90
+
91
+
// A list of [`IpdosType`]s.
92
+
//
93
+
// This type is for example used to return from [`ipdos_decision_list`].
94
+
typedef struct IpdosTypeList {
95
+
// The number of elements in the `constraints` array.
96
+
uintptr_t len;
97
+
// An array of constraint types.
98
+
const struct IpdosType *types;
99
+
} IpdosTypeList;
100
+
101
+
// Representation of a type of objective strategies, discerned by its
102
+
// identifier and the type of its argument.
103
+
typedef struct IpdosObjective {
104
+
// The identifier of the objective type.
105
+
const char *ident;
106
+
// The type of the expected argument for the constraint type.
107
+
struct IpdosType arg_type;
108
+
} IpdosObjective;
109
+
110
+
// A list of [`IpdosObjective`]s.
111
+
//
112
+
// This type is, for example, used to return from [`ipdos_objective_list`].
113
+
typedef struct IpdosObjectiveList {
114
+
// The number of elements in the `options` array.
115
+
uintptr_t len;
116
+
// An array of option definitions.
117
+
const struct IpdosObjective *options;
118
+
} IpdosObjectiveList;
119
+
120
+
// The storage of the value content of a [`IpdosValue`].
121
+
typedef union IpdosValueContent {
122
+
// Decision index storage
123
+
uintptr_t decision_index;
124
+
// Boolean value storage
125
+
bool bool_value;
126
+
// Integer value storage
127
+
int64_t integer_value;
128
+
// Float value storage
129
+
double float_value;
130
+
// Set of integers value storage
131
+
//
132
+
// The set of integers is represented using a range list. The array of
133
+
// `i64` values should be interpreted as a list of inclusive ranges,
134
+
// where each range is represented by two values.
135
+
//
136
+
// Note that the number of `i64` values is stored in the `len` field. Since
137
+
// each range is represented by two values, it can be assumed that `len mod
138
+
// 2 == 0`.
139
+
const int64_t *set_of_int_value;
140
+
// Set of floating point value storage
141
+
//
142
+
// The set of floating point values is represented using a range list. The
143
+
// array of `f64` values should be interpreted as a list of inclusive
144
+
// ranges, where each range is represented by two values.
145
+
//
146
+
// Note that the number of `f64` values is stored in the `len` field. Since
147
+
// each range is represented by two values, it can be assumed that `len mod
148
+
// 2 == 0`.
149
+
const int64_t *set_of_float_value;
150
+
// String value storage
151
+
//
152
+
// Note that the `string_value` field is intended to be interpreted as a
153
+
// C-string. It should be \0 terminated, use UTF-8 encoding, and be valid
154
+
// for the lifetime of the value.
155
+
const char *string_value;
156
+
// List value storage
157
+
//
158
+
// Note that the the number of elements in the list is stored in the `len`
159
+
// field.
160
+
const struct IpdosValue *list_value;
161
+
} IpdosValueContent;
162
+
163
+
// The value representation used for values assigned to decision variables in
164
+
// solution, as constraint/annotation arguments, and option parameters.
165
+
typedef struct IpdosValue {
166
+
// The kind of the value
167
+
//
168
+
// This field is used to determine what field in the `value` union is
169
+
// allowed to be accessed.
170
+
enum IpdosValueKind kind;
171
+
// A field containing the size of the value.
172
+
//
173
+
// This field is used when the value is a list, set of float/int, and
174
+
// string, to determine the number of elements in the C array type.
175
+
uint32_t len;
176
+
// The storage of the actual data of the value.
177
+
union IpdosValueContent content;
178
+
} IpdosValue;
179
+
180
+
// The definition of an option that is available to be set for the solver.
181
+
typedef struct IpdosOption {
182
+
// The identifier used to set the option or get the current value of the option.
183
+
const char *ident;
184
+
// The type of value that is expected for this option.
185
+
struct IpdosValue arg_ty;
186
+
// The default value for this option.
187
+
struct IpdosType arg_def;
188
+
} IpdosOption;
189
+
190
+
// A list of [`IpdosOption`]s.
191
+
//
192
+
// This type is, for example, used to return from [`ipdos_option_list`].
193
+
typedef struct IpdosOptionList {
194
+
// The number of elements in the `options` array.
195
+
uintptr_t len;
196
+
// An array of option definitions.
197
+
const struct IpdosOption *options;
198
+
} IpdosOptionList;
199
+
200
+
// The handle to a solver instance.
201
+
//
202
+
// This type is opaque to the user. Only pointers of this type are ever used.
203
+
//
204
+
// In implementation of the IPDOS interface, a pointer is generally cast to
205
+
// this type, i.e. `(IpdosSolver*) my_solver`. A similar cast can be used to
206
+
// cast the pointer back to the original type, e.g. `(MySolverType*)
207
+
// ipdos_solver`.
208
+
typedef void IpdosSolver;
209
+
210
+
// The handle to a the data of a model instance.
211
+
//
212
+
// This type is opaque to the user. Only pointers of this type are ever used.
213
+
//
214
+
// In implementation of the IPDOS interface, a pointer is generally cast to
215
+
// this type, i.e. `(IpdosModelData*) my_model`. A similar cast can be used to
216
+
// cast the pointer back to the original type, e.g. `(MyModel*) ipdos_model`.
217
+
typedef void IpdosModelData;
218
+
219
+
// An interface to a model instance used to communicate with the solver.
220
+
//
221
+
// The solver can use the included function callbacks to interact with the
222
+
// model.
223
+
typedef struct IpdosModel {
224
+
// The handle to the data of the model instance.
225
+
//
226
+
// This handle is passed to all the different function callbacks to
227
+
// interact with the model.
228
+
const IpdosModelData *data;
229
+
// Returns the current number of model layers currently contained in the
230
+
// model.
231
+
//
232
+
// Note that it is required that the solver calls [`ipdos_solver_push`] and
233
+
// [`ipdos_solver_pop`] whenever a layer is added or removed.
234
+
uintptr_t (*layers)(const IpdosModelData *model);
235
+
// Retrieve the number of constraints currently contained in the model.
236
+
uintptr_t (*constraint_len)(const IpdosModelData *model);
237
+
// Retrieve the constraint index of the first new constraint in the layer.
238
+
//
239
+
// This can be equivalent to `constraint_len` if the layer does not add any
240
+
// new constraints.
241
+
uintptr_t (*constraint_layer_start)(const IpdosModelData *model);
242
+
// Retrieve the identifier of a constraint.
243
+
//
244
+
// The returned pointer can be assumed to have the the same lifetime as the
245
+
// constraint reference and must be valid UTF8.
246
+
const char *(*constraint_ident)(const IpdosModelData *model, uintptr_t constraint);
247
+
// Retrieve the number of arguments of a constraint.
248
+
uintptr_t (*constraint_argument_len)(const IpdosModelData *model, uintptr_t constraint);
249
+
// Retrieve the value of the constraint's argument at the given index.
250
+
//
251
+
// The `index` argument must be less than the value returned by
252
+
// `constraint_argument_len` for the given constraint.
253
+
struct IpdosValue (*constraint_argument)(const IpdosModelData *model,
254
+
uintptr_t constraint,
255
+
uintptr_t index);
256
+
// Retrieve the number of decisions currently contained in the model.
257
+
uintptr_t (*decision_len)(const IpdosModelData *model);
258
+
// Retrieve the decision index of the first new decision in the layer.
259
+
//
260
+
// This can be equivalent to `decision_len` if the layer does not add any
261
+
// new decisions.
262
+
uintptr_t (*decision_layer_start)(const IpdosModelData *model);
263
+
// Retrieve the domain of the given decision.
264
+
//
265
+
// Note that the the value might have [`IpdosValueKind::IpdosValueAbsent`] if
266
+
// the decision variable does not have an explicit domain.
267
+
struct IpdosValue (*decision_domain)(const IpdosModelData *model, uintptr_t decision);
268
+
// Retrieve the name of a decision variable, if it exists.
269
+
//
270
+
// Note that names are only available for debugging purposes. Decisions are
271
+
// identified using their index in the model. If the decision variable does
272
+
// not have a name, this function returns a null pointer.
273
+
const char *(*decision_name)(const IpdosModelData *model, uintptr_t decision);
274
+
// Check whether the decision variable is functionally defined by a
275
+
// constraint.
276
+
bool (*decision_defined)(const IpdosModelData *model, uintptr_t decision);
277
+
// Request the identifier of the type of objective strategy to be used when
278
+
// solving the model.
279
+
//
280
+
// Note that the function can return a null pointer if the model does not
281
+
// have an objective strategy.
282
+
const char *(*objective_ident)(const IpdosModelData *model);
283
+
// Retrieve the argument of the objective strategy.
284
+
struct IpdosValue (*objective_arg)(const IpdosModelData *model);
285
+
} IpdosModel;
286
+
287
+
// The handle to a the data of a solution instance.
288
+
//
289
+
// This type is opaque to the user. Only pointers of this type are ever used.
290
+
//
291
+
// In implementation of the IPDOS interface, a pointer is generally cast to
292
+
// this type, i.e. `(IpdosSolutionData*) my_solution`. A similar cast can be
293
+
// used to cast the pointer back to the original type, e.g. `(MySolution*)
294
+
// ipdos_solution`.
295
+
typedef void IpdosSolutionData;
296
+
297
+
// Structure used to represent a solution emitted by the solver.
298
+
//
299
+
// The caller can use the `get_value` function to retrieve the value of the
300
+
// used decision variables.
301
+
typedef struct IpdosSolution {
302
+
// The data pointer to be the first argument of `get_value`.
303
+
const IpdosSolutionData *data;
304
+
// Function callback to retrieve the value assigned to a decision variable
305
+
// in the solution.
306
+
struct IpdosValue (*get_value)(const IpdosSolutionData *data, uintptr_t decision_index);
307
+
} IpdosSolution;
308
+
309
+
// Returns the list of available constraint that can be added to the solver.
310
+
struct IpdosConstraintList ipdos_constraint_list(void);
311
+
312
+
// Returns the list of types for which decision variable can be created by the
313
+
// solver.
314
+
struct IpdosTypeList ipdos_decision_list(void);
315
+
316
+
// Returns the list of available objective that can be achieved by the solver.
317
+
struct IpdosObjectiveList ipdos_objective_list(void);
318
+
319
+
// Returns the list of available options that can be set of the solver.
320
+
struct IpdosOptionList ipdos_option_list(void);
321
+
322
+
// Create a new solver instance
323
+
IpdosSolver *ipdos_solver_create(void);
324
+
325
+
// Free a solver instance, releasing all resources associated with it.
326
+
//
327
+
// The pointer to the solver instance will be invalid after this function has
328
+
// been called.
329
+
void ipdos_solver_free(IpdosSolver *solver);
330
+
331
+
// Get the current value of an option for the solver.
332
+
//
333
+
// Note that this is only valid to be called with options named by
334
+
// `ipdos_option_list`.
335
+
struct IpdosValue ipdos_solver_option_get(const IpdosSolver *solver, const char *ident);
336
+
337
+
// Set the current value of an option for the solver.
338
+
//
339
+
// Note that this is only valid to be called with options named by
340
+
// `ipdos_option_list`, and the value must be of the correct type.
341
+
bool ipdos_solver_option_set(IpdosSolver *solver, const char *ident, struct IpdosValue value);
342
+
343
+
// Remove a model layer from the solver.
344
+
//
345
+
// It is required that `model.layers` is less than the number of layers in the
346
+
// last call to `ipdos_solver_push` or `ipdos_solver_pop`. It is allowed to
347
+
// remove multiple model layers at the same time.
348
+
void ipdos_solver_pop(IpdosSolver *solver, struct IpdosModel model);
349
+
350
+
// Add a new model layer to the solver.
351
+
//
352
+
// It is required that `model.layers` is exactly one larger than the previous
353
+
// call to `ipdos_solver_push` or `ipdos_solver_pop`. It is NOT allowed to add
354
+
// multiple model layers at the same time.
355
+
bool ipdos_solver_push(IpdosSolver *solver, struct IpdosModel model);
356
+
357
+
// Read an error message from the solver.
358
+
//
359
+
// This function is expected to be called after solver interactions signal an
360
+
// error has occurred. For example, if [`ipdos_solver_run`] returns
361
+
// [`IpdosError`] or [`ipdos_solver_push`] returns `false`.
362
+
void ipdos_solver_read_error(IpdosSolver *solver,
363
+
void *context,
364
+
void (*read_error)(void *context, const char *error));
365
+
366
+
// Run the solver with the given model
367
+
enum IpdosStatus ipdos_solver_run(IpdosSolver *solver,
368
+
void *context,
369
+
void (*on_solution)(void *context,
370
+
const struct IpdosSolution *solution));
371
+
372
+
#endif /* ipdos_protocol_h */
+482
crates/ipdos_protocol/src/lib.rs
+482
crates/ipdos_protocol/src/lib.rs
···
1
+
//! IPDOS Protocol
2
+
//!
3
+
//! This crate defines the IPDOS protocol, which allows the incremental usage of
4
+
//! solvers that can solve decision and optimization problems. The goal of the
5
+
//! interface is to easily use different solvers in a unified way.
6
+
#![allow(missing_debug_implementations)]
7
+
8
+
use std::{ffi, marker::PhantomData};
9
+
10
+
#[repr(C)]
11
+
/// Representation of a type of constraint, discerned by its identifier and the
12
+
/// types of its arguments.
13
+
pub struct IpdosConstraintType<'a> {
14
+
/// The identifier of the constraint type.
15
+
pub ident: *const ffi::c_char,
16
+
/// The number of expected arguments for the constraint type.
17
+
pub arg_len: usize,
18
+
/// The types of the expected arguments for the constraint type.
19
+
pub arg_types: *const IpdosType,
20
+
/// The lifetime for which the `ident` and `arg_types` attributes are
21
+
/// allocated.
22
+
pub lifetime: PhantomData<&'a ()>,
23
+
}
24
+
25
+
#[repr(C)]
26
+
/// A list of [`IpdosConstraintType`]s.
27
+
///
28
+
/// This type is for example used to return from [`ipdos_constraint_list`].
29
+
pub struct IpdosConstraintList<'a> {
30
+
/// The number of elements in the `constraints` array.
31
+
pub len: usize,
32
+
/// An array of constraint types.
33
+
pub constraints: *const IpdosConstraintType<'a>,
34
+
/// The lifetime for which the `constraints` attribute is allocated.
35
+
pub lifetime: PhantomData<&'a ()>,
36
+
}
37
+
38
+
#[repr(C)]
39
+
/// An interface to a model instance used to communicate with the solver.
40
+
///
41
+
/// The solver can use the included function callbacks to interact with the
42
+
/// model.
43
+
pub struct IpdosModel<'a> {
44
+
/// The handle to the data of the model instance.
45
+
///
46
+
/// This handle is passed to all the different function callbacks to
47
+
/// interact with the model.
48
+
data: &'a IpdosModelData,
49
+
/// Returns the current number of model layers currently contained in the
50
+
/// model.
51
+
///
52
+
/// Note that it is required that the solver calls [`ipdos_solver_push`] and
53
+
/// [`ipdos_solver_pop`] whenever a layer is added or removed.
54
+
layers: extern "C" fn(model: &IpdosModelData) -> usize,
55
+
56
+
/// Retrieve the number of constraints currently contained in the model.
57
+
constraint_len: extern "C" fn(model: &IpdosModelData) -> usize,
58
+
/// Retrieve the constraint index of the first new constraint in the layer.
59
+
///
60
+
/// This can be equivalent to `constraint_len` if the layer does not add any
61
+
/// new constraints.
62
+
constraint_layer_start: extern "C" fn(model: &IpdosModelData) -> usize,
63
+
/// Retrieve the identifier of a constraint.
64
+
///
65
+
/// The returned pointer can be assumed to have the the same lifetime as the
66
+
/// constraint reference and must be valid UTF8.
67
+
constraint_ident:
68
+
extern "C" fn(model: &IpdosModelData, constraint: usize) -> *const ffi::c_char,
69
+
/// Retrieve the number of arguments of a constraint.
70
+
constraint_argument_len: extern "C" fn(model: &IpdosModelData, constraint: usize) -> usize,
71
+
/// Retrieve the value of the constraint's argument at the given index.
72
+
///
73
+
/// The `index` argument must be less than the value returned by
74
+
/// `constraint_argument_len` for the given constraint.
75
+
constraint_argument:
76
+
extern "C" fn(model: &IpdosModelData, constraint: usize, index: usize) -> IpdosValue<'_>,
77
+
78
+
/// Retrieve the number of decisions currently contained in the model.
79
+
decision_len: extern "C" fn(model: &IpdosModelData) -> usize,
80
+
/// Retrieve the decision index of the first new decision in the layer.
81
+
///
82
+
/// This can be equivalent to `decision_len` if the layer does not add any
83
+
/// new decisions.
84
+
decision_layer_start: extern "C" fn(model: &IpdosModelData) -> usize,
85
+
/// Retrieve the domain of the given decision.
86
+
///
87
+
/// Note that the the value might have [`IpdosValueKind::IpdosValueAbsent`] if
88
+
/// the decision variable does not have an explicit domain.
89
+
decision_domain: extern "C" fn(model: &IpdosModelData, decision: usize) -> IpdosValue<'_>,
90
+
/// Retrieve the name of a decision variable, if it exists.
91
+
///
92
+
/// Note that names are only available for debugging purposes. Decisions are
93
+
/// identified using their index in the model. If the decision variable does
94
+
/// not have a name, this function returns a null pointer.
95
+
decision_name: extern "C" fn(model: &IpdosModelData, decision: usize) -> *const ffi::c_char,
96
+
/// Check whether the decision variable is functionally defined by a
97
+
/// constraint.
98
+
decision_defined: extern "C" fn(model: &IpdosModelData, decision: usize) -> bool,
99
+
100
+
/// Request the identifier of the type of objective strategy to be used when
101
+
/// solving the model.
102
+
///
103
+
/// Note that the function can return a null pointer if the model does not
104
+
/// have an objective strategy.
105
+
objective_ident: extern "C" fn(model: &IpdosModelData) -> *const ffi::c_char,
106
+
/// Retrieve the argument of the objective strategy.
107
+
objective_arg: extern "C" fn(model: &IpdosModelData) -> IpdosValue<'_>,
108
+
}
109
+
110
+
#[repr(transparent)]
111
+
/// The handle to a the data of a model instance.
112
+
///
113
+
/// This type is opaque to the user. Only pointers of this type are ever used.
114
+
///
115
+
/// In implementation of the IPDOS interface, a pointer is generally cast to
116
+
/// this type, i.e. `(IpdosModelData*) my_model`. A similar cast can be used to
117
+
/// cast the pointer back to the original type, e.g. `(MyModel*) ipdos_model`.
118
+
pub struct IpdosModelData(ffi::c_void);
119
+
120
+
#[repr(C)]
121
+
/// Representation of a type of objective strategies, discerned by its
122
+
/// identifier and the type of its argument.
123
+
pub struct IpdosObjective<'a> {
124
+
/// The identifier of the objective type.
125
+
pub ident: *const ffi::c_char,
126
+
/// The type of the expected argument for the constraint type.
127
+
pub arg_type: IpdosType,
128
+
/// The lifetime for which the `ident` attribute is allocated.
129
+
pub lifetime: PhantomData<&'a ()>,
130
+
}
131
+
132
+
#[repr(C)]
133
+
/// A list of [`IpdosObjective`]s.
134
+
///
135
+
/// This type is, for example, used to return from [`ipdos_objective_list`].
136
+
pub struct IpdosObjectiveList<'a> {
137
+
/// The number of elements in the `options` array.
138
+
pub len: usize,
139
+
/// An array of option definitions.
140
+
pub options: *const IpdosObjective<'a>,
141
+
/// The lifetime of the `options` attribute.
142
+
pub lifetime: PhantomData<&'a ()>,
143
+
}
144
+
145
+
#[repr(C)]
146
+
/// The definition of an option that is available to be set for the solver.
147
+
pub struct IpdosOption<'a> {
148
+
/// The identifier used to set the option or get the current value of the option.
149
+
pub ident: *const ffi::c_char,
150
+
/// The type of value that is expected for this option.
151
+
pub arg_ty: IpdosValue<'a>,
152
+
/// The default value for this option.
153
+
pub arg_def: IpdosType,
154
+
/// The lifetime of the `ident` attribute.
155
+
pub lifetime: PhantomData<&'a ()>,
156
+
}
157
+
158
+
#[repr(C)]
159
+
/// A list of [`IpdosOption`]s.
160
+
///
161
+
/// This type is, for example, used to return from [`ipdos_option_list`].
162
+
pub struct IpdosOptionList<'a> {
163
+
/// The number of elements in the `options` array.
164
+
pub len: usize,
165
+
/// An array of option definitions.
166
+
pub options: *const IpdosOption<'a>,
167
+
/// The lifetime of the `options` attribute.
168
+
pub lifetime: PhantomData<&'a ()>,
169
+
}
170
+
171
+
#[repr(C)]
172
+
/// Structure used to represent a solution emitted by the solver.
173
+
///
174
+
/// The caller can use the `get_value` function to retrieve the value of the
175
+
/// used decision variables.
176
+
pub struct IpdosSolution<'a> {
177
+
/// The data pointer to be the first argument of `get_value`.
178
+
data: &'a IpdosSolutionData,
179
+
/// Function callback to retrieve the value assigned to a decision variable
180
+
/// in the solution.
181
+
get_value: extern "C" fn(data: &IpdosSolutionData, decision_index: usize) -> IpdosValue<'_>,
182
+
}
183
+
184
+
#[repr(transparent)]
185
+
/// The handle to a the data of a solution instance.
186
+
///
187
+
/// This type is opaque to the user. Only pointers of this type are ever used.
188
+
///
189
+
/// In implementation of the IPDOS interface, a pointer is generally cast to
190
+
/// this type, i.e. `(IpdosSolutionData*) my_solution`. A similar cast can be
191
+
/// used to cast the pointer back to the original type, e.g. `(MySolution*)
192
+
/// ipdos_solution`.
193
+
pub struct IpdosSolutionData(ffi::c_void);
194
+
195
+
#[repr(transparent)]
196
+
/// The handle to a solver instance.
197
+
///
198
+
/// This type is opaque to the user. Only pointers of this type are ever used.
199
+
///
200
+
/// In implementation of the IPDOS interface, a pointer is generally cast to
201
+
/// this type, i.e. `(IpdosSolver*) my_solver`. A similar cast can be used to
202
+
/// cast the pointer back to the original type, e.g. `(MySolverType*)
203
+
/// ipdos_solver`.
204
+
pub struct IpdosSolver(ffi::c_void);
205
+
206
+
#[repr(C)]
207
+
/// The status returned by `ipdos_solver_run`, indicating whether the solver
208
+
/// completed its search.
209
+
pub enum IpdosStatus {
210
+
/// The solver explored the full search space and yielded all relevant
211
+
/// solutions.
212
+
IpdosComplete,
213
+
/// The solver did not explore the full search space due to a timeout or
214
+
/// other termination condition. Additional (better) solutions might be
215
+
/// possible.
216
+
IpdosIncomplete,
217
+
/// An error occurred during the solver's execution.
218
+
///
219
+
/// `ipdos_solver_read_error` can be used to retrieve the error message.
220
+
IpdosError,
221
+
}
222
+
223
+
#[repr(C)]
224
+
/// Representation of a type to signal and check whether an argument takes the
225
+
/// correct type.
226
+
pub struct IpdosType {
227
+
/// Whether the type is a list of values.
228
+
pub list_of: bool,
229
+
/// Whether the argument can be or contain decision variables (represented as
230
+
/// decision indexes).
231
+
pub decision: bool,
232
+
/// Whether expected type is an set of values of the base type.
233
+
pub set_of: bool,
234
+
/// The expected base type of the argument.
235
+
pub base: IpdosTypeBase,
236
+
}
237
+
238
+
#[repr(C)]
239
+
/// Representation of the base type of a value.
240
+
pub enum IpdosTypeBase {
241
+
/// Boolean type
242
+
IpdosTypeBaseBool,
243
+
/// Integer numeric type
244
+
IpdosTypeBaseInt,
245
+
/// Floating point numeric type
246
+
IpdosTypeBaseFloat,
247
+
/// Character string type
248
+
IpdosTypeBaseString,
249
+
}
250
+
251
+
#[repr(C)]
252
+
/// A list of [`IpdosType`]s.
253
+
///
254
+
/// This type is for example used to return from [`ipdos_decision_list`].
255
+
pub struct IpdosTypeList<'a> {
256
+
/// The number of elements in the `constraints` array.
257
+
pub len: usize,
258
+
/// An array of constraint types.
259
+
pub types: *const IpdosType,
260
+
/// The lifetime for which the `types` attribute is allocated.
261
+
pub lifetime: PhantomData<&'a ()>,
262
+
}
263
+
264
+
#[repr(C)]
265
+
/// The value representation used for values assigned to decision variables in
266
+
/// solution, as constraint/annotation arguments, and option parameters.
267
+
pub struct IpdosValue<'a> {
268
+
/// The kind of the value
269
+
///
270
+
/// This field is used to determine what field in the `value` union is
271
+
/// allowed to be accessed.
272
+
pub kind: IpdosValueKind,
273
+
/// A field containing the size of the value.
274
+
///
275
+
/// This field is used when the value is a list, set of float/int, and
276
+
/// string, to determine the number of elements in the C array type.
277
+
pub len: u32,
278
+
/// The storage of the actual data of the value.
279
+
pub content: IpdosValueContent<'a>,
280
+
/// Lifetime of the value (only relevant in Rust).
281
+
pub lifetime: PhantomData<&'a ()>,
282
+
}
283
+
284
+
#[repr(C)]
285
+
/// The storage of the value content of a [`IpdosValue`].
286
+
pub union IpdosValueContent<'a> {
287
+
/// Decision index storage
288
+
pub decision_index: usize,
289
+
/// Boolean value storage
290
+
pub bool_value: bool,
291
+
/// Integer value storage
292
+
pub integer_value: i64,
293
+
/// Float value storage
294
+
pub float_value: f64,
295
+
/// Set of integers value storage
296
+
///
297
+
/// The set of integers is represented using a range list. The array of
298
+
/// `i64` values should be interpreted as a list of inclusive ranges,
299
+
/// where each range is represented by two values.
300
+
///
301
+
/// Note that the number of `i64` values is stored in the `len` field. Since
302
+
/// each range is represented by two values, it can be assumed that `len mod
303
+
/// 2 == 0`.
304
+
pub set_of_int_value: *const i64,
305
+
/// Set of floating point value storage
306
+
///
307
+
/// The set of floating point values is represented using a range list. The
308
+
/// array of `f64` values should be interpreted as a list of inclusive
309
+
/// ranges, where each range is represented by two values.
310
+
///
311
+
/// Note that the number of `f64` values is stored in the `len` field. Since
312
+
/// each range is represented by two values, it can be assumed that `len mod
313
+
/// 2 == 0`.
314
+
pub set_of_float_value: *const i64,
315
+
/// String value storage
316
+
///
317
+
/// Note that the `string_value` field is intended to be interpreted as a
318
+
/// C-string. It should be \0 terminated, use UTF-8 encoding, and be valid
319
+
/// for the lifetime of the value.
320
+
pub string_value: *const ffi::c_char,
321
+
/// List value storage
322
+
///
323
+
/// Note that the the number of elements in the list is stored in the `len`
324
+
/// field.
325
+
pub list_value: *const IpdosValue<'a>,
326
+
}
327
+
328
+
#[repr(C)]
329
+
/// Enumerated type used to mark the kind of [`IpdosValue`]. This is used to
330
+
/// determine which field in the [`IpdosValueContent`] union to access.
331
+
pub enum IpdosValueKind {
332
+
/// No value is stored.
333
+
IpdosValueAbsent,
334
+
/// The value is stored in `decision_index`.
335
+
IpdosValueDecision,
336
+
/// The value is stored in `boolean_value`.
337
+
IpdosValueBoolean,
338
+
/// The value is stored in `integer_value`.
339
+
IpdosValueInteger,
340
+
/// The value is stored in `float_value`.
341
+
IpdosValueFloat,
342
+
/// The value is stored in `string_value`.
343
+
IpdosValueString,
344
+
/// The value is stored in `list_value`.
345
+
IpdosValueList,
346
+
}
347
+
348
+
#[unsafe(no_mangle)]
349
+
/// Returns the list of available constraint that can be added to the solver.
350
+
pub extern "C" fn ipdos_constraint_list() -> IpdosConstraintList<'static> {
351
+
unimplemented!()
352
+
}
353
+
354
+
#[unsafe(no_mangle)]
355
+
/// Returns the list of types for which decision variable can be created by the
356
+
/// solver.
357
+
pub extern "C" fn ipdos_decision_list() -> IpdosTypeList<'static> {
358
+
unimplemented!()
359
+
}
360
+
361
+
#[unsafe(no_mangle)]
362
+
/// Returns the list of available objective that can be achieved by the solver.
363
+
pub extern "C" fn ipdos_objective_list() -> IpdosObjectiveList<'static> {
364
+
unimplemented!()
365
+
}
366
+
367
+
#[unsafe(no_mangle)]
368
+
/// Returns the list of available options that can be set of the solver.
369
+
pub extern "C" fn ipdos_option_list() -> IpdosOptionList<'static> {
370
+
unimplemented!()
371
+
}
372
+
373
+
#[unsafe(no_mangle)]
374
+
/// Create a new solver instance
375
+
pub extern "C" fn ipdos_solver_create() -> Box<IpdosSolver> {
376
+
unimplemented!()
377
+
}
378
+
379
+
/// Free a solver instance, releasing all resources associated with it.
380
+
///
381
+
/// The pointer to the solver instance will be invalid after this function has
382
+
/// been called.
383
+
#[unsafe(no_mangle)]
384
+
pub extern "C" fn ipdos_solver_free(solver: Box<IpdosSolver>) {
385
+
let _ = solver;
386
+
unimplemented!()
387
+
}
388
+
389
+
#[unsafe(no_mangle)]
390
+
/// Get the current value of an option for the solver.
391
+
///
392
+
/// Note that this is only valid to be called with options named by
393
+
/// `ipdos_option_list`.
394
+
pub extern "C" fn ipdos_solver_option_get(
395
+
solver: &IpdosSolver,
396
+
ident: *const ffi::c_char,
397
+
) -> IpdosValue<'_> {
398
+
let _ = solver;
399
+
let _ = ident;
400
+
unimplemented!()
401
+
}
402
+
403
+
#[unsafe(no_mangle)]
404
+
/// Set the current value of an option for the solver.
405
+
///
406
+
/// Note that this is only valid to be called with options named by
407
+
/// `ipdos_option_list`, and the value must be of the correct type.
408
+
pub extern "C" fn ipdos_solver_option_set(
409
+
solver: &mut IpdosSolver,
410
+
ident: *const ffi::c_char,
411
+
value: IpdosValue<'_>,
412
+
) -> bool {
413
+
let _ = solver;
414
+
let _ = ident;
415
+
let _ = value;
416
+
unimplemented!()
417
+
}
418
+
419
+
#[unsafe(no_mangle)]
420
+
/// Remove a model layer from the solver.
421
+
///
422
+
/// It is required that `model.layers` is less than the number of layers in the
423
+
/// last call to `ipdos_solver_push` or `ipdos_solver_pop`. It is allowed to
424
+
/// remove multiple model layers at the same time.
425
+
pub extern "C" fn ipdos_solver_pop(solver: &mut IpdosSolver, model: IpdosModel) {
426
+
let _ = solver;
427
+
let _ = model;
428
+
unimplemented!()
429
+
}
430
+
431
+
#[unsafe(no_mangle)]
432
+
/// Add a new model layer to the solver.
433
+
///
434
+
/// It is required that `model.layers` is exactly one larger than the previous
435
+
/// call to `ipdos_solver_push` or `ipdos_solver_pop`. It is NOT allowed to add
436
+
/// multiple model layers at the same time.
437
+
pub extern "C" fn ipdos_solver_push(solver: &mut IpdosSolver, model: IpdosModel) -> bool {
438
+
let _ = solver;
439
+
let _ = model;
440
+
unimplemented!()
441
+
}
442
+
443
+
#[unsafe(no_mangle)]
444
+
/// Read an error message from the solver.
445
+
///
446
+
/// This function is expected to be called after solver interactions signal an
447
+
/// error has occurred. For example, if [`ipdos_solver_run`] returns
448
+
/// [`IpdosError`] or [`ipdos_solver_push`] returns `false`.
449
+
pub extern "C" fn ipdos_solver_read_error(
450
+
solver: &mut IpdosSolver,
451
+
context: &mut ffi::c_void,
452
+
read_error: extern "C" fn(context: &mut ffi::c_void, error: *const ffi::c_char),
453
+
) {
454
+
let _ = solver;
455
+
let _ = context;
456
+
let _ = read_error;
457
+
unimplemented!()
458
+
}
459
+
460
+
#[unsafe(no_mangle)]
461
+
/// Run the solver with the given model
462
+
pub extern "C" fn ipdos_solver_run(
463
+
solver: &mut IpdosSolver,
464
+
context: &mut ffi::c_void,
465
+
on_solution: extern "C" fn(context: &mut ffi::c_void, solution: &IpdosSolution),
466
+
) -> IpdosStatus {
467
+
let _ = solver;
468
+
let _ = context;
469
+
let _ = on_solution;
470
+
unimplemented!()
471
+
}
472
+
473
+
#[cfg(test)]
474
+
mod tests {
475
+
use crate::{IpdosType, IpdosValue};
476
+
477
+
#[test]
478
+
fn memory() {
479
+
assert_eq!(size_of::<IpdosValue>(), size_of::<u128>());
480
+
assert_eq!(size_of::<IpdosType>(), size_of::<u64>());
481
+
}
482
+
}