picos.expressions.exp_biaffine

Implements BiaffineExpression.

Outline

Classes

BiaffineExpression

A multidimensional (complex) biaffine expression.

Classes

BiaffineExpression

class picos.expressions.exp_biaffine.BiaffineExpression(string, shape=(1, 1), coefficients={})[source]

Bases: picos.expressions.expression.Expression, abc.ABC

A multidimensional (complex) biaffine expression.

Abstract base class for the affine ComplexAffineExpression and its real subclass AffineExpression. Quadratic expressions are stored in QuadraticExpression instead.

In general this expression has the form

A(x,y) = B(x,y) + P(x) + Q(y) + C

where x \in \mathbb{R}^p and y \in \mathbb{R}^q are variable vectors, B : \mathbb{R}^p \times \mathbb{R}^q \to \mathbb{C}^{m \times n} is a bilinear function, P : \mathbb{R}^p \to \mathbb{C}^{m \times n} and Q : \mathbb{R}^q \to \mathbb{C}^{m \times n} are linear functions, and C \in \mathbb{C}^{m \times n} is a constant.

If no coefficient matrices defining B and Q are provided on subclass instanciation, then this acts as an affine function of x.

In a more technical sense, the notational variables x and y each represent a stack of vectorizations of a number of actual scalar, vector, or matrix variables or parameters X_i and Y_j with i, j \in \mathbb{Z}_{\geq 0} and this class stores the nonzero (bi)linear functions B_{i,j}(\operatorname{vec}(X_i),\operatorname{vec}(Y_j)), P_{i}(\operatorname{vec}(X_i)) and Q_{j}(\operatorname{vec}(Y_j)) in the form of separate sparse coefficient matrices.

Htranspose()[source]

Return the conjugate (or Hermitian) transpose.

Deprecated since version 2.0: Use H instead.

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

Initialize a (complex) biaffine expression.

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

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

  • coefficients (dict) – Maps mutable pairs, single mutables or the empty tuple to sparse represetations of the coefficient matrices B, P and Q, and C, respectively.

Warning

If the given coefficients 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 biaffine 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 (bi)affine expression.

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

If the operand is not already a PICOS expression, an attempt is made to load it as a constant affine expression. In this case, no reshaping or broadcasting is used to bring the constant term 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
factor_out(mutable)[source]

Factor out a single mutable from a vector expression.

If this expression is a column vector a that depends on some mutable x with a trivial internal vectorization format (i.e. FullVectorization), then this method, called with x as its argument, returns a pair of expressions (a_x, a_0) such that a = a_x\operatorname{vec}(x) + a_0.

Returns

Two refined BiaffineExpression instances that do not depend on mutable.

Raises
  • TypeError – If the expression is not a column vector or if mutable is not a Mutable or does not have a trivial vectorization format.

  • LookupError – If the expression does not depend on mutable.

Example

>>> from picos import RealVariable
>>> from picos.uncertain import UnitBallPerturbationSet
>>> x = RealVariable("x", 3)
>>> z = UnitBallPerturbationSet("z", 3).parameter
>>> a = ((2*x + 3)^z) + 4*x + 5; a
<3×1 Uncertain Affine Expression: (2·x + [3])⊙z + 4·x + [5]>
>>> sorted(a.mutables, key=lambda mtb: mtb.name)
[<3×1 Real Variable: x>, <3×1 Perturbation: z>]
>>> az, a0 = a.factor_out(z)
>>> az
<3×3 Real Affine Expression: ((2·x + [3])⊙z + 4·x + [5])_z>
>>> a0
<3×1 Real Affine Expression: ((2·x + [3])⊙z + 4·x + [5])_0>
>>> sorted(az.mutables.union(a0.mutables), key=lambda mtb: mtb.name)
[<3×1 Real Variable: x>]
>>> (az*z + a0).equals(a)
True
classmethod fromMatrix(matrix, size=None)[source]

Create a class instance from a numeric matrix.

Deprecated since version 2.0: Use from_constant instead.

classmethod fromScalar(scalar)[source]

Create a class instance 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 mutables.

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.

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 bilin

Pure bilinear part of the expression.

property complex

Whether the expression can be complex-valued.

property conj

Complex conjugate.

property cst

Constant part of the expression.

property desvec

The reverse operation of svec.

Raises
  • TypeError

    If the expression is not a vector or has a dimension outside the permissible set

    \left\{ \frac{n(n + 1)}{2}
    \mid n \in \mathbb{Z}_{\geq 1} \right\}
 = \left\{ n \in \mathbb{Z}_{\geq 1}
    \mid \frac{1}{2} \left( \sqrt{8n + 1} - 1 \right)
        \in \mathbb{Z}_{\geq 1} \right\}.

  • ValueError – In the case of a complex vector, If the vector is not in the image of svec.

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.

Uses RELATIVE_HERMITIANNESS_TOLERANCE.

If PICOS rejects your near-hermitian (near-symmetric) expression as not hermitian (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 a projection onto the subspace of symmetric matrices.

property imag

Imaginary part of the 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 expression.

property maindiag

The main diagonal of the expression as a column vector.

property noncst

Nonconstant part of the expression.

property real

Real part of the expression.

property sum

Sum over all scalar elements of the expression.

property svec

An isometric vectorization of a symmetric or hermitian expression.

In the real symmetric case

  • the vectorization format is precisely the one define in [svec],

  • the vectorization is isometric and isomorphic, and

  • this is the same vectorization as used internally by the SymmetricVariable class.

In the complex hermitian case

  • the same format is used, now resulting in a complex vector,

  • the vectorization is isometric but not isomorphic as there are guaranteed zeros in the imaginary part of the vector, and

  • this is not the same vectorization as the isomorphic, real-valued one used by HermitianVariable.

The reverse operation is denoted by desvec in either case.

Raises
  • TypeError – If the expression is not square.

  • ValueError – If the expression is not hermitian.

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