Module declare4py.constraint_checkers.relation

Expand source code
from ..enums import TraceState
from ..models import CheckerResult
from ..parsers import parse_data_cond, parse_time_cond
from datetime import timedelta

# Defining global and local functions/variables to use within eval() to prevent code injection
glob = {'__builtins__': None}


# mp-responded-existence constraint checker
# Description:
# The future constraining and history-based constraint
# respondedExistence(a, b) indicates that, if event a occurs in the trace
# then event b occurs in the trace as well.
# Event a activates the constraint.
def mp_responded_existence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    pendings = []
    num_fulfillments = 0
    num_violations = 0
    num_pendings = 0

    for event in trace:
        if event["concept:name"] == a:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                pendings.append(event)

    for event in trace:
        if not pendings:
            break

        if event["concept:name"] == b:
            for A in reversed(pendings):
                locl = {'A': A, 'T': event, 'timedelta': timedelta, 'abs': abs, 'float': float}
                if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                    pendings.remove(A)
                    num_fulfillments += 1

    if done:
        num_violations = len(pendings)
    else:
        num_pendings = len(pendings)

    num_activations = num_fulfillments + num_violations + num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif done and num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)


# mp-response constraint checker
# Description:
# The future constraining constraint response(a, b) indicates that
# if event a occurs in the trace, then event b occurs after a.
# Event a activates the constraint.
def mp_response(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    pendings = []
    num_fulfillments = 0
    num_violations = 0
    num_pendings = 0

    for event in trace:
        if event["concept:name"] == a:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                pendings.append(event)

        if pendings and event["concept:name"] == b:
            for A in reversed(pendings):
                locl = {'A': A, 'T': event, 'timedelta': timedelta, 'abs': abs, 'float': float}
                if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                    pendings.remove(A)
                    num_fulfillments += 1

    if done:
        num_violations = len(pendings)
    else:
        num_pendings = len(pendings)

    num_activations = num_fulfillments + num_violations + num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_pendings > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_pendings == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif done and num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)


# mp-alternate-response constraint checker
# Description:
# The future constraining constraint alternateResponse(a, b) indicates that
# each time event a occurs in the trace then event b occurs afterwards
# before event a recurs.
# Event a activates the constraint.
def mp_alternate_response(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    pending = None
    num_activations = 0
    num_fulfillments = 0
    num_pendings = 0

    for event in trace:
        if event["concept:name"] == a:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                pending = event
                num_activations += 1

        if event["concept:name"] == b and pending is not None:
            locl = {'A': pending, 'T': event, 'timedelta': timedelta, 'abs': abs, 'float': float}
            if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                pending = None
                num_fulfillments += 1

    if not done and pending is not None:
        num_pendings = 1

    num_violations = num_activations - num_fulfillments - num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0 or (done and num_pendings > 0):
        state = TraceState.VIOLATED
    elif done and num_violations == 0 and num_pendings == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)


# mp-chain-response constraint checker
# Description:
# The future constraining constraint chain_response(a, b) indicates that,
# each time event a occurs in the trace, event b occurs immediately afterwards.
# Event a activates the constraint.
def mp_chain_response(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0
    num_pendings = 0

    for index, event in enumerate(trace):

        if event["concept:name"] == a:
            locl = {'A': event}

            if eval(activation_rules, glob, locl):
                num_activations += 1

                if index < len(trace) - 1:
                    if trace[index+1]["concept:name"] == b:
                        locl = {'A': event, 'T': trace[index+1], 'timedelta': timedelta, 'abs': abs, 'float': float}
                        if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                            num_fulfillments += 1
                else:
                    if not done:
                        num_pendings = 1

    num_violations = num_activations - num_fulfillments - num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0 or (done and num_pendings > 0):
        state = TraceState.VIOLATED
    elif done and num_violations == 0 and num_pendings == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)


# mp-precedence constraint checker
# Description:
# The history-based constraint precedence(a,b) indicates that event b occurs
# only in the trace, if preceded by a. Event b activates the constraint.
def mp_precedence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0
    Ts = []

    for event in trace:
        if event["concept:name"] == a:
            Ts.append(event)

        if event["concept:name"] == b:
            locl = {'A': event}

            if eval(activation_rules, glob, locl):
                num_activations += 1

                for T in Ts:
                    locl = {'A': event, 'T': T, 'timedelta': timedelta, 'abs': abs, 'float': float}
                    if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                        num_fulfillments += 1
                        break

    num_violations = num_activations - num_fulfillments
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=None,
                         num_activations=num_activations, state=state)


# mp-alternate-precedence constraint checker
# Description:
# The history-based constraint alternatePrecedence(a, b) indicates that
# each time event b occurs in the trace
# it is preceded by event a and no other event b can recur in between.
# Event b activates the constraint.
def mp_alternate_precedence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0
    Ts = []

    for event in trace:
        if event["concept:name"] == a:
            Ts.append(event)

        if event["concept:name"] == b:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                num_activations += 1
                for T in Ts:
                    locl = {'A': event, 'T': T, 'timedelta': timedelta, 'abs': abs, 'float': float}
                    if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                        num_fulfillments += 1
                        break
                Ts = []

    num_violations = num_activations - num_fulfillments
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=None,
                         num_activations=num_activations, state=state)


# mp-chain-precedence constraint checker
# Description:
# The history-based constraint chain_precedence(a, b) indicates that,
# each time event b occurs in the trace, event a occurs immediately beforehand.
# Event b activates the constraint.
def mp_chain_precedence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0

    for index, event in enumerate(trace):
        if event["concept:name"] == b:
            locl = {'A': event}

            if eval(activation_rules, glob, locl):
                num_activations += 1

                if index != 0 and trace[index-1]["concept:name"] == a:
                    locl = {'A': event, 'T': trace[index-1], 'timedelta': timedelta, 'abs': abs, 'float': float}
                    if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                        num_fulfillments += 1

    num_violations = num_activations - num_fulfillments
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=None,
                         num_activations=num_activations, state=state)

Functions

def mp_alternate_precedence(trace, done, a, b, rules)
Expand source code
def mp_alternate_precedence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0
    Ts = []

    for event in trace:
        if event["concept:name"] == a:
            Ts.append(event)

        if event["concept:name"] == b:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                num_activations += 1
                for T in Ts:
                    locl = {'A': event, 'T': T, 'timedelta': timedelta, 'abs': abs, 'float': float}
                    if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                        num_fulfillments += 1
                        break
                Ts = []

    num_violations = num_activations - num_fulfillments
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=None,
                         num_activations=num_activations, state=state)
def mp_alternate_response(trace, done, a, b, rules)
Expand source code
def mp_alternate_response(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    pending = None
    num_activations = 0
    num_fulfillments = 0
    num_pendings = 0

    for event in trace:
        if event["concept:name"] == a:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                pending = event
                num_activations += 1

        if event["concept:name"] == b and pending is not None:
            locl = {'A': pending, 'T': event, 'timedelta': timedelta, 'abs': abs, 'float': float}
            if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                pending = None
                num_fulfillments += 1

    if not done and pending is not None:
        num_pendings = 1

    num_violations = num_activations - num_fulfillments - num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0 or (done and num_pendings > 0):
        state = TraceState.VIOLATED
    elif done and num_violations == 0 and num_pendings == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)
def mp_chain_precedence(trace, done, a, b, rules)
Expand source code
def mp_chain_precedence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0

    for index, event in enumerate(trace):
        if event["concept:name"] == b:
            locl = {'A': event}

            if eval(activation_rules, glob, locl):
                num_activations += 1

                if index != 0 and trace[index-1]["concept:name"] == a:
                    locl = {'A': event, 'T': trace[index-1], 'timedelta': timedelta, 'abs': abs, 'float': float}
                    if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                        num_fulfillments += 1

    num_violations = num_activations - num_fulfillments
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=None,
                         num_activations=num_activations, state=state)
def mp_chain_response(trace, done, a, b, rules)
Expand source code
def mp_chain_response(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0
    num_pendings = 0

    for index, event in enumerate(trace):

        if event["concept:name"] == a:
            locl = {'A': event}

            if eval(activation_rules, glob, locl):
                num_activations += 1

                if index < len(trace) - 1:
                    if trace[index+1]["concept:name"] == b:
                        locl = {'A': event, 'T': trace[index+1], 'timedelta': timedelta, 'abs': abs, 'float': float}
                        if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                            num_fulfillments += 1
                else:
                    if not done:
                        num_pendings = 1

    num_violations = num_activations - num_fulfillments - num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0 and num_pendings == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0 or (done and num_pendings > 0):
        state = TraceState.VIOLATED
    elif done and num_violations == 0 and num_pendings == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)
def mp_precedence(trace, done, a, b, rules)
Expand source code
def mp_precedence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    num_activations = 0
    num_fulfillments = 0
    Ts = []

    for event in trace:
        if event["concept:name"] == a:
            Ts.append(event)

        if event["concept:name"] == b:
            locl = {'A': event}

            if eval(activation_rules, glob, locl):
                num_activations += 1

                for T in Ts:
                    locl = {'A': event, 'T': T, 'timedelta': timedelta, 'abs': abs, 'float': float}
                    if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                        num_fulfillments += 1
                        break

    num_violations = num_activations - num_fulfillments
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=None,
                         num_activations=num_activations, state=state)
def mp_responded_existence(trace, done, a, b, rules)
Expand source code
def mp_responded_existence(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    pendings = []
    num_fulfillments = 0
    num_violations = 0
    num_pendings = 0

    for event in trace:
        if event["concept:name"] == a:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                pendings.append(event)

    for event in trace:
        if not pendings:
            break

        if event["concept:name"] == b:
            for A in reversed(pendings):
                locl = {'A': A, 'T': event, 'timedelta': timedelta, 'abs': abs, 'float': float}
                if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                    pendings.remove(A)
                    num_fulfillments += 1

    if done:
        num_violations = len(pendings)
    else:
        num_pendings = len(pendings)

    num_activations = num_fulfillments + num_violations + num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_violations == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif done and num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)
def mp_response(trace, done, a, b, rules)
Expand source code
def mp_response(trace, done, a, b, rules):
    activation_rules = parse_data_cond(rules["activation"])
    correlation_rules = parse_data_cond(rules["correlation"])
    time_rule = parse_time_cond(rules["time"])

    pendings = []
    num_fulfillments = 0
    num_violations = 0
    num_pendings = 0

    for event in trace:
        if event["concept:name"] == a:
            locl = {'A': event}
            if eval(activation_rules, glob, locl):
                pendings.append(event)

        if pendings and event["concept:name"] == b:
            for A in reversed(pendings):
                locl = {'A': A, 'T': event, 'timedelta': timedelta, 'abs': abs, 'float': float}
                if eval(correlation_rules, glob, locl) and eval(time_rule, glob, locl):
                    pendings.remove(A)
                    num_fulfillments += 1

    if done:
        num_violations = len(pendings)
    else:
        num_pendings = len(pendings)

    num_activations = num_fulfillments + num_violations + num_pendings
    vacuous_satisfaction = rules["vacuous_satisfaction"]
    state = None

    if not vacuous_satisfaction and num_activations == 0:
        if done:
            state = TraceState.VIOLATED
        else:
            state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_pendings > 0:
        state = TraceState.POSSIBLY_VIOLATED
    elif not done and num_pendings == 0:
        state = TraceState.POSSIBLY_SATISFIED
    elif done and num_violations > 0:
        state = TraceState.VIOLATED
    elif done and num_violations == 0:
        state = TraceState.SATISFIED

    return CheckerResult(num_fulfillments=num_fulfillments, num_violations=num_violations, num_pendings=num_pendings,
                         num_activations=num_activations, state=state)