Today I began to learn Thevenin's theorem and realized that the idea of the @assume directive in the CIR script should be extended. Currently, cirsys.pl only allows Maple equations and unknown definitions to appear in @assume blocks. It is sufficient if we only want to change the pure mathematical hypotheses of the circuit problem. In the case that we wish to modify the circuit arrangement a little, say, replace an existing resistor with an independent voltage source or even with a complicated sub-circuit, there will be no way to describe such things in a single cir file since we can't put circuit wire definitions to @assume blocks due to the current implementation.
To make the modifications easier, I think it's a good style to abstract the data structure corresponding to the mathematical model out of the CirSys.pm module and refactor it in a structural fashion. I'd like to isolate the math-model to an independent module named Math::Model, and add inherent support for assuming and backtracking. The structure may be similar to those of symbol tables commonly used in modern compilers that support scoped identifiers.
Specifically, the whole structure is a tree or something alike. @assume blocks are represented by tree nodes. The sub-blocks nested in an outer @assume block correspond to child nodes. For a given node, we only store the equations asserted in this block but maintain a whole copy of the symbol table for unknowns. Thus, if a user invokes @solve in a block, CirSys will induce the set of equations by "summing" all the equation lists stored in ancestor nodes and the ones stored in the current node itself.
The scope of a equation is constrained by the @assume block containing it. One way and the only way to remove an equation defined earlier is to let it "out of scope". Similar consequence applies to unknowns too. Things will get more sophisticated if we consider occurrences of circuit element declarations and circuit wire definitions in an @assume block:
a, b: R1
Element type declarations involve no actions. They only concern symbol table tricks in cirsys.pl. Unfortunately, life won't be so easy when we have a different type declaration for the element R1. If we *do* allow such things, the semantics of overriding an existing element declared in the outer layer can be full of ambiguities. The best solution to this is simply disabling any forms of element redefinition. Or we are just asking for trouble then.
Let's consider the case of wire definitions. Adding new wires is a trivial task but removing an old one is not. Any actions carried out during the earlier wire definition should be "canceled" out completely when the wire is "out of scope" (passing through @end_assume for the current block). So the question is what indeed happened when we inserted a wire to the circuit? We built the data structures for the wire, along with the elements connected serially in it, and applied the "prior equations" associated with these mechanisms (via calling the ->apply method of Wire and Elem objects). Since system of equations is well managed by Math::Model, thus we needn't worry about it too much on the CIR compiler level. Almost everything will be handled transparently for us. We only need to call some procedure like ->new_layer in order to ask Math::Model to create a new layer for ensuing equation insertions or unknown definitions, and later to invoke ->bye_layer to inform Math::Model that we are leaving the current @assume scope.
Based on the preceding discussion, we can see that for wire cancellation, the only question remained is how to cancel the *pure* data structure, which is actually easy to implement.
The solution to the @assume feature of CIR script is completely general for problem-solving practices, since we encounter the notion of sub-problems almost very where in our textbooks.