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
### 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], ) RHYTHMBLOCK_PATTERN.obs_proc = _rhythmblock_obs_proc RHYTHMBLOCK_PATTERN.freeze()
_qrs_fin_pause_tconst) EXTRASYSTOLE_PATTERN.add_transition(7, 8, o.PWave, ABSTRACTED, _p_qrs_tconst) EXTRASYSTOLE_PATTERN.add_transition(8, 9, o.TWave, ABSTRACTED, _t_qrs_tconst, _extrasyst_gconst) EXTRASYSTOLE_PATTERN.add_transition(7, 9, o.TWave, ABSTRACTED, _t_qrs_tconst, _extrasyst_gconst) EXTRASYSTOLE_PATTERN.add_transition(7, 9, gconst=_extrasyst_gconst) ##Second way: Ventricular extrasystole without compensatory pause EXTRASYSTOLE_PATTERN.add_transition(3, 10, o.QRS, ABSTRACTED, _qrs_ext_tconst(True), _qrs_ext_gconst_npause) EXTRASYSTOLE_PATTERN.add_transition(10, 11, o.TWave, ABSTRACTED, _t_qrs_tconst) EXTRASYSTOLE_PATTERN.add_transition(10, 12, o.QRS, ABSTRACTED, _qrs_fin_npause_tconst) EXTRASYSTOLE_PATTERN.add_transition(11, 12, o.QRS, ABSTRACTED, _qrs_fin_npause_tconst) EXTRASYSTOLE_PATTERN.add_transition(12, 13, o.PWave, ABSTRACTED, _p_qrs_tconst) EXTRASYSTOLE_PATTERN.add_transition(13, 14, o.TWave, ABSTRACTED, _t_qrs_tconst, _extrasyst_gconst) EXTRASYSTOLE_PATTERN.add_transition(12, 14, o.TWave, ABSTRACTED, _t_qrs_tconst, _extrasyst_gconst) EXTRASYSTOLE_PATTERN.add_transition(12, 14, gconst=_extrasyst_gconst) EXTRASYSTOLE_PATTERN.final_states.add(9) EXTRASYSTOLE_PATTERN.final_states.add(14) EXTRASYSTOLE_PATTERN.abstractions[o.QRS] = ( EXTRASYSTOLE_PATTERN.transitions[4], EXTRASYSTOLE_PATTERN.transitions[8], EXTRASYSTOLE_PATTERN.transitions[14], ) EXTRASYSTOLE_PATTERN.freeze()
# BIGEMINY_PATTERN.add_transition(8, 9, o.PWave, ABS, get_p_tconst(2)) BIGEMINY_PATTERN.add_transition(8, 10, o.TWave, ABS, get_t_tconst(2)) BIGEMINY_PATTERN.add_transition(8, 10) BIGEMINY_PATTERN.add_transition(9, 10, o.TWave, ABS, get_t_tconst(2)) # BIGEMINY_PATTERN.add_transition(10, 11, o.PWave, ABS, get_p_tconst(3)) BIGEMINY_PATTERN.add_transition(10, 12, o.TWave, ABS, get_t_tconst(3)) BIGEMINY_PATTERN.add_transition(10, 12) BIGEMINY_PATTERN.add_transition(11, 12, o.TWave, ABS, get_t_tconst(-2)) # BIGEMINY_PATTERN.add_transition(12, 13, o.PWave, ABS, get_p_tconst(-1)) BIGEMINY_PATTERN.add_transition(12, 14, o.TWave, ABS, get_t_tconst(-1)) BIGEMINY_PATTERN.add_transition(12, 14) BIGEMINY_PATTERN.add_transition(13, 14, o.TWave, ABS, get_t_tconst(-1)) # Each new cycle adds a new extrasystole and a new regular QRS # V BIGEMINY_PATTERN.add_transition(14, 15, o.QRS, ABS, _ect_qrs_tconst) # N BIGEMINY_PATTERN.add_transition(15, 16, o.QRS, ABS, _reg_qrs_tconst, _cycle_finished_gconst) # And the corresponding optional P and T waves. BIGEMINY_PATTERN.add_transition(16, 11, o.PWave, ABS, get_p_tconst(-2)) BIGEMINY_PATTERN.add_transition(16, 12, o.TWave, ABS, get_t_tconst(-2)) BIGEMINY_PATTERN.add_transition(16, 12) # Pattern finishing BIGEMINY_PATTERN.final_states.add(14) BIGEMINY_PATTERN.abstractions[o.QRS] = (BIGEMINY_PATTERN.transitions[2], ) BIGEMINY_PATTERN.obs_proc = _rhythm_obs_proc BIGEMINY_PATTERN.freeze()
TRIGEMINY_PATTERN.add_transition(11, 13, o.TWave, ABS, get_t_tconst(3)) TRIGEMINY_PATTERN.add_transition(11, 13) TRIGEMINY_PATTERN.add_transition(12, 13, o.TWave, ABS, get_t_tconst(-3)) # TRIGEMINY_PATTERN.add_transition(13, 14, o.PWave, ABS, get_p_tconst(-2)) TRIGEMINY_PATTERN.add_transition(13, 15, o.TWave, ABS, get_t_tconst(-2)) TRIGEMINY_PATTERN.add_transition(13, 15) TRIGEMINY_PATTERN.add_transition(14, 15, o.TWave, ABS, get_t_tconst(-2)) # TRIGEMINY_PATTERN.add_transition(15, 16, o.PWave, ABS, get_p_tconst(-1)) TRIGEMINY_PATTERN.add_transition(15, 17, o.TWave, ABS, get_t_tconst(-1)) TRIGEMINY_PATTERN.add_transition(15, 17) TRIGEMINY_PATTERN.add_transition(16, 17, o.TWave, ABS, get_t_tconst(-1)) # Each new cycle adds three more QRS complexes. # N TRIGEMINY_PATTERN.add_transition(17, 18, o.QRS, ABS, _reg_nae_tconst) # V TRIGEMINY_PATTERN.add_transition(18, 19, o.QRS, ABS, _ect_qrs_tconst) # N TRIGEMINY_PATTERN.add_transition(19, 20, o.QRS, ABS, _reg_ae_tconst, _cycle_finished_gconst) # And the corresponding P and T waves. TRIGEMINY_PATTERN.add_transition(20, 12, o.PWave, ABS, get_p_tconst(-3)) TRIGEMINY_PATTERN.add_transition(20, 13, o.TWave, ABS, get_t_tconst(-3)) TRIGEMINY_PATTERN.add_transition(20, 13) # TRIGEMINY_PATTERN.final_states.add(17) TRIGEMINY_PATTERN.abstractions[o.QRS] = (TRIGEMINY_PATTERN.transitions[2], ) TRIGEMINY_PATTERN.obs_proc = _rhythm_obs_proc TRIGEMINY_PATTERN.freeze()
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) COUPLET_PATTERN.add_transition(7, 8, o.PWave, ABS, _p_tconst) COUPLET_PATTERN.add_transition(7, 9, o.TWave, ABS, _t_tconst) COUPLET_PATTERN.add_transition(7, 9) COUPLET_PATTERN.add_transition(8, 9, o.TWave, ABS, _t_tconst) COUPLET_PATTERN.final_states.add(9) COUPLET_PATTERN.abstractions[o.QRS] = (COUPLET_PATTERN.transitions[2], ) COUPLET_PATTERN.freeze()
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()
# T waves are searched after the necessary QRS have been observed. ST = 9 + C.AFIB_MIN_NQRS - 4 for i in range(C.AFIB_MIN_NQRS - 2): AFIB_PATTERN.add_transition(ST, ST + 1, o.TWave, ABSTRACTED, get_t_tconst(i + 1)) AFIB_PATTERN.add_transition(ST, ST + 1) ST += 1 # T wave for the last observed QRS, and cycle QRS observation. AFIB_PATTERN.add_transition(ST, ST + 1, o.TWave, ABSTRACTED, get_t_tconst(-1)) AFIB_PATTERN.add_transition(ST, ST + 1) AFIB_PATTERN.add_transition(ST + 1, ST, o.QRS, ABSTRACTED, _qrs_tconst, _qrs_gconst) AFIB_PATTERN.final_states.add(8) AFIB_PATTERN.final_states.add(ST + 1) AFIB_PATTERN.abstractions[o.QRS] = (AFIB_PATTERN.transitions[5], ) AFIB_PATTERN.obs_proc = _rhythm_obs_proc AFIB_PATTERN.freeze() ######################### ### Helper structures ### ######################### # The following polynomial gives us the minimum number of Non-Empty Cells in # the RdR plot to positively detect atrial fibrillation, as described in # "Lian: A simple method to detect Atrial Fibrillation using RR intervals" _NEC = scipy.interpolate.interpolate.lagrange([32, 64, 128], [23, 40, 65]) if __name__ == "__main__": pass
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 = 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) ASYSTOLE_PATTERN.add_transition(2, 3, o.QRS, ABSTRACTED, _qrs2_tconst, _asystole_gconst) ASYSTOLE_PATTERN.add_transition(3, 4, o.PWave, ABSTRACTED, _p_qrs_tconst) ASYSTOLE_PATTERN.add_transition(4, 5, o.TWave, ABSTRACTED, _t_qrs_tconst) ASYSTOLE_PATTERN.add_transition(3, 5, o.TWave, ABSTRACTED, _t_qrs_tconst) ASYSTOLE_PATTERN.add_transition(3, 5) ASYSTOLE_PATTERN.final_states.add(5) ASYSTOLE_PATTERN.abstractions[o.QRS] = (ASYSTOLE_PATTERN.transitions[2], )
# 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]: i += 1 if i == 0 or i == len(hist[1]): return 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" CARDIAC_CYCLE_PATTERN.Hypothesis = o.Normal_Cycle CARDIAC_CYCLE_PATTERN.add_transition(0, 1, o.CardiacCycle, ENVIRONMENT, _envbeat_tconst) CARDIAC_CYCLE_PATTERN.add_transition(1, 2, o.QRS, ENVIRONMENT, _envbeat_tconst) CARDIAC_CYCLE_PATTERN.add_transition(2, 3, o.QRS, ABSTRACTED, _btime_tconst) CARDIAC_CYCLE_PATTERN.add_transition(3, 4, o.PWave, ABSTRACTED, _p_qrs_tconst) CARDIAC_CYCLE_PATTERN.add_transition(4, 5, o.TWave, ABSTRACTED, _t_qrs_tconst, _cycle_gconst) CARDIAC_CYCLE_PATTERN.add_transition(3, 5, o.TWave, ABSTRACTED, _t_qrs_tconst, _cycle_gconst) CARDIAC_CYCLE_PATTERN.add_transition(3, 5,