{
  "category": "Math and trigonometry functions",
  "display_name": "MMULT",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.MMULT",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 7 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 7 recorded answers",
    "workbook_corpus": {
      "cells": 7,
      "matched": 7
    }
  },
  "implementations": [
    {
      "commit": "9e1884369a678158dd82d3d74188daf986995e87",
      "flavour": "excel-compatibility-candidate",
      "language": "Rust",
      "name": "CalcFunc",
      "own_code": [
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/math.rs#L227-L232",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/ported/matrix_family.rs#L132-L163"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [
        "catalogue/math::matrix"
      ],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/math.rs#L227-L232",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 7 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.MMULT.json",
  "name": "MMULT",
  "notes": [],
  "schema": "efh.function-reference/v1",
  "section_order": [
    "what-it-does",
    "try-it",
    "quirks",
    "how-to-implement",
    "version-differences",
    "alternatives"
  ],
  "sections": {
    "alternatives": "\n### Relationships\n\n\n- **[MINVERSE](FUNC.MINVERSE.md)** — `MMULT(A, MINVERSE(A))` against [MUNIT](FUNC.MUNIT.md) is the\n  residual test, and the most useful single thing to do with this family. Read the caution on the\n  `MINVERSE` page first: a small residual is *not* a certificate of an accurate inverse.\n- **[MDETERM](FUNC.MDETERM.md)** — det(AB) = det A · det B is a metamorphic identity requiring no\n  oracle and exercising two surfaces at once.\n- **[MUNIT](FUNC.MUNIT.md)** — the multiplicative identity; `MMULT(A, MUNIT(n))` should return *A*\n  unchanged, entry for entry, which is a strong exactness test because every dot product is a sum\n  of exact zeros and one exact copy.\n- **`SUMPRODUCT`** — the single-dot-product function. `MMULT` of a row by a column is\n  `SUMPRODUCT`, and the two are worth comparing: if they accumulate in different orders they will\n  differ in the last bits for the same data, which is a clean, oracle-free way to see accumulation\n  order made visible.\n- **`TRANSPOSE`** — the other shape operator, and the partner in (AB)ᵀ = BᵀAᵀ.\n- **`LINEST` and the regression family** — they form and solve normal equations, which are\n  `MMULT(TRANSPOSE(X), X)` in disguise. That product is famously the step that squares the\n  condition number, which is why forming normal equations explicitly is discouraged.\n\n\n",
    "how-to-implement": "\n### Numerical notes\n\n\n`MMULT` has no pivoting, no division, and no singularity. Every last bit it produces is decided by\none thing: **the order in which the dot products accumulate.**\n\n### The error bound\n\nFor a single dot product of length n computed by sequential accumulation, the standard bound\n(Higham, *Accuracy and Stability of Numerical Algorithms*, chapter 3) is\n\n    |fl(xᵀy) − xᵀy| ≤ γₙ · |x|ᵀ|y|,      γₙ = n·u / (1 − n·u)\n\nwith u the unit roundoff. Two things follow immediately, and they are the whole practical story:\n\n1. **The bound grows with n**, the shared dimension — not with the size of the result. A product of\n   two 2×500 and 500×2 matrices has four entries and five hundred terms each; it is a *harder*\n   computation than a 20×20 by 20×20 product with four hundred entries.\n2. **The bound is in terms of |x|ᵀ|y|, not |xᵀy|.** When the terms of the dot product have mixed\n   signs and largely cancel, the relative error in the answer is unbounded. This is the matrix\n   version of catastrophic cancellation, and it is why differencing data before multiplying is\n   dangerous.\n\n### Accumulation order changes the answer, and every implementation chooses one\n\nFloating-point addition is not associative, so:\n\n- Sequential left-to-right, pairwise, and blocked accumulation give different bits.\n- A **fused multiply-add** contracts `a*b + c` into one rounding instead of two, changing the\n  result — usually improving it, always changing it.\n- Cache-blocked and vectorised kernels reorder the sum by construction; a BLAS `dgemm` and a naive\n  triple loop will not agree to the last bit.\n- Strassen-style fast algorithms change the arithmetic entirely and have a **different, weaker**\n  error bound — they are componentwise less stable, which is why they are rare in general-purpose\n  libraries.\n\nFor a compatibility implementation the consequence is the same one recorded on\n[MINVERSE](FUNC.MINVERSE.md): matching another implementation's bits means matching its **op\ngraph**, not merely its algorithm. `MMULT` is the cleanest place in the family to see that,\nbecause there is nothing else going on.\n\n### If you need accuracy rather than compatibility\n\nThe remedies are well established and none of them is reachable from the worksheet:\n\n- **Compensated (Kahan/Neumaier) summation**, or the Dot2 algorithm of Ogita, Rump and Oishi\n  (\"Accurate sum and dot product\", *SIAM Journal on Scientific Computing* 26(6), 2005), which\n  delivers a result as accurate as one computed in twice the working precision, at a small constant\n  multiple of the cost.\n- **Extended-precision accumulation** of the dot product before rounding once at the end.\n- **Sorting or grouping terms by magnitude** before summing, which helps in the mixed-sign case.\n\nA `natural-best` implementation of `MMULT` should use a compensated dot product; an\n`excel-bitexact` one must not, unless Excel does.\nSee [implementation options](../model/07-implementation-options.md) for why those are different\ndeliverables.\n\n### Overflow\n\n",
    "quirks": "\n### Result and edge cases\n\n\nReturns `Array` of `Number`.\n\n- **A logical argument** and **numeric text** both produce `#VALUE!`, consistent with the\n  documented \"must contain only numbers\" rule for text and undocumented for logicals — the same\n  family-wide refusal noted on [MDETERM](FUNC.MDETERM.md).\n- **An empty range** produces `#VALUE!`, matching the documented empty-cell rule.\n- **Two inline 2×2 arrays** produce the expected 2×2 product.\n\n\n\n### Errors\n\n\n| Error | Documented condition |\n|---|---|\n| `#VALUE!` | \"Any cells are empty or contain text\" |\n| `#VALUE!` | \"The number of columns in array1 is different from the number of rows in array2\" |\n\nError values inside either array propagate; the family declares a reduction error-collapse profile,\nso competing errors are folded rather than surfacing independently\n([coercion and lifting](../model/02-coercion-and-lifting.md)).\n\nMicrosoft's page documents **no `#NUM!`** for `MMULT`, and no overflow condition at all. Given that\na dot product of large entries overflows readily, that is a gap rather than a guarantee.\n\n\n",
    "sources": "- Microsoft, \"MMULT function\" —\n  <https://support.microsoft.com/en-us/office/mmult-function-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb>.\n  Retrieved for this page: the syntax, both array argument descriptions and their permitted forms,\n  the conformability and numbers-only rule, the two `#VALUE!` conditions, the summation form of the\n  product, and the array-formula entry mechanics. The page as retrieved documents no overflow\n  condition.\n- Handbook evidence record `EV-STRUCT-0012` — provisional structural COM probe over the matrix\n  family; pinned values, no count, no named build, and the publication-shape note establishing that\n  the scalar case returns a 1×1 array from the function.\n- N. J. Higham, *Accuracy and Stability of Numerical Algorithms*, 2nd edition, chapter 3 — the\n  dot-product error bound quoted above and the analysis of matrix multiplication; chapter 23 for\n  fast (Strassen-type) algorithms and their weaker bounds.\n- T. Ogita, S. M. Rump and S. Oishi, \"Accurate sum and dot product\", *SIAM Journal on Scientific\n  Computing* 26(6), 2005 — the compensated dot product recommended above.\n- G. H. Golub and C. F. Van Loan, *Matrix Computations*, 4th edition, chapter 1 — blocked\n  formulations and their effect on accumulation order.\n- Handbook, [MDETERM](FUNC.MDETERM.md), [MINVERSE](FUNC.MINVERSE.md), [MUNIT](FUNC.MUNIT.md) — the\n  rest of the family; [implementation options](../model/07-implementation-options.md) — why the\n  accurate and the compatible implementations differ here;\n  [coercion and lifting](../model/02-coercion-and-lifting.md) — error propagation and the reduction\n  fold.\n\n\n",
    "version-differences": "No comparison between specific Excel builds is recorded here yet. This does not establish that all builds behave alike.",
    "what-it-does": "\n`MMULT(array1, array2)` returns the **matrix product** of two matrices.\n\n    (AB)ᵢⱼ = Σₖ₌₁ⁿ aᵢₖ · bₖⱼ\n\nEach entry of the result is a **dot product**: one row of the left matrix against one column of\nthe right. Microsoft's page states the shape rule that makes this well defined:\n\n> \"The number of columns in array1 must be the same as the number of rows in array2, and both\n> arrays must contain only numbers.\"\n\nSo an m×n matrix times an n×p matrix gives an m×p result, and the shared dimension n — the length\nof every dot product — vanishes from the answer's shape.\n\n**Algebraic properties**, all of which readers use and some of which surprise them:\n\n- **Associative**: (AB)C = A(BC). *Not commutative*: AB ≠ BA in general, and the two products may\n  not even have the same shape.\n- **Distributive** over addition, and compatible with scalar multiplication.\n- **[MUNIT](FUNC.MUNIT.md) is the identity**: AI = IA = A at the matching size.\n- **det(AB) = det A · det B** — the bridge to [MDETERM](FUNC.MDETERM.md), and the cheapest\n  metamorphic test available for this family.\n- **(AB)ᵀ = BᵀAᵀ** and **(AB)⁻¹ = B⁻¹A⁻¹** — both reverse the order.\n- Matrix multiplication is exactly function composition for the corresponding linear maps, which\n  is why the order reverses and why it fails to commute.\n\n**Domain and range.** Domain: any conformable pair of numeric matrices. Range: numeric matrices.\nThe product is a polynomial in the entries — continuous, smooth, no poles, no branch cuts. As with\n[MDETERM](FUNC.MDETERM.md), every difficulty here is numerical rather than analytic; unlike\n[MINVERSE](FUNC.MINVERSE.md), there is no singularity to approach.\n\n\n\n### Arguments\n\n\n| Argument | Meaning | Required |\n|---|---|---|\n| `array1` | The left factor. | Yes |\n| `array2` | The right factor. | Yes |\n\nBoth required; the declared arity is exactly two. Microsoft's page notes that both \"can be given\nas cell ranges, array constants, or references\".\n\n**Result shape and entry.** `MMULT` returns an array with the row count of `array1` and the column\ncount of `array2`. Microsoft's page gives the entry mechanics: in current Microsoft 365 the formula\nspills from a single cell, and in earlier versions the output range must be selected first and the\nformula confirmed with Ctrl+Shift+Enter.\n\n\n"
  },
  "signature": {
    "display": "MMULT(array1, array2)",
    "parameters": [
      {
        "description": null,
        "name": "array1",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "array2",
        "optional": false,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 2,
        "min": 2
      },
      "category": "Math and trigonometry functions",
      "classification": {
        "arg_preparation_profile": "RefsVisibleInAdapter",
        "coercion_lift_profile": "Custom",
        "determinism_class": "Deterministic",
        "error_collapse_profile": "ReductionFold",
        "fec_dependency_profile": "RefOnly",
        "host_interaction_class": "None",
        "kernel_signature_class": "Custom",
        "rich_value_usage": "RichBlind",
        "surface_fec_dependency_profile": "RefOnly",
        "thread_safety_class": "SafePure",
        "volatility_class": "NonVolatile"
      },
      "descriptions": {
        "long": null,
        "short": "Matrix multiplication: rows of the first array times columns of the second",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Returns the matrix product of two arrays",
        "microsoft_url": "https://support.microsoft.com/en-us/office/mmult-function-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "MMULT"
        },
        {
          "locale": "bg-BG",
          "name": "MMULT"
        },
        {
          "locale": "cs-CZ",
          "name": "MMULT"
        },
        {
          "locale": "da-DK",
          "name": "MPRODUKT"
        },
        {
          "locale": "de-DE",
          "name": "MMULT"
        },
        {
          "locale": "el-GR",
          "name": "MMULT"
        },
        {
          "locale": "en-AU",
          "name": "MMULT"
        },
        {
          "locale": "en-GB",
          "name": "MMULT"
        },
        {
          "locale": "en-US",
          "name": "MMULT"
        },
        {
          "locale": "es-ES",
          "name": "MMULT"
        },
        {
          "locale": "et-EE",
          "name": "MMULT"
        },
        {
          "locale": "fi-FI",
          "name": "MKERRO"
        },
        {
          "locale": "fr-FR",
          "name": "PRODUITMAT*"
        },
        {
          "locale": "he-IL",
          "name": "MMULT"
        },
        {
          "locale": "hr-HR",
          "name": "MMULT"
        },
        {
          "locale": "hu-HU",
          "name": "MSZORZAT"
        },
        {
          "locale": "id-ID",
          "name": "MMULT"
        },
        {
          "locale": "it-IT",
          "name": "MMULT"
        },
        {
          "locale": "ja-JP",
          "name": "MMULT"
        },
        {
          "locale": "ko-KR",
          "name": "MMULT"
        },
        {
          "locale": "lt-LT",
          "name": "MMULT"
        },
        {
          "locale": "lv-LV",
          "name": "MMULT"
        },
        {
          "locale": "nb-NO",
          "name": "MMULT"
        },
        {
          "locale": "nl-NL",
          "name": "PRODUCTMAT"
        },
        {
          "locale": "pl-PL",
          "name": "MACIERZ.ILOCZYN"
        },
        {
          "locale": "pt-BR",
          "name": "MMULT"
        },
        {
          "locale": "pt-PT",
          "name": "MATRIZ.MULT"
        },
        {
          "locale": "ro-RO",
          "name": "MMULT"
        },
        {
          "locale": "ru-RU",
          "name": "МУМНОЖ"
        },
        {
          "locale": "sk-SK",
          "name": "MMULT"
        },
        {
          "locale": "sl-SI",
          "name": "MMULT"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "MMULT"
        },
        {
          "locale": "sv-SE",
          "name": "MMULT"
        },
        {
          "locale": "th-TH",
          "name": "MMULT"
        },
        {
          "locale": "tr-TR",
          "name": "DÇARP işlevi"
        },
        {
          "locale": "uk-UA",
          "name": "MMULT"
        },
        {
          "locale": "vi-VN",
          "name": "MMULT"
        },
        {
          "locale": "zh-CN",
          "name": "MMULT"
        },
        {
          "locale": "zh-HK",
          "name": "MMULT"
        },
        {
          "locale": "zh-TW",
          "name": "MMULT"
        }
      ],
      "signature": {
        "display": "MMULT(array1, array2)",
        "parameters": [
          {
            "description": null,
            "name": "array1",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "array2",
            "optional": false,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "MMULT",
      "version_marker": null,
      "xlcall": {
        "code": "165",
        "symbol": "xlfMmult"
      }
    },
    "microsoft": "https://support.microsoft.com/en-us/office/mmult-function-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb"
  },
  "try_it": {
    "formula": "=MMULT({1,2;3,4},{1;2})"
  },
  "url": "https://handbook.dnacalc.com/functions/MMULT.html",
  "variants": [],
  "versions": []
}