GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • General Principles
  • API Reference
  • General Operations
  • Arithmetic Operations
  • Tile Operations
  • Math Operations
  • Matrix Multiplication
  • Reductions and Scans
  • Memory Operations
  • Extents and Strides
  • Tile
  • Layout Mappings
  • Tensor Span
  • Partition View

This view

  • Original page
  • New address

GeezerVison is showing docs.nvidia.com — Matrix Multiplication — CUDA Tile C++ API Reference 13.5 documentation

Matrix Multiplication

Matrix multiplication of tiles is supported through the ct::matmul(), ct::mma(), and ct::scaled_mma() functions. The ct::mma and ct::scaled_mma functions accept an accumulator argument that is added to the multiplication result. These functions can perform batched matrix multiplies when the arguments have rank \(3\).

The result of the matrix multiplication operations is an approximation of an infinitely precise mathematical computation. The exhaustive numerical behavior for these operation is not currently specified. Information about the error bounds, rounding modes, and the behavior of non-finite, signed zero, and subnormal arguments may be absent.

Additionally, there are unspecified scenarios where overflow may occur while performing matrix multiplication of integral operands. In these scenarios, the result of the operation is unspecified.

cuda::tiles::mma_compatible

template<typename L, typename R, typename A>
concept mma_compatible = ct::numeric_tile<L> && ct::numeric_tile<R> && ct::numeric_tile<A> && /* atomic constraint */;

Indicates whether the types \(L\) (the left matrix), \(R\) (the right matrix) and \(A\) (the accumulator matrix) can participate in a matrix multiply accumulate operation.

The types \(L\), \(R\) and \(A\) can participate in a matrix multiply accumulate if

  1. The element types of \(L\) and \(R\) are integral scalars of the same bitwidth or they are floating point scalars of the same conversion rank

  2. The element types of \(L\), \(R\) and \(A\) are selected from a row in the following table:

    Element Type of \(L\) and \(R\)

    Element type of \(A\)

    \(8\) bit integral types

    \(32\) bit signed integral types

    • __nv_fp8_e4m3

    • __nv_fp8_e5m2

    • __half

    • __half

    • float

    • __nv_bfloat16

    • __nv_tf32

    • float

    • float

    • double

    • double

  3. All of \(L\), \(R\) and \(A\) have the same rank and the rank is either \(2\) or \(3\).

  4. In the case where the rank is \(2\), the shapes of \(L\), \(R\) and \(A\) are taken from the table for some integers \(N\), \(K\) and \(M\):

    Shape of \(L\)

    Shape of \(R\)

    Shape of \(A\)

    \(N \times K\)

    \(K \times M\)

    \(N \times M\)

  5. In the case where the rank is \(3\), the shapes of \(L\), \(R\) and \(A\) are taken from the table for some integers \(A\), \(B\), \(C\), \(N\), \(K\) and \(M\):

    Shape of \(L\)

    Shape of \(R\)

    Shape of \(A\)

    \(A \times N \times K\)

    \(B \times K \times M\)

    \(C \times N \times M\)

    Additionally \(A\) and \(B\) satisfy the following predicates:

    \[\begin{split}\begin{cases} A = C & \text{or} \\ A = 1 & \end{cases} \quad \quad \begin{cases} B = C & \text{or} \\ B = 1 & \end{cases}\end{split}\]

cuda::tiles::matmul_compatible

template<typename L, typename R>
concept matmul_compatible = ct::numeric_tile<Lhs> && ct::numeric_tile<Rhs> && /* atomic constraint */;

Indicates whether the types \(L\) and \(R\) may participate in a matrix multiply operation.

The types \(L\) and \(R\) may participate in a matrix multiply operation if:

  1. The element types of \(L\) and \(R\) are integral scalars whose bitwidth is \(8\) or they are floating point scalars of the same conversion rank.

  2. \(L\) and \(R\) have the same rank and that rank is either \(2\) or \(3\).

  3. In the case where the rank is \(2\), the shapes of \(L\) and \(R\) are taken from the table below for some integers \(N\), \(K\) and \(M\):

    Shape of \(L\)

    Shape of \(R\)

    \(N \times K\)

    \(K \times M\)

    The hypothetical ct::extents specialization of dimensions \(N \times M\) must be tile compatible.

  4. In the case where the rank is \(3\), the shapes of \(L\) and \(R\) are taken from the table below for some integers \(A\), \(B\), \(N\), \(K\) and \(M\):

    Shape of \(L\)

    Shape of \(R\)

    \(A \times N \times K\)

    \(B \times K \times M\)

    Additionally, either \(A = B\) or at least one of them is equal to \(1\). The hypothetical ct::extents specialization whose dimensions are \(\operatorname{max}(A, B) \times N \times M\) must be tile compatible.

cuda::tiles::matmul_result_t

template<ct::numeric_tile L, ct::numeric_tile R>
requires ct::matmul_compatible<L, R>
using matmul_result_t = /* see below */;

Yields the result type of performing matrix multiplication on operands of types \(L\) and \(R\)

The result type is a specialization of ct::tile. The element type of the result is determined by the element types of \(L\) and \(R\) according to the following table:

\(L\) and \(R\) element type

Result Element Type

8 bit integral types

int32_t

  • __nv_fp8_e4m3

  • __nv_fp8_e5m2

  • __half

__half

  • __nv_bfloat16

  • __nv_tf32

  • float

float

  • double

double

The shape of the result depends on the rank of \(L\) and \(R\):

  1. If \(L\) and \(R\) have rank is \(2\) with shapes \(N \times K\) and \(K \times M\) respectively, the result shape is \(N \times M\).

  2. If \(L\) and \(R\) have rank \(3\) with shapes \(A \times N \times K\) and \(B \times K \times M\), the result shape is \(C \times N \times M\) where \(C\) is the larger of \(A\) and \(B\).

Note

The result element type for matmul cannot be configured. To use a different element type, use ct::mma() with a zeroed accumulator.

cuda::tiles::mma

template<ct::numeric_tile L, ct::numeric_tile R, ct::numeric_tile A>
requires ct::mma_compatible<L, R, A>
__tile__ A mma(L lhs, R rhs, A acc) noexcept;

Performs matrix multiply on lhs and rhs and adds the result to acc. For rank \(3\) arguments, the matrix multiply accumulate is performed for each set of matrices along the first dimension of the operands.

If the operands have rank \(3\), the lhs undergoes broadcast conversion to the shape \(A_0 \times L_1 \times L_2\) and rhs undergoes broadcast conversion to the shape \(A_0 \times R_1 \times R_2\).

Let \(a\), \(b\) and \(c\) denote the converted operands lhs, rhs, and acc respectively.

For rank \(2\) arguments, the result is an approximation of the matrix \(r\) determined by

\[r(i_0, i_1) = \sum_{k = 0}^{k < L_1} a(i_0, k) \cdot b(k, i_1) + c(i_0, i_1)\]

For rank \(3\) arguments, the result is an approximation of the matrix \(r\) determined by

\[r(i_0, i_1, i_2) = \sum_{k = 0}^{k < L_2} a(i_0, i_1, k) \cdot b(i_0, k, i_2) + c(i_0, i_1, i_2)\]

Example

The following example shows a matrix multiply accumulate computation for rank \(2\) arguments:

namespace ct = ::cuda::tiles;
using i32x2x4 = ct::tile<int, ct::shape<2, 4>>;
using i32x4x2 = ct::tile<int, ct::shape<4, 2>>;
using i32x2x2 = ct::tile<int, ct::shape<2, 2>>;

auto lhs = ct::element_cast<float>(ct::iota<i32x2x4>());
auto rhs = ct::element_cast<float>(ct::iota<i32x4x2>());
auto acc = ct::element_cast<float>(ct::iota<i32x2x2>());

auto r = ct::mma(lhs, rhs, acc);
\[\begin{split}\begin{pmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ \end{pmatrix} \times \begin{pmatrix} 0 & 1 \\ 2 & 3 \\ 4 & 5 \\ 6 & 7 \end{pmatrix} + \begin{pmatrix} 0 & 1 \\ 2 & 3 \end{pmatrix} \rightarrow \begin{pmatrix} 28 & 35 \\ 78 & 101 \end{pmatrix}\end{split}\]

Example

The following example shows a batched matrix multiply accumulate computation for rank \(3\) arguments:

namespace ct = ::cuda::tiles;
using i32x1x2x4 = ct::tile<int, ct::shape<1, 2, 4>>;
using i32x1x4x2 = ct::tile<int, ct::shape<1, 4, 2>>;
using i32x1x2x2 = ct::tile<int, ct::shape<1, 2, 2>>;

auto lhs = ct::element_cast<float>(
             ct::cat<0>(ct::iota<i32x1x2x4>(), ct::iota<i32x1x2x4>()));
auto rhs = ct::element_cast<float>(
             ct::cat<0>(ct::iota<i32x1x4x2>(), -ct::iota<i32x1x4x2>()));
auto acc = ct::element_cast<float>(
             ct::cat<0>(ct::iota<i32x1x2x2>(), -ct::iota<i32x1x2x2>()));

auto r = ct::mma(lhs, rhs, acc);
\[\begin{split}\begin{pmatrix} \begin{pmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ \end{pmatrix} \\ \begin{pmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ \end{pmatrix} \end{pmatrix} \times \begin{pmatrix} \begin{pmatrix} 0 & 1 \\ 2 & 3 \\ 4 & 5 \\ 6 & 7 \end{pmatrix} \\ \begin{pmatrix} 0 & -1 \\ -2 & -3 \\ -4 & -5 \\ -6 & -7 \end{pmatrix} \end{pmatrix} + \begin{pmatrix} \begin{pmatrix} 0 & 1 \\ 2 & 3 \end{pmatrix} \\ \begin{pmatrix} 0 & -1 \\ -2 & -3 \end{pmatrix} \end{pmatrix} \rightarrow \begin{pmatrix} \begin{pmatrix} 28 & 35 \\ 78 & 101 \end{pmatrix} \\ \begin{pmatrix} -28 & -35 \\ -78 & -101 \end{pmatrix} \end{pmatrix}\end{split}\]

cuda::tiles::matmul

template<ct::numeric_tile L, ct::numeric_tile R>
requires ct::matmul_compatible<L, R>
__tile__ ct::matmul_result_t<L, R> matmul(L lhs, R rhs) noexcept;

Performs matrix multiply on lhs and rhs. For rank \(3\) arguments, the matrix multiply is performed for each set of matrices along the first dimension of the operands.

If the operands have rank \(3\), the lhs undergoes broadcast conversion to the shape \(\operatorname{max}(L_0, R_0) \times L_1 \times L_2\) and rhs undergoes broadcast conversion to the shape \(\operatorname{max}(L_0, R_0) \times R_1 \times R_2\).

Let \(a\) and \(b\) denote the converted operands lhs and rhs respectively.

For rank \(2\) arguments, the result is an approximation of the matrix \(r\) determined by

\[r(i_0, i_1) = \sum_{k = 0}^{k < L_1} a(i_0, k) \cdot b(k, i_1)\]

For rank \(3\) arguments, the result is an approximation of the matrix \(r\) determined by

\[r(i_0, i_1, i_2) = \sum_{k = 0}^{k < L_2} a(i_0, i_1, k) \cdot b(i_0, k, i_2)\]

Example

The following example shows a matrix multiply computation for rank \(2\) arguments:

namespace ct = ::cuda::tiles;
using i32x2x4 = ct::tile<int, ct::shape<2, 4>>;
using i32x4x2 = ct::tile<int, ct::shape<4, 2>>;

auto lhs = ct::element_cast<float>(ct::iota<i32x2x4>());
auto rhs = ct::element_cast<float>(ct::iota<i32x4x2>());

auto r = ct::matmul(lhs, rhs);
\[\begin{split}\begin{pmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ \end{pmatrix} \times \begin{pmatrix} 0 & 1 \\ 2 & 3 \\ 4 & 5 \\ 6 & 7 \end{pmatrix} \rightarrow \begin{pmatrix} 28 & 34 \\ 76 & 98 \end{pmatrix}\end{split}\]

Example

The following example shows a batched matrix multiply computation for rank \(3\) arguments:

namespace ct = ::cuda::tiles;
using i32x1x2x4 = ct::tile<int, ct::shape<1, 2, 4>>;
using i32x1x4x2 = ct::tile<int, ct::shape<1, 4, 2>>;

auto lhs = ct::element_cast<float>(ct::cat<0>(ct::iota<i32x1x2x4>(), ct::iota<i32x1x2x4>()));
auto rhs = ct::element_cast<float>(ct::cat<0>(ct::iota<i32x1x4x2>(), -ct::iota<i32x1x4x2>()));

auto r = ct::matmul(lhs, rhs);
\[\begin{split}\begin{pmatrix} \begin{pmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ \end{pmatrix} \\ \begin{pmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ \end{pmatrix} \end{pmatrix} \times \begin{pmatrix} \begin{pmatrix} 0 & 1 \\ 2 & 3 \\ 4 & 5 \\ 6 & 7 \end{pmatrix} \\ \begin{pmatrix} 0 & -1 \\ -2 & -3 \\ -4 & -5 \\ -6 & -7 \end{pmatrix} \end{pmatrix} \rightarrow \begin{pmatrix} \begin{pmatrix} 28 & 34 \\ 76 & 98 \end{pmatrix} \\ \begin{pmatrix} -28 & -34 \\ -76 & -98 \end{pmatrix} \end{pmatrix}\end{split}\]

cuda::tiles::scaled_mma CUDA 13.5

template<ct::numeric_tile L, ct::numeric_tile LS, ct::numeric_tile R, ct::numeric_tile RS, ct::numeric_tile A>
requires /* atomic constraint */
__tile__ Acc scaled_mma(L lhs, LS lhsScale, R rhs, RS rhsScale, A acc) noexcept;

Performs block scaled matrix multiply and accumulate on the operands lhs, rhs, and acc using the scale factors specified in lhsScale and rhsScale. For rank \(3\) operands, the matrix multiply is batched along the first dimension and all inputs are broadcasted to match the batch length of the accumulator.

The left and right matrices are subdivided into vectors. Each vector is associated with a separate scaling factor which is used to scale the values in the vector before performing the matrix multiply. The number of elements which correspond to a single scaling factor is called the vector length.

For rank \(2\) arguments, the result is an approximation of the matrix \(r\) determined by

\[r(i_0, i_1) = \sum_{k = 0}^{k < L_1} \text{lhs}(i_0, k) \cdot \text{lhsScale}\left(i_0, \left\lfloor\frac{k}{V}\right\rfloor\right) \cdot \text{rhs}(k, i_1) \cdot \text{rhsScale}\left(\left\lfloor\frac{k}{V}\right\rfloor, i_1\right) + \text{acc}(i_0, i_1)\]

where \(V = \frac{L_1}{LS_1}\) indicates the vector length.

For rank \(3\) operands, the inputs undergo broadcast conversion as follows:

  • lhs is broadcasted to shape \(A_0 \times L_1 \times L_2\)

  • lhsScale is broadcasted to shape \(A_0 \times LS_1 \times LS_2\)

  • rhs is broadcasted to shape \(A_0 \times R_1 \times R_2\)

  • rhsScale is broadcasted to shape \(A_0 \times RS_1 \times RS_2\)

The result is an approximation of the matrix \(r\) determined by the following equation on the converted operands:

\[r(i_0, i_1, i_2) = \sum_{k = 0}^{k < L_2} \text{lhs}(i_0, i_1, k) \cdot \text{lhsScale}\left(i_0, i_1, \left\lfloor\frac{k}{V}\right\rfloor\right) \cdot \text{rhs}(i_0, k, i_2) \cdot \text{rhsScale}\left(i_0, \left\lfloor\frac{k}{V}\right\rfloor, i_2\right) + \text{acc}(i_0, i_1, i_2)\]

where \(V = \frac{L_2}{LS_2}\) indicates the vector length.

The atomic constraint validates the following:

Shape Constraints

All arguments have the same rank and the rank is \(2\) or \(3\).

For rank \(2\) operands, the shape of each operand has the following format:

Shape of \(L\)

Shape of \(LS\)

Shape of \(R\)

Shape of \(RS\)

Shape of \(A\)

\(N \times S \cdot V\)

\(N \times S\)

\(S \cdot V \times M\)

\(S \times M\)

\(N \times M\)

for some integers \(N\), \(M\), \(S\), and \(V\). The value of \(V\) (the vector size) must be consistent with the type constraint table below.

For rank \(3\) operands, the shape of each operand has the following format:

Shape of \(L\)

Shape of \(LS\)

Shape of \(R\)

Shape of \(RS\)

Shape of \(A\)

\(B_0 \times N \times S \cdot V\)

\(B_1 \times N \times S\)

\(B_2 \times S \cdot V \times M\)

\(B_3 \times S \times M\)

\(B \times N \times M\)

For some integers \(N\), \(M\), \(S\), \(V\), \(B_0\), \(B_1\), \(B_2\), \(B_3\), and \(B\). The value of \(V\) (the vector size) must be consistent with the type constraint table below. For each \(B_i\), either \(B_i = B\) or \(B_i = 1\).

Type Constraints

The element types of \(L\) and \(R\) must be the same. The element types of \(LS\) and \(RS\) must be the same. The element type of \(A\) must be float.

The element types and vector size, \(V\), must be selected from one of the rows in the following table:

Element Type of \(L\)/\(R\)

Element Type of \(LS\)/\(RS\)

Vector Size \(V\)

__nv_fp8_e4m3

__nv_fp8_e8m0

\(32\)

__nv_fp8_e5m2

__nv_fp8_e8m0

\(32\)

__nv_fp4_e2m1

__nv_fp8_e8m0

\(16\) or \(32\)

__nv_fp4_e2m1

__nv_fp8_e4m3

\(16\)

__nv_fp4_e2m1

__nv_fp8_ue5m3

\(16\) or \(32\)

Example

The following example shows block scaled matrix multiply with a vector length of \(32\).

The lhs matrix is \(2 \times 64\) and has a total of four scale factors corresponding to the four \(1 \times 32\) quadrants of lhs. The rhs matrix is \(64 \times 2\) and has a total of four scale factors corresponding to the four \(32 \times 1\) quadrants of rhs.

namespace ct = ::cuda::tiles;
using e5m2 = __nv_fp8_e5m2;
using e8m0 = __nv_fp8_e8m0;

e5m2 lhsData[2][64] = {};
lhsData[0][0]  = e5m2(2.0f);   lhsData[0][1]  = e5m2(3.0f);
lhsData[1][0]  = e5m2(4.0f);   lhsData[1][1]  = e5m2(6.0f);
lhsData[0][32] = e5m2(0.5f);   lhsData[0][33] = e5m2(2.0f);
lhsData[1][32] = e5m2(-1.0f);  lhsData[1][33] = e5m2(4.0f);

e8m0 lhsScaleData[2][2] = {
    {e8m0(1.0f), e8m0(2.0f)},
    {e8m0(4.0f), e8m0(8.0f)}
};

e5m2 rhsData[64][2] = {};
rhsData[0][0]  = e5m2(-3.0f);  rhsData[0][1]  = e5m2(2.0f);
rhsData[1][0]  = e5m2(0.5f);   rhsData[1][1]  = e5m2(1.5f);
rhsData[32][0] = e5m2(-2.0f);  rhsData[32][1] = e5m2(2.0f);
rhsData[33][0] = e5m2(2.0f);   rhsData[33][1] = e5m2(0.5f);

e8m0 rhsScaleData[2][2] = {
    {e8m0(16.0f), e8m0(1.0f)},
    {e8m0(0.5f),  e8m0(4.0f)}
};

float accData[2][2] = {
  {3.0f, -9.0f},
  {5.0f, 7.0f}
};

using i32x2x64 = ct::tile<int, ct::shape<2, 64>>;
using i32x2x2 = ct::tile<int, ct::shape<2, 2>>;
using i32x64x2 = ct::tile<int, ct::shape<64, 2>>;

auto lhs = ct::load(&lhsData[0][0] + ct::iota<i32x2x64>());
auto lhsScale = ct::load(&lhsScaleData[0][0] + ct::iota<i32x2x2>());
auto rhs = ct::load(&rhsData[0][0] + ct::iota<i32x64x2>());
auto rhsScale = ct::load(&rhsScaleData[0][0] + ct::iota<i32x2x2>());
auto acc = ct::load(&accData[0][0] + ct::iota<i32x2x2>());

auto r = ct::scaled_mma(lhs, lhsScale, rhs, rhsScale, acc);

The lhs argument before scaling:

\[\begin{split}\text{lhs} = \begin{matrix} \underbrace{\begin{array}{|c} \fcolorbox{black}{cyan}{$\begin{matrix} 2 & 3 & 0 & \cdots & 0 \end{matrix}$} \\ \fcolorbox{black}{violet}{$\begin{matrix} 4 & 6 & 0 & \cdots & 0 \end{matrix}$} \end{array}}_{\text{Block 0}} & \underbrace{\begin{array}{c|} \fcolorbox{black}{yellow}{$\begin{matrix}0.5 & 2 & 0 & \cdots & 0\end{matrix}$} \\ \fcolorbox{black}{lime}{$\begin{matrix} -1 & 4 & 0 & \cdots & 0 \end{matrix}$}\end{array}}_{\text{Block 1}} \end{matrix}\end{split}\]
\[\begin{split}\text{lhsScale} = \begin{matrix} \underbrace{\begin{array}{|c} \fcolorbox{black}{cyan}{1} \\ \fcolorbox{black}{violet}{4} \end{array}}_{\text{Block 0}} & \underbrace{\begin{array}{c|} \fcolorbox{black}{yellow}{2} \\ \fcolorbox{black}{lime}{8} \end{array}}_{\text{Block 1}} \end{matrix}\end{split}\]

The rhs argument before scaling:

\[\begin{split}\text{rhs} = \begin{matrix} \begin{array}{|cc|} \fcolorbox{black}{cyan}{$\begin{matrix} -3 \\ 0.5 \\ 0 \\ \vdots \\ 0 \end{matrix}$} & \fcolorbox{black}{yellow}{$\begin{matrix} 2 \\ 1.5 \\ 0 \\ \vdots \\ 0 \end{matrix}$} \end{array} & \left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0 \end{matrix}}\right\} \text{Block 0} \\ \begin{array}{|cc|} \fcolorbox{black}{violet}{$\begin{matrix} -2 \\ 2 \\ 0 \\ \vdots \\ 0 \end{matrix}$} & \fcolorbox{black}{lime}{$\begin{matrix} 2 \\ 0.5 \\ 0 \\ \vdots \\ 0 \end{matrix}$} \end{array} & \left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0 \end{matrix}}\right\}\text{Block 1} \end{matrix} \quad \quad \quad \text{rhsScale} = \begin{matrix} \begin{array}{|cc|} \fcolorbox{black}{cyan}{$\hphantom{.}16$} & \fcolorbox{black}{yellow}{1} \end{array} & \left.\vphantom{\begin{matrix} 0 \end{matrix}}\right\} \text{Block 0} \\ \begin{array}{|cc|} \fcolorbox{black}{violet}{$0.5$} & \fcolorbox{black}{lime}{4} \end{array} & \left.\vphantom{\begin{matrix} 0 \end{matrix}}\right\}\text{Block 1} \end{matrix}\end{split}\]

The matrix multiply accumulate after scaling is performed:

\[\begin{split}\begin{matrix} \underbrace{\begin{array}{|ccccc} \hphantom{0}2 & \hphantom{0}3 & 0 & \cdots & 0 \\ 16 & 24 & 0 & \cdots & 0 \end{array}}_{\text{Block 0}} & \underbrace{\begin{array}{ccccc|} \hphantom{0}1 & \hphantom{0}4 & 0 & \cdots & 0 \\ -8 & 32 & 0 & \cdots & 0 \end{array}}_{\text{Block 1}} \end{matrix} \quad \times \quad \begin{matrix} \begin{array}{|cc|} -48 & 2 \\ 8 & 1.5 \\ 0 & 0 \\ \vdots & \vdots \\ 0 & 0 \end{array} & \left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0 \end{matrix}}\right\} \text{Block 0} \\ \begin{array}{|cc|} -1\hphantom{0} & \hphantom{0}8\hphantom{.} \\ 1 & 2 \\ 0 & 0 \\ \vdots & \vdots \\ 0 & 0 \end{array} & \left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0 \end{matrix}}\right\}\text{Block 1} \end{matrix} \quad + \quad \begin{pmatrix} 3 & -9 \\ 5 & 7 \end{pmatrix}\end{split}\]
Privacy Policy | Your Privacy Choices | Terms of Service | Accessibility | Corporate Policies | Product Security | Contact
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.