Inheritance, Mixins, etc.

Contents

Inheritance, Mixins, etc.#

GPU#

Our current strategy for utilizing GPU acceleration is to use the object.__init_subclass__() functionality introduced to Python in PEP 487. The idea is that the core computational loop will use GPU-enable methods like get_Vext_GPU() etc. These should be carefully coded so as to use GPU-arrays without transferring data to the CPU and will return a reference to the GPU array. Adding _GPU to the name of the method tells the user/developer that they must be aware of these issues.

This creates two problems:

As a quick check, note that __init_subclass__() is called once per class.

class Base:
    def __init_subclass__(cls):
        print(cls.__name__)
        super().__init_subclass__()


class A(Base):
    pass
    

class B(A):
    pass
A
B
  1. When analyzing data, plotting, etc. we need the data on the CPU. The default implementation would require explicitly getting the data when needed, but only when working on the GPU.

    To solve this problem, we would like to “automatically” generate non-GPU methods like get_Vext() that will call get_Vext_GPU(), then transfer the data to a CPU array iff needed.

  2. New users will probably not want to be bothered with these complications, so we would like them to be able to simply define get_Vext() and move on. utils.GPUHelper.__init_subclass__() will detect this and provide appropriate GPU-methods like get_Vext_GPU() that raise a performance warning, but then which delegate to get_Vext() so that the code functions correctly.

The current solution is to ensure that any classes using *_GPU methods inherit from . This will provide the require matching methods. Base classes should define the *_GPU methods required by the core evolution code. This is done in all of the foundation classes starting from gpe.bec.StateDFTBase.

digraph inheritance874d5c6930 { bgcolor=transparent; rankdir=LR; size="8.0, 12.0"; "ArrayStateMixin" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="Mixin providing support for states with a single data array."]; "StateFlatMixin" -> "ArrayStateMixin" [arrowsize=0.5,style="setlinewidth(0.5)"]; "AsNumpyMixin" [URL="../_generated/api/gpe/utils/index.html#gpe.utils.AsNumpyMixin",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top",tooltip="Mixin providing asnumpy method."]; "StateMixin" -> "AsNumpyMixin" [arrowsize=0.5,style="setlinewidth(0.5)"]; "GPUHelper" -> "AsNumpyMixin" [arrowsize=0.5,style="setlinewidth(0.5)"]; "GPEMixin" [URL="../_generated/api/gpe/bec/index.html#gpe.bec.GPEMixin",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top",tooltip="Mixing providing the GPE equation of state."]; "GPUHelper" -> "GPEMixin" [arrowsize=0.5,style="setlinewidth(0.5)"]; "GPUHelper" [URL="../_generated/api/gpe/utils/index.html#gpe.utils.GPUHelper",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top",tooltip="Class for helping with GPU and other accelerator support."]; "HOMixin" [URL="../_generated/api/gpe/bec/index.html#gpe.bec.HOMixin",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top",tooltip="Helper mixin class for harmonically trapped systems."]; "GPUHelper" -> "HOMixin" [arrowsize=0.5,style="setlinewidth(0.5)"]; "ObjectBase" [URL="https://mmfutils.readthedocs.io/en/topic-0.7-ci/mmfutils.containers.html#mmfutils.containers.ObjectBase",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top",tooltip="General base class with a few convenience methods."]; "State" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="State class for backwards compatibility."]; "HOMixin" -> "State" [arrowsize=0.5,style="setlinewidth(0.5)"]; "StateGPEBase" -> "State" [arrowsize=0.5,style="setlinewidth(0.5)"]; "StateDFTBase" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="Underlying implementation of features needed for IStateDFT."]; "AsNumpyMixin" -> "StateDFTBase" [arrowsize=0.5,style="setlinewidth(0.5)"]; "ArrayStateMixin" -> "StateDFTBase" [arrowsize=0.5,style="setlinewidth(0.5)"]; "GPUHelper" -> "StateDFTBase" [arrowsize=0.5,style="setlinewidth(0.5)"]; "ObjectBase" -> "StateDFTBase" [arrowsize=0.5,style="setlinewidth(0.5)"]; "StateFlatMixin" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="Mixing to define `flat` from `ravel()` and `unravel_from()`."]; "StateMixin" -> "StateFlatMixin" [arrowsize=0.5,style="setlinewidth(0.5)"]; "StateGPEBase" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="State for the GPE in a homogeneous box."]; "GPEMixin" -> "StateGPEBase" [arrowsize=0.5,style="setlinewidth(0.5)"]; "_StateBase" -> "StateGPEBase" [arrowsize=0.5,style="setlinewidth(0.5)"]; "StateMixin" [URL="../_generated/api/gpe/mixins/index.html#gpe.mixins.StateMixin",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top"]; "StateMixin" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="Mixin implementing all IState methods from the minimal set of required methods."]; "_StateBase" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="State class implementing the DFT for a single-component BEC."]; "StateDFTBase" -> "_StateBase" [arrowsize=0.5,style="setlinewidth(0.5)"]; }
from gpe.utils_docs import get_inheritance_graph
import gpe.bec
display(get_inheritance_graph(gpe.bec.State, mro=True))
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/backend/execute.py:76, in run_check(cmd, input_lines, encoding, quiet, **kwargs)
     75         kwargs['stdout'] = kwargs['stderr'] = subprocess.PIPE
---> 76     proc = _run_input_lines(cmd, input_lines, kwargs=kwargs)
     77 else:

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/backend/execute.py:96, in _run_input_lines(cmd, input_lines, kwargs)
     95 def _run_input_lines(cmd, input_lines, *, kwargs):
---> 96     popen = subprocess.Popen(cmd, stdin=subprocess.PIPE, **kwargs)
     98     stdin_write = popen.stdin.write

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/subprocess.py:1039, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask, pipesize, process_group)
   1036             self.stderr = io.TextIOWrapper(self.stderr,
   1037                     encoding=encoding, errors=errors)
-> 1039     self._execute_child(args, executable, preexec_fn, close_fds,
   1040                         pass_fds, cwd, env,
   1041                         startupinfo, creationflags, shell,
   1042                         p2cread, p2cwrite,
   1043                         c2pread, c2pwrite,
   1044                         errread, errwrite,
   1045                         restore_signals,
   1046                         gid, gids, uid, umask,
   1047                         start_new_session, process_group)
   1048 except:
   1049     # Cleanup if the child failed starting.

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/subprocess.py:1990, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session, process_group)
   1989 if err_filename is not None:
-> 1990     raise child_exception_type(errno_num, err_msg, err_filename)
   1991 else:

FileNotFoundError: [Errno 2] No such file or directory: PosixPath('dot')

The above exception was the direct cause of the following exception:

ExecutableNotFound                        Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/jupyter_integration.py:98, in JupyterIntegration._repr_mimebundle_(self, include, exclude, **_)
     96 include = set(include) if include is not None else {self._jupyter_mimetype}
     97 include -= set(exclude or [])
---> 98 return {mimetype: getattr(self, method_name)()
     99         for mimetype, method_name in MIME_TYPES.items()
    100         if mimetype in include}

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/jupyter_integration.py:112, in JupyterIntegration._repr_image_svg_xml(self)
    110 def _repr_image_svg_xml(self) -> str:
    111     """Return the rendered graph as SVG string."""
--> 112     return self.pipe(format='svg', encoding=SVG_ENCODING)

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/piping.py:104, in Pipe.pipe(self, format, renderer, formatter, neato_no_op, quiet, engine, encoding)
     55 def pipe(self,
     56          format: typing.Optional[str] = None,
     57          renderer: typing.Optional[str] = None,
   (...)     61          engine: typing.Optional[str] = None,
     62          encoding: typing.Optional[str] = None) -> typing.Union[bytes, str]:
     63     """Return the source piped through the Graphviz layout command.
     64 
     65     Args:
   (...)    102         '<?xml version='
    103     """
--> 104     return self._pipe_legacy(format,
    105                              renderer=renderer,
    106                              formatter=formatter,
    107                              neato_no_op=neato_no_op,
    108                              quiet=quiet,
    109                              engine=engine,
    110                              encoding=encoding)

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/_tools.py:185, in deprecate_positional_args.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
    177     wanted = ', '.join(f'{name}={value!r}'
    178                        for name, value in deprecated.items())
    179     warnings.warn(f'The signature of {func_name} will be reduced'
    180                   f' to {supported_number} positional arg{s_}{qualification}'
    181                   f' {list(supported)}: pass {wanted} as keyword arg{s_}',
    182                   stacklevel=stacklevel,
    183                   category=category)
--> 185 return func(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/piping.py:121, in Pipe._pipe_legacy(self, format, renderer, formatter, neato_no_op, quiet, engine, encoding)
    112 @_tools.deprecate_positional_args(supported_number=1, ignore_arg='self')
    113 def _pipe_legacy(self,
    114                  format: typing.Optional[str] = None,
   (...)    119                  engine: typing.Optional[str] = None,
    120                  encoding: typing.Optional[str] = None) -> typing.Union[bytes, str]:
--> 121     return self._pipe_future(format,
    122                              renderer=renderer,
    123                              formatter=formatter,
    124                              neato_no_op=neato_no_op,
    125                              quiet=quiet,
    126                              engine=engine,
    127                              encoding=encoding)

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/piping.py:149, in Pipe._pipe_future(self, format, renderer, formatter, neato_no_op, quiet, engine, encoding)
    146 if encoding is not None:
    147     if codecs.lookup(encoding) is codecs.lookup(self.encoding):
    148         # common case: both stdin and stdout need the same encoding
--> 149         return self._pipe_lines_string(*args, encoding=encoding, **kwargs)
    150     try:
    151         raw = self._pipe_lines(*args, input_encoding=self.encoding, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/backend/piping.py:212, in pipe_lines_string(engine, format, input_lines, encoding, renderer, formatter, neato_no_op, quiet)
    206 cmd = dot_command.command(engine, format,
    207                           renderer=renderer,
    208                           formatter=formatter,
    209                           neato_no_op=neato_no_op)
    210 kwargs = {'input_lines': input_lines, 'encoding': encoding}
--> 212 proc = execute.run_check(cmd, capture_output=True, quiet=quiet, **kwargs)
    213 return proc.stdout

File ~/checkouts/readthedocs.org/user_builds/gpe/conda/latest/lib/python3.14/site-packages/graphviz/backend/execute.py:81, in run_check(cmd, input_lines, encoding, quiet, **kwargs)
     79 except OSError as e:
     80     if e.errno == errno.ENOENT:
---> 81         raise ExecutableNotFound(cmd) from e
     82     raise
     84 if not quiet and proc.stderr:

ExecutableNotFound: failed to execute PosixPath('dot'), make sure the Graphviz executables are on your systems' PATH
<graphviz.graphs.Digraph at 0x7393b007d6a0>