use v6; class Math::Model; use Math::RungeKutta; # TODO: only load when needed use SVG; use SVG::Plot; has %.derivatives; has %.variables; has %.initials; has @.captures is rw; has %!inv = %!derivatives.invert; # in Math::Model all variables are accessible by name # in contrast Math::RungeKutta uses vectors, so we need # to define an (arbitrary) ordering # @!deriv-names holds the names of the derivatives in a fixed # order, sod @!deriv-names[$number] turns the number into a name # %!deriv-keying{$name} translates a name into the corresponding index has @!deriv-names = %!inv.keys; has %!deriv-keying = @!deriv-names Z=> 0..Inf; # snapshot of all variables in the current model has %!current-values; has %.results; has @.time; has $.numeric-error is rw = 0.0001; my sub param-names(&c) { &c.signature.paramsĀ».nameĀ».substr(1).grep({ $_ ne '_'}); } method !params-for(&c) { param-names(&c).map( {; $_ => %!current-values{$_} } ).hash; } method topo-sort(*@vars) { my %seen; my @order; sub topo(*@a) { for @a { next if %!inv.exists($_) || %seen{$_} || $_ eq 'time'; die "Undeclared variable '$_' used in model" unless %.variables.exists($_); topo(param-names(%.variables{$_})); @order.push: $_; %seen{$_}++; } } topo(@vars); # say @order.perl; @order; } method integrate(:$from = 0, :$to = 10, :$min-resolution = ($to - $from) / 20, :$verbose) { for %.derivatives -> $d { die "There must be a variable defined for each derivative, missing for '$d.key()'" unless %.variables.exists($d.key) || %!inv.exists($d.key); die "There must be an initial value defined for each derivative target, missing for '$d.value()'" unless %.initials.exists($d.value); } %!current-values = %.initials; %!current-values