Selaa lähdekoodia

Optimized allInstances with faster reverseKeyLookupMulti

Yentl Van Tendeloo 8 vuotta sitten
vanhempi
commit
262ee0bfc7

+ 27 - 7
bootstrap/object_operations.alc

@@ -10,17 +10,16 @@ Element function allInstances(model : Element, type_name : String):
 		Element keys
 		Element tm
 		String key
+		String class
+		Element results
 
 		result = create_node()
 		accepted = get_subclasses(model["metamodel"], type_name)
 
-		tm = model["type_mapping"]
-		keys = dict_keys(tm)
-		while (0 < list_len(keys)):
-			key = set_pop(keys)
-			if (set_in(accepted, tm[key])):
-				set_add(result, key)
-
+		while (read_nr_out(accepted) > 0):
+			class = set_pop(accepted)
+			results = reverseKeyLookupMulti(model["type_mapping"], class)
+			set_merge(result, results)
 		return result!
 	else:
 		log("No such type in the metamodel: " + type_name)
@@ -144,6 +143,7 @@ Element function getInstantiatableAttributes(model : Element, element : String):
 	return result!
 
 String function reverseKeyLookup(dict : Element, element : Element):
+	// TODO don't know if this AL will actually work...
 	Integer nr_in
 	Integer nr_out
 	Integer counter
@@ -159,7 +159,27 @@ String function reverseKeyLookup(dict : Element, element : Element):
 		counter = counter + 1
 	
 	return string_join(string_join("(unknown: ", cast_e2s(element)), " )")!
+
+Element function reverseKeyLookupMulti(dict : Element, element : Element):
+	// TODO don't know if this AL will actually work...
+	Integer nr_in
+	Integer nr_out
+	Integer counter
+	Element link
+	Element result
+
+	result = create_node()
+	nr_in = read_nr_in(element)
+	counter = 0
+	while (counter < nr_in):
+		if (element_eq(read_edge_src(read_in(element, counter)), dict)):
+			// Got a match
+			set_add(result, read_edge_dst(read_out(read_in(element, counter), 0)))
+
+		counter = counter + 1
 	
+	return result!
+
 String function print_dict(dict : Element):
 	Element keys
 	Element key

+ 6 - 0
bootstrap/primitives.alc

@@ -343,3 +343,9 @@ Void function dict_overwrite(d : Element, key : Element, value : Element):
 	dict_add(d, key, value)
 
 	return !
+
+Element function set_merge(a : Element, b : Element):
+	b = set_copy(b)
+	while (read_nr_out(b) > 0):
+		set_add(a, set_pop(b))
+	return a!

+ 1 - 0
interface/HUTN/includes/object_operations.alh

@@ -6,6 +6,7 @@ Element function allIncomingAssociationInstances(model: Element, target_name : S
 Element function getAttributeList(model: Element, element: String)
 Element function getInstantiatableAttributes(model: Element, element: String)
 String function reverseKeyLookup(a: Element, b: Element)
+Element function reverseKeyLookupMulti(a: Element, b: Element)
 String function print_dict(dict : Element)
 String function readAssociationSource(model : Element, name : String)
 String function readAssociationDestination(model : Element, name : String)

+ 1 - 0
interface/HUTN/includes/primitives.alh

@@ -109,3 +109,4 @@ Element function dict_copy(dict : Element)
 Element function set_to_list(s : Element)
 Element function create_tuple(a : Element, b : Element)
 Void function dict_overwrite(a : Element, b : Element, c : Element)
+Element function set_merge(sa : Element, sb : Element)

+ 22 - 334
kernel/modelverse_kernel/compiled.py

@@ -1,6 +1,22 @@
 from modelverse_kernel.primitives import PrimitiveFinished
 import modelverse_jit.runtime as jit_runtime 
 
+def reverseKeyLookupMulti(a, b, **remainder):
+    edges, = yield [("RO", [a])]
+    b_val, = yield [("RV", [b])]
+    expanded_edges = yield [("RE", [i]) for i in edges]
+    values = yield [("RV", [i[1]]) for i in expanded_edges]
+    result, = yield [("CN", [])]
+
+    for i, edge in enumerate(values):
+        if b_val == edge:
+            # Found our edge: edges[i]
+            outgoing, = yield [("RO", [edges[i]])]
+            value, = yield [("RE", [outgoing[0]])]
+            yield [("CE", [result, value[1]])]
+
+    raise PrimitiveFinished(result)
+
 def reverseKeyLookup(a, b, **remainder):
     edges, = yield [("RO", [a])]
     expanded_edges = yield [("RE", [i]) for i in edges]
@@ -14,65 +30,6 @@ def reverseKeyLookup(a, b, **remainder):
     result, = yield [("CNV", ["(unknown: %s)" % b])]
     raise PrimitiveFinished(result)
 
-"""
-def read_attribute(a, b, c, **remainder):
-    model_dict, b_val, c_val, type_mapping = \
-                    yield [("RD", [a, "model"]),
-                           ("RV", [b]),
-                           ("RV", [c]),
-                           ("RD", [a, "type_mapping"]),
-                           ]
-    model_instance, = \
-                    yield [("RD", [model_dict, b_val])]
-    edges, =        yield [("RO", [model_instance])]
-    edge_types =    yield [("RDN", [type_mapping, i]) for i in edges]
-    type_edge_val = yield [("RE", [i]) for i in edge_types]
-
-    src_nodes = set([i[0] for i in type_edge_val])
-
-    found_edges =   yield [("RDE", [i, c_val]) for i in src_nodes]
-
-    for e1 in found_edges:
-        if e1 is not None:
-            # Found an edge!
-            for i, e2 in enumerate(edge_types):
-                if e1 == e2:
-                    # The instance of this edge is the one we want!
-                    edge = edges[i]
-                    edge_val, = yield [("RE", [edge])]
-                    result = edge_val[1]
-                    raise PrimitiveFinished(result)
-    else:
-        result, = yield [("RR", [])]
-        raise PrimitiveFinished(result)
-
-    raise Exception("Error in reading edge!")
-"""
-
-"""
-def dict_eq(a, b, **remainder):
-    keys_a, keys_b = yield [("RDK", [a]), ("RDK", [b])]
-    if len(keys_a) != len(keys_b):
-        false, = yield [("CNV", [False])]
-        raise PrimitiveFinished(false)
-
-    resolved_names_a = yield [("RV", [i]) for i in keys_a]
-    resolved_names_b = yield [("RV", [i]) for i in keys_b]
-
-    if (set(resolved_names_a) != set(resolved_names_b)):
-        false, = yield [("CNV", [False])]
-        raise PrimitiveFinished(false)
-
-    values_a = yield [("RD", [a, i]) for i in resolved_names_a]
-    values_b = yield [("RD", [b, i]) for i in resolved_names_b]
-
-    dict_a = dict(zip(resolved_names_a, values_a))
-    dict_b = dict(zip(resolved_names_b, values_b))
-
-    result, = yield [("CNV", [dict_a == dict_b])]
-    raise PrimitiveFinished(result)
-"""
-
 def set_copy(a, **remainder):
     b, =         yield [("CN", [])]
     links, =     yield [("RO", [a])]
@@ -80,281 +37,6 @@ def set_copy(a, **remainder):
     _ =         yield [("CE", [b, i[1]]) for i in exp_links]
     raise PrimitiveFinished(b)
 
-"""
-def allInstances(a, b, **remainder):
-    b_val, =     yield [("RV", [b])]
-    model_dict,= yield [("RD", [a, "model"])]
-    metamodel, = yield [("RD", [a, "metamodel"])]
-    m3, =        yield [("RD", [metamodel, "metamodel"])]
-    m3_model, =  yield [("RD", [m3, "model"])]
-    mm_dict, =   yield [("RD", [metamodel, "model"])]
-    typing, =    yield [("RD", [a, "type_mapping"])]
-    elem_keys, = yield [("RDK", [model_dict])]
-    elems =     yield [("RDN", [model_dict, i]) for i in elem_keys]
-    mms =       yield [("RDN", [typing, i]) for i in elems]
-
-    # Have the type for each name
-    types_to_name_nodes = {}
-    for key, mm in zip(elem_keys, mms):
-        types_to_name_nodes.setdefault(mm, set()).add(key)
-    # And now we have the inverse mapping: for each type, we have the node containing the name
-
-    # Get the inheritance link type
-    inheritance_type, =  yield [("RD", [m3_model, "Inheritance"])]
-
-    # Now we figure out which types are valid for the specified model
-    desired_types = set()
-    mm_element, =    yield [("RD", [mm_dict, b_val])]
-
-    work_list = []
-    work_list.append(mm_element)
-    mm_typing, =     yield [("RD", [metamodel, "type_mapping"])]
-
-    while work_list:
-        mm_element = work_list.pop()
-        if mm_element in desired_types:
-            # Already been here, so stop
-            continue
-
-        # New element, so continue
-        desired_types.add(mm_element)
-
-        # Follow all inheritance links that COME IN this node, as all these are subtypes and should also match
-        incoming, =  yield [("RI", [mm_element])]
-        for i in incoming:
-            t, =     yield [("RDN", [mm_typing, i])]
-            if t == inheritance_type:
-                e, = yield [("RE", [i])]
-                # Add the source of the inheritance link to the work list
-                work_list.append(e[0])
-
-    # Now desired_types holds all the direct types that we are interested in!
-    # Construct the result out of all models that are direct instances of our specified type
-    final = set()
-    for t in desired_types:
-        final |= types_to_name_nodes.get(t, set())
-
-    # Result is a Python set with nodes, so just make this a Mv set
-    result, =    yield [("CN", [])]
-    v =         yield [("RV", [i]) for i in final]
-    _ =    yield [("CE", [result, i]) for i in final]
-    raise PrimitiveFinished(result)
-"""
-
-"""
-def add_AL(a, b, **remainder):
-    worklist = [(b, "funcdef")]
-    added = set()
-    type_cache = {}
-
-    model_dict, = yield [("RD", [a, "model"])]
-    metamodel, = yield [("RD", [a, "metamodel"])]
-    metamodel_dict, = yield [("RD", [metamodel, "model"])]
-    type_map, = yield [("RD", [a, "type_mapping"])]
-    outgoing, = yield [("RO", [model_dict])]
-    edges = yield [("RE", [i]) for i in outgoing]
-    added |= set([i[1] for i in edges])
-
-    result, = yield [("CNV", ["__%s" % b])]
-
-    # All the action language elements and their expected output links
-    type_links = {
-            "if":       [("cond", ""), ("then", ""), ("else", ""), ("next", "")],
-            "while":    [("cond", ""), ("body", ""), ("next", "")],
-            "assign":   [("var", ""), ("value", ""), ("next", "")],
-            "break":    [("while", "while")],
-            "continue": [("while", "while")],
-            "return":   [("value", "")],
-            "resolve":  [("var", "")],
-            "access":   [("var", "")],
-            "constant": [("node", "")],
-            "output":   [("node", ""), ("next", "")],
-            "global":   [("var", "String"), ("next", "")],
-            "param":    [("name", "String"), ("value", ""), ("next_param", "param")],
-            "funcdef":  [("body", ""), ("next", "")],
-            "call":     [("func", ""), ("params", "param"), ("last_param", "param"), ("next", "")],
-        }
-
-    # Already add some often used types to the type cache, so we don't have to check for their presence
-    to_str, string = yield [("CNV", ["to_str"]),
-                            ("CNV", ["String"])]
-
-    type_cache = {"to_str": to_str,
-                  "String": string}
-
-    while worklist:
-        # Fetch the element and see if we need to add it
-        worknode, expected_type = worklist.pop(0)
-        if worknode in added:
-            continue
-
-        # Determine type of element
-        if expected_type == "":
-            value, = yield [("RV", [worknode])]
-            if (isinstance(value, dict)) and ("value" in value):
-                v = value["value"]
-                if v in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]:
-                    expected_type = v
-                else:
-                    expected_type = "Any"
-            else:
-                expected_type = "Any"
-
-        # Fill the cache
-        if expected_type not in type_cache:
-            type_cache[expected_type], = yield [("CNV", [expected_type])]
-
-        # Need to add it now
-        nodename = "__%s" % worknode
-        yield [("CD", [model_dict, nodename, worknode])]
-        added.add(worknode)
-        _, =        yield [("CD", [type_map, nodename, type_cache[expected_type]])]
-
-        # Now add all its outgoing links, depending on the type we actually saw
-        links = type_links.get(expected_type, [])
-        for link in links:
-            link_name, destination_type = link
-
-            # Check if the link actually exists
-            destination, = yield [("RD", [worknode, link_name])]
-            if destination is not None:
-                # If so, we add it and continue
-                edge, = yield [("RDE", [worknode, link_name])]
-                edge_outlinks, = yield [("RO", [edge])]
-                edge_outlink = edge_outlinks[0]
-                edge_name, = yield [("RE", [edge_outlink])]
-                edge_name = edge_name[1]
-                # Now add: edge, edge_outlink, edge_name
-
-                # Add 'edge'
-                edgename = "__%s" % edge
-                yield [("CD", [model_dict, edgename, edge])]
-                added.add(edge)
-                link_type = "%s_%s" % (expected_type, link_name)
-                if link_type not in type_cache:
-                    type_cache[link_type], = yield [("CNV", [link_type])]
-                _, = yield [("CD", [type_map, edge_name, type_cache[link_type]])]
-
-                # Add 'edge_outlink'
-                edgename = "__%s" % edge_outlink
-                yield [("CD", [model_dict, edgename, edge_outlink])]
-                added.add(edge_outlink)
-                _, = yield [("CD", [type_map, edgename, type_cache["to_str"]])]
-
-                # Add 'edge_name' (if not present)
-                if edge_name not in added:
-                    edgename = "__%s" % edge_name
-                    yield [("CD", [model_dict, edgename, edge_name])]
-                    _, = yield [("CD", [type_map, edgename, type_cache["String"]])]
-                    added.add(edge_name)
-
-                # Add the destination to the worklist
-                worklist.append((destination, destination_type))
-
-    raise PrimitiveFinished(result)
-"""
-
-"""
-def get_superclasses(a, b, **remainder):
-    mm, =            yield [("RD", [a, "metamodel"])]
-    mm, =            yield [("RD", [mm, "metamodel"])]
-    m, =             yield [("RD", [mm, "model"])]
-    inheritance, =   yield [("RD", [m, "Inheritance"])]
-    model_dict, =    yield [("RD", [a, "model"])]
-    b_v, =           yield [("RV", [b])]
-    subclass, =      yield [("RD", [model_dict, b_v])]
-    type_mapping, =  yield [("RD", [a, "type_mapping"])]
-    names, =         yield [("RDK", [model_dict])]
-    elems =         yield [("RDN", [model_dict, i]) for i in names]
-    elem_to_name =  dict(zip(elems, names))
-
-    result, =        yield [("CN", [])]
-    worklist = [subclass]
-
-    touched = set()
-
-    while worklist:
-        subclass = worklist.pop()
-        res = elem_to_name[subclass]
-
-        if subclass not in touched:
-            touched.add(subclass)
-            yield [("CE", [result, res])]
-
-            outgoing, =      yield [("RO", [subclass])]
-            types =         yield [("RDN", [type_mapping, i]) for i in outgoing]
-
-            for i, t in enumerate(types):
-                if t == inheritance:
-                    # Found an inheritance link!
-                    elem = outgoing[i]
-                    srcdst, = yield [("RE", [elem])]
-                    src, dst = srcdst
-                    # Find elem in elems
-                    worklist.append(dst)
-                    if dst is None:
-                        print("Read edge gives error for edge: " + str(elem))
-
-    raise PrimitiveFinished(result)
-"""
-
-"""
-def selectPossibleIncoming(a, b, c, **remainder):
-    model_dict, =    yield [("RD", [a, "model"])]
-    limit_set_links, = \
-                    yield [("RO", [c])]
-    limit_set =     yield [("RE", [i]) for i in limit_set_links]
-    limit_set_names = [i[1] for i in limit_set]
-    name_values =   yield [("RV", [i]) for i in limit_set_names]
-    limit_set =     yield [("RD", [model_dict, i]) for i in name_values]
-
-    superclasses, = yield [("CALL_ARGS", [get_superclasses, (a, b)])]
-    vals, = yield [("RO", [superclasses])]
-    superclasses = yield [("RE", [i]) for i in vals]
-    superclasses = [i[1] for i in superclasses]
-
-    superclass_names = yield [("RV", [i]) for i in superclasses]
-    elems =         yield [("RD", [model_dict, i]) for i in superclass_names]
-
-    result, =        yield [("CN", [])]
-    for i, edge in enumerate(limit_set):
-        srcdst, =  yield [("RE", [edge])]
-        src, dst = srcdst
-        if dst in elems:
-            yield [("CE", [result, limit_set_names[i]])]
-
-    raise PrimitiveFinished(result)
-"""
-
-"""
-def selectPossibleOutgoing(a, b, c, **remainder):
-    model_dict, =    yield [("RD", [a, "model"])]
-    limit_set_links, = \
-                    yield [("RO", [c])]
-    limit_set =     yield [("RE", [i]) for i in limit_set_links]
-    limit_set_names = \
-                    [i[1] for i in limit_set]
-    name_values =   yield [("RV", [i]) for i in limit_set_names]
-    limit_set =     yield [("RD", [model_dict, i]) for i in name_values]
-
-    superclasses, = yield [("CALL_ARGS", [get_superclasses, (a, b)])]
-    vals, = yield [("RO", [superclasses])]
-    superclasses = yield [("RE", [i]) for i in vals]
-    superclasses = [i[1] for i in superclasses]
-
-    superclass_names = yield [("RV", [i]) for i in superclasses]
-    elems =         yield [("RD", [model_dict, i]) for i in superclass_names]
-
-    result, =        yield [("CN", [])]
-    for i, edge in enumerate(limit_set):
-        srcdst, =  yield [("RE", [edge])]
-        src, dst = srcdst
-        if src in elems:
-            yield [("CE", [result, limit_set_names[i]])]
-
-    raise PrimitiveFinished(result)
-"""
-
 def check_symbols(a, b, c, **remainder):
     symbols = {}
     function_name, = yield [("RV", [b])]
@@ -407,3 +89,9 @@ def instantiated_name(a, b, **remainder):
     if name_value == "":
         b, = yield [("CNV", ["__" + str(a)])]
     raise PrimitiveFinished(b)
+
+def set_merge(a, b, **remainder):
+    outputs, = yield [("RO", [b])]
+    values = yield [("RE", [i]) for i in outputs]
+    yield [("CE", [a, i[1]]) for i in values]
+    raise PrimitiveFinished(a)

+ 3 - 3
models/matches.mvc

@@ -11,12 +11,12 @@ A B {
                 Pre_Query/Place {
                     label = "2"
                 }
-                Pre_ReachabilityGraph/Place pre_3 {
-                    label = "3"
-                }
                 Pre_ReachabilityGraph/State pre_4 {
                     label = "4"
                 }
+                Pre_ReachabilityGraph/Place pre_3 {
+                    label = "3"
+                }
                 Pre_ReachabilityGraph/Contains (pre_4, pre_3) {
                     label = "5"
                 }

+ 1 - 0
models/reachability.alc

@@ -184,5 +184,6 @@ Element function reachability_graph(params : Element, output_mms : Element):
 				new_transition = instantiate_link(out_model, "Transition", "", cast_i2s(state_id), cast_i2s(target_id))
 				instantiate_attribute(out_model, new_transition, "name", read_attribute(in_model, transition, "name"))
 
+	log("# reachable states: " + cast_v2s(next_id))
 	dict_add_fast(result, "ReachabilityGraph", out_model)
 	return result!