Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add binary and/or integer variables #187

Open
5 tasks
jetuk opened this issue May 12, 2024 · 1 comment
Open
5 tasks

Add binary and/or integer variables #187

jetuk opened this issue May 12, 2024 · 1 comment
Labels
enhancement New feature or request rust Pull requests that update Rust code

Comments

@jetuk
Copy link
Member

jetuk commented May 12, 2024

Link to the Pywr v1.x issue: pywr/pywr#702

Following use cases

Discrete flow

The flow in a given edge should only be a discrete value. For example, 0 or 10.0 (but no other value). This will require adding a binary (or integer) value and relating it to the capacity constraints.

Current max capacity constraint: $x_e \le b_e^{max}$

With discrete variable: $x_e - x_e^b b_e^{max} = 0$

The min flow constraints will be similar.

The user should be able to associate a cost with the variable. However, the units might get confusing.

Mutually exclusive flows

There can only be flow in one edge from a given set. This will require two new constraints to be added. The first is the same as above where the flow capacity is constrained by a new variable $x_e^b$. Then a second constraint is added which ensures that only one of the binaries can "on": $\sum_{e \in E} x_e \le 1$. An alternative could be an equality constraint to require one of the set to be active.

If this is implemented it would allow a more correct minimum flow to be set when configuring a bi-directional transfer.

Schema

How would this look to the user? Ideally we would have a DiscreteLink or similar node that defined the discrete flow requirement. The mutual exclusivity one is similar to an aggregated node, and perhaps could be an optional field on AggregatedNode that, if defined, would create the additional constraints.

Implementation challenges

  • This feature would require a MILP solver. CBC is the obvious choice as we are already using CLP. Highs is also MILP capable.
  • Solvers that cannot handle this feature should error when given a model with them in. See solver features.
  • Adding the additional constraints and updating them will require some additional methods in the solver builder routines.
  • I suggest we try just binary variables to begin with.
  • The value of these variables should be available via metrics/attributes (e.g. isActive for the new DiscreteLink) for use in the model and/or output.
@jetuk jetuk added enhancement New feature or request rust Pull requests that update Rust code labels May 12, 2024
jetuk added a commit that referenced this issue Jun 7, 2024
This is the first commit of using the Highs solver to model
binary variables, and using those variables to apply a mutual
exclusivity constraint. I.e. flow is allow through up to N nodes
at a time.

TODO:
- [ ] Add support in pywr-schema.
- [ ] Implement in CLP/CBC solver.
- [ ] Add additional tests.

This starts to resolve issue #187.
jetuk added a commit that referenced this issue Jun 7, 2024
This is the first commit of using the Highs solver to model
binary variables, and using those variables to apply a mutual
exclusivity constraint. I.e. flow is allow through up to N nodes
at a time.

TODO:
- [ ] Add support in pywr-schema.
- [ ] Implement in CLP/CBC solver.
- [ ] Add additional tests.

This starts to resolve issue #187.
jetuk added a commit that referenced this issue Jul 9, 2024
This is the first commit of using the Highs solver to model
binary variables, and using those variables to apply a mutual
exclusivity constraint. I.e. flow is allow through up to N nodes
at a time.

TODO:
- [ ] Add support in pywr-schema.
- [ ] Implement in CLP/CBC solver.
- [ ] Add additional tests.

This starts to resolve issue #187.
@jetuk
Copy link
Member Author

jetuk commented Jul 9, 2024

The mutual exclusivity one is similar to an aggregated node, and perhaps could be an optional field on AggregatedNode that, if defined, would create the additional constraints.

See below for a suggestion on this. Here we use the Factors enum to specify that the nodes in this AggregatedNode are mutually exclusive. This implies (I think correctly) that you can't have regular flow factors across mutually nodes. @Batch21 @s-simoncelli any thoughts on this?

#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema, PywrVisitAll)]
#[serde(tag = "type")]
pub enum Factors {
    Proportion {
        factors: Vec<Metric>,
    },
    Ratio {
        factors: Vec<Metric>,
    },
    /// Flows are exclusive with at least `min` and at most `max` nodes active.
    Exclusive {
        min: Option<usize>,
        max: Option<usize>,
    },
}

#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, JsonSchema, PywrVisitAll)]
pub struct AggregatedNode {
    #[serde(flatten)]
    pub meta: NodeMeta,
    pub nodes: Vec<String>,
    pub max_flow: Option<Metric>,
    pub min_flow: Option<Metric>,
    pub factors: Option<Factors>,
}

jetuk added a commit that referenced this issue Aug 2, 2024
This is the first commit of using the Highs solver to model
binary variables, and using those variables to apply a mutual
exclusivity constraint. I.e. flow is allow through up to N nodes
at a time.

TODO:
- [ ] Add support in pywr-schema.
- [ ] Implement in CLP/CBC solver.
- [ ] Add additional tests.

This starts to resolve issue #187.
jetuk added a commit that referenced this issue Sep 23, 2024
This is the initial support for using binary variables to apply a mutual
exclusivity constraint. I.e. flow is allow through up to N nodes
at a time. Aggregated node is refactored to allow
support for sets of nodes. These are typically created when a
compound node (e.g. PiecewiseLink) is added to an AggregatedNode.
The compound node creates multiple "core" nodes, and these should
all be constrained by the AggregatedNode's factors or exclusivity.

The same consideration applies to the virtual storage nodes.

To do this there is a new method on Node in the schema to retrieve
the core node indices which that node has added to the core model.
For now this is a static implementation, but there are some nodes
which the user may to configure how this is done. E.g. should a
LossLink be constrained on its net or gross flow.

Renames aggregated node `Factors` to `Relationship`.

This starts to resolve issue #187.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request rust Pull requests that update Rust code
Projects
None yet
Development

No branches or pull requests

1 participant