| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- from types import SimpleNamespace
- from typing import Tuple
- import numpy as np
- class EventLibrary:
- """
- Defines an event library ot maintain a list of events. Provides methods to insert new data and find existing data.
- Sequence Properties:
- - keys - A list of event IDs
- - data - A struct array with field 'array' to store data of varying lengths, remaining compatible with codegen.
- - lengths - Corresponding lengths of the data arrays
- - type - Type to distinguish events in the same class (e.g. trapezoids and arbitrary gradients)
- Sequence Methods:
- - find - Find an event in the library
- - insert - Add a new event to the library
- See also `Sequence.py`.
- Attributes
- ----------
- keys : dict{str, int}
- Key-value pairs of event keys and corresponding... event keys.
- data : dict{str: numpy.array}
- Key-value pairs of event keys and corresponding data.
- lengths : dict{str, int}
- Key-value pairs of event keys and corresponding length of data values in `self.data`.
- type : dict{str, str}
- Key-value pairs of event keys and corresponding event types.
- keymap : dict{str, int}
- Key-value pairs of data values and corresponding event keys.
- """
- def __init__(self):
- self.keys = dict()
- self.data = dict()
- self.lengths = dict()
- self.type = dict()
- self.keymap = dict()
- self.next_free_ID = 1
- def __str__(self) -> str:
- s = "EventLibrary:"
- s += "\nkeys: " + str(len(self.keys))
- s += "\ndata: " + str(len(self.data))
- s += "\nlengths: " + str(len(self.lengths))
- s += "\ntype: " + str(len(self.type))
- return s
- def find(self, new_data: np.ndarray) -> Tuple[int, bool]:
- """
- Finds data `new_data` in event library.
- Parameters
- ----------
- new_data : numpy.ndarray
- Data to be found in event library.
- Returns
- -------
- key_id : int
- Key of `new_data` in event library, if found.
- found : bool
- If `new_data` was found in the event library or not.
- """
- new_data = np.array(new_data)
- data_string = np.array2string(
- new_data, formatter={"float": lambda x: f"{x:.6g}"}
- )
- data_string = data_string.replace("[", "")
- data_string = data_string.replace("]", "")
- try:
- key_id = self.keymap[data_string]
- found = True
- except KeyError:
- key_id = 1 if len(self.keys) == 0 else max(self.keys) + 1
- found = False
- return key_id, found
- def find_or_insert(
- self, new_data: np.ndarray, data_type: str = str()
- ) -> Tuple[int, bool]:
- """
- Lookup a data structure in the given library and return the index of the data in the library. If the data does
- not exist in the library it is inserted right away. The data is a 1xN array with event-specific data.
- See also insert `pypulseq.Sequence.sequence.Sequence.add_block()`.
- Parameters
- ----------
- new_data : numpy.ndarray
- Data to be found (or added, if not found) in event library.
- data_type : str, default=str()
- Type of data.
- Returns
- -------
- key_id : int
- Key of `new_data` in event library, if found.
- found : bool
- If `new_data` was found in the event library or not.
- """
- if not isinstance(new_data, np.ndarray):
- new_data = np.array(new_data)
- data_string = new_data.tobytes()
- if data_string in self.keymap:
- key_id = self.keymap[data_string]
- found = True
- else:
- key_id = self.next_free_ID
- found = False
- # Insert
- self.keys[key_id] = key_id
- self.data[key_id] = new_data
- self.lengths[key_id] = np.max(new_data.shape)
- if data_type != str():
- self.type[key_id] = data_type
- self.keymap[data_string] = key_id
- self.next_free_ID = key_id + 1 # Update next_free_id
- return key_id, found
- def insert(self, key_id: int, new_data: np.ndarray, data_type: str = str()) -> int:
- """
- Add event to library.
- See also `pypulseq.event_library.EventLibrary.find()`.
- Parameters
- ----------
- key_id : int
- Key of `new_data`.
- new_data : numpy.ndarray
- Data to be inserted into event library.
- data_type : str, default=str()
- Data type of `new_data`.
- Returns
- -------
- key_id : int
- Key ID of inserted event.
- """
- if isinstance(key_id, float):
- key_id = int(key_id)
- if key_id == 0:
- key_id = self.next_free_ID
- new_data = np.array(new_data)
- self.keys[key_id] = key_id
- self.data[key_id] = new_data
- self.lengths[key_id] = max(new_data.shape)
- if data_type != str():
- self.type[key_id] = data_type
- data_string = np.array2string(
- new_data, formatter={"float_kind": lambda x: "%.6g" % x}
- )
- data_string = data_string.replace("[", "")
- data_string = data_string.replace("]", "")
- self.keymap[data_string] = key_id
- if key_id >= self.next_free_ID:
- self.next_free_ID += 1 # Update next_free_id
- return key_id
- def get(self, key_id: int) -> dict:
- """
- Parameters
- ----------
- key_id : int
- Returns
- -------
- dict
- """
- return {
- "key": self.keys[key_id],
- "data": self.data[key_id],
- "length": self.lengths[key_id],
- "type": self.type[key_id],
- }
- def out(self, key_id: int) -> SimpleNamespace:
- """
- Get element from library by key.
- See also `pypulseq.event_library.EventLibrary.find()`.
- Parameters
- ----------
- key_id : int
- Returns
- -------
- out : SimpleNamespace
- """
- out = SimpleNamespace()
- out.key = self.keys[key_id]
- out.data = self.data[key_id]
- out.length = self.lengths[key_id]
- out.type = self.type[key_id]
- return out
- def update(
- self,
- key_id: int,
- old_data: np.ndarray,
- new_data: np.ndarray,
- data_type: str = str(),
- ):
- """
- Parameters
- ----------
- key_id : int
- old_data : numpy.ndarray
- new_data : numpy.ndarray
- data_type : str, default=str()
- """
- if len(self.keys) >= key_id:
- data_string = np.array2string(
- old_data, formatter={"float_kind": lambda x: "%.6g" % x}
- )
- data_string = data_string.replace("[", "")
- data_string = data_string.replace("]", "")
- del self.keymap[data_string]
- self.insert(key_id, new_data, data_type)
- def update_data(
- self,
- key_id: int,
- old_data: np.ndarray,
- new_data: np.ndarray,
- data_type: str = str(),
- ):
- """
- Parameters
- ----------
- key_id : int
- old_data : np.ndarray
- new_data : np.ndarray
- data_type : str
- """
- self.update(key_id, old_data, new_data, data_type)
|