picos.expressions.exp_affine

Implements affine expression types.

Outline

Classes

AffineExpression

A multidimensional real affine expression.

ComplexAffineExpression

A multidimensional complex affine expression.

Functions

Constant

Create a constant PICOS expression.

Classes

AffineExpression

class picos.expressions.exp_affine.AffineExpression(string, shape=1, 1, coefficients={}, constant=0j)[source]

Bases: picos.expressions.exp_affine.ComplexAffineExpression

A multidimensional real affine expression.

property H

The regular transpose of the AffineExpression.

property conj

The AffineExpression as is.

property exp

The exponential function applied to the expression.

property imag

A zero of same shape as the AffineExpression.

property isreal

Always true for AffineExpression instances.

property log

The Logarithm of the expression.

property real

The AffineExpression as is.

ComplexAffineExpression

class picos.expressions.exp_affine.ComplexAffineExpression(string, shape=1, 1, coefficients={}, constant=0j)[source]

Bases: picos.expressions.expression.Expression

A multidimensional complex affine expression.

Base class for the real AffineExpression.

Htranspose()[source]

Return the conjugate (or Hermitian) transpose.

Deprecated since version 2.0: Use H instead.

__init__(string, shape=1, 1, coefficients={}, constant=0j)[source]

Initialize a (complex) affine expression.

This constructor is meant for internal use. As a user, you will most likely want to build expressions starting from variables or a Constant.

Parameters
  • string (str) – A symbolic string description.

  • shape (int or tuple or list) – Shape of a vector or matrix expression.

  • coefficients (dict) – The linear part of the affine expression; maps the vectorization of variables to their coefficients.

  • constant – The constant part of the affine expression.

Warning

If the given coefficients and constant are already of the desired (numeric) type and shape, they are stored by reference. Modifying such data can lead to unexpected results as PICOS expressions are supposed to be immutable (to allow caching of results).

If you create affine expressions by any other means than this constructor, PICOS makes a copy of your data to prevent future modifications to it from causing inconsistencies.

broadcasted(shape)[source]

Return the expression broadcasted to the given shape.

Example

>>> from picos import Constant
>>> C = Constant("C", range(6), (2, 3))
>>> print(C)
[ 0.00e+00  2.00e+00  4.00e+00]
[ 1.00e+00  3.00e+00  5.00e+00]
>>> print(C.broadcasted((6, 6)))
[ 0.00e+00  2.00e+00  4.00e+00  0.00e+00  2.00e+00  4.00e+00]
[ 1.00e+00  3.00e+00  5.00e+00  1.00e+00  3.00e+00  5.00e+00]
[ 0.00e+00  2.00e+00  4.00e+00  0.00e+00  2.00e+00  4.00e+00]
[ 1.00e+00  3.00e+00  5.00e+00  1.00e+00  3.00e+00  5.00e+00]
[ 0.00e+00  2.00e+00  4.00e+00  0.00e+00  2.00e+00  4.00e+00]
[ 1.00e+00  3.00e+00  5.00e+00  1.00e+00  3.00e+00  5.00e+00]
conjugate()[source]

Return the complex conjugate.

Deprecated since version 2.0: Use conj instead.

copy()[source]

Return a deep copy of the expression.

Deprecated since version 2.0: PICOS expressions are now immutable.

dupdiag(n)[source]

Return a matrix with the (repeated) expression on the diagonal.

Vectorization is performed in column-major order.

Parameters

n (int) – Number of times to duplicate the vectorization.

dupvec(n)[source]

Return a (repeated) column-major vectorization of the expression.

Parameters

n (int) – Number of times to duplicate the vectorization.

Returns

A column vector.

Example

>>> from picos import Constant
>>> A = Constant("A", [[1, 2], [3, 4]])
>>> A.dupvec(1) is A.vec
True
>>> A.dupvec(3).equals(A.vec // A.vec // A.vec)
True
equals(other, absTol=None, relTol=None)[source]

Check mathematical equality with another affine expression.

Checks whether the affine expression involves the same variables with the same coefficients and an equal constant term as the given PICOS (affine) expression or constant term.

The type of both expressions may differ. In particular, a ComplexAffineExpression with real coefficients and constant can be equal to an AffineExpression.

If the argument is a constant term, no reshaping or broadcasting is used to bring it to the same shape as this expression. In particular,

  • 0 refers to a scalar zero (see also is0),

  • lists and tuples are treated as column vectors and

  • algebraic strings must specify a shape (see load_data).

Parameters
  • other – Another PICOS expression or a constant numeric data value supported by load_data.

  • absTol – As long as all absolute differences between scalar entries of the coefficient matrices and the constant terms being compared does not exceed this bound, consider the expressions equal.

  • relTol – As long as all absolute differences between scalar entries of the coefficient matrices and the constant terms being compared divided by the maximum absolute value found in either term does not exceed this bound, consider the expressions equal.

Example

>>> from picos import Constant
>>> A = Constant("A", 0, (5,5))
>>> repr(A)
'<5×5 Real Constant: A>'
>>> A.is0
True
>>> A.equals(0)
False
>>> A.equals("|0|(5,5)")
True
>>> repr(A*1j)
'<5×5 Complex Constant: A·1j>'
>>> A.equals(A*1j)
True
classmethod fromMatrix(matrix, size=None)[source]

Create a ComplexAffineExpression from a numeric matrix.

Deprecated since version 2.0: Use from_constant instead.

classmethod fromScalar(scalar)[source]

Create a ComplexAffineExpression from a numeric scalar.

Deprecated since version 2.0: Use from_constant instead.

classmethod from_constant(constant, shape=None, name=None)[source]

Create a class instance from the given numeric constant.

Loads the given constant as a PICOS expression, optionally broadcasted or reshaped to the given shape and named as specified.

See load_data for supported data formats and broadcasting and reshaping rules.

Unlike Constant, this class method always creates an instance of the class that it is called on, instead of tailoring towards the numeric type of the data.

Note

When an operation involves both a PICOS expression and a constant value of another type, PICOS converts the constant on the fly so that you rarely need to use this method.

hadamard(fact)[source]

Denote the elementwise (or Hadamard) product.

Deprecated since version 2.0: Use object.__xor__ instead.

isconstant()[source]

Whether the expression involves no variables.

Deprecated since version 2.0: Use constant instead.

kron(other)[source]

Denote the Kronecker product from the right hand side.

Python 3 users can use the infix @ operator instead.

leftkron(other)[source]

Denote the Kronecker product from the left hand side.

Python 3 users can use the infix @ operator instead.

partial_trace(subsystems, dimensions=2)[source]

Return the partial trace over selected subsystems.

If the expression can be written as A_0 \otimes \cdots \otimes A_{n-1} for matrices A_0, \ldots, A_{n-1} with shapes given in dimensions, then this returns B_0 \otimes \cdots \otimes B_{n-1} with B_i = \operatorname{tr}(A_i), if i in subsystems (with i = -1 read as n-1), and B_i = A_i, otherwise.

Parameters
  • subsystems (int or tuple or list) – A collection of or a single subystem number, indexed from zero, corresponding to subsystems that shall be traced over. The value -1 refers to the last subsystem.

  • dimensions (int or tuple or list) – Either an integer d so that the subsystems are assumed to be all of shape d \times d, or a sequence of subsystem shapes where an integer d within the sequence is read as d \times d. In any case, the elementwise product over all subsystem shapes must equal the expression’s shape.

Raises
  • TypeError – If the subsystems do not match the expression or if a non-square subsystem is to be traced over.

  • IndexError – If the subsystem selection is invalid in any other way.

Example

>>> from picos import Constant
>>> A = Constant("A", range(16), (4, 4))
>>> print(A) 
[ 0.00e+00  4.00e+00  8.00e+00  1.20e+01]
[ 1.00e+00  5.00e+00  9.00e+00  1.30e+01]
[ 2.00e+00  6.00e+00  1.00e+01  1.40e+01]
[ 3.00e+00  7.00e+00  1.10e+01  1.50e+01]
>>> A0 = A.partial_trace(0); A0
<2×2 Real Constant: A.{tr([2×2])⊗[2×2]}>
>>> print(A0) 
[ 1.00e+01  1.80e+01]
[ 1.20e+01  2.00e+01]
>>> A1 = A.partial_trace(1); A1
<2×2 Real Constant: A.{[2×2]⊗tr([2×2])}>
>>> print(A1) 
[ 5.00e+00  2.10e+01]
[ 9.00e+00  2.50e+01]
partial_transpose(subsystems, dimensions=2)[source]

Return the expression with selected subsystems transposed.

If the expression can be written as A_0 \otimes \cdots \otimes A_{n-1} for matrices A_0, \ldots, A_{n-1} with shapes given in dimensions, then this returns B_0 \otimes \cdots \otimes B_{n-1} with B_i = A_i^T, if i in subsystems (with i = -1 read as n-1), and B_i = A_i, otherwise.

Parameters
  • subsystems (int or tuple or list) – A collection of or a single subystem number, indexed from zero, corresponding to subsystems that shall be transposed. The value -1 refers to the last subsystem.

  • dimensions (int or tuple or list) – Either an integer d so that the subsystems are assumed to be all of shape d \times d, or a sequence of subsystem shapes where an integer d within the sequence is read as d \times d. In any case, the elementwise product over all subsystem shapes must equal the expression’s shape.

Raises
  • TypeError – If the subsystems do not match the expression.

  • IndexError – If the subsystem selection is invalid.

Example

>>> from picos import Constant
>>> A = Constant("A", range(16), (4, 4))
>>> print(A) 
[ 0.00e+00  4.00e+00  8.00e+00  1.20e+01]
[ 1.00e+00  5.00e+00  9.00e+00  1.30e+01]
[ 2.00e+00  6.00e+00  1.00e+01  1.40e+01]
[ 3.00e+00  7.00e+00  1.10e+01  1.50e+01]
>>> A0 = A.partial_transpose(0); A0
<4×4 Real Constant: A.{[2×2]ᵀ⊗[2×2]}>
>>> print(A0) 
[ 0.00e+00  4.00e+00  2.00e+00  6.00e+00]
[ 1.00e+00  5.00e+00  3.00e+00  7.00e+00]
[ 8.00e+00  1.20e+01  1.00e+01  1.40e+01]
[ 9.00e+00  1.30e+01  1.10e+01  1.50e+01]
>>> A1 = A.partial_transpose(1); A1
<4×4 Real Constant: A.{[2×2]⊗[2×2]ᵀ}>
>>> print(A1) 
[ 0.00e+00  1.00e+00  8.00e+00  9.00e+00]
[ 4.00e+00  5.00e+00  1.20e+01  1.30e+01]
[ 2.00e+00  3.00e+00  1.00e+01  1.10e+01]
[ 6.00e+00  7.00e+00  1.40e+01  1.50e+01]
renamed(string)[source]

Return the expression with a modified string description.

reshaped(shape)[source]

Return the expression reshaped in column-major order.

Example

>>> from picos import Constant
>>> C = Constant("C", range(6), (2, 3))
>>> print(C)
[ 0.00e+00  2.00e+00  4.00e+00]
[ 1.00e+00  3.00e+00  5.00e+00]
>>> print(C.reshaped((3, 2)))
[ 0.00e+00  3.00e+00]
[ 1.00e+00  4.00e+00]
[ 2.00e+00  5.00e+00]
reshaped_or_broadcasted(shape)[source]

Return the expression reshaped or broadcasted.

Unlike with reshaped and broadcasted, the target shape may not contain a wildcard character.

If the wildcard-free target shape has the same number of elements as the current shape, then this is the same as reshaped, otherwise it is the same as broadcasted.

reshuffled(permutation='ikjl', dimensions=None, order='C')[source]

Return the reshuffled or realigned expression.

This operation works directly on matrices. However, it is equivalent to the following sequence of operations:

  1. The matrix is reshaped to a tensor with the given dimensions and according to order.

  2. The tensor’s axes are permuted according to permutation.

  3. The tensor is reshaped back to the shape of the original matrix according to order.

For comparison, the following function applies the same operation to a 2D NumPy ndarray:

def reshuffle_numpy(matrix, permutation, dimensions, order):
    P = "{} -> {}".format("".join(sorted(permutation)), permutation)
    reshuffled = numpy.reshape(matrix, dimensions, order)
    reshuffled = numpy.einsum(P, reshuffled)
    return numpy.reshape(reshuffled, matrix.shape, order)
Parameters
  • permutation (str or tuple or list) – A sequence of comparable elements with length equal to the number of tensor dimensions. The sequence is compared to its ordered version and the resulting permutation pattern is used to permute the tensor indices. For instance, the string "ikjl" is compared to its sorted version "ijkl" and denotes that the second and third axis should be swapped.

  • dimensions (None or tuple or list) – If this is an integer sequence, then it defines the dimensions of the tensor. If this is None, then the tensor is assumed to be hypercubic and the number of dimensions is inferred from the permutation argument.

  • order (str) – The indexing order to use for the virtual reshaping. Must be either "F" for Fortran-order (generalization of column-major) or "C" for C-order (generalization of row-major). Note that PICOS usually reshapes in Fortran-order while NumPy defaults to C-order.

Example

>>> from picos import Constant
>>> A = Constant("A", range(16), (4, 4))
>>> print(A) 
[ 0.00e+00  4.00e+00  8.00e+00  1.20e+01]
[ 1.00e+00  5.00e+00  9.00e+00  1.30e+01]
[ 2.00e+00  6.00e+00  1.00e+01  1.40e+01]
[ 3.00e+00  7.00e+00  1.10e+01  1.50e+01]
>>> R = A.reshuffled(); R
<4×4 Real Constant: shuffled(A,ikjl,C)>
>>> print(R) 
[ 0.00e+00  4.00e+00  1.00e+00  5.00e+00]
[ 8.00e+00  1.20e+01  9.00e+00  1.30e+01]
[ 2.00e+00  6.00e+00  3.00e+00  7.00e+00]
[ 1.00e+01  1.40e+01  1.10e+01  1.50e+01]
>>> A.reshuffled("ji").equals(A.T)     # Regular transposition.
True
>>> A.reshuffled("3214").equals(A.T0)  # Partial transposition (1).
True
>>> A.reshuffled("1432").equals(A.T1)  # Partial transposition (2).
True
same_as(other)[source]

Check mathematical equality with another affine expression.

Deprecated since version 2.0: Use equals instead.

soft_copy()[source]

Return a shallow copy of the expression.

Deprecated since version 2.0: PICOS expressions are now immutable.

sparse_rows(varOffsetMap, lowerTriangle=False, upperTriangle=False, indexFunction=None)[source]

Return a sparse list representation of the expression.

The method is intended for internal use: It simplifies passing affine constraints to solvers that support only scalar constraints. The idea is to pose the constraint as a single (multidimensional) affine expression bounded by zero, and use the coefficients and the constant term of this expression to fill the solver’s constraint matrix (with columns representing scalar variables and rows representing scalar constraints).

Parameters
  • varOffsetMap (dict) – Maps variables to column offsets.

  • lowerTriangle (bool) – Whether to return only the lower triangular part of the expression.

  • upperTriangle (bool) – Whether to return only the upper triangular part of the expression.

  • 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.

transpose()[source]

Return the matrix transpose.

Deprecated since version 2.0: Use T instead.

classmethod zero(shape=1, 1)[source]

Return a constant zero expression of given shape.

property H

Conjugate (or Hermitian) transpose.

property T

Matrix transpose.

property T0

Expression with the first 2 \times 2 subsystem transposed.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_transpose otherwise.

property T1

Expression with the second 2 \times 2 subsystem transposed.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_transpose otherwise.

property T2

Expression with the third 2 \times 2 subsystem transposed.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_transpose otherwise.

property T3

Expression with the fourth 2 \times 2 subsystem transposed.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_transpose otherwise.

property Tl

Expression with the last 2 \times 2 subsystem transposed.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_transpose otherwise.

property Tx

Auto-detect few subsystems of same shape and transpose the last.

Deprecated since version 2.0: Use partial_transpose instead.

property complex

Whether the expression can be complex-valued.

property conj

Complex conjugate.

property constant

Whether the expression involves no variables.

property cst

Constant part of the affine expression.

property diag

Diagonal matrix with the expression on the main diagonal.

Vectorization is performed in column-major order.

property hermitian

Whether the expression is a hermitian (or symmetric) matrix.

A tolerance is used to compensate for small numeric errors that can occur when creating hermtian expressions by means of matrix multiplication, see RELATIVE_HERMITIANNESS_TOLERANCE.

If PICOS still rejects your expression as not hermitian (or as not symmetric), you can use hermitianized to correct larger numeric errors or the effects of noisy data.

property hermitianized

The expression projected onto the subspace of hermitian matrices.

For a square (complex) affine expression A, this is \frac{1}{2}(A + A^H).

If the expression is not complex, then this is the projection onto the subspace of symmetric matrices.

property imag

Imaginary part of a complex affine expression.

property is0

Whether this is a constant scalar, vector or matrix of all zeros.

property is1

Whether this is a constant scalar or vector of all ones.

property isI

Whether this is a constant identity matrix.

property isreal

Whether the expression is always real-valued.

property lin

Linear part of the affine expression.

property maindiag

The main diagonal of the expression as a column vector.

property real

Real part of a complex affine expression.

property scalar

Whether this is a scalar affine expression.

property square

Whether the expression is a square matrix.

property sum

Sum over all scalar elements of the expression.

property tr

Trace of a square expression.

property tr0

Expression with the first 2 \times 2 subsystem traced out.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_trace otherwise.

property tr1

Expression with the second 2 \times 2 subsystem traced out.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_trace otherwise.

property tr2

Expression with the third 2 \times 2 subsystem traced out.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_trace otherwise.

property tr3

Expression with the fourth 2 \times 2 subsystem traced out.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_trace otherwise.

property trilvec

Column-major vectorization of the lower triangular part.

Returns

A column vector of all elements A_{ij} that satisfy i \geq j.

Note

If you want a row-major vectorization instead, write A.T.triuvec instead of A.trilvec.

Example

>>> from picos import Constant
>>> A = Constant("A", [[1, 2], [3, 4], [5, 6]])
>>> print(A)
[ 1.00e+00  2.00e+00]
[ 3.00e+00  4.00e+00]
[ 5.00e+00  6.00e+00]
>>> print(A.trilvec)
[ 1.00e+00]
[ 3.00e+00]
[ 5.00e+00]
[ 4.00e+00]
[ 6.00e+00]
property triuvec

Column-major vectorization of the upper triangular part.

Returns

A column vector of all elements A_{ij} that satisfy i \leq j.

Note

If you want a row-major vectorization instead, write A.T.trilvec instead of A.triuvec.

Example

>>> from picos import Constant
>>> A = Constant("A", [[1, 2, 3], [4, 5, 6]])
>>> print(A)
[ 1.00e+00  2.00e+00  3.00e+00]
[ 4.00e+00  5.00e+00  6.00e+00]
>>> print(A.triuvec)
[ 1.00e+00]
[ 2.00e+00]
[ 5.00e+00]
[ 3.00e+00]
[ 6.00e+00]
property trl

Expression with the last 2 \times 2 subsystem traced out.

Only available for a 2^k \times 2^k matrix with all subsystems of shape 2 \times 2. Use partial_trace otherwise.

property vec

Column-major vectorization of the expression as a column vector.

Note

Given an expression A, A.vec and A[:] produce the same result (up to its string description) but A.vec is faster and its result is cached.

Example

>>> from picos import Constant
>>> A = Constant("A", [[1, 2], [3, 4]])
>>> A.vec.equals(A[:])
True
>>> A[:] is A[:]
False
>>> A.vec is A.vec
True

Functions

Constant

picos.expressions.exp_affine.Constant(name_or_value, value=None, shape=None)[source]

Create a constant PICOS expression.

Loads the given numeric value as a constant ComplexAffineExpression or AffineExpression, depending on the value. Optionally, the value is broadcasted or reshaped according to the shape argument.

Parameters
  • name_or_value (str) – Symbolic string description of the constant. If None or the empty string, a string will be generated. If this is the only positional parameter (i.e.``value`` is not given), then this position is used as the value argument instead!

  • value – The numeric constant to load.

See load_data for supported data formats and broadcasting and reshaping rules.

Example

>>> from picos import Constant
>>> Constant(1)
<1×1 Real Constant: 1>
>>> Constant(1, shape=(2, 2))
<2×2 Real Constant: [1]>
>>> Constant("one", 1)
<1×1 Real Constant: one>
>>> Constant("J", 1, (2, 2))
<2×2 Real Constant: J>

Data

picos.expressions.exp_affine.RELATIVE_HERMITIANNESS_TOLERANCE

Relative tolerance when checking if an expression is hermitian.

A square (complex) affine expression A is considered hermitian if

\max_{1 \leq i, j \leq n} |(A - A^H)_{ij}|
\leq
\varepsilon \max_{1 \leq i, j \leq n} |A_{ij}|

where \varepsilon is this tolerance.

Used by ComplexAffineExpression.hermitian.