Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config class #66

Closed
odashi opened this issue Oct 25, 2022 · 9 comments
Closed

Config class #66

odashi opened this issue Oct 25, 2022 · 9 comments
Assignees
Labels
Milestone

Comments

@odashi
Copy link
Collaborator

odashi commented Oct 25, 2022

Follows #65

It may be good if we provide a config class integrating every setting, which are currently passed directly to with_latex:

import latexify

config = latexify.Config.defaults()
config.use_math_symbols()
config.expand_function("expit")

@latexify.with_latex(config)
def f(x):
    return expit(x)

# Will generate: \mathrm{f}(x) \triangleq \frac{1}{1+\exp{(-x)}}
@odashi odashi added the feature label Oct 25, 2022
@odashi odashi added this to the v0.2 milestone Oct 25, 2022
@odashi
Copy link
Collaborator Author

odashi commented Oct 25, 2022

possible logic:

inner_config = copy(config if config is not None else Config.defaults())
for each kwarg:
    inner_config.update(kwarg)

inner_process(fn, inner_config)

@odashi odashi self-assigned this Oct 30, 2022
@odashi odashi removed this from the v0.2 milestone Oct 31, 2022
@odashi odashi added this to the v0.3 milestone Nov 1, 2022
@chunibyo-wly
Copy link
Contributor

working on this

@odashi
Copy link
Collaborator Author

odashi commented Nov 8, 2022

@chunibyo-wly
Thanks for working on this. Since this feature should be carefully designed, could you provide your idea about the implementation before throwing PRs?

@chunibyo-wly
Copy link
Contributor

hello, thanks for your reply, could you please give me some time to do some tests?
I will give you answer later

@odashi
Copy link
Collaborator Author

odashi commented Nov 10, 2022

@chunibyo-wly Sure. I targeted this feature on the release after the next (0.3) so we don't need to rush at this point.

@chunibyo-wly
Copy link
Contributor

  1. I think we could use bitwise and dynamic method creation to do this
from __future__ import annotations

from latexify.constants import CONFIGURATION
from typing import Callable

class Config:
    def __init__(self,
        identifiers: dict[str, str] | None = None,
        reduce_assignments: bool = False,
        use_math_symbols: bool = False,
        use_raw_function_name: bool = False,
        use_signature: bool = True,) -> None:

        self.flag: int = reduce_assignments * CONFIGURATION['reduce_assignments'] \
                    + use_math_symbols * CONFIGURATION["use_math_symbols"] \
                    + use_raw_function_name * CONFIGURATION["use_raw_function_name"] \
                    + use_signature * CONFIGURATION["use_signature"]
        self.identifiers: dict[str, str] = identifiers
        self.expanded_function: set[str] = set()

        for _config in CONFIGURATION:
            setattr(self, _config, self.__make_method_change_flag(_config))
            setattr(self, f"is_{_config}", self.__make_method_use_flag(_config))

    @staticmethod
    def defaults() -> Config:
        return Config()

    def expand_function(self, function: str | list[str]) -> None:
        if type(function) == str:
            self.expanded_function.add(function)
        elif type(function) == list:
            self.expand_function.update(function)

    def __make_method_change_flag(self, config_name: str) -> Callable:
        def _method(enabled: bool | None = True) -> None:
            if enabled:
                self.flag += CONFIGURATION[config_name]
            else:
                self.flag -= CONFIGURATION[config_name]
        return _method

    def __make_method_use_flag(self, config_name: str) -> Callable:
        @property
        def _method() -> bool:
            return (self.flag & CONFIGURATION[config_name]) != 0
        return _method

and constants is look like this:

CONFIGURATION = {
    "reduce_assignments": 0,
    "use_math_symbols": 2,
    "use_raw_function_name": 4,
    "use_signature": 8
}
  1. change frontend get_latex and reserved original parameter for user convenience
def get_latex(
    fn: Callable[..., Any],
    *,
    identifiers: dict[str, str] | None = None,
    reduce_assignments: bool = False,
    use_math_symbols: bool = False,
    use_raw_function_name: bool = False,
    use_signature: bool = True,
    configuration: Config | None = None,
) -> str:

@odashi
Copy link
Collaborator Author

odashi commented Nov 12, 2022

@chunibyo-wly Thanks! I think we don't need bitset here and it is enough to store individual members for all boolean configs. We basically don't need to care about the efficiency of this class because parsing and codegen are much (maybe a hundred of times) slower.

@odashi
Copy link
Collaborator Author

odashi commented Nov 12, 2022

I think we can simply define the config class as a dataclass with only nullable members, with the merge method that generates a new Config while maintaining the original config unchanged:

@dataclasses.dataclass
class Config(frozen=True):
    some_flag: SomeType | None
    ...

    def merge(self, *, config: Config | None = None, **kwargs) -> Config:
        def merge_field(name: str) -> Any:
            # Precedence: kwargs -> config -> self
            arg = kwargs.get(name)
            if arg is None and config is not None:
                arg = getattr(config, name)
            if arg is None:
                arg = getattr(self, name)
            return arg

        return Config(
            **{f.name: merge_field(f.name) for f in dataclasses.fields(self)}
        )

then define the defaults with non-null values:

_DEFAULT_CONFIG = Config(
    some_flag=some_value,
    ...
)

In get_latex, we can construct the final config as follows:

def get_latex(fn: Callable[..., Any], *, config: Config | None, **kwargs):
    merged_config = _DEFAULT_CONFIG.merge(config=config, **kwargs)

@chunibyo-wly
Copy link
Contributor

thanks for your advice, I will write code based on your suggestions later

@odashi odashi mentioned this issue Nov 14, 2022
@odashi odashi closed this as completed Nov 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants