Math::Symbolic::Custom::LaTeXDumper - Math::Symbolic LaTeX output


Math-Symbolic-Custom-LaTeXDumper documentation Contained in the Math-Symbolic-Custom-LaTeXDumper distribution.

Index


Code Index:

NAME

Top

Math::Symbolic::Custom::LaTeXDumper - Math::Symbolic LaTeX output

SYNOPSIS

Top

  use Math::Symbolic qw/parse_from_string/;
  use Math::Symbolic::Custom::LaTeXDumper;
  $term = parse_from_string(...);
  print $term->to_latex(...);

DESCRIPTION

Top

This class provides the to_latex() method for all Math::Symbolic trees. It is a rewrite of the to_latex() method that was supplied by Math::Symbolic prior to version 0.201.

For details on how the custom method delegation model works, please have a look at the Math::Symbolic::Custom and Math::Symbolic::Custom::Base classes.

EXPORT

Please see the docs for Math::Symbolic::Custom::Base for details, but you should not try to use the standard Exporter semantics with this class.

SUBROUTINES

Top

to_latex

It returns a LaTeX representation of the Math::Symbolic tree it is called on. The LaTeX is meant to be included in a LaTeX source document. It does not include an enclosing math environment.

The method uses named parameter passing style. Valid parameters are:

implicit_multiplication

Used to turn on the '.' (\cdot) operators for all multiplications. Defaults to true, that is, the multiplication operators are not present.

no_fractions

Use '/' division operator instead of fractions. Defaults to false, that is, use fractions. See also: max_fractions parameter.

max_fractions

Setting this parameter to a positive integer results in a limitation of the number of nested fractions. It defaults to 2. Setting this to 0 results in arbitrarily nested fractions. To disable fractions altogether, set the no_fraction parameter.

exclude_signature

By default, the method includes all variables' signatures in parenthesis if present. Set this to true to omit variable signatures.

replace_default_greek

By default, all variable names are outputted as LaTeX in a way that makes them show up exactly as they did in your code. If you set this option to true, Math::Symbolic will try to replace as many greek character names with the appropriates symbols as possible.

Valid LaTeX symbols that are matched are:

  Lower case letters:
    alpha, beta, gamma, delta, epsilon, zeta, eta, theta,
    iota, kappa, lambda, mu, nu, xi, pi, rho, sigma,
    tau, upsilon, phi, chi, psi, omega

  Variant forms of small letters:
    varepsilon, vartheta, varpi, varrho, varsigma, varphi

  Upper case letters:
    Gamma, Delta, Theta, Lambda, Xi, Pi, Sigma, Upsilon, Phi,
    Psi, Omega

variable_mappings

Because not all variable names come out as you might want them to, you may use the 'variable_mappings' option to replace variable names in the output LaTeX stream with custom LaTeX. For example, the variable x_i should probably indicate an 'x' with a subscripted i. The argument to variable_mappings needs to be a hash reference which contains variable name / LaTeX mapping pairs.

If a variable is replaced in the above fashion, other options that modify the outcome of the conversion of variable names to LaTeX are ignored.

subscript

Set this option to a true value to have all underscores in variable names interpreted as subscripts. Once an underscore is encountered, the rest of the variable name is treated as subscripted. If multiple underscores are found, this mechanism works recursively.

Defaults to true. Set to a false value to turn this off. This is automatically turned off for variables that are mapped to custom LaTeX by the variable_mappings parameter.

no_sqrt

By default, the dumper tries to convert exponents of 1/2 (or 0.5) or anything numeric that ends up being 1/2 to a square root. If this gives inconvenient results, set this option to a true value to turn the heuristics off.

SEE ALSO

Top

New versions of this module can be found on http://steffen-mueller.net or CPAN. The module development takes place on Sourceforge at http://sourceforge.net/projects/math-symbolic/

Math::Symbolic::Custom Math::Symbolic::Custom::DefaultMods Math::Symbolic::Custom::DefaultDumpers Math::Symbolic::Custom::DefaultTests Math::Symbolic

AUTHOR

Top

Steffen Müller, smueller@cpan.org

COPYRIGHT AND LICENSE

Top


Math-Symbolic-Custom-LaTeXDumper documentation Contained in the Math-Symbolic-Custom-LaTeXDumper distribution.
package Math::Symbolic::Custom::LaTeXDumper;

use 5.006;
use strict;
use warnings;
no warnings 'recursion';

our $VERSION = '0.207';

use Math::Symbolic::Custom::Base;
BEGIN { *import = \&Math::Symbolic::Custom::Base::aggregate_import }

use Math::Symbolic::ExportConstants qw/:all/;

use Carp;

# Class Data: Special variable required by Math::Symbolic::Custom
# importing/exporting functionality.
# All subroutines that are to be exported to the Math::Symbolic::Custom
# namespace should be listed here.

our $Aggregate_Export = [
    qw/
      to_latex
      /
];

sub to_latex {
    my $self   = shift;
    my %config = @_;
    $config{implicit_multiplication} = 1
      unless defined $config{implicit_multiplication};
    $config{no_fractions}      = 0 unless defined $config{no_fractions};
    $config{max_fractions}     = 2 if not exists $config{max_fractions};
    $config{exclude_signature} = 0 unless defined $config{exclude_signature};
    $config{no_sqrt} = 0 unless defined $config{no_sqrt};
    $config{replace_default_greek} = 0
      unless defined $config{replace_default_greek};

    $config{subscript} = 1 if not exists $config{subscript};

    $config{variable_mappings} = {}
      if not exists $config{variable_mappings}
      or not ref( $config{variable_mappings} ) eq 'HASH';

    my $default_greek = qr/(?<![a-zA-Z])
                alpha|beta|gamma|delta|epsilon|zeta|eta|theta
                |iota|kappa|lambda|mu|nu|xi|pi|rho|sigma|tau|upsilon
                |phi|chi|psi|omega
                |varepsilon|vartheta|varpi|varrho|varsigma|varphi
                |Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega
                (?![a-zA-Z])
        /x;

    my $greekify = sub {
        my $s = $_[0];
        $s =~ s/($default_greek)/\\$1/g if $config{replace_default_greek};
        return $s;
    };
    
    my $subscriptify;
       $subscriptify = sub {
        my $s = $_[0];
        $s = '' if not defined $s;
        $s =~ s{_(.*)$}/length($1) == 1 ? "_$1" : '_{'.$subscriptify->($1).'}'/e if $config{subscript};
        return $s;
    };
    
    my $precedence = [
        1,     # B_SUM
        1,     # B_DIFFERENCE
        5,     # B_PRODUCT
        5,     # B_DIVISION
        15,    # U_MINUS
        20,    # U_P_DERIVATIVE
        20,    # U_T_DERIVATIVE
        25,    # B_EXP
        50,    # B_LOG
        50,    # U_SINE
        50,    # U_COSINE
        50,    # U_TANGENT
        50,    # U_COTANGENT
        50,    # U_ARCSINE
        50,    # U_ARCCOSINE
        50,    # U_ARCTANGENT
        50,    # U_ARCCOTANGENT
        50,    # U_SINE_H
        50,    # U_COSINE_H
        50,    # U_AREASINE_H
        50,    # U_AREACOSINE_H
        50,    # B_ARCTANGENT_TWO
    ];

    my $op_to_tex = [

        # B_SUM
        sub { "$_[0] + $_[1]" },

        # B_DIFFERENCE
        sub { "$_[0] - $_[1]" },

        # B_PRODUCT
        sub {
            $config{implicit_multiplication}
              ? "$_[0] $_[1]"
              : "$_[0] \\cdot $_[1]";
        },

        # B_DIVISION
        sub {
            if ( $config{no_fractions} ) {
                "$_[0] / $_[1]"
            }
            elsif (!$config{max_fractions} or $config{max_fractions} > $_[2]) {
                "\\frac{$_[0]}{$_[1]}"
            }
            else {
                "$_[0] / $_[1]"
            }
        },

        # U_MINUS
        sub { "-$_[0]" },

        # U_P_DERIVATIVE
        sub { "\\frac{\\partial $_[0]}{\\partial $_[1]}" },

        # U_T_DERIVATIVE
        sub { "\\frac{d $_[0]}{d $_[1]}" },

        # B_EXP
        sub {
            if (!$config{no_sqrt} and length($_[1]) > 2 and $_[1] !~ /\{|\}|\^|_|\-|\\|[A-DF-Za-df-z]/ and $_[1] - 0.5 < 1e-28) {
                return "\\sqrt{$_[0]}";
            }
            length($_[1]) == 1 ? "$_[0]^$_[1]" : "$_[0]^{$_[1]}"
        },

        # B_LOG
        sub { "\\log_{$_[0]}$_[1]" },

        # U_SINE
        sub { "\\sin{$_[0]}" },

        # U_COSINE
        sub { "\\cos{$_[0]}" },

        # U_TANGENT
        sub { "\\tan{$_[0]}" },

        # U_COTANGENT
        sub { "\\cot{$_[0]}" },

        # U_ARCSINE
        sub { "\\arcsin{$_[0]}" },

        # U_ARCCOSINE
        sub { "\\arccos{$_[0]}" },

        # U_ARCTANGENT
        sub { "\\arctan{$_[0]}" },

        # U_ARCCOTANGENT
        sub { "\\mathrm{cosec}{$_[0]}" },

        # U_SINE_H
        sub { "\\sinh{$_[0]}" },

        # U_COSINE_H
        sub { "\\cosh{$_[0]}" },

        # U_AREASINE_H
        sub { "\\mathrm{arsinh}{$_[0]}" },

        # U_AREACOSINE_H
        sub { "\\mathrm{arcosh}{$_[0]}" },

        # B_ARCTANGENT_TWO
        sub { "\\mathrm{atan2}{$_[0], $_[1]}" },
    ];

    my $tex = $self->descend(
        in_place       => 0,
        operand_finder => sub { $_[0]->descending_operands('all') },
        before         => sub {
            $_[0]->{__precedences} = [
                map {
                    my $ttype = $_->term_type();
                    if ( $ttype == T_OPERATOR ) {
                        $precedence->[ $_->type() ];
                    }
                    elsif ( $ttype == T_VARIABLE ) {
                        100;
                    }
                    elsif ( $ttype == T_CONSTANT ) {
                        100;
                    }
                    else { die "Should not be reached"; }
                  } @{ $_[0]->{operands} }
            ];
            my $fraction_depth = $_[0]->{__fract_depth} || 0;
            $fraction_depth++ if $_[0]->term_type() == T_OPERATOR and $_[0]->type() == B_DIVISION;
            for (@{ $_[0]->{operands} }) {
                $_->{__fract_depth} = $fraction_depth;
            }
            return ();
        },
        after => sub {
            my $self  = $_[0];
            my $ttype = $self->term_type();
            if ( $ttype == T_CONSTANT ) {
                $self->{text} = $self->value();
            }
            elsif ( $ttype == T_VARIABLE ) {
                my $name        = $self->name();
                my $edited_name = $name;
                if ( exists $config{variable_mappings}{$name} ) {
                    $edited_name = $config{variable_mappings}{$name};
                }
                else {
                    $edited_name = $greekify->($name);
                    $edited_name = $subscriptify->($edited_name);
                }
                unless ( $config{exclude_signature} ) {
                    my @sig =
                      map {
                        if ( exists $config{variable_mappings}{$_} )
                        {
                            $config{variable_mappings}{$_};
                        }
                        else {
                            $_ = $subscriptify->($_);
                            s/_/\_/g;
                            $greekify->($_);
                        }
                      }
                      grep $_ ne $name, $self->signature();
                    if (@sig) {
                        $self->{text} = "$edited_name(" . join( ', ', @sig ) . ')';
                    }
                    else {
                        $self->{text} = $edited_name;
                    }
                }
                else {
                    $self->{text} = $edited_name;
                }
            }
            elsif ( $ttype == T_OPERATOR ) {
                my $type  = $self->type();
                my $prec  = $precedence->[$type];
                my $precs = $self->{__precedences};
                my @ops   = @{ $self->{operands} };
                unless ( $type == B_DIVISION and !$config{no_fractions} ) {
                    for ( my $i = 0 ; $i < @ops ; $i++ ) {
                        my $obj = $ops[$i];
                        if ( $precs->[$i] < $prec
                             or $precs->[$i] == $prec && $prec == 50 # prec == 50 is a function call
                             or $i == 1 # second operand
                                && ($type == B_PRODUCT||$type == B_SUM||$type==B_DIFFERENCE)
                                && ($obj->term_type == T_CONSTANT && $obj->value < 0)
                                   || ($obj->term_type == T_OPERATOR && $obj->type == U_MINUS) )
                        {
                            $ops[$i]->{text} = '\\left(' . $ops[$i]->{text} . '\\right)'
                        }
                    }
                }
                my $fraction_depth = $self->{__fract_depth}||0;
                my $text = $op_to_tex->[$type]->((map $_->{text}, @ops), $fraction_depth);
                $self->{text} = $text;
            }
            else {
                die "Should never be reached";
            }
        },
    );

    return $tex->{text};
}

1;
__END__