rust-or / rust-lp-modeler

Lp modeler written in Rust
MIT License
96 stars 29 forks source link

Consider transforming Status to Result #40

Open jcavat opened 5 years ago

jcavat commented 5 years ago

And putting a solution only for Optimal and Suboptimal.

This would avoid testing the context in many situation.

jcavat commented 5 years ago

Optimal/Suboptimal for something ok and grouping Infeasible/Unbounded and Notsolved into one case containing error message details.

zappolowski commented 5 years ago

Does this mean you're planning to treat Infeasible, Unbounded and/or Notsolved the same way as failing to read/write files or executing the solver? Or did you mean using Either instead of Result (which could be one interpretation of your 2nd comment) like:

Result<Either<WithSolution, WithoutSolution>, String>

I wouldn't aggregate status at this point already as this restricts the freedom of the consumer of this crate. Especially if the error cases are converted to String then, proper error handling will be messy and fragile (due to the need to match on these strings).

jcavat commented 5 years ago

I would prefer one of these solution :

1)

Result<Solution, Error>

2)

Create our own Result with somethink like (or a variant of) :

enum Result {
  Optimal(Solution),
  SubOptimal(Solution),
  SolutionError(String),
  Error(Error)
}

I would prefer solution 1 for my part

zappolowski commented 5 years ago

The first proposal would mix errors originating from actually trying to run the optimization (e.g. missing optimizer executable, unable to write either input or output files) with the infeasibility to find an optimal solution, which might in some cases be a valid outcome as well (e.g. for distribution problems Infeasible would indicate that the number of agents needs to be increased).

For the second proposal I see problems in error handling. I'm not sure, if it's easy to use ? in that case.

My proposal would be something like:

Result<Outcome, SomeErrorType or String>

with

enum Outcome {
    Optimal(Solution),
    SubOptimal(Solution),
    Infeasible,
    Unbounded,
}

(Outcome is the best I can come up with right now ... I'm bad at naming).

That way, one could use the standard error handling mechanisms and still has more fine grained control on how to interpret the actual optimization result. Usage in that case would roughly look like

let solution = match solver.run(&problem)? {
    Optimal(solution) | SubOptimal(solution) => solution,
    _ => return Err("boo"),
}