Inputs, Outputs, and Checks

Every stage of a numerical project has a contract: the values it receives, the result it produces, and the conditions required for that result to be meaningful. This lesson turns those conditions into clear validation, shape checks, and named outputs.

The previous lesson divided the project into stages. We can now make the boundary of each stage precise by asking:

  1. What values does this stage receive?
  2. What values does it return or create?
  3. What must be true for its result to be meaningful?

The answers form a contract. A function contract describes the inputs a function accepts, the result it produces, and the conditions its caller and implementation must satisfy.

Record more than a variable name

The name x alone does not explain enough. In this project, x should be a one-dimensional NumPy array of finite floating-point values. Its length should match the length of y.

Write down the important properties of each value:

ValueRoleRequired properties
configinputvalid seed, at least two points, non-negative noise scale
xgenerated dataone-dimensional, finite, same shape as y
ygenerated dataone-dimensional, finite, same shape as x
candidate_slopesfitting inputone-dimensional, finite, not empty
fitted_slopefitting resultfinite and selected from the candidates
lossfitting resultfinite and non-negative

A finite number is neither positive or negative infinity nor NaN. NaN means “not a number” and commonly appears when a numerical operation has no valid numerical result.

This table is not merely documentation. Its statements can guide type hints, validation, tests, and error messages.

Validate configuration at the boundary

Configuration describes choices made before the calculation starts. Reject an invalid choice when the configuration enters the computation:

The message names both the invalid field and its rule. Compare it with a vague message such as "bad configuration", which forces the reader to inspect the code to discover what failed.

At least two points are required because one point does not provide enough information to inspect a line's direction. A negative noise scale is rejected because the scale represents a non-negative amount of variation.

Check paired arrays before fitting

The fitting function treats x[i] and y[i] as one measured pair. Matching lengths alone are not enough if one array has shape (4,) and another has shape (4, 1). NumPy may broadcast those arrays into a two-dimensional result instead of reporting an error.

Check the intended structure explicitly:

The order of these checks helps interpretation. First confirm the number of dimensions, then compare the complete shapes, and finally check the number of paired observations.

Numerical arrays should also contain usable values:

np.isfinite produces one Boolean value per element. np.all requires every element to pass.

Check candidate slopes before the search

The search needs at least one finite candidate:

Without the empty-array check, a later operation such as np.argmin fails farther from the cause. Checking the boundary produces an error that explains the actual project rule.

Return results with visible meaning

A fitting function produces two different numbers:

The variable names make the tuple positions visible at the call site. A small result record can make the relationship even more explicit:

The fitting function can then return FitResult(slope=..., loss=...), and callers can read result.slope and result.loss. Either form is reasonable for this project. The important rule is that the caller should not have to guess what an unlabeled number means.

Check properties, not one memorized result

Noisy data may change when the seed changes. A useful check therefore targets properties required for every valid run:

These assertions state expectations that should already be guaranteed by the program's implementation. They help expose a programming mistake during development.

Use an explicit exception such as ValueError when rejecting invalid values supplied at a function boundary. Use an assertion for an internal condition that correct program logic should make true. Assertions can be disabled when Python is run with optimization, so they should not be the only protection against invalid external input.

Run the contracts with valid and invalid values

The example below validates one configuration and one pair of arrays. Change point_count, noise_scale, or the shape of y to see which contract reports the problem.

Validate the project inputs

The configuration and data checks run before fitting. Each failure names the value and rule that need attention.

Ready to run.

With the supplied values, all checks pass. If a check fails, the project stops before producing a fitted slope or plot that might look plausible despite invalid input.

Checks support reasoning but do not replace it

A program can verify that arrays have matching shapes, values are finite, and loss is non-negative. These checks cannot establish that:

  • the generated data represents a useful real-world process;
  • the candidate range contains every scientifically reasonable slope;
  • mean squared error is the right measure for a future application;
  • a fitted relationship causes one quantity to change another.

Those are questions about the model, data, and interpretation. Mechanical checks protect the computation. They do not turn an unsuitable investigation into a sound one.

Exercise: Check the shape of paired arrays

Which condition verifies that x and y have exactly the same NumPy shape?

Choose the shape check

Select one choice, then check.

HintCompare the complete tuples

For example, (4,) and (4, 1) have different shape tuples.

SolutionCompare x.shape with y.shape

Use x.shape == y.shape. Equal sizes do not require the same arrangement, and equal ndim values do not require equal lengths along each dimension.

Exercise: Predict a configuration failure

What happens when this configuration reaches validate_config?

Choose the validation result

Select one choice, then check.

HintInspect the value range

The project treats noise scale as a non-negative amount.

SolutionReject the negative noise scale

noise_scale=-0.25 violates the rule noise_scale >= 0.0, so validation raises ValueError. The point count is valid because 8 is at least 2.

Exercise: Classify a value as input or output

Which value is an output of fit_slope(x, y, candidate_slopes)?

Choose the fitting output

Select one choice, then check.

HintRead the function boundary

Values inside the parentheses are inputs supplied by the caller.

SolutionLoss is produced by fitting

x, y, and candidate_slopes enter the function. The selected slope and its loss are results produced by the fitting calculation.

Exercise: Choose an informative validation message

Which error message gives the most useful information when candidate_slopes is empty?

Choose the error message

Select one choice, then check.

HintMake the repair visible

The reader should know what to change without searching for the failed rule.

SolutionName the value and its rule

candidate_slopes must not be empty identifies the input and the violated requirement. The other messages do not explain what needs repair.

Exercise: Repair the paired-data checks

Complete check_measured_data so it rejects two-dimensional or mismatched arrays. The supplied valid arrays must produce:

valid pairs: 3

Validate paired one-dimensional arrays

Ready to run.

HintWrite two separate conditions

First compare each .ndim with 1. Then compare x.shape with y.shape.

SolutionCheck dimensions before matching shapes

The first condition protects the intended one-dimensional representation. The second protects the one-to-one pairing. Keeping them separate also produces a more specific error message.

Write contracts before debugging results

For each project stage, record:

inputs
required properties
outputs
guaranteed properties

Then place checks near the boundary where a rule first becomes known. This turns hidden assumptions into visible program behavior and prevents invalid values from travelling into later calculations.