Skip to content

Added initial metabolic polytope simplification with HiGHS - #493

Open
DimitriosPavlouGR wants to merge 5 commits into
GeomScale:developfrom
DimitriosPavlouGR:gsoc-simplification
Open

Added initial metabolic polytope simplification with HiGHS#493
DimitriosPavlouGR wants to merge 5 commits into
GeomScale:developfrom
DimitriosPavlouGR:gsoc-simplification

Conversation

@DimitriosPavlouGR

Copy link
Copy Markdown

Summary

This PR adds simplification support for metabolic networks as a preprocessing step for sampling and volume approximation.

It includes:

  • A new class for metabolic network polytopes, MetabolicPolytope
  • A BiGG JSON model parser for metabolic networks
  • An LP based simplification routine built with HiGHS that relaxes redundant flux bounds and fixes degenerate dimensions.

Metabolic Polytope

convex_bodies/metabolic_polytope.h defines MetabolicPolytope, a metabolic network instance.

Parser

io/bigg_parser.hpp loads a standard BiGG JSON model and stores it in a MetabolicPolytope.

Simplification

simplification/warm_start.hpp simplifies the MetabolicPolytope by converting degenerate dimensions to equalities and redundant inequalities to relaxed +inf/-inf bounds.

The simplification happens in three key steps:

  • build_lp - constructs a HiGHS LP problem that describes the feasible region of the polytope.
  • build_simplified_polytope - constructs the output MetabolicPolytope directly from the HiGHS model, once at the end of the simplification.
  • simplify - iterates over all the variables until no simplifications can happen. For each variable it solves four LP problems to detect whether a bound is redundant, in which case it is relaxed, and whether the variable is pinned to a single value, in which case it is turned into an equality.

Tests

test_metabolic_polytope.cpp covers construction, test_highs covers general highs functionalities, and test_simplification covers simplification on general shapes such as a cube or a simplex.

Benchmarks

benchmark_simplification.cpp runs simplification across selected models present in test/bigg.

DimitriosPavlouGR and others added 2 commits June 28, 2026 21:02
MetabolicPolytope with sparse equality constraints, BiGG JSON parser, warm_start::simplify (relax redundant bounds, fix degenerate dimensions), tests, benchmark, and PolyRound comparison.

@vissarion vissarion left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR!

I wrote a couple of comments inline.

Also, json.h is an external file and should be placed in external directory together with some attribution note, license etc

Comment thread test/benchmark_simplification.cpp Outdated
VT const& getEqualityBounds() const { return this->b_eq; }

// @return true if the polytope has equality constraints
bool hasEqualities() const { return A_eq.rows() > 0; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not used and can simply computed as getNumEqualities()>0 if needed. So should we remove it?

unsigned d; // the dimension
MT A_eq; // equality constraint matrix
VT b_l, b_u; // lower & upper bounds on x
VT b_eq; // equality RHS, A_eq x = b_eq

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be in above line for readability. What is RHS?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will change the placement, RHS stands for right hand side of the equation A_eq x = b_eq. Do you want me to update the comment?

Comment thread include/io/bigg_parser.hpp Outdated
#include <string>
#include <limits>

namespace bigg {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

volesti does not uses namespaces this way, it is useful to use them but need to be part of a larger plan

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to other places where namespaces are introduced in this PR

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I remove them entirely?

Comment thread include/simplification/warm_start.hpp Outdated
Comment thread include/simplification/warm_start.hpp
Comment thread include/simplification/warm_start.hpp Outdated
Comment thread include/simplification/warm_start.hpp Outdated
Comment thread test/CMakeLists.txt
Polytope P2 = result.P;

CHECK(result.bounds_relaxed == 2*d);
CHECK(result.dims_fixed == d);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you end up with an empty polytope here? Could you add a not that extreme example? e.g. a 3d cube in a 10d space, a square in 3d?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, actually this test creates a 2d dimensional cube with d dimensions that are degenerate (we have 1 <= x_k <= 1+e^-12, for d variables). So after simplification the derived polytope should be a d-dimensional cube.

Notice that:

  • result.dims_fixed counts the number of added equalities, which should be d after simplification.
  • result.bounds_relaxed counts the number of relaxed inequalities, which should be 2d after simplification , because every degenerate dimension double inequality of the form 1 <= xk <= 1+e^-12, became the double inequality -inf <= xk <= inf (so we have 2 relaxations per degenerate dimension).

So this test tries to create the scenario you described.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. But should hold xk=1 not -inf <= xk <= inf after simplification

@DimitriosPavlouGR DimitriosPavlouGR Jun 30, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I wasn't clear. After simplification we will have xk=1, and this information will be encoded in A_eq and b_eq. However the flux bounds will be changed to bu(k)=inf, bl(k)=-inf.

The idea is that once we get to the transformation step, we can easily filter the non-redundant constraints by checking if the inequalities are of the form: xk <= inf or xk >= -inf. Once we discard these bounds, we will derive a simplified polytope A x <= b. And then we can proceed with solving the system A_eq x = b_eq (where xk= 1 is encoded) and deriving a particular solution x0 which can then be used to drop the equalities and form the final h-polytope A' x <= b'.

This is what I had in mind, let me know if you have any other ideas.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK this is how polyround solves the problem, right? We can either follow this or merge simplification and transformation in one function. But at this point this PR is OK.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's essentially the same thing. The difference is that in their code they explicitly remove the inequalities A_i x<=b_i that are redundant at the simplification phase. That's natural for their implementation because they use the representation: A_eq x = b_eq, A x <= b.

However, due to the formulation we use i.e.: A_eq x = b_eq, bl <= x <= bu, I think it's more natural for us to remove them implicitly (i.e. by marking them as redundant) at the simplification phase by setting bl(k)/bu(k) to -inf/+inf. Then once we get to the transformation phase of the algorithm, we can remove them explicitly by not adding them to the h-polytope Ax <= b. (*)

(*) The important thing (which both us and polyround do) is that we drop the redundant constraints bl(k) <= xk <= bu(k) immediately from the lp model, so that subsequent lp calls take less time. Keeping them temporarily in the metabolic polytope as bl(k)=-inf and bu(k)=inf does not affect the running time of the method and it's more or less a design choice.

@vissarion

Copy link
Copy Markdown
Member

Also it seems that some tests are failing. Could you please check them out?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants