code: def __init__(
      self, ast: LazyOp, opts: Optional[LinearizerOptions] = None
  ):
      self.opts = opts or (
          device.linearizer_opts
          if isinstance(device := Device[Device.DEFAULT], Compiled)
          else LinearizerOptions(Device.DEFAULT)
      )
      self.ast = ast
      assert (
          ast.op == BufferOps.STORE
      ), f"kernels must have a store as the output, got {ast.op}"
  
      # fetch lazyop info
      self.info: FlopCounter = get_lazyop_info(self.ast)
  
      # there's only allowed to be one reduceop
      reduceops = [x for x in self.ast.lazyops if x.op in ReduceOps]
      assert len(dedup(reduceops)) <= 1, "max one reduce op in an ast"
      self.reduceop = reduceops[0] if reduceops else None
  
      self.bufs: List[
          Union[MemBuffer, ConstBuffer, LocalBuffer]
      ] = dedup([x.arg for x in self.ast.lazyops if x.op in BufferOps])
      assert (
          isinstance(self.bufs[0], MemBuffer) and self.bufs[0].idx == 0
      ), f"buffer 0 is not the store buffer {self.bufs[0]}"
  
      # get earlybufs, before the one reduce op
      self.earlybufs = (
          [x.arg for x in self.reduceop.lazyops if x.op in BufferOps]
          if self.reduceop
          else []
      )
      self.full_buf_index: int = (
          self.bufs.index(self.earlybufs[0]) if self.earlybufs else 0
      )
  
      # create new shapetrackers inside this kernel, we will permute them
      self.sts: List[ShapeTracker] = [
          x.st
          for x in cast(List[Union[MemBuffer, ConstBuffer]], self.bufs)
      ]
  
      # move all reduce axes to the end
      reduce = list(enumerate(zip(self.full_shape, self.sts[0].shape)))
      permute = tuple(
          [i for i, (s, n) in reduce if s == n]
          + [i for i, (s, n) in reduce if s != n]
      )
      self.reshape_and_permute(None, permute)
  
      # parameters for optimization
      self.applied_opts: List[Opt] = []
      self.group_for_reduce: List[int] = []
      self.upcasted: int = 0
      self.local_dims: int = 0
      self.local_alias: Dict[int, LocalBuffer] = {}
      self.tensor_core: Optional[TensorCore] = None
      self.dont_use_locals: bool = False
  
      # group simplifies
      self.simplify_ones()
      self.simplify_merge_adjacent()
  
      # cache
      self.applied_opts_cache: Optional[List[Opt]] = None
  
imports: from __future__ import annotations
  import os, math, itertools
  from typing import NamedTuple, Optional, List, Tuple, cast, Dict, Union
  from tinygrad.ops import LazyOp, FlopCounter, get_lazyop_info, UnaryOps, BinaryOps, ReduceOps, MemBuffer, ConstBuffer, BufferOps
  from tinygrad.device import Device, Compiled
  from tinygrad.dtype import dtypes, ImageDType, DType
  from tinygrad.helpers import dedup, colored, ansilen, getenv, prod, DEBUG, round_up, all_int
  from tinygrad.shape.shapetracker import ShapeTracker, get_contraction
  from tinygrad.shape.symbolic import sint
  from tinygrad.shape.view import View, strides_for_shape
  from dataclasses import dataclass
  from enum import Enum, auto
label_desc: Use image node_id 1 to create a new Kernel object  with example: obj = Kernel(ast, opts)