What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

For NumPy arrays, multiply matrices with A @ B (the clearest choice), np.matmul(A, B) (the explicit function form), or np.dot(A, B) (equivalent for two-dimensional inputs). Do not use A * B for a matrix product: NumPy reserves * for element-by-element multiplication.

If A has shape (m, n), B must have shape (n, p), and the result has shape (m, p). The important difference appears with vectors and arrays above two dimensions, where matmul broadcasts stacks of matrices while dot follows a different axis-contraction rule.

Set up a small, reproducible example

Install NumPy in the environment that will run your script:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
python -m pip install numpy

Then create two compatible two-dimensional arrays:

import numpy as np

A = np.array([[1, 2],
              [3, 4]])
B = np.array([[5, 6],
              [7, 8]])

Both arrays have shape (2, 2), so their product is defined. Every method below returns:

[[19 22]
 [43 50]]

That result comes from row-by-column products: the top-left value is 1*5 + 2*7, not an elementwise pairing of 1 with 5 alone.

1. Use Python’s @ operator

The shortest and most readable NumPy spelling is:

C = A @ B
print(C)
print(C.shape)  # (2, 2)

PEP 465 introduced @ and @= in Python 3.5. Python defines the operator protocol; array libraries decide what the operation means for their own types. NumPy ndarrays implement @ with matmul semantics.

When @ is the best choice

  • Use it in ordinary numerical code where the reader can see that both operands are arrays.
  • Use it for chained products such as prediction = X @ weights + bias.
  • Use @= when you intentionally want the left-hand variable replaced by its matrix product, for example A @= B. Check shapes first, because the assignment still has to fit the resulting array.

Shape validation

For two-dimensional arrays, the inner dimensions must match. This succeeds:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
A = np.ones((3, 4))
B = np.ones((4, 2))
C = A @ B                 # shape (3, 2)

This fails with a dimension-mismatch error because 3 and 5 do not match:

A = np.ones((2, 3))
B = np.ones((5, 4))
C = A @ B

2. Call np.matmul explicitly

np.matmul(A, B) performs the same operation as A @ B for NumPy arrays:

C = np.matmul(A, B)
print(C)

Why choose the function form?

  • It makes the operation obvious in code that is teaching array shapes or accepting an operation as a callable.
  • It exposes function arguments such as out= when you need NumPy to write into a preallocated output array.
  • It is the natural form when composing NumPy functions or reading code that already uses function calls consistently.

NumPy’s documented recommendation for a two-dimensional matrix product is @ or matmul. They share the same matrix and batch rules, so select between them for readability rather than numerical meaning.

Stacks of matrices and broadcasting

For arrays with three or more dimensions, matmul treats the final two axes as each matrix and broadcasts the earlier axes as batch dimensions. For example:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
A = np.ones((7, 2, 3))
B = np.ones((1, 3, 4))
C = np.matmul(A, B)
print(C.shape)  # (7, 2, 4)

Each of the seven 2x3 matrices in A is multiplied by the compatible 3x4 matrix in B; the leading dimension of B broadcasts from 1 to 7. If batch dimensions cannot broadcast, NumPy raises an error rather than silently reshaping your data.

Vectors are a special case

matmul handles one-dimensional operands by temporarily treating them as a row or column for the calculation and then removing that temporary axis. Thus a shape (3,) vector multiplied by a shape (3, 2) matrix produces shape (2,), while a shape (2, 3) matrix multiplied by a shape (3,) vector produces shape (2,). A zero-dimensional scalar is not a valid operand for matmul; use ordinary scalar multiplication for that case.

3. Use np.dot

For two-dimensional arrays, this is also a matrix product:

C = np.dot(A, B)
print(C)

Consequently, existing code that uses dot with two-dimensional arrays is mathematically correct. NumPy documentation nevertheless favors @ or matmul when you specifically mean matrix multiplication, because dot changes its contraction behavior as dimensionality increases.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The higher-dimensional difference

For inputs above two dimensions, dot contracts the last axis of its first argument with the second-to-last axis of its second argument. It then keeps the remaining axes in a result whose layout follows that rule. For example:

a = np.ones((2, 3, 4))
b = np.ones((5, 4, 6))
c = np.dot(a, b)
print(c.shape)  # (2, 3, 5, 6)

That is not the same interpretation as a stack of matrices. matmul would regard the last two axes of each operand as the matrix dimensions and try to broadcast the leading batch dimensions. In this example, batch dimensions (2,) and (5,) cannot broadcast, so a @ b raises a shape error instead of returning (2, 3, 5, 6).

For batched linear algebra, choose @ or matmul and make the batch axes explicit. Reserve dot for code whose contraction rule is intentional, or for straightforward two-dimensional products where you are maintaining an existing API.

Do not confuse * with a matrix product

NumPy’s multiplication operator is elementwise:

A * B
# array([[ 5, 12],
#        [21, 32]])

Each position is multiplied independently. Elementwise multiplication can broadcast compatible shapes, but it never performs row-by-column summation. Use * for masks, per-feature scaling, and other corresponding-element operations; use @, matmul, or (in the two-dimensional case) dot for matrix algebra.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Comparison at a glance

Expression 2-D inputs Higher-dimensional inputs Best use
A @ B Matrix product Broadcasted stack-of-matrices semantics Concise everyday NumPy code
np.matmul(A, B) Matrix product Same semantics as @ Explicit calls and shape-focused code
np.dot(A, B) Matrix product Contracts last axis of A with second-to-last axis of B Two-dimensional legacy code or deliberate dot contraction
A * B Elementwise multiplication Elementwise broadcasting Corresponding entries, not matrix algebra

Debugging matrix-multiplication errors

“Mismatch in core dimension” or incompatible dimensions

  • Print both shapes immediately: print(A.shape, B.shape).
  • For 2-D operands, verify A.shape[1] == B.shape[0].
  • For batched operands, verify the final two axes are (m, n) and (n, p), then check that all leading batch axes broadcast.
  • Do not “fix” an error with a blind transpose. Transpose only when your data’s mathematical orientation is actually reversed.

Unexpected four-dimensional output from dot

This is usually the documented high-dimensional dot contraction, not corruption. Replace it with @ or np.matmul when your arrays represent batches of matrices, and arrange the batch and matrix axes accordingly.

Unexpected numbers

  • Check whether you wrote * when you intended @.
  • Inspect dtypes with A.dtype and B.dtype. Integer arrays remain integer arrays, so very large integer products can overflow the selected integer type.
  • Verify that rows and columns represent the dimensions you think they do; a valid product can still be mathematically mis-oriented.

Python lists do not behave like ndarrays

Convert nested lists before multiplying:

A = np.asarray([[1, 2], [3, 4]])
B = np.asarray([[5, 6], [7, 8]])
C = A @ B

Keeping the conversion at the input boundary gives you predictable shape checks and NumPy’s matrix operators throughout the rest of the program.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, memory, and reproducibility

  • Choose the operator by semantics first; the three forms are interfaces to the same NumPy matrix-multiplication behavior for the cases described above, not separate mathematical algorithms.
  • Large products allocate a result array. If you repeatedly multiply arrays in a loop, plan memory use and consider the out= argument of np.matmul when its documented constraints fit your code.
  • Keep shapes explicit at API boundaries and assert them in tests. A small check such as assert A.shape[-1] == B.shape[-2] catches many data-pipeline mistakes before an expensive product.
  • Record NumPy and Python versions for reproducible numerical work. Exact floating-point results can vary slightly across platforms and numerical backends even when the operation and inputs are the same.

Or skip the browser setup

If what you actually need is a clean image or PDF of a documentation page, ScreenshotNeo provides a single HTTP request instead of a locally managed browser. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms along with newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

For example, this cURL request captures the NumPy site as WebP (see the ScreenshotNeo API documentation for all options):

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://numpy.org -o shot.webp

The equivalent Python call is:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://numpy.org"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

From Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://numpy.org' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo includes full-page lazy-image loading, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper and margin controls, custom CSS and JavaScript, click and wait actions, request/resource blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, which can simplify migration.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing provides two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to start without a card.

FAQ

Can I multiply matrices without NumPy?

Yes, you can write nested Python loops, but you must implement dimension checks, accumulation, and type handling yourself. NumPy is the practical choice for array-based numerical programs.

Should new two-dimensional code use dot?

It works, but current NumPy guidance favors @ or matmul because their names and behavior communicate matrix multiplication more directly, especially when code later grows to batched arrays.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I check a product’s dimensions before calculating it?

Inspect A.ndim and A.shape, then validate the inner matrix axes and any batch-axis broadcasting. Printing shapes in a failing test is often more useful than printing full array contents.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.