Spec-Zone.ru › TensorFlow

tf.linalg.LinearOperatorCirculant

LinearOperator, действующий как циркулянтная матрица.

Наследует от: LinearOperator, Module

Просмотреть псевдонимы

Псевдонимы совместимости для миграции

Подробнее см. в руководстве по миграции.

tf.compat.v1.linalg.LinearOperatorCirculant

tf.linalg.LinearOperatorCirculant(
    spectrum: tf.Tensor,
    input_output_dtype=tf.dtypes.complex64,
    is_non_singular: bool = None,
    is_self_adjoint: bool = None,
    is_positive_definite: bool = None,
    is_square: bool = True,
    name='LinearOperatorCirculant'
)

Этот оператор действует как циркулянтная матрица A с формой [B1,...,Bb, N, N] для некоторого b >= 0. Первые b индексы задают элемент пакета. Для каждого индекса пакета (i1,...,ib), A[i1,...,ib, : :] является матрицей N x N. Эта матрица A не материализуется, но эта форма важна для широковещательной передачи.

Описание в терминах циркулянтных матриц

Циркулянтная матрица означает, что элементы A порождаются одним вектором — ядром свёртки h: A_{mn} := h_{m-n mod N}. При h = [w, x, y, z],

A = |w z y x|
    |x w z y|
    |y x w z|
    |z y x w|

Это означает, что результат умножения матрицы v = Au имеет столбец Lth, полученный циклической свёрткой h с Lth столбцом u.

Описание в терминах частотного спектра

Существует эквивалентное описание через спектр [пакета] H и преобразования Фурье. Здесь рассмотрим A.shape = [N, N] и будем игнорировать размерности пакета. Определим дискретное преобразование Фурье (ДПФ) и его обратное преобразование следующим образом:

DFT[ h[n] ] = H[k] := sum_{n = 0}^{N - 1} h_n e^{-i 2pi k n / N}
IDFT[ H[k] ] = h[n] = N^{-1} sum_{k = 0}^{N - 1} H_k e^{i 2pi k n / N}

Из этих определений следует, что

H[0] = sum_{n = 0}^{N - 1} h_n
H[1] = "the first positive frequency"
H[N - 1] = "the first negative frequency"

В общих чертах, при поэлементном умножении * умножение матрицы эквивалентно действию фурье-множителя: A u = IDFT[ H * DFT[u] ]. Точнее, для матрицы [N, R] u пусть DFT[u] будет матрицей [N, R], у которой rth столбец равен ДПФ rth столбца u. Аналогично определим IDFT. Умножение матрицы можно выразить по столбцам:

(A u)_r = IDFT[ H * (DFT[u])_r ]

Свойства оператора, выводимые из спектра.

Пусть U — kth единичный вектор евклидова базиса, а U = IDFT[u]. Приведённые выше формулы показывают, чтоA U = H_k * U. Следовательно, элементы H являются собственными значениями этого оператора. Поэтому

  • Этот оператор является положительно определённым тогда и только тогда, когда Real{H} > 0.

Общее свойство преобразований Фурье — соответствие между эрмитовыми функциями и вещественными преобразованиями.

Предположим, что H.shape = [B1,...,Bb, N]. Будем говорить, что H является эрмитовым спектром, если при %, где подразумевается деление по модулю,

H[..., n % N] = ComplexConjugate[ H[..., (-n) % N] ]

  • Этому оператору соответствует вещественная матрица тогда и только тогда, когда H является эрмитовым.
  • Этот оператор является самосопряжённым тогда и только тогда, когда H вещественно.

См., например, «Discrete-Time Signal Processing», Oppenheim и Schafer.

Пример самосопряжённого положительно определённого оператора

# spectrum is real ==> operator is self-adjoint
# spectrum is positive ==> operator is positive definite
spectrum = [6., 4, 2]

operator = LinearOperatorCirculant(spectrum)

# IFFT[spectrum]
operator.convolution_kernel()
==> [4 + 0j, 1 + 0.58j, 1 - 0.58j]

operator.to_dense()
==> [[4 + 0.0j, 1 - 0.6j, 1 + 0.6j],
     [1 + 0.6j, 4 + 0.0j, 1 - 0.6j],
     [1 - 0.6j, 1 + 0.6j, 4 + 0.0j]]

Пример определения через вещественное ядро свёртки

# convolution_kernel is real ==> spectrum is Hermitian.
convolution_kernel = [1., 2., 1.]]
spectrum = tf.signal.fft(tf.cast(convolution_kernel, tf.complex64))

# spectrum is Hermitian ==> operator is real.
# spectrum is shape [3] ==> operator is shape [3, 3]
# We force the input/output type to be real, which allows this to operate
# like a real matrix.
operator = LinearOperatorCirculant(spectrum, input_output_dtype=tf.float32)

operator.to_dense()
==> [[ 1, 1, 2],
     [ 2, 1, 1],
     [ 1, 2, 1]]

Пример эрмитова спектра

# spectrum is shape [3] ==> operator is shape [3, 3]
# spectrum is Hermitian ==> operator is real.
spectrum = [1, 1j, -1j]

operator = LinearOperatorCirculant(spectrum)

operator.to_dense()
==> [[ 0.33 + 0j,  0.91 + 0j, -0.24 + 0j],
     [-0.24 + 0j,  0.33 + 0j,  0.91 + 0j],
     [ 0.91 + 0j, -0.24 + 0j,  0.33 + 0j]

Пример принудительного задания вещественного dtype для эрмитова спектра

# spectrum is shape [4] ==> operator is shape [4, 4]
# spectrum is real ==> operator is self-adjoint
# spectrum is Hermitian ==> operator is real
# spectrum has positive real part ==> operator is positive-definite.
spectrum = [6., 4, 2, 4]

# Force the input dtype to be float32.
# Cast the output to float32.  This is fine because the operator will be
# real due to Hermitian spectrum.
operator = LinearOperatorCirculant(spectrum, input_output_dtype=tf.float32)

operator.shape
==> [4, 4]

operator.to_dense()
==> [[4, 1, 0, 1],
     [1, 4, 1, 0],
     [0, 1, 4, 1],
     [1, 0, 1, 4]]

# convolution_kernel = tf.signal.ifft(spectrum)
operator.convolution_kernel()
==> [4, 1, 0, 1]

Производительность

Предположим, что operator — LinearOperatorCirculant формы [N, N], а x.shape = [N, R]. Тогда

  • operator.matmul(x) — O(R*N*Log[N])
  • operator.solve(x) — O(R*N*Log[N])
  • operator.determinant() включает reduce_prod размера N.

Если же operator и x имеют форму [B1,...,Bb, N, N] и [B1,...,Bb, N, R], сложность каждой операции возрастает на B1*...*Bb.

Подсказки о свойствах матрицы

Этот LinearOperator инициализируется логическими флагами вида is_X для X = non_singular, self_adjoint, positive_definite, square. Они имеют следующий смысл:

  • Если is_X == True, вызывающий код может рассчитывать, что оператор обладает свойством X. Это обещание, которое должно выполняться, но оно не является проверкой во время выполнения. Например, конечная точность чисел с плавающей запятой может привести к нарушению этих обещаний.
  • Если is_X == False, вызывающий код может рассчитывать, что оператор не обладает свойством X.
  • Если is_X == None (значение по умолчанию), у вызывающего кода не должно быть ожиданий ни в ту, ни в другую сторону.
Ссылки
Тёплицевы и циркулянтные матрицы — обзор: Gray, 2006 (pdf)
Аргументы
spectrum Форма [B1,...,Bb, N] Tensor. Допустимые типы данных: float16, float32, float64, complex64, complex128. Тип может отличаться от input_output_dtype
input_output_dtype dtype для ввода/вывода.
is_non_singular Предполагается, что этот оператор невырожден.
is_self_adjoint Предполагается, что этот оператор равен своему эрмитово-сопряжённому. Если spectrum вещественно, это всегда будет выполняться.
is_positive_definite Предполагается, что этот оператор положительно определён, то есть квадратичная форма x^H A x имеет положительную вещественную часть для всех ненулевых x. Обратите внимание: для положительной определённости мы не требуем, чтобы оператор был самосопряжённым. См.: https://en.wikipedia.org/wiki/Positive-definite_matrix
#Extension_for_non_symmetric_matrices
is_square Предполагается, что этот оператор действует как квадратные [пакетные] матрицы.
name Имя, добавляемое в начало имён всех операций, создаваемых этим классом.
Атрибуты
H Возвращает сопряжённый оператор для текущего LinearOperator.

Для A, представляющего этот LinearOperator, возвращает A*. Обратите внимание: вызовы self.adjoint() и self.H эквивалентны.

batch_shape TensorShape размерностей пакета этого LinearOperator.

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращает TensorShape([B1,...,Bb]), что эквивалентно A.shape[:-2]

block_depth Глубина рекурсивно определённых циркулянтных блоков, задающих этот Operator.

Пусть A — плотное представление этого Operator.

block_depth = 1 означает, что A является симметричной циркулянтной матрицей. Например:

A = |w z y x|
    |x w z y|
    |y x w z|
    |z y x w|

block_depth = 2 означает, что A является блочной симметричной циркулянтной матрицей с симметричными циркулянтными блоками. Например, при W, X, Y и симметричной циркулянтной Z:

A = |W Z Y X|
    |X W Z Y|
    |Y X W Z|
    |Z Y X W|

block_depth = 3 означает, что A является блочной симметричной циркулянтной матрицей с блочными симметричными циркулянтными блоками.

block_shape
domain_dimension Размерность области определения этого оператора (в смысле векторных пространств).

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращает N.

dtype DType обрабатываемых этим LinearOperator Tensor.
graph_parents Список зависимостей графа этого LinearOperator. (устарело)
Устарело: ЭТА ФУНКЦИЯ УСТАРЕЛА. Она будет удалена в будущей версии. Инструкции по обновлению: не вызывайте graph_parents.
is_non_singular
is_positive_definite
is_self_adjoint
is_square Возвращает True/False в зависимости от того, является ли оператор квадратным.
parameters Словарь параметров, использованных для создания экземпляра этого LinearOperator.
range_dimension Размерность области значений этого оператора (в смысле векторных пространств).

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращает M.

shape TensorShape этого LinearOperator.

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращает TensorShape([B1,...,Bb, M, N]), что эквивалентно A.shape.

spectrum
tensor_rank Ранг матрицы, соответствующей этому оператору (в смысле тензоров).

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращает b + 2.

Методы

add_to_tensor

Просмотреть исходный код

add_to_tensor(
    x, name='add_to_tensor'
)

Прибавляет матрицу, представленную этим оператором, к x. Эквивалентно A + x.

Аргументы
x Tensor с тем же dtype и формой, допускающей широковещательную передачу к self.shape.
name Имя для этого Op.
Возвращает
Tensor с формой, полученной широковещательной передачей, и тем же dtype, что и у self.

adjoint

Просмотреть исходный код

adjoint(
    name: str = 'adjoint'
) -> 'LinearOperator'

Возвращает сопряжённый оператор для текущего LinearOperator.

Для A, представляющего этот LinearOperator, возвращает A*. Обратите внимание: вызовы self.adjoint() и self.H эквивалентны.

Аргументы
name Имя для этого Op.
Возвращает
LinearOperator, представляющий сопряжённый оператор для этого LinearOperator.

assert_hermitian_spectrum

Просмотреть исходный код

assert_hermitian_spectrum(
    name='assert_hermitian_spectrum'
)

Возвращает Op, проверяющий, что спектр этого оператора является эрмитовым.

Этому оператору соответствует матрица с вещественными значениями тогда и только тогда, когда его спектр является эрмитовым.

Аргументы
name Имя для этого Op.
Возвращает
Op, проверяющий, что спектр этого оператора является эрмитовым.

assert_non_singular

Просмотреть исходный код

assert_non_singular(
    name='assert_non_singular'
)

Возвращает Op, проверяющий, что этот оператор невырожден.

Оператор считается невырожденным, если

ConditionNumber < max{100, range_dimension, domain_dimension} * eps,
eps := np.finfo(self.dtype.as_numpy_dtype).eps
Аргументы
name Строковое имя, добавляемое в начало имён создаваемых операций.
Возвращает
Assert Op, при выполнении которого будет вызвано исключение InvalidArgumentError, если оператор является вырожденным.

assert_positive_definite

Просмотреть исходный код

assert_positive_definite(
    name='assert_positive_definite'
)

Возвращает Op, проверяющий, что этот оператор положительно определён.

Здесь положительная определённость означает, что квадратичная форма x^H A x имеет положительную вещественную часть для всех ненулевых x. Обратите внимание: для положительной определённости мы не требуем, чтобы оператор был самосопряжённым.

Аргументы
name Имя для этого Op.
Возвращает
Assert Op, при выполнении которого будет вызвано исключение InvalidArgumentError, если оператор не является положительно определённым.

assert_self_adjoint

Просмотреть исходный код

assert_self_adjoint(
    name='assert_self_adjoint'
)

Возвращает Op, проверяющий, что этот оператор является самосопряжённым.

Здесь проверяется, что этот оператор точно равен своему эрмитово-сопряжённому.

Аргументы
name Строковое имя, добавляемое в начало имён создаваемых операций.
Возвращает
Assert Op, при выполнении которого будет вызвано исключение InvalidArgumentError, если оператор не является самосопряжённым.

batch_shape_tensor

Просмотреть исходный код

batch_shape_tensor(
    name='batch_shape_tensor'
)

Форма размерностей пакета этого оператора, определяемая во время выполнения.

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращает Tensor, содержащий [B1,...,Bb].

Аргументы
name Имя для этого Op.
Возвращает
int32 Tensor

block_shape_tensor

Просмотреть исходный код

block_shape_tensor()

Форма блочных размерностей self.spectrum.

cholesky

Просмотреть исходный код

cholesky(
    name: str = 'cholesky'
) -> 'LinearOperator'

Возвращает множитель Холецкого в виде LinearOperator.

Для A, представляющего этот LinearOperator, если A является положительно определённым самосопряжённым оператором, возвращает L, где A = L L^T, то есть разложение Холецкого.

Аргументы
name Имя для этого Op.
Возвращает
LinearOperator, представляющий нижнюю треугольную матрицу в разложении Холецкого.
Вызывает исключение
ValueError Если для LinearOperator не указано, что он положительно определён и самосопряжён.

cond

Просмотреть исходный код

cond(
    name='cond'
)

Возвращает число обусловленности этого линейного оператора.

Аргументы
name Имя для этого Op.
Возвращает
Форма [B1,...,Bb] Tensor с тем же dtype, что и у self.

convolution_kernel

Просмотреть исходный код

convolution_kernel(
    name='convolution_kernel'
)

Ядро свёртки, соответствующее self.spectrum.

ДПФ ядра размерности D представляет частотный спектр этого оператора.

Аргументы
name Имя для этого Op.
Возвращает
Tensor с dtype self.dtype.

determinant

Просмотреть исходный код

determinant(
    name='det'
)

Определитель для каждого элемента пакета.

Аргументы
name Имя для этого Op.
Возвращает
Tensor с формой self.batch_shape и тем же dtype, что и у self.
Вызывает исключение
NotImplementedError Если self.is_square является False.

diag_part

Просмотреть исходный код

diag_part(
    name='diag_part'
)

Эффективно получает диагональную часть [пакета] этого оператора.

Если форма этого оператора [B1,...,Bb, M, N], возвращает Tensor diagonal формы [B1,...,Bb, min(M, N)], где diagonal[b1,...,bb, i] = self.to_dense()[b1,...,bb, i, i].

my_operator = LinearOperatorDiag([1., 2.])

# Efficiently get the diagonal
my_operator.diag_part()
==> [1., 2.]

# Equivalent, but inefficient method
tf.linalg.diag_part(my_operator.to_dense())
==> [1., 2.]
Аргументы
name Имя для этого Op.
Возвращает
diag_part Tensor с тем же dtype, что и у self.

domain_dimension_tensor

Просмотреть исходный код

domain_dimension_tensor(
    name='domain_dimension_tensor'
)

Размерность области определения этого оператора (в смысле векторных пространств).

Определяется во время выполнения.

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращает N.

Аргументы
name Имя для этого Op.
Возвращает
int32 Tensor

eigvals

Просмотреть исходный код

eigvals(
    name='eigvals'
)

Возвращает собственные значения этого линейного оператора.

Если оператор помечен как самосопряжённый (с помощью is_self_adjoint), вычисление может быть более эффективным.

Примечание: В настоящее время поддерживаются только самосопряжённые операторы.
Аргументы
name Имя для этого Op.
Возвращает
Форма [B1,...,Bb, N] Tensor с тем же dtype, что и у self.

inverse

Просмотреть исходный код

inverse(
    name: str = 'inverse'
) -> 'LinearOperator'

Возвращает обратный оператор для этого LinearOperator.

Для A, представляющего этот LinearOperator, возвращает LinearOperator, представляющий A^-1.

Аргументы
name Пространство имён, используемое для операций, добавляемых этим методом.
Возвращает
LinearOperator, представляющий обратную матрицу.
Вызывает исключение
ValueError Если для LinearOperator не указано, что он является non_singular.

log_abs_determinant

Посмотреть исходный код

log_abs_determinant(
    name='log_abs_det'
)

Логарифм абсолютного значения определителя для каждого элемента пакета.

Аргументы
name Имя этого Op.
Возвращает
Tensor с формой self.batch_shape и тем же dtype, что и у self.
Вызывает исключение
NotImplementedError Если self.is_square — False.

matmul

Посмотреть исходный код

matmul(
    x, adjoint=False, adjoint_arg=False, name='matmul'
)

Преобразует [пакет] матрицу x левым умножением: x --> Ax.

# Make an operator acting like batch matrix A.  Assume A.shape = [..., M, N]
operator = LinearOperator(...)
operator.shape = [..., M, N]

X = ... # shape [..., N, R], batch matrix, R > 0.

Y = operator.matmul(X)
Y.shape
==> [..., M, R]

Y[..., :, r] = sum_j A[..., :, j] X[j, r]
Аргументы
x LinearOperator или Tensor с совместимой формой и тем же dtype, что и у self. Определение совместимости см. в документации класса.
adjoint Python bool. Если True, выполнить левое умножение на сопряжённый оператор: A^H x.
adjoint_arg Python bool. Если True, вычислить A x^H, где x^H — эрмитово сопряжение (транспонирование и комплексное сопряжение).
name Имя этого Op.
Возвращает
LinearOperator или Tensor с формой [..., M, R] и тем же dtype, что и у self.

matvec

Посмотреть исходный код

matvec(
    x, adjoint=False, name='matvec'
)

Преобразует [пакет] вектор x левым умножением: x --> Ax.

# Make an operator acting like batch matrix A.  Assume A.shape = [..., M, N]
operator = LinearOperator(...)

X = ... # shape [..., N], batch vector

Y = operator.matvec(X)
Y.shape
==> [..., M]

Y[..., :] = sum_j A[..., :, j] X[..., j]
Аргументы
x Tensor с совместимой формой и тем же dtype, что и у self. x рассматривается как [пакет] векторов: для каждого набора ведущих измерений последнее измерение задаёт вектор. Определение совместимости см. в документации класса.
adjoint Python bool. Если True, выполнить левое умножение на сопряжённый оператор: A^H x.
name Имя этого Op.
Возвращает
Tensor с формой [..., M] и тем же dtype, что и у self.

range_dimension_tensor

Посмотреть исходный код

range_dimension_tensor(
    name='range_dimension_tensor'
)

Размерность образа этого оператора (в смысле векторных пространств).

Определяется во время выполнения.

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращается M.

Аргументы
name Имя этого Op.
Возвращает
int32 Tensor

shape_tensor

Посмотреть исходный код

shape_tensor(
    name='shape_tensor'
)

Форма этого LinearOperator, определяемая во время выполнения.

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращается Tensor, содержащий [B1,...,Bb, M, N], что эквивалентно tf.shape(A).

Аргументы
name Имя этого Op.
Возвращает
int32 Tensor

solve

Посмотреть исходный код

solve(
    rhs, adjoint=False, adjoint_arg=False, name='solve'
)

Решает (точно или приближённо) R (пакет) систем уравнений: A X = rhs.

Возвращённое значение Tensor будет близко к точному решению, если A хорошо обусловлен. В противном случае степень близости будет различаться. Подробности см. в документации класса.

Примеры:

# Make an operator acting like batch matrix A.  Assume A.shape = [..., M, N]
operator = LinearOperator(...)
operator.shape = [..., M, N]

# Solve R > 0 linear systems for every member of the batch.
RHS = ... # shape [..., M, R]

X = operator.solve(RHS)
# X[..., :, r] is the solution to the r'th linear system
# sum_j A[..., :, j] X[..., j, r] = RHS[..., :, r]

operator.matmul(X)
==> RHS
Аргументы
rhs Tensor с тем же dtype, что и у этого оператора, и совместимой формой. rhs рассматривается как [пакет] матриц: для каждого набора ведущих измерений два последних измерения задают матрицу. Определение совместимости см. в документации класса.
adjoint Python bool. Если True, решить систему с сопряжённым оператором этого LinearOperator: A^H X = rhs.
adjoint_arg Python bool. Если True, решить A X = rhs^H, где rhs^H — эрмитово сопряжение (транспонирование и комплексное сопряжение).
name Область имён для операций, добавляемых этим методом.
Возвращает
Tensor с формой [...,N, R] и тем же dtype, что и у rhs.
Вызывает исключение
NotImplementedError Если self.is_non_singular или is_square равно False.

solvevec

Посмотреть исходный код

solvevec(
    rhs, adjoint=False, name='solve'
)

Решает одно уравнение, используя наилучшее приближение: A X = rhs.

Возвращённое значение Tensor будет близко к точному решению, если A хорошо обусловлен. В противном случае степень близости будет различаться. Подробности см. в документации класса.

Примеры:

# Make an operator acting like batch matrix A.  Assume A.shape = [..., M, N]
operator = LinearOperator(...)
operator.shape = [..., M, N]

# Solve one linear system for every member of the batch.
RHS = ... # shape [..., M]

X = operator.solvevec(RHS)
# X is the solution to the linear system
# sum_j A[..., :, j] X[..., j] = RHS[..., :]

operator.matvec(X)
==> RHS
Аргументы
rhs Tensor с тем же dtype, что и у этого оператора. rhs рассматривается как [пакет] векторов: для каждого набора ведущих измерений последнее измерение задаёт вектор. Определение совместимости пакетных измерений см. в документации класса.
adjoint Python bool. Если True, решить систему с сопряжённым оператором этого LinearOperator: A^H X = rhs.
name Область имён для операций, добавляемых этим методом.
Возвращает
Tensor с формой [...,N] и тем же dtype, что и у rhs.
Вызывает исключение
NotImplementedError Если self.is_non_singular или is_square равно False.

tensor_rank_tensor

Посмотреть исходный код

tensor_rank_tensor(
    name='tensor_rank_tensor'
)

Ранг (в смысле тензоров) матрицы, соответствующей этому оператору.

Если этот оператор действует как пакетная матрица A с A.shape = [B1,...,Bb, M, N], то возвращается b + 2.

Аргументы
name Имя этого Op.
Возвращает
int32 Tensor, определяется во время выполнения.

to_dense

Посмотреть исходный код

to_dense(
    name='to_dense'
)

Возвращает плотную (пакетную) матрицу, представляющую этот оператор.

trace

Посмотреть исходный код

trace(
    name='trace'
)

След линейного оператора, равный сумме self.diag_part().

Если оператор квадратный, это также сумма собственных значений.

Аргументы
name Имя этого Op.
Возвращает
Форма [B1,...,Bb] Tensor с тем же dtype, что и у self.

__getitem__

Посмотреть исходный код

__getitem__(
    slices
)

__matmul__

Посмотреть исходный код

__matmul__(
    other
)

© 2022 The TensorFlow Authors. All rights reserved.
Licensed under the Creative Commons Attribution License 4.0.
Code samples licensed under the Apache 2.0 License.
https://www.tensorflow.org/api_docs/python/tf/linalg/LinearOperatorCirculant

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API