## Group GEMM Implementation in Triton

**Task:** Write a Triton kernel for implementing the Group GEMM operation.

**Requirements:**

1. **Functionality:** Group GEMM allows executing multiple independent GEMM operations simultaneously in a single GPU kernel launch. Mathematically, it can be represented as a set of operations {C_i = α_i * A_i * B_i + β_i * C_i | i = 1, ..., n}, where each operation i can have different matrix dimensions, strides, and scaling factors (α_i and β_i), all computed concurrently on the GPU.
2. **Data Types:** The kernel should support float32, float16, and bfloat16 data types for the input matrices A, B, and C.
3. **Problem Sizes:** The kernel should be able to handle arbitrary problem sizes (matrix dimensions).
4. **Verification:** Include a Python function to verify the correctness of the Triton kernel implementation by comparing its output with PyTorch's built-in operators, using `torch.allclose` with tolerances `atol=1e-2` and `rtol=1e-2`.
5. **Test Cases:** To ensure the implementation is completely accurate, you should use the following test cases:

```python
group_m = [1024, 512, 256, 128]
group_n = [1024, 512, 256, 128]
group_k = [1024, 512, 256, 128]
group_A = []
group_B = []
assert len(group_m) == len(group_n)
assert len(group_n) == len(group_k)
group_size = len(group_m)
for i in range(group_size):
    M = group_m[i]
    N = group_n[i]
    K = group_k[i]
    A = torch.rand((M, K), device="cuda", dtype=torch.float16)
    B = torch.rand((K, N), device="cuda", dtype=torch.float16)
    group_A.append(A)
    group_B.append(B)

tri_out = group_gemm_fn(group_A, group_B)
ref_out = [torch.matmul(a, b) for a, b in zip(group_A, group_B)]

@triton.jit
def grouped_matmul_kernel(
    # device tensor of matrices pointers
    group_a_ptrs,
    group_b_ptrs,
    group_c_ptrs,
    # device tensor of gemm sizes. its shape is [group_size, 3]
    # dim 0 is group_size, dim 1 is the values of <M, N, K> of each gemm
    group_gemm_sizes,
    # device tensor of leading dimension sizes. its shape is [group_size, 3]
    # dim 0 is group_size, dim 1 is the values of <lda, ldb, ldc> of each gemm
    g_lds,
    # number of gemms
    group_size,
    # number of virtual SM
    NUM_SM: tl.constexpr,
    # tile sizes
    BLOCK_SIZE_M: tl.constexpr,
    BLOCK_SIZE_N: tl.constexpr,
    BLOCK_SIZE_K: tl.constexpr,
):
```

Please continue the Triton kernel implementation. You should call the kernel only once, instead of calling it multiple times for each group.
To implement group gemm kernel please launches a fixed number of CTA to compute a group of gemms. The scheduling is static and we do it on device.
The basic idea is to loop over the gemms and assign each gemm to a CTA. The CTA will compute the gemm and then move to the next one.
Check the bounds of the gemm_id and the program_id to avoid out-of-bounds accesses.
Please focus on FP16 as test case data type, you can use `.to(tl.pointer_type(tl.float16))` to cast th data pointer to FP16.

You response format should follow this structure:

** Overview **

** Implementation Highlights **

** Kernel Implementation **

** Verification Function and Test Cases ** 

** Complete Corrected Code for Reference **

** Notes and Considerations **