Beispiel #1
0
 def test_inject_context_IR(self):
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Ghana',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom'
     }, {
         ctx.place: 'England',
         ctx.device: 'phone',
         ctx.datetime: '2020-04-30 12:00:00'
     }, {
         's': {
             'wikidata': 'url://wikidata',
             'worldbank': 'wbankID'
         },
         't': '2020'
     }]
     a.set(tt.CONTEXT, ctx1)
     alist = frank.context.inject_retrieval_context(a, 'worldbank')
     self.assertEqual(alist.get(tt.TIME), '2020')
Beispiel #2
0
def reduce(alist: Alist, children: List[Alist], G: InferenceGraph):
    y_predict = None
    X = []
    y = []
    data_pts = []
    for c in children:
        opVarValue = c.instantiation_value(c.get(tt.OPVAR))
        if utils.is_numeric(opVarValue) and utils.is_numeric(c.get(tt.TIME)):
            x_val = utils.get_number(c.get(tt.TIME), None)
            y_val = utils.get_number(opVarValue, None)
            X.append([x_val])
            y.append(y_val)
            data_pts.append([x_val, y_val])
    X = np.array(X)
    y = np.array(y)
    reg = LinearRegression().fit(X, y)
    x_predict = utils.get_number(alist.get(tt.TIME), None)
    y_predict = reg.predict(np.array([[x_predict]]))[0]
    prediction = [x_predict, y_predict]
    coeffs = [v for v in reg.coef_]
    coeffs.insert(0, reg.intercept_)
    fnStr = 'LIN;' + ';'.join([str(v) for v in reg.coef_])
    fnAndData = \
        """{{"function":{coeffs}, "data":{data_pts}, "prediction":{prediction}}}""".format(
            coeffs=coeffs, data_pts=data_pts, prediction=prediction)

    alist.instantiate_variable(alist.get(tt.OPVAR), y_predict)
    alist.set(tt.FNPLOT, fnAndData)

    alist.instantiate_variable(
        tt.COV,
        estimate_uncertainty(children,
                             len(data_pts) == len(children), alist.get(tt.OP),
                             len(children)))
    return alist
Beispiel #3
0
 def test_context_composition(self):
     G = InferenceGraph()
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Ghana',
             tt.PROPERTY: 'P1082',
             tt.TIME: '2023',
             tt.OBJECT: '',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom'
     }, {
         ctx.place: 'United Kingdom',
         ctx.device: 'computer',
         ctx.datetime: '2010-07-27 11:00:00'
     }, {}]
     a.set(tt.CONTEXT, ctx1)
     G.add_alist(a)
     query_ctx = frank.context.inject_query_context
     # query context should infer the ctx.accuracy from ctx.device
     op_alist = Temporal().decompose(query_ctx(a), G)
     self.assertEqual(
         (op_alist.get(tt.OP), len(G.child_alists(op_alist.id))),
         ('gpregress', 19))
Beispiel #4
0
def _search_cache(tx, alist_to_instantiate: Alist, attribute_to_instantiate,
                  search_attributes) -> (bool, list):
    alists = []
    conditions = ""
    resultStatus = False
    if not attribute_to_instantiate:
        return (resultStatus, [alist_to_instantiate])
    conditions = " AND ".join([
        f"n.{x}='{alist_to_instantiate.instantiation_value(x)}'"
        for x in search_attributes
    ])
    results = tx.run("MATCH (n:Alist)" f"WHERE {conditions}" "RETURN n")
    for node in results:
        if node[0]:
            try:
                alist = Alist(**node[0]._properties)
                if alist.is_instantiated(attribute_to_instantiate):
                    alist.set(
                        attribute_to_instantiate,
                        alist.instantiation_value(attribute_to_instantiate))
                    alist.attributes.pop('sessionId', None)
                    alist.attributes.pop('parentId', None)
                    alist.attributes.pop('src', None)
                    alists.append(alist)
                    resultStatus = True
            except:
                print("Error during alist creation from search result")
    return (resultStatus, alists)
Beispiel #5
0
 def test_inject_context_inference(self):
     G = InferenceGraph()
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Ghana',
             tt.PROPERTY: 'P1082',
             tt.TIME: '2023',
             tt.OBJECT: '',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom',
         ctx.accuracy: 'low',
         ctx.speed: 'low'
     }, {
         ctx.place: 'United Kingdom',
         ctx.device: 'phone',
         ctx.datetime: '2020-07-27 11:00:00'
     }, {}]
     a.set(tt.CONTEXT, ctx1)
     G.add_alist(a)
     op_alist = Temporal().decompose(a, G)
     self.assertEqual(op_alist.get(tt.OP), 'regress')
Beispiel #6
0
def reduce(alist: Alist, children: List[Alist], G: InferenceGraph):
    # do comparisons
    vars_to_compare = alist.get(tt.OPVAR).split(' ')

    # propagate projection vars to parent5
    propagate.projections(alist, tuple(children))
    response_var = "?_lte_"
    if len(vars_to_compare) == 0:
        alist.set(response_var, "false")
        return alist

    result = True
    if len(vars_to_compare) > 1 and utils.is_numeric(
            alist.instantiation_value(vars_to_compare[0])):
        for x in vars_to_compare[1:]:
            if utils.is_numeric(
                    alist.instantiation_value(x)) and utils.is_numeric(
                        alist.instantiation_value(x)):
                result = (utils.get_number(
                    alist.instantiation_value(
                        vars_to_compare[0]), 0) <= utils.get_number(
                            alist.instantiation_value(x), 0)) and result
            else:
                result = False
                break
    else:
        result = False
    alist.set(response_var, str(result).lower())

    # alist.instantiate_variable(tt.COV, estimate_uncertainty(
    #   children, True, alist.get(tt.OP), len(children)
    # ))
    return alist
Beispiel #7
0
 def test_getNestingVars(self):
     alist = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: '$y',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '?x',
             tt.TIME: '2010',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     alist.set('$y', {"$filter": [{"p": "type", "o": "country"}]})
     self.assertTrue(alist.nesting_variables())
Beispiel #8
0
 def test_getVariables(self):
     alist = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Africa',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '?x',
             tt.TIME: '2010',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     alist.set('#d', 34)
     variables = alist.variables()
     self.assertTrue(len(variables) == 3, "should have 3 items in dict")
Beispiel #9
0
 def test_instantiateVariables(self):
     alist = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Africa',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '?x',
             tt.TIME: '2010',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     alist.set('#d', '')
     alist.set('?x', '#d')
     alist.instantiate_variable('#d', 99)
     self.assertEqual(alist.get('?x'), 99, "OBJECT should be 99.")
Beispiel #10
0
 def test_getInstantiatedAttributes(self):
     alist = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Africa',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '?x',
             tt.TIME: '2010',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     alist.set('#d', 34)
     alist.set('?x', 100)
     instantiatedVars = alist.instantiated_attributes()
     self.assertTrue(
         len(instantiatedVars) > 0,
         "there should be 2 instantiated variables.")
Beispiel #11
0
 def test_isInstantiated2(self):
     alist = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Africa',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '?x',
             tt.TIME: '2010',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     alist.set('#d', 34)
     results = [
         alist.is_instantiated(tt.SUBJECT),
         alist.is_instantiated(tt.OBJECT),
         alist.is_instantiated('#d')
     ]
     self.assertEqual(results, [True, False, True],
                      "should result in list [True, False, True].")
Beispiel #12
0
def cli(query, context={}):
    session_id = uuid.uuid4().hex
    interactive = False
    answer = None
    if query:
        query_json = None
    else:
        print(f"\n{pcol.CYAN}FRANK CLI{pcol.RESETALL}")
        query = input(f"Enter question or alist query: \n {pcol.CYAN}>{pcol.RESETALL} ")
        context = input(f"Enter context: \n {pcol.CYAN}>{pcol.RESETALL} ")
        query_json = None
        interactive = True
    
    # check if input is question or alist
    if type(query) == str and '{' in query and '"' in query:
        query_json = json.loads(query)
    elif type(query) == dict:
        query_json = query
    else:
        parser = frank.query_parser.parser.Parser()
        parsedQuestion = parser.getNextSuggestion(query)
        query_json = parsedQuestion['alist']

    if query_json:        
        alist = Alist(**query_json)
        if context:
            context_json = json.loads(context) if type(context) == str else context
            alist.set(tt.CONTEXT, context_json)
        print(f"{pcol.YELLOW} ├── query alist:{json.dumps(alist.attributes)} {pcol.RESETALL}")
        print(f"{pcol.YELLOW} └── session id:{session_id} {pcol.RESETALL}\n")
        launch = Launcher()
        launch.start(alist, session_id, inference_graphs)

        if session_id in inference_graphs:
            answer = inference_graphs[session_id]['answer']['answer']
            graph = inference_graphs[session_id]['graph']
            graph.plot_plotly(query)
        if interactive:
            input("\npress any key to exit")
    else:
        print("\nCould not parse question. Please try again.")
    return answer
Beispiel #13
0
    def why(self, G: InferenceGraph, alist: Alist, decomp_op, in_place=True):
        ''' Explain a decomposition of this alist. 
            Assumes a failed instantiation of this alist following KB searches'''
        expl = ""
        time = ""
        children = G.child_alists(alist.id)
        if alist.get(tt.TIME):
            time = f" in {alist.get(tt.TIME)}"
        if decomp_op == 'temporal':
            expl = f"Could not find the {alist.get(tt.PROPERTY)} of {alist.instantiation_value(tt.SUBJECT)}{time}. "
            decomp_items = []
            # for c in alist.children[0].children:
            for c in children:
                decomp_items.append(c.get(tt.TIME))
            if len(decomp_items) >= 2:
                expl += f"Attempted to infer the required value{time} by finding the {alist.get(tt.PROPERTY)} of {alist.instantiation_value(tt.SUBJECT)} " + \
                    f"at other times between {min(decomp_items)} and {max(decomp_items)}."

        elif decomp_op == 'geospatial':
            expl = f"Could not find the {alist.get(tt.PROPERTY)} of {alist.instantiation_value(tt.SUBJECT)}{time}. "
            decomp_items = []
            # for c in alist.children[0].children:
            for c in G.child_alists(children[0].id):
                decomp_items.append(c.instantiation_value(tt.SUBJECT))
            entities = ''
            if len(decomp_items) > 8:
                entities = f"{', '.join(decomp_items[0:8])} etc"
            else:
                entities = f"{', '.join(decomp_items[0:len(decomp_items)-1])} and {decomp_items[-1]}"
            if decomp_items:
                expl += f"Finding the {alist.get(tt.PROPERTY)}{time} for the constituent parts of " + \
                    f" {alist.instantiation_value(tt.SUBJECT)}: {entities}."

        elif decomp_op == 'normalize':
            expl = f"Need to solve the sub-queries before determining the {alist.get(tt.PROPERTY)}{time}."

        elif decomp_op == 'comparison':
            expl = f"Need to solve the sub-queries to determine the items to compare."

        if in_place:
            alist.set("why", expl)
            G.add_alist(alist)
Beispiel #14
0
 def test_inject_context_query(self):
     # query context
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Ghana',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom'
     }, {
         ctx.place: 'United Kingdom',
         ctx.device: 'phone',
         ctx.datetime: '2010-04-30 12:00:00'
     }, {}]
     a.set(tt.CONTEXT, ctx1)
     alist = frank.context.inject_query_context(a)
     ctx2 = a.get(tt.CONTEXT)
     self.assertEqual(ctx2[0][ctx.accuracy], 'low')
Beispiel #15
0
 def test_add_context(self):
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Africa',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '',
             tt.TIME: '2010',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom'
     }, {
         ctx.place: 'United Kingdom',
         ctx.device: 'phone',
         ctx.datetime: '2020-04-30 12:00:00'
     }, {}]
     a.set(tt.CONTEXT, ctx1)
     ctx2 = a.get(tt.CONTEXT)
     alistJson = json.loads(json.dumps(a.attributes))
     self.assertEqual(ctx1, ctx2, "Context values do not match")
Beispiel #16
0
 def test_wikidata_query_object_with_context4(self):
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'London',
             tt.PROPERTY: 'P6',
             tt.OBJECT: '',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom'
     }, {
         ctx.place: 'England',
         ctx.device: 'phone',
         ctx.datetime: '2020-04-30 12:00:00',
         tt.SUBJECT: 'http://www.wikidata.org/entity/Q84'
     }, {}]
     a.set(tt.CONTEXT, ctx1)
     frank.context.inject_retrieval_context(a, "wikidata")
     result = wikidata.find_property_object(a)
     self.assertTrue(len(result) > 0, "should have at least one item")
Beispiel #17
0
 def test_inject_query_context_loc(self):
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'London',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom'
     }, {
         ctx.place: 'England',
         ctx.device: 'phone',
         ctx.datetime: '2020-04-30 12:00:00'
     }, {}]
     a.set(tt.CONTEXT, ctx1)
     alist = frank.context.inject_query_context(a)
     self.assertEqual(
         alist.get(tt.CONTEXT)[2][tt.SUBJECT]['wikidata'],
         'http://www.wikidata.org/entity/Q84')
Beispiel #18
0
def inject_retrieval_context(alist: Alist, source) -> Alist:
    """
    Inject context values into alist attributes to be used for Information Retrieval from KBs.
    """

    context = alist.get(tt.CONTEXT)
    if not context:
        return alist

    context = alist.get(tt.CONTEXT)
    context_store = {}
    context_store = {
        **context[0],
        **context[1],
        **context[2]
    } if context else {}
    for a in alist.attributes.keys():
        if a in context_store:
            if type(context_store[a]) is dict and source in context_store[a]:
                alist.set(a, context_store[a][source])
            elif type(context_store[a]) is not dict:
                alist.set(a, context_store[a])
    return alist
Beispiel #19
0
def reduce(alist: Alist, children: List[Alist], G: InferenceGraph):
    # do comparisons
    print(children)
    vars_to_compare = alist.get(tt.OPVAR).split(' ')

    # # copy projection vars of min alist to parent
    # for c in children:
    #   if c.get(tt.OP) != 'comp':
    #     for vc in vars_to_compare:
    #       if vc in c.attributes:
    #         projVars = c.projection_variables()
    #         alist.instantiate_variable(vc, c.instantiation_value(vc), insert_missing=True)

    # for x in vars_to_compare:
    #   if alist.is_instantiated(x) == False:
    #     # return None
    #     pass

    # propagate projection vars to parent
    propagate.projections(alist, tuple(children))
    response_var = "?_eq_"
    if len(vars_to_compare) == 0:
        alist.set(response_var, "false")
        return alist

    result = True
    for x in vars_to_compare:
        result = (alist.instantiation_value(x) ==
                  alist.instantiation_value(vars_to_compare[0])) and result

    alist.instantiate_variable(response_var, str(
        result).lower(), insert_missing=True)

    # alist.instantiate_variable(tt.COV, estimate_uncertainty(
    #   children, True, alist.get(tt.OP), len(children)
    # ))
    return alist
Beispiel #20
0
 def test_search_with_context(self):
     a = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Ghana',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     ctx1 = [{
         ctx.nationality: 'United Kingdom'
     }, {
         ctx.place: 'United Kingdom',
         ctx.device: 'phone',
         ctx.datetime: '2010-04-30 12:00:00'
     }, {}]
     a.set(tt.CONTEXT, ctx1)
     a = frank.context.inject_retrieval_context(a, "wikidata")
     result = wikidata.find_property_object(a)
     v = '0'
     if result:
         v = result[0].get(tt.OBJECT)
     self.assertAlmostEqual(utils.get_number(v, 0), 26908262, 1)
Beispiel #21
0
 def test_getInstantiationValue(self):
     alist = Alist(
         **{
             tt.ID: '1',
             tt.SUBJECT: 'Africa',
             tt.PROPERTY: 'P1082',
             tt.OBJECT: '?x',
             tt.TIME: '2010',
             tt.OPVAR: '?x',
             tt.COST: 1
         })
     alist.set('#d', '')
     alist.set('?x', '#d')
     alist.set('$y', '')
     alist.instantiate_variable('#d', 99)
     results = (alist.instantiation_value('#d'),
                alist.instantiation_value('?x'),
                alist.instantiation_value(tt.OBJECT),
                alist.instantiation_value(tt.TIME),
                alist.instantiation_value('$y'))
     self.assertEqual(results, (99, 99, 99, '2010', ''))
Beispiel #22
0
def inject_query_context(alist: Alist) -> Alist:
    """
    Inject context values into query alist.
    """
    context = alist.get(tt.CONTEXT)
    if not context and len(context) < 2:
        return alist

    if len(context) == 3 and \
            ctx.accuracy not in context[0] and \
            ctx.device in context[1]:
        if context[1][ctx.device] == 'phone':
            context[0][ctx.accuracy] = 'low'
        elif context[1][ctx.device] == 'computer':
            context[0][ctx.accuracy] = 'high'

    # alist context
    alist.set(tt.CONTEXT, context)

    # check for time in alist
    if not alist.get(tt.TIME):
        time = get_env_context(alist, ctx.datetime)
        year = ''
        if time:
            year = datetime.strptime(time, '%Y-%m-%d %H:%M:%S').year
            year = str(year)
        alist.set(tt.TIME, year)
        set_query_context(alist, tt.TIME, year)

    # disambiguate entity based on user location context
    user_place = get_env_context(alist, ctx.place)
    user_nationality = get_user_context(alist, ctx.nationality)
    s_context = {}
    if user_place:
        if alist.get(tt.SUBJECT) == '':
            # if subject is empty, use place context
            alist.set(tt.SUBJECT, user_place)
            for source_name, source in {
                    'wikidata': wikidata,
                    'worldbank': worldbank
            }.items():
                locations = source.find_location_of_entity(
                    alist.get(tt.SUBJECT))
                set_flag = False
                # use nationality as context for the user's location if no subject
                if user_nationality:
                    for loc in locations:
                        if user_nationality == loc[2]:
                            s_context[source_name] = loc[0]
                            set_flag = True
                            break

                if not set_flag and locations and source_name in ['wikidata']:
                    # if no location matched the nationality, use the first location
                    s_context[source_name] = locations[0][0]
                    # set_query_context(alist, tt.SUBJECT, {source_name: locations[0][0]})
        else:
            for source_name, source in {
                    'wikidata': wikidata,
                    'worldbank': worldbank
            }.items():
                locations = source.find_location_of_entity(
                    alist.get(tt.SUBJECT))
                set_flag = False
                for loc in locations:
                    if user_place == loc[2]:
                        s_context[source_name] = loc[0]
                        set_flag = True
                        break
                # if set_flag:
                #     set_query_context(alist, tt.SUBJECT, s_context)
                if not set_flag and locations and source_name in ['wikidata']:
                    # if no location matched the nationality, use the first location
                    s_context[source_name] = locations[0][0]
                    # set_query_context(alist, tt.SUBJECT, {source_name: locations[0][0]})
        if s_context:
            set_query_context(alist, tt.SUBJECT, s_context)

    return alist
Beispiel #23
0
    def what(self,
             G: InferenceGraph,
             alist: Alist,
             is_reduced: bool,
             in_place=True):
        ''' Explain a reduction of this alist. 
        '''
        what = ''
        how = ''
        time = ""
        if alist.get(tt.TIME):
            time = f" in {alist.get(tt.TIME)}"

        if not is_reduced:
            if alist.get(tt.OP) in ['eq', 'gt', 'gte', 'lt', 'lte']:
                what = f"Failed to compare the values since the values of all items being compare are not known. "
            elif alist.get(tt.OP) in ['comp']:
                what = f"Failed to solve the sub-problem. "
            elif alist.get(tt.OP) in ['value', 'values']:
                what = f"Failed to determine the {self.ops_text[alist.get(tt.OP)]} of {alist.get(tt.PROPERTY)}{time}."
            else:
                what = f"Failed to calculate the {self.ops_text[alist.get(tt.OP)]} of {alist.get(tt.PROPERTY)}{time}."

        else:
            if alist.get(tt.OP) in ['eq', 'gt', 'gte', 'lt', 'lte']:
                vars_compared = alist.get(tt.OPVAR).split(' ')
                if len(vars_compared) > 1:
                    what = f"Inferred value is '{alist.instantiation_value('?'+ alist.get(tt.OP))}'."
                    how = f"Did a comparison to determine if {alist.instantiation_value(vars_compared[0])} is " + \
                          f"{self.ops_text[alist.get(tt.OP)]} {alist.instantiation_value(vars_compared[1])}."
            elif alist.get(tt.OP) in ['comp']:
                listed_str = ''

                listed = alist.instantiation_value(alist.get(tt.OPVAR))
                if listed:
                    listed = listed.split(',')
                    if len(listed) > 8:
                        listed_str += f"{', '.join(listed[0:8])}, etc"
                    else:
                        listed_str += ', '.join(listed)

                if listed_str:
                    what = f"Solved the sub-query and found the following values: {listed_str}."
            else:
                inferred_value = ''
                projected = alist.projection_variables()
                if projected:
                    inferred_value = list(projected.values())[0]
                if not inferred_value:
                    inferred_value = alist.instantiation_value(
                        alist.get(tt.OPVAR))
                if inferred_value:
                    if ':' in alist.get(tt.PROPERTY):
                        listed_str = ''
                        listed = alist.instantiation_value(alist.get(
                            tt.OPVAR)).split(',')
                        if len(listed) > 8:
                            listed_str += f"{', '.join(listed[0:8])}, etc"
                        else:
                            listed_str += ', '.join(listed)
                        what = f"The {alist.get(tt.PROPERTY).split(':')[1]} values found for the sub-query include: {listed_str}."
                    elif (projected
                          or inferred_value) and not alist.get(tt.PROPERTY):
                        # for alists with just a projected value but no property
                        what = f"An input value for operation is {inferred_value}."
                    elif projected and alist.get(
                            tt.OPVAR) not in projected and alist.get(
                                tt.OP) in ['max', 'min']:
                        what = f"The entity whose {alist.get(tt.PROPERTY)}{time} has the {self.ops_text[alist.get(tt.OP)]} of {alist.instantiation_value(alist.get(tt.OPVAR))} is {inferred_value}."
                    elif projected and alist.get(
                            tt.OPVAR) not in projected and alist.get(
                                tt.OP) not in ['max', 'min']:
                        what = f"The {self.ops_text[alist.get(tt.OP)]} of the {alist.get(tt.PROPERTY)}{time} of {inferred_value} is {alist.instantiation_value(alist.get(tt.OPVAR))}."
                    else:
                        what = f"The {self.ops_text[alist.get(tt.OP)]} of the {alist.get(tt.PROPERTY)} of {alist.instantiation_value(tt.SUBJECT)}{time} is {inferred_value}."
                if alist.get(tt.OP) in [
                        'regress', 'nnpredict', 'linregress', 'gpregress',
                        'nnregress'
                ]:
                    decomp_items = []
                    children = G.child_alists(alist.id)
                    # for c in alist.children[0].children:
                    for c in G.child_alists(children[0].id):
                        decomp_items.append(c.get(tt.TIME))
                    if len(decomp_items) > 0:
                        how = f"Generated a regression function from times between {min(decomp_items)} and {max(decomp_items)}."

        if in_place:
            alist.set("what", what)
            alist.set("how", how)
            G.add_alist(alist)