Typorithm · easy

PyTorch: views, clones and shared storage

pythontorchindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

PyTorch: views, clones and shared storage

0%

Concept and contract

Choose intentionally whether an operation shares or owns tensor storage.

Two tensor objects can share the same underlying storage. A view changes how that storage is interpreted without necessarily copying the values, so an in-place modification can affect another reference.

clone creates a new tensor with copied data. That ownership decision matters when preprocessing inputs, keeping baselines or comparing values before and after an operation.

Mechanism and worked example

The helper constructs a source tensor, clones it and changes only the clone’s first value.

The helper constructs a source tensor, clones it and changes only the clone’s first value. Returning both arrays makes the ownership boundary observable.

Empty input is handled without indexing. The example is about storage behavior, not autograd history: clone and detach answer different questions, and a detached tensor may still share storage with its source.

Complete worked example

python

from bootstrap_lab import torch_setup
torch = torch_setup()

def copied_change(values):
    source = torch.tensor(values, dtype=torch.float32)
    copy = source.clone()
    if copy.numel():
        copy[0] = 99
    return {'source': source.tolist(), 'copy': copy.tolist()}
Reasoning
The helper constructs a source tensor, clones it and changes only the clone’s first value. Returning both arrays makes the ownership boundary observable.

Counterexamples and common mistakes

The counterexample assigns copy = source, creating a second Python reference to the same tensor.

The counterexample assigns copy = source, creating a second Python reference to the same tensor. The first value in both returned arrays then changes.

Similar surprises arise with reshape or slicing when they produce views, and torch.from_numpy can share CPU memory with the original array. Do not assume a new variable name implies an independent allocation.

Counterexample: a runnable framework mistake

python

from bootstrap_lab import torch_setup
torch = torch_setup()

def copied_change(values):
    source = torch.tensor(values, dtype=torch.float32)
    copy = source
    if copy.numel():
        copy[0] = 99
    return {'source': source.tolist(), 'copy': copy.tolist()}
Reasoning
The counterexample assigns copy = source, creating a second Python reference to the same tensor. The first value in both returned arrays then changes.

Independent practice: one bounded framework change

Change the second copied value to minus one when at least two values exist, while preserving the source.

Change the second copied value to minus one when at least two values exist, while preserving the source. Keep the existing first-value modification and empty handling.

This adds one guarded mutation. Avoid mutating a caller-provided list or adding an unnecessary device transfer; the exercise focuses on explicit copied storage and safe index boundaries.

Step 1: Change a second copied element only when it exists. Step 2: Keep clone as the storage boundary.

Practice starter: solve before reading the solution

python

from bootstrap_lab import torch_setup
torch = torch_setup()

def copied_change(values):
    # Remove this placeholder, then adapt the retained baseline:
    # Change a second copied element only when it exists.
    raise NotImplementedError
    source = torch.tensor(values, dtype=torch.float32)
    copy = source.clone()
    if copy.numel():
        copy[0] = 99
    return {'source': source.tolist(), 'copy': copy.tolist()}
Reasoning
Change a second copied element only when it exists. Keep clone as the storage boundary.
  • Hint 1: Guard the second index with numel.
  • Hint 2: Inspect both source and result arrays.

Solution, tests and trade-offs

The fixtures inspect a two-item input, a one-item input and an empty list.

The fixtures inspect a two-item input, a one-item input and an empty list. Practice checks the additional second-value change only where valid.

Both source and copy must match their expected results. Comparing only the changed copy would miss the central aliasing defect, and checking only length would say nothing about ownership or unintended data mutation.

Practice solution and acceptance checks

python

from bootstrap_lab import torch_setup
torch = torch_setup()

def copied_change(values):
    source = torch.tensor(values, dtype=torch.float32)
    copy = source.clone()
    if copy.numel():
        copy[0] = 99
    if copy.numel() >= 2:
        copy[1] = -1
    return {'source': source.tolist(), 'copy': copy.tolist()}
Reasoning
The fixtures inspect a two-item input, a one-item input and an empty list. Practice checks the additional second-value change only where valid.

Interview reasoning and next step

Before in-place tensor operations, identify all views and aliases that may observe the change.

Before in-place tensor operations, identify all views and aliases that may observe the change. In training, autograd can also require saved values and reject unsafe in-place modifications.

Prefer clear out-of-place expressions until memory pressure justifies a carefully reviewed alternative. For NumPy interchange, document whether sharing or copying is intended and test mutation from both sides of the boundary.

Optional stretch — after the core task works: Compare clone, detach and from_numpy with owned mutation probes.

Computer practice: https://applydjinn.com/typorithm/python-frameworks-lab-20261105.zip. Install the pinned dependencies in an isolated environment, run verify.py --track torch, then edit the retained exercise baseline and check it with --practice pt-storage. The lab uses owned in-memory databases, local framework clients and tiny CPU tensors. It never reads the working ApplyDjinn database, contacts production feeds or downloads models.

Choose intentionally whether an operation shares or owns tensor storage.