import numpy as np

def relu(z):    return np.maximum(0, z)
def sigmoid(z):  return 1 / (1 + np.exp(-z))

def forward_pass(x, W1, b1, W2, b2):
    Z1 = x @ W1 + b1
    A  = relu(Z1)
    Z2 = A @ W2 + b2
    Q  = sigmoid(Z2)
    return Z1, A, Z2, Q

# ---- matches the worked example in the lecture (2 -> 2 -> 1 network) ----
x  = np.array([[0.6, -0.2]])                     # (1, 2)
W1 = np.array([[0.2, 0.4], [-0.3, 0.1]])          # (2, 2)
b1 = np.array([[0.1, 0.0]])                       # (1, 2)
W2 = np.array([[0.5], [-0.6]])                    # (2, 1)
b2 = np.array([[0.2]])                            # (1, 1)

Z1, A, Z2, Q = forward_pass(x, W1, b1, W2, b2)
print("Z1 =", Z1)   # expect [[0.28, 0.22]]
print("A  =", A)    # ReLU leaves both unchanged (both positive)
print("Z2 =", Z2)   # expect [[0.208]]
print("Q  =", Q)    # expect ~[[0.5518]]

# ---- sanity check: a purely linear network collapses to one matrix ----
W_linear_combined = W1 @ W2
print("\nW2 . W1 collapsed into a single (2,1) matrix (no activation case):")
print(W_linear_combined)
