Elementwise Math
Power and Sqrt
Each element squared in a first loop, then square-rooted in a second loop.
The replay shows squares filling up completely before the second loop starts
building roots, making the two-pass structure explicit.
By hand
With NumPy
a ** 2 raises every element to the power 2 in one call. np.sqrt(a) applies
the square-root ufunc elementwise. Both operations return new arrays without an
explicit loop. The snapshot shows both results in labeled blocks.
naive.py
import math
values = [1, 2, 3, 4, 5, 6]
squares = []
for v in values:
squares.append(v * v)
roots = []
for v in values:
roots.append(round(math.sqrt(v), 2))
print('RESULT:', (squares, roots))
library.py
import numpy as np
values = [1, 2, 3, 4, 5, 6]
a = np.array(values)
squares = a ** 2
roots = np.sqrt(a)
sq_vals = squares.tolist()
rt_vals = [round(v, 2) for v in roots.tolist()]
print(f'squares: shape: {squares.shape} dtype: {squares.dtype} values: {sq_vals}')
print(f'roots: shape: {roots.shape} dtype: {roots.dtype} values: {rt_vals}')
print('RESULT:', (sq_vals, rt_vals))
squares: shape: (6,) dtype: int64 values: [1, 4, 9, 16, 25, 36]
roots: shape: (6,) dtype: float64 values: [1.0, 1.41, 1.73, 2.0, 2.24, 2.45]
RESULT: ([1, 4, 9, 16, 25, 36], [1.0, 1.41, 1.73, 2.0, 2.24, 2.45])
Implementation notes
np.sqrt,np.sin,np.log, and similar functions are ufuncs (universal functions). A ufunc applies a C-level scalar kernel to every element of its input array, returning an array of the same shape — the NumPy equivalent of the hand-written loop here.- For this integer array,
a ** 2keeps the integer dtype (int64), whilenp.sqrt(a)promotes tofloat64.np.sqrtpreserves an input's float dtype — e.g. afloat32input staysfloat32. - Shape, dtype, and values are shown explicitly here because
ndarray.__repr__output varies with NumPy version and print options.