From 28663e398e1510e1f8e2b4443ef3d94825b3b0a7 Mon Sep 17 00:00:00 2001 From: Aaron David Schneider Date: Tue, 28 Feb 2023 16:31:12 +0100 Subject: [PATCH 1/3] add fortran interpreter, missing tests --- m2cgen/__init__.py | 2 + m2cgen/cli.py | 1 + m2cgen/exporters.py | 24 ++++ m2cgen/interpreters/__init__.py | 2 + m2cgen/interpreters/fortran/__init__.py | 0 m2cgen/interpreters/fortran/code_generator.py | 59 ++++++++++ m2cgen/interpreters/fortran/interpreter.py | 105 ++++++++++++++++++ .../interpreters/fortran/linear_algebra.f90 | 24 ++++ m2cgen/interpreters/fortran/log1p.f90 | 79 +++++++++++++ m2cgen/interpreters/fortran/sigmoid.f90 | 13 +++ m2cgen/interpreters/fortran/softmax.f90 | 24 ++++ tests/e2e/executors/__init__.py | 2 + tests/e2e/executors/fortran.py | 76 +++++++++++++ tests/e2e/test_e2e.py | 2 + tests/test_exporters.py | 9 ++ tools/generate_code_examples.py | 1 + 16 files changed, 423 insertions(+) create mode 100644 m2cgen/interpreters/fortran/__init__.py create mode 100644 m2cgen/interpreters/fortran/code_generator.py create mode 100644 m2cgen/interpreters/fortran/interpreter.py create mode 100644 m2cgen/interpreters/fortran/linear_algebra.f90 create mode 100644 m2cgen/interpreters/fortran/log1p.f90 create mode 100644 m2cgen/interpreters/fortran/sigmoid.f90 create mode 100644 m2cgen/interpreters/fortran/softmax.f90 create mode 100644 tests/e2e/executors/fortran.py diff --git a/m2cgen/__init__.py b/m2cgen/__init__.py index f88be1ec..246f8776 100644 --- a/m2cgen/__init__.py +++ b/m2cgen/__init__.py @@ -6,6 +6,7 @@ export_to_dart, export_to_elixir, export_to_f_sharp, + export_to_fortran, export_to_go, export_to_haskell, export_to_java, @@ -34,6 +35,7 @@ export_to_haskell, export_to_ruby, export_to_f_sharp, + export_to_fortran, export_to_rust, export_to_elixir, ] diff --git a/m2cgen/cli.py b/m2cgen/cli.py index 2cafde0a..ff628e18 100644 --- a/m2cgen/cli.py +++ b/m2cgen/cli.py @@ -31,6 +31,7 @@ "ruby": (m2cgen.export_to_ruby, ["indent", "function_name"]), "f_sharp": (m2cgen.export_to_f_sharp, ["indent", "function_name"]), "rust": (m2cgen.export_to_rust, ["indent", "function_name"]), + "fortran": (m2cgen.export_to_fortran, ["indent", "function_name"]), "elixir": (m2cgen.export_to_elixir, ["module_name", "indent", "function_name"]), } diff --git a/m2cgen/exporters.py b/m2cgen/exporters.py index 0c5d39ef..49e18a96 100644 --- a/m2cgen/exporters.py +++ b/m2cgen/exporters.py @@ -378,6 +378,30 @@ def export_to_ruby(model, indent=4, function_name="score"): return _export(model, interpreter) +def export_to_fortran(model, indent=4, function_name="score"): + """ + Generates a Fortran code representation of the given model. + + Parameters + ---------- + model : object + The model object that should be transpiled into code. + indent : int, optional + The size of indents in the generated code. + function_name : string, optional + Name of the function in the generated code. + + Returns + ------- + code : string + """ + interpreter = interpreters.FortranInterpreter( + indent=indent, + function_name=function_name + ) + return _export(model, interpreter) + + def export_to_f_sharp(model, indent=4, function_name="score"): """ Generates a F# code representation of the given model. diff --git a/m2cgen/interpreters/__init__.py b/m2cgen/interpreters/__init__.py index 6e43dde9..9844974f 100644 --- a/m2cgen/interpreters/__init__.py +++ b/m2cgen/interpreters/__init__.py @@ -3,6 +3,7 @@ from m2cgen.interpreters.dart.interpreter import DartInterpreter from m2cgen.interpreters.elixir.interpreter import ElixirInterpreter from m2cgen.interpreters.f_sharp.interpreter import FSharpInterpreter +from m2cgen.interpreters.fortran.interpreter import FortranInterpreter from m2cgen.interpreters.go.interpreter import GoInterpreter from m2cgen.interpreters.haskell.interpreter import HaskellInterpreter from m2cgen.interpreters.java.interpreter import JavaInterpreter @@ -30,6 +31,7 @@ HaskellInterpreter, RubyInterpreter, FSharpInterpreter, + FortranInterpreter, RustInterpreter, ElixirInterpreter, ] diff --git a/m2cgen/interpreters/fortran/__init__.py b/m2cgen/interpreters/fortran/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/m2cgen/interpreters/fortran/code_generator.py b/m2cgen/interpreters/fortran/code_generator.py new file mode 100644 index 00000000..6c4a0d5a --- /dev/null +++ b/m2cgen/interpreters/fortran/code_generator.py @@ -0,0 +1,59 @@ +from contextlib import contextmanager + +from m2cgen.interpreters.code_generator import CodeTemplate, ImperativeCodeGenerator + + +class FortranCodeGenerator(ImperativeCodeGenerator): + tpl_num_value = CodeTemplate("{value}") + tpl_infix_expression = CodeTemplate("{left} {op} {right}") + tpl_return_statement_vec = CodeTemplate("{func_name}(:) = {value}") + tpl_return_statement_single = CodeTemplate("{func_name} = {value}") + tpl_array_index_access = CodeTemplate("{array_name}({index})") + tpl_if_statement = CodeTemplate("if ({if_def}) then") + tpl_else_statement = CodeTemplate("else") + tpl_var_assignment = CodeTemplate("{var_name} = {value}") + tpl_scalar_var_declare = CodeTemplate("double precision :: {var_name}") + tpl_vector_var_declare = CodeTemplate("double precision, dimension({size}) :: {var_name}") + + tpl_block_termination = CodeTemplate("end if") + + def add_return_statement(self, value, func_name, output_size): + if output_size > 1: + tpl = self.tpl_return_statement_vec + else: + tpl = self.tpl_return_statement_single + + self.add_code_line(tpl(value=value, func_name=func_name)) + + def _declaration(self, var_name, size): + if size > 1: + tpl = self.tpl_vector_var_declare + else: + tpl = self.tpl_scalar_var_declare + + return tpl(var_name=var_name, size=size) + + def add_function_def(self, name, args, output_size): + function_def = f"function {name}({', '.join(args)})" + self.add_code_line(function_def) + self.increase_indent() + self.add_code_line(self._declaration(var_name=name, size=output_size)) + self.add_code_lines([self.tpl_vector_var_declare(var_name=arg, size=":") for arg in args]) + + def add_function_end(self, name): + self.add_code_line("return") + self.decrease_indent() + self.add_code_line(f"end function {name}") + + def add_var_declaration(self, size): + # We use implicit declerations for the local variables + return self.get_var_name() + + @contextmanager + def function_definition(self, name, args, output_size): + self.add_function_def(name, args, output_size) + yield + self.add_function_end(name) + + def vector_init(self, values): + return f"(/ {', '.join(values)} /)" diff --git a/m2cgen/interpreters/fortran/interpreter.py b/m2cgen/interpreters/fortran/interpreter.py new file mode 100644 index 00000000..3a30cd20 --- /dev/null +++ b/m2cgen/interpreters/fortran/interpreter.py @@ -0,0 +1,105 @@ +from pathlib import Path + +from m2cgen.ast import BinNumOpType +from m2cgen.interpreters.interpreter import ImperativeToCodeInterpreter +from m2cgen.interpreters.mixins import BinExpressionDepthTrackingMixin, LinearAlgebraMixin, PowExprFunctionMixin +from m2cgen.interpreters.fortran.code_generator import FortranCodeGenerator +from m2cgen.interpreters.utils import get_file_content + + +class FortranInterpreter(ImperativeToCodeInterpreter, + PowExprFunctionMixin, + BinExpressionDepthTrackingMixin, + LinearAlgebraMixin): + # needs to be tested. + bin_depth_threshold = 55 + + supported_bin_vector_ops = { + BinNumOpType.ADD: "add_vectors", + } + + supported_bin_vector_num_ops = { + BinNumOpType.MUL: "mul_vector_number", + } + + abs_function_name = "ABS" + atan_function_name = "ATAN" + exponent_function_name = "EXP" + logarithm_function_name = "LOG" + log1p_function_name = "LOG1P" + sigmoid_function_name = "SIGMOID" + softmax_function_name = "SOFTMAX" + sqrt_function_name = "SQRT" + tanh_function_name = "TANH" + + pow_operator = "**" + + with_sigmoid_expr = False + with_softmax_expr = False + with_log1p_expr = False + + def __init__(self, indent=4, function_name="score", *args, **kwargs): + self.function_name = function_name + + cg = FortranCodeGenerator(indent=indent) + super().__init__(cg, *args, **kwargs) + + def interpret(self, expr): + self._cg.reset_state() + self._reset_reused_expr_cache() + + with self._cg.function_definition( + name=self.function_name, + args=[self._feature_array_name], + output_size=expr.output_size, + ): + last_result = self._do_interpret(expr) + self._cg.add_return_statement(last_result, self.function_name, expr.output_size) + + current_dir = Path(__file__).absolute().parent + + if self.with_linear_algebra \ + or self.with_softmax_expr \ + or self.with_sigmoid_expr \ + or self.with_log1p_expr: + self._cg.add_code_line("contains") + + if self.with_linear_algebra: + filename = current_dir / "linear_algebra.f90" + self._add_contain_statement(filename) + + if self.with_softmax_expr: + filename = current_dir / "softmax.f90" + self._add_contain_statement(filename) + + if self.with_sigmoid_expr: + filename = current_dir / "sigmoid.f90" + self._add_contain_statement(filename) + + if self.with_log1p_expr: + filename = current_dir / "log1p.f90" + self._add_contain_statement(filename) + + return self._cg.finalize_and_get_generated_code() + + def _add_contain_statement(self, filename): + self._cg.increase_indent() + self._cg.add_code_lines(get_file_content(filename)) + self._cg.decrease_indent() + + def interpret_abs_expr(self, expr, **kwargs): + nested_result = self._do_interpret(expr.expr, **kwargs) + return self._cg.function_invocation( + self.abs_function_name, nested_result) + + def interpret_log1p_expr(self, expr, **kwargs): + self.with_log1p_expr = True + return super().interpret_softmax_expr(expr, **kwargs) + + def interpret_softmax_expr(self, expr, **kwargs): + self.with_softmax_expr = True + return super().interpret_softmax_expr(expr, **kwargs) + + def interpret_sigmoid_expr(self, expr, **kwargs): + self.with_sigmoid_expr = True + return super().interpret_sigmoid_expr(expr, **kwargs) diff --git a/m2cgen/interpreters/fortran/linear_algebra.f90 b/m2cgen/interpreters/fortran/linear_algebra.f90 new file mode 100644 index 00000000..4496175b --- /dev/null +++ b/m2cgen/interpreters/fortran/linear_algebra.f90 @@ -0,0 +1,24 @@ +function add_vectors(v1, v2) result(res) + implicit none + double precision, dimension(:), intent(in) :: v1, v2 + double precision, dimension(size(v1)) :: res + integer :: i + + do i = 1, size(v1) + res(i) = v1(i) + v2(i) + end do + +end function add_vectors + +function mul_vector_number(v1, num) result(res) + implicit none + double precision, dimension(:), intent(in) :: v1 + double precision, intent(in) :: num + double precision, dimension(size(v1)) :: res + integer :: i + + do i = 1, size(v1) + res(i) = v1(i) * num + end do + +end function mul_vector_number diff --git a/m2cgen/interpreters/fortran/log1p.f90 b/m2cgen/interpreters/fortran/log1p.f90 new file mode 100644 index 00000000..71b6e914 --- /dev/null +++ b/m2cgen/interpreters/fortran/log1p.f90 @@ -0,0 +1,79 @@ +function ChebyshevBroucke(x, coeffs) result(result) + implicit none + double precision, intent(in) :: x + double precision, intent(in) :: coeffs(:) + double precision :: b0, b1, b2, x2, result + integer :: i + b2 = 0.0d0 + b1 = 0.0d0 + b0 = 0.0d0 + x2 = x * 2.0d0 + do i = size(coeffs, 1), 1, -1 + b2 = b1 + b1 = b0 + b0 = x2 * b1 - b2 + coeffs(i) + end do + result = (b0 - b2) * 0.5d0 +end function ChebyshevBroucke + +function Log1p(x) result(result) + implicit none + double precision, intent(in) :: x + double precision :: res, xAbs + double precision, parameter :: eps = 2.220446049250313d-16 + double precision, parameter :: coeff(21) = (/ 0.10378693562743769800686267719098d1, & + -0.13364301504908918098766041553133d0, & + 0.19408249135520563357926199374750d-1, & + -0.30107551127535777690376537776592d-2, & + 0.48694614797154850090456366509137d-3, & + -0.81054881893175356066809943008622d-4, & + 0.13778847799559524782938251496059d-4, & + -0.23802210894358970251369992914935d-5, & + 0.41640416213865183476391859901989d-6, & + -0.73595828378075994984266837031998d-7, & + 0.13117611876241674949152294345011d-7, & + -0.23546709317742425136696092330175d-8, & + 0.42522773276034997775638052962567d-9, & + -0.77190894134840796826108107493300d-10, & + 0.14075746481359069909215356472191d-10, & + -0.25769072058024680627537078627584d-11, & + 0.47342406666294421849154395005938d-12, & + -0.87249012674742641745301263292675d-13, & + 0.16124614902740551465739833119115d-13, & + -0.29875652015665773006710792416815d-14, & + 0.55480701209082887983041321697279d-15, & + -0.10324619158271569595141333961932d-15 /) + + if (x == 0.0d0) then + result = 0.0d0 + return + end if + if (x == -1.0d0) then + result = -huge(1.0d0) + return + end if + if (x < -1.0) then + result = 0.0d0 / 0.0d0 + return + end if + + xAbs = abs(x) + if (xAbs < 0.5 * eps) then + result = x + return + end if + + if ((x > 0.0 .and. x < 1.0e-8) .or. (x > -1.0e-9 .and. x < 0.0)) then + result = x * (1.0 - x * 0.5) + return + end if + + if (xAbs < 0.375) then + result = x * (1.0 - x * ChebyshevBroucke(x / 0.375, coeff)) + return + end if + + result = log(1.0 + x) + +end function Log1p + diff --git a/m2cgen/interpreters/fortran/sigmoid.f90 b/m2cgen/interpreters/fortran/sigmoid.f90 new file mode 100644 index 00000000..60f50195 --- /dev/null +++ b/m2cgen/interpreters/fortran/sigmoid.f90 @@ -0,0 +1,13 @@ +function sigmoid(x) result(res) + implicit none + double precision, intent(in) :: x + double precision :: z + + if (x < 0.0d0) then + z = exp(x) + res = z / (1.0d0 + z) + else + res = 1.0d0 / (1.0d0 + exp(-x)) + end if + +end function sigmoid diff --git a/m2cgen/interpreters/fortran/softmax.f90 b/m2cgen/interpreters/fortran/softmax.f90 new file mode 100644 index 00000000..96ba4913 --- /dev/null +++ b/m2cgen/interpreters/fortran/softmax.f90 @@ -0,0 +1,24 @@ +function softmax(x) result(res) + implicit none + double precision, dimension(:), intent(in) :: x + double precision, dimension(size(x)) :: res + double precision :: max_val, sum_val + integer :: i + + ! Find maximum value in x + max_val = x(1) + do i = 2, size(x) + if (x(i) > max_val) then + max_val = x(i) + end if + end do + + ! Compute softmax values + sum_val = 0.0d0 + do i = 1, size(x) + res(i) = exp(x(i) - max_val) + sum_val = sum_val + res(i) + end do + res = res / sum_val + +end function softmax diff --git a/tests/e2e/executors/__init__.py b/tests/e2e/executors/__init__.py index b3aea8da..b1a2c4e2 100644 --- a/tests/e2e/executors/__init__.py +++ b/tests/e2e/executors/__init__.py @@ -3,6 +3,7 @@ from tests.e2e.executors.dart import DartExecutor from tests.e2e.executors.elixir import ElixirExecutor from tests.e2e.executors.f_sharp import FSharpExecutor +from tests.e2e.executors.fortran import FortranExecutor from tests.e2e.executors.go import GoExecutor from tests.e2e.executors.haskell import HaskellExecutor from tests.e2e.executors.java import JavaExecutor @@ -21,6 +22,7 @@ CExecutor, GoExecutor, JavascriptExecutor, + FortranExecutor, VisualBasicExecutor, CSharpExecutor, PowershellExecutor, diff --git a/tests/e2e/executors/fortran.py b/tests/e2e/executors/fortran.py new file mode 100644 index 00000000..66843d85 --- /dev/null +++ b/tests/e2e/executors/fortran.py @@ -0,0 +1,76 @@ +from m2cgen.assemblers import get_assembler_cls +from m2cgen.interpreters import CInterpreter + +from tests import utils +from tests.e2e.executors.base import BaseExecutor + +# TODO: All of this is still C... +EXECUTOR_CODE_TPL = """ +{model_code} + +progtam main +{{ + implicit none + for (int i = 1; i < argc; ++i) {{ + sscanf(argv[i], "%lf", &input[i-1]); + }} + + {print_code} + + return 0; +}} +""" + +EXECUTE_AND_PRINT_SCALAR = """ + printf("%f\\n", score(input)); +""" + +EXECUTE_AND_PRINT_VECTOR_TPL = """ + double result[{size}]; + score(input, result); + + for (int i = 0; i < {size}; ++i) {{ + printf("%f ", *(result+i)); + }} +""" + + +class FortranExecutor(BaseExecutor): + + def __init__(self, model): + self.model_name = "score" + self.model = model + self.interpreter = CInterpreter() + + assembler_cls = get_assembler_cls(model) + self.model_ast = assembler_cls(model).assemble() + + self.exec_path = None + + def predict(self, X): + exec_args = [str(self.exec_path), *map(utils.format_arg, X)] + return utils.predict_from_commandline(exec_args) + + def prepare(self): + if self.model_ast.output_size > 1: + print_code = EXECUTE_AND_PRINT_VECTOR_TPL.format( + size=self.model_ast.output_size) + else: + print_code = EXECUTE_AND_PRINT_SCALAR + + executor_code = EXECUTOR_CODE_TPL.format( + model_code=self.interpreter.interpret(self.model_ast), + print_code=print_code) + + file_name = self._resource_tmp_dir / f"{self.model_name}.c" + utils.write_content_to_file(executor_code, file_name) + + self.exec_path = self._resource_tmp_dir / self.model_name + flags = ["-std=c99", "-lm"] + utils.execute_command([ + "gcc", + str(file_name), + "-o", + str(self.exec_path), + *flags + ]) diff --git a/tests/e2e/test_e2e.py b/tests/e2e/test_e2e.py index 356829ff..23f1ab50 100644 --- a/tests/e2e/test_e2e.py +++ b/tests/e2e/test_e2e.py @@ -31,6 +31,7 @@ RUBY = pytest.mark.ruby F_SHARP = pytest.mark.f_sharp RUST = pytest.mark.rust +FORTRAN = pytest.mark.fortran ELIXIR = pytest.mark.elixir REGRESSION = pytest.mark.regr REGRESSION_WITH_MISSING_VALUES = pytest.mark.regr_missing_val @@ -162,6 +163,7 @@ def classification_binary_random_w_missing_values(model, test_fraction=0.02): (executors.DartExecutor, DART), (executors.HaskellExecutor, HASKELL), (executors.RubyExecutor, RUBY), + (executors.FortranExecutor, FORTRAN), (executors.FSharpExecutor, F_SHARP), (executors.RustExecutor, RUST), (executors.ElixirExecutor, ELIXIR), diff --git a/tests/test_exporters.py b/tests/test_exporters.py index aa388f83..7ba2ca80 100644 --- a/tests/test_exporters.py +++ b/tests/test_exporters.py @@ -122,3 +122,12 @@ def test_export_to_rust(trained_model): assert generated_code.startswith(""" fn score(input: Vec) -> f64 { """.strip()) + + +def test_export_to_fortran(trained_model): + generated_code = exporters.export_to_fortran(trained_model).strip() + assert generated_code.startswith(""" +function score(input) + double precision :: score + double precision, dimension(:) :: input +""".strip()) diff --git a/tools/generate_code_examples.py b/tools/generate_code_examples.py index ba88a5fa..a912560c 100644 --- a/tools/generate_code_examples.py +++ b/tools/generate_code_examples.py @@ -47,6 +47,7 @@ ("f_sharp", m2c.export_to_f_sharp, "fs"), ("rust", m2c.export_to_rust, "rs"), ("elixir", m2c.export_to_elixir, "ex"), + ("fortran", m2c.export_to_fortran, "fo"), ] EXAMPLE_MODELS = [ From 97559dce6044883f2d90e1d095b0a77367512452 Mon Sep 17 00:00:00 2001 From: Aaron David Schneider Date: Wed, 1 Mar 2023 12:42:29 +0100 Subject: [PATCH 2/3] - executor for e2e added - bug fixing in interpreter --- README.md | 1 + m2cgen/exporters.py | 7 +- m2cgen/interpreters/code_generator.py | 13 +-- m2cgen/interpreters/fortran/code_generator.py | 44 ++++++++-- m2cgen/interpreters/fortran/interpreter.py | 54 ++++++------ m2cgen/interpreters/fortran/log1p.f90 | 9 +- m2cgen/interpreters/fortran/sigmoid.f90 | 1 + tests/e2e/executors/fortran.py | 85 ++++++++++++++----- 8 files changed, 144 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index d075a050..8afb465e 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ make docker-build docker-pre-pr - C# - Dart - F# +- Fortran90 - Go - Haskell - Java diff --git a/m2cgen/exporters.py b/m2cgen/exporters.py index 49e18a96..b3a4489d 100644 --- a/m2cgen/exporters.py +++ b/m2cgen/exporters.py @@ -378,7 +378,7 @@ def export_to_ruby(model, indent=4, function_name="score"): return _export(model, interpreter) -def export_to_fortran(model, indent=4, function_name="score"): +def export_to_fortran(model, module_name="Model", indent=4, function_name="score"): """ Generates a Fortran code representation of the given model. @@ -386,6 +386,8 @@ def export_to_fortran(model, indent=4, function_name="score"): ---------- model : object The model object that should be transpiled into code. + module_name : string, optional + The name of the generated module. indent : int, optional The size of indents in the generated code. function_name : string, optional @@ -397,7 +399,8 @@ def export_to_fortran(model, indent=4, function_name="score"): """ interpreter = interpreters.FortranInterpreter( indent=indent, - function_name=function_name + function_name=function_name, + module_name=module_name ) return _export(model, interpreter) diff --git a/m2cgen/interpreters/code_generator.py b/m2cgen/interpreters/code_generator.py index afd03ab7..5f540102 100644 --- a/m2cgen/interpreters/code_generator.py +++ b/m2cgen/interpreters/code_generator.py @@ -88,16 +88,19 @@ def add_code_lines(self, lines): indent = " " * self._current_indent self._write_to_code_buffer(indent + f"\n{indent}".join(lines) + "\n") - def prepend_code_line(self, line): + def prepend_code_line(self, line, indent=0): if not line: return - self.prepend_code_lines([line.strip()]) + self.prepend_code_lines([line.strip()], indent) + + def prepend_code_lines(self, lines, indent=0): + new_line = f"\n" + add_indent = f"{indent * ' '}" + new_line_with_indent = new_line + add_indent - def prepend_code_lines(self, lines): - new_line = "\n" if isinstance(lines, str): lines = lines.strip().split(new_line) - self._write_to_code_buffer(f"{new_line.join(lines)}{new_line}", prepend=True) + self._write_to_code_buffer(f"{add_indent}{new_line_with_indent.join(lines)}{new_line}", prepend=True) # Following methods simply compute expressions using templates without changing result. diff --git a/m2cgen/interpreters/fortran/code_generator.py b/m2cgen/interpreters/fortran/code_generator.py index 6c4a0d5a..bd49a766 100644 --- a/m2cgen/interpreters/fortran/code_generator.py +++ b/m2cgen/interpreters/fortran/code_generator.py @@ -17,6 +17,14 @@ class FortranCodeGenerator(ImperativeCodeGenerator): tpl_block_termination = CodeTemplate("end if") + def reset_state(self): + self._var_declarations = [] + super().reset_state() + + def array_index_access(self, array_name, index): + """Note: Fortran starts array indexing at 1 and not at 0""" + return self.tpl_array_index_access(array_name=array_name, index=index + 1) + def add_return_statement(self, value, func_name, output_size): if output_size > 1: tpl = self.tpl_return_statement_vec @@ -33,26 +41,48 @@ def _declaration(self, var_name, size): return tpl(var_name=var_name, size=size) - def add_function_def(self, name, args, output_size): + def prepend_function_def(self, name, args, output_size): function_def = f"function {name}({', '.join(args)})" - self.add_code_line(function_def) self.increase_indent() - self.add_code_line(self._declaration(var_name=name, size=output_size)) - self.add_code_lines([self.tpl_vector_var_declare(var_name=arg, size=":") for arg in args]) + self.prepend_code_line(self._declaration(var_name=name, size=output_size), indent=2 * self._indent) + self.prepend_code_lines(self._var_declarations, indent=2 * self._indent) + self.prepend_code_lines([self.tpl_vector_var_declare(var_name=arg, size=":") for arg in args], + indent=2 * self._indent) + self.prepend_code_line("implicit none", indent=2 * self._indent) + self.prepend_code_line(function_def, indent=self._indent) def add_function_end(self, name): + self.decrease_indent() self.add_code_line("return") self.decrease_indent() self.add_code_line(f"end function {name}") def add_var_declaration(self, size): - # We use implicit declerations for the local variables - return self.get_var_name() + var_name = self.get_var_name() + self._var_declarations.append(self._declaration(var_name, size)) + return var_name + + def prepend_module_def(self, name): + self.prepend_code_line(f"contains") + self.prepend_code_line(f"implicit none", indent=self._indent) + self.prepend_code_line(f"module {name}") + + def add_module_end(self, name): + self.decrease_indent() + self.add_code_line(f"end module {name}") + + @contextmanager + def module_definition(self, name): + self.increase_indent() + yield + self.prepend_module_def(name) + self.add_module_end(name) @contextmanager def function_definition(self, name, args, output_size): - self.add_function_def(name, args, output_size) + self.increase_indent() yield + self.prepend_function_def(name, args, output_size) self.add_function_end(name) def vector_init(self, values): diff --git a/m2cgen/interpreters/fortran/interpreter.py b/m2cgen/interpreters/fortran/interpreter.py index 3a30cd20..e3e24c67 100644 --- a/m2cgen/interpreters/fortran/interpreter.py +++ b/m2cgen/interpreters/fortran/interpreter.py @@ -1,9 +1,9 @@ from pathlib import Path from m2cgen.ast import BinNumOpType +from m2cgen.interpreters.fortran.code_generator import FortranCodeGenerator from m2cgen.interpreters.interpreter import ImperativeToCodeInterpreter from m2cgen.interpreters.mixins import BinExpressionDepthTrackingMixin, LinearAlgebraMixin, PowExprFunctionMixin -from m2cgen.interpreters.fortran.code_generator import FortranCodeGenerator from m2cgen.interpreters.utils import get_file_content @@ -38,8 +38,9 @@ class FortranInterpreter(ImperativeToCodeInterpreter, with_softmax_expr = False with_log1p_expr = False - def __init__(self, indent=4, function_name="score", *args, **kwargs): + def __init__(self, indent=4, function_name="score", module_name="Model", *args, **kwargs): self.function_name = function_name + self.module_name = module_name cg = FortranCodeGenerator(indent=indent) super().__init__(cg, *args, **kwargs) @@ -48,37 +49,32 @@ def interpret(self, expr): self._cg.reset_state() self._reset_reused_expr_cache() - with self._cg.function_definition( - name=self.function_name, - args=[self._feature_array_name], - output_size=expr.output_size, - ): - last_result = self._do_interpret(expr) - self._cg.add_return_statement(last_result, self.function_name, expr.output_size) + with self._cg.module_definition(self.module_name): + with self._cg.function_definition( + name=self.function_name, + args=[self._feature_array_name], + output_size=expr.output_size, + ): + last_result = self._do_interpret(expr) + self._cg.add_return_statement(last_result, self.function_name, expr.output_size) - current_dir = Path(__file__).absolute().parent + current_dir = Path(__file__).absolute().parent - if self.with_linear_algebra \ - or self.with_softmax_expr \ - or self.with_sigmoid_expr \ - or self.with_log1p_expr: - self._cg.add_code_line("contains") + if self.with_linear_algebra: + filename = current_dir / "linear_algebra.f90" + self._add_contain_statement(filename) - if self.with_linear_algebra: - filename = current_dir / "linear_algebra.f90" - self._add_contain_statement(filename) + if self.with_softmax_expr: + filename = current_dir / "softmax.f90" + self._add_contain_statement(filename) - if self.with_softmax_expr: - filename = current_dir / "softmax.f90" - self._add_contain_statement(filename) + if self.with_sigmoid_expr: + filename = current_dir / "sigmoid.f90" + self._add_contain_statement(filename) - if self.with_sigmoid_expr: - filename = current_dir / "sigmoid.f90" - self._add_contain_statement(filename) - - if self.with_log1p_expr: - filename = current_dir / "log1p.f90" - self._add_contain_statement(filename) + if self.with_log1p_expr: + filename = current_dir / "log1p.f90" + self._add_contain_statement(filename) return self._cg.finalize_and_get_generated_code() @@ -94,7 +90,7 @@ def interpret_abs_expr(self, expr, **kwargs): def interpret_log1p_expr(self, expr, **kwargs): self.with_log1p_expr = True - return super().interpret_softmax_expr(expr, **kwargs) + return super().interpret_log1p_expr(expr, **kwargs) def interpret_softmax_expr(self, expr, **kwargs): self.with_softmax_expr = True diff --git a/m2cgen/interpreters/fortran/log1p.f90 b/m2cgen/interpreters/fortran/log1p.f90 index 71b6e914..cdd715ed 100644 --- a/m2cgen/interpreters/fortran/log1p.f90 +++ b/m2cgen/interpreters/fortran/log1p.f90 @@ -1,7 +1,7 @@ function ChebyshevBroucke(x, coeffs) result(result) implicit none double precision, intent(in) :: x - double precision, intent(in) :: coeffs(:) + double precision, dimension(22), intent(in) :: coeffs double precision :: b0, b1, b2, x2, result integer :: i b2 = 0.0d0 @@ -17,11 +17,12 @@ function ChebyshevBroucke(x, coeffs) result(result) end function ChebyshevBroucke function Log1p(x) result(result) + use, intrinsic :: ieee_arithmetic, only : IEEE_Value, IEEE_QUIET_NAN implicit none double precision, intent(in) :: x - double precision :: res, xAbs + double precision :: result, xAbs double precision, parameter :: eps = 2.220446049250313d-16 - double precision, parameter :: coeff(21) = (/ 0.10378693562743769800686267719098d1, & + double precision, dimension(22), parameter :: coeff = (/ 0.10378693562743769800686267719098d1, & -0.13364301504908918098766041553133d0, & 0.19408249135520563357926199374750d-1, & -0.30107551127535777690376537776592d-2, & @@ -53,7 +54,7 @@ function Log1p(x) result(result) return end if if (x < -1.0) then - result = 0.0d0 / 0.0d0 + result = IEEE_VALUE(result, IEEE_QUIET_NAN) return end if diff --git a/m2cgen/interpreters/fortran/sigmoid.f90 b/m2cgen/interpreters/fortran/sigmoid.f90 index 60f50195..d3f4ae28 100644 --- a/m2cgen/interpreters/fortran/sigmoid.f90 +++ b/m2cgen/interpreters/fortran/sigmoid.f90 @@ -2,6 +2,7 @@ function sigmoid(x) result(res) implicit none double precision, intent(in) :: x double precision :: z + double precision :: res if (x < 0.0d0) then z = exp(x) diff --git a/tests/e2e/executors/fortran.py b/tests/e2e/executors/fortran.py index 66843d85..1eae5afb 100644 --- a/tests/e2e/executors/fortran.py +++ b/tests/e2e/executors/fortran.py @@ -1,37 +1,58 @@ +import os +from contextlib import contextmanager + +import numpy as np + from m2cgen.assemblers import get_assembler_cls -from m2cgen.interpreters import CInterpreter +from m2cgen.interpreters import FortranInterpreter from tests import utils from tests.e2e.executors.base import BaseExecutor -# TODO: All of this is still C... EXECUTOR_CODE_TPL = """ {model_code} -progtam main -{{ +program main + use Model implicit none - for (int i = 1; i < argc; ++i) {{ - sscanf(argv[i], "%lf", &input[i-1]); - }} + integer, parameter :: max_input_dim = 10000 + integer :: i, j, io + integer, parameter :: size = {size} + double precision :: x + double precision, dimension({size}) :: result + double precision, dimension(max_input_dim) :: input + + open(10, file="{fname}", action='read') + input(:) = 0.0d0 + i = 1 + do + read(10,*,iostat=io) x + if (io > 0) then + write(*,*) 'Check input. Something was wrong' + exit + else if (io < 0) then + exit + else + input(i) = x + i = i+1 + end if + end do + close(10) {print_code} - return 0; -}} +end program main """ EXECUTE_AND_PRINT_SCALAR = """ - printf("%f\\n", score(input)); + result(1) = score(input) + print '(e21.14)', result(1) """ EXECUTE_AND_PRINT_VECTOR_TPL = """ - double result[{size}]; - score(input, result); - - for (int i = 0; i < {size}; ++i) {{ - printf("%f ", *(result+i)); - }} + result = score(input) + print '({size}e21.14)', result + """ @@ -40,16 +61,29 @@ class FortranExecutor(BaseExecutor): def __init__(self, model): self.model_name = "score" self.model = model - self.interpreter = CInterpreter() + self.interpreter = FortranInterpreter() assembler_cls = get_assembler_cls(model) self.model_ast = assembler_cls(model).assemble() self.exec_path = None + self._input_fname = None + + @contextmanager + def do_predict(self, X): + try: + np.savetxt(self._input_fname, X) + yield + finally: + os.remove(self._input_fname) def predict(self, X): - exec_args = [str(self.exec_path), *map(utils.format_arg, X)] - return utils.predict_from_commandline(exec_args) + exec_args = [str(self.exec_path)] + + with self.do_predict(X): + prediction = utils.predict_from_commandline(exec_args) + + return prediction def prepare(self): if self.model_ast.output_size > 1: @@ -58,17 +92,22 @@ def prepare(self): else: print_code = EXECUTE_AND_PRINT_SCALAR + self._input_fname = f'{self._resource_tmp_dir}/readinput.dat' + executor_code = EXECUTOR_CODE_TPL.format( model_code=self.interpreter.interpret(self.model_ast), - print_code=print_code) + print_code=print_code, + size=self.model_ast.output_size, + fname=self._input_fname + ) - file_name = self._resource_tmp_dir / f"{self.model_name}.c" + file_name = self._resource_tmp_dir / f"{self.model_name}.f90" utils.write_content_to_file(executor_code, file_name) self.exec_path = self._resource_tmp_dir / self.model_name - flags = ["-std=c99", "-lm"] + flags = ["-ffree-line-length-none"] utils.execute_command([ - "gcc", + "gfortran", str(file_name), "-o", str(self.exec_path), From 0e7b3df0984f8c411926cfbb46186f2c455a4e40 Mon Sep 17 00:00:00 2001 From: Aaron David Schneider Date: Wed, 1 Mar 2023 19:54:29 +0100 Subject: [PATCH 3/3] - add double precision to format specifier specifier - add code examples --- .github/workflows/main.yml | 1 + Dockerfile | 1 + .../fortran/classification/decision_tree.f90 | 29 +++++ .../fortran/classification/lightgbm.f90 | 111 ++++++++++++++++++ .../fortran/classification/linear.f90 | 12 ++ .../fortran/classification/random_forest.f90 | 71 +++++++++++ .../fortran/classification/svm.f90 | 67 +++++++++++ .../fortran/classification/xgboost.f90 | 91 ++++++++++++++ .../fortran/regression/decision_tree.f90 | 29 +++++ .../fortran/regression/lightgbm.f90 | 39 ++++++ .../fortran/regression/linear.f90 | 12 ++ .../fortran/regression/random_forest.f90 | 47 ++++++++ .../fortran/regression/svm.f90 | 12 ++ .../fortran/regression/xgboost.f90 | 39 ++++++ m2cgen/interpreters/fortran/code_generator.py | 2 +- m2cgen/interpreters/fortran/interpreter.py | 26 ++-- tests/e2e/executors/fortran.py | 4 +- tests/utils.py | 3 +- tools/generate_code_examples.py | 2 +- 19 files changed, 578 insertions(+), 20 deletions(-) create mode 100644 generated_code_examples/fortran/classification/decision_tree.f90 create mode 100644 generated_code_examples/fortran/classification/lightgbm.f90 create mode 100644 generated_code_examples/fortran/classification/linear.f90 create mode 100644 generated_code_examples/fortran/classification/random_forest.f90 create mode 100644 generated_code_examples/fortran/classification/svm.f90 create mode 100644 generated_code_examples/fortran/classification/xgboost.f90 create mode 100644 generated_code_examples/fortran/regression/decision_tree.f90 create mode 100644 generated_code_examples/fortran/regression/lightgbm.f90 create mode 100644 generated_code_examples/fortran/regression/linear.f90 create mode 100644 generated_code_examples/fortran/regression/random_forest.f90 create mode 100644 generated_code_examples/fortran/regression/svm.f90 create mode 100644 generated_code_examples/fortran/regression/xgboost.f90 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 06b3ccd7..e8e79c15 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,6 +26,7 @@ jobs: - "c_sharp or visual_basic or f_sharp" - "r_lang" - "elixir" + - "fortran" - "dart or powershell" steps: - name: Checkout repository diff --git a/Dockerfile b/Dockerfile index a572f07e..5c2f29d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,7 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ dotnet-sdk-6.0 \ g++ \ gcc \ + gfortran \ git \ golang-go \ haskell-platform \ diff --git a/generated_code_examples/fortran/classification/decision_tree.f90 b/generated_code_examples/fortran/classification/decision_tree.f90 new file mode 100644 index 00000000..22c10344 --- /dev/null +++ b/generated_code_examples/fortran/classification/decision_tree.f90 @@ -0,0 +1,29 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision, dimension(3) :: var0 + double precision, dimension(3) :: score + if (input(3) <= 2.449999988079071d0) then + var0 = (/ 1.0d0, 0.0d0, 0.0d0 /) + else + if (input(4) <= 1.75d0) then + if (input(3) <= 4.950000047683716d0) then + if (input(4) <= 1.6500000357627869d0) then + var0 = (/ 0.0d0, 1.0d0, 0.0d0 /) + else + var0 = (/ 0.0d0, 0.0d0, 1.0d0 /) + end if + else + var0 = (/ 0.0d0, 0.3333333333333333d0, 0.6666666666666666d0 /) + end if + else + var0 = (/ 0.0d0, 0.021739130434782608d0, 0.9782608695652174d0 /) + end if + end if + score(:) = var0 + return + end function score +end module Model diff --git a/generated_code_examples/fortran/classification/lightgbm.f90 b/generated_code_examples/fortran/classification/lightgbm.f90 new file mode 100644 index 00000000..ac016669 --- /dev/null +++ b/generated_code_examples/fortran/classification/lightgbm.f90 @@ -0,0 +1,111 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision :: var0 + double precision :: var1 + double precision :: var2 + double precision :: var3 + double precision :: var4 + double precision :: var5 + double precision, dimension(3) :: score + if (input(3) > 3.1500000000000004d0) then + var0 = -1.1986122886681099d0 + else + if (input(2) > 3.35d0) then + var0 = -0.8986122886681098d0 + else + var0 = -0.9136122886681098d0 + end if + end if + if (input(3) > 3.1500000000000004d0) then + if (input(3) > 4.450000000000001d0) then + var1 = -0.09503010837903424d0 + else + var1 = -0.09563272415214283d0 + end if + else + if (input(2) > 3.35d0) then + var1 = 0.16640323607832397d0 + else + var1 = 0.15374604217339707d0 + end if + end if + if (input(3) > 1.8d0) then + if (input(4) > 1.6500000000000001d0) then + var2 = -1.2055899476674514d0 + else + var2 = -0.9500445227622534d0 + end if + else + var2 = -1.2182214705715104d0 + end if + if (input(4) > 0.45000000000000007d0) then + if (input(4) > 1.6500000000000001d0) then + var3 = -0.08146437273923739d0 + else + var3 = 0.14244886188108738d0 + end if + else + if (input(3) > 1.4500000000000002d0) then + var3 = -0.0950888159264695d0 + else + var3 = -0.09438233722389686d0 + end if + end if + if (input(4) > 1.6500000000000001d0) then + if (input(3) > 5.3500000000000005d0) then + var4 = -0.8824095771015287d0 + else + var4 = -0.9121126703829481d0 + end if + else + if (input(3) > 4.450000000000001d0) then + var4 = -1.1277829563828181d0 + else + var4 = -1.1794405099157212d0 + end if + end if + if (input(3) > 4.750000000000001d0) then + if (input(3) > 5.150000000000001d0) then + var5 = 0.16625543464258166d0 + else + var5 = 0.09608601737074281d0 + end if + else + if (input(1) > 4.950000000000001d0) then + var5 = -0.09644547407948921d0 + else + var5 = -0.08181864271444342d0 + end if + end if + score(:) = SOFTMAX((/ var0 + var1, var2 + var3, var4 + var5 /)) + return + end function score + function softmax(x) result(res) + implicit none + double precision, dimension(:), intent(in) :: x + double precision, dimension(size(x)) :: res + double precision :: max_val, sum_val + integer :: i + + ! Find maximum value in x + max_val = x(1) + do i = 2, size(x) + if (x(i) > max_val) then + max_val = x(i) + end if + end do + + ! Compute softmax values + sum_val = 0.0d0 + do i = 1, size(x) + res(i) = exp(x(i) - max_val) + sum_val = sum_val + res(i) + end do + res = res / sum_val + + end function softmax +end module Model diff --git a/generated_code_examples/fortran/classification/linear.f90 b/generated_code_examples/fortran/classification/linear.f90 new file mode 100644 index 00000000..f33701b3 --- /dev/null +++ b/generated_code_examples/fortran/classification/linear.f90 @@ -0,0 +1,12 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + + double precision, dimension(3) :: score + score(:) = (/ 9.700311953536998d0 + input(1) * -0.4128360473754751d0 + input(2) * 0.9680426131053453d0 + input(3) * -2.498310603183548d0 + input(4) * -1.0723230787022542d0, 2.1575759475871163d0 + input(1) * 0.5400806228605453d0 + input(2) * -0.3245383349519669d0 + input(3) * -0.2034493200950831d0 + input(4) * -0.9338183426196143d0, -11.857887901124615d0 + input(1) * -0.12724457548509432d0 + input(2) * -0.6435042781533917d0 + input(3) * 2.7017599232786216d0 + input(4) * 2.006141421321863d0 /) + return + end function score +end module Model diff --git a/generated_code_examples/fortran/classification/random_forest.f90 b/generated_code_examples/fortran/classification/random_forest.f90 new file mode 100644 index 00000000..d91a138c --- /dev/null +++ b/generated_code_examples/fortran/classification/random_forest.f90 @@ -0,0 +1,71 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision, dimension(3) :: var0 + double precision, dimension(3) :: var1 + double precision, dimension(3) :: score + if (input(4) <= 0.75d0) then + var0 = (/ 1.0d0, 0.0d0, 0.0d0 /) + else + if (input(3) <= 4.75d0) then + var0 = (/ 0.0d0, 1.0d0, 0.0d0 /) + else + if (input(3) <= 5.049999952316284d0) then + if (input(4) <= 1.75d0) then + var0 = (/ 0.0d0, 0.8333333333333334d0, 0.16666666666666666d0 /) + else + var0 = (/ 0.0d0, 0.08333333333333333d0, 0.9166666666666666d0 /) + end if + else + var0 = (/ 0.0d0, 0.0d0, 1.0d0 /) + end if + end if + end if + if (input(4) <= 0.800000011920929d0) then + var1 = (/ 1.0d0, 0.0d0, 0.0d0 /) + else + if (input(1) <= 6.25d0) then + if (input(3) <= 4.8500001430511475d0) then + var1 = (/ 0.0d0, 0.9487179487179487d0, 0.05128205128205128d0 /) + else + var1 = (/ 0.0d0, 0.0d0, 1.0d0 /) + end if + else + if (input(4) <= 1.550000011920929d0) then + var1 = (/ 0.0d0, 0.8333333333333334d0, 0.16666666666666666d0 /) + else + var1 = (/ 0.0d0, 0.02564102564102564d0, 0.9743589743589743d0 /) + end if + end if + end if + score(:) = mul_vector_number(add_vectors(var0, var1), 0.5d0) + return + end function score + function add_vectors(v1, v2) result(res) + implicit none + double precision, dimension(:), intent(in) :: v1, v2 + double precision, dimension(size(v1)) :: res + integer :: i + + do i = 1, size(v1) + res(i) = v1(i) + v2(i) + end do + + end function add_vectors + + function mul_vector_number(v1, num) result(res) + implicit none + double precision, dimension(:), intent(in) :: v1 + double precision, intent(in) :: num + double precision, dimension(size(v1)) :: res + integer :: i + + do i = 1, size(v1) + res(i) = v1(i) * num + end do + + end function mul_vector_number +end module Model diff --git a/generated_code_examples/fortran/classification/svm.f90 b/generated_code_examples/fortran/classification/svm.f90 new file mode 100644 index 00000000..21d0a607 --- /dev/null +++ b/generated_code_examples/fortran/classification/svm.f90 @@ -0,0 +1,67 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision :: var0 + double precision :: var1 + double precision :: var2 + double precision :: var3 + double precision :: var4 + double precision :: var5 + double precision :: var6 + double precision :: var7 + double precision :: var8 + double precision :: var9 + double precision :: var10 + double precision :: var11 + double precision :: var12 + double precision :: var13 + double precision :: var14 + double precision :: var15 + double precision :: var16 + double precision :: var17 + double precision :: var18 + double precision :: var19 + double precision :: var20 + double precision :: var21 + double precision :: var22 + double precision :: var23 + double precision :: var24 + double precision :: var25 + double precision :: var26 + double precision :: var27 + double precision, dimension(3) :: score + var0 = EXP(-0.06389634699048878d0 * ((5.1d0 - input(1)) ** 2.0d0 + (2.5d0 - input(2)) ** 2.0d0 + (3.0d0 - input(3)) ** 2.0d0 + (1.1d0 - input(4)) ** 2.0d0)) + var1 = EXP(-0.06389634699048878d0 * ((4.9d0 - input(1)) ** 2.0d0 + (2.4d0 - input(2)) ** 2.0d0 + (3.3d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0)) + var2 = EXP(-0.06389634699048878d0 * ((6.3d0 - input(1)) ** 2.0d0 + (2.5d0 - input(2)) ** 2.0d0 + (4.9d0 - input(3)) ** 2.0d0 + (1.5d0 - input(4)) ** 2.0d0)) + var3 = EXP(-0.06389634699048878d0 * ((5.4d0 - input(1)) ** 2.0d0 + (3.0d0 - input(2)) ** 2.0d0 + (4.5d0 - input(3)) ** 2.0d0 + (1.5d0 - input(4)) ** 2.0d0)) + var4 = EXP(-0.06389634699048878d0 * ((6.2d0 - input(1)) ** 2.0d0 + (2.2d0 - input(2)) ** 2.0d0 + (4.5d0 - input(3)) ** 2.0d0 + (1.5d0 - input(4)) ** 2.0d0)) + var5 = EXP(-0.06389634699048878d0 * ((5.6d0 - input(1)) ** 2.0d0 + (2.9d0 - input(2)) ** 2.0d0 + (3.6d0 - input(3)) ** 2.0d0 + (1.3d0 - input(4)) ** 2.0d0)) + var6 = EXP(-0.06389634699048878d0 * ((6.7d0 - input(1)) ** 2.0d0 + (3.0d0 - input(2)) ** 2.0d0 + (5.0d0 - input(3)) ** 2.0d0 + (1.7d0 - input(4)) ** 2.0d0)) + var7 = EXP(-0.06389634699048878d0 * ((5.0d0 - input(1)) ** 2.0d0 + (2.3d0 - input(2)) ** 2.0d0 + (3.3d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0)) + var8 = EXP(-0.06389634699048878d0 * ((6.0d0 - input(1)) ** 2.0d0 + (2.7d0 - input(2)) ** 2.0d0 + (5.1d0 - input(3)) ** 2.0d0 + (1.6d0 - input(4)) ** 2.0d0)) + var9 = EXP(-0.06389634699048878d0 * ((5.9d0 - input(1)) ** 2.0d0 + (3.2d0 - input(2)) ** 2.0d0 + (4.8d0 - input(3)) ** 2.0d0 + (1.8d0 - input(4)) ** 2.0d0)) + var10 = EXP(-0.06389634699048878d0 * ((5.7d0 - input(1)) ** 2.0d0 + (2.6d0 - input(2)) ** 2.0d0 + (3.5d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0)) + var11 = EXP(-0.06389634699048878d0 * ((5.0d0 - input(1)) ** 2.0d0 + (3.0d0 - input(2)) ** 2.0d0 + (1.6d0 - input(3)) ** 2.0d0 + (0.2d0 - input(4)) ** 2.0d0)) + var12 = EXP(-0.06389634699048878d0 * ((5.4d0 - input(1)) ** 2.0d0 + (3.4d0 - input(2)) ** 2.0d0 + (1.7d0 - input(3)) ** 2.0d0 + (0.2d0 - input(4)) ** 2.0d0)) + var13 = EXP(-0.06389634699048878d0 * ((5.7d0 - input(1)) ** 2.0d0 + (3.8d0 - input(2)) ** 2.0d0 + (1.7d0 - input(3)) ** 2.0d0 + (0.3d0 - input(4)) ** 2.0d0)) + var14 = EXP(-0.06389634699048878d0 * ((4.8d0 - input(1)) ** 2.0d0 + (3.4d0 - input(2)) ** 2.0d0 + (1.9d0 - input(3)) ** 2.0d0 + (0.2d0 - input(4)) ** 2.0d0)) + var15 = EXP(-0.06389634699048878d0 * ((4.5d0 - input(1)) ** 2.0d0 + (2.3d0 - input(2)) ** 2.0d0 + (1.3d0 - input(3)) ** 2.0d0 + (0.3d0 - input(4)) ** 2.0d0)) + var16 = EXP(-0.06389634699048878d0 * ((5.7d0 - input(1)) ** 2.0d0 + (4.4d0 - input(2)) ** 2.0d0 + (1.5d0 - input(3)) ** 2.0d0 + (0.4d0 - input(4)) ** 2.0d0)) + var17 = EXP(-0.06389634699048878d0 * ((5.1d0 - input(1)) ** 2.0d0 + (3.8d0 - input(2)) ** 2.0d0 + (1.9d0 - input(3)) ** 2.0d0 + (0.4d0 - input(4)) ** 2.0d0)) + var18 = EXP(-0.06389634699048878d0 * ((5.1d0 - input(1)) ** 2.0d0 + (3.3d0 - input(2)) ** 2.0d0 + (1.7d0 - input(3)) ** 2.0d0 + (0.5d0 - input(4)) ** 2.0d0)) + var19 = EXP(-0.06389634699048878d0 * ((6.2d0 - input(1)) ** 2.0d0 + (2.8d0 - input(2)) ** 2.0d0 + (4.8d0 - input(3)) ** 2.0d0 + (1.8d0 - input(4)) ** 2.0d0)) + var20 = EXP(-0.06389634699048878d0 * ((7.2d0 - input(1)) ** 2.0d0 + (3.0d0 - input(2)) ** 2.0d0 + (5.8d0 - input(3)) ** 2.0d0 + (1.6d0 - input(4)) ** 2.0d0)) + var21 = EXP(-0.06389634699048878d0 * ((6.1d0 - input(1)) ** 2.0d0 + (3.0d0 - input(2)) ** 2.0d0 + (4.9d0 - input(3)) ** 2.0d0 + (1.8d0 - input(4)) ** 2.0d0)) + var22 = EXP(-0.06389634699048878d0 * ((6.0d0 - input(1)) ** 2.0d0 + (3.0d0 - input(2)) ** 2.0d0 + (4.8d0 - input(3)) ** 2.0d0 + (1.8d0 - input(4)) ** 2.0d0)) + var23 = EXP(-0.06389634699048878d0 * ((4.9d0 - input(1)) ** 2.0d0 + (2.5d0 - input(2)) ** 2.0d0 + (4.5d0 - input(3)) ** 2.0d0 + (1.7d0 - input(4)) ** 2.0d0)) + var24 = EXP(-0.06389634699048878d0 * ((7.9d0 - input(1)) ** 2.0d0 + (3.8d0 - input(2)) ** 2.0d0 + (6.4d0 - input(3)) ** 2.0d0 + (2.0d0 - input(4)) ** 2.0d0)) + var25 = EXP(-0.06389634699048878d0 * ((5.6d0 - input(1)) ** 2.0d0 + (2.8d0 - input(2)) ** 2.0d0 + (4.9d0 - input(3)) ** 2.0d0 + (2.0d0 - input(4)) ** 2.0d0)) + var26 = EXP(-0.06389634699048878d0 * ((6.0d0 - input(1)) ** 2.0d0 + (2.2d0 - input(2)) ** 2.0d0 + (5.0d0 - input(3)) ** 2.0d0 + (1.5d0 - input(4)) ** 2.0d0)) + var27 = EXP(-0.06389634699048878d0 * ((6.3d0 - input(1)) ** 2.0d0 + (2.8d0 - input(2)) ** 2.0d0 + (5.1d0 - input(3)) ** 2.0d0 + (1.5d0 - input(4)) ** 2.0d0)) + score(:) = (/ 0.11172510039290856d0 + var0 * -0.8898986041811555d0 + var1 * -0.8898986041811555d0 + var2 * -0.0d0 + var3 * -0.0d0 + var4 * -0.0d0 + var5 * -0.756413813553974d0 + var6 * -0.0d0 + var7 * -0.8898986041811555d0 + var8 * -0.0d0 + var9 * -0.0d0 + var10 * -0.8898986041811555d0 + var11 * 0.04218875216876044d0 + var12 * 0.7142250613852136d0 + var13 * 0.0d0 + var14 * 0.8898986041811555d0 + var15 * 0.8898986041811555d0 + var16 * 0.0d0 + var17 * 0.8898986041811555d0 + var18 * 0.8898986041811555d0, -0.04261957451303831d0 + var19 * -0.37953658977037247d0 + var20 * -0.0d0 + var21 * -0.0d0 + var22 * -0.37953658977037247d0 + var23 * -0.37953658977037247d0 + var24 * -0.26472396872040066d0 + var25 * -0.3745962010653211d0 + var26 * -0.10077618026650095d0 + var27 * -0.0d0 + var11 * 0.0d0 + var12 * 0.0d0 + var13 * 0.37953658977037247d0 + var14 * 0.37953658977037247d0 + var15 * 0.3044555865539922d0 + var16 * 0.05610417372785803d0 + var17 * 0.37953658977037247d0 + var18 * 0.37953658977037247d0, 1.8136162062461285d0 + var19 * -110.34516826676301d0 + var20 * -13.999391039896215d0 + var21 * -108.44329471899991d0 + var22 * -110.34516826676301d0 + var23 * -22.21095753342801d0 + var24 * -0.0d0 + var25 * -0.0d0 + var26 * -65.00217641452454d0 + var27 * -110.34516826676301d0 + var0 * 0.0d0 + var1 * 0.0d0 + var2 * 110.34516826676301d0 + var3 * 62.115561183470184d0 + var4 * 37.19509025661546d0 + var5 * 0.0d0 + var6 * 110.34516826676301d0 + var7 * 0.0d0 + var8 * 110.34516826676301d0 + var9 * 110.34516826676301d0 + var10 * 0.0d0 /) + return + end function score +end module Model diff --git a/generated_code_examples/fortran/classification/xgboost.f90 b/generated_code_examples/fortran/classification/xgboost.f90 new file mode 100644 index 00000000..c88cc18b --- /dev/null +++ b/generated_code_examples/fortran/classification/xgboost.f90 @@ -0,0 +1,91 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision :: var0 + double precision :: var1 + double precision :: var2 + double precision :: var3 + double precision :: var4 + double precision :: var5 + double precision, dimension(3) :: score + if (input(3) >= 2.45d0) then + var0 = -0.21995015d0 + else + var0 = 0.4302439d0 + end if + if (input(3) >= 2.45d0) then + var1 = -0.19691855d0 + else + var1 = 0.29493433d0 + end if + if (input(3) >= 2.45d0) then + if (input(4) >= 1.75d0) then + var2 = -0.20051816d0 + else + var2 = 0.36912444d0 + end if + else + var2 = -0.21512198d0 + end if + if (input(3) >= 2.45d0) then + if (input(3) >= 4.8500004d0) then + var3 = -0.14888482d0 + else + var3 = 0.2796613d0 + end if + else + var3 = -0.19143805d0 + end if + if (input(4) >= 1.6500001d0) then + var4 = 0.40298507d0 + else + if (input(3) >= 4.95d0) then + var4 = 0.21724138d0 + else + var4 = -0.21974029d0 + end if + end if + if (input(3) >= 4.75d0) then + if (input(4) >= 1.75d0) then + var5 = 0.28692952d0 + else + var5 = 0.06272897d0 + end if + else + if (input(4) >= 1.55d0) then + var5 = 0.009899145d0 + else + var5 = -0.19659369d0 + end if + end if + score(:) = SOFTMAX((/ 0.5d0 + var0 + var1, 0.5d0 + var2 + var3, 0.5d0 + var4 + var5 /)) + return + end function score + function softmax(x) result(res) + implicit none + double precision, dimension(:), intent(in) :: x + double precision, dimension(size(x)) :: res + double precision :: max_val, sum_val + integer :: i + + ! Find maximum value in x + max_val = x(1) + do i = 2, size(x) + if (x(i) > max_val) then + max_val = x(i) + end if + end do + + ! Compute softmax values + sum_val = 0.0d0 + do i = 1, size(x) + res(i) = exp(x(i) - max_val) + sum_val = sum_val + res(i) + end do + res = res / sum_val + + end function softmax +end module Model diff --git a/generated_code_examples/fortran/regression/decision_tree.f90 b/generated_code_examples/fortran/regression/decision_tree.f90 new file mode 100644 index 00000000..ac211e59 --- /dev/null +++ b/generated_code_examples/fortran/regression/decision_tree.f90 @@ -0,0 +1,29 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision :: var0 + double precision :: score + if (input(13) <= 9.724999904632568d0) then + if (input(6) <= 7.437000036239624d0) then + if (input(8) <= 1.4849499464035034d0) then + var0 = 50.0d0 + else + var0 = 26.681034482758605d0 + end if + else + var0 = 44.96896551724139d0 + end if + else + if (input(13) <= 16.085000038146973d0) then + var0 = 20.284353741496595d0 + else + var0 = 14.187142857142863d0 + end if + end if + score = var0 + return + end function score +end module Model diff --git a/generated_code_examples/fortran/regression/lightgbm.f90 b/generated_code_examples/fortran/regression/lightgbm.f90 new file mode 100644 index 00000000..6b6c82b0 --- /dev/null +++ b/generated_code_examples/fortran/regression/lightgbm.f90 @@ -0,0 +1,39 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision :: var0 + double precision :: var1 + double precision :: score + if (input(13) > 9.725000000000003d0) then + if (input(13) > 16.205000000000002d0) then + var0 = 21.71499740307178d0 + else + var0 = 22.322292901846218d0 + end if + else + if (input(6) > 7.418000000000001d0) then + var0 = 24.75760617150803d0 + else + var0 = 23.02910423871904d0 + end if + end if + if (input(6) > 6.837500000000001d0) then + if (input(6) > 7.462000000000001d0) then + var1 = 2.0245964808123453d0 + else + var1 = 0.859548540618913d0 + end if + else + if (input(13) > 14.365d0) then + var1 = -0.7009440524656984d0 + else + var1 = 0.052794864734003494d0 + end if + end if + score = var0 + var1 + return + end function score +end module Model diff --git a/generated_code_examples/fortran/regression/linear.f90 b/generated_code_examples/fortran/regression/linear.f90 new file mode 100644 index 00000000..f4858593 --- /dev/null +++ b/generated_code_examples/fortran/regression/linear.f90 @@ -0,0 +1,12 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + + double precision :: score + score = 36.367080746577244d0 + input(1) * -0.10861311354908008d0 + input(2) * 0.046461486329936456d0 + input(3) * 0.027432259970172148d0 + input(4) * 2.6160671309537777d0 + input(5) * -17.51793656329737d0 + input(6) * 3.7674418196772255d0 + input(7) * -0.000021581753164971046d0 + input(8) * -1.4711768622633645d0 + input(9) * 0.2956767140062958d0 + input(10) * -0.012233831527259383d0 + input(11) * -0.9220356453705304d0 + input(12) * 0.009038220462695552d0 + input(13) * -0.542583033714222d0 + return + end function score +end module Model diff --git a/generated_code_examples/fortran/regression/random_forest.f90 b/generated_code_examples/fortran/regression/random_forest.f90 new file mode 100644 index 00000000..7a1b424b --- /dev/null +++ b/generated_code_examples/fortran/regression/random_forest.f90 @@ -0,0 +1,47 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision :: var0 + double precision :: var1 + double precision :: score + if (input(13) <= 9.845000267028809d0) then + if (input(6) <= 6.959500074386597d0) then + if (input(7) <= 96.20000076293945d0) then + var0 = 25.093162393162395d0 + else + var0 = 50.0d0 + end if + else + var0 = 38.074999999999996d0 + end if + else + if (input(13) <= 15.074999809265137d0) then + var0 = 20.518439716312056d0 + else + var0 = 14.451282051282046d0 + end if + end if + if (input(13) <= 9.650000095367432d0) then + if (input(6) <= 7.437000036239624d0) then + if (input(8) <= 1.47284996509552d0) then + var1 = 50.0d0 + else + var1 = 26.7965317919075d0 + end if + else + var1 = 44.21176470588236d0 + end if + else + if (input(13) <= 17.980000495910645d0) then + var1 = 19.645652173913035d0 + else + var1 = 12.791919191919195d0 + end if + end if + score = (var0 + var1) * 0.5d0 + return + end function score +end module Model diff --git a/generated_code_examples/fortran/regression/svm.f90 b/generated_code_examples/fortran/regression/svm.f90 new file mode 100644 index 00000000..7ae63dd0 --- /dev/null +++ b/generated_code_examples/fortran/regression/svm.f90 @@ -0,0 +1,12 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + + double precision :: score + score = 25.346480984077544d0 + EXP(-0.0000036459736698188483d0 * ((16.8118d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.7d0 - input(5)) ** 2.0d0 + (5.277d0 - input(6)) ** 2.0d0 + (98.1d0 - input(7)) ** 2.0d0 + (1.4261d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (30.81d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((38.3518d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (5.453d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.4896d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (30.59d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.84054d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (5.599d0 - input(6)) ** 2.0d0 + (85.7d0 - input(7)) ** 2.0d0 + (4.4546d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (303.42d0 - input(12)) ** 2.0d0 + (16.51d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.15172d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (5.701d0 - input(6)) ** 2.0d0 + (95.0d0 - input(7)) ** 2.0d0 + (3.7872d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (358.77d0 - input(12)) ** 2.0d0 + (18.35d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((24.8017d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (5.349d0 - input(6)) ** 2.0d0 + (96.0d0 - input(7)) ** 2.0d0 + (1.7028d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (19.77d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((41.5292d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (5.531d0 - input(6)) ** 2.0d0 + (85.4d0 - input(7)) ** 2.0d0 + (1.6074d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (329.46d0 - input(12)) ** 2.0d0 + (27.38d0 - input(13)) ** 2.0d0)) * -0.3490103966325617d0 + EXP(-0.0000036459736698188483d0 * ((0.38735d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (25.65d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.581d0 - input(5)) ** 2.0d0 + (5.613d0 - input(6)) ** 2.0d0 + (95.6d0 - input(7)) ** 2.0d0 + (1.7572d0 - input(8)) ** 2.0d0 + (2.0d0 - input(9)) ** 2.0d0 + (188.0d0 - input(10)) ** 2.0d0 + (19.1d0 - input(11)) ** 2.0d0 + (359.29d0 - input(12)) ** 2.0d0 + (27.26d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.05602d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (2.46d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.488d0 - input(5)) ** 2.0d0 + (7.831d0 - input(6)) ** 2.0d0 + (53.6d0 - input(7)) ** 2.0d0 + (3.1992d0 - input(8)) ** 2.0d0 + (3.0d0 - input(9)) ** 2.0d0 + (193.0d0 - input(10)) ** 2.0d0 + (17.8d0 - input(11)) ** 2.0d0 + (392.63d0 - input(12)) ** 2.0d0 + (4.45d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((25.0461d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (5.987d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.5888d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (26.77d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((8.26725d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.668d0 - input(5)) ** 2.0d0 + (5.875d0 - input(6)) ** 2.0d0 + (89.6d0 - input(7)) ** 2.0d0 + (1.1296d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (347.88d0 - input(12)) ** 2.0d0 + (8.88d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((5.66998d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.631d0 - input(5)) ** 2.0d0 + (6.683d0 - input(6)) ** 2.0d0 + (96.8d0 - input(7)) ** 2.0d0 + (1.3567d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (375.33d0 - input(12)) ** 2.0d0 + (3.73d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.51902d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (19.58d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.605d0 - input(5)) ** 2.0d0 + (8.375d0 - input(6)) ** 2.0d0 + (93.9d0 - input(7)) ** 2.0d0 + (2.162d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (403.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (388.45d0 - input(12)) ** 2.0d0 + (3.32d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.29819d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (6.2d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.504d0 - input(5)) ** 2.0d0 + (7.686d0 - input(6)) ** 2.0d0 + (17.0d0 - input(7)) ** 2.0d0 + (3.3751d0 - input(8)) ** 2.0d0 + (8.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (17.4d0 - input(11)) ** 2.0d0 + (377.51d0 - input(12)) ** 2.0d0 + (3.92d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((3.32105d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (19.58d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.871d0 - input(5)) ** 2.0d0 + (5.403d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.3216d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (403.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (26.82d0 - input(13)) ** 2.0d0)) * -0.400989603367655d0 + EXP(-0.0000036459736698188483d0 * ((0.61154d0 - input(1)) ** 2.0d0 + (20.0d0 - input(2)) ** 2.0d0 + (3.97d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.647d0 - input(5)) ** 2.0d0 + (8.704d0 - input(6)) ** 2.0d0 + (86.9d0 - input(7)) ** 2.0d0 + (1.801d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (264.0d0 - input(10)) ** 2.0d0 + (13.0d0 - input(11)) ** 2.0d0 + (389.7d0 - input(12)) ** 2.0d0 + (5.12d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.02009d0 - input(1)) ** 2.0d0 + (95.0d0 - input(2)) ** 2.0d0 + (2.68d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.4161d0 - input(5)) ** 2.0d0 + (8.034d0 - input(6)) ** 2.0d0 + (31.9d0 - input(7)) ** 2.0d0 + (5.118d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (224.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (390.55d0 - input(12)) ** 2.0d0 + (2.88d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.08187d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (2.89d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.445d0 - input(5)) ** 2.0d0 + (7.82d0 - input(6)) ** 2.0d0 + (36.9d0 - input(7)) ** 2.0d0 + (3.4952d0 - input(8)) ** 2.0d0 + (2.0d0 - input(9)) ** 2.0d0 + (276.0d0 - input(10)) ** 2.0d0 + (18.0d0 - input(11)) ** 2.0d0 + (393.53d0 - input(12)) ** 2.0d0 + (3.57d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.57834d0 - input(1)) ** 2.0d0 + (20.0d0 - input(2)) ** 2.0d0 + (3.97d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.575d0 - input(5)) ** 2.0d0 + (8.297d0 - input(6)) ** 2.0d0 + (67.0d0 - input(7)) ** 2.0d0 + (2.4216d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (264.0d0 - input(10)) ** 2.0d0 + (13.0d0 - input(11)) ** 2.0d0 + (384.54d0 - input(12)) ** 2.0d0 + (7.44d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.35472d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (6.072d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (4.175d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (376.73d0 - input(12)) ** 2.0d0 + (13.04d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.52693d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (6.2d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.504d0 - input(5)) ** 2.0d0 + (8.725d0 - input(6)) ** 2.0d0 + (83.0d0 - input(7)) ** 2.0d0 + (2.8944d0 - input(8)) ** 2.0d0 + (8.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (17.4d0 - input(11)) ** 2.0d0 + (382.0d0 - input(12)) ** 2.0d0 + (4.63d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.33147d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (6.2d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.507d0 - input(5)) ** 2.0d0 + (8.247d0 - input(6)) ** 2.0d0 + (70.4d0 - input(7)) ** 2.0d0 + (3.6519d0 - input(8)) ** 2.0d0 + (8.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (17.4d0 - input(11)) ** 2.0d0 + (378.95d0 - input(12)) ** 2.0d0 + (3.95d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.13081d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (5.713d0 - input(6)) ** 2.0d0 + (94.1d0 - input(7)) ** 2.0d0 + (4.233d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (360.17d0 - input(12)) ** 2.0d0 + (22.6d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((4.89822d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.631d0 - input(5)) ** 2.0d0 + (4.97d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.3325d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (375.52d0 - input(12)) ** 2.0d0 + (3.26d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.25179d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (5.57d0 - input(6)) ** 2.0d0 + (98.1d0 - input(7)) ** 2.0d0 + (3.7979d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (376.57d0 - input(12)) ** 2.0d0 + (21.02d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.06129d0 - input(1)) ** 2.0d0 + (20.0d0 - input(2)) ** 2.0d0 + (3.33d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.4429d0 - input(5)) ** 2.0d0 + (7.645d0 - input(6)) ** 2.0d0 + (49.7d0 - input(7)) ** 2.0d0 + (5.2119d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (216.0d0 - input(10)) ** 2.0d0 + (14.9d0 - input(11)) ** 2.0d0 + (377.07d0 - input(12)) ** 2.0d0 + (3.01d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((9.2323d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.631d0 - input(5)) ** 2.0d0 + (6.216d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.1691d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (366.15d0 - input(12)) ** 2.0d0 + (9.53d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((2.77974d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (19.58d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.871d0 - input(5)) ** 2.0d0 + (4.903d0 - input(6)) ** 2.0d0 + (97.8d0 - input(7)) ** 2.0d0 + (1.3459d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (403.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (29.29d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.01381d0 - input(1)) ** 2.0d0 + (80.0d0 - input(2)) ** 2.0d0 + (0.46d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.422d0 - input(5)) ** 2.0d0 + (7.875d0 - input(6)) ** 2.0d0 + (32.0d0 - input(7)) ** 2.0d0 + (5.6484d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (255.0d0 - input(10)) ** 2.0d0 + (14.4d0 - input(11)) ** 2.0d0 + (394.23d0 - input(12)) ** 2.0d0 + (2.97d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.01538d0 - input(1)) ** 2.0d0 + (90.0d0 - input(2)) ** 2.0d0 + (3.75d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.394d0 - input(5)) ** 2.0d0 + (7.454d0 - input(6)) ** 2.0d0 + (34.2d0 - input(7)) ** 2.0d0 + (6.3361d0 - input(8)) ** 2.0d0 + (3.0d0 - input(9)) ** 2.0d0 + (244.0d0 - input(10)) ** 2.0d0 + (15.9d0 - input(11)) ** 2.0d0 + (386.34d0 - input(12)) ** 2.0d0 + (3.11d0 - input(13)) ** 2.0d0)) * 0.7500000000002167d0 + EXP(-0.0000036459736698188483d0 * ((1.38799d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (5.95d0 - input(6)) ** 2.0d0 + (82.0d0 - input(7)) ** 2.0d0 + (3.99d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (232.6d0 - input(12)) ** 2.0d0 + (27.71d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.83377d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (19.58d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.605d0 - input(5)) ** 2.0d0 + (7.802d0 - input(6)) ** 2.0d0 + (98.2d0 - input(7)) ** 2.0d0 + (2.0407d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (403.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (389.61d0 - input(12)) ** 2.0d0 + (1.92d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.31533d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (6.2d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.504d0 - input(5)) ** 2.0d0 + (8.266d0 - input(6)) ** 2.0d0 + (78.3d0 - input(7)) ** 2.0d0 + (2.8944d0 - input(8)) ** 2.0d0 + (8.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (17.4d0 - input(11)) ** 2.0d0 + (385.05d0 - input(12)) ** 2.0d0 + (4.14d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((9.91655d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (5.852d0 - input(6)) ** 2.0d0 + (77.8d0 - input(7)) ** 2.0d0 + (1.5004d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (338.16d0 - input(12)) ** 2.0d0 + (29.97d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.01501d0 - input(1)) ** 2.0d0 + (90.0d0 - input(2)) ** 2.0d0 + (1.21d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.401d0 - input(5)) ** 2.0d0 + (7.923d0 - input(6)) ** 2.0d0 + (24.8d0 - input(7)) ** 2.0d0 + (5.885d0 - input(8)) ** 2.0d0 + (1.0d0 - input(9)) ** 2.0d0 + (198.0d0 - input(10)) ** 2.0d0 + (13.6d0 - input(11)) ** 2.0d0 + (395.52d0 - input(12)) ** 2.0d0 + (3.16d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.25387d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (6.91d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.448d0 - input(5)) ** 2.0d0 + (5.399d0 - input(6)) ** 2.0d0 + (95.3d0 - input(7)) ** 2.0d0 + (5.87d0 - input(8)) ** 2.0d0 + (3.0d0 - input(9)) ** 2.0d0 + (233.0d0 - input(10)) ** 2.0d0 + (17.9d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (30.81d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((14.2362d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (6.343d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.5741d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (20.32d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((22.5971d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.7d0 - input(5)) ** 2.0d0 + (5.0d0 - input(6)) ** 2.0d0 + (89.5d0 - input(7)) ** 2.0d0 + (1.5184d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (396.9d0 - input(12)) ** 2.0d0 + (31.99d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((67.9208d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (5.683d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.4254d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (384.97d0 - input(12)) ** 2.0d0 + (22.98d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.61282d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (6.096d0 - input(6)) ** 2.0d0 + (96.9d0 - input(7)) ** 2.0d0 + (3.7598d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (248.31d0 - input(12)) ** 2.0d0 + (20.34d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.46336d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (19.58d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.605d0 - input(5)) ** 2.0d0 + (7.489d0 - input(6)) ** 2.0d0 + (90.8d0 - input(7)) ** 2.0d0 + (1.9709d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (403.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (374.43d0 - input(12)) ** 2.0d0 + (1.73d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((7.67202d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (5.747d0 - input(6)) ** 2.0d0 + (98.9d0 - input(7)) ** 2.0d0 + (1.6334d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (393.1d0 - input(12)) ** 2.0d0 + (19.92d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((2.01019d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (19.58d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.605d0 - input(5)) ** 2.0d0 + (7.929d0 - input(6)) ** 2.0d0 + (96.2d0 - input(7)) ** 2.0d0 + (2.0459d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (403.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (369.3d0 - input(12)) ** 2.0d0 + (3.7d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((45.7461d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.693d0 - input(5)) ** 2.0d0 + (4.519d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.6582d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (88.27d0 - input(12)) ** 2.0d0 + (36.98d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.03578d0 - input(1)) ** 2.0d0 + (20.0d0 - input(2)) ** 2.0d0 + (3.33d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.4429d0 - input(5)) ** 2.0d0 + (7.82d0 - input(6)) ** 2.0d0 + (64.5d0 - input(7)) ** 2.0d0 + (4.6947d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (216.0d0 - input(10)) ** 2.0d0 + (14.9d0 - input(11)) ** 2.0d0 + (387.31d0 - input(12)) ** 2.0d0 + (3.76d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.18337d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (27.74d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.609d0 - input(5)) ** 2.0d0 + (5.414d0 - input(6)) ** 2.0d0 + (98.3d0 - input(7)) ** 2.0d0 + (1.7554d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (711.0d0 - input(10)) ** 2.0d0 + (20.1d0 - input(11)) ** 2.0d0 + (344.05d0 - input(12)) ** 2.0d0 + (23.97d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((6.53876d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (1.0d0 - input(4)) ** 2.0d0 + (0.631d0 - input(5)) ** 2.0d0 + (7.016d0 - input(6)) ** 2.0d0 + (97.5d0 - input(7)) ** 2.0d0 + (1.2024d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (392.05d0 - input(12)) ** 2.0d0 + (2.96d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((1.22358d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (19.58d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.605d0 - input(5)) ** 2.0d0 + (6.943d0 - input(6)) ** 2.0d0 + (97.4d0 - input(7)) ** 2.0d0 + (1.8773d0 - input(8)) ** 2.0d0 + (5.0d0 - input(9)) ** 2.0d0 + (403.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (363.43d0 - input(12)) ** 2.0d0 + (4.59d0 - input(13)) ** 2.0d0)) * 1.0d0 + EXP(-0.0000036459736698188483d0 * ((10.8342d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.679d0 - input(5)) ** 2.0d0 + (6.782d0 - input(6)) ** 2.0d0 + (90.8d0 - input(7)) ** 2.0d0 + (1.8195d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (21.57d0 - input(12)) ** 2.0d0 + (25.79d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.98843d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (8.14d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.538d0 - input(5)) ** 2.0d0 + (5.813d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (4.0952d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (307.0d0 - input(10)) ** 2.0d0 + (21.0d0 - input(11)) ** 2.0d0 + (394.54d0 - input(12)) ** 2.0d0 + (19.88d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((18.0846d0 - input(1)) ** 2.0d0 + (0.0d0 - input(2)) ** 2.0d0 + (18.1d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.679d0 - input(5)) ** 2.0d0 + (6.434d0 - input(6)) ** 2.0d0 + (100.0d0 - input(7)) ** 2.0d0 + (1.8347d0 - input(8)) ** 2.0d0 + (24.0d0 - input(9)) ** 2.0d0 + (666.0d0 - input(10)) ** 2.0d0 + (20.2d0 - input(11)) ** 2.0d0 + (27.25d0 - input(12)) ** 2.0d0 + (29.05d0 - input(13)) ** 2.0d0)) * -1.0d0 + EXP(-0.0000036459736698188483d0 * ((0.0351d0 - input(1)) ** 2.0d0 + (95.0d0 - input(2)) ** 2.0d0 + (2.68d0 - input(3)) ** 2.0d0 + (0.0d0 - input(4)) ** 2.0d0 + (0.4161d0 - input(5)) ** 2.0d0 + (7.853d0 - input(6)) ** 2.0d0 + (33.2d0 - input(7)) ** 2.0d0 + (5.118d0 - input(8)) ** 2.0d0 + (4.0d0 - input(9)) ** 2.0d0 + (224.0d0 - input(10)) ** 2.0d0 + (14.7d0 - input(11)) ** 2.0d0 + (392.78d0 - input(12)) ** 2.0d0 + (3.81d0 - input(13)) ** 2.0d0)) * 1.0d0 + return + end function score +end module Model diff --git a/generated_code_examples/fortran/regression/xgboost.f90 b/generated_code_examples/fortran/regression/xgboost.f90 new file mode 100644 index 00000000..75f249a4 --- /dev/null +++ b/generated_code_examples/fortran/regression/xgboost.f90 @@ -0,0 +1,39 @@ +module Model + implicit none +contains + function score(input) + implicit none + double precision, dimension(:) :: input + double precision :: var0 + double precision :: var1 + double precision :: score + if (input(13) >= 9.725d0) then + if (input(13) >= 19.23d0) then + var0 = 3.5343752d0 + else + var0 = 5.5722494d0 + end if + else + if (input(6) >= 6.941d0) then + var0 = 11.1947155d0 + else + var0 = 7.4582143d0 + end if + end if + if (input(13) >= 5.1549997d0) then + if (input(13) >= 15.0d0) then + var1 = 2.8350503d0 + else + var1 = 4.8024607d0 + end if + else + if (input(6) >= 7.406d0) then + var1 = 10.0011215d0 + else + var1 = 6.787523d0 + end if + end if + score = 0.5d0 + var0 + var1 + return + end function score +end module Model diff --git a/m2cgen/interpreters/fortran/code_generator.py b/m2cgen/interpreters/fortran/code_generator.py index bd49a766..fc01cde9 100644 --- a/m2cgen/interpreters/fortran/code_generator.py +++ b/m2cgen/interpreters/fortran/code_generator.py @@ -4,7 +4,7 @@ class FortranCodeGenerator(ImperativeCodeGenerator): - tpl_num_value = CodeTemplate("{value}") + tpl_num_value = CodeTemplate("{value}d0") tpl_infix_expression = CodeTemplate("{left} {op} {right}") tpl_return_statement_vec = CodeTemplate("{func_name}(:) = {value}") tpl_return_statement_single = CodeTemplate("{func_name} = {value}") diff --git a/m2cgen/interpreters/fortran/interpreter.py b/m2cgen/interpreters/fortran/interpreter.py index e3e24c67..d8f3805c 100644 --- a/m2cgen/interpreters/fortran/interpreter.py +++ b/m2cgen/interpreters/fortran/interpreter.py @@ -3,25 +3,13 @@ from m2cgen.ast import BinNumOpType from m2cgen.interpreters.fortran.code_generator import FortranCodeGenerator from m2cgen.interpreters.interpreter import ImperativeToCodeInterpreter -from m2cgen.interpreters.mixins import BinExpressionDepthTrackingMixin, LinearAlgebraMixin, PowExprFunctionMixin +from m2cgen.interpreters.mixins import PowExprInfixMixin, LinearAlgebraMixin from m2cgen.interpreters.utils import get_file_content class FortranInterpreter(ImperativeToCodeInterpreter, - PowExprFunctionMixin, - BinExpressionDepthTrackingMixin, - LinearAlgebraMixin): - # needs to be tested. - bin_depth_threshold = 55 - - supported_bin_vector_ops = { - BinNumOpType.ADD: "add_vectors", - } - - supported_bin_vector_num_ops = { - BinNumOpType.MUL: "mul_vector_number", - } - + LinearAlgebraMixin, + PowExprInfixMixin): abs_function_name = "ABS" atan_function_name = "ATAN" exponent_function_name = "EXP" @@ -34,6 +22,14 @@ class FortranInterpreter(ImperativeToCodeInterpreter, pow_operator = "**" + supported_bin_vector_ops = { + BinNumOpType.ADD: "add_vectors", + } + + supported_bin_vector_num_ops = { + BinNumOpType.MUL: "mul_vector_number", + } + with_sigmoid_expr = False with_softmax_expr = False with_log1p_expr = False diff --git a/tests/e2e/executors/fortran.py b/tests/e2e/executors/fortran.py index 1eae5afb..a5c5849c 100644 --- a/tests/e2e/executors/fortran.py +++ b/tests/e2e/executors/fortran.py @@ -46,12 +46,12 @@ EXECUTE_AND_PRINT_SCALAR = """ result(1) = score(input) - print '(e21.14)', result(1) + print '(e22.14)', result(1) """ EXECUTE_AND_PRINT_VECTOR_TPL = """ result = score(input) - print '({size}e21.14)', result + print '({size}e22.14)', result """ diff --git a/tests/utils.py b/tests/utils.py index 4c5630d6..a2654e71 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -278,7 +278,8 @@ def execute_command(exec_args, shell=False): def predict_from_commandline(exec_args): - items = execute_command(exec_args).split(" ") + output = execute_command(exec_args).split(" ") + items = [o for o in output if o != ""] if len(items) == 1: return np.float64(items[0]) else: diff --git a/tools/generate_code_examples.py b/tools/generate_code_examples.py index a912560c..6e3a7944 100644 --- a/tools/generate_code_examples.py +++ b/tools/generate_code_examples.py @@ -47,7 +47,7 @@ ("f_sharp", m2c.export_to_f_sharp, "fs"), ("rust", m2c.export_to_rust, "rs"), ("elixir", m2c.export_to_elixir, "ex"), - ("fortran", m2c.export_to_fortran, "fo"), + ("fortran", m2c.export_to_fortran, "f90"), ] EXAMPLE_MODELS = [