Skip to content

Commit

Permalink
Make the scoring function name parametrized (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrshu committed Feb 20, 2020
1 parent d28cb00 commit a4ef90f
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 67 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ public class Model {

`m2cgen` can be used as a CLI tool to generate code using serialized model objects (pickle protocol):
```
$ m2cgen <pickle_file> --language <language> [--indent <indent>] [--class_name <class_name>]
[--module_name <module_name>] [--package_name <package_name>] [--namespace <namespace>]
[--recursion-limit <recursion_limit>]
$ m2cgen <pickle_file> --language <language> [--indent <indent>] [--function_name <function_name>]
[--class_name <class_name>] [--module_name <module_name>] [--package_name <package_name>]
[--namespace <namespace>] [--recursion-limit <recursion_limit>]
```
Don't forget that for unpickling serialized model objects their classes must be defined in the top level of an importable module in the unpickling environment.

Expand Down
55 changes: 37 additions & 18 deletions m2cgen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,26 @@
import pickle
import argparse
import sys
import inspect
import numpy as np

import m2cgen


LANGUAGE_TO_EXPORTER = {
"python": (m2cgen.export_to_python, ["indent"]),
"java": (m2cgen.export_to_java, ["indent", "class_name", "package_name"]),
"c": (m2cgen.export_to_c, ["indent"]),
"go": (m2cgen.export_to_go, ["indent"]),
"javascript": (m2cgen.export_to_javascript, ["indent"]),
"python": (m2cgen.export_to_python, ["indent", "function_name"]),
"java": (m2cgen.export_to_java, ["indent", "class_name", "package_name",
"function_name"]),
"c": (m2cgen.export_to_c, ["indent", "function_name"]),
"go": (m2cgen.export_to_go, ["indent", "function_name"]),
"javascript": (m2cgen.export_to_javascript, ["indent", "function_name"]),
"visual_basic": (m2cgen.export_to_visual_basic,
["module_name", "indent"]),
["module_name", "indent", "function_name"]),
"c_sharp": (m2cgen.export_to_c_sharp,
["indent", "class_name", "namespace"]),
"powershell": (m2cgen.export_to_powershell, ["indent"]),
"r": (m2cgen.export_to_r, ["indent"]),
"php": (m2cgen.export_to_php, ["indent"]),
["indent", "class_name", "namespace", "function_name"]),
"powershell": (m2cgen.export_to_powershell, ["indent", "function_name"]),
"r": (m2cgen.export_to_r, ["indent", "function_name"]),
"php": (m2cgen.export_to_php, ["indent", "function_name"]),
}


Expand All @@ -38,35 +40,42 @@

parser = argparse.ArgumentParser(
prog="m2cgen",
description="Generate code in native language for provided model")
description="Generate code in native language for provided model.")
parser.add_argument(
"infile", type=argparse.FileType("rb"), nargs="?",
default=sys.stdin.buffer,
help="File with pickle representation of the model")
help="File with pickle representation of the model.")
parser.add_argument(
"--language", "-l", type=str,
choices=LANGUAGE_TO_EXPORTER.keys(),
help="Target language",
help="Target language.",
required=True)
parser.add_argument(
"--function_name", "-fn", dest="function_name", type=str,
# The default value is conditional and will be set in the argument's
# post-processing, based on the signature of the `export` function
# that belongs to the specified target language.
default=None,
help="Name of the function in the generated code.")
parser.add_argument(
"--class_name", "-cn", dest="class_name", type=str,
help="Name of the generated class (if supported by target language)")
help="Name of the generated class (if supported by target language).")
parser.add_argument(
"--package_name", "-pn", dest="package_name", type=str,
help="Package name for the generated code "
"(if supported by target language)")
"(if supported by target language).")
parser.add_argument(
"--module_name", "-mn", dest="module_name", type=str,
help="Module name for the generated code "
"(if supported by target language)")
"(if supported by target language).")
parser.add_argument(
"--namespace", "-ns", dest="namespace", type=str,
help="Namespace for the generated code "
"(if supported by target language)")
"(if supported by target language).")
parser.add_argument(
"--indent", "-i", dest="indent", type=int,
default=4,
help="Indentation for the generated code")
help="Indentation for the generated code.")
parser.add_argument(
"--recursion-limit", "-rl", type=int,
help="Sets the maximum depth of the Python interpreter stack. "
Expand Down Expand Up @@ -95,6 +104,16 @@ def generate_code(args):
if arg_value is not None:
kwargs[arg_name] = arg_value

# Special handling for the function_name parameter, which needs to be
# the same as the default value of the keyword argument of the exporter
# (this is due to languages like C# which prefer their method names to
# follow PascalCase unlike all the other supported languages -- see
# https://github.com/BayesWitnesses/m2cgen/pull/166/files#r379867601
# for more).
if arg_name == 'function_name' and arg_value is None:
param = inspect.signature(exporter).parameters['function_name']
kwargs[arg_name] = param.default

return exporter(model, **kwargs)


Expand Down
93 changes: 72 additions & 21 deletions m2cgen/exporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from m2cgen import interpreters


def export_to_java(model, package_name=None, class_name="Model", indent=4):
def export_to_java(model, package_name=None, class_name="Model", indent=4,
function_name="score"):
"""
Generates a Java code representation of the given model.
Expand All @@ -16,6 +17,8 @@ def export_to_java(model, package_name=None, class_name="Model", indent=4):
The name of the generated class.
indent : int, optional
The size of indents in the generated code.
function_name : string, optional
Name of the function in the generated code.
Returns
-------
Expand All @@ -24,11 +27,13 @@ def export_to_java(model, package_name=None, class_name="Model", indent=4):
interpreter = interpreters.JavaInterpreter(
package_name=package_name,
class_name=class_name,
indent=indent)
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_python(model, indent=4):
def export_to_python(model, indent=4, function_name="score"):
"""
Generates a Python code representation of the given model.
Expand All @@ -38,16 +43,21 @@ def export_to_python(model, indent=4):
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.PythonInterpreter(indent=indent)
interpreter = interpreters.PythonInterpreter(
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_c(model, indent=4):
def export_to_c(model, indent=4, function_name="score"):
"""
Generates a C code representation of the given model.
Expand All @@ -57,16 +67,21 @@ def export_to_c(model, indent=4):
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.CInterpreter(indent=indent)
interpreter = interpreters.CInterpreter(
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_go(model, indent=4):
def export_to_go(model, indent=4, function_name="score"):
"""
Generates a Go code representation of the given model.
Expand All @@ -76,16 +91,21 @@ def export_to_go(model, indent=4):
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.GoInterpreter(indent=indent)
interpreter = interpreters.GoInterpreter(
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_javascript(model, indent=4):
def export_to_javascript(model, indent=4, function_name="score"):
"""
Generates a JavaScript code representation of the given model.
Expand All @@ -95,16 +115,22 @@ def export_to_javascript(model, indent=4):
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.JavascriptInterpreter(indent=indent)
interpreter = interpreters.JavascriptInterpreter(
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_visual_basic(model, module_name="Model", indent=4):
def export_to_visual_basic(model, module_name="Model", indent=4,
function_name="score"):
"""
Generates a Visual Basic (also can be treated as VBA
with some small manual changes, see a note below)
Expand Down Expand Up @@ -158,17 +184,23 @@ def export_to_visual_basic(model, module_name="Model", indent=4):
The name of the generated module.
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.VisualBasicInterpreter(module_name=module_name,
indent=indent)
interpreter = interpreters.VisualBasicInterpreter(
module_name=module_name,
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_c_sharp(model, namespace="ML", class_name="Model", indent=4):
def export_to_c_sharp(model, namespace="ML", class_name="Model", indent=4,
function_name="Score"):
"""
Generates a C# code representation of the given model.
Expand All @@ -182,6 +214,8 @@ def export_to_c_sharp(model, namespace="ML", class_name="Model", indent=4):
The name of the generated class.
indent : int, optional
The size of indents in the generated code.
function_name : string, optional
Name of the function in the generated code.
Returns
-------
Expand All @@ -190,11 +224,13 @@ def export_to_c_sharp(model, namespace="ML", class_name="Model", indent=4):
interpreter = interpreters.CSharpInterpreter(
namespace=namespace,
class_name=class_name,
indent=indent)
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_powershell(model, indent=4):
def export_to_powershell(model, indent=4, function_name="Score"):
"""
Generates a PowerShell code representation of the given model.
Expand All @@ -204,16 +240,21 @@ def export_to_powershell(model, indent=4):
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.PowershellInterpreter(indent=indent)
interpreter = interpreters.PowershellInterpreter(
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_r(model, indent=4):
def export_to_r(model, indent=4, function_name="score"):
"""
Generates a R code representation of the given model.
Expand All @@ -223,16 +264,21 @@ def export_to_r(model, indent=4):
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.RInterpreter(indent=indent)
interpreter = interpreters.RInterpreter(
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


def export_to_php(model, indent=4):
def export_to_php(model, indent=4, function_name="score"):
"""
Generates a PHP code representation of the given model.
Expand All @@ -242,12 +288,17 @@ def export_to_php(model, indent=4):
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.PhpInterpreter(indent=indent)
interpreter = interpreters.PhpInterpreter(
indent=indent,
function_name=function_name
)
return _export(model, interpreter)


Expand Down
6 changes: 4 additions & 2 deletions m2cgen/interpreters/c/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ class CInterpreter(ToCodeInterpreter,
power_function_name = "pow"
tanh_function_name = "tanh"

def __init__(self, indent=4, *args, **kwargs):
def __init__(self, indent=4, function_name="score", *args, **kwargs):
self.function_name = function_name

cg = CCodeGenerator(indent=indent)
super(CInterpreter, self).__init__(cg, *args, **kwargs)

Expand All @@ -37,7 +39,7 @@ def interpret(self, expr):
args += [(True, "output")]

with self._cg.function_definition(
name="score",
name=self.function_name,
args=args,
is_scalar_output=expr.output_size == 1):

Expand Down

0 comments on commit a4ef90f

Please sign in to comment.