33"""
44
55import copy
6- from itertools import product
7- import itertools
8- import logging
96import re
10- from typing import Annotated , Any , TypeAlias
7+ from typing import Annotated , Any , TypeAlias , Union
118
129import pydot
10+ from pydantic import Discriminator , Tag
1311
1412from accelforge .util .parallel import _SVGJupyterRender
1513
1917 EvalableList ,
2018 EvalableModel ,
2119 EvalsTo ,
20+ _get_tag ,
2221)
2322from accelforge .util ._visualization import _pydot_graph
2423from accelforge .frontend .renames import (
@@ -833,22 +832,29 @@ class Adapter(EvalableModel):
833832 """
834833 name : EvalsTo [str ]
835834
836- adapter_class : EvalsTo [str ]
837- """
838- The class of the adapter. Supported classes are:
839- - copy
840- - collective
841- """
842835
836+ class CopyAdapter (Adapter ):
843837 tensor : EvalsTo [str ]
844838
845839
840+ EinsumOrAdapter = Annotated [
841+ Union [
842+ Annotated ["Einsum" , Tag ("Einsum" )],
843+ Annotated ["CopyAdapter" , Tag ("Copy" )],
844+ ],
845+ Discriminator (lambda v : _get_tag (v , default = "Einsum" )),
846+ ]
847+
848+
846849class Workload (EvalableModel ):
847850 """
848851 The workload specification as a cascade of Einsums, with each Einsum being a
849852 computation step in the workload.
850853 """
851854
855+ einsums_and_adapters : EvalableList [EinsumOrAdapter ] = EvalableList ()
856+ """Einsums and adapters in the workload."""
857+
852858 einsums : EvalableList [Einsum ] = EvalableList ()
853859 """ The Einsums in the workload. """
854860
@@ -891,11 +897,6 @@ class Workload(EvalableModel):
891897 matching tensors as persistent. Example: "weight" or "~(Outputs | Intermediates)".
892898 """
893899
894- adapters : EvalableList [Adapter ] = EvalableList ()
895- """
896- Adapters for shared tensors.
897- """
898-
899900 def _for_einsum (self , einsum_name : EinsumName ) -> "Workload" :
900901 """Return a copy of the workload with only the Einsum with the given name."""
901902 new = self .model_copy (deep = False )
@@ -913,6 +914,8 @@ def __init__(self, **data):
913914 processed_einsums .append (_parse_einsum_entry (einsum_entry ))
914915 data ["einsums" ] = processed_einsums
915916
917+ self ._canonicalize_einsums_and_adapters (data )
918+
916919 super ().__init__ (** data )
917920
918921 def model_post_init (self , __context__ = None ) -> None :
@@ -953,6 +956,26 @@ def _validate(self):
953956 )} "
954957 )
955958
959+ def _canonicalize_einsums_and_adapters (self , data ):
960+ """
961+ Checks that only one of einsums or einsums_and_adapters is specified
962+ and fills in the other.
963+ """
964+ has_einsums = "einsums" in data and len (data ["einsums" ]) > 0
965+ has_einsums_and_adapters = "einsums_and_adapters" in data and len (data ["einsums_and_adapters" ]) > 0
966+
967+ if has_einsums and has_einsums_and_adapters :
968+ raise EvaluationError ("Please use only one of keywords einsums or einsums_and_adapters" )
969+
970+ def is_einsum (d ):
971+ return not hasattr (d , "tag" ) or d .tag == "!Einsum"
972+
973+ if has_einsums :
974+ data ["einsums_and_adapters" ] = data ["einsums" ]
975+ data ["einsums" ] = [e for e in data ["einsums_and_adapters" ] if is_einsum (e )]
976+ elif has_einsums_and_adapters :
977+ data ["einsums" ] = [e for e in data ["einsums_and_adapters" ] if is_einsum (e )]
978+
956979 @property
957980 def einsum_names (self ) -> list [EinsumName ]:
958981 """Returns the names of the Einsums in the workload."""
@@ -1334,9 +1357,3 @@ def get_compute_intensity(self, einsum_name: str) -> float:
13341357 self .get_tensor_size (tensor )
13351358 for tensor in self .einsums [einsum_name ].tensor_names
13361359 )
1337-
1338- def get_adapter_for (self , tensor_name : TensorName ) -> Adapter :
1339- for adapter in self .adapters :
1340- if adapter .tensor == tensor_name :
1341- return adapter
1342- return ValueError (f"No adapter found for tensor { tensor_name } " )
0 commit comments