...
 
Commits (344)
......@@ -3,8 +3,10 @@
# Working files
patch
fix*.py
fix*/
fix_*.py
fix_*/
test_*.py
test_*/
# Byte-compiled / optimized / DLL files
__pycache__/
......
......@@ -4,25 +4,34 @@ stages:
- test
- deploy
old test:
testbench (py3):
stage: test
before_script:
- pacman -Sy --noconfirm python-six python-numpy python-cvxopt
# Install PICOS dependencies.
- pacman -Sy --noconfirm python-numpy python-cvxopt
# Install additional solvers.
- pacman -S --noconfirm python-pip glpk gcc
- pip install swiglpk smcp
script:
- python ./test_picos.py
- python ./test.py -vv -s cvxopt glpk smcp
old test (py2):
testbench (py2):
stage: test
before_script:
- pacman -Sy --noconfirm python2-six python2-numpy python2-cvxopt
# Install PICOS dependencies.
- pacman -Sy --noconfirm python2-numpy python2-cvxopt
# Install additional solvers.
- pacman -S --noconfirm python2-pip glpk gcc
- pip2 install swiglpk smcp
script:
- python2 ./test_picos.py
- python2 ./test.py -vv -s cvxopt glpk smcp
doctest:
stage: test
before_script:
- pacman -Sy --noconfirm python-six python-numpy python-cvxopt
- pacman -Sy --noconfirm python-pip python-numpy python-cvxopt
- pacman -S --noconfirm m2r python-sphinx python-matplotlib python-networkx
- pip install sphinx-automodapi
script:
- sphinx-build -b doctest doc doctest
......@@ -41,7 +50,7 @@ anaconda:
stage: deploy
before_script:
# Install Miniconda from the AUR.
- pacman -Sy --noconfirm wget fakeroot
- pacman -Sy --noconfirm wget fakeroot audit
- wget https://aur.archlinux.org/cgit/aur.git/snapshot/miniconda3.tar.gz
- tar xf miniconda3.tar.gz
- cd miniconda3
......@@ -61,12 +70,12 @@ pages:
stage: deploy
before_script:
# Install PICOS dependencies.
- pacman -Sy --noconfirm python-six python-numpy python-cvxopt
- pacman -Sy --noconfirm python-numpy python-cvxopt
# Install documentation built dependencies.
- pacman -S --noconfirm m2r python-sphinx python-matplotlib python-networkx
- pacman -S --noconfirm texlive-most python-sphinx_rtd_theme python-scipy
- pacman -S --noconfirm git python-pip glpk
- pip install swiglpk
- pacman -S --noconfirm git python-pip glpk graphviz
- pip install sphinx-automodapi swiglpk
script:
- sphinx-build -b html doc html
- mkdir public
......
......@@ -3,6 +3,7 @@
This file documents major changes to PICOS.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[1.2.0]: https://gitlab.com/picos-api/picos/compare/v1.1.3...v1.2.0
[1.1.3]: https://gitlab.com/picos-api/picos/compare/v1.1.2...v1.1.3
[1.1.2]: https://gitlab.com/picos-api/picos/compare/v1.1.1...v1.1.2
[1.1.1]: https://gitlab.com/picos-api/picos/compare/v1.1.0...v1.1.1
......@@ -16,6 +17,64 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[0.1.0]: about:blank
## [1.2.0] - 2019-01-11
### Important
- :attr:`A scalar expression's value <picos.expressions.Expression.value>` and
:attr:`a scalar constraint's dual <picos.constraints.Constraint.dual>` are
returned as scalar types as opposed to 1×1 matrices.
- The dual value returned for rotated second order cone constraints is now a
proper member of the dual cone (which equals the primal cone up to a factor of
:math:`4`). Previously, the dual of an equivalent second order cone constraint
was returned.
- The Python 2/3 compatibility library ``six`` is no longer a dependency.
### Added
- Support for the ECOS solver.
- Experimental support for MOSEK's new Fusion API.
- Full support for exponential cone programming.
- A production testing framework featuring around 40 novel optimization test
cases that allows quick selection of tests, solvers, and solver options.
- A "glyph" system that allows the user to adjust the string representations of
future expressions and constraints. For instance,
:func:`picos.latin1() <picos.glyphs.latin1>` disables use of unicode symbols.
- Support for symmetric variables with all solvers, even if they do not support
semidefinite programming.
### Changed
- Solver implementations each have a source file of their own, and a common
interface that makes implementing new solvers easier.
- Likewise, constraint implementations each have a source file of their own.
- The implementations of CPLEX, Gurobi, MOSEK and SCIP have been rewritten.
- Solver selection takes into account how well a problem is supported,
distinguishing between native, secondary, experimental and limited support.
- Unsupported operations on expressions now produce meaningful exceptions.
- :func:`add_constraint <picos.Problem.add_constraint>` and
:func:`add_list_of_constraints <picos.Problem.add_list_of_constraints>`
always return the constraints
passed to them.
- :func:`add_list_of_constraints <picos.Problem.add_list_of_constraints>`
and :func:`picos.sum <picos.tools.sum>` find a short string representation
automatically.
### Removed
- The old production testing script.
- Support for the SDPA solver.
- Support for sequential quadratic programming.
- The options ``convert_quad_to_socp_if_needed``, ``pass_simple_cons_as_bound``,
``return_constraints``, ``handleBarVars``, ``handleConeVars`` and
``smcp_feas``.
- Support for GLPK and MOSEK through CVXOPT.
### Fixed
- Performance issues when exporting variable bounds to CVXOPT.
- Hadamard product involving complex matrices.
- Adding constant terms to quadratic expression.
- Incorrect or redundant expression string representations.
- GLPK handling of the default ``maxit`` option.
- Miscellaneous solver-specific bugs in the solvers that were re-implemented.
## [1.1.3] - 2018-10-05
### Added
......@@ -106,8 +165,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Support for inequalities involving the :math:`L_{p,q}`-norm of an affine
expression, see :func:`norm <picos.tools.norm>`.
- Support for equalities involving complex coefficients.
- New :attr:`variable type <picos.Variable.vtype>` for antisymmetric matrix
variables: ``antisym``.
- New :attr:`variable type <picos.expressions.Variable.vtype>` for antisymmetric
matrix variables: ``antisym``.
- Set expressions that affine expressions can be constrained to be an element
of, see :func:`ball <picos.tools.ball>`, :func:`simplex <picos.tools.simplex>`
and :func:`truncated_simplex <picos.tools.truncated_simplex>`.
......@@ -118,11 +177,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
``^`` operator, read :ref:`the tutorial on overloads <overloads>`.
- Partial transposition of an aAffine Expression, see
:func:`partial_transpose <picos.tools.partial_transpose>` and
:attr:`AffinExp.Tx <picos.AffinExp.Tx>`.
:attr:`AffinExp.Tx <picos.expressions.AffinExp.Tx>`.
### Changed
- Improved efficiency of the sparse SDPA file format writer.
- Improved efficiency of :func:`to_real <picos.Problem.to_real>`.
- Improved efficiency of :func:`to_real <picos.Problem.as_real>`.
### Fixed
- Scalar product of hermitian matrices.
......@@ -136,15 +195,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
:ref:`the documentation on complex problems <complex>`.
- Helper function to input (multicommodity) graph flow problems, see
:ref:`the tutorial on flow constraints <flowcons>`.
- Additional ``coef`` argument to :func:`picos.tracepow <picos.tools.tracepow>`,
to represent constraints of the form
- Additional ``coef`` argument to :func:`tracepow <picos.tools.tracepow>`, to
represent constraints of the form
:math:`\operatorname{trace}(M X^p) \geq t`.
### Changed
- Significantly improved performance of
:func:`AffinExp.__getitem__ <picos.AffinExp.__getitem__>`.
- Significantly improved slicing performance for
:class:`affine expressions <picos.expressions.AffinExp>`.
- Improved performance of
:func:`_retrieve_matrix <picos.tools._retrieve_matrix>`.
:func:`retrieve_matrix <picos.tools.retrieve_matrix>`.
- Improved performance when retrieving primal solution from CPLEX.
- The documentation received an overhaul.
......@@ -159,11 +218,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
with :func:`picos.detrootn <picos.tools.detrootn>`.
- Ability to specify variable bounds directly rather than by adding constraints,
see :func:`add_variable <picos.Problem.add_variable>`,
:func:`set_lower() <picos.Variable.set_lower>`,
:func:`set_upper() <picos.Variable.set_upper>`,
:func:`set_sparse_lower() <picos.Variable.set_sparse_lower>` and
:func:`set_sparse_upper() <picos.Variable.set_sparse_upper>`.
- Problem dualization, see :func:`dualize <picos.Problem.dualize>`.
:func:`set_lower() <picos.expressions.Variable.set_lower>`,
:func:`set_upper() <picos.expressions.Variable.set_upper>`,
:func:`set_sparse_lower() <picos.expressions.Variable.set_sparse_lower>` and
:func:`set_sparse_upper() <picos.expressions.Variable.set_sparse_upper>`.
- Problem dualization, see :func:`dualize <picos.Problem.as_dual>`.
- Option ``solve_via_dual`` which controls passing the dual problem to the
solver instead of the primal problem. This can result in a significant
speedup for certain problems.
......
......@@ -7,10 +7,10 @@ solvers, very much like [YALMIP](http://users.isy.liu.se/johanl/yalmip/) or
PICOS allows you to enter a mathematical optimization problem as a **high level
model**, with painless support for **(complex) vector and matrix variables** and
**multidemensional algebra**. Your model will be transformed to the standard form
understood by an appropriate solver that is available at runtime. This makes
your application **portable** as users have the choice between several commercial
and open source solvers.
**multidemensional algebra**. Your model will be transformed to the standard
form understood by an appropriate solver that is available at runtime. This
makes your application **portable** as users have the choice between several
commercial and open source solvers.
Features
--------
......@@ -23,16 +23,15 @@ along with the Python interface listed here.
| --------------------------------------------------------- | ---------------------------------------------------------- | --- | --- | --- | --- | --- | --- | -------- |
| [CPLEX](https://www.ibm.com/analytics/cplex-optimizer) | included | Yes | Yes | | Yes | | Yes | non-free |
| [CVXOPT](https://cvxopt.org/) | native | Yes | Yes | Yes | Yes | Yes¹| | [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) |
| [ECOS](https://www.embotech.com/ECOS)² | [ecos-python](https://github.com/embotech/ecos-python) | Yes | Yes | | Yes | Yes | Yes | [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) |
| [GLPK](https://www.gnu.org/software/glpk/)² | [swiglpk](https://github.com/biosustain/swiglpk) | Yes | | | | | Yes | [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) |
| [ECOS](https://www.embotech.com/ECOS) | [ecos-python](https://github.com/embotech/ecos-python) | Yes | Yes | | Yes | Yes | Yes | [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) |
| [GLPK](https://www.gnu.org/software/glpk/) | [swiglpk](https://github.com/biosustain/swiglpk) | Yes | | | | | Yes | [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) |
| [Gurobi](http://www.gurobi.com/products/gurobi-optimizer) | included | Yes | Yes | | Yes | | Yes | non-free |
| [MOSEK](https://www.mosek.com/) | included | Yes | Yes | Yes | Yes | | Yes | non-free |
| [SMCP](http://smcp.readthedocs.io/en/latest/) | native | Yes³| Yes³| Yes | Yes³| | | [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) |
| [SCIP](http://scip.zib.de/)² | [PySCIPOpt](https://github.com/SCIP-Interfaces/PySCIPOpt/) | Yes | Yes | | Yes | | Yes | [ZIB](https://scip.zib.de/academic.txt)/[MIT](https://github.com/SCIP-Interfaces/PySCIPOpt/blob/master/LICENSE) |
| [SMCP](http://smcp.readthedocs.io/en/latest/) | native | Yes²| Yes²| Yes | Yes²| | | [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) |
| [SCIP](http://scip.zib.de/) | [PySCIPOpt](https://github.com/SCIP-Interfaces/PySCIPOpt/) | Yes | Yes | | Yes | | Yes | [ZIB](https://scip.zib.de/academic.txt)/[MIT](https://github.com/SCIP-Interfaces/PySCIPOpt/blob/master/LICENSE) |
¹ only [geometric programming](https://en.wikipedia.org/wiki/Geometric_programming),
² [work in progress](https://gitlab.com/picos-api/picos/tree/future),
³ experimental
² experimental
### Example
......@@ -94,7 +93,6 @@ If you are installing PICOS manually, you can choose between a number of
[source releases](https://gitlab.com/picos-api/picos/tags).
You will need to have at least the following Python packages installed:
- [Six](https://pypi.org/project/six/)
- [NumPy](http://www.numpy.org/)
- [CVXOPT](https://cvxopt.org/)
......
......@@ -23,7 +23,6 @@ requirements:
- python {{ python }}
- cvxopt
- numpy
- six
test:
imports:
......
build/
automodapi/
*.xcf
......@@ -3,10 +3,45 @@
API Reference
=============
.. rubric:: Basic interface
To use PICOS to its full extend you just need to know about the two components
below.
.. toctree::
:maxdepth: 3
api/problem.rst
api/tools.rst
.. rubric:: Generated expressions and constraints
When you use the basic interface above, PICOS creates mathematical expressions
and constraints involving your variables and data. The following sections
describe these objects.
.. toctree::
:maxdepth: 3
api/expressions.rst
api/constraints.rst
.. rubric:: Customizing PICOS
The following components allow you to fine-tune the behavior of PICOS.
.. toctree::
:maxdepth: 3
api/glyphs.rst
.. rubric:: Internal Gears
You don't usually come in direct contact with the classes and functions below,
but if you like to poke around or :ref:`contribute to PICOS <contributing>` we
got you covered!
.. toctree::
:maxdepth: 2
:maxdepth: 3
problem.rst
expression.rst
constraint.rst
tools.rst
api/solvers.rst
.. _constraints:
The Constraints package
=======================
.. automodapi:: picos.constraints
:no-heading:
:headings: =-
.. _expression:
The Expressions module
======================
.. automodule:: picos.expressions
.. _glyphs:
The Glyphs module
=================
.. automodule:: picos.glyphs
.. _problem:
The Problem class
=================
The :class:`Problem <picos.Problem>` class represents your optimization problem
and is your main way of interfacing PICOS. After you create a problem instance,
you can add variables to it via :meth:`add_variable
<picos.Problem.add_variable>` and use standard Python algebraic operators
(:ref:`cheatsheet`) and algebraic functions (:ref:`tools`) to create expressions
and constraints involving these variables.
.. autoclass:: picos.Problem
.. _solvers:
The Solvers package
===================
.. automodapi:: picos.solvers
:no-heading:
:headings: =-
.. _tools:
The Tools module
================
Many of the tools, in particular the algebraic functions, are also available
in the ``picos`` namespace. For example, you can write :func:`picos.sum
<picos.tools.sum>` instead of :func:`picos.tools.sum <picos.tools.sum>`.
In the future, we are looking to split the toolbox into mutltiple modules, so
that it is clear which of the functions are imported into the ``picos``
namespace.
.. automodule:: picos.tools
......@@ -137,16 +137,16 @@ Other useful functions
Transform a problem
~~~~~~~~~~~~~~~~~~~
+-----------------------------------------------------------------+------------------------------------------------------+
| **function** | **short doc** |
+=================================================================+======================================================+
|:func:`convert_quad_to_socp() <picos.tools.convert_quad_to_socp>`| replaces quadratic constraints by |
| | equivalent second order cone constraints |
+-----------------------------------------------------------------+------------------------------------------------------+
|:func:`to_real() <picos.tools.to_real>` | transform complex SDP to equivalent real-valued SDP |
+-----------------------------------------------------------------+------------------------------------------------------+
|:func:`dualize() <picos.tools.dualize>` | returns Lagrangian dual of a problem |
+-----------------------------------------------------------------+------------------------------------------------------+
+-------------------------------------------------------------------+------------------------------------------------------+
| **function** | **short doc** |
+===================================================================+======================================================+
|:func:`convert_quad_to_socp() <picos.Problem.convert_quad_to_socp>`| replaces quadratic constraints by |
| | equivalent second order cone constraints |
+-------------------------------------------------------------------+------------------------------------------------------+
|:func:`as_real() <picos.Problem.as_real>` | transform complex SDP to equivalent real-valued SDP |
+-------------------------------------------------------------------+------------------------------------------------------+
|:func:`as_dual() <picos.Problem.as_dual>` | returns Lagrangian dual of a problem |
+-------------------------------------------------------------------+------------------------------------------------------+
Get information on a problem
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -168,15 +168,15 @@ Get information on a problem
Miscellaneous
~~~~~~~~~~~~~
+-----------------------------------------------------------+-------------------------------------------+
| **function** | **short doc** |
+===========================================================+===========================================+
|:func:`available_solvers() <picos.tools.available_solvers>`| lists installed solvers |
+-----------------------------------------------------------+-------------------------------------------+
|:func:`import_cbf() <picos.tools.import_cbf>` | imports data from a .cbf file |
+-----------------------------------------------------------+-------------------------------------------+
|:func:`eval_dict() <picos.tools.eval_dict>` | evaluates a dictionary of picos variables |
| | (after a problem has been solved) |
+-----------------------------------------------------------+-------------------------------------------+
| :func:`write_to_file() <picos.Problem.write_to_file>` | writes problem to a file |
+-----------------------------------------------------------+-------------------------------------------+
+-------------------------------------------------------------+-------------------------------------------+
| **function** | **short doc** |
+=============================================================+===========================================+
|:func:`available_solvers() <picos.solvers.available_solvers>`| lists installed solvers |
+-------------------------------------------------------------+-------------------------------------------+
|:func:`import_cbf() <picos.tools.import_cbf>` | imports data from a .cbf file |
+-------------------------------------------------------------+-------------------------------------------+
|:func:`eval_dict() <picos.tools.eval_dict>` | evaluates a dictionary of picos variables |
| | (after a problem has been solved) |
+-------------------------------------------------------------+-------------------------------------------+
| :func:`write_to_file() <picos.Problem.write_to_file>` | writes problem to a file |
+-------------------------------------------------------------+-------------------------------------------+
......@@ -24,9 +24,9 @@ it automatically creates two variables called ``Z_RE`` and ``Z_IM`` which contai
real and imaginary part of ``Z``, and that be accessed by using the ``real`` and ``imag`` properties:
>>> Z.real
# variable Z_RE:(3 x 2),continuous #
<3×2 Continuous Variable: Z_RE>
>>> Z.imag
# variable Z_IM:(3 x 2),continuous #
<3×2 Continuous Variable: Z_IM>
>>> Z.vtype
'complex'
......@@ -41,7 +41,7 @@ anf ``H`` (Hermitian transposition, i.e. ``exp.H`` returns ``exp.conj.T`` ).
>>> X = P.add_variable('X',(3,3),'hermitian')
>>> X >> 0
# (3x3)-LMI constraint X ≽ |0| #
<3×3 LMI Constraint: X ≽ 0>
Fidelity in Quantum Information Theory
......@@ -131,15 +131,15 @@ This Problem can be solved as follows in PICOS
:options: +NORMALIZE_WHITESPACE
---------------------
optimization problem (SDP):
optimization problem (Complex SDP):
18 variables, 0 affine constraints, 21 vars in 1 SD cones
Z_IM : (3, 3), continuous
Z_RE : (3, 3), continuous
maximize trace( 0.5*( Z + Z.H ) )
maximize trace(0.5·(Z + Zᴴ))
such that
[P,Z;Z.H,Q] ≽ |0|
[P, Z; Zᴴ, Q] ≽ 0
---------------------
fidelity: F(P,Q) = 37.4742
optimal matrix Z:
......@@ -228,10 +228,10 @@ This problem can be implemented as follows using Picos:
U : (8, 8), hermitian
minimize 〈 U | M 〉
minimize ⟨U, M⟩
such that
U[i,i] = 1.0 for all i
U ≽ |0|
U[i,i] = 1 ∀ i ∈ [0…4]
U ≽ 0
---------------------
optimal variable: U=
......
......@@ -15,24 +15,41 @@ sys.path.insert(0, os.path.abspath(".."))
# Add Sphinx extensions.
extensions = [
'sphinx.ext.autodoc',
# Use for a final build.
#'sphinx.ext.intersphinx'
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.doctest',
'sphinx.ext.imgmath',
'matplotlib.sphinxext.only_directives',
'sphinx_automodapi.automodapi',
'sphinx_automodapi.smart_resolver',
'matplotlib.sphinxext.plot_directive',
'm2r' # Markdown support.
]
# Configure autodoc.
autoclass_content = 'both'
autodoc_member_order = 'groupwise'
autodoc_default_options = {
'members': None,
'undoc-members': None,
# TODO: Uncomment once autodoc_inherit_docstrings works.
#'inherited-members': None,
'show-inheritance': True
}
# HACK: The following does not seem to work as expected, but at least it makes
# an unrelated warning go away…
autodoc_inherit_docstrings = False
# Configure automodapi.
automodapi_toctreedirnm = 'automodapi'
automodsumm_inherited_members = True
# Configure intersphinx.
intersphinx_cache_limit = 10
intersphinx_mapping = {
'sphinx': ('http://sphinx.pocoo.org', None),
'python': ('http://docs.python.org/2.7', None),
'matplotlib':('http://matplotlib.sourceforge.net', None),
'python': ('http://docs.python.org/3', None),
'numpy': ('http://docs.scipy.org/doc/numpy', None),
'cvxopt': ('http://abel.ee.ucla.edu/cvxopt/userguide', None),
'cvxopt': ('http://cvxopt.org/userguide', None)
}
# Configure matplotlib.
......@@ -40,25 +57,31 @@ plot_html_show_source_link = False
plot_formats = ['png']
plot_html_show_formats = False
# ???
# Configure todo.
todo_include_todos = True
# Add template directories.
templates_path = []
# Warn about all invalid references.
nitpicky = True
# HACK: Work around namedtuple instances throwing bad nitpicky warnings.
nitpick_ignore = [
("py:class", "SOCC"),
("py:class", "RSOCC")
]
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
projectname = u'PICOS'
project = u'PICOS'
copyright = u'2012–2018 G. Sagnol, 2017–2018 M. Stahlberg'
copyright = u'2012–2018 G. Sagnol, 2017–2019 M. Stahlberg'
# Name the version and release strings to use throughout the docuemnt.
from version import get_version, SHORT
......@@ -92,6 +115,9 @@ show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# Disable function parentheses.
add_function_parentheses = False
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
......
constraint.py
=============
Constraint
----------
.. autoclass:: picos.Constraint
:members:
GeoMeanConstraint
-----------------
.. autoclass:: picos.GeoMeanConstraint
:members:
:inherited-members: slack
NormP_Constraint
----------------
.. autoclass:: picos.NormP_Constraint
:members:
:inherited-members: slack
NormPQ_Constraint
-----------------
.. autoclass:: picos.NormPQ_Constraint
:members:
:inherited-members: slack
TracePow_Constraint
-------------------
.. autoclass:: picos.TracePow_Constraint
:members:
:inherited-members: slack
Flow_Constraint
---------------
.. autoclass:: picos.Flow_Constraint
:members:
DetRootN_Constraint
-------------------
.. autoclass:: picos.DetRootN_Constraint
:members:
:inherited-members: slack
Sumklargest_Constraint
----------------------
.. autoclass:: picos.Sumklargest_Constraint
:members:
:inherited-members: slack
expression.py
=============
Expression
----------
.. autoclass:: picos.Expression
:members:
AffinExp
--------
.. autoclass:: picos.AffinExp
:members:
Variable
--------
.. _variable:
.. autoclass:: picos.Variable
:members:
Norm
----
.. autoclass:: picos.Norm
:members:
QuadExp
-------
.. autoclass:: picos.QuadExp
:members:
LogSumExp
---------
.. autoclass:: picos.LogSumExp
:members:
GeoMeanExp
----------
.. autoclass:: picos.GeoMeanExp
:members:
NormP_Exp
---------
.. autoclass:: picos.NormP_Exp
:members:
TracePow_Exp
------------
.. autoclass:: picos.TracePow_Exp
:members:
DetRootN_Exp
------------
.. autoclass:: picos.DetRootN_Exp
:members:
Set
---
.. autoclass:: picos.Set
:members:
Ball
----
.. autoclass:: picos.Ball
:members:
Truncated_Simplex
-----------------
.. autoclass:: picos.Truncated_Simplex
:members:
......@@ -180,7 +180,7 @@ function :func:`flow_Constraint <picos.tools.flow_Constraint>`:
F=maxflow2.add_variable('F',1)
# Enforce all flow constraints at once.
maxflow2.addConstraint(pic.flow_Constraint(
maxflow2.add_constraint(pic.flow_Constraint(
G, f, source=16, sink=10, capacity='capacity', flow_value=F, graphName='G'))
# Set the objective.
......@@ -199,7 +199,7 @@ Let us now draw the maximum flow computed with the second approach:
new_figure()
# Determine which edges carry flow.
flow_edges=[e for e in G.edges() if f[e].value[0] > 1e-4]
flow_edges=[e for e in G.edges() if f[e].value > 1e-4]
# Draw the nodes and the edges that don't carry flow.
nx.draw_networkx(G, pos, edge_color='lightgrey', node_color=node_colors,
......@@ -306,9 +306,9 @@ We solve the min-cut problem below, again for ``s=16`` and ``t=10``:
# Determine the cut edges and node sets.
# Rounding is done because solvers might return near-optimal solutions due to
# numerical precision issues.
cut=[e for e in G.edges() if abs(d[e].value[0]-1) < 1e-6]
S =[n for n in G.nodes() if abs(p[n].value[0]-1) < 1e-6]
T =[n for n in G.nodes() if abs(p[n].value[0] ) < 1e-6]
cut=[e for e in G.edges() if abs(d[e].value-1) < 1e-6]
S =[n for n in G.nodes() if abs(p[n].value-1) < 1e-6]
T =[n for n in G.nodes() if abs(p[n].value ) < 1e-6]
Note that the minimum-cut can also be derived from the dual variables of the
max-flow LP:
......@@ -461,7 +461,7 @@ We solve the multicut problem below, for the terminal pairs
multicut.solve(verbose=0,solver='glpk')
# Extract the cut.
cut=[e for e in G.edges() if y[e].value[0]==1]
cut=[e for e in G.edges() if y[e].value==1]
Let us now draw the multicut:
......@@ -614,7 +614,7 @@ Then, we perform the random projection:
while (count < 100 or obj < 0.878*obj_sdp):
r=cvx.normal(20,1)
x=cvx.matrix(np.sign(V*r))
o=(x.T*L*x).value[0]
o=(x.T*L*x).value
if o > obj:
x_cut=x
obj=o
......
This diff is collapsed.
problem.py
==========
Problem
-------
.. autoclass:: picos.Problem
:members:
tools.py
========
.. automodule:: picos.tools
:members:
This diff is collapsed.
......@@ -22,12 +22,28 @@
import os
from .problem import *
from .expression import *
from .constraint import *
from .tools import sum,lse,new_param,diag,diag_vect,geomean,norm,tracepow,trace,detrootn,QuadAsSocpError,NotAppropriateSolverError,NonConvexError,flow_Constraint,ball,simplex,truncated_simplex,partial_trace,partial_transpose,import_cbf,sum_k_largest,sum_k_largest_lambda,lambda_max,sum_k_smallest,sum_k_smallest_lambda,lambda_min, kron
# The Problem class is the user's main way of interfacing PICOS.
from .problem import Problem
new_problem = Problem
__all__=['tools','constraint','expression','problem']
# Make character set changes available.
from .glyphs import ascii, latin1, unicode, default as default_charset
# Basic tools.
from .tools import import_cbf, new_param
# Constraints and expressions that cannot be created algebraically.
from .tools import ball, expcone, flow_Constraint, simplex, truncated_simplex
# Algebraic functions. (TODO: Give them a module of their own.)
from .tools import detrootn, diag, diag_vect, exp, geomean, kron, \
kullback_leibler, lambda_max, lambda_min, log, logsumexp, lse, norm, \
partial_trace, partial_transpose, sum, sumexp, sum_k_largest, \
sum_k_largest_lambda, sum_k_smallest, sum_k_smallest_lambda, trace, tracepow
# Exceptions that the user might want to catch.
from .tools import DualizationError, NonConvexError, NotAppropriateSolverError,\
QuadAsSocpError
LOCATION = os.path.realpath(os.path.join(os.getcwd(),os.path.dirname(__file__)))
VERSION_FILE = os.path.join(LOCATION, ".version")
......
This diff is collapsed.
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file serves as a helper to include all constraint types.
#-------------------------------------------------------------------------------
# Import the constraint base classes (for the automatic API documentation).
from .constraint import Constraint, MetaConstraint
# Import the native constraints.
from .con_affine import AffineConstraint
from .con_expcone import ExpConeConstraint
from .con_lmi import LMIConstraint
from .con_quad import QuadConstraint
from .con_rsoc import RSOCConstraint
from .con_soc import SOCConstraint
# Import the derived constraints.
from .meta_absolute import AbsoluteValueConstraint
from .meta_detrootn import DetRootNConstraint
from .meta_flow import FlowConstraint
from .meta_geomean import GeoMeanConstraint
from .meta_kldiv import KullbackLeiblerConstraint
from .meta_log import LogConstraint
from .meta_lse import LSEConstraint
from .meta_pnorm import PNormConstraint
from .meta_pqnorm import PQNormConstraint
from .meta_simplex import SymTruncSimplexConstraint
from .meta_sumexp import SumExpConstraint
from .meta_sumk import SumExtremesConstraint
from .meta_tracepow import TracePowConstraint
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file implements affine constraints.
#-------------------------------------------------------------------------------
from .. import glyphs
from .constraint import Constraint
class AffineConstraint(Constraint):
"""
An equality or inequality between two affine expressions.
"""
def __init__(self, lhs, relation, rhs, customString = None):
from ..expressions import AffinExp
assert isinstance(lhs, AffinExp)
assert isinstance(rhs, AffinExp)
assert relation in self.LE + self.GE + self.EQ
if lhs.size != rhs.size:
raise ValueError("Failed to form a constraint: "
"Expressions have incompatible dimensions.")
self.lhs = lhs
self.rhs = rhs
self.relation = relation
super(AffineConstraint, self).__init__(
"Affine", customString, printSize = True)
smaller = property(
lambda self: self.rhs if self.relation is self.GE else self.lhs)
"""The smaller-or-equal side expression in case of an inequality, otherwise
the left hand side."""
greater = property(
lambda self: self.lhs if self.relation is self.GE else self.rhs)
"""The greater-or-equal side expression in case of an inequality, otherwise
the right hand side."""
le0 = property(lambda self:
self.rhs-self.lhs if self.relation is self.GE else self.lhs-self.rhs)
"""The expression posed to be less or equal than zero in case of an
inequality, otherwise the left hand side minus the right hand side."""
ge0 = property(lambda self:
self.lhs-self.rhs if self.relation is self.GE else self.rhs-self.lhs)
"""The expression posed to be greater or equal than zero in case of an
inequality, otherwise the right hand side minus the left hand side."""
def _expression_names(self):
yield "lhs"
yield "rhs"
def _str(self):
if self.relation is self.LE:
return glyphs.le(self.lhs.string, self.rhs.string)
elif self.relation is self.GE:
return glyphs.ge(self.lhs.string, self.rhs.string)
else:
return glyphs.eq(self.lhs.string, self.rhs.string)
def _get_size(self):
return self.lhs.size
def _get_slack(self):
if self.relation is self.LE:
delta = self.rhs.eval() - self.lhs.eval()
else:
delta = self.lhs.eval() - self.rhs.eval()
return -abs(delta) if self.relation is self.EQ else delta
def bounded_linear_form(self):
"""
Separates the constraint into a linear function on the left hand side
and a constant bound on the right hand side.
:returns: A pair ``(linear, bound)`` where ``linear`` is a pure linear
expression and ``bound`` is a constant expression.
"""
from ..expressions import AffinExp
linear = self.lhs - self.rhs
if linear.constant:
bound = AffinExp(size = linear.size, constant = -linear.constant)
else:
bound = AffinExp(size = linear.size) + 0
linear += bound
return (linear, bound)
def sparse_Ab_rows(self, varOffsetMap, indexFunction = None):
"""
A sparse list representation of the constraint, given a mapping of PICOS
variables to column offsets (or alternatively given an index function).
The constraint is brought into a bounded linear form A • b, where • is
one of ≤, ≥, or =, depending on the constraint relation, and the rows
returned correspond to the matrix [A|b].
:param dict varOffsetMap: Maps variables or variable start indices to
column offsets.
:param indexFunction: Instead of adding the local variable index to the
value returned by varOffsetMap, use the return value of this
function, that takes as argument the variable and its local index,
as the "column index", which need not be an integer. When this
parameter is passed, the parameter varOffsetMap is ignored.
:returns: A list of triples (J, V, c) where J contains column indices
(representing scalar variables), V contains coefficients for each
column index and c is a constant term. Each entry of the list
represents a row in a constraint matrix.
"""
lhs = self.lhs - self.rhs
rows = lhs.sparse_rows(varOffsetMap, indexFunction = indexFunction)
for localConIndex in range(len(lhs)):
rows[localConIndex][2] = -rows[localConIndex][2]
return rows
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file implements exponential cone constraints.
#-------------------------------------------------------------------------------
import numpy as np
from .. import glyphs
from .constraint import Constraint
class ExpConeConstraint(Constraint):
"""
An exponential cone constraint stating that :math:`(x, y, z)` fulfills
:math:`x \\geq y e^{\\frac{z}{y}} \\land x > 0 \\land y > 0`.
"""
def __init__(self, element, customString = None):
from ..expressions import AffinExp
assert isinstance(element, AffinExp)
assert len(element) == 3
self.element = element
super(ExpConeConstraint, self).__init__(
"Exponential Cone", customString, printSize = False)
x = property(lambda self: self.element[0])
y = property(lambda self: self.element[1])
z = property(lambda self: self.element[2])
def _expression_names(self):
yield "element"
def _str(self):
return glyphs.ge(self.element[0].string,
glyphs.mul(self.element[1].string, glyphs.exp(glyphs.div(
self.element[2].string, self.element[1].string))))
def _get_size(self):
return (3, 1)
def _get_slack(self):
x = self.element.value
# TODO: Also consider x_1 > 0 and x_2 > 0 by projecting x on the cone's
# surface and measuring the distance between the projection and x.
return x[0] - x[1] * np.exp(x[2] / x[1])
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file implements linear matrix inequality (SDP) constraints.
#-------------------------------------------------------------------------------
from ..tools import svecm1_identity_factor
from .. import glyphs
from .constraint import Constraint
class LMIConstraint(Constraint):
"""
An inequality with respect to the positive semidefinite cone, also known as
a Linear Matrix Inequality (LMI) or an SDP constraint.
"""
def __init__(self, lhs, relation, rhs, customString = None):
from ..expressions import AffinExp
assert isinstance(lhs, AffinExp)
assert isinstance(rhs, AffinExp)
assert relation in self.LE + self.GE
if lhs.size != rhs.size:
raise ValueError("Failed to form a constraint: "
"Expressions have incompatible dimensions.")
if lhs.size[0] != lhs.size[1]:
raise ValueError("Failed to form a constraint: "
"LMI expressions are not square.")
self.lhs = lhs
self.rhs = rhs
self.relation = relation
# Check if the constraint simply poses positive semidefiniteness on a
# matrix variable, as certain solvers can handle this more effeciently
# than a general linear matrix inequality.
self.semidefVar = None
psd = self.psd
if len(psd.factors) == 1 and not psd.constant:
variable = list(psd.factors.keys())[0]
if svecm1_identity_factor(psd.factors, variable):
if variable.vtype == "continuous":
raise ValueError(
"A matrix variable that has not been declared as "
"symmetric is constrained to be positive semidefinite.")
elif variable.vtype == "complex":
raise ValueError(
"A complex matrix variable that has not been declared "
"hermitian is constrained to be positive semidefinite.")
elif variable.vtype in ("symmetric", "hermitian"):
self.semidefVar = variable
super(LMIConstraint, self).__init__(
"LMI", customString, printSize = True)
smaller = property(
lambda self: self.rhs if self.relation is self.GE else self.lhs)
"""The smaller-or-equal side expression."""
greater = property(
lambda self: self.lhs if self.relation is self.GE else self.rhs)
"""The greater-or-equal side expression."""
psd = property(lambda self:
self.lhs-self.rhs if self.relation is self.GE else self.rhs-self.lhs)
"""The matrix expression posed to be positive semidefinite."""
nsd = property(lambda self:
self.rhs-self.lhs if self.relation is self.GE else self.lhs-self.rhs)
"""The matrix expression posed to be negative semidefinite."""
nnd = psd
"""The matrix expression posed to be nonnegative definite."""
npd = nsd
"""The matrix expression posed to be nonpositive definite."""
def _expression_names(self):
yield "lhs"
yield "rhs"
def _variable_names(self):
if self.semidefVar:
yield "semidefVar"
def _str(self):
if self.relation is self.LE:
return glyphs.psdle(self.lhs.string, self.rhs.string)
else:
return glyphs.psdge(self.lhs.string, self.rhs.string)
def _get_size(self):
return self.lhs.size
def _get_slack(self):
if self.relation == self.LE:
return self.rhs.eval() - self.lhs.eval()
else:
return self.lhs.eval() - self.rhs.eval()
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file implements quadratic constraints.
#-------------------------------------------------------------------------------
from .. import glyphs
from .constraint import Constraint
class QuadConstraint(Constraint):
"""
An upper bound on a scalar quadratic expression.
"""
def __init__(self, lowerEqualZero, customString = None):
from ..expressions import QuadExp
assert isinstance(lowerEqualZero, QuadExp)
self.le0 = lowerEqualZero
super(QuadConstraint, self).__init__(
"Quadratic", customString, printSize = False)
def _expression_names(self):
yield "le0"
def _str(self):
return glyphs.le(self.le0.string, 0)
def _get_size(self):
return (1, 1)
def _get_slack(self):
return -self.le0.eval()[0]
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file implements rotated second order cone (RSOC) constraints.
#-------------------------------------------------------------------------------
import cvxopt
from .. import glyphs
from .constraint import Constraint
class RSOCConstraint(Constraint):
"""
A rotated second order cone constraint.
"""
def __init__(self, normedExpression, upperBoundFactor1,
upperBoundFactor2 = None, customString = None):
from ..expressions import AffinExp
if upperBoundFactor2 is None:
upperBoundFactor2 = AffinExp() + 1
assert isinstance(normedExpression, AffinExp)
assert isinstance(upperBoundFactor1, AffinExp)
assert isinstance(upperBoundFactor2, AffinExp)
assert len(upperBoundFactor1) == 1
assert len(upperBoundFactor2) == 1
self.ne = normedExpression
self.ub1 = upperBoundFactor1
self.ub2 = upperBoundFactor2
super(RSOCConstraint, self).__init__(
"RSOC", customString, printSize = True)
def _expression_names(self):
yield "ne"
yield "ub1"
yield "ub2"
def _str(self):
if self.ub2.is1():
return glyphs.le(glyphs.squared(glyphs.norm(self.ne.string)),
self.ub1.string)
else:
return glyphs.le(glyphs.squared(glyphs.norm(self.ne.string)),
glyphs.mul(self.ub1.string, self.ub2.string))
def _get_size(self):
return (len(self.ne) + 2, 1)
def _get_slack(self):
return (self.ub1.eval() * self.ub2.eval() - (abs(self.ne)**2).eval())[0]
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file implements second order cone (SOC) constraints.
#-------------------------------------------------------------------------------
import cvxopt
from .. import glyphs
from .constraint import Constraint
class SOCConstraint(Constraint):
"""
A second order cone (2-norm cone, Lorentz cone) constraint.
"""
def __init__(self, normedExpression, upperBound, customString = None):
from ..expressions import AffinExp
assert isinstance(normedExpression, AffinExp)
assert isinstance(upperBound, AffinExp)
assert len(normedExpression) > 1, \
"Tried to form an SOC constraint for an affine inequality."
assert len(upperBound) == 1
self.ne = normedExpression
self.ub = upperBound
super(SOCConstraint, self).__init__(
"SOC", customString, printSize = True)
def _expression_names(self):
yield "ne"
yield "ub"
def _str(self):
return glyphs.le(glyphs.norm(self.ne.string), self.ub.string)
def _get_size(self):
return (len(self.ne) + 1, 1)
def _get_slack(self):
return (self.ub.eval() - abs(self.ne).eval())[0]
This diff is collapsed.
# coding: utf-8
#-------------------------------------------------------------------------------
# Copyright (C) 2018 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# This file implements an absolute value constraint.
#-------------------------------------------------------------------------------
from .. import glyphs
from .constraint import MetaConstraint
class AbsoluteValueConstraint(MetaConstraint):
def __init__(self, signedScalar, upperBound):
from ..problem import Problem
from ..expressions import AffinExp
assert isinstance(signedScalar, AffinExp)
assert isinstance(upperBound, AffinExp)
assert len(signedScalar) == 1
assert len(upperBound) == 1