_tree_utils.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. from typing import Any, Callable, Dict, Optional
  2. from torch.utils._pytree import Context, TreeSpec
  3. def reorder_kwargs(user_kwargs: Dict[str, Any], spec: TreeSpec) -> Dict[str, Any]:
  4. """Reorder user-provided kwargs to match the order in `spec`. `spec` is
  5. expected to be the in_spec of an exported program, i.e. the spec that
  6. results from flattening `(args, kwargs)`.
  7. We need this to provide consistent input ordering, such so that users can
  8. pass in foo(a=a, b=b) OR foo(b=b, a=a) and receive the same result.
  9. """
  10. # Make sure that the spec is actually shaped like (args, kwargs)
  11. assert spec.type is tuple
  12. assert spec.num_children == 2
  13. kwargs_spec = spec.children_specs[1]
  14. assert kwargs_spec.type is dict
  15. if set(user_kwargs) != set(kwargs_spec.context):
  16. raise ValueError(
  17. f"kwarg key mismatch: "
  18. f"Got {list(user_kwargs)} but expected {kwargs_spec.context}"
  19. )
  20. reordered_kwargs = {}
  21. for kw in kwargs_spec.context:
  22. reordered_kwargs[kw] = user_kwargs[kw]
  23. return reordered_kwargs
  24. def is_equivalent(
  25. spec1: TreeSpec,
  26. spec2: TreeSpec,
  27. equivalence_fn: Callable[[Optional[type], Context, Optional[type], Context], bool],
  28. ) -> bool:
  29. """Customizable equivalence check for two TreeSpecs.
  30. Arguments:
  31. spec1: The first TreeSpec to compare
  32. spec2: The second TreeSpec to compare
  33. equivalence_fn: A function to determine the equivalence of two
  34. TreeSpecs by examining their types and contexts. It will be called like:
  35. equivalence_fn(spec1.type, spec1.context, spec2.type, spec2.context)
  36. This function will be applied recursively to all children.
  37. Returns:
  38. True if the two TreeSpecs are equivalent, False otherwise.
  39. """
  40. if not equivalence_fn(spec1.type, spec1.context, spec2.type, spec2.context):
  41. return False
  42. # Recurse on children
  43. if len(spec1.children_specs) != len(spec2.children_specs):
  44. return False
  45. for child_spec1, child_spec2 in zip(spec1.children_specs, spec2.children_specs):
  46. if not is_equivalent(child_spec1, child_spec2, equivalence_fn):
  47. return False
  48. return True