Skip to content

Add ObjectivePkg for string-defined objective functions - #165

Open
cshenry wants to merge 1 commit into
ModelSEED:devfrom
cshenry:add-objective-package
Open

cshenry wants to merge 1 commit into
ModelSEED:devfrom
cshenry:add-objective-package

Conversation

@cshenry

@cshenry cshenry commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

What this accomplishes

Adds modelseedpy/fbapkg/objectivepkg.py and exports ObjectivePkg from modelseedpy.fbapkg.

It lets an objective be expressed as a string — MAX{(2)+rxn00001_c0|(3)rxn00002_c0} — parsed into a structured ObjectiveData, applied to a COBRApy model, cached by name, and restored later. A signed term weights one directional variable; an unsigned term weights both, with the reverse variable negated.

This is the mechanism MSGrowthPhenotypes uses to set a per-phenotype objective without the caller assembling optlang expressions by hand. It is a prerequisite for the phenotype-simulation work that follows, so it is submitted on its own first.

Class Responsibility
ObjectiveTerm one coefficient / variable / direction triple, with from_string and to_string
ObjectiveData a sense plus a term list; parses and emits the MAX{…} / MIN{…} form and builds the COBRApy objective
ObjectivePkg BaseFBAPkg subclass; build_package applies and caches an objective, restore_objective reinstates a cached one

This is a new file, so nothing existing imports it and the only change to current behaviour is one added export line.

Four defects fixed before submission

These were in the code as written and are fixed here rather than carried in:

  1. Silent numeric corruption. ObjectiveTerm.from_string truncated the coefficient to a single character — float(array[0][1]) — so "(12)" parsed as 1 and "(2.5)" as 2. Now float(array[0][1:]).
  2. String round-trip was lossy. to_string emitted a float repr, so a coefficient parsed from "(1)" came back as "(1.0)". Because build_package uses to_string() as both the cache key and the returned name, a caller could not restore_objective() with the string it had just passed in. Integral coefficients now emit without the trailing .0.
  3. build_package returned the wrong value. It returned its objective_name parameter rather than self.objective_name, so it returned None whenever the name was derived from the objective string.
  4. restore_objective discarded the cached original. It overwrote self.original_model_objective with the current objective before restoring, defeating the purpose of caching it in build_package.

Tests

tests/fbapkg/test_objectivepkg.py — 17 cases over an inline two-reaction model, covering term parsing (including the multi-digit and decimal regressions above), string round-trip for both senses, directional coefficient assignment, the missing-reaction warning, and the build / restore / cache paths.

No fixture files are used, so these tests do not depend on a biochemistry database or a commercial solver licence.

tests/fbapkg/test_objectivepkg.py  17 passed
tests/                             75 passed

Adds modelseedpy/fbapkg/objectivepkg.py and exports ObjectivePkg from
modelseedpy.fbapkg.

What this accomplishes: it lets an objective be expressed as a string --
MAX{(2)+rxn00001_c0|(3)rxn00002_c0} -- parsed into a structured
ObjectiveData, applied to a COBRApy model, cached by name, and restored
later. A signed term weights one directional variable; an unsigned term
weights both, with the reverse variable negated. This is the mechanism
MSGrowthPhenotypes uses to set a per-phenotype objective without the
caller assembling optlang expressions by hand.

Three classes:
  ObjectiveTerm  -- one coefficient/variable/direction triple, with
                    from_string and to_string
  ObjectiveData  -- a sense plus a term list; parses and emits the
                    MAX{...}/MIN{...} form and builds the COBRApy objective
  ObjectivePkg   -- BaseFBAPkg subclass; build_package applies and caches
                    an objective, restore_objective reinstates a cached one

Four defects were fixed before submission rather than carried in:

1. ObjectiveTerm.from_string truncated the coefficient to a single
   character -- float(array[0][1]) -- so "(12)" silently parsed as 1 and
   "(2.5)" as 2. Now float(array[0][1:]). This was silent numeric
   corruption of any coefficient that was not a single digit.
2. to_string emitted a float repr, so a coefficient parsed from "(1)" came
   back as "(1.0)" and from_string -> to_string was not lossless. Since
   build_package uses to_string() as both the cache key and the returned
   name, a caller could not restore_objective() with the string it had
   passed in. Integral coefficients now emit without the trailing ".0".
3. build_package returned its objective_name parameter rather than
   self.objective_name, so it returned None whenever the name was derived
   from the objective string.
4. restore_objective overwrote self.original_model_objective with the
   current objective before restoring, discarding the original that
   build_package had cached.

Tests: tests/fbapkg/test_objectivepkg.py, 17 cases over an inline
two-reaction model -- term parsing including the multi-digit and decimal
regressions, string round-trip for both senses, directional coefficient
assignment, the missing-reaction warning, and the build/restore/cache
paths. No fixture files, so the suite does not depend on a biochemistry
database or a commercial solver. Full suite: 75 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant