def release(self):
        """Release the currently held lock.

        In case the current thread holds no lock, a ValueError is thrown."""

        me = currentThread()
        self.__condition.acquire()
        try:
            if self.__writer is me:
                # We are the writer, take one nesting depth away.
                self.__writercount -= 1
                if not self.__writercount:
                    # No more write locks; take our writer position away and
                    # notify waiters of the new circumstances.
                    self.__writer = None
                    self.__condition.notifyAll()
            elif me in self.__readers:
                # We are a reader currently, take one nesting depth away.
                self.__readers[me] -= 1
                if not self.__readers[me]:
                    # No more read locks, take our reader position away.
                    del self.__readers[me]
                    if not self.__readers:
                        # No more readers, notify waiters of the new
                        # circumstances.
                        self.__condition.notifyAll()
            else:
                raise ValueError("Trying to release unheld lock")
        finally:
            self.__condition.release()


def init_logging(config):
    """Initialize base logger named 'wsgidav'.

    The base logger is filtered by the `verbose` configuration option.
    Log entries will have a time stamp and thread id.

    :Parameters:
        verbose : int
            Verbosity configuration (0..5)
        enable_loggers : string list
            List of module logger names, that will be switched to DEBUG level.

    Module loggers
    ~~~~~~~~~~~~~~
    Module loggers (e.g 'wsgidav.lock_manager') are named loggers, that can be
    independently switched to DEBUG mode.

    Except for verbosity, they will inherit settings from the base logger.

    They will suppress DEBUG level messages, unless they are enabled by passing
    their name to util.init_logging().

    If enabled, module loggers will print DEBUG messages, even if verbose == 3.

    Example initialize and use a module logger, that will generate output,
    if enabled (and verbose >= 2)::

        _logger = util.get_module_logger(__name__)
        [..]
        _logger.debug("foo: '{}'".format(s))

    This logger would be enabled by passing its name to init_logging()::

        enable_loggers = ["lock_manager",
                          "property_manager",
                         ]
        util.init_logging(2, enable_loggers)

    Log Level Matrix
    ~~~~~~~~~~~~~~~~

    +---------+--------+---------------------------------------------------------------+
    | Verbose | Option |                       Log level                               |
    | level   |        +-------------+------------------------+------------------------+
    |         |        | base logger | module logger(default) | module logger(enabled) |
    +=========+========+=============+========================+========================+
    |    0    | -qqq   | CRITICAL    | CRITICAL               | CRITICAL               |
    +---------+--------+-------------+------------------------+------------------------+
    |    1    | -qq    | ERROR       | ERROR                  | ERROR                  |
    +---------+--------+-------------+------------------------+------------------------+
    |    2    | -q     | WARN        | WARN                   | WARN                   |
    +---------+--------+-------------+------------------------+------------------------+
    |    3    |        | INFO        | INFO                   | **DEBUG**              |
    +---------+--------+-------------+------------------------+------------------------+
    |    4    | -v     | DEBUG       | DEBUG                  | DEBUG                  |
    +---------+--------+-------------+------------------------+------------------------+
    |    5    | -vv    | DEBUG       | DEBUG                  | DEBUG                  |
    +---------+--------+-------------+------------------------+------------------------+

    """
    verbose = config.get("verbose", 3)

    enable_loggers = config.get("enable_loggers", [])
    if enable_loggers is None:
        enable_loggers = []

    logger_date_format = config.get("logger_date_format", "%Y-%m-%d %H:%M:%S")
    logger_format = config.get(
        "logger_format",
        "%(asctime)s.%(msecs)03d - <%(thread)d> %(name)-27s %(levelname)-8s:  %(message)s",
    )

    formatter = logging.Formatter(logger_format, logger_date_format)

    # Define handlers
    consoleHandler = logging.StreamHandler(sys.stdout)
    #    consoleHandler = logging.StreamHandler(sys.stderr)
    consoleHandler.setFormatter(formatter)
    # consoleHandler.setLevel(logging.DEBUG)

    # Add the handlers to the base logger
    logger = logging.getLogger(BASE_LOGGER_NAME)

    if verbose >= 4:  # --verbose
        logger.setLevel(logging.DEBUG)
    elif verbose == 3:  # default
        logger.setLevel(logging.INFO)
    elif verbose == 2:  # --quiet
        logger.setLevel(logging.WARN)
        # consoleHandler.setLevel(logging.WARN)
    elif verbose == 1:  # -qq
        logger.setLevel(logging.ERROR)
        # consoleHandler.setLevel(logging.WARN)
    else:  # -qqq
        logger.setLevel(logging.CRITICAL)
        # consoleHandler.setLevel(logging.ERROR)

    # Don't call the root's handlers after our custom handlers
    logger.propagate = False

    # Remove previous handlers
    for hdlr in logger.handlers[:]:  # Must iterate an array copy
        try:
            hdlr.flush()
            hdlr.close()
        except Exception:
            pass
        logger.removeHandler(hdlr)

    logger.addHandler(consoleHandler)

    if verbose >= 3:
        for e in enable_loggers:
            if not e.startswith(BASE_LOGGER_NAME + "."):
                e = BASE_LOGGER_NAME + "." + e
            lg = logging.getLogger(e.strip())
            lg.setLevel(logging.DEBUG)


def dynamic_instantiate_middleware(name, args, expand=None):
    """Import a class and instantiate with custom args.

    Example:
        name = "my.module.Foo"
        args_dict = {
            "bar": 42,
            "baz": "qux"
            }
        =>
        from my.module import Foo
        return Foo(bar=42, baz="qux")
    """

    def _expand(v):
        """Replace some string templates with defined values."""
        if expand and compat.is_basestring(v) and v.lower() in expand:
            return expand[v]
        return v

    try:
        the_class = dynamic_import_class(name)
        inst = None
        if type(args) in (tuple, list):
            args = tuple(map(_expand, args))
            inst = the_class(*args)
        else:
            assert type(args) is dict
            args = {k: _expand(v) for k, v in args.items()}
            inst = the_class(**args)

        _logger.debug("Instantiate {}({}) => {}".format(name, args, inst))
    except Exception:
        _logger.exception("ERROR: Instantiate {}({}) => {}".format(name, args, inst))

    return inst

Import a class and instantiate with custom args .


def string_repr(s):
    """Return a string as hex dump."""
    if compat.is_bytes(s):
        res = "{!r}: ".format(s)
        for b in s:
            if type(b) is str:  # Py2
                b = ord(b)
            res += "%02x " % b
        return res
    return "{}".format(s)

def
string_repr
(
s
)
:
if
compat
.
is_bytes
(
s
)
:
res
=
"{!r}: "
.
format
(
s
)
for
b
in
s
:
if
type
(
b
)
is
str
:
# Py2
b
=
ord
(
b
)
res
+=
"%02x "
%
b
return
res
return
"{}"
.
format
(
s
)


def byte_number_string(
    number, thousandsSep=True, partition=False, base1024=True, appendBytes=True
):
    """Convert bytes into human-readable representation."""
    magsuffix = ""
    bytesuffix = ""

    if partition:
        magnitude = 0
        if base1024:
            while number >= 1024:
                magnitude += 1
                number = number >> 10
        else:
            while number >= 1000:
                magnitude += 1
                number /= 1000.0
        # TODO: use "9 KB" instead of "9K Bytes"?
        # TODO use 'kibi' for base 1024?
        # http://en.wikipedia.org/wiki/Kibi-#IEC_standard_prefixes
        magsuffix = ["", "K", "M", "G", "T", "P"][magnitude]

    if appendBytes:
        if number == 1:
            bytesuffix = " Byte"
        else:
            bytesuffix = " Bytes"

    if thousandsSep and (number >= 1000 or magsuffix):
        # locale.setlocale(locale.LC_ALL, "")
        # # TODO: make precision configurable
        # snum = locale.format("%d", number, thousandsSep)
        snum = "{:,d}".format(number)
    else:
        snum = str(number)

    return "{}{}{}".format(snum, magsuffix, bytesuffix)

Convert bytes into human - readable representation .


def read_and_discard_input(environ):
    """Read 1 byte from wsgi.input, if this has not been done yet.

    Returning a response without reading from a request body might confuse the
    WebDAV client.
    This may happen, if an exception like '401 Not authorized', or
    '500 Internal error' was raised BEFORE anything was read from the request
    stream.

    See GC issue 13, issue 23
    See http://groups.google.com/group/paste-users/browse_frm/thread/fc0c9476047e9a47?hl=en

    Note that with persistent sessions (HTTP/1.1) we must make sure, that the
    'Connection: closed' header is set with the response, to prevent reusing
    the current stream.
    """
    if environ.get("wsgidav.some_input_read") or environ.get("wsgidav.all_input_read"):
        return
    cl = get_content_length(environ)
    assert cl >= 0
    if cl == 0:
        return

    READ_ALL = True

    environ["wsgidav.some_input_read"] = 1
    if READ_ALL:
        environ["wsgidav.all_input_read"] = 1

    wsgi_input = environ["wsgi.input"]

    # TODO: check if still required after GC issue 24 is fixed
    if hasattr(wsgi_input, "_consumed") and hasattr(wsgi_input, "length"):
        # Seems to be Paste's httpserver.LimitedLengthFile
        # see http://groups.google.com/group/paste-users/browse_thread/thread/fc0c9476047e9a47/aa4a3aa416016729?hl=en&lnk=gst&q=.input#aa4a3aa416016729  # noqa
        # Consume something if nothing was consumed *and* work
        # around a bug where paste.httpserver allows negative lengths
        if wsgi_input._consumed == 0 and wsgi_input.length > 0:
            # This seems to work even if there's 10K of input.
            if READ_ALL:
                n = wsgi_input.length
            else:
                n = 1
            body = wsgi_input.read(n)
            _logger.debug(
                "Reading {} bytes from potentially unread httpserver.LimitedLengthFile: '{}'...".format(
                    n, body[:50]
                )
            )

    elif hasattr(wsgi_input, "_sock") and hasattr(wsgi_input._sock, "settimeout"):
        # Seems to be a socket
        try:
            # Set socket to non-blocking
            sock = wsgi_input._sock
            timeout = sock.gettimeout()
            sock.settimeout(0)
            # Read one byte
            try:
                if READ_ALL:
                    n = cl
                else:
                    n = 1
                body = wsgi_input.read(n)
                _logger.debug(
                    "Reading {} bytes from potentially unread POST body: '{}'...".format(
                        n, body[:50]
                    )
                )
            except socket.error as se:
                # se(10035, 'The socket operation could not complete without blocking')
                _logger.error("-> read {} bytes failed: {}".format(n, se))
            # Restore socket settings
            sock.settimeout(timeout)
        except Exception:
            _logger.error("--> wsgi_input.read(): {}".format(sys.exc_info()))

Read 1 byte from wsgi . input if this has not been done yet .


def join_uri(uri, *segments):
    """Append segments to URI.

    Example: join_uri("/a/b", "c", "d")
    """
    sub = "/".join(segments)
    if not sub:
        return uri
    return uri.rstrip("/") + "/" + sub

def join_uri ( uri , * segments ) : sub = "/" . join ( segments ) if not sub : return uri return uri . rstrip ( "/" ) + "/" + sub


def is_child_uri(parentUri, childUri):
    """Return True, if childUri is a child of parentUri.

    This function accounts for the fact that '/a/b/c' and 'a/b/c/' are
    children of '/a/b' (and also of '/a/b/').
    Note that '/a/b/cd' is NOT a child of 'a/b/c'.
    """
    return (
        parentUri
        and childUri
        and childUri.rstrip("/").startswith(parentUri.rstrip("/") + "/")
    )

def is_child_uri ( parentUri , childUri ) : return ( parentUri and childUri and childUri . rstrip ( "/" ) . startswith ( parentUri . rstrip ( "/" ) + "/" ) )

Return True if childUri is a child of parentUri .


def is_equal_or_child_uri(parentUri, childUri):
    """Return True, if childUri is a child of parentUri or maps to the same resource.

    Similar to <util.is_child_uri>_ ,  but this method also returns True, if parent
    equals child. ('/a/b' is considered identical with '/a/b/').
    """
    return (
        parentUri
        and childUri
        and (childUri.rstrip("/") + "/").startswith(parentUri.rstrip("/") + "/")
    )

def is_equal_or_child_uri ( parentUri , childUri ) : return ( parentUri and childUri and ( childUri . rstrip ( "/" ) + "/" ) . startswith ( parentUri . rstrip ( "/" ) + "/" ) )

Return True if childUri is a child of parentUri or maps to the same resource .


def make_complete_url(environ, localUri=None):
    """URL reconstruction according to PEP 333.
    @see https://www.python.org/dev/peps/pep-3333/#url-reconstruction
    """
    url = environ["wsgi.url_scheme"] + "://"

    if environ.get("HTTP_HOST"):
        url += environ["HTTP_HOST"]
    else:
        url += environ["SERVER_NAME"]

        if environ["wsgi.url_scheme"] == "https":
            if environ["SERVER_PORT"] != "443":
                url += ":" + environ["SERVER_PORT"]
        else:
            if environ["SERVER_PORT"] != "80":
                url += ":" + environ["SERVER_PORT"]

    url += compat.quote(environ.get("SCRIPT_NAME", ""))

    if localUri is None:
        url += compat.quote(environ.get("PATH_INFO", ""))
        if environ.get("QUERY_STRING"):
            url += "?" + environ["QUERY_STRING"]
    else:
        url += localUri  # TODO: quote?
    return url

def
make_complete_url
(
environ
,
localUri
=
)
:
url
=
environ
[
"wsgi.url_scheme"
]
+
"://"
if
environ
.
get
(
"HTTP_HOST"
)
:
url
+=
environ
[
"HTTP_HOST"
]
else
:
url
+=
environ
[
"SERVER_NAME"
]
if
environ
[
"wsgi.url_scheme"
]
==
"https"
:
if
environ
[
"SERVER_PORT"
]
!=
"443"
:
url
+=
":"
+
environ
[
"SERVER_PORT"
]
else
:
if
environ
[
"SERVER_PORT"
]
!=
"80"
:
url
+=
":"
+
environ
[
"SERVER_PORT"
]
url
+=
compat
.
quote
(
environ
.
get
(
"SCRIPT_NAME"
,
""
)
)
if
localUri
is
:
url
+=
compat
.
quote
(
environ
.
get
(
"PATH_INFO"
,
""
)
)
if
environ
.
get
(
"QUERY_STRING"
)
:
url
+=
"?"
+
environ
[
"QUERY_STRING"
]
else
:
url
+=
localUri
# TODO: quote?
return
url

URL reconstruction according to PEP 333 .


def parse_xml_body(environ, allow_empty=False):
    """Read request body XML into an etree.Element.

    Return None, if no request body was sent.
    Raise HTTP_BAD_REQUEST, if something else went wrong.

    TODO: this is a very relaxed interpretation: should we raise HTTP_BAD_REQUEST
    instead, if CONTENT_LENGTH is missing, invalid, or 0?

    RFC: For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing
    a message-body MUST include a valid Content-Length header field unless the
    server is known to be HTTP/1.1 compliant.
    If a request contains a message-body and a Content-Length is not given, the
    server SHOULD respond with 400 (bad request) if it cannot determine the
    length of the message, or with 411 (length required) if it wishes to insist
    on receiving a valid Content-Length."

    So I'd say, we should accept a missing CONTENT_LENGTH, and try to read the
    content anyway.
    But WSGI doesn't guarantee to support input.read() without length(?).
    At least it locked, when I tried it with a request that had a missing
    content-type and no body.

    Current approach: if CONTENT_LENGTH is

    - valid and >0:
      read body (exactly <CONTENT_LENGTH> bytes) and parse the result.
    - 0:
      Assume empty body and return None or raise exception.
    - invalid (negative or not a number:
      raise HTTP_BAD_REQUEST
    - missing:
      NOT: Try to read body until end and parse the result.
      BUT: assume '0'
    - empty string:
      WSGI allows it to be empty or absent: treated like 'missing'.
    """
    #
    clHeader = environ.get("CONTENT_LENGTH", "").strip()
    #    content_length = -1 # read all of stream
    if clHeader == "":
        # No Content-Length given: read to end of stream
        # TODO: etree.parse() locks, if input is invalid?
        #        pfroot = etree.parse(environ["wsgi.input"]).getroot()
        # requestbody = environ["wsgi.input"].read()  # TODO: read() should be
        # called in a loop?
        requestbody = ""
    else:
        try:
            content_length = int(clHeader)
            if content_length < 0:
                raise DAVError(HTTP_BAD_REQUEST, "Negative content-length.")
        except ValueError:
            raise DAVError(HTTP_BAD_REQUEST, "content-length is not numeric.")

        if content_length == 0:
            requestbody = ""
        else:
            requestbody = environ["wsgi.input"].read(content_length)
            environ["wsgidav.all_input_read"] = 1

    if requestbody == "":
        if allow_empty:
            return None
        else:
            raise DAVError(HTTP_BAD_REQUEST, "Body must not be empty.")

    try:
        rootEL = etree.fromstring(requestbody)
    except Exception as e:
        raise DAVError(HTTP_BAD_REQUEST, "Invalid XML format.", src_exception=e)

    # If dumps of the body are desired, then this is the place to do it pretty:
    if environ.get("wsgidav.dump_request_body"):
        _logger.info(
            "{} XML request body:\n{}".format(
                environ["REQUEST_METHOD"],
                compat.to_native(xml_to_bytes(rootEL, pretty_print=True)),
            )
        )
        environ["wsgidav.dump_request_body"] = False

    return rootEL

Read request body XML into an etree . Element .


def send_status_response(environ, start_response, e, add_headers=None, is_head=False):
    """Start a WSGI response for a DAVError or status code."""
    status = get_http_status_string(e)
    headers = []
    if add_headers:
        headers.extend(add_headers)
    #    if 'keep-alive' in environ.get('HTTP_CONNECTION', '').lower():
    #        headers += [
    #            ('Connection', 'keep-alive'),
    #        ]

    if e in (HTTP_NOT_MODIFIED, HTTP_NO_CONTENT):
        # See paste.lint: these code don't have content
        start_response(
            status, [("Content-Length", "0"), ("Date", get_rfc1123_time())] + headers
        )
        return [b""]

    if e in (HTTP_OK, HTTP_CREATED):
        e = DAVError(e)
    assert isinstance(e, DAVError)

    content_type, body = e.get_response_page()
    if is_head:
        body = compat.b_empty

    assert compat.is_bytes(body), body  # If not, Content-Length is wrong!
    start_response(
        status,
        [
            ("Content-Type", content_type),
            ("Date", get_rfc1123_time()),
            ("Content-Length", str(len(body))),
        ]
        + headers,
    )
    return [body]

#            ('Connection', 'keep-alive'),

Start a WSGI response for a DAVError or status code .


def calc_base64(s):
    """Return base64 encoded binarystring."""
    s = compat.to_bytes(s)
    s = compat.base64_encodebytes(s).strip()  # return bytestring
    return compat.to_native(s)

def
calc_base64
(
s
)
:
s
=
compat
.
to_bytes
(
s
)
s
=
compat
.
base64_encodebytes
(
s
)
.
strip
(
)
# return bytestring
return
compat
.
to_native
(
s
)


def read_timeout_value_header(timeoutvalue):
    """Return -1 if infinite, else return numofsecs."""
    timeoutsecs = 0
    timeoutvaluelist = timeoutvalue.split(",")
    for timeoutspec in timeoutvaluelist:
        timeoutspec = timeoutspec.strip()
        if timeoutspec.lower() == "infinite":
            return -1
        else:
            listSR = reSecondsReader.findall(timeoutspec)
            for secs in listSR:
                timeoutsecs = int(secs)
                if timeoutsecs > MAX_FINITE_TIMEOUT_LIMIT:
                    return -1
                if timeoutsecs != 0:
                    return timeoutsecs
    return None

def read_timeout_value_header ( timeoutvalue ) : timeoutsecs = 0 timeoutvaluelist = timeoutvalue . split ( "," ) for timeoutspec in timeoutvaluelist : timeoutspec = timeoutspec . strip ( ) if timeoutspec . lower ( ) == "infinite" : return - 1 else : listSR = reSecondsReader . findall ( timeoutspec ) for secs in listSR : timeoutsecs = int ( secs ) if timeoutsecs > MAX_FINITE_TIMEOUT_LIMIT : return - 1 if timeoutsecs != 0 : return timeoutsecs return None

Return - 1 if infinite else return numofsecs .


def parse_if_header_dict(environ):
    """Parse HTTP_IF header into a dictionary and lists, and cache the result.

    @see http://www.webdav.org/specs/rfc4918.html#HEADER_If
    """
    if "wsgidav.conditions.if" in environ:
        return

    if "HTTP_IF" not in environ:
        environ["wsgidav.conditions.if"] = None
        environ["wsgidav.ifLockTokenList"] = []
        return

    iftext = environ["HTTP_IF"].strip()
    if not iftext.startswith("<"):
        iftext = "<*>" + iftext

    ifDict = dict([])
    ifLockList = []

    resource1 = "*"
    for (tmpURLVar, URLVar, _tmpContentVar, contentVar) in reIfSeparator.findall(
        iftext
    ):
        if tmpURLVar != "":
            resource1 = URLVar
        else:
            listTagContents = []
            testflag = True
            for listitem in reIfTagListContents.findall(contentVar):
                if listitem.upper() != "NOT":
                    if listitem.startswith("["):
                        listTagContents.append(
                            (testflag, "entity", listitem.strip('"[]'))
                        )
                    else:
                        listTagContents.append(
                            (testflag, "locktoken", listitem.strip("<>"))
                        )
                        ifLockList.append(listitem.strip("<>"))
                testflag = listitem.upper() != "NOT"

            if resource1 in ifDict:
                listTag = ifDict[resource1]
            else:
                listTag = []
                ifDict[resource1] = listTag
            listTag.append(listTagContents)

    environ["wsgidav.conditions.if"] = ifDict
    environ["wsgidav.ifLockTokenList"] = ifLockList
    _logger.debug("parse_if_header_dict\n{}".format(pformat(ifDict)))
    return

def parse_if_header_dict ( environ ) : if "wsgidav.conditions.if" in environ : return if "HTTP_IF" not in environ : environ [ "wsgidav.conditions.if" ] = None environ [ "wsgidav.ifLockTokenList" ] = [ ] return iftext = environ [ "HTTP_IF" ] . strip ( ) if not iftext . startswith ( "<" ) : iftext = "<*>" + iftext ifDict = dict ( [ ] ) ifLockList = [ ] resource1 = "*" for ( tmpURLVar , URLVar , _tmpContentVar , contentVar ) in reIfSeparator . findall ( iftext ) : if tmpURLVar != "" : resource1 = URLVar else : listTagContents = [ ] testflag = True for listitem in reIfTagListContents . findall ( contentVar ) : if listitem . upper ( ) != "NOT" : if listitem . startswith ( "[" ) : listTagContents . append ( ( testflag , "entity" , listitem . strip ( '"[]' ) ) ) else : listTagContents . append ( ( testflag , "locktoken" , listitem . strip ( "<>" ) ) ) ifLockList . append ( listitem . strip ( "<>" ) ) testflag = listitem . upper ( ) != "NOT" if resource1 in ifDict : listTag = ifDict [ resource1 ] else : listTag = [ ] ifDict [ resource1 ] = listTag listTag . append ( listTagContents ) environ [ "wsgidav.conditions.if" ] = ifDict environ [ "wsgidav.ifLockTokenList" ] = ifLockList _logger . debug ( "parse_if_header_dict\n{}" . format ( pformat ( ifDict ) ) ) return

Parse HTTP_IF header into a dictionary and lists and cache the result .


def guess_mime_type(url):
    """Use the mimetypes module to lookup the type for an extension.

    This function also adds some extensions required for HTML5
    """
    (mimetype, _mimeencoding) = mimetypes.guess_type(url)
    if not mimetype:
        ext = os.path.splitext(url)[1]
        mimetype = _MIME_TYPES.get(ext)
        _logger.debug("mimetype({}): {}".format(url, mimetype))
    if not mimetype:
        mimetype = "application/octet-stream"
    return mimetype

def
guess_mime_type
(
url
)
:
(
mimetype
,
_mimeencoding
)
=
mimetypes
.
guess_type
(
url
)
if
not
mimetype
:
ext
=
os
.
path
.
splitext
(
url
)
[
1
]
mimetype
=
_MIME_TYPES
.
get
(
ext
)
_logger
.
debug
(
"mimetype({}): {}"
.
format
(
url
,
mimetype
)
)
if
not
mimetype
:
mimetype
=
"application/octet-stream"
return
mimetype

Use the mimetypes module to lookup the type for an extension .


def add_members(self, new_members):
        """
        Add objects to the group.

        Parameters
        ----------
        new_members : list
            A list of cobrapy objects to add to the group.

        """

        if isinstance(new_members, string_types) or \
                hasattr(new_members, "id"):
            warn("need to pass in a list")
            new_members = [new_members]

        self._members.update(new_members)

def
add_members
(
self
,
new_members
)
:
if
isinstance
(
new_members
,
string_types
)
or
hasattr
(
new_members
,
"id"
)
:
warn
(
"need to pass in a list"
)
new_members
=
[
new_members
]
self
.
_members
.
update
(
new_members
)


def remove_members(self, to_remove):
        """
        Remove objects from the group.

        Parameters
        ----------
        to_remove : list
            A list of cobra objects to remove from the group
        """

        if isinstance(to_remove, string_types) or \
                hasattr(to_remove, "id"):
            warn("need to pass in a list")
            to_remove = [to_remove]

        self._members.difference_update(to_remove)

def
remove_members
(
self
,
to_remove
)
:
if
isinstance
(
to_remove
,
string_types
)
or
hasattr
(
to_remove
,
"id"
)
:
warn
(
"need to pass in a list"
)
to_remove
=
[
to_remove
]
self
.
_members
.
difference_update
(
to_remove
)


def geometric_fba(model, epsilon=1E-06, max_tries=200, processes=None):
    """
    Perform geometric FBA to obtain a unique, centered flux distribution.

    Geometric FBA [1]_ formulates the problem as a polyhedron and
    then solves it by bounding the convex hull of the polyhedron.
    The bounding forms a box around the convex hull which reduces
    with every iteration and extracts a unique solution in this way.

    Parameters
    ----------
    model: cobra.Model
        The model to perform geometric FBA on.
    epsilon: float, optional
        The convergence tolerance of the model (default 1E-06).
    max_tries: int, optional
        Maximum number of iterations (default 200).
    processes : int, optional
        The number of parallel processes to run. If not explicitly passed,
        will be set from the global configuration singleton.

    Returns
    -------
    cobra.Solution
        The solution object containing all the constraints required
        for geometric FBA.

    References
    ----------
    .. [1] Smallbone, Kieran & Simeonidis, Vangelis. (2009).
           Flux balance analysis: A geometric perspective.
           Journal of theoretical biology.258. 311-5.
           10.1016/j.jtbi.2009.01.027.

    """

    with model:
        # Variables' and constraints' storage variables.
        consts = []
        obj_vars = []
        updating_vars_cons = []

        # The first iteration.
        prob = model.problem
        add_pfba(model)  # Minimize the solution space to a convex hull.
        model.optimize()
        fva_sol = flux_variability_analysis(model, processes=processes)
        mean_flux = (fva_sol["maximum"] + fva_sol["minimum"]).abs() / 2

        # Set the gFBA constraints.
        for rxn in model.reactions:
            var = prob.Variable("geometric_fba_" + rxn.id,
                                lb=0,
                                ub=mean_flux[rxn.id])
            upper_const = prob.Constraint(rxn.flux_expression - var,
                                          ub=mean_flux[rxn.id],
                                          name="geometric_fba_upper_const_" +
                                          rxn.id)
            lower_const = prob.Constraint(rxn.flux_expression + var,
                                          lb=fva_sol.at[rxn.id, "minimum"],
                                          name="geometric_fba_lower_const_" +
                                          rxn.id)
            updating_vars_cons.append((rxn.id, var, upper_const, lower_const))
            consts.extend([var, upper_const, lower_const])
            obj_vars.append(var)
        model.add_cons_vars(consts)

        # Minimize the distance between the flux distribution and center.
        model.objective = prob.Objective(Zero, sloppy=True, direction="min")
        model.objective.set_linear_coefficients({v: 1.0 for v in obj_vars})
        # Update loop variables.
        sol = model.optimize()
        fva_sol = flux_variability_analysis(model, processes=processes)
        mean_flux = (fva_sol["maximum"] + fva_sol["minimum"]).abs() / 2
        delta = (fva_sol["maximum"] - fva_sol["minimum"]).max()
        count = 1
        LOGGER.debug("Iteration: %d; delta: %.3g; status: %s.",
                     count, delta, sol.status)

        # Following iterations that minimize the distance below threshold.
        while delta > epsilon and count < max_tries:
            for rxn_id, var, u_c, l_c in updating_vars_cons:
                var.ub = mean_flux[rxn_id]
                u_c.ub = mean_flux[rxn_id]
                l_c.lb = fva_sol.at[rxn_id, "minimum"]
            # Update loop variables.
            sol = model.optimize()
            fva_sol = flux_variability_analysis(model, processes=processes)
            mean_flux = (fva_sol["maximum"] + fva_sol["minimum"]).abs() / 2
            delta = (fva_sol["maximum"] - fva_sol["minimum"]).max()
            count += 1
            LOGGER.debug("Iteration: %d; delta: %.3g; status: %s.",
                         count, delta, sol.status)

        if count == max_tries:
            raise RuntimeError(
                "The iterations have exceeded the maximum value of {}. "
                "This is probably due to the increased complexity of the "
                "model and can lead to inaccurate results. Please set a "
                "different convergence tolerance and/or increase the "
                "maximum iterations".format(max_tries)
            )

    return sol

Perform geometric FBA to obtain a unique centered flux distribution .


def _generate_index(self):
        """rebuild the _dict index"""
        self._dict = {v.id: k for k, v in enumerate(self)}

def _generate_index ( self ) : self . _dict = { v . id : k for k , v in enumerate ( self ) }


def get_by_any(self, iterable):
        """
        Get a list of members using several different ways of indexing

        Parameters
        ----------
        iterable : list (if not, turned into single element list)
            list where each element is either int (referring to an index in
            in this DictList), string (a id of a member in this DictList) or
            member of this DictList for pass-through

        Returns
        -------
        list
            a list of members
        """
        def get_item(item):
            if isinstance(item, int):
                return self[item]
            elif isinstance(item, string_types):
                return self.get_by_id(item)
            elif item in self:
                return item
            else:
                raise TypeError("item in iterable cannot be '%s'" % type(item))

        if not isinstance(iterable, list):
            iterable = [iterable]
        return [get_item(item) for item in iterable]

def
get_by_any
(
self
,
iterable
)
:
def
get_item
(
item
)
:
if
isinstance
(
item
,
int
)
:
return
self
[
item
]
elif
isinstance
(
item
,
string_types
)
:
return
self
.
get_by_id
(
item
)
elif
item
in
self
:
return
item
else
:
raise
TypeError
(
"item in iterable cannot be '%s'"
%
type
(
item
)
)
if
not
isinstance
(
iterable
,
list
)
:
iterable
=
[
iterable
]
return
[
get_item
(
item
)
for
item
in
iterable
]


def query(self, search_function, attribute=None):
        """Query the list

        Parameters
        ----------
        search_function : a string, regular expression or function
            Used to find the matching elements in the list.
            - a regular expression (possibly compiled), in which case the
            given attribute of the object should match the regular expression.
            - a function which takes one argument and returns True for
            desired values

        attribute : string or None
            the name attribute of the object to passed as argument to the
            `search_function`. If this is None, the object itself is used.

        Returns
        -------
        DictList
            a new list of objects which match the query

        Examples
        --------
        >>> import cobra.test
        >>> model = cobra.test.create_test_model('textbook')
        >>> model.reactions.query(lambda x: x.boundary)
        >>> import re
        >>> regex = re.compile('^g', flags=re.IGNORECASE)
        >>> model.metabolites.query(regex, attribute='name')
        """
        def select_attribute(x):
            if attribute is None:
                return x
            else:
                return getattr(x, attribute)

        try:
            # if the search_function is a regular expression
            regex_searcher = re.compile(search_function)

            if attribute is not None:
                matches = (
                    i for i in self if
                    regex_searcher.findall(select_attribute(i)) != [])

            else:
                # Don't regex on objects
                matches = (
                    i for i in self if
                    regex_searcher.findall(getattr(i, 'id')) != [])

        except TypeError:
            matches = (
                i for i in self if search_function(select_attribute(i)))

        results = self.__class__()
        results._extend_nocheck(matches)
        return results


def _replace_on_id(self, new_object):
        """Replace an object by another with the same id."""
        the_id = new_object.id
        the_index = self._dict[the_id]
        list.__setitem__(self, the_index, new_object)

def _replace_on_id ( self , new_object ) : the_id = new_object . id the_index = self . _dict [ the_id ] list . __setitem__ ( self , the_index , new_object )

Replace an object by another with the same id .


def append(self, object):
        """append object to end"""
        the_id = object.id
        self._check(the_id)
        self._dict[the_id] = len(self)
        list.append(self, object)

def append ( self , object ) : the_id = object . id self . _check ( the_id ) self . _dict [ the_id ] = len ( self ) list . append ( self , object )


def union(self, iterable):
        """adds elements with id's not already in the model"""
        _dict = self._dict
        append = self.append
        for i in iterable:
            if i.id not in _dict:
                append(i)

def union ( self , iterable ) : _dict = self . _dict append = self . append for i in iterable : if i . id not in _dict : append ( i )

adds elements with id s not already in the model


def extend(self, iterable):
        """extend list by appending elements from the iterable"""
        # Sometimes during initialization from an older pickle, _dict
        # will not have initialized yet, because the initialization class was
        # left unspecified. This is an issue because unpickling calls
        # DictList.extend, which requires the presence of _dict. Therefore,
        # the issue is caught and addressed here.
        if not hasattr(self, "_dict") or self._dict is None:
            self._dict = {}
        _dict = self._dict
        current_length = len(self)
        list.extend(self, iterable)
        for i, obj in enumerate(islice(self, current_length, None),
                                current_length):
            the_id = obj.id
            if the_id not in _dict:
                _dict[the_id] = i
            else:
                # undo the extend and raise an error
                self = self[:current_length]
                self._check(the_id)
                # if the above succeeded, then the id must be present
                # twice in the list being added
                raise ValueError("id '%s' at index %d is non-unique. "
                                 "Is it present twice?" % (str(the_id), i))


def _extend_nocheck(self, iterable):
        """extends without checking for uniqueness

        This function should only be used internally by DictList when it
        can guarantee elements are already unique (as in when coming from
        self or other DictList). It will be faster because it skips these
        checks.

        """
        current_length = len(self)
        list.extend(self, iterable)
        _dict = self._dict
        if current_length is 0:
            self._generate_index()
            return
        for i, obj in enumerate(islice(self, current_length, None),
                                current_length):
            _dict[obj.id] = i

def _extend_nocheck ( self , iterable ) : current_length = len ( self ) list . extend ( self , iterable ) _dict = self . _dict if current_length is 0 : self . _generate_index ( ) return for i , obj in enumerate ( islice ( self , current_length , None ) , current_length ) : _dict [ obj . id ] = i


def index(self, id, *args):
        """Determine the position in the list

        id: A string or a :class:`~cobra.core.Object.Object`

        """
        # because values are unique, start and stop are not relevant
        if isinstance(id, string_types):
            try:
                return self._dict[id]
            except KeyError:
                raise ValueError("%s not found" % id)
        try:
            i = self._dict[id.id]
            if self[i] is not id:
                raise ValueError(
                    "Another object with the identical id (%s) found" % id.id)
            return i
        except KeyError:
            raise ValueError("%s not found" % str(id))


def insert(self, index, object):
        """insert object before index"""
        self._check(object.id)
        list.insert(self, index, object)
        # all subsequent entries now have been shifted up by 1
        _dict = self._dict
        for i, j in iteritems(_dict):
            if j >= index:
                _dict[i] = j + 1
        _dict[object.id] = index


def elements(self):
        """ Dictionary of elements as keys and their count in the metabolite
        as integer. When set, the `formula` property is update accordingly """
        tmp_formula = self.formula
        if tmp_formula is None:
            return {}
        # necessary for some old pickles which use the deprecated
        # Formula class
        tmp_formula = str(self.formula)
        # commonly occurring characters in incorrectly constructed formulas
        if "*" in tmp_formula:
            warn("invalid character '*' found in formula '%s'" % self.formula)
            tmp_formula = tmp_formula.replace("*", "")
        if "(" in tmp_formula or ")" in tmp_formula:
            warn("invalid formula (has parenthesis) in '%s'" % self.formula)
            return None
        composition = {}
        parsed = element_re.findall(tmp_formula)
        for (element, count) in parsed:
            if count == '':
                count = 1
            else:
                try:
                    count = float(count)
                    int_count = int(count)
                    if count == int_count:
                        count = int_count
                    else:
                        warn("%s is not an integer (in formula %s)" %
                             (count, self.formula))
                except ValueError:
                    warn("failed to parse %s (in formula %s)" %
                         (count, self.formula))
                    return None
            if element in composition:
                composition[element] += count
            else:
                composition[element] = count
        return composition

Dictionary of elements as keys and their count in the metabolite as integer . When set the formula property is update accordingly


def shadow_price(self):
        """
        The shadow price in the most recent solution.

        Shadow price is the dual value of the corresponding constraint in the
        model.

        Warnings
        --------
        * Accessing shadow prices through a `Solution` object is the safer,
          preferred, and only guaranteed to be correct way. You can see how to
          do so easily in the examples.
        * Shadow price is retrieved from the currently defined
          `self._model.solver`. The solver status is checked but there are no
          guarantees that the current solver state is the one you are looking
          for.
        * If you modify the underlying model after an optimization, you will
          retrieve the old optimization values.

        Raises
        ------
        RuntimeError
            If the underlying model was never optimized beforehand or the
            metabolite is not part of a model.
        OptimizationError
            If the solver status is anything other than 'optimal'.

        Examples
        --------
        >>> import cobra
        >>> import cobra.test
        >>> model = cobra.test.create_test_model("textbook")
        >>> solution = model.optimize()
        >>> model.metabolites.glc__D_e.shadow_price
        -0.09166474637510488
        >>> solution.shadow_prices.glc__D_e
        -0.091664746375104883
        """
        try:
            check_solver_status(self._model.solver.status)
            return self._model.constraints[self.id].dual
        except AttributeError:
            raise RuntimeError(
                "metabolite '{}' is not part of a model".format(self.id))
        # Due to below all-catch, which sucks, need to reraise these.
        except (RuntimeError, OptimizationError) as err:
            raise_with_traceback(err)
        # Would love to catch CplexSolverError and GurobiError here.
        except Exception as err:
            raise_from(OptimizationError(
                "Likely no solution exists. Original solver message: {}."
                "".format(str(err))), err)

The shadow price in the most recent solution .


def to_yaml(model, sort=False, **kwargs):
    """
    Return the model as a YAML document.

    ``kwargs`` are passed on to ``yaml.dump``.

    Parameters
    ----------
    model : cobra.Model
        The cobra model to represent.
    sort : bool, optional
        Whether to sort the metabolites, reactions, and genes or maintain the
        order defined in the model.

    Returns
    -------
    str
        String representation of the cobra model as a YAML document.

    See Also
    --------
    save_yaml_model : Write directly to a file.
    ruamel.yaml.dump : Base function.
    """

    obj = model_to_dict(model, sort=sort)
    obj["version"] = YAML_SPEC
    return yaml.dump(obj, **kwargs)

def to_yaml ( model , sort = False , * * kwargs ) : obj = model_to_dict ( model , sort = sort ) obj [ "version" ] = YAML_SPEC return yaml . dump ( obj , * * kwargs )


def save_yaml_model(model, filename, sort=False, **kwargs):
    """
    Write the cobra model to a file in YAML format.

    ``kwargs`` are passed on to ``yaml.dump``.

    Parameters
    ----------
    model : cobra.Model
        The cobra model to represent.
    filename : str or file-like
        File path or descriptor that the YAML representation should be
        written to.
    sort : bool, optional
        Whether to sort the metabolites, reactions, and genes or maintain the
        order defined in the model.

    See Also
    --------
    to_yaml : Return a string representation.
    ruamel.yaml.dump : Base function.
    """
    obj = model_to_dict(model, sort=sort)
    obj["version"] = YAML_SPEC
    if isinstance(filename, string_types):
        with io.open(filename, "w") as file_handle:
            yaml.dump(obj, file_handle, **kwargs)
    else:
        yaml.dump(obj, filename, **kwargs)

def save_yaml_model ( model , filename , sort = False , * * kwargs ) : obj = model_to_dict ( model , sort = sort ) obj [ "version" ] = YAML_SPEC if isinstance ( filename , string_types ) : with io . open ( filename , "w" ) as file_handle : yaml . dump ( obj , file_handle , * * kwargs ) else : yaml . dump ( obj , filename , * * kwargs )

Write the cobra model to a file in YAML format .


def load_yaml_model(filename):
    """
    Load a cobra model from a file in YAML format.

    Parameters
    ----------
    filename : str or file-like
        File path or descriptor that contains the YAML document describing the
        cobra model.

    Returns
    -------
    cobra.Model
        The cobra model as represented in the YAML document.

    See Also
    --------
    from_yaml : Load from a string.
    """
    if isinstance(filename, string_types):
        with io.open(filename, "r") as file_handle:
            return model_from_dict(yaml.load(file_handle))
    else:
        return model_from_dict(yaml.load(filename))

def load_yaml_model ( filename ) : if isinstance ( filename , string_types ) : with io . open ( filename , "r" ) as file_handle : return model_from_dict ( yaml . load ( file_handle ) ) else : return model_from_dict ( yaml . load ( filename ) )

Load a cobra model from a file in YAML format .


def add_pfba(model, objective=None, fraction_of_optimum=1.0):
    """Add pFBA objective

    Add objective to minimize the summed flux of all reactions to the
    current objective.

    See Also
    -------
    pfba

    Parameters
    ----------
    model : cobra.Model
        The model to add the objective to
    objective :
        An objective to set in combination with the pFBA objective.
    fraction_of_optimum : float
        Fraction of optimum which must be maintained. The original objective
        reaction is constrained to be greater than maximal_value *
        fraction_of_optimum.
    """
    if objective is not None:
        model.objective = objective
    if model.solver.objective.name == '_pfba_objective':
        raise ValueError('The model already has a pFBA objective.')
    sutil.fix_objective_as_constraint(model, fraction=fraction_of_optimum)
    reaction_variables = ((rxn.forward_variable, rxn.reverse_variable)
                          for rxn in model.reactions)
    variables = chain(*reaction_variables)
    model.objective = model.problem.Objective(
        Zero, direction='min', sloppy=True, name="_pfba_objective")
    model.objective.set_linear_coefficients({v: 1.0 for v in variables})


def _process_flux_dataframe(flux_dataframe, fva, threshold, floatfmt):
    """Some common methods for processing a database of flux information into
    print-ready formats. Used in both model_summary and metabolite_summary. """

    abs_flux = flux_dataframe['flux'].abs()
    flux_threshold = threshold * abs_flux.max()

    # Drop unused boundary fluxes
    if fva is None:
        flux_dataframe = flux_dataframe.loc[
            abs_flux >= flux_threshold, :].copy()
    else:
        flux_dataframe = flux_dataframe.loc[
            (abs_flux >= flux_threshold) |
            (flux_dataframe['fmin'].abs() >= flux_threshold) |
            (flux_dataframe['fmax'].abs() >= flux_threshold), :].copy()

        # Why set to zero? If included show true value?
        # flux_dataframe.loc[
        #     flux_dataframe['flux'].abs() < flux_threshold, 'flux'] = 0

    # Make all fluxes positive
    if fva is None:
        flux_dataframe['is_input'] = (flux_dataframe['flux'] >= 0)
        flux_dataframe['flux'] = flux_dataframe['flux'].abs()
    else:

        def get_direction(flux, fmin, fmax):
            """ decide whether or not to reverse a flux to make it positive """

            if flux < 0:
                return -1
            elif flux > 0:
                return 1
            elif (fmax > 0) & (fmin <= 0):
                return 1
            elif (fmax < 0) & (fmin >= 0):
                return -1
            elif ((fmax + fmin) / 2) < 0:
                return -1
            else:
                return 1

        sign = flux_dataframe.apply(
            lambda x: get_direction(x.flux, x.fmin, x.fmax), 1)

        flux_dataframe['is_input'] = sign == 1

        flux_dataframe.loc[:, ['flux', 'fmin', 'fmax']] = \
            flux_dataframe.loc[:, ['flux', 'fmin', 'fmax']].multiply(
                sign, 0).astype('float').round(6)

        flux_dataframe.loc[:, ['flux', 'fmin', 'fmax']] = \
            flux_dataframe.loc[:, ['flux', 'fmin', 'fmax']].applymap(
                lambda x: x if abs(x) > 1E-6 else 0)

    if fva is not None:
        flux_dataframe['fva_fmt'] = flux_dataframe.apply(
            lambda x: ("[{0.fmin:" + floatfmt + "}, {0.fmax:" +
                       floatfmt + "}]").format(x), 1)

        flux_dataframe = flux_dataframe.sort_values(
            by=['flux', 'fmax', 'fmin', 'id'],
            ascending=[False, False, False, True])

    else:
        flux_dataframe = flux_dataframe.sort_values(
            by=['flux', 'id'], ascending=[False, True])

    return flux_dataframe

Some common methods for processing a database of flux information into print - ready formats . Used in both model_summary and metabolite_summary .


def linear_reaction_coefficients(model, reactions=None):
    """Coefficient for the reactions in a linear objective.

    Parameters
    ----------
    model : cobra model
        the model object that defined the objective
    reactions : list
        an optional list for the reactions to get the coefficients for. All
        reactions if left missing.

    Returns
    -------
    dict
        A dictionary where the key is the reaction object and the value is
        the corresponding coefficient. Empty dictionary if there are no
        linear terms in the objective.
    """
    linear_coefficients = {}
    reactions = model.reactions if not reactions else reactions
    try:
        objective_expression = model.solver.objective.expression
        coefficients = objective_expression.as_coefficients_dict()
    except AttributeError:
        return linear_coefficients
    for rxn in reactions:
        forward_coefficient = coefficients.get(rxn.forward_variable, 0)
        reverse_coefficient = coefficients.get(rxn.reverse_variable, 0)
        if forward_coefficient != 0:
            if forward_coefficient == -reverse_coefficient:
                linear_coefficients[rxn] = float(forward_coefficient)
    return linear_coefficients

def linear_reaction_coefficients ( model , reactions = None ) : linear_coefficients = { } reactions = model . reactions if not reactions else reactions try : objective_expression = model . solver . objective . expression coefficients = objective_expression . as_coefficients_dict ( ) except AttributeError : return linear_coefficients for rxn in reactions : forward_coefficient = coefficients . get ( rxn . forward_variable , 0 ) reverse_coefficient = coefficients . get ( rxn . reverse_variable , 0 ) if forward_coefficient != 0 : if forward_coefficient == - reverse_coefficient : linear_coefficients [ rxn ] = float ( forward_coefficient ) return linear_coefficients

Coefficient for the reactions in a linear objective .


def _valid_atoms(model, expression):
    """Check whether a sympy expression references the correct variables.

    Parameters
    ----------
    model : cobra.Model
        The model in which to check for variables.
    expression : sympy.Basic
        A sympy expression.

    Returns
    -------
    boolean
        True if all referenced variables are contained in model, False
        otherwise.
    """
    atoms = expression.atoms(optlang.interface.Variable)
    return all(a.problem is model.solver for a in atoms)

def _valid_atoms ( model , expression ) : atoms = expression . atoms ( optlang . interface . Variable ) return all ( a . problem is model . solver for a in atoms )

Check whether a sympy expression references the correct variables .


def set_objective(model, value, additive=False):
    """Set the model objective.

    Parameters
    ----------
    model : cobra model
       The model to set the objective for
    value : model.problem.Objective,
            e.g. optlang.glpk_interface.Objective, sympy.Basic or dict

        If the model objective is linear, the value can be a new Objective
        object or a dictionary with linear coefficients where each key is a
        reaction and the element the new coefficient (float).

        If the objective is not linear and `additive` is true, only values
        of class Objective.

    additive : boolmodel.reactions.Biomass_Ecoli_core.bounds = (0.1, 0.1)
        If true, add the terms to the current objective, otherwise start with
        an empty objective.
    """
    interface = model.problem
    reverse_value = model.solver.objective.expression
    reverse_value = interface.Objective(
        reverse_value, direction=model.solver.objective.direction,
        sloppy=True)

    if isinstance(value, dict):
        if not model.objective.is_Linear:
            raise ValueError('can only update non-linear objectives '
                             'additively using object of class '
                             'model.problem.Objective, not %s' %
                             type(value))

        if not additive:
            model.solver.objective = interface.Objective(
                Zero, direction=model.solver.objective.direction)
        for reaction, coef in value.items():
            model.solver.objective.set_linear_coefficients(
                {reaction.forward_variable: coef,
                 reaction.reverse_variable: -coef})

    elif isinstance(value, (Basic, optlang.interface.Objective)):
        if isinstance(value, Basic):
            value = interface.Objective(
                value, direction=model.solver.objective.direction,
                sloppy=False)
        # Check whether expression only uses variables from current model
        # clone the objective if not, faster than cloning without checking
        if not _valid_atoms(model, value.expression):
            value = interface.Objective.clone(value, model=model.solver)

        if not additive:
            model.solver.objective = value
        else:
            model.solver.objective += value.expression
    else:
        raise TypeError(
            '%r is not a valid objective for %r.' % (value, model.solver))

    context = get_context(model)
    if context:
        def reset():
            model.solver.objective = reverse_value
            model.solver.objective.direction = reverse_value.direction

        context(reset)


def interface_to_str(interface):
    """Give a string representation for an optlang interface.

    Parameters
    ----------
    interface : string, ModuleType
        Full name of the interface in optlang or cobra representation.
        For instance 'optlang.glpk_interface' or 'optlang-glpk'.

    Returns
    -------
    string
       The name of the interface as a string
    """
    if isinstance(interface, ModuleType):
        interface = interface.__name__
    return re.sub(r"optlang.|.interface", "", interface)

def interface_to_str ( interface ) : if isinstance ( interface , ModuleType ) : interface = interface . __name__ return re . sub ( r"optlang.|.interface" , "" , interface )

Give a string representation for an optlang interface .


def get_solver_name(mip=False, qp=False):
    """Select a solver for a given optimization problem.

    Parameters
    ----------
    mip : bool
        Does the solver require mixed integer linear programming capabilities?
    qp : bool
        Does the solver require quadratic programming capabilities?

    Returns
    -------
    string
        The name of feasible solver.

    Raises
    ------
    SolverNotFound
        If no suitable solver could be found.
    """
    if len(solvers) == 0:
        raise SolverNotFound("no solvers installed")
    # Those lists need to be updated as optlang implements more solvers
    mip_order = ["gurobi", "cplex", "glpk"]
    lp_order = ["glpk", "cplex", "gurobi"]
    qp_order = ["gurobi", "cplex"]

    if mip is False and qp is False:
        for solver_name in lp_order:
            if solver_name in solvers:
                return solver_name
        # none of them are in the list order - so return the first one
        return list(solvers)[0]
    elif qp:  # mip does not yet matter for this determination
        for solver_name in qp_order:
            if solver_name in solvers:
                return solver_name
        raise SolverNotFound("no qp-capable solver found")
    else:
        for solver_name in mip_order:
            if solver_name in solvers:
                return solver_name
    raise SolverNotFound("no mip-capable solver found")

Select a solver for a given optimization problem .


def choose_solver(model, solver=None, qp=False):
    """Choose a solver given a solver name and model.

    This will choose a solver compatible with the model and required
    capabilities. Also respects model.solver where it can.

    Parameters
    ----------
    model : a cobra model
        The model for which to choose the solver.
    solver : str, optional
        The name of the solver to be used.
    qp : boolean, optional
        Whether the solver needs Quadratic Programming capabilities.

    Returns
    -------
    solver : an optlang solver interface
        Returns a valid solver for the problem.

    Raises
    ------
    SolverNotFound
        If no suitable solver could be found.
    """
    if solver is None:
        solver = model.problem
    else:
        model.solver = solver

    # Check for QP, raise error if no QP solver found
    if qp and interface_to_str(solver) not in qp_solvers:
        solver = solvers[get_solver_name(qp=True)]

    return solver

Choose a solver given a solver name and model .


def add_cons_vars_to_problem(model, what, **kwargs):
    """Add variables and constraints to a Model's solver object.

    Useful for variables and constraints that can not be expressed with
    reactions and lower/upper bounds. Will integrate with the Model's context
    manager in order to revert changes upon leaving the context.

    Parameters
    ----------
    model : a cobra model
       The model to which to add the variables and constraints.
    what : list or tuple of optlang variables or constraints.
       The variables or constraints to add to the model. Must be of class
       `model.problem.Variable` or
       `model.problem.Constraint`.
    **kwargs : keyword arguments
        passed to solver.add()
    """
    context = get_context(model)

    model.solver.add(what, **kwargs)
    if context:
        context(partial(model.solver.remove, what))

def add_cons_vars_to_problem ( model , what , * * kwargs ) : context = get_context ( model ) model . solver . add ( what , * * kwargs ) if context : context ( partial ( model . solver . remove , what ) )

Add variables and constraints to a Model s solver object .


def remove_cons_vars_from_problem(model, what):
    """Remove variables and constraints from a Model's solver object.

    Useful to temporarily remove variables and constraints from a Models's
    solver object.

    Parameters
    ----------
    model : a cobra model
       The model from which to remove the variables and constraints.
    what : list or tuple of optlang variables or constraints.
       The variables or constraints to remove from the model. Must be of
       class `model.problem.Variable` or
       `model.problem.Constraint`.
    """
    context = get_context(model)

    model.solver.remove(what)
    if context:
        context(partial(model.solver.add, what))

def remove_cons_vars_from_problem ( model , what ) : context = get_context ( model ) model . solver . remove ( what ) if context : context ( partial ( model . solver . add , what ) )

Remove variables and constraints from a Model s solver object .


def add_absolute_expression(model, expression, name="abs_var", ub=None,
                            difference=0, add=True):
    """Add the absolute value of an expression to the model.

    Also defines a variable for the absolute value that can be used in other
    objectives or constraints.

    Parameters
    ----------
    model : a cobra model
       The model to which to add the absolute expression.
    expression : A sympy expression
       Must be a valid expression within the Model's solver object. The
       absolute value is applied automatically on the expression.
    name : string
       The name of the newly created variable.
    ub : positive float
       The upper bound for the variable.
    difference : positive float
        The difference between the expression and the variable.
    add : bool
        Whether to add the variable to the model at once.

    Returns
    -------
    namedtuple
        A named tuple with variable and two constraints (upper_constraint,
        lower_constraint) describing the new variable and the constraints
        that assign the absolute value of the expression to it.
    """
    Components = namedtuple('Components', ['variable', 'upper_constraint',
                                           'lower_constraint'])
    variable = model.problem.Variable(name, lb=0, ub=ub)
    # The following constraints enforce variable > expression and
    # variable > -expression
    upper_constraint = model.problem.Constraint(expression - variable,
                                                ub=difference,
                                                name="abs_pos_" + name),
    lower_constraint = model.problem.Constraint(expression + variable,
                                                lb=difference,
                                                name="abs_neg_" + name)
    to_add = Components(variable, upper_constraint, lower_constraint)
    if add:
        add_cons_vars_to_problem(model, to_add)
    return to_add

Add the absolute value of an expression to the model .


def fix_objective_as_constraint(model, fraction=1, bound=None,
                                name='fixed_objective_{}'):
    """Fix current objective as an additional constraint.

    When adding constraints to a model, such as done in pFBA which
    minimizes total flux, these constraints can become too powerful,
    resulting in solutions that satisfy optimality but sacrifices too
    much for the original objective function. To avoid that, we can fix
    the current objective value as a constraint to ignore solutions that
    give a lower (or higher depending on the optimization direction)
    objective value than the original model.

    When done with the model as a context, the modification to the
    objective will be reverted when exiting that context.

    Parameters
    ----------
    model : cobra.Model
        The model to operate on
    fraction : float
        The fraction of the optimum the objective is allowed to reach.
    bound : float, None
        The bound to use instead of fraction of maximum optimal value. If
        not None, fraction is ignored.
    name : str
        Name of the objective. May contain one `{}` placeholder which is filled
        with the name of the old objective.

    Returns
    -------
        The value of the optimized objective * fraction
    """
    fix_objective_name = name.format(model.objective.name)
    if fix_objective_name in model.constraints:
        model.solver.remove(fix_objective_name)
    if bound is None:
        bound = model.slim_optimize(error_value=None) * fraction
    if model.objective.direction == 'max':
        ub, lb = None, bound
    else:
        ub, lb = bound, None
    constraint = model.problem.Constraint(
        model.objective.expression,
        name=fix_objective_name, ub=ub, lb=lb)
    add_cons_vars_to_problem(model, constraint, sloppy=True)
    return bound

def fix_objective_as_constraint ( model , fraction = 1 , bound = None , name = 'fixed_objective_{}' ) : fix_objective_name = name . format ( model . objective . name ) if fix_objective_name in model . constraints : model . solver . remove ( fix_objective_name ) if bound is None : bound = model . slim_optimize ( error_value = None ) * fraction if model . objective . direction == 'max' : ub , lb = None , bound else : ub , lb = bound , None constraint = model . problem . Constraint ( model . objective . expression , name = fix_objective_name , ub = ub , lb = lb ) add_cons_vars_to_problem ( model , constraint , sloppy = True ) return bound

Fix current objective as an additional constraint .


def check_solver_status(status, raise_error=False):
    """Perform standard checks on a solver's status."""
    if status == OPTIMAL:
        return
    elif (status in has_primals) and not raise_error:
        warn("solver status is '{}'".format(status), UserWarning)
    elif status is None:
        raise OptimizationError(
            "model was not optimized yet or solver context switched")
    else:
        raise OptimizationError("solver status is '{}'".format(status))

Perform standard checks on a solver s status .


def assert_optimal(model, message='optimization failed'):
    """Assert model solver status is optimal.

    Do nothing if model solver status is optimal, otherwise throw
    appropriate exception depending on the status.

    Parameters
    ----------
    model : cobra.Model
        The model to check the solver status for.
    message : str (optional)
        Message to for the exception if solver status was not optimal.
    """
    status = model.solver.status
    if status != OPTIMAL:
        exception_cls = OPTLANG_TO_EXCEPTIONS_DICT.get(
            status, OptimizationError)
        raise exception_cls("{} ({})".format(message, status))

def
assert_optimal
(
model
,
message
=
'optimization failed'
)
:
status
=
model
.
solver
.
status
if
status
!=
OPTIMAL
:
exception_cls
=
OPTLANG_TO_EXCEPTIONS_DICT
.
get
(
status
,
OptimizationError
)
raise
exception_cls
(
"{} ({})"
.
format
(
message
,
status
)
)


def add_lp_feasibility(model):
    """
    Add a new objective and variables to ensure a feasible solution.

    The optimized objective will be zero for a feasible solution and otherwise
    represent the distance from feasibility (please see [1]_ for more
    information).

    Parameters
    ----------
    model : cobra.Model
        The model whose feasibility is to be tested.

    References
    ----------
    .. [1] Gomez, Jose A., Kai Höffner, and Paul I. Barton.
    “DFBAlab: A Fast and Reliable MATLAB Code for Dynamic Flux Balance
    Analysis.” BMC Bioinformatics 15, no. 1 (December 18, 2014): 409.
    https://doi.org/10.1186/s12859-014-0409-8.

    """

    obj_vars = []
    prob = model.problem
    for met in model.metabolites:
        s_plus = prob.Variable("s_plus_" + met.id, lb=0)
        s_minus = prob.Variable("s_minus_" + met.id, lb=0)

        model.add_cons_vars([s_plus, s_minus])
        model.constraints[met.id].set_linear_coefficients(
            {s_plus: 1.0, s_minus: -1.0})
        obj_vars.append(s_plus)
        obj_vars.append(s_minus)

    model.objective = prob.Objective(Zero, sloppy=True, direction="min")
    model.objective.set_linear_coefficients({v: 1.0 for v in obj_vars})

def add_lp_feasibility ( model ) : obj_vars = [ ] prob = model . problem for met in model . metabolites : s_plus = prob . Variable ( "s_plus_" + met . id , lb = 0 ) s_minus = prob . Variable ( "s_minus_" + met . id , lb = 0 ) model . add_cons_vars ( [ s_plus , s_minus ] ) model . constraints [ met . id ] . set_linear_coefficients ( { s_plus : 1.0 , s_minus : - 1.0 } ) obj_vars . append ( s_plus ) obj_vars . append ( s_minus ) model . objective = prob . Objective ( Zero , sloppy = True , direction = "min" ) model . objective . set_linear_coefficients ( { v : 1.0 for v in obj_vars } )

Add a new objective and variables to ensure a feasible solution .


def add_lexicographic_constraints(model,
                                  objectives,
                                  objective_direction='max'):
    """
    Successively optimize separate targets in a specific order.

    For each objective, optimize the model and set the optimal value as a
    constraint. Proceed in the order of the objectives given. Due to the
    specific order this is called lexicographic FBA [1]_. This
    procedure is useful for returning unique solutions for a set of important
    fluxes. Typically this is applied to exchange fluxes.

    Parameters
    ----------
    model : cobra.Model
        The model to be optimized.
    objectives : list
        A list of reactions (or objectives) in the model for which unique
        fluxes are to be determined.
    objective_direction : str or list, optional
        The desired objective direction for each reaction (if a list) or the
        objective direction to use for all reactions (default maximize).

    Returns
    -------
    optimized_fluxes : pandas.Series
        A vector containing the optimized fluxes for each of the given
        reactions in `objectives`.

    References
    ----------
    .. [1] Gomez, Jose A., Kai Höffner, and Paul I. Barton.
    “DFBAlab: A Fast and Reliable MATLAB Code for Dynamic Flux Balance
    Analysis.” BMC Bioinformatics 15, no. 1 (December 18, 2014): 409.
    https://doi.org/10.1186/s12859-014-0409-8.

    """

    if type(objective_direction) is not list:
        objective_direction = [objective_direction] * len(objectives)

    constraints = []
    for rxn_id, obj_dir in zip(objectives, objective_direction):
        model.objective = model.reactions.get_by_id(rxn_id)
        model.objective_direction = obj_dir
        constraints.append(fix_objective_as_constraint(model))

    return pd.Series(constraints, index=objectives)

def add_lexicographic_constraints ( model , objectives , objective_direction = 'max' ) : if type ( objective_direction ) is not list : objective_direction = [ objective_direction ] * len ( objectives ) constraints = [ ] for rxn_id , obj_dir in zip ( objectives , objective_direction ) : model . objective = model . reactions . get_by_id ( rxn_id ) model . objective_direction = obj_dir constraints . append ( fix_objective_as_constraint ( model ) ) return pd . Series ( constraints , index = objectives )

Successively optimize separate targets in a specific order .


def shared_np_array(shape, data=None, integer=False):
    """Create a new numpy array that resides in shared memory.

    Parameters
    ----------
    shape : tuple of ints
        The shape of the new array.
    data : numpy.array
        Data to copy to the new array. Has to have the same shape.
    integer : boolean
        Whether to use an integer array. Defaults to False which means
        float array.

    """

    size = np.prod(shape)

    if integer:
        array = Array(ctypes.c_int64, int(size))
        np_array = np.frombuffer(array.get_obj(), dtype="int64")
    else:
        array = Array(ctypes.c_double, int(size))
        np_array = np.frombuffer(array.get_obj())

    np_array = np_array.reshape(shape)

    if data is not None:
        if len(shape) != len(data.shape):
            raise ValueError("`data` must have the same dimensions"
                             "as the created array.")
        same = all(x == y for x, y in zip(shape, data.shape))

        if not same:
            raise ValueError("`data` must have the same shape"
                             "as the created array.")
        np_array[:] = data

    return np_array

def
shared_np_array
(
shape
,
data
=
,
integer
=
False
)
:
size
=
np
.
prod
(
shape
)
if
integer
:
array
=
Array
(
ctypes
.
c_int64
,
int
(
size
)
)
np_array
=
np
.
frombuffer
(
array
.
get_obj
(
)
,
dtype
=
"int64"
)
else
:
array
=
Array
(
ctypes
.
c_double
,
int
(
size
)
)
np_array
=
np
.
frombuffer
(
array
.
get_obj
(
)
)
np_array
=
np_array
.
reshape
(
shape
)
if
data
is
not
:
if
len
(
shape
)
!=
len
(
data
.
shape
)
:
raise
ValueError
(
"`data` must have the same dimensions"
"as the created array."
)
same
=
all
(
x
==
y
for
x
,
y
in
zip
(
shape
,
data
.
shape
)
)
if
not
same
:
raise
ValueError
(
"`data` must have the same shape"
"as the created array."
)
np_array
[
:
]
=
data
return
np_array

Create a new numpy array that resides in shared memory .


def step(sampler, x, delta, fraction=None, tries=0):
    """Sample a new feasible point from the point `x` in direction `delta`."""

    prob = sampler.problem
    valid = ((np.abs(delta) > sampler.feasibility_tol) &
             np.logical_not(prob.variable_fixed))

    # permissible alphas for staying in variable bounds
    valphas = ((1.0 - sampler.bounds_tol) * prob.variable_bounds -
               x)[:, valid]
    valphas = (valphas / delta[valid]).flatten()

    if prob.bounds.shape[0] > 0:
        # permissible alphas for staying in constraint bounds
        ineqs = prob.inequalities.dot(delta)
        valid = np.abs(ineqs) > sampler.feasibility_tol
        balphas = ((1.0 - sampler.bounds_tol) * prob.bounds -
                   prob.inequalities.dot(x))[:, valid]
        balphas = (balphas / ineqs[valid]).flatten()

        # combined alphas
        alphas = np.hstack([valphas, balphas])
    else:
        alphas = valphas
    pos_alphas = alphas[alphas > 0.0]
    neg_alphas = alphas[alphas <= 0.0]
    alpha_range = np.array([neg_alphas.max() if len(neg_alphas) > 0 else 0,
                            pos_alphas.min() if len(pos_alphas) > 0 else 0])

    if fraction:
        alpha = alpha_range[0] + fraction * (alpha_range[1] - alpha_range[0])
    else:
        alpha = np.random.uniform(alpha_range[0], alpha_range[1])

    p = x + alpha * delta

    # Numerical instabilities may cause bounds invalidation
    # reset sampler and sample from one of the original warmup directions
    # if that occurs. Also reset if we got stuck.
    if (np.any(sampler._bounds_dist(p) < -sampler.bounds_tol) or
            np.abs(np.abs(alpha_range).max() * delta).max() <
            sampler.bounds_tol):
        if tries > MAX_TRIES:
            raise RuntimeError("Can not escape sampling region, model seems"
                               " numerically unstable :( Reporting the "
                               "model to "
                               "https://github.com/opencobra/cobrapy/issues "
                               "will help us to fix this :)")
        LOGGER.info("found bounds infeasibility in sample, "
                    "resetting to center")
        newdir = sampler.warmup[np.random.randint(sampler.n_warmup)]
        sampler.retries += 1

        return step(sampler, sampler.center, newdir - sampler.center, None,
                    tries + 1)
    return p

Sample a new feasible point from the point x in direction delta .


def __build_problem(self):
        """Build the matrix representation of the sampling problem."""

        # Set up the mathematical problem
        prob = constraint_matrices(self.model, zero_tol=self.feasibility_tol)

        # check if there any non-zero equality constraints
        equalities = prob.equalities
        b = prob.b
        bounds = np.atleast_2d(prob.bounds).T
        var_bounds = np.atleast_2d(prob.variable_bounds).T
        homogeneous = all(np.abs(b) < self.feasibility_tol)
        fixed_non_zero = np.abs(prob.variable_bounds[:, 1]) > \
            self.feasibility_tol
        fixed_non_zero &= prob.variable_fixed

        # check if there are any non-zero fixed variables, add them as
        # equalities to the stoichiometric matrix
        if any(fixed_non_zero):
            n_fixed = fixed_non_zero.sum()
            rows = np.zeros((n_fixed, prob.equalities.shape[1]))
            rows[range(n_fixed), np.where(fixed_non_zero)] = 1.0
            equalities = np.vstack([equalities, rows])
            var_b = prob.variable_bounds[:, 1]
            b = np.hstack([b, var_b[fixed_non_zero]])
            homogeneous = False

        # Set up a projection that can cast point into the nullspace
        nulls = nullspace(equalities)

        # convert bounds to a matrix and add variable bounds as well
        return Problem(
            equalities=shared_np_array(equalities.shape, equalities),
            b=shared_np_array(b.shape, b),
            inequalities=shared_np_array(prob.inequalities.shape,
                                         prob.inequalities),
            bounds=shared_np_array(bounds.shape, bounds),
            variable_fixed=shared_np_array(prob.variable_fixed.shape,
                                           prob.variable_fixed, integer=True),
            variable_bounds=shared_np_array(var_bounds.shape, var_bounds),
            nullspace=shared_np_array(nulls.shape, nulls),
            homogeneous=homogeneous
        )

Build the matrix representation of the sampling problem .


def generate_fva_warmup(self):
        """Generate the warmup points for the sampler.

        Generates warmup points by setting each flux as the sole objective
        and minimizing/maximizing it. Also caches the projection of the
        warmup points into the nullspace for non-homogeneous problems (only
        if necessary).

        """

        self.n_warmup = 0
        reactions = self.model.reactions
        self.warmup = np.zeros((2 * len(reactions), len(self.model.variables)))
        self.model.objective = Zero

        for sense in ("min", "max"):
            self.model.objective_direction = sense

            for i, r in enumerate(reactions):
                variables = (self.model.variables[self.fwd_idx[i]],
                             self.model.variables[self.rev_idx[i]])

                # Omit fixed reactions if they are non-homogeneous
                if r.upper_bound - r.lower_bound < self.bounds_tol:
                    LOGGER.info("skipping fixed reaction %s" % r.id)
                    continue

                self.model.objective.set_linear_coefficients(
                    {variables[0]: 1, variables[1]: -1})

                self.model.slim_optimize()

                if not self.model.solver.status == OPTIMAL:
                    LOGGER.info("can not maximize reaction %s, skipping it" %
                                r.id)
                    continue

                primals = self.model.solver.primal_values
                sol = [primals[v.name] for v in self.model.variables]
                self.warmup[self.n_warmup, ] = sol
                self.n_warmup += 1

                # Reset objective
                self.model.objective.set_linear_coefficients(
                    {variables[0]: 0, variables[1]: 0})

        # Shrink to measure
        self.warmup = self.warmup[0:self.n_warmup, :]

        # Remove redundant search directions
        keep = np.logical_not(self._is_redundant(self.warmup))
        self.warmup = self.warmup[keep, :]
        self.n_warmup = self.warmup.shape[0]

        # Catch some special cases
        if len(self.warmup.shape) == 1 or self.warmup.shape[0] == 1:
            raise ValueError("Your flux cone consists only of a single point!")
        elif self.n_warmup == 2:
            if not self.problem.homogeneous:
                raise ValueError("Can not sample from an inhomogenous problem"
                                 " with only 2 search directions :(")
            LOGGER.info("All search directions on a line, adding another one.")
            newdir = self.warmup.T.dot([0.25, 0.25])
            self.warmup = np.vstack([self.warmup, newdir])
            self.n_warmup += 1

        # Shrink warmup points to measure
        self.warmup = shared_np_array(
            (self.n_warmup, len(self.model.variables)), self.warmup)

Generate the warmup points for the sampler .


def _reproject(self, p):
        """Reproject a point into the feasibility region.

        This function is guaranteed to return a new feasible point. However,
        no guarantees in terms of proximity to the original point can be made.

        Parameters
        ----------
        p : numpy.array
            The current sample point.

        Returns
        -------
        numpy.array
            A new feasible point. If `p` was feasible it wil return p.

        """

        nulls = self.problem.nullspace
        equalities = self.problem.equalities

        # don't reproject if point is feasible
        if np.allclose(equalities.dot(p), self.problem.b,
                       rtol=0, atol=self.feasibility_tol):
            new = p
        else:
            LOGGER.info("feasibility violated in sample"
                        " %d, trying to reproject" % self.n_samples)
            new = nulls.dot(nulls.T.dot(p))

        # Projections may violate bounds
        # set to random point in space in that case
        if any(new != p):
            LOGGER.info("reprojection failed in sample"
                        " %d, using random point in space" % self.n_samples)
            new = self._random_point()

        return new

Reproject a point into the feasibility region .


def _random_point(self):
        """Find an approximately random point in the flux cone."""

        idx = np.random.randint(self.n_warmup,
                                size=min(2, np.ceil(np.sqrt(self.n_warmup))))
        return self.warmup[idx, :].mean(axis=0)

def _random_point ( self ) : idx = np . random . randint ( self . n_warmup , size = min ( 2 , np . ceil ( np . sqrt ( self . n_warmup ) ) ) ) return self . warmup [ idx , : ] . mean ( axis = 0 )

Find an approximately random point in the flux cone .


def _is_redundant(self, matrix, cutoff=None):
        """Identify rdeundant rows in a matrix that can be removed."""

        cutoff = 1.0 - self.feasibility_tol

        # Avoid zero variances
        extra_col = matrix[:, 0] + 1

        # Avoid zero rows being correlated with constant rows
        extra_col[matrix.sum(axis=1) == 0] = 2
        corr = np.corrcoef(np.c_[matrix, extra_col])
        corr = np.tril(corr, -1)

        return (np.abs(corr) > cutoff).any(axis=1)

Identify rdeundant rows in a matrix that can be removed .


def _bounds_dist(self, p):
        """Get the lower and upper bound distances. Negative is bad."""

        prob = self.problem
        lb_dist = (p - prob.variable_bounds[0, ]).min()
        ub_dist = (prob.variable_bounds[1, ] - p).min()

        if prob.bounds.shape[0] > 0:
            const = prob.inequalities.dot(p)
            const_lb_dist = (const - prob.bounds[0, ]).min()
            const_ub_dist = (prob.bounds[1, ] - const).min()
            lb_dist = min(lb_dist, const_lb_dist)
            ub_dist = min(ub_dist, const_ub_dist)

        return np.array([lb_dist, ub_dist])

def _bounds_dist ( self , p ) : prob = self . problem lb_dist = ( p - prob . variable_bounds [ 0 , ] ) . min ( ) ub_dist = ( prob . variable_bounds [ 1 , ] - p ) . min ( ) if prob . bounds . shape [ 0 ] > 0 : const = prob . inequalities . dot ( p ) const_lb_dist = ( const - prob . bounds [ 0 , ] ) . min ( ) const_ub_dist = ( prob . bounds [ 1 , ] - const ) . min ( ) lb_dist = min ( lb_dist , const_lb_dist ) ub_dist = min ( ub_dist , const_ub_dist ) return np . array ( [ lb_dist , ub_dist ] )

Get the lower and upper bound distances . Negative is bad .


def batch(self, batch_size, batch_num, fluxes=True):
        """Create a batch generator.

        This is useful to generate n batches of m samples each.

        Parameters
        ----------
        batch_size : int
            The number of samples contained in each batch (m).
        batch_num : int
            The number of batches in the generator (n).
        fluxes : boolean
            Whether to return fluxes or the internal solver variables. If set
            to False will return a variable for each forward and backward flux
            as well as all additional variables you might have defined in the
            model.

        Yields
        ------
        pandas.DataFrame
            A DataFrame with dimensions (batch_size x n_r) containing
            a valid flux sample for a total of n_r reactions (or variables if
            fluxes=False) in each row.

        """

        for i in range(batch_num):
            yield self.sample(batch_size, fluxes=fluxes)

def batch ( self , batch_size , batch_num , fluxes = True ) : for i in range ( batch_num ) : yield self . sample ( batch_size , fluxes = fluxes )


def validate(self, samples):
        """Validate a set of samples for equality and inequality feasibility.

        Can be used to check whether the generated samples and warmup points
        are feasible.

        Parameters
        ----------
        samples : numpy.matrix
            Must be of dimension (n_samples x n_reactions). Contains the
            samples to be validated. Samples must be from fluxes.

        Returns
        -------
        numpy.array
            A one-dimensional numpy array of length containing
            a code of 1 to 3 letters denoting the validation result:

            - 'v' means feasible in bounds and equality constraints
            - 'l' means a lower bound violation
            - 'u' means a lower bound validation
            - 'e' means and equality constraint violation

        """

        samples = np.atleast_2d(samples)
        prob = self.problem

        if samples.shape[1] == len(self.model.reactions):
            S = create_stoichiometric_matrix(self.model)
            b = np.array([self.model.constraints[m.id].lb for m in
                          self.model.metabolites])
            bounds = np.array([r.bounds for r in self.model.reactions]).T
        elif samples.shape[1] == len(self.model.variables):
            S = prob.equalities
            b = prob.b
            bounds = prob.variable_bounds
        else:
            raise ValueError("Wrong number of columns. samples must have a "
                             "column for each flux or variable defined in the "
                             "model!")

        feasibility = np.abs(S.dot(samples.T).T - b).max(axis=1)
        lb_error = (samples - bounds[0, ]).min(axis=1)
        ub_error = (bounds[1, ] - samples).min(axis=1)

        if (samples.shape[1] == len(self.model.variables) and
                prob.inequalities.shape[0]):
            consts = prob.inequalities.dot(samples.T)
            lb_error = np.minimum(
                lb_error,
                (consts - prob.bounds[0, ]).min(axis=1))
            ub_error = np.minimum(
                ub_error,
                (prob.bounds[1, ] - consts).min(axis=1)
            )

        valid = (
            (feasibility < self.feasibility_tol) &
            (lb_error > -self.bounds_tol) &
            (ub_error > -self.bounds_tol))
        codes = np.repeat("", valid.shape[0]).astype(np.dtype((str, 3)))
        codes[valid] = "v"
        codes[lb_error <= -self.bounds_tol] = np.char.add(
            codes[lb_error <= -self.bounds_tol], "l")
        codes[ub_error <= -self.bounds_tol] = np.char.add(
            codes[ub_error <= -self.bounds_tol], "u")
        codes[feasibility > self.feasibility_tol] = np.char.add(
            codes[feasibility > self.feasibility_tol], "e")

        return codes

Validate a set of samples for equality and inequality feasibility .


def prune_unused_metabolites(cobra_model):
    """Remove metabolites that are not involved in any reactions and
    returns pruned model

    Parameters
    ----------
    cobra_model: class:`~cobra.core.Model.Model` object
        the model to remove unused metabolites from

    Returns
    -------
    output_model: class:`~cobra.core.Model.Model` object
        input model with unused metabolites removed
    inactive_metabolites: list of class:`~cobra.core.reaction.Reaction`
        list of metabolites that were removed
    """

    output_model = cobra_model.copy()
    inactive_metabolites = [m for m in output_model.metabolites
                            if len(m.reactions) == 0]
    output_model.remove_metabolites(inactive_metabolites)
    return output_model, inactive_metabolites

def prune_unused_metabolites ( cobra_model ) : output_model = cobra_model . copy ( ) inactive_metabolites = [ m for m in output_model . metabolites if len ( m . reactions ) == 0 ] output_model . remove_metabolites ( inactive_metabolites ) return output_model , inactive_metabolites


def prune_unused_reactions(cobra_model):
    """Remove reactions with no assigned metabolites, returns pruned model

    Parameters
    ----------
    cobra_model: class:`~cobra.core.Model.Model` object
        the model to remove unused reactions from

    Returns
    -------
    output_model: class:`~cobra.core.Model.Model` object
        input model with unused reactions removed
    reactions_to_prune: list of class:`~cobra.core.reaction.Reaction`
        list of reactions that were removed
    """

    output_model = cobra_model.copy()
    reactions_to_prune = [r for r in output_model.reactions
                          if len(r.metabolites) == 0]
    output_model.remove_reactions(reactions_to_prune)
    return output_model, reactions_to_prune

def prune_unused_reactions ( cobra_model ) : output_model = cobra_model . copy ( ) reactions_to_prune = [ r for r in output_model . reactions if len ( r . metabolites ) == 0 ] output_model . remove_reactions ( reactions_to_prune ) return output_model , reactions_to_prune

Remove reactions with no assigned metabolites returns pruned model


def undelete_model_genes(cobra_model):
    """Undoes the effects of a call to delete_model_genes in place.

    cobra_model:  A cobra.Model which will be modified in place

    """

    if cobra_model._trimmed_genes is not None:
        for x in cobra_model._trimmed_genes:
            x.functional = True

    if cobra_model._trimmed_reactions is not None:
        for the_reaction, (lower_bound, upper_bound) in \
                cobra_model._trimmed_reactions.items():
            the_reaction.lower_bound = lower_bound
            the_reaction.upper_bound = upper_bound

    cobra_model._trimmed_genes = []
    cobra_model._trimmed_reactions = {}
    cobra_model._trimmed = False

def undelete_model_genes ( cobra_model ) : if cobra_model . _trimmed_genes is not None : for x in cobra_model . _trimmed_genes : x . functional = True if cobra_model . _trimmed_reactions is not None : for the_reaction , ( lower_bound , upper_bound ) in cobra_model . _trimmed_reactions . items ( ) : the_reaction . lower_bound = lower_bound the_reaction . upper_bound = upper_bound cobra_model . _trimmed_genes = [ ] cobra_model . _trimmed_reactions = { } cobra_model . _trimmed = False

Undoes the effects of a call to delete_model_genes in place .


def find_gene_knockout_reactions(cobra_model, gene_list,
                                 compiled_gene_reaction_rules=None):
    """identify reactions which will be disabled when the genes are knocked out

    cobra_model: :class:`~cobra.core.Model.Model`

    gene_list: iterable of :class:`~cobra.core.Gene.Gene`

    compiled_gene_reaction_rules: dict of {reaction_id: compiled_string}
        If provided, this gives pre-compiled gene_reaction_rule strings.
        The compiled rule strings can be evaluated much faster. If a rule
        is not provided, the regular expression evaluation will be used.
        Because not all gene_reaction_rule strings can be evaluated, this
        dict must exclude any rules which can not be used with eval.

    """
    potential_reactions = set()
    for gene in gene_list:
        if isinstance(gene, string_types):
            gene = cobra_model.genes.get_by_id(gene)
        potential_reactions.update(gene._reaction)
    gene_set = {str(i) for i in gene_list}
    if compiled_gene_reaction_rules is None:
        compiled_gene_reaction_rules = {r: parse_gpr(r.gene_reaction_rule)[0]
                                        for r in potential_reactions}

    return [r for r in potential_reactions
            if not eval_gpr(compiled_gene_reaction_rules[r], gene_set)]

def find_gene_knockout_reactions ( cobra_model , gene_list , compiled_gene_reaction_rules = None ) : potential_reactions = set ( ) for gene in gene_list : if isinstance ( gene , string_types ) : gene = cobra_model . genes . get_by_id ( gene ) potential_reactions . update ( gene . _reaction ) gene_set = { str ( i ) for i in gene_list } if compiled_gene_reaction_rules is None : compiled_gene_reaction_rules = { r : parse_gpr ( r . gene_reaction_rule ) [ 0 ] for r in potential_reactions } return [ r for r in potential_reactions if not eval_gpr ( compiled_gene_reaction_rules [ r ] , gene_set ) ]


def remove_genes(cobra_model, gene_list, remove_reactions=True):
    """remove genes entirely from the model

    This will also simplify all gene_reaction_rules with this
    gene inactivated."""
    gene_set = {cobra_model.genes.get_by_id(str(i)) for i in gene_list}
    gene_id_set = {i.id for i in gene_set}
    remover = _GeneRemover(gene_id_set)
    ast_rules = get_compiled_gene_reaction_rules(cobra_model)
    target_reactions = []
    for reaction, rule in iteritems(ast_rules):
        if reaction.gene_reaction_rule is None or \
                len(reaction.gene_reaction_rule) == 0:
            continue
        # reactions to remove
        if remove_reactions and not eval_gpr(rule, gene_id_set):
            target_reactions.append(reaction)
        else:
            # if the reaction is not removed, remove the gene
            # from its gpr
            remover.visit(rule)
            new_rule = ast2str(rule)
            if new_rule != reaction.gene_reaction_rule:
                reaction.gene_reaction_rule = new_rule
    for gene in gene_set:
        cobra_model.genes.remove(gene)
        # remove reference to the gene in all groups
        associated_groups = cobra_model.get_associated_groups(gene)
        for group in associated_groups:
            group.remove_members(gene)
    cobra_model.remove_reactions(target_reactions)


def gapfill(model, universal=None, lower_bound=0.05,
            penalties=None, demand_reactions=True, exchange_reactions=False,
            iterations=1):
    """Perform gapfilling on a model.

    See documentation for the class GapFiller.

    Parameters
    ----------
    model : cobra.Model
        The model to perform gap filling on.
    universal : cobra.Model, None
        A universal model with reactions that can be used to complete the
        model. Only gapfill considering demand and exchange reactions if
        left missing.
    lower_bound : float
        The minimally accepted flux for the objective in the filled model.
    penalties : dict, None
        A dictionary with keys being 'universal' (all reactions included in
        the universal model), 'exchange' and 'demand' (all additionally
        added exchange and demand reactions) for the three reaction types.
        Can also have reaction identifiers for reaction specific costs.
        Defaults are 1, 100 and 1 respectively.
    iterations : int
        The number of rounds of gapfilling to perform. For every iteration,
        the penalty for every used reaction increases linearly. This way,
        the algorithm is encouraged to search for alternative solutions
        which may include previously used reactions. I.e., with enough
        iterations pathways including 10 steps will eventually be reported
        even if the shortest pathway is a single reaction.
    exchange_reactions : bool
        Consider adding exchange (uptake) reactions for all metabolites
        in the model.
    demand_reactions : bool
        Consider adding demand reactions for all metabolites.

    Returns
    -------
    iterable
        list of lists with on set of reactions that completes the model per
        requested iteration.

    Examples
    --------
    >>> import cobra.test as ct
    >>> from cobra import Model
    >>> from cobra.flux_analysis import gapfill
    >>> model = ct.create_test_model("salmonella")
    >>> universal = Model('universal')
    >>> universal.add_reactions(model.reactions.GF6PTA.copy())
    >>> model.remove_reactions([model.reactions.GF6PTA])
    >>> gapfill(model, universal)
    """
    gapfiller = GapFiller(model, universal=universal,
                          lower_bound=lower_bound, penalties=penalties,
                          demand_reactions=demand_reactions,
                          exchange_reactions=exchange_reactions)
    return gapfiller.fill(iterations=iterations)

def gapfill ( model , universal = None , lower_bound = 0.05 , penalties = None , demand_reactions = True , exchange_reactions = False , iterations = 1 ) : gapfiller = GapFiller ( model , universal = universal , lower_bound = lower_bound , penalties = penalties , demand_reactions = demand_reactions , exchange_reactions = exchange_reactions ) return gapfiller . fill ( iterations = iterations )


def extend_model(self, exchange_reactions=False, demand_reactions=True):
        """Extend gapfilling model.

        Add reactions from universal model and optionally exchange and
        demand reactions for all metabolites in the model to perform
        gapfilling on.

        Parameters
        ----------
        exchange_reactions : bool
            Consider adding exchange (uptake) reactions for all metabolites
            in the model.
        demand_reactions : bool
            Consider adding demand reactions for all metabolites.
        """
        for rxn in self.universal.reactions:
            rxn.gapfilling_type = 'universal'
        new_metabolites = self.universal.metabolites.query(
            lambda metabolite: metabolite not in self.model.metabolites
                                                           )
        self.model.add_metabolites(new_metabolites)
        existing_exchanges = []
        for rxn in self.universal.boundary:
            existing_exchanges = existing_exchanges + \
                [met.id for met in list(rxn.metabolites)]

        for met in self.model.metabolites:
            if exchange_reactions:
                # check for exchange reaction in model already
                if met.id not in existing_exchanges:
                    rxn = self.universal.add_boundary(
                        met, type='exchange_smiley', lb=-1000, ub=0,
                        reaction_id='EX_{}'.format(met.id))
                    rxn.gapfilling_type = 'exchange'
            if demand_reactions:
                rxn = self.universal.add_boundary(
                    met, type='demand_smiley', lb=0, ub=1000,
                    reaction_id='DM_{}'.format(met.id))
                rxn.gapfilling_type = 'demand'

        new_reactions = self.universal.reactions.query(
            lambda reaction: reaction not in self.model.reactions
        )
        self.model.add_reactions(new_reactions)