NQLib documentation

NQLib

NQLib is a Python library to design noise shaping quantizer for discrete-valued input control.

## What can I do with NQLib?

In the real world, a dynamic system may have to be controlled by discrete-valued signals due to the inclusion of actuators that are driven by ON/OFF or network with capacity limitations. In such a case, a good output may be obtained by converting continuous-valued input to discrete-valued input with a quantizer designed by NQLib.

## Documentation

All the documentation is available at https://knttnk.github.io/NQLib/.

Examples of usage are available at https://colab.research.google.com/drive/1Ui-XqaTZCjwqRXC3ZeMeMCbPqGxK9YXO.

## References

NQLib is a Python library version of ODQ Toolbox, which were developed in MATLAB.

The algorithms used in NQLib are based on the following paper.

  • [1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53, pp. 2064–2075 (2008)

  • [2] S. Azuma, Y. Minami and T. Sugie: Optimal dynamic quantizers for feedback control with discrete-level actuators; Journal of Dynamic Systems, Measurement, and Control, Vol. 133, No. 2, 021005 (2011)

  • [3] 南,加嶋:システムの直列分解に基づく動的量子化器設計;計測自動制御学会論文集,Vol. 52, pp. 46–51(2016)

  • [4] R. Morita, S. Azuma, Y. Minami and T. Sugie: Graphical design software for dynamic quantizers in control systems; SICE Journal of Control, Measurement, and System Integration, Vol. 4, No. 5, pp. 372-379 (2011)

  • [5] Y. Minami and T. Muromaki: Differential evolution-based synthesis of dynamic quantizers with fixed-structures; International Journal of Computational Intelligence and Applications, Vol. 15, No. 2, 1650008 (2016)

## License

This software is released under the MIT License, see LICENSE.txt.

class nqlib.Controller(A, B1, B2, C, D1, D2)

Bases: object

State-space model of a controller K.

The controller K is given by:

K{ x(t+1) = A x(t) + B1 r(t) + B2 y(t)

{ u(t) = C x(t) + D1 r(t) + D2 y(t)

References

[5] Y. Minami and T. Muromaki: Differential evolution-based synthesis of dynamic quantizers with fixed-structures; International Journal of Computational Intelligence and Applications, Vol. 15, No. 2, 1650008 (2016)

Example

>>> import nqlib
>>> K = nqlib.Controller(A=0,
...                      B1=0,
...                      B2=[0, 0],
...                      C=0,
...                      D1=1,
...                      D2=[-20, -3])
>>> K.A
array([[0]])
__init__(A, B1, B2, C, D1, D2)

Initialize a Controller instance.

Parameters:
  • A (array_like) – State matrix (n x n), real.

  • B1 (array_like) – Input matrix for r (n x l), real.

  • B2 (array_like) – Input matrix for y (n x p), real.

  • C (array_like) – Output matrix (m x n), real.

  • D1 (array_like) – Feedthrough matrix for r (m x l), real.

  • D2 (array_like) – Feedthrough matrix for y (m x p), real.

Raises:
  • TypeError – If any argument cannot be interpreted as a matrix.

  • ValueError – If matrix dimensions are inconsistent.

Example

>>> import nqlib
>>> K = nqlib.Controller(A=0,
...                      B1=0,
...                      B2=[0, 0],
...                      C=0,
...                      D1=1,
...                      D2=[-20, -3])
>>> K.B1
array([[0]])
class nqlib.DynamicQuantizer(A, B, C, q)

Bases: object

Dynamic quantizer.

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(1.0)
>>> Q = nqlib.DynamicQuantizer(0.6, 1, 1, q)
>>> Q.quantize([0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4])
array([[ 0.,  0.,  0.,  0.,  0., -1., -2., -3.]])
property A

State matrix (N x N, real). N >= 1.

property B

Input matrix (N x m, real). m >= 1.

property C

Output matrix (m x N, real).

property N

Order of this dynamic quantizer (N >= 1).

Equals the number of rows (or columns) in A.

__init__(A, B, C, q)

Initialize a DynamicQuantizer instance.

The dynamic quantizer is defined by the following equations:

Q{ xi(t+1) = A xi(t) + B u(t)

{ v(t) = q( C xi(t) + u(t) )

Parameters:
  • A (NDArrayNum) – State matrix (N x N, real). N >= 1.

  • B (NDArrayNum) – Input matrix (N x m, real). m >= 1.

  • C (NDArrayNum) – Output matrix (m x N, real).

  • q (StaticQuantizer) – Static quantizer instance.

Raises:

ValueError – If matrix dimensions are inconsistent.

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(1.0)
>>> Q = nqlib.DynamicQuantizer(0.6, 1.0, 1.0, q)
>>> Q.quantize([0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6])
array([[1., 1., 1., 1., 1., 2., 3.]])
cost(system, steptime=InfInt.Value, _check_stability=True)

Compute the cost (E(Q)) for the quantizer and system.

Parameters:
  • system (System) – System instance.

  • steptime (int or InfInt, optional) – Number of steps (default: infint, which implies that this function calculates until convergence). steptime >= 1.

  • _check_stability (bool, optional) – If True, check stability (default: True).

Returns:

Estimation of E(Q) in the given steptime.

Return type:

float

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control; IEEE Transactions on Automatic Control, Vol. 53, pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> import numpy as np
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[1],
...         [1]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.], C2=[-15., -3.],
...     D1=0, D2=0,
... )
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_AG(
...     system=G,
...     q=q,
... )
>>> np.isclose(E, Q.cost(G))
np.True_
property delta

The maximum allowed quantization error of q. Declares that for any real vector u, max(abs(q(u)-u)) <= delta. delta > 0.

static design(system, *, q, steptime=InfInt.Value, max_gain_wv=inf, max_N=InfInt.Value, verbose=False, use_analytical_method=True, use_LP_method=True, use_design_GB_method=True, use_DE_method=False, solver=None)

Design a stable and optimal dynamic quantizer for a system.

Parameters:
  • system (System) – Stable system instance.

  • q (StaticQuantizer) – Static quantizer instance.

  • steptime (int or InfInt, optional) – Estimation time (default: infint). steptime >= 1.

  • max_gain_wv (float, optional) – Upper limit of gain w->v (default: inf). max_gain_wv >= 0.

  • max_N (int or InfInt, optional) – Upper limit of quantizer order (default: infint). max_N >= 1.

  • verbose (bool, optional) – If True, print progress (default: False).

  • use_analytical_method (bool, optional) – Use analytical method (default: True).

  • use_LP_method (bool, optional) – Use LP method (default: True).

  • use_design_GB_method (bool, optional) – Use gradient-based method (default: True).

  • use_DE_method (bool, optional) – Use differential evolution method (default: False).

  • solver (str or None, optional) – CVXPY solver name (default: None).

Returns:

  • Q (DynamicQuantizer or None) – Designed quantizer or None if not found.

  • E (float) – Estimated E(Q). If Q is None, E is inf.

Raises:

ValueError – If system is unstable.

Example

>>> import nqlib
>>> P = nqlib.Plant(
...     A =[[ 1.8, 0.8],
...         [-1.0, 0. ]],
...     B =[[1.],
...         [0.]],
...     C1 =[0.01, -0.09],
...     C2 =[0, 0],
... )
>>> G = nqlib.System.from_FF(P)
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design(
...     system=G,
...     q=q,
...     steptime=100,
... )
>>> Q.is_stable
True
>>> E < 0.5
np.True_
static design_AG(system, *, q, allow_unstable=False, verbose=False)

Algebraically design a stable and optimal dynamic quantizer for a system.

Parameters:
  • system (System) – Stable and SISO system instance.

  • q (StaticQuantizer) – Static quantizer instance.

  • allow_unstable (bool, optional) – Allow unstable quantizer (default: False). It is recommended to set verbose to True, so that you are reminded the result is unstable. If this is True, the design method in reference [3] will not be used.

  • verbose (bool, optional) – If True, print progress (default: False).

Returns:

  • Q (DynamicQuantizer or None) – Designed quantizer or None if not found.

  • E (float) – Estimated E(Q). If Q is None, E is inf.

Raises:

ValueError – If system is unstable.

Example

>>> import nqlib
>>> P = nqlib.Plant(
...     A =[[ 1.8, 0.8],
...         [-1.0, 0. ]],
...     B =[[1.],
...         [0.]],
...     C1 =[0.01, -0.09],
...     C2 =[0, 0],
... )
>>> G = nqlib.System.from_FF(P)
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_AG(system=G, q=q)
>>> Q.is_stable
True
>>> E < 0.5
np.True_
static design_DE(system, *, q, N, steptime=InfInt.Value, max_gain_wv=inf, verbose=False)

Design a stable and optimal dynamic quantizer using differential evolution.

Parameters:
  • system (System) – Stable and SISO system instance.

  • q (StaticQuantizer) – Static quantizer instance.

  • N (int) – Order of the resulting quantizer. N >= 1.

  • steptime (int or InfInt, optional) – Estimation time (default: infint). steptime >= 1.

  • max_gain_wv (float, optional) – Upper limit of gain w->v (default: inf). max_gain_wv >= 0.

  • verbose (bool, optional) – If True, print progress (default: False).

Returns:

  • Q (DynamicQuantizer or None) – Designed quantizer or None if not found.

  • E (float) – Estimated E(Q). If Q is None, E is inf.

Raises:

ValueError – If system is unstable.

References

[5] Y. Minami and T. Muromaki: Differential evolution-based synthesis of dynamic quantizers with fixed-structures; International Journal of Computational Intelligence and Applications, Vol. 15, No. 2, 1650008 (2016)

Example

>>> import nqlib
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[1],
...         [1]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.], C2=[-15., -3.],
...     D1=0, D2=0,
... )
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_DE(
...     system=G,
...     q=q,
...     N=2,
... )
>>> Q.is_stable
True
>>> E < 0.01
np.True_
static design_GD(system, *, q, N, steptime=InfInt.Value, max_gain_wv=inf, verbose=False, method='SLSQP', obj_type='exp')

Design a stable and optimal dynamic quantizer using gradient-based optimization.

Parameters:
  • system (System) – Stable and SISO system instance.

  • q (StaticQuantizer) – Static quantizer instance.

  • N (int) – Order of the resulting quantizer. N >= 1.

  • steptime (int or InfInt, optional) – Estimation time (default: infint). steptime >= 1.

  • max_gain_wv (float, optional) – Upper limit of gain w->v (default: inf). max_gain_wv >= 0.

  • verbose (bool, optional) – If True, print progress (default: False).

  • method (str, optional) – Optimization method for scipy.optimize.minimize (default: ‘SLSQP’). (If None, this function does not specify the method).

  • obj_type (str, optional) – Objective function type (default: ‘exp’).

Returns:

  • Q (DynamicQuantizer or None) – Designed quantizer or None if not found.

  • E (float) – Estimated E(Q). If Q is None, E is inf.

Raises:

ValueError – If system is unstable.

References

[5] Y. Minami and T. Muromaki: Differential evolution-based synthesis of dynamic quantizers with fixed-structures; International Journal of Computational Intelligence and Applications, Vol. 15, No. 2, 1650008 (2016)

Example

>>> import nqlib
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[1],
...         [1]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.], C2=[-15., -3.],
...     D1=0, D2=0,
... )
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_GD(
...     system=G,
...     q=q,
...     N=2,
... )
>>> Q.is_stable
True
>>> E < 0.01
np.True_
static design_LP(system, *, q, dim=InfInt.Value, T=InfInt.Value, max_gain_wv=inf, solver=None, verbose=False)

Design a stable and optimal dynamic quantizer using the linear programming method.

Note that this method does not guarantee that Q.gain_wv() < max_gain_wv will be True.

Parameters:
  • system (System) – Stable and SISO system instance.

  • q (StaticQuantizer) – Static quantizer instance.

  • dim (int or InfInt, optional) – Upper limit of quantizer order (default: infint). dim >= 1.

  • T (int or InfInt, optional) – Estimation time (default: infint) (T > 0).

  • max_gain_wv (float, optional) – Upper limit of gain w->v (default: inf) (max_gain_wv > 0).

  • solver (str or None, optional) – CVXPY solver name (default: None). You can check the available solvers by nqlib.installed_solvers(). (If None, this function does not specify the solver).

  • verbose (bool, optional) – If True, print progress (default: False).

Returns:

  • Q (DynamicQuantizer or None) – Designed quantizer or None if not found.

  • E (float) – Estimated E(Q). If Q is None, E is inf.

Raises:

ValueError – If system is unstable.

References

[4] R. Morita, S. Azuma, Y. Minami and T. Sugie: Graphical design software for dynamic quantizers in control systems; SICE Journal of Control, Measurement, and System Integration, Vol. 4, No. 5, pp. 372-379 (2011)

Example

>>> import nqlib
>>> P = nqlib.Plant(
...     A =[[ 1.8, 0.8],
...         [-1.0, 0. ]],
...     B =[[1.],
...         [0.]],
...     C1 =[0.01, -0.09],
...     C2 =[0, 0],
... )
>>> G = nqlib.System.from_FF(P)
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_LP(
...     system=G,
...     q=q,
...     T=100,
...     dim=2,
...     max_gain_wv=2.0,
... )
>>> Q.is_stable
True
>>> E < 0.5
np.True_
static from_SISO_parameters(parameters, *, q)

Create a SISO dynamic quantizer from parameters. The resulting dynamic quantizer is in a reachable canonical form from a 1D array of parameters, which is a concatenation of the coefficients.

The form of the parameters is as follows:

| a = parameters[:N]
| c = parameters[N:]
| A = [[      0,      1,      0,    ...,      0]
|      [      0,      0,      1,    ...,      0]
|                                      :
|      [      0,      0,      0,    ...,      1]
|      [  -a[0],  -a[1],  -a[2],    ..., -a[N-1]]
| B =  [    [0],    [0],    [0],    ...,    [1]]
| C =  [   c[0],   c[1],   c[2],    ...,  c[N-1]]
Parameters:
  • parameters (NDArrayNum) – 1D array of parameters, concatenating a and c.

  • q (StaticQuantizer) – Static quantizer that this dynamic quantizer uses.

Returns:

Dynamic quantizer created from the parameters.

Return type:

DynamicQuantizer

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> Q = nqlib.DynamicQuantizer.from_SISO_parameters([1, 0.33], q=q)
>>> Q.A
array([[-1.]])
>>> Q.C
array([[0.33]])
property gain_uv

Gain from u to v.

gain_wv(steptime=InfInt.Value, verbose=False)

Compute the gain from w to v for this quantizer.

Parameters:
  • steptime (int or InfInt, optional) – The number of time steps to use for the gain calculation. If infint, calculation continues until convergence. steptime >= 1 (default: infint, which means until convergence).

  • verbose (bool, optional) – If True, print progress information during calculation (default: False).

Returns:

Estimated gain w->v.

Return type:

float

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control; IEEE Transactions on Automatic Control, Vol. 53, pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> dq = nqlib.DynamicQuantizer(0.5, 0.1, 0.1, q)
>>> dq.gain_wv() < 1.021
np.True_
property is_stable

Check if the quantizer is stable.

Returns:

True if stable, False otherwise.

Return type:

bool

References

[2] S. Azuma, Y. Minami and T. Sugie: Optimal dynamic quantizers for feedback control with discrete-level actuators; Journal of Dynamic Systems, Measurement, and Control, Vol. 133, No. 2, 021005 (2011)

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> Q = nqlib.DynamicQuantizer(0.5, 1.0, 1.0, q)
>>> Q.is_stable
False
property m

Number of inputs (m >= 1).

Equals the number of columns in B and the number of rows in C.

property minreal

Minimal realization of this quantizer.

Returns:

Minimal realization of this quantizer.

Return type:

DynamicQuantizer

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> Q = nqlib.DynamicQuantizer(1, 1, 1, q)
>>> Q2 = Q.minreal
>>> Q2.N
1
objective_function(system, *, steptime_gain_wv=InfInt.Value, steptime_E=InfInt.Value, max_gain_wv=inf, obj_type='exp')

Objective function designed for numerical optimization.

If this value is less than 0, the quantizer satisfies the stability and max_gain_wv constraints. The less this value is, the better the quantizer (i.e., the less system.E(Q)).

Parameters:
  • system (System) – The system for which the quantizer is being optimized. Must be stable and SISO.

  • steptime_gain_wv (int or InfInt, optional) – The number of time steps for calculating max_gain_wv. steptime_gain_wv >= 1 (default: infint, which means until convergence).

  • steptime_E (int or InfInt, optional) – The number of time steps for calculating system.E(Q). steptime_E >= 1 (default: infint, which means until convergence).

  • max_gain_wv (float, optional) – Upper limit for the w->v gain. max_gain_wv >= 0 (default: np.inf).

  • obj_type (str, optional) – Objective function type. Must be one of [‘exp’, ‘atan’, ‘1.1’, ‘100*1.1’] (default: ‘exp’).

Returns:

Objective value. If the value is less than 0, the quantizer satisfies the constraints. Otherwise, the value is max(eig_max(A + B @ C) - 1, Q.gain_wv() - max_gain_wv).

Return type:

float

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> Q = nqlib.DynamicQuantizer(1, 0.1, 0.1, q)
>>> Q.is_stable
False
>>> system = nqlib.System(0, 0, 0, 0, 0, 0, 0)
>>> Q.objective_function(
...     system,
...     steptime_gain_wv=10,
...     steptime_E=10,
...     max_gain_wv=1.0,
...     obj_type="exp",
... ) > 0  # because unstable
np.True_

References

[5] Y. Minami and T. Muromaki: Differential evolution-based synthesis of dynamic quantizers with fixed-structures; International Journal of Computational Intelligence and Applications, Vol. 15, No. 2, 1650008 (2016)

order_reduced(new_N)

Returns a reduced-order quantizer.

Parameters:

new_N (int) – Desired order (1 <= new_N < self.N).

Returns:

Reduced-order quantizer.

Return type:

DynamicQuantizer

Raises:
  • ImportError – If slycot is not installed.

  • ValueError – If new_N is not in the valid range.

Notes

Note that the quantizer with the reduced order will generally have larger E(Q) and a larger gain_wv than those of the original quantizer. You should check the performance and gain yourself.

This function requires slycot. Please install it.

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> import numpy as np
>>> Q = nqlib.DynamicQuantizer(np.eye(2)*0.5, np.eye(2, 1), np.eye(1, 2), q)
>>> Q.N  # Original order
2
>>> Q2 = Q.order_reduced(1)  # Reduce the order to 1
>>> Q2.N
1
property q

Static quantizer which is used in this dynamic quantizer.

quantize(u)

Quantize the input signal using this dynamic quantizer.

Parameters:

u (NDArrayNum) – Input signal. Shape: (m, length).

Returns:

Quantized signal. Shape: (1, length).

Return type:

NDArrayNum

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.3)
>>> Q = nqlib.DynamicQuantizer(1, 1, 1, q)
>>> Q.quantize([[0.2, 0.4]])
array([[0.3, 0.6]])
spec(steptime=InfInt.Value, show=True)

Returns a string summary of the quantizer’s specification.

Parameters:
  • steptime (int or InfInt, optional) – Number of steps (default: infint, which implies that this function calculates until convergence). steptime >= 1.

  • show (bool, optional) – If True, print the summary (default: True).

Returns:

Specification summary.

Return type:

str

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> Q = nqlib.DynamicQuantizer(1, 1, 1, q)
>>> Q.spec(show=False)
'The specs of ...'
to_parameters(*, minreal=False)

Convert the dynamic quantizer to a 1D array of parameters.

Parameters here means the elements of the A and C matrices of a reachable canonical form. This quantizer must be SISO.

The form of the parameters is as follows:

| A = [[      0,      1,      0,    ...,      0]
|      [      0,      0,      1,    ...,      0]
|                                      :
|      [      0,      0,      0,    ...,      1]
|      [  -a[0],  -a[1],  -a[2],    ...,-a[N-1]]]
| B =  [    [0],    [0],    [0],    ...,    [1]]
| C =  [   c[0],   c[1],   c[2],    ...,  c[N-1]]
| parameters = [*a, *c]
Parameters:

minreal (bool, optional) – If True, return the parameters of the minimal realization of this quantizer (default: False).

Returns:

parameters – 1D array of parameters, concatenating a and c.

Return type:

NDArrayNum

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> Q = nqlib.DynamicQuantizer(-1, 1, 0.33, q)
>>> Q.to_parameters()
array([1.  , 0.33])
class nqlib.Plant(A, B, C1, C2)

Bases: object

State-space model of a plant P.

The plant P is given by:

P{ x(t+1) = A x(t) + B u(t)

{ z(t) = C1 x(t) { y(t) = C2 x(t)

Example

>>> import nqlib
>>> import numpy as np
>>> P = nqlib.Plant(A=[[1.15, 0.05],
...                    [0, 0.99]],
...                 B=[[0.004],
...                    [0.099]],
...                 C1=[1, 0],
...                 C2=np.eye(2))
>>> P.A
array([[1.15, 0.05],
       [0.  , 0.99]])
__init__(A, B, C1, C2)

Initialize a Plant instance.

Parameters:
  • A (array_like) – State matrix (n x n), real.

  • B (array_like) – Input matrix (n x m), real.

  • C1 (array_like) – Output matrix for z (l1 x n), real.

  • C2 (array_like) – Output matrix for y (l2 x n), real.

Raises:
  • TypeError – If any argument cannot be interpreted as a matrix.

  • ValueError – If matrix dimensions are inconsistent.

References

[5] Y. Minami and T. Muromaki: Differential evolution-based synthesis of dynamic quantizers with fixed-structures; International Journal of Computational Intelligence and Applications, Vol. 15, No. 2, 1650008 (2016)

Example

>>> import nqlib
>>> import numpy as np
>>> P = nqlib.Plant(A=[[1.15, 0.05],
...                    [0, 0.99]],
...                 B=[[0.004],
...                    [0.099]],
...                 C1=[1, 0],
...                 C2=np.eye(2))
>>> P.A
array([[1.15, 0.05],
       [0.  , 0.99]])
static from_TF(tf)

Create a Plant instance from a transfer function.

Parameters:

tf (control.TransferFunction) – Transfer function from input u to output z.

Returns:

Plant instance with C2 set to zero.

Return type:

Plant

Example

>>> import nqlib
>>> import control
>>> tf = control.TransferFunction([1], [1, 2, 1], 1)  # 1 / (s^2 + 2s + 1)
>>> P = nqlib.Plant.from_TF(tf)
>>> P.A.shape[0]
2
class nqlib.StaticQuantizer(function, delta, *, error_on_excess=True)

Bases: object

Static quantizer.

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> q([0.04, 0.16])
array([0. , 0.2])
__init__(function, delta, *, error_on_excess=True)

Initialize a StaticQuantizer q.

Parameters:
  • function (Callable[[NDArrayNum], NDArrayNum]) – Quantization function. Must be callable.

  • delta (float) – The maximum allowed quantization error. Declares that for any real vector u, max(abs(q(u)-u)) <= delta. delta > 0.

  • error_on_excess (bool, optional) – If True, raises an error when the error exceeds delta (default: True). That is, whether to raise an error when max(abs(q(u)-u)) > delta becomes True.

Raises:
  • TypeError – If function is not callable.

  • ValueError – If quantization error exceeds delta and error_on_excess is True.

Example

>>> import nqlib
>>> import numpy as np
>>> q = nqlib.StaticQuantizer(lambda u: np.round(u), 1.0)
>>> q([1.2, 2.3])
array([1., 2.])
property delta

The maximum allowed quantization error. Declares that for any real vector u, max(abs(q(u)-u)) <= delta. delta > 0.

static mid_riser(d, bit=InfInt.Value, *, error_on_excess=True)

Create a mid-riser uniform StaticQuantizer.

Parameters:
  • d (float) – Quantization step size. For a real vector u, max(abs(q(u)-u)) <= d/2. delta > 0.

  • bit (int or InfInt, optional) – Number of bits. Must satisfy bit >= 1 (default: infint). That is, the returned function can take 2**n values.

  • error_on_excess (bool, optional) – If True, raises an error when the error exceeds delta (=d/2) (default: True). That is, whether to raise an error when max(abs(q(u)-u)) > delta becomes True. This error should not occur, but for numerical safety, set this to True.

Returns:

Mid-riser quantizer instance.

Return type:

StaticQuantizer

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_riser(0.5)
>>> q([0.2, 0.7, 1.1, 0, -1])
array([ 0.25,  0.75,  1.25,  0.25, -0.75])
static mid_tread(d, bit=InfInt.Value, *, error_on_excess=True)

Create a mid-tread uniform StaticQuantizer.

Parameters:
  • d (float) – Quantization step size. For a real vector u, max(abs(q(u)-u)) <= d/2. delta > 0.

  • bit (int or InfInt, optional) – Number of bits. Must satisfy bit >= 1 (default: infint). That is, the returned function can take 2**n values.

  • error_on_excess (bool, optional) – If True, raises an error when the error exceeds delta (=d/2) (default: True). That is, whether to raise an error when max(abs(q(u)-u)) > delta becomes True. This error should not occur, but for numerical safety, set this to True.

Returns:

q – Mid-tread quantizer instance.

Return type:

StaticQuantizer

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.5)
>>> q([0.2, 0.7, 1.1, 0, -1])
array([ 0. ,  0.5,  1. ,  0. , -1. ])
quantize(u)

Quantize the input signal.

Parameters:

u (NDArrayNum) – Input signal.

Returns:

Quantized signal.

Return type:

NDArrayNum

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.5)
>>> u = [0, 0.32, 0.44]
>>> all(q.quantize(u) == q(u))
True
>>> q.quantize(u)
array([0. , 0.5, 0.5])
class nqlib.System(A, B1, B2, C1, C2, D1, D2)

Bases: object

State-space model of an ideal system. Ideal here means that the system does not have a quantizer.

The system is given by:

G{ x(t+1) = A x(t) + B1 r(t) + B2 v(t)

{ z(t) = C1 x(t) + D1 r(t) { u(t) = C2 x(t) + D2 r(t)

Example

>>> import nqlib
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[0.],
...         [0.]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.],
...     C2=[-15., -3.],
...     D1=0,
...     D2=1,
... )
>>> G.A
array([[1.15, 0.05],
       [0.  , 0.99]])
E(Q, steptime=InfInt.Value, _check_stability=True, verbose=False)

Estimate E(Q) for the system and quantizer.

Parameters:
  • Q (DynamicQuantizer) – Dynamic quantizer instance. The quantizer whose performance is evaluated.

  • steptime (int or InfInt, optional) – Evaluation time. Must be a natural number (steptime >= 1). Default: infint.

  • _check_stability (bool, optional) – If True, check stability (default: True). This shouldn’t be changed.

  • verbose (bool, optional) – If True, print progress (default: False).

Returns:

Estimated value of E(Q) in the given steptime.

Return type:

float

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> import numpy as np
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[1],
...         [1]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.], C2=[-15., -3.],
...     D1=0, D2=0,
... )
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_AG(
...     system=G,
...     q=q,
... )
>>> np.isclose(E, G.E(Q))
np.True_
__init__(A, B1, B2, C1, C2, D1, D2)

Initialize a System instance (ideal system without quantizer).

Parameters:
  • A (array_like) – State matrix (n x n), real.

  • B1 (array_like) – Input matrix for r (n x p), real.

  • B2 (array_like) – Input matrix for v (n x m), real.

  • C1 (array_like) – Output matrix for z (l x n), real.

  • C2 (array_like) – Output matrix for u (m x n), real.

  • D1 (array_like) – Feedthrough matrix for r to z (l x p), real.

  • D2 (array_like) – Feedthrough matrix for r to u (m x p), real.

Raises:
  • TypeError – If any argument cannot be interpreted as a matrix.

  • ValueError – If matrix dimensions are inconsistent.

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[0.],
...         [0.]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.],
...     C2=[-15., -3.],
...     D1=0,
...     D2=1,
... )
>>> G.A
array([[1.15, 0.05],
       [0.  , 0.99]])
static from_FBIQ(P, K)

Alias for from_FB_connection_with_input_quantizer.

Parameters:
  • P (Plant) – Plant instance. The plant in the feedback loop.

  • K (Controller) – Controller instance. The controller in the feedback loop.

Returns:

System with quantizer inserted at controller input (feedback).

Return type:

System

Example

>>> import nqlib
>>> P = nqlib.Plant(0.5, 1, 1, 1)
>>> K = nqlib.Controller(0, 1, 1, 1, 1, 1)
>>> sys = nqlib.System.from_FBIQ(P, K)
>>> sys.is_stable
False
static from_FBOQ(P, K)

Alias for from_FB_connection_with_output_quantizer.

Parameters:
  • P (Plant) – Plant instance. The plant in the feedback loop.

  • K (Controller) – Controller instance. The controller in the feedback loop.

Returns:

System with quantizer inserted at controller output (feedback).

Return type:

System

Example

>>> import nqlib
>>> P = nqlib.Plant(0.5, 1, 1, 1)
>>> K = nqlib.Controller(0, 1, 1, 1, 1, 1)
>>> sys = nqlib.System.from_FBOQ(P, K)
>>> sys.is_stable
False
static from_FB_connection_with_input_quantizer(P, K)

Create a System instance from Plant and Controller with input quantizer (feedback connection).

‘from_FB_connection_with_input_quantizer’ means that a quantizer is inserted as shown in the following figure.

|       ┌───────┐     ┌───────┐     ┌───────┐
| r ───>│       │  u  │       │  v  │       ├───> z
|       │   K   ├────>│   Q   ├────>│   P   │
|    ┌─>│       │     │       │     │       ├──┐
|    │  └───────┘     └───────┘     └───────┘  │ y
|    └─────────────────────────────────────────┘
Parameters:
  • P (Plant) – Plant instance. The plant in the feedback loop.

  • K (Controller) – Controller instance. The controller in the feedback loop.

Returns:

System with quantizer inserted at controller input (feedback).

Return type:

System

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> P = nqlib.Plant(0.5, 1, 1, 1)
>>> K = nqlib.Controller(0, 1, 1, 1, 1, 1)
>>> sys = nqlib.System.from_FB_connection_with_input_quantizer(P, K)
>>> sys.is_stable
False
static from_FB_connection_with_output_quantizer(P, K)

Create a System instance from Plant and Controller with output quantizer (feedback connection).

‘from_FB_connection_with_output_quantizer’ means that a quantizer is inserted as shown in the following figure.

|       ┌───────┐           ┌───────┐
| r ───>│       │           │       ├───> z
|       │   K   ├──────────>│   P   │
|    ┌─>│       │           │       ├──┐
|  v │  └───────┘  ┌─────┐  └───────┘  │ u
|    └─────────────┤  Q  │<────────────┘
|                  └─────┘
Parameters:
  • P (Plant) – Plant instance. The plant in the feedback loop.

  • K (Controller) – Controller instance. The controller in the feedback loop.

Returns:

System with quantizer inserted at controller output (feedback).

Return type:

System

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> P = nqlib.Plant(0.5, 1, 1, 1)
>>> K = nqlib.Controller(0, 1, 1, 1, 1, 1)
>>> sys = nqlib.System.from_FB_connection_with_output_quantizer(P, K)
>>> sys.is_stable
False
static from_FF(P)

Create a System instance from a Plant (feedforward connection).

‘from_FF’ means that a quantizer is inserted as shown in the following figure.

|       ┌─────┐  v  ┌─────┐
| u ───>│  Q  ├────>│  P  ├───> z
|       └─────┘     └─────┘
Parameters:

P (Plant) – Plant instance. The plant to which the quantizer is connected in feedforward.

Returns:

System with quantizer inserted before plant (feedforward).

Return type:

System

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> P = nqlib.Plant(
...     A =[[ 1.8, 0.8],
...         [-1.0, 0. ]],
...     B =[[1.],
...         [0.]],
...     C1 =[0.01, -0.09],
...     C2 =[0, 0],
... )
>>> G = nqlib.System.from_FF(P)
>>> G.is_stable
True
property is_stable

Check if the closed-loop system is stable.

Returns:

True if stable, False otherwise.

Return type:

bool

Example

>>> import nqlib
>>> sys = nqlib.System(0.3, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5)
>>> sys.is_stable
True
is_stable_with(Q)

Alias for is_stable_with_quantizer.

Parameters:

Q (DynamicQuantizer or StaticQuantizer) – Quantizer instance. The quantizer to check stability with.

Returns:

True if stable, False otherwise.

Return type:

bool

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[1],
...         [1]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.], C2=[-15., -3.],
...     D1=0, D2=0,
... )
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_AG(
...     system=G,
...     q=q,
... )
>>> G.is_stable_with(Q)
True
is_stable_with_quantizer(Q)

Check if the system is stable with the given quantizer.

Parameters:

Q (DynamicQuantizer or StaticQuantizer) – Quantizer instance. The quantizer to check stability with.

Returns:

True if stable, False otherwise.

Return type:

bool

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> G = nqlib.System(
...     A=[[1.15, 0.05],
...        [0.00, 0.99]],
...     B1=[[1],
...         [1]],
...     B2=[[0.004],
...         [0.099]],
...     C1=[1., 0.], C2=[-15., -3.],
...     D1=0, D2=0,
... )
>>> q = nqlib.StaticQuantizer.mid_tread(d=2)
>>> Q, E = nqlib.DynamicQuantizer.design_AG(
...     system=G,
...     q=q,
... )
>>> G.is_stable_with_quantizer(Q)
True
response(input, x_0)

Simulate the system and return results.

Parameters:
  • input (array_like) – Input signal (reference r). Shape: (p, length).

  • x_0 (array_like) – Initial state vector. Shape: (n, 1).

Returns:

  • t (np.ndarray) – Time steps. Shape: (1, length).

  • u (np.ndarray) – Input signal to plant. Shape: (m, length).

  • z (np.ndarray) – Output signal. Shape: (l, length).

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> import numpy as np
>>> sys = nqlib.System(1, 1, 1, 1, 1, 1, 1)
>>> t, u, z = sys.response(np.ones((1, 5)), np.zeros((1, 1)))
>>> t.shape
(1, 5)
response_with_quantizer(quantizer, input, x_0)

Simulate the system with a quantizer and return results.

Parameters:
  • quantizer (DynamicQuantizer or StaticQuantizer) – Quantizer to use in the simulation.

  • input (array_like) – Input signal (reference r). Shape: (p, length).

  • x_0 (array_like) – Initial state vector. Shape: (n, 1).

Returns:

  • t (np.ndarray) – Time steps. Shape: (1, length).

  • u (np.ndarray) – Input to quantizer. Shape: (m, length).

  • v (np.ndarray) – Quantized input. Shape: (m, length).

  • z (np.ndarray) – Output signal. Shape: (l, length).

References

[1] S. Azuma and T. Sugie: Synthesis of optimal dynamic quantizers for discrete-valued input control;IEEE Transactions on Automatic Control, Vol. 53,pp. 2064–2075 (2008)

Example

>>> import nqlib
>>> import numpy as np
>>> sys = nqlib.System(1, 1, 1, 1, 1, 1, 1)
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> t, u, v, z = sys.response_with_quantizer(q, np.ones((1, 5)), np.zeros((1, 1)))
>>> t.shape
(1, 5)
nqlib.installed_solvers()

List the installed solvers.

nqlib.order_reduced(Q, new_N)

Returns the quantizer with its order reduced.

Note that the quantizer with the reduced order will generally have larger E(Q) and a larger gain_wv than those of the original quantizer. You should check the performance and gain yourself.

This function requires slycot. Please install it.

Parameters:
  • Q (DynamicQuantizer) – The quantizer to be reduced. Must be an instance of DynamicQuantizer.

  • new_N (int) – Desired order (1 <= new_N < Q.N).

Returns:

Quantizer with reduced order.

Return type:

DynamicQuantizer

Example

>>> import nqlib
>>> q = nqlib.StaticQuantizer.mid_tread(0.1)
>>> import numpy as np
>>> Q = nqlib.DynamicQuantizer(np.eye(2)*0.4, np.eye(2, 1), np.eye(1, 2), q)
>>> Q.N  # Original order
2
>>> Q2 = order_reduced(Q, 1)  # Reduce the order to 1
>>> Q2.N
1