Browse Source

generalized type inference, to also work with unit inference

Cláudio Gomes 3 years ago
parent
commit
8f2d6b5a31

+ 67 - 0
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation.tests/input/power_window_case_study/lazy.sa

@@ -0,0 +1,67 @@
+semantic adaptation reactive moore LazySA lazy_sa
+at "./path/to/LazySA.fmu"
+
+	for inner fmu Controller controller
+	at "./path/to/Controller.fmu"
+	with input ports Bool obj_detected, Bool passenger_up, Bool passenger_down, Bool passenger_stop, Bool driver_up, Bool driver_down, Bool driver_stop
+	with output ports Bool up, Bool down, Bool stop
+
+input ports obj_detected -> controller.obj_detected,
+			passenger_up -> controller.passenger_up,
+			passenger_down -> controller.passenger_down,
+			passenger_stop -> controller.passenger_stop,
+			driver_up -> controller.driver_up,
+			driver_down -> controller.driver_down,
+			driver_stop -> controller.driver_stop
+
+param 	INIT_OBJ_DETECTED := false,
+		INIT_PASSENGER_UP := false,
+		INIT_PASSENGER_DOWN := false,
+		INIT_PASSENGER_STOP := false,
+		INIT_DRIVER_UP := false,
+		INIT_DRIVER_DOWN := false,
+		INIT_DRIVER_STOP := false;
+
+control var	tn := -1.0,
+			tl := -1.0,
+			prev_obj_detected := INIT_OBJ_DETECTED,
+			prev_passenger_up := INIT_PASSENGER_UP,
+			prev_passenger_down := INIT_PASSENGER_DOWN,
+			prev_passenger_stop := INIT_PASSENGER_STOP,
+			prev_driver_up := INIT_DRIVER_UP,
+			prev_driver_down := INIT_DRIVER_DOWN,
+			prev_driver_stop := INIT_DRIVER_STOP;
+
+control rules {
+	if (tl < 0.0){
+		tl := t;
+	}
+	
+	var step_size := min(H, tn - t); 
+	if (lazy_sa.obj_detected != prev_obj_detected or
+		lazy_sa.passenger_up != prev_passenger_up or
+		lazy_sa.passenger_down != prev_passenger_down or
+		lazy_sa.passenger_stop != prev_passenger_stop or
+		lazy_sa.driver_up != prev_driver_up or
+		lazy_sa.driver_down != prev_driver_down or
+		lazy_sa.driver_stop != prev_driver_stop or
+		(t+H) >= tn
+	){
+		var Real step_to_be_done := (t+H-tl);
+		var step_done := do_step(controller, t, step_to_be_done); 
+		tn := tl + step_done + get_next_time_step(controller); 
+		step_size := tl + step_done - t; 
+		tl := tl + step_done; 
+	}
+	
+	prev_obj_detected := lazy_sa.obj_detected;
+	prev_passenger_up := lazy_sa.passenger_up;
+	prev_passenger_down := lazy_sa.passenger_down;
+	prev_passenger_stop := lazy_sa.passenger_stop;
+	prev_driver_up := lazy_sa.driver_up;
+	prev_driver_down := lazy_sa.driver_down;
+	prev_driver_stop := lazy_sa.driver_stop;
+	
+	return step_size;
+}
+

+ 74 - 0
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation.tests/input/power_window_case_study/loop_canonical.sa

@@ -0,0 +1,74 @@
+semantic adaptation reactive moore LoopSA loop_sa
+at "./path/to/LoopSA.fmu"
+	
+	for inner fmu WindowSA window_sa
+		at "./path/to/WindowSA.fmu"
+		with input ports displacement (rad), speed (rad/s), reaction_force (N)
+		with output ports disp (m), tau (N.m)
+	
+	for inner fmu Obstacle obstacle
+		at "./path/to/Obstacle.fmu"
+		with input ports disp (m)
+		with output ports reaction_force (m)
+	
+	coupled as window_sa.disp -> obstacle.disp, 
+				obstacle.reaction_force -> window_sa.reaction_force
+
+input ports displacement, speed
+
+output ports tau
+
+param 	MAXITER := 10, REL_TOL := 1e-05, ABS_TOL := 1e-05,
+		INIT_LOOP_SA_DISPLACEMENT := 0.0,
+		INIT_LOOP_SA_SPEED := 0.0,
+		INIT_WINDOW_SA_DISP := 0.0,
+		INIT_WINDOW_SA_TAU := 0.0,
+		INIT_OBSTACLE_REACTION_FORCE := 0.0;
+
+control var prev_disp := 0.0;
+control rules {
+	var repeat := false;
+	for (var iter in 0 .. MAXITER) {
+		save_state(obstacle);
+		save_state(window_sa);
+		obstacle.disp := prev_disp;
+		do_step(obstacle,t,H);
+		window_sa.reaction_force := stored_obstacle_reaction_force;
+		do_step(window_sa,t,H);
+		
+		repeat := is_close(prev_disp, stored_window_sa_disp, REL_TOL, ABS_TOL);
+		prev_disp := stored_window_sa_disp;
+		if (repeat) {
+			break;
+		} else {
+			rollback(obstacle);
+			rollback(window_sa);
+		}
+	}
+	return H;
+}
+
+in var 	stored_loop_sa_displacement := INIT_LOOP_SA_DISPLACEMENT,
+		stored_loop_sa_speed := INIT_LOOP_SA_SPEED;
+in rules {
+	true -> {
+		stored_loop_sa_displacement := loop_sa.displacement;
+		stored_loop_sa_speed := loop_sa.speed;
+	} --> {
+		window_sa.displacement := stored_loop_sa_displacement;
+		window_sa.speed := stored_loop_sa_speed;
+	};
+}
+
+out var	stored_window_sa_disp := INIT_WINDOW_SA_DISP,
+		stored_window_sa_tau := INIT_WINDOW_SA_TAU,
+		stored_obstacle_reaction_force := INIT_OBSTACLE_REACTION_FORCE;
+out rules{
+	true -> {
+		stored_window_sa_disp := window_sa.disp;
+		stored_window_sa_tau := window_sa.tau;
+		stored_obstacle_reaction_force := obstacle.tau;
+	} --> {
+		loop_sa.tau := stored_window_sa_tau;
+	};
+}

+ 1 - 1
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation.tests/input/power_window_case_study/window_sa.BASE.sa

@@ -6,7 +6,7 @@ at "./path/to/WindowSA.fmu"
 		with input ports displacement(rad), speed (rad/s), reaction_force (N)
 		with output ports height (cm), reaction_torque (N.m)
 
-output ports disp (m)  <- window.height, tau
+output ports disp (m)  <- window.height, tau (N)
 
 out rules {
 	true -> {} --> {

+ 6 - 1
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation.tests/src/be/uantwerpen/ansymo/semanticadaptation/tests/SemanticAdaptationGeneratorTest.xtend

@@ -18,13 +18,18 @@ class SemanticAdaptationGeneratorTest extends AbstractSemanticAdaptationTest{
 	
 	@Inject extension CompilationTestHelper
 	
+	@Test def window_SA() { __generate('input/power_window_case_study/window_sa.BASE.sa') }
+	
 	@Test def lazy_SA() { __generate('input/power_window_case_study/lazy.sa') }
 	
+	
 	def void __generate(String filename) {
 		//readFile(filename).assertCompilesTo('oracles/power_window_case_study/lazy.BASE.sa')
 		
 		readFile(filename).compile(new IAcceptor<CompilationTestHelper.Result>(){
-			override accept(Result t) { }
+			override accept(Result t) {
+				// TODO: What kind of acceptance for generated text files? File comparison is very very brittle.
+			}
 		})
 		
 	}

+ 370 - 26
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation/src/be/uantwerpen/ansymo/semanticadaptation/generator/SemanticAdaptationCanonicalGenerator.xtend

@@ -23,6 +23,7 @@ import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.StringLiteral
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Variable
 import java.io.ByteArrayOutputStream
 import java.util.HashMap
+import org.eclipse.emf.ecore.EObject
 import org.eclipse.emf.ecore.resource.Resource
 import org.eclipse.xtext.EcoreUtil2
 import org.eclipse.xtext.generator.AbstractGenerator
@@ -95,8 +96,51 @@ class SemanticAdaptationCanonicalGenerator extends AbstractGenerator {
 	
 	def canonicalize(Adaptation sa){
 		
+		//inferUnits(sa)
+		
+		
 		// Type inference
-		inferTypes(sa)
+		genericDeclarationInferenceAlgorithm(sa , 
+			[// getField
+				element | {
+					if (element instanceof SingleParamDeclaration) {
+						return element.type
+					} else if (element instanceof Port){
+						return element.type
+					} else if (element instanceof SingleVarDeclaration){
+						return element.type
+					} else {
+						throw new Exception("Unexpected element type: " + element)
+					}
+				}
+			],
+			[// setField
+				element, value | {
+					if (element instanceof SingleParamDeclaration) {
+						element.type = value as String
+					} else if (element instanceof Port){
+						element.type = value as String
+					} else if (element instanceof SingleVarDeclaration){
+						element.type = value as String
+					} else {
+						throw new Exception("Unexpected element type: " + element)
+					}
+				}
+			],
+			[// inferField
+				element | {
+					if (element instanceof SingleParamDeclaration) {
+						return extractTypeFromExpression(element.expr, element.name)
+					} else if (element instanceof Port){
+						return getPortType(element)
+					} else if (element instanceof SingleVarDeclaration){
+						return extractTypeFromExpression(element.expr, element.name)
+					} else {
+						throw new Exception("Unexpected element type: " + element)
+					}
+				}
+			]
+		)
 		
 		// TODO Add input ports
 		
@@ -105,6 +149,184 @@ class SemanticAdaptationCanonicalGenerator extends AbstractGenerator {
 		
 	}
 	
+	def genericDeclarationInferenceAlgorithm(Adaptation sa, 
+												(EObject)=>Object getField, 
+												(EObject, Object)=>void setField,
+												(EObject)=>Object inferField
+	){
+		println("Running generic inference algorithm...")
+		
+		/*
+		 * Dumbest (and simplest) algorithm for this is a fixed point computation:
+		 * 1. Look for every var/port declaration
+		 * 2. If that var has a XXX already, nothing else to be done.
+		 * 3. If that var has no XXX declared, then
+		 * 3.1 If var/port has an initial value or connection, then
+		 * 3.1.1 If the initial_value/connection has a XXX declared, then var gets that XXX.
+		 * 3.1.2 Otherwise, nothing else to be done.
+		 * 3.2 If var/port has no initial value or connection then this either is a missing feature, or an error.
+		 * 3.3 If something has changed, go to 1. Otherwise, end.
+		 */
+		var fixedPoint = false
+		var untypedElementsCounter = 0
+		while (! fixedPoint){
+			fixedPoint = true
+			untypedElementsCounter = 0
+			
+			println("Inferring parameter fields...")
+			
+			for (paramDeclarations : sa.params) {
+				for (paramDeclaration : paramDeclarations.declarations) {
+					println("Computing field for param " + paramDeclaration.name)
+					if(getField.apply(paramDeclaration) !== null){
+						println("Already has been inferred: " + getField.apply(paramDeclaration))
+					} else {
+						println("Has not been inferred yet.")
+						//var inferredTypeAttempt = extractTypeFromExpression(paramDeclaration.expr, paramDeclaration.name)
+						var inferredTypeAttempt = inferField.apply(paramDeclaration)
+						if (inferredTypeAttempt !== null){
+							//paramDeclaration.type = inferredTypeAttempt
+							setField.apply(paramDeclaration, inferredTypeAttempt)
+							fixedPoint = false
+							println("Got new field: " + inferredTypeAttempt)
+						} else {
+							untypedElementsCounter++
+							println("Cannot infer field now.")
+						}
+					}
+				}
+			}
+			
+			if(sa.inner !== null){
+				if(sa.inner instanceof InnerFMUDeclarationFull){
+					var innerFMUFull = sa.inner as InnerFMUDeclarationFull
+					for(fmu : innerFMUFull.fmus){
+						println("Inferring port fields of FMU " + fmu.name)
+						for (port : EcoreUtil2.getAllContentsOfType(fmu, Port)) {
+							if(getField.apply(port) !== null){
+								println("Already has a type: " + port.type)
+							//} else if(inferPortType(port)) {
+							} else {
+								// TODO Refactor this with the above code, to check and infer field of generic type.
+								var inferredTypeAttempt = inferField.apply(port)
+								if (inferredTypeAttempt !== null){
+									setField.apply(port, inferredTypeAttempt)
+									fixedPoint = false
+									println("Got new field: " + inferredTypeAttempt)
+								} else {
+									untypedElementsCounter++
+									println("Cannot infer field now.")
+								}
+							}
+						}
+					}
+					
+					if (innerFMUFull.connection.size > 0){
+						println("Inferring port fields using internal scenario bindings.")
+						for (binding : innerFMUFull.connection){
+							if (getField.apply(binding.src.port) !== null && getField.apply(binding.tgt.port) !== null){
+								println("Both ports have fields already.")
+							//} else if (inferPortTypeViaConnection(binding)){
+							} else {
+								var inferredTypeAttempt = inferPortFieldViaConnection(binding, getField, setField, inferField)
+								if (inferredTypeAttempt !== null){
+									setField.apply(binding, inferredTypeAttempt)
+									fixedPoint = false
+									untypedElementsCounter--
+									println("Got new field: " + inferredTypeAttempt)
+								} else {
+									println("Cannot infer field from binding now.")
+								}
+							}
+						}
+					}
+				} else {
+					throw new Exception("Field inference only supported for InnerFMUDeclarationFull.")
+				}
+			}
+			
+			println("Inferring external port fields...")
+			
+			for (port : sa.inports) {
+				//if (port.type !== null){
+				if (getField.apply(port) !== null){
+					println("Already has a field: " + getField.apply(port))
+					//if (pushPortType(port)){
+					if (pushPortField(port, getField, setField, inferField)){
+						fixedPoint = false
+						untypedElementsCounter--
+					} 
+				//} else if (inferPortType(port)){
+				} else {
+					// TODO Refactor this with the above code, to check and infer field of generic type.
+					var inferredTypeAttempt = inferField.apply(port)
+					if (inferredTypeAttempt !== null){
+						setField.apply(port, inferredTypeAttempt)
+						fixedPoint = false
+						println("Got new field: " + inferredTypeAttempt)
+					} else {
+						untypedElementsCounter++
+						println("Cannot infer field now.")
+					}
+				}
+			}
+			for (port : sa.outports) {
+				//if (port.type !== null){
+				// TODO Refactor this with the above code, the treatment to external output ports is the exact same as for external input ports.
+				if (getField.apply(port) !== null){
+					println("Already has a field: " + getField.apply(port))
+					//if (pushPortType(port)){
+					if (pushPortField(port, getField, setField, inferField)){
+						fixedPoint = false
+						untypedElementsCounter--
+					} 
+				//} else if (inferPortType(port)){
+				} else {
+					// TODO Refactor this with the above code, to check and infer field of generic type.
+					var inferredTypeAttempt = inferField.apply(port)
+					if (inferredTypeAttempt !== null){
+						setField.apply(port, inferredTypeAttempt)
+						fixedPoint = false
+						println("Got new field: " + inferredTypeAttempt)
+					} else {
+						untypedElementsCounter++
+						println("Cannot infer field now.")
+					}	
+				}
+			}
+			
+			println("Inferring all other declaration fields...")
+			
+			for (varDeclaration : EcoreUtil2.getAllContentsOfType(sa, SingleVarDeclaration)) {
+				println("Computing type for declaration " + varDeclaration.name)
+				//if(varDeclaration.type !== null){
+				if(getField.apply(varDeclaration) !== null){
+					println("Already has a field: " + getField.apply(varDeclaration))
+				} else {
+					//var inferredTypeAttempt = extractTypeFromExpression(varDeclaration.expr, varDeclaration.name)
+					var inferredTypeAttempt = inferField.apply(varDeclaration)
+					if (inferredTypeAttempt !== null){
+						//varDeclaration.type = inferredTypeAttempt
+						setField.apply(varDeclaration, inferredTypeAttempt)
+						fixedPoint = false
+						println("Got new type: " + inferredTypeAttempt)
+					} else {
+						untypedElementsCounter++
+						println("Cannot infer field now.")
+					}
+				}
+			}
+			
+			println("Ended iteration with unfielded elements remaining: " + untypedElementsCounter)
+		} // while (! fixedPoint)
+		
+		if (untypedElementsCounter > 0){
+			throw new Exception("Could not infer all fields. There are " + untypedElementsCounter + " unfielded elements.")
+		}
+		
+		println("Running generic inference algorithm... DONE")
+	}
+	
 	def inferTypes(Adaptation sa){
 		println("Inferring types...")
 		
@@ -280,11 +502,31 @@ class SemanticAdaptationCanonicalGenerator extends AbstractGenerator {
 		return null
 	}
 	
+	def inferPortFieldViaConnection(Connection binding, 
+										(EObject)=>Object getField, 
+										(EObject, Object)=>void setField,
+										(EObject)=>Object inferField
+	){
+		var Object resultField = null
+		if (getField.apply(binding.src.port) !== null && getField.apply(binding.tgt.port) !== null){
+			throw new Exception("Wrong way of using this function. It assumes type is not inferred yet.")
+		} else if (getField.apply(binding.src.port) !== null){
+			//binding.tgt.port.type = binding.src.port.type
+			resultField = getField.apply(binding.src.port)
+			println("Target port "+ binding.tgt.port.name +" got new type: " + resultField)
+		} else if (getField.apply(binding.tgt.port) !== null){
+			//binding.src.port.type = binding.tgt.port.type
+			resultField = getField.apply(binding.tgt.port)
+			println("Target port "+ binding.src.port.name +" got new type: " + resultField)
+		}
+		
+		return resultField
+	}
+	
 	def inferPortTypeViaConnection(Connection binding){
 		var typeInferred = false
 		
 		if (binding.src.port.type !== null && binding.tgt.port.type !== null){
-			println("Both ports have types already.")
 			throw new Exception("Wrong way of using this function. It assumes type is not inferred yet.")
 		} else if (binding.src.port.type !== null){
 			binding.tgt.port.type = binding.src.port.type
@@ -297,34 +539,48 @@ class SemanticAdaptationCanonicalGenerator extends AbstractGenerator {
 		return typeInferred
 	}
 	
-	def inferPortTypeFromBindings(Port port){
+	def pushPortField(Port port, 
+							(EObject)=>Object getField, 
+							(EObject, Object)=>void setField,
+							(EObject)=>Object inferField){
 		var typeInferred = false
-		if(port.sourcedependency !== null){
-			println("Has a source dependency: " + port.sourcedependency.port.name)
-			if(port.sourcedependency.port.type === null){
-				println("Dependency has no type yet.")
+		println("Pushing field of port " + port.name + " to its bindings.")
+		
+		if(getField.apply(port) === null){
+			println("Has no field to be pushed.")
+			// TODO Throw exception wrong usage
+		} else {
+			println("Pushing field: " + getField.apply(port))
+			if(port.sourcedependency !== null){
+				println("Has a source dependency: " + port.sourcedependency.port.name)
+				if(getField.apply(port.sourcedependency.port) === null){
+					//port.sourcedependency.port.type = port.type
+					setField.apply(port.sourcedependency.port, getField.apply(port))
+					println("Port " + port.sourcedependency.port.name + " got new type: " + getField.apply(port.sourcedependency.port))
+					typeInferred = true
+				} else {
+					println("Source port already has field.")
+				}
 			} else {
-				port.type = port.sourcedependency.port.type
-				println("Got new type: " + port.type)
-				typeInferred = true
+				println("Has no source dependency.")
 			}
-		} else {
-			println("Has no source dependency.")
-		}
-		
-		if (port.targetdependency !== null && !typeInferred) {
-			println("Has a target dependency: " + port.targetdependency.owner.name + "." + port.targetdependency.port.name)
-			if(port.targetdependency.port.type === null){
-				//println("Port object: " + port.targetdependency.port)
-				println("Dependency has no type yet.")
+			
+			if (port.targetdependency !== null) {
+				println("Has a target dependency: " + port.targetdependency.port.name)
+				if(getField.apply(port.targetdependency.port) === null){
+					println("Dependency has no field yet.")
+					//port.targetdependency.port.type = port.type
+					setField.apply(port.targetdependency.port, getField.apply(port))
+					println("Port " + port.targetdependency.port.name + " got new type: " + getField.apply(port.targetdependency.port))
+					typeInferred = true
+				} else {
+					println("Target port already has field.")
+				}
 			} else {
-				port.type = port.targetdependency.port.type
-				println("Got new type: " + port.type)
-				typeInferred = true
+				println("Has no target dependency.")
 			}
-		} else {
-			println("Has no target dependency, or type has already been inferred from source dependency.")
 		}
+		
 		return typeInferred
 	}
 	
@@ -367,17 +623,105 @@ class SemanticAdaptationCanonicalGenerator extends AbstractGenerator {
 		return typeInferred
 	}
 	
+	def getPortType(Port port){
+		var typeInferred = false
+		
+		println("Computing type for port " + port.name)
+		//println("Object: " + port)
+		
+		var String returnType = null
+		
+		if(port.type !== null){
+			throw new Exception("Wrong way of using this function. It assumes type is not inferred yet.")
+		} else {
+			println("Has no type.")
+			
+			println("Attempting to infer type from units.")
+			if (port.unity !== null){
+				returnType = "Real"
+				println("Got new type: " + returnType)
+				typeInferred = true
+			} else {
+				println("Attempting to infer type from bindings.")
+
+				if(port.sourcedependency !== null){
+					println("Has a source dependency: " + port.sourcedependency.port.name)
+					if(port.sourcedependency.port.type === null){
+						println("Dependency has no type yet.")
+					} else {
+						returnType = port.sourcedependency.port.type
+						println("Got new type: " + returnType)
+						typeInferred = true
+					}
+				} else {
+					println("Has no source dependency.")
+				}
+				
+				if (port.targetdependency !== null && !typeInferred) {
+					println("Has a target dependency: " + port.targetdependency.owner.name + "." + port.targetdependency.port.name)
+					if(port.targetdependency.port.type === null){
+						//println("Port object: " + port.targetdependency.port)
+						println("Dependency has no type yet.")
+					} else {
+						returnType = port.targetdependency.port.type
+						println("Got new type: " + port.type)
+						typeInferred = true
+					}
+				} else {
+					println("Has no target dependency, or type has already been inferred from source dependency.")
+				}
+			}
+		}
+		
+		return returnType
+	}
+	
 	def inferPortType(Port port){
 		var typeInferred = false
 		println("Computing type for port " + port.name)
 		//println("Object: " + port)
 			
 		if(port.type !== null){
-			println("Already has a type: " + port.type)
 			throw new Exception("Wrong way of using this function. It assumes type is not inferred yet.")
 		} else {
 			println("Has no type.")
-			typeInferred = inferPortTypeFromBindings(port)
+			
+			println("Attempting to infer type from units.")
+			if (port.unity !== null){
+				port.type = "Real"
+				println("Got new type: " + port.type)
+				typeInferred = true
+			} else {
+				println("Attempting to infer type from bindings.")
+
+				if(port.sourcedependency !== null){
+					println("Has a source dependency: " + port.sourcedependency.port.name)
+					if(port.sourcedependency.port.type === null){
+						println("Dependency has no type yet.")
+					} else {
+						port.type = port.sourcedependency.port.type
+						println("Got new type: " + port.type)
+						typeInferred = true
+					}
+				} else {
+					println("Has no source dependency.")
+				}
+				
+				if (port.targetdependency !== null && !typeInferred) {
+					println("Has a target dependency: " + port.targetdependency.owner.name + "." + port.targetdependency.port.name)
+					if(port.targetdependency.port.type === null){
+						//println("Port object: " + port.targetdependency.port)
+						println("Dependency has no type yet.")
+					} else {
+						port.type = port.targetdependency.port.type
+						println("Got new type: " + port.type)
+						typeInferred = true
+					}
+				} else {
+					println("Has no target dependency, or type has already been inferred from source dependency.")
+				}
+				
+			}
 		}
 		
 		return typeInferred