=from 2005.4.14.8:00.AM
=to ...4.14.9:45.AM
Now I decide to sit down to consider seriously the matter of hacking on Math::Model. The basic code can be borrowed from the existing CirSys.pm module. I think a procedural design will be appropriate here.
Math::Model should offer the following subroutines:
reg_eq
unreg_eq
reg_var
unreg_var
reg_eqs
get_eqs
get_vars
use_solve
new_layer
bye_layer
The last two functions are completely new. They provide the magic of assuming and backtracking. Once the user invokes new_layer, Math::Model will create a new layer (or a new "copy") for the math model, so any subsequent updates to the model won't spoil the original one. When the user calls bye_layer some time later, Math::Model will recover the model immediately back to the initial state just before the last new_layer calling. In other words, a call of bye_layer will effectively remove all the modifications to the math model (through reg_eq, unreg_var, etc.) since the last invocation of new_layer. This behavior is very similar to assuming and backtracking.
I finally prefer stack structures to do such trick. The implementation of bye_layer and new_layer is straightforward:
our (@ModelStack, %Vars, @Equas);
# new_layer: Create and enter a new layer.
sub new_layer {
push @ModelStack, {%Vars}, [@Equas];
@Equas = ();
}
# bye_layer: quit the current layer.
sub bye_layer {
my $ref = pop @ModelStack;
return unless $ref;
@Equas = @$ref;
$ref = pop @ModelStack;
%Vars = %$ref;
}
I hasten to ask the following question: What do we mean by a math model? A math model is composed of nothing but several equations, some optional inequalities, and a list of unknown variables. This basic concept should be clarified first at this point.
For each layer, there are two "localized" structures, %Vars and @Equas. %Vars is inherited directly from the outer layer (or parent layer), while @Equas holds only the equations built on the current layer due to efficiency considerations. The realization of the get_eqs function therefore needs a thorough rewrite accordingly:
# get_eqs: get all the equations
sub get_eqs {
my @equas = get_eq_and_des();
my @eqs = ();
for (my $i=0; $i < @equas; $i += 2) {
push @eqs, $equas[$i];
}
return @eqs;
}
# get_eq_and_des: get all the equations and corresponding descriptions:
sub get_eq_and_des {
my @equas;
for (my $i = 1; $i < @ModelStack; $i += 2) {
my $ref = $ModelStack[$i];
push @equas, @$ref;
}
return (@equas, @Equas);
}
There is no need to modify the get_vars function originated from CirSys.pm since the semantics of the %Vars structure does not change.
Now that we've got an implementation, it's time for us to write some tests...
I'm afraid I'm not a real XP programmer as I seldom write tests before any actual coding. Sigh.
With an excellent repertoire of math model management utilities, we can then happily implement the @assume language mechanism for our CIR scripts, which cannot be difficult. Aha!