def generate_Deflection_Patterns(npats): """ This function creates a set of *PatternAutomata* patterns, each one responsible for obtaining the i-th relevant deflection in a given scope. It allows to overcome the limitation of having a single hypothesis for a pattern with the same base evidence (in the case of this pattern, this base evidence is None, since the only transition of the pattern automata does not include any observable, only general constraints). Parameters ---------- npats: Integer specifying the number of the generated patterns. Returns: out: Ordered list with *n* abstraction patterns, each one responsible for the deduction of the *i-th* interesting energy interval in a specific area delimited by the hypothesis. """ pats = [] for i in range(npats): pat = PatternAutomata() pat.name = "Deflection" pat.Hypothesis = o.Deflection pat.add_transition(0, 1, tconst=_def_tconst, gconst=get_gconst(i)) pat.final_states.add(1) pat.freeze() pats.append(pat) return pats
verify(abs(prevrr - rr) >= C.RR_MAX_DIFF) ############################# ### Observation procedure ### ############################# def _rhythmblock_obs_proc(pattern): """Observation procedure executed once the rhythm pattern has finished""" # We asign the endpoint of the hypothesis. pattern.hypothesis.end.value = pattern.evidence[o.QRS][-1].time.value RHYTHMBLOCK_PATTERN = PatternAutomata() RHYTHMBLOCK_PATTERN.name = 'Rhythm Block' RHYTHMBLOCK_PATTERN.Hypothesis = o.RhythmBlock RHYTHMBLOCK_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst, _prev_rhythm_gconst) RHYTHMBLOCK_PATTERN.add_transition(0, 2, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst, _prev_asyst_gconst) RHYTHMBLOCK_PATTERN.add_transition(1, 2, o.QRS, ENVIRONMENT, _qrs1_tconst) RHYTHMBLOCK_PATTERN.add_transition(2, 3, o.QRS, ENVIRONMENT, _qrs2_tconst) RHYTHMBLOCK_PATTERN.add_transition(3, 4, o.QRS, ABSTRACTED, _qrs3_tconst, _qrs_gconst) RHYTHMBLOCK_PATTERN.add_transition(4, 5, o.PWave, ABSTRACTED, _p_qrs_tconst) RHYTHMBLOCK_PATTERN.add_transition(4, 6, o.TWave, ABSTRACTED, _t_qrs_tconst) RHYTHMBLOCK_PATTERN.add_transition(4, 6) RHYTHMBLOCK_PATTERN.add_transition(5, 6, o.TWave, ABSTRACTED, _t_qrs_tconst) RHYTHMBLOCK_PATTERN.add_transition(6, 4, o.QRS, ABSTRACTED, _qrsn_tconst, _qrs_gconst) RHYTHMBLOCK_PATTERN.final_states.add(6) RHYTHMBLOCK_PATTERN.abstractions[o.QRS] = ( RHYTHMBLOCK_PATTERN.transitions[4], )
has been observed. """ beats = pattern.evidence[o.QRS] if pattern.istate == 0: # We ensure that there are no missed beats. _check_missed_beats(pattern) # We must ensure that the first two beats and the last one have the # same shape. verify((beats[-3].paced and beats[-1].paced) or signal_match(beats[-3].shape, beats[-1].shape)) EXTRASYSTOLE_PATTERN = PatternAutomata() EXTRASYSTOLE_PATTERN.name = 'Extrasystole' EXTRASYSTOLE_PATTERN.Hypothesis = o.Extrasystole EXTRASYSTOLE_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst, _prev_rhythm_gconst) EXTRASYSTOLE_PATTERN.add_transition(1, 2, o.QRS, ENVIRONMENT, _qrs_rref_tconst) EXTRASYSTOLE_PATTERN.add_transition(0, 2, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst, _prev_rhythm_nreg_gconst) EXTRASYSTOLE_PATTERN.add_transition(2, 3, o.QRS, ENVIRONMENT, _qrs_env_tconst) # Now there are two ways in the automata, one in which we look for the # compensatory pause, and another in which a ventricular extrasystole may not # involve a compensatory pause. ##First way: Compensatory pause. EXTRASYSTOLE_PATTERN.add_transition(3, 4, o.QRS, ABSTRACTED, _qrs_ext_tconst(False), _qrs_ext_gconst) EXTRASYSTOLE_PATTERN.add_transition(4, 5, o.PWave, ABSTRACTED, _p_qrs_tconst) EXTRASYSTOLE_PATTERN.add_transition(5, 6, o.TWave, ABSTRACTED, _t_qrs_tconst) EXTRASYSTOLE_PATTERN.add_transition(4, 6, o.TWave, ABSTRACTED, _t_qrs_tconst) EXTRASYSTOLE_PATTERN.add_transition(4, 7, o.QRS, ABSTRACTED,
else: pqm, pqst = np.mean(pqs), max(np.std(pqs), C.MIN_QT_STD) if len(rts) == 0: rtm, rtst = 0, 0 elif len(rts) == 1: rtm, rtst = rts[0], C.QT_ERR_STD else: rtm, rtst = np.mean(rts), max(np.std(rts), C.MIN_QT_STD) pattern.hypothesis.meas = o.CycleMeasurements((np.mean(rrs), np.std(rrs)), (rtm, rtst), (pqm, pqst)) BIGEMINY_PATTERN = PatternAutomata() BIGEMINY_PATTERN.name = "Bigeminy" BIGEMINY_PATTERN.Hypothesis = o.Bigeminy BIGEMINY_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENV, _prev_rhythm_tconst, _prev_rhythm_gconst) # Necessary evidence are QRS complexes. # N BIGEMINY_PATTERN.add_transition(1, 2, o.QRS, ENV, _reg_qrs_tconst) # V BIGEMINY_PATTERN.add_transition(2, 3, o.QRS, ABS, _ect_qrs_tconst, _ect0_gconst) # N BIGEMINY_PATTERN.add_transition(3, 4, o.QRS, ABS, _reg_qrs_tconst, _cycle_finished_gconst) # V BIGEMINY_PATTERN.add_transition(4, 5, o.QRS, ABS, _ect_qrs_tconst) # N BIGEMINY_PATTERN.add_transition(5, 6, o.QRS, ABS, _reg_qrs_tconst, _cycle_finished_gconst) # Optional P and T waves for the necessary evidence
else: pqm, pqst = np.mean(pqs), max(np.std(pqs), C.MIN_QT_STD) if len(rts) == 0: rtm, rtst = 0, 0 elif len(rts) == 1: rtm, rtst = rts[0], C.QT_ERR_STD else: rtm, rtst = np.mean(rts), max(np.std(rts), C.MIN_QT_STD) pattern.hypothesis.meas = o.CycleMeasurements((np.mean(rrs), np.std(rrs)), (rtm, rtst), (pqm, pqst)) TRIGEMINY_PATTERN = PatternAutomata() TRIGEMINY_PATTERN.name = "Trigeminy" TRIGEMINY_PATTERN.Hypothesis = o.Trigeminy TRIGEMINY_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENV, _prev_rhythm_tconst) # N TRIGEMINY_PATTERN.add_transition(1, 2, o.QRS, ENV, _env_qrs_tconst) # V TRIGEMINY_PATTERN.add_transition(2, 3, o.QRS, ABS, _ect_qrs_tconst, _ect0_gconst) # N TRIGEMINY_PATTERN.add_transition(3, 4, o.QRS, ABS, _reg_ae_tconst, _cycle_finished_gconst) # N TRIGEMINY_PATTERN.add_transition(4, 5, o.QRS, ABS, _reg_nae_tconst) # V TRIGEMINY_PATTERN.add_transition(5, 6, o.QRS, ABS, _ect_qrs_tconst) # N TRIGEMINY_PATTERN.add_transition(6, 7, o.QRS, ABS, _reg_ae_tconst, _cycle_finished_gconst)
only observed after the second extrasystole QRS has been properly observed. """ BASIC_TCONST(pattern, twave) tnet = pattern.last_tnet beats = pattern.evidence[o.QRS] # The T wave must be in the hole between the two QRS complexes, and must # finish at least 1mm before the next QRS starts. tnet.set_before(beats[1].end, twave.start) tnet.set_before(twave.end, beats[2].start) tnet.add_constraint(twave.end, beats[2].start, Iv(C.TMARGIN, np.Inf)) COUPLET_PATTERN = PatternAutomata() COUPLET_PATTERN.name = "Couplet" COUPLET_PATTERN.Hypothesis = o.Couplet COUPLET_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENV, _prev_rhythm_tconst) # N COUPLET_PATTERN.add_transition(1, 2, o.QRS, ENV, _n0_tconst) # V COUPLET_PATTERN.add_transition(2, 3, o.QRS, ABS, _v0_tconst, _v0_gconst) # V COUPLET_PATTERN.add_transition(3, 4, o.QRS, ABS, _v1_tconst) # The T wave of the first extrasystole is only observed after the second # extrasystole QRS complex. COUPLET_PATTERN.add_transition(4, 5, o.TWave, ABS, _tv0_tconst) COUPLET_PATTERN.add_transition(4, 6, o.TWave, ABS, _t_tconst) # N COUPLET_PATTERN.add_transition(4, 7, o.QRS, ABS, _n1_tconst, _couplet_gconst) COUPLET_PATTERN.add_transition(5, 6, o.TWave, ABS, _t_tconst) COUPLET_PATTERN.add_transition(5, 6) COUPLET_PATTERN.add_transition(6, 7, o.QRS, ABS, _n1_tconst, _couplet_gconst)
def create_regular_rhythm(name, hypothesis, rr_bounds): """ Creates a new abstraction pattern automata with the properties of a regular rhythm, but allows to parameterize the RR limits, the hypothesis observable type and the name of the pattern. """ # The hypothesis must be a cardiac rhythm. assert issubclass(hypothesis, o.Cardiac_Rhythm) def _pair_gconst(pattern, _): """ General constraints to be satisfied when a regular rhythm consists of only two beats. """ if pattern.evidence[o.Cardiac_Rhythm]: _check_missed_beats(pattern) prhythm = pattern.evidence[o.Cardiac_Rhythm][0] rhythm = pattern.hypothesis # Previous rhythm cannot be a regular rhythm. verify(not isinstance(prhythm, o.RegularCardiacRhythm)) mrr, stdrr = prhythm.meas.rr beats = pattern.evidence[o.QRS] rr = beats[-1].time.start - beats[0].time.start verify(rr in rr_bounds) # Avoid duplicate hypotheses with overlapping rhythms. if pattern.automata is SINUS_PATTERN: verify(C.TACHY_RR.end < rr < C.BRADY_RR.start) maxvar = max(C.TMARGIN, min(C.RR_MAX_DIFF, 2.5 * stdrr)) verify(rr in Iv(mrr - maxvar, mrr + maxvar)) # Besides being in rhythm, the two beats must share the morphology. verify(signal_match(beats[0].shape, beats[1].shape)) # The amplitude difference is also constrained for lead in beats[0].shape: if lead in beats[1].shape: samp, qamp = (beats[0].shape[lead].amplitude, beats[1].shape[lead].amplitude) verify( min(samp, qamp) / max(samp, qamp) >= C.MISSED_QRS_MAX_DIFF) rhythm.meas = o.CycleMeasurements( (rr, stdrr), (prhythm.meas.rt[0], C.QT_ERR_STD), (prhythm.meas.pq[0], C.QT_ERR_STD)) _qrs_tconst = _get_qrs_tconst(rr_bounds) # Automata definition automata = PatternAutomata() automata.name = name automata.Hypothesis = hypothesis automata.add_transition(0, 1, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst, _prev_rhythm_gconst) automata.add_transition(1, 2, o.QRS, ENVIRONMENT, _qrs_tconst) automata.add_transition(2, 3, o.QRS, ABSTRACTED, _qrs_tconst) automata.add_transition(2, 9, o.QRS, ABSTRACTED, _qrs_tconst, _pair_gconst) automata.add_transition(3, 4, o.PWave, ABSTRACTED, _p_qrs_tconst) automata.add_transition(3, 5, o.TWave, ABSTRACTED, _t_qrs_tconst) automata.add_transition(3, 8, o.QRS, ABSTRACTED, _qrs_tconst, _cycle_finished_gconst) automata.add_transition(4, 5, o.TWave, ABSTRACTED, _t_qrs_tconst) automata.add_transition(5, 6, o.QRS, ABSTRACTED, _qrs_tconst) automata.add_transition(5, 8, o.QRS, ABSTRACTED, _qrs_tconst, _cycle_finished_gconst) automata.add_transition(6, 7, o.PWave, ABSTRACTED, _p_qrs_tconst) automata.add_transition(6, 8, o.TWave, ABSTRACTED, _t_qrs_tconst, _cycle_finished_gconst) automata.add_transition(7, 8, o.TWave, ABSTRACTED, _t_qrs_tconst, _cycle_finished_gconst) automata.add_transition(8, 6, o.QRS, ABSTRACTED, _qrs_tconst) automata.add_transition(8, 8, o.QRS, ABSTRACTED, _qrs_tconst, _cycle_finished_gconst) automata.add_transition(9, 10, o.PWave, ABSTRACTED, _p_qrs_tconst) automata.add_transition(9, 11, o.TWave, ABSTRACTED, _t_qrs_tconst) automata.add_transition(9, 11) automata.add_transition(10, 11, o.TWave, ABSTRACTED, _t_qrs_tconst) automata.final_states.add(8) automata.final_states.add(11) automata.abstractions[o.QRS] = (automata.transitions[2], automata.transitions[3]) automata.obs_proc = _rhythm_obs_proc automata.freeze() return automata
hyp.end.value = Iv(beg + end, beg + end) ################################################################### # Amplitude conditions (between 0.5mV and 6.5 mV in at least one # lead or an identified pattern in most leads). ################################################################### verify( len(hyp.shape) > len(sig_buf.get_available_leads()) / 2.0 or ph2dg(0.5) <= max(s.amplitude for s in hyp.shape.itervalues()) <= ph2dg(6.5) ) hyp.freeze() ######################### ## Automata definition ## ######################### QRS_PATTERN = PatternAutomata() QRS_PATTERN.name = 'QRS' QRS_PATTERN.Hypothesis = o.QRS QRS_PATTERN.add_transition(0, 1, o.RDeflection, ABSTRACTED, _qrs_tconst, _qrs_gconst) QRS_PATTERN.final_states.add(1) QRS_PATTERN.abstractions[o.RDeflection] = (QRS_PATTERN.transitions[0],) QRS_PATTERN.freeze() if __name__ == "__main__": SHAPES = set(QRS_SHAPES.iterkeys()) for S, V in QRS_SHAPES.iteritems(): assert S in V assert V.issubset(SHAPES)
the global list for an appropriate annotation. """ if ANNOTS is None: _load_annots() leads = IN.SIG.get_available_leads() # We find all the annotations in the given interval. rdef = pattern.hypothesis beg = min(int(rdef.earlystart), IN.get_acquisition_point()) + IN._OFFSET end = min(int(rdef.lateend), IN.get_acquisition_point()) + IN._OFFSET dummy = MITAnnotation() dummy.time = beg bidx = ANNOTS.bisect_left(dummy) dummy.time = end eidx = ANNOTS.bisect_right(dummy) verify(eidx > bidx) selected = max(ANNOTS[bidx:eidx], key=operator.attrgetter('num')) time = selected.time - IN._OFFSET rdef.time.value = Iv(time, time) rdef.start.value = Iv(time, time) rdef.end.value = Iv(time, time) rdef.level = {lead: 127 for lead in leads} rdef.level[leads[selected.chan]] = 127 - selected.num RDEFLECTION_PATTERN = PatternAutomata() RDEFLECTION_PATTERN.name = 'R-Deflection' RDEFLECTION_PATTERN.Hypothesis = o.RDeflection RDEFLECTION_PATTERN.add_transition(0, 1, gconst=_rdef_gconst) RDEFLECTION_PATTERN.final_states.add(1) RDEFLECTION_PATTERN.freeze()
# With this check, we avoid false positives with bigeminies, checking # the RR constraints with even and odd rrs. if len(beats) >= 6: _verify_afib_rhythm(rrs[0::2]) _verify_afib_rhythm(rrs[1::2]) _verify_afib_rhythm(np.diff(rpks[0::2])) # Atrial activity is only checked at the beginning of the pattern and if # there are not previous atrial fibrillation episodes. if 1 < len(beats) < 32 and not prevafib: _verify_atrial_activity(pattern) AFIB_PATTERN = PatternAutomata() AFIB_PATTERN.name = "Atrial Fibrillation" AFIB_PATTERN.Hypothesis = o.Atrial_Fibrillation AFIB_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENVIRONMENT, _prev_afib_tconst, _prev_afib_gconst) AFIB_PATTERN.add_transition(1, 1, o.Cardiac_Rhythm, ENVIRONMENT, _prev_multrhythm_tconst, _prev_rhythm_gconst) AFIB_PATTERN.add_transition(1, 2, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst, _prev_rhythm_gconst) AFIB_PATTERN.add_transition(0, 2, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst, _prev_rhythm_gconst) AFIB_PATTERN.add_transition(2, 3, o.QRS, ENVIRONMENT, _qrs0_tconst) AFIB_PATTERN.add_transition(3, 4, o.QRS, ABSTRACTED, _qrs_tconst, _qrs_gconst) AFIB_PATTERN.add_transition(4, 5, o.QRS, ABSTRACTED, _qrs_tconst, _qrs_gconst) # We have two different ways to get a sufficient set of evidence. If there is # a previous Afib episode, then we only need 3 QRS to have a firm hypothesis of # a new episode. AFIB_PATTERN.add_transition(5, 6, gconst=_prev_afib_exists_gconst) # Optional T waves are searched now AFIB_PATTERN.add_transition(6, 7, o.TWave, ABSTRACTED, get_t_tconst(1))
for lead in sig_buf.get_available_leads(): if _is_VF(sig_buf.get_signal_fragment(beg, end, lead=lead)[0]): lpos += 1 ltot += 1 verify(lpos / ltot > 0.5) defls = pattern.evidence[o.Deflection] if len(defls) > 1: rrs = np.diff([defl.earlystart for defl in defls]) hyp.meas = o.CycleMeasurements((np.mean(rrs), np.std(rrs)), (0, 0), (0, 0)) VFLUTTER_PATTERN = PatternAutomata() VFLUTTER_PATTERN.name = "Ventricular Flutter" VFLUTTER_PATTERN.Hypothesis = o.Ventricular_Flutter VFLUTTER_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENVIRONMENT, _prev_rhythm_tconst) VFLUTTER_PATTERN.add_transition(1, 2, o.QRS, ENVIRONMENT, _qrs0_tconst) VFLUTTER_PATTERN.add_transition(2, 3, o.Deflection, ABSTRACTED, _def0_tconst, _vflut_gconst) VFLUTTER_PATTERN.add_transition(3, 3, o.Deflection, ABSTRACTED, _deflection_tconst, _vflut_gconst) VFLUTTER_PATTERN.add_transition(3, 4, o.QRS, ABSTRACTED, _qrs_tconst, _vflut_gconst) VFLUTTER_PATTERN.final_states.add(4) VFLUTTER_PATTERN.abstractions[o.Deflection] = ( VFLUTTER_PATTERN.transitions[2], ) VFLUTTER_PATTERN.freeze() if __name__ == "__main__": pass
pattern.hypothesis.meas = o.CycleMeasurements((ms2sp(800), ms2sp(200)), (0, 0), (0, 0)) def _asystole_gconst(pattern, _): """General constraints of the asystole pattern.""" # The rhythm information is copied from the precedent rhythm. if pattern.evidence[o.Cardiac_Rhythm]: rhythm = pattern.evidence[o.Cardiac_Rhythm][0] pattern.hypothesis.meas = copy.copy(rhythm.meas) RHYTHMSTART_PATTERN = PatternAutomata() RHYTHMSTART_PATTERN.name = "Rhythm Start" RHYTHMSTART_PATTERN.Hypothesis = o.RhythmStart RHYTHMSTART_PATTERN.add_transition(0, 1, o.QRS, ABSTRACTED, _rstart_tconst, _rhythmstart_gconst) RHYTHMSTART_PATTERN.add_transition(1, 2, o.PWave, ABSTRACTED, _p_qrs_tconst) RHYTHMSTART_PATTERN.add_transition(2, 3, o.TWave, ABSTRACTED, _t_qrs_tconst) RHYTHMSTART_PATTERN.add_transition(1, 3, o.TWave, ABSTRACTED, _t_qrs_tconst) RHYTHMSTART_PATTERN.add_transition(1, 3) RHYTHMSTART_PATTERN.final_states.add(3) RHYTHMSTART_PATTERN.abstractions[o.QRS] = ( RHYTHMSTART_PATTERN.transitions[0], ) RHYTHMSTART_PATTERN.freeze() ASYSTOLE_PATTERN = PatternAutomata() ASYSTOLE_PATTERN.name = "Asystole" ASYSTOLE_PATTERN.Hypothesis = o.Asystole ASYSTOLE_PATTERN.add_transition(0, 1, o.Cardiac_Rhythm, ENVIRONMENT, _asyst_prev_rhythm_tconst) ASYSTOLE_PATTERN.add_transition(1, 2, o.QRS, ENVIRONMENT, _qrs1_tconst)
>= (defl.lateend - defl.earlystart) / 2.0 ) # If the Deflection is a R-Deflection, we require a margin before # the end of the twave. if isinstance(defl, o.RDeflection): verify(twave.lateend - defl.time.end > C.TW_RDEF_MIN_DIST) ########################### ### Automata definition ### ########################### TWAVE_PATTERN = PatternAutomata() TWAVE_PATTERN.name = 'T Wave' TWAVE_PATTERN.Hypothesis = o.TWave TWAVE_PATTERN.add_transition(0, 1, o.QRS, ENVIRONMENT, _t_qrs_tconst) TWAVE_PATTERN.add_transition(1, 2, o.Deflection, ABSTRACTED, _t_defl_tconst, _t_gconst) TWAVE_PATTERN.final_states.add(2) TWAVE_PATTERN.freeze() ################################################## ### Statistical knowledge stored as histograms ### ################################################## def _check_histogram(hist, value): """ Obtains a score of a value according to an histogram, between 0.0 and 1.0 """ i = 0 while i < len(hist[1]) and value > hist[1][i]:
mu_t = rtmean + k_t * meas_err sigma_t = (1.0 - k_t) * sigma_tbar # PR interval pr = 0.0 if isinstance(pattern.obs_seq[3], o.PWave): pr = qrs.time.start - pattern.obs_seq[3].earlystart prmean = pattern.obs_seq[0].meas.pq[0] pr = (pr + prmean) / 2 if prmean > 0 else pr pattern.hypothesis.meas = o.CycleMeasurements( (rr, 0.0), (mu_t, np.sqrt(sigma_t)), (pr, 0.0)) FIRST_BEAT_PATTERN = PatternAutomata() FIRST_BEAT_PATTERN.name = "First Beat" FIRST_BEAT_PATTERN.Hypothesis = o.FirstBeat FIRST_BEAT_PATTERN.add_transition(0, 1, o.QRS, ABSTRACTED, _btime_tconst) FIRST_BEAT_PATTERN.add_transition(1, 2, o.PWave, ABSTRACTED, _p_qrs_tconst) FIRST_BEAT_PATTERN.add_transition(2, 3, o.TWave, ABSTRACTED, _t_qrs_tconst, _firstbeat_gconst) FIRST_BEAT_PATTERN.add_transition(1, 3, o.TWave, ABSTRACTED, _t_qrs_tconst, _firstbeat_gconst) FIRST_BEAT_PATTERN.add_transition(1, 3, tconst=_qrs_time_tconst, gconst=_firstbeat_gconst) FIRST_BEAT_PATTERN.final_states.add(3) FIRST_BEAT_PATTERN.abstractions[o.QRS] = (FIRST_BEAT_PATTERN.transitions[0], ) FIRST_BEAT_PATTERN.freeze() CARDIAC_CYCLE_PATTERN = PatternAutomata() CARDIAC_CYCLE_PATTERN.name = "Cardiac Cycle"