# Exercise: Working with Tensors in PyTorch

Objective: Create, manipulate, and analyze tensors using various methods in PyTorch. This exercise will help you understand different tensor operations, their applications, and how to leverage GPU capabilities.

Tasks:

  1. Create Tensors:

    • Create a 1-D tensor with values [1, 2, 3, 4, 5].
    • Create a 2-D tensor with shape [3, 2] and values:
      [[1, 2],
      [3, 4],
      [5, 6]]
    • Create a tensor filled with zeros of shape [2, 3] and another tensor filled with ones of the same shape.
  2. Tensor Datatypes:

    • Create a tensor with dtype float32 and values [1.5, 2.5, 3.5].
    • Create a tensor with dtype int64 and values [1, 2, 3].
  3. Basic Operations:

    • Compute the sum, mean, maximum, and minimum of the 2-D tensor created in Task 1.
    • Perform element-wise addition of 10 to the 1-D tensor created in Task 1.
  4. Reshape and Manipulate Tensors:

    • Reshape the 1-D tensor from Task 1 into a 2-D tensor with shape [5, 1].
    • Stack two of the tensors created in Task 1 (zeros and ones) along a new dimension to form a 3-D tensor.
    • Squeeze and unsqueeze the 3-D tensor created in the previous step.
  5. GPU Operations:

    • Check if a GPU is available and move one of the tensors created to the GPU.
    • Perform an operation (e.g., add a scalar) on the tensor located on the GPU.

import torch

# 1. Create Tensors
tensor_1d = torch.tensor([1, 2, 3, 4, 5])
tensor_2d = torch.tensor([[1, 2], [3, 4], [5, 6]])
zeros_tensor = torch.zeros(2, 3)
ones_tensor = torch.ones(2, 3)

# Print created tensors
print("1-D Tensor:")
print(tensor_1d)
print("\n2-D Tensor:")
print(tensor_2d)
print("\nZeros Tensor:")
print(zeros_tensor)
print("\nOnes Tensor:")
print(ones_tensor)

# 2. Tensor Datatypes
tensor_float = torch.tensor([1.5, 2.5, 3.5], dtype=torch.float32)
tensor_int = torch.tensor([1, 2, 3], dtype=torch.int64)

print("\nFloat32 Tensor:")
print(tensor_float)
print("Data type:", tensor_float.dtype)

print("\nInt64 Tensor:")
print(tensor_int)
print("Data type:", tensor_int.dtype)

# 3. Basic Operations
sum_tensor_2d = tensor_2d.sum()
mean_tensor_2d = tensor_2d.mean()
max_tensor_2d = tensor_2d.max()
min_tensor_2d = tensor_2d.min()

tensor_1d_add = tensor_1d + 10

print("\nSum of 2-D Tensor:", sum_tensor_2d.item())
print("Mean of 2-D Tensor:", mean_tensor_2d.item())
print("Max of 2-D Tensor:", max_tensor_2d.item())
print("Min of 2-D Tensor:", min_tensor_2d.item())
print("\n1-D Tensor after adding 10:")
print(tensor_1d_add)

# 4. Reshape and Manipulate Tensors
reshaped_tensor_1d = tensor_1d.reshape(5, 1)
stacked_tensor = torch.stack([zeros_tensor, ones_tensor])

squeezed_tensor = stacked_tensor.squeeze()
unsqueezed_tensor = squeezed_tensor.unsqueeze(0)

print("\nReshaped 1-D Tensor:")
print(reshaped_tensor_1d)
print("\nStacked 3-D Tensor:")
print(stacked_tensor)
print("\nSqueezed Tensor:")
print(squeezed_tensor)
print("\nUnsqueezed Tensor:")
print(unsqueezed_tensor)

# 5. GPU Operations
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print("\nUsing device:", device)

# Create a tensor and move to GPU if available
tensor_gpu = torch.tensor([1, 2, 3], device=device)
print("Tensor on device:")
print(tensor_gpu)

# Perform an operation
result_gpu = tensor_gpu + 10
print("Tensor after addition on GPU:")
print(result_gpu)