Actual Implementation of Math::Model

agent on 2005-04-16T08:01:00

=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!