Skip to content

Commit

Permalink
Merge pull request #19 from zhanglab/python3-six
Browse files Browse the repository at this point in the history
Python3 support through six module
  • Loading branch information
jonls committed Jul 1, 2015
2 parents 5a07c29 + 0287ced commit 8f51b42
Show file tree
Hide file tree
Showing 28 changed files with 328 additions and 267 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ install: pip install tox
script: tox -v
env:
- TOXENV=py27-nosolver
- TOXENV=py34-nosolver
- TOXENV=docs
5 changes: 3 additions & 2 deletions psamm/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,13 @@
from . import fluxanalysis, massconsistency, fastcore
from .lpsolver import generic

from six import add_metaclass

# Module-level logging
logger = logging.getLogger(__name__)


@add_metaclass(abc.ABCMeta)
class Command(object):
"""Represents a command in the interface, operating on a model
Expand All @@ -65,8 +68,6 @@ class Command(object):
namespace will be passed to the constructor.
"""

__metaclass__ = abc.ABCMeta

def __init__(self, model, args):
self._model = model
self._args = args
Expand Down
9 changes: 5 additions & 4 deletions psamm/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import abc
from collections import defaultdict, Mapping

from six import iteritems, add_metaclass

from .reaction import Reaction


Expand Down Expand Up @@ -91,11 +93,10 @@ def __array__(self):
return matrix


@add_metaclass(abc.ABCMeta)
class MetabolicDatabase(object):
"""Database of metabolic reactions"""

__metaclass__ = abc.ABCMeta

@abc.abstractproperty
def reactions(self):
"""Iterator of reactions IDs in the database"""
Expand Down Expand Up @@ -198,7 +199,7 @@ def is_reversible(self, reaction_id):
def get_reaction_values(self, reaction_id):
if reaction_id not in self._reactions:
raise ValueError('Unknown reaction: {}'.format(repr(reaction_id)))
return self._reactions[reaction_id].iteritems()
return iteritems(self._reactions[reaction_id])

def get_compound_reactions(self, compound_id):
return iter(self._compound_reactions[compound_id])
Expand Down Expand Up @@ -235,7 +236,7 @@ def set_reaction(self, reaction_id, reaction):
# Remove reaction from compound reactions if the resulting
# stoichiometric value turned out to be zero.
zero_compounds = set()
for compound, value in self._reactions[reaction_id].iteritems():
for compound, value in iteritems(self._reactions[reaction_id]):
if value == 0:
zero_compounds.add(compound)

Expand Down
5 changes: 3 additions & 2 deletions psamm/datasource/native.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import csv

import yaml
from six import string_types

from ..reaction import Reaction, Compound
from . import modelseed
Expand Down Expand Up @@ -59,7 +60,7 @@ class FilePathContext(object):
def __init__(self, arg):
"""Create new context from a path or existing context"""

if isinstance(arg, basestring):
if isinstance(arg, string_types):
self._filepath = arg
else:
self._filepath = arg.filepath
Expand Down Expand Up @@ -373,7 +374,7 @@ def parse_compound_list(l):
compound = Compound(compound_id, compartment=compound_compartment)
yield compound, value

if isinstance(equation_def, basestring):
if isinstance(equation_def, string_types):
return modelseed.parse_reaction(equation_def).normalized()

compartment = equation_def.get('compartment', None)
Expand Down
6 changes: 4 additions & 2 deletions psamm/datasource/sbml.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from functools import partial
from itertools import count

from six import itervalues

from ..database import MetabolicDatabase, DictDatabase
from ..reaction import Reaction, Compound

Expand Down Expand Up @@ -359,15 +361,15 @@ def get_species(self, species_id):
@property
def reactions(self):
"""Iterator over :class:`ReactionEntries <.ReactionEntry>`"""
return self._model_reactions.itervalues()
return itervalues(self._model_reactions)

@property
def species(self):
"""Iterator over :class:`SpeciesEntries <.SpeciesEntry>`
This will not yield boundary condition species if those are ignored.
"""
return (c for c in self._model_species.itervalues()
return (c for c in itervalues(self._model_species)
if not self._ignore_boundary or not c.boundary)

@property
Expand Down
45 changes: 34 additions & 11 deletions psamm/expression/affine.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import functools
from collections import Counter, defaultdict

from six import string_types, iteritems, iterkeys, itervalues


@functools.total_ordering
class Variable(object):
Expand Down Expand Up @@ -103,6 +105,11 @@ def __rmul__(self, other):
def __div__(self, other):
return Expression({ self: 1 }) / other

__truediv__ = __div__

def __floordiv__(self, other):
return Expression({ self: 1}) // other

def __neg__(self):
return Expression({ self: -1 })

Expand Down Expand Up @@ -142,15 +149,15 @@ def __init__(self, *args):
Expression('x + y')
"""

if len(args) == 1 and isinstance(args[0], basestring):
if len(args) == 1 and isinstance(args[0], string_types):
# Parse as string
self._variables, self._offset = self._parse_string(args[0])
elif len(args) <= 2:
self._variables = {}
self._offset = args[1] if len(args) >= 2 else 0

variables = args[0] if len(args) >= 1 else {}
for var, value in variables.iteritems():
for var, value in iteritems(variables):
if not isinstance(var, Variable):
raise ValueError('Not a variable: {}'.format(var))
if value != 0:
Expand Down Expand Up @@ -209,7 +216,8 @@ def _parse_string(self, s):
raise ValueError('Invalid token in expression string: {}'.format(match.group(0)))

# Remove zero-coefficient elements
variables = { var: value for var, value in variables.iteritems() if value != 0 }
variables = {var: value for var, value in iteritems(variables)
if value != 0}
return variables, offset

def simplify(self):
Expand All @@ -223,7 +231,7 @@ def simplify(self):
if len(result._variables) == 0:
return result._offset
elif len(result._variables) == 1 and result._offset == 0:
var, value = next(result._variables.iteritems())
var, value = next(iteritems(result._variables))
if value == 1:
return var
return result
Expand All @@ -237,7 +245,7 @@ def substitute(self, mapping):
Expression('x + 2z')
"""
expr = self.__class__()
for var, value in self._variables.iteritems():
for var, value in iteritems(self._variables):
expr += value * var.substitute(mapping)
return (expr + self._offset).simplify()

Expand All @@ -255,7 +263,8 @@ def __add__(self, other):
elif isinstance(other, Expression):
variables = Counter(self._variables)
variables.update(other._variables)
variables = { var: value for var, value in variables.iteritems() if value != 0 }
variables = {var: value for var, value in iteritems(variables)
if value != 0 }
return self.__class__(variables, self._offset + other._offset)
return NotImplemented

Expand All @@ -274,7 +283,9 @@ def __mul__(self, other):
if isinstance(other, numbers.Number):
if other == 0:
return self.__class__()
return self.__class__({ var: value*other for var, value in self._variables.iteritems()}, self._offset*other)
return self.__class__({var: value*other for var, value
in iteritems(self._variables)},
self._offset*other)
return NotImplemented

def __rmul__(self, other):
Expand All @@ -283,7 +294,18 @@ def __rmul__(self, other):
def __div__(self, other):
"""Divide by scalar"""
if isinstance(other, numbers.Number):
return self.__class__({ var: value/other for var, value in self._variables.iteritems()}, self._offset/other)
return self.__class__({var: value / other for var, value
in iteritems(self._variables)},
self._offset / other)
return NotImplemented

__truediv__ = __div__

def __floordiv__(self, other):
if isinstance(other, numbers.Number):
return self.__class__({var: value // other for val, value
in iteritems(self._variables)},
self._offset // other)
return NotImplemented

def __neg__(self):
Expand All @@ -297,8 +319,8 @@ def __eq__(self, other):
# Check that there is just one variable in the expression
# with a coefficient of one.
return (self._offset == 0 and len(self._variables) == 1 and
next(self._variables.iterkeys()) == other and
next(self._variables.itervalues()) == 1)
next(iterkeys(self._variables)) == other and
next(itervalues(self._variables)) == 1)
elif isinstance(other, numbers.Number):
return len(self._variables) == 0 and self._offset == other
return False
Expand All @@ -309,7 +331,8 @@ def __ne__(self, other):
def __str__(self):
def all_terms():
count_vars = 0
for symbol, value in sorted((var.symbol, value) for var, value in self._variables.iteritems()):
for symbol, value in sorted((var.symbol, value) for var, value
in iteritems(self._variables)):
if value != 0:
count_vars += 1
yield symbol, value
Expand Down
10 changes: 6 additions & 4 deletions psamm/fastcore.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
from .fluxanalysis import flux_balance
from .metabolicmodel import FlipableModelView

from six import iteritems

# Module-level logging
logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -89,11 +91,11 @@ def lp7(model, reaction_subset, epsilon, solver):
prob.add_linear_constraints(v >= z)

massbalance_lhs = { compound: 0 for compound in model.compounds }
for spec, value in model.matrix.iteritems():
for spec, value in iteritems(model.matrix):
compound, rxnid = spec
massbalance_lhs[compound] += prob.var(('v', rxnid)) * value
prob.add_linear_constraints(
*(lhs == 0 for compound, lhs in massbalance_lhs.iteritems()))
*(lhs == 0 for compound, lhs in iteritems(massbalance_lhs)))

# Solve
result = prob.solve(lp.ObjectiveSense.Maximize)
Expand Down Expand Up @@ -136,11 +138,11 @@ def lp10(model, subset_k, subset_p, epsilon, scaling, solver, weights={}):
prob.add_linear_constraints(z >= v, v >= -z)

massbalance_lhs = { compound: 0 for compound in model.compounds }
for spec, value in model.matrix.iteritems():
for spec, value in iteritems(model.matrix):
compound, rxnid = spec
massbalance_lhs[compound] += prob.var(('v', rxnid)) * value
prob.add_linear_constraints(
*(lhs == 0 for compound, lhs in massbalance_lhs.iteritems()))
*(lhs == 0 for compound, lhs in iteritems(massbalance_lhs)))

# Solve
result = prob.solve(lp.ObjectiveSense.Minimize)
Expand Down
20 changes: 11 additions & 9 deletions psamm/fluxanalysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

from .lpsolver import lp

from six import iteritems

# Module-level logging
logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -56,10 +58,10 @@ def __init__(self, model, solver):

# Define constraints
massbalance_lhs = { compound: 0 for compound in model.compounds }
for spec, value in model.matrix.iteritems():
for spec, value in iteritems(model.matrix):
compound, reaction_id = spec
massbalance_lhs[compound] += self.get_flux_var(reaction_id) * value
for compound, lhs in massbalance_lhs.iteritems():
for compound, lhs in iteritems(massbalance_lhs):
self._prob.add_linear_constraints(lhs == 0)

@property
Expand All @@ -77,7 +79,7 @@ def solve(self, reaction):

if isinstance(reaction, dict):
objective = sum(v * self.get_flux_var(r)
for r, v in reaction.iteritems())
for r, v in iteritems(reaction))
else:
objective = self.get_flux_var(reaction)

Expand Down Expand Up @@ -132,11 +134,11 @@ def __init__(self, model, solver, epsilon=1e-5, em=1e5):
p.define(*(('mu', compound) for compound in model.compounds))

tdbalance_lhs = {reaction_id: 0 for reaction_id in model.reactions}
for spec, value in model.matrix.iteritems():
for spec, value in iteritems(model.matrix):
compound, reaction_id = spec
if not model.is_exchange(reaction_id):
tdbalance_lhs[reaction_id] += p.var(('mu', compound)) * value
for reaction_id, lhs in tdbalance_lhs.iteritems():
for reaction_id, lhs in iteritems(tdbalance_lhs):
if not model.is_exchange(reaction_id):
p.add_linear_constraints(lhs == p.var(('dmu', reaction_id)))

Expand Down Expand Up @@ -189,7 +191,7 @@ def flux_variability(model, reactions, fixed, tfba, solver):

fba = _get_fba_problem(model, tfba, solver)

for reaction_id, value in fixed.iteritems():
for reaction_id, value in iteritems(fixed):
flux = fba.get_flux_var(reaction_id)
fba.prob.add_linear_constraints(flux >= value)

Expand Down Expand Up @@ -241,10 +243,10 @@ def flux_minimization(model, fixed, solver, weights={}):
prob.add_linear_constraints(z >= v, v >= -z)

massbalance_lhs = { compound: 0 for compound in model.compounds }
for spec, value in model.matrix.iteritems():
for spec, value in iteritems(model.matrix):
compound, rxnid = spec
massbalance_lhs[compound] += value * prob.var(('v', rxnid))
for compound, lhs in massbalance_lhs.iteritems():
for compound, lhs in iteritems(massbalance_lhs):
prob.add_linear_constraints(lhs == 0)

# Solve
Expand Down Expand Up @@ -285,7 +287,7 @@ def flux_randomization(model, fixed, tfba, solver):
optimize[reaction_id] = random.random()

fba = _get_fba_problem(model, tfba, solver)
for reaction_id, value in fixed.iteritems():
for reaction_id, value in iteritems(fixed):
fba.prob.add_linear_constraints(fba.get_flux_var(reaction_id) >= value)

fba.solve(optimize)
Expand Down

0 comments on commit 8f51b42

Please sign in to comment.