Randy Paredis 4 年之前
父節點
當前提交
50acbb065b

+ 6 - 0
doc/changelog.rst

@@ -3,6 +3,12 @@ Changelog
 
 .. code-block:: text
 
+    Version 1.4
+        * Bugfixes:
+             #21: KeyError CBD <...> in LinearSolver.constructInput
+             #22: AssertionError because a DelayBlock is part of an algebraic
+                  loop at iteration 1.
+
     Version 1.3
         *   Optimized LaTeX renderer. Now, it can also output a stepwise
             trace of the system.

+ 3 - 0
docs.sh

@@ -0,0 +1,3 @@
+cd doc
+export PYTHONPATH="$PYTHONPATH:../src"
+make html

+ 11 - 11
examples/Fibonacci/Fibonacci_experiment.py

@@ -15,14 +15,14 @@ cbd = FibonacciGen("FibonacciGen")
 sim = Simulator(cbd)
 sim.run(10)
 
-data = cbd.getSignal('OUT1')
-t, v = [t for t, _ in data], [v for _, v in data]
-
-print(v)
-fig = plt.figure()
-ax = fig.subplots()
-ax.set_title("Fibonacci Numbers")
-ax.set_xlabel("N")
-ax.set_ylabel("Value")
-ax.scatter(t, v)
-plt.show()
+# data = cbd.getSignal('OUT1')
+# t, v = [t for t, _ in data], [v for _, v in data]
+#
+# print(v)
+# fig = plt.figure()
+# ax = fig.subplots()
+# ax.set_title("Fibonacci Numbers")
+# ax.set_xlabel("N")
+# ax.set_ylabel("Value")
+# ax.scatter(t, v)
+# plt.show()

+ 6 - 1
examples/Fibonacci/Fibonacci_simple.py

@@ -14,9 +14,10 @@ class FibonacciGen(CBD):
         # Create the Blocks
         self.addBlock(DelayBlock("delay1"))
         self.addBlock(DelayBlock("delay2"))
-        self.addBlock(AdderBlock("sum"))
+        self.addBlock(AdderBlock("sum", 3))
         self.addBlock(ConstantBlock("zero", value=(0)))
         self.addBlock(ConstantBlock("one", value=(1)))
+        self.addBlock(IntegratorBlock("int"))
 
         # Create the Connections
         self.addConnection("delay1", "delay2", output_port_name='OUT1', input_port_name='IN1')
@@ -26,5 +27,9 @@ class FibonacciGen(CBD):
         self.addConnection("sum", "OUT1", output_port_name='OUT1')
         self.addConnection("zero", "delay1", output_port_name='OUT1', input_port_name='IC')
         self.addConnection("one", "delay2", output_port_name='OUT1', input_port_name='IC')
+        self.addConnection("one", "int", output_port_name='OUT1', input_port_name='IC')
+        self.addConnection("one", "int", output_port_name='OUT1', input_port_name='delta_t')
+        self.addConnection("sum", "int", output_port_name='OUT1', input_port_name='IN1')
+        self.addConnection("int", "sum", output_port_name='OUT1', input_port_name='IN3')
 
 

+ 2 - 2
src/CBD/converters/CBD2C/__init__.py

@@ -13,7 +13,7 @@ Note:
 	To compile the generated code, execute :code:`gcc <filename>.c lsolve.c -lm`.
 """
 from CBD.scheduling import TopologicalScheduler
-from CBD.solver import GaussianJordanLinearSolver
+from CBD.solver import LinearSolver
 from CBD.depGraph import DepGraph, createDepGraph, gvDepGraph
 from CBD.converters.latexify import CBD2Latex, _BLOCK_MAP
 import CBD.naivelog as naivelog
@@ -52,7 +52,7 @@ class CBD2C:
 		for block in self.model.getBlocks():
 			block.setBlockName(block.getBlockName().replace("-", "_"))
 		self.scheduler = TopologicalScheduler()
-		self.solver = GaussianJordanLinearSolver(naivelog.getLogger("CBD"))
+		self.solver = LinearSolver(naivelog.getLogger("CBD"))
 
 	def get_blocks(self):
 		"""

+ 7 - 8
src/CBD/scheduling.py

@@ -17,15 +17,15 @@ class Scheduler:
 		ensure all strongly connected components are grouped together!
 
 	Args:
-		recompute_at (iter):    An iterable of numeric values, identifying the times at
+		recompute_at (iter):    An iterable of numeric values, identifying the iterations at
 								which the schedule must be recomputed. When :code:`True`,
 								the schedule will be recomputed every time. Defaults to
-								:code:`(0.0,)` (i.e. only at simulation start).
+								:code:`(0, 1)` (i.e. only at simulation start and iteration 1).
 		rates (dict):           A dictionary of :code:`block path -> rate`; indentifying how often
 								they should fire. The rate is a float, which will be compared
 								against the time. :code:`None` identifies the empty dictionary.
 	"""
-	def __init__(self, recompute_at=(0.0,), rates=None):
+	def __init__(self, recompute_at=(0, 1), rates=None):
 		self.recompte_at = recompute_at
 		self.rates = {} if rates is None else rates
 		self.__last_schedule = None
@@ -76,7 +76,7 @@ class Scheduler:
 		"""
 		self.rates[block_name] = rate
 
-	def obtain(self, depGraph, curIt, time, rtime):
+	def obtain(self, depGraph, curIt, time):
 		"""
 		Obtains the schedule at a specific iteration/time, optionally recomputing
 		the value if required.
@@ -85,13 +85,12 @@ class Scheduler:
 			depGraph (CBD.depGraph.DepGraph):   The dependency graph of the model.
 			curIt (int):                        The current iteration value.
 			time (float):                       The current simulation time.
-			rtime (float):                      The current relative time.
 		"""
 		if self.recompte_at is True:
 			return self.schedule(depGraph, curIt, time)
 		else:
-			for t in self.recompte_at:
-				if abs((t - rtime) - time) < 1e-6:
+			for it in self.recompte_at:
+				if it == curIt:
 					self.__last_schedule = self.schedule(depGraph, curIt, time)
 					break
 			if self.__last_schedule is None:  # Force computation of schedule
@@ -112,7 +111,7 @@ class Scheduler:
 
 class TopologicalScheduler(Scheduler):
 	"""
-	Does a topological sort of the dependency graph.
+	Does a topological sort of the dependency graph, using Tarjan's algorithm.
 
 	Note:
 		This code was previously located in the :class:`CBD.depGraph.DepGraph` and

+ 4 - 4
src/CBD/simulator.py

@@ -3,7 +3,7 @@ import time
 import threading
 from . import naivelog
 from .depGraph import createDepGraph
-from .solver import GaussianJordanLinearSolver
+from .solver import LinearSolver
 from .realtime.threadingBackend import ThreadingBackend, Platform
 from .util import PYTHON_VERSION, hash64
 from .scheduling import TopologicalScheduler
@@ -61,7 +61,7 @@ class Simulator:
 	     - :code:`False`
 	     - :func:`setProgressBar`
 	   * - strong component system solver
-	     - :class:`CBD.solver.GaussianJordanLinearSolver`
+	     - :class:`CBD.solver.LinearSolver`
 	     - N/A
 	"""
 	def __init__(self, model):
@@ -112,7 +112,7 @@ class Simulator:
 		
 		# TODO: make this variable, given more solver implementations
 		# self.__solver = SympySolver(self.__logger)
-		self.__solver = GaussianJordanLinearSolver(self.__logger)
+		self.__solver = LinearSolver(self.__logger)
 
 	def run(self, term_time=None):
 		"""
@@ -473,7 +473,7 @@ class Simulator:
 		if curIt < 2 or self.__sim_data[0] is None:
 			self.__sim_data[0] = createDepGraph(self.model, curIt)
 			# self.__sim_data[1] = self.__sim_data[0].getStrongComponents(curIt)
-		self.__sim_data[1] = self.__scheduler.obtain(self.__sim_data[0], curIt, self.getTime(), self.getRelativeTime())
+		self.__sim_data[1] = self.__scheduler.obtain(self.__sim_data[0], curIt, self.getTime())
 		self.__computeBlocks(self.__sim_data[1], self.__sim_data[0], self.__sim_data[2])
 		self.__sim_data[2] += 1
 		self.signal("poststep")

+ 13 - 10
src/CBD/solver.py

@@ -63,7 +63,10 @@ class LinearSolver(Solver):
 			self._logger.fatal("Cannot solve non-linear algebraic loop.\nSelf: {}\nComponents: {}".format(path, component))
 
 	def __isLinear(self, strongComponent):
-		"""Determines if an algebraic loop describes a linear equation or not
+		"""Determines if an algebraic loop describes a linear equation or not.
+
+		For a block to comprise the strong component, at least one of its dependencies must be in the strong
+		component as well.
 
         Args:
             strongComponent (list): The detected loop, in a list (of BaseBlock instances)
@@ -74,10 +77,6 @@ class LinearSolver(Solver):
 		# TO IMPLEMENT
 
 		"""
-		The strong component parameter is a list of blocks that comprise the strong component.
-		For a block to comprise the strong component, at least one of its dependencies must be in the strong
-		component as well.
-		
 		A non-linear equation is generated when the following conditions occur:
 		 (1) there is a multiplication operation being performed between two unknowns.
 		 (2) there is an invertion operation being performed in an unknown.
@@ -91,21 +90,21 @@ class LinearSolver(Solver):
 		"""
 
 		# WON'T APPEAR: Constant, Sequence, Time, Logging
-		# LINEAR: Negator, Adder, Delay, Input, Output, Wire, TimedGate
-		# NON-LINEAR: Inverter, Modulo, Root, LT, EQ, LTE, Not, Or, And, MUX, Generic, ABS, Int
+		# LINEAR: Negator, Adder, Delay, Input, Output, Wire
+		# NON-LINEAR: Inverter, Modulo, Root, LT, EQ, LTE, Not, Or, And, MUX, Generic, ABS, Int, Power
 		# SEMI-LINEAR: Product
 
 		for block in strongComponent:
 			# condition (1)
 			if block.getBlockType() == "ProductBlock":
 				dependenciesUnknown = [x for x in block.getDependencies(0) if x in strongComponent]
-				if len(dependenciesUnknown) == 2:
+				if len(dependenciesUnknown) >= 2:
 					return False
 
 			# condition (2) and (3)
 			if block.getBlockType() in ["InverterBlock", "ModuloBlock", "RootBlock", "LessThanBlock", "EqualsBlock",
 			                            "LessThanOrEqualsBlock", "NotBlock", "OrBlock", "AndBlock",
-			                            "MultiplexerBlock", "GenericBlock", "AbsBlock", "IntBlock"]:
+			                            "MultiplexerBlock", "GenericBlock", "AbsBlock", "IntBlock", "PowerBlock"]:
 				return False
 
 		return True
@@ -163,7 +162,11 @@ class LinearSolver(Solver):
 			elif block.getBlockType() == "OutputPortBlock" or block.getBlockType() == "WireBlock":
 				# M2 can stay 0
 				M1[i, i] = 1
-				M1[i, indexdict[block.getDependencies(0)[0]]] = - 1
+				dblock = block.getDependencies(0)[0]
+				if isinstance(dblock, CBD):
+					oport = block.getLinksIn()['IN1'].output_port
+					dblock = dblock.getBlockByName(oport).getLinksIn()['IN1'].block
+				M1[i, indexdict[dblock]] = - 1
 			elif block.getBlockType() == "DelayBlock":
 				# If a delay is in a strong component, this is the first iteration
 				assert curIteration == 0