symprog 0.1.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- symprog/__init__.py +7 -0
- symprog/__main__.py +0 -0
- symprog/base.py +30 -0
- symprog/config.py +243 -0
- symprog/lang/__init__.py +2 -0
- symprog/lang/language.py +489 -0
- symprog/lang/languages/__init__.py +2 -0
- symprog/lang/languages/constraint.py +685 -0
- symprog/lang/languages/plp.py +1487 -0
- symprog/lang/languages/utils.py +212 -0
- symprog/registry.py +124 -0
- symprog/solvers/__init__.py +2 -0
- symprog/solvers/factory.py +46 -0
- symprog/solvers/klay_interface.py +161 -0
- symprog/solvers/mapper.py +283 -0
- symprog/solvers/numerical.py +612 -0
- symprog/solvers/numerical_interfaces/__init__.py +4 -0
- symprog/solvers/numerical_interfaces/jax.py +186 -0
- symprog/solvers/numerical_interfaces/python.py +175 -0
- symprog/solvers/numerical_interfaces/pytorch.py +377 -0
- symprog/solvers/numerical_interfaces/symbolic.py +494 -0
- symprog/solvers/semiring.py +470 -0
- symprog/solvers/semirings/__init__.py +6 -0
- symprog/solvers/semirings/common.py +306 -0
- symprog/solvers/semirings/count.py +76 -0
- symprog/solvers/semirings/entropy.py +118 -0
- symprog/solvers/semirings/formula.py +41 -0
- symprog/solvers/semirings/fuzzy.py +63 -0
- symprog/solvers/semirings/prob.py +114 -0
- symprog/solvers/semirings/viterbi.py +167 -0
- symprog/solvers/solver.py +1231 -0
- symprog/solvers/solver_interfaces/__init__.py +6 -0
- symprog/solvers/solver_interfaces/bdd.py +327 -0
- symprog/solvers/solver_interfaces/pysat.py +263 -0
- symprog/solvers/solver_interfaces/sdd.py +484 -0
- symprog/solvers/solver_interfaces/sympy.py +139 -0
- symprog/solvers/solver_interfaces/utils.py +6 -0
- symprog/solvers/solver_interfaces/z3.py +180 -0
- symprog/solvers/solver_utils.py +193 -0
- symprog/solvers/tensorization.py +411 -0
- symprog/version.py +1 -0
- symprog-0.1.0a1.dist-info/METADATA +348 -0
- symprog-0.1.0a1.dist-info/RECORD +45 -0
- symprog-0.1.0a1.dist-info/WHEEL +4 -0
- symprog-0.1.0a1.dist-info/licenses/LICENSE +202 -0
symprog/__init__.py
ADDED
symprog/__main__.py
ADDED
|
File without changes
|
symprog/base.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from abc import ABC
|
|
3
|
+
from typing import TypeVar, Type
|
|
4
|
+
|
|
5
|
+
C = TypeVar("C", bound="UtilBase")
|
|
6
|
+
|
|
7
|
+
class UtilBase(ABC):
|
|
8
|
+
"""Library base class"""
|
|
9
|
+
|
|
10
|
+
@property
|
|
11
|
+
def logging(self) -> logging.RootLogger:
|
|
12
|
+
"""Returns an instance logger
|
|
13
|
+
|
|
14
|
+
:rtype: logging.RootLogger
|
|
15
|
+
"""
|
|
16
|
+
return logging.getLogger(f"{__name__}.{type(self).__name__}") # type: ignore
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def from_config(cls: Type[C], **kwargs) -> C:
|
|
20
|
+
"""Builds an instance from configuration
|
|
21
|
+
|
|
22
|
+
Arguments:
|
|
23
|
+
config: The incoming configuration.
|
|
24
|
+
"""
|
|
25
|
+
return cls(**kwargs)
|
|
26
|
+
|
|
27
|
+
class ExperimentalWarning(UserWarning):
|
|
28
|
+
"""Warning for experimental functionality (e.g., tensorization, the JAX backend),
|
|
29
|
+
whose behavior and API may change. Can be silenced with
|
|
30
|
+
`warnings.filterwarnings("ignore", category=symprog.ExperimentalWarning)`."""
|
symprog/config.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import inspect
|
|
4
|
+
from abc import abstractmethod
|
|
5
|
+
from typing import TypeVar, Optional, Any, Type
|
|
6
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
7
|
+
from dataclasses import dataclass, fields, asdict
|
|
8
|
+
from argparse import ArgumentParser, BooleanOptionalAction
|
|
9
|
+
|
|
10
|
+
C = TypeVar("C", bound="BaseConfig")
|
|
11
|
+
|
|
12
|
+
__all__ = ["UtilityConfig", "FastConfig"]
|
|
13
|
+
|
|
14
|
+
class BaseConfig:
|
|
15
|
+
"""Base class for configuration objects.
|
|
16
|
+
|
|
17
|
+
Configuration objects are used throughout for instantiating objects of all kinds.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def to_dict(self) -> dict[str, Any]:
|
|
21
|
+
"""Maps configuration to a dictionary."""
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
def dump(self, path: str) -> None:
|
|
25
|
+
"""Dumps the configuration to file
|
|
26
|
+
|
|
27
|
+
Arguments:
|
|
28
|
+
path: The path to dump configuration to.
|
|
29
|
+
"""
|
|
30
|
+
with open(path, "w") as config_out:
|
|
31
|
+
config_out.write(json.dumps(self.to_dict(), indent=4))
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def load_from_file(cls: Type[C], file_path: str) -> C:
|
|
35
|
+
"""Loads a configuration from file.
|
|
36
|
+
|
|
37
|
+
Arguments:
|
|
38
|
+
file_path: The path to the file containing configuration
|
|
39
|
+
values.
|
|
40
|
+
"""
|
|
41
|
+
with open(file_path) as config_in:
|
|
42
|
+
config_json = json.loads(config_in.read())
|
|
43
|
+
return cls(**config_json)
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def build_parser(cls, **kwargs) -> ArgumentParser:
|
|
47
|
+
"""Builds
|
|
48
|
+
|
|
49
|
+
Arguments:
|
|
50
|
+
argv: The command line input.
|
|
51
|
+
"""
|
|
52
|
+
parser = ArgumentParser()
|
|
53
|
+
return cls._populate_parser(parser, **kwargs)
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def _populate_parser(cls: Type[C], parser: ArgumentParser, **kwargs) -> ArgumentParser:
|
|
57
|
+
"""Adds parameters to an argument parser base on underlyinh configuration class.
|
|
58
|
+
|
|
59
|
+
Arguments:
|
|
60
|
+
parser: The input argument parser.
|
|
61
|
+
"""
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def parse_cli(cls: Type[C], argv: Optional[list[str]] = sys.argv) -> C:
|
|
66
|
+
"""Creates configuration instance from CLI arguments. Automatically
|
|
67
|
+
builds corresponding argparse parser.
|
|
68
|
+
|
|
69
|
+
Arguments:
|
|
70
|
+
argv: The raw CLI input.
|
|
71
|
+
"""
|
|
72
|
+
parser = cls.build_parser()
|
|
73
|
+
config = parser.parse_args(argv)
|
|
74
|
+
|
|
75
|
+
return cls(**vars(config))
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_kwargs(cls: Type[C], **kwargs: Any) -> C:
|
|
79
|
+
"""Build config from direct kwargs only.
|
|
80
|
+
|
|
81
|
+
Arguments:
|
|
82
|
+
kwargs: The input kwargs.
|
|
83
|
+
"""
|
|
84
|
+
return cls(**kwargs)
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def build_config(
|
|
88
|
+
cls: Type[C],
|
|
89
|
+
argv: Optional[list[str]] = None,
|
|
90
|
+
config_path: Optional[str] = None,
|
|
91
|
+
**kwargs,
|
|
92
|
+
) -> C:
|
|
93
|
+
"""Builds a configuration instance, either from CLI input
|
|
94
|
+
or direct arguments.
|
|
95
|
+
|
|
96
|
+
Arguments:
|
|
97
|
+
argv: (Optional) command line configuration values.
|
|
98
|
+
Returns:
|
|
99
|
+
A configuration object with the values based on either CLI
|
|
100
|
+
input, an external file or direct keyword args (in that order).
|
|
101
|
+
"""
|
|
102
|
+
if argv is not None:
|
|
103
|
+
return cls.parse_cli(argv)
|
|
104
|
+
elif config_path is not None:
|
|
105
|
+
return cls.load_from_file(config_path)
|
|
106
|
+
return cls.from_kwargs(**kwargs)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class UtilityConfig(BaseModel, BaseConfig):
|
|
110
|
+
"""A Pydantic configuration implementation.
|
|
111
|
+
|
|
112
|
+
This kind of verification is most useful for complex configurations
|
|
113
|
+
that might involve certain types of pydantic-style verification.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
model_config = ConfigDict(protected_namespaces=())
|
|
117
|
+
|
|
118
|
+
def to_dict(self) -> dict[str, Any]:
|
|
119
|
+
"""Maps configuration to a dictionary.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Returns a string indexed dictionary representation of the
|
|
123
|
+
configuration.
|
|
124
|
+
"""
|
|
125
|
+
return self.model_dump()
|
|
126
|
+
|
|
127
|
+
# @classmethod
|
|
128
|
+
# def _populate_parser(
|
|
129
|
+
# cls: Type[C], parser: ArgumentParser, **kwargs
|
|
130
|
+
# ) -> Type[ArgumentParser]:
|
|
131
|
+
# """Adds parameters to an argument parser base on underlyinh configuration class.
|
|
132
|
+
|
|
133
|
+
# Arguments:
|
|
134
|
+
# parser: The input argument parser.
|
|
135
|
+
# """
|
|
136
|
+
# for field_name, field in cls.model_fields.items(): # type: ignore
|
|
137
|
+
# type_annot = None
|
|
138
|
+
# if inspect.isclass(field.annotation):
|
|
139
|
+
# type_annot = field.annotation
|
|
140
|
+
|
|
141
|
+
# args = {
|
|
142
|
+
# "help": field.description,
|
|
143
|
+
# "type": type_annot,
|
|
144
|
+
# "required": field.is_required(),
|
|
145
|
+
# "default": field.default,
|
|
146
|
+
# }
|
|
147
|
+
|
|
148
|
+
# if isinstance(field.default, bool):
|
|
149
|
+
# args.pop("type", None)
|
|
150
|
+
# # args["type"] = bool
|
|
151
|
+
# args["required"] = False
|
|
152
|
+
# args["action"] = BooleanOptionalAction
|
|
153
|
+
|
|
154
|
+
# parser.add_argument(f"--{field_name}", **args)
|
|
155
|
+
|
|
156
|
+
# return parser
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class DataclassMeta(type):
|
|
160
|
+
"""Metaclass for avoiding needing to use dataclass decorator explicitly when building config"""
|
|
161
|
+
|
|
162
|
+
def __new__(mcs, name, bases, dct):
|
|
163
|
+
cls = super().__new__(mcs, name, bases, dct)
|
|
164
|
+
return dataclass(cls) # type: ignore
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class FastConfig(BaseConfig, metaclass=DataclassMeta):
|
|
168
|
+
"""An alternative configuration based instead on dataclasses. Should be used for
|
|
169
|
+
lower-level object configurations.
|
|
170
|
+
|
|
171
|
+
NOTE: Do not use a dataclass decorator with this class, it is applied automatically.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def _populate_parser(cls, parser: ArgumentParser, **kwargs) -> ArgumentParser:
|
|
176
|
+
"""Adds parameters to an argument parser base on underlying
|
|
177
|
+
configuration class.
|
|
178
|
+
|
|
179
|
+
Arguments:
|
|
180
|
+
parser: The input argument parser.
|
|
181
|
+
"""
|
|
182
|
+
for field in fields(cls): # type: ignore
|
|
183
|
+
|
|
184
|
+
type_annot = None
|
|
185
|
+
if inspect.isclass(field.type):
|
|
186
|
+
type_annot = field.type
|
|
187
|
+
|
|
188
|
+
args = {"type": type_annot, "default": field.default}
|
|
189
|
+
|
|
190
|
+
if isinstance(field.type, bool):
|
|
191
|
+
args["type"] = bool
|
|
192
|
+
args["required"] = None
|
|
193
|
+
args["action"] = BooleanOptionalAction
|
|
194
|
+
|
|
195
|
+
parser.add_argument(f"--{field.name}", **args)
|
|
196
|
+
|
|
197
|
+
return parser
|
|
198
|
+
|
|
199
|
+
def to_dict(self) -> dict[str, Any]:
|
|
200
|
+
"""Maps configuration to a dictionary.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Returns a string indexed dictionary representation of the
|
|
204
|
+
configuration.
|
|
205
|
+
"""
|
|
206
|
+
return asdict(self) # type: ignore
|
|
207
|
+
|
|
208
|
+
@classmethod
|
|
209
|
+
def from_dict(
|
|
210
|
+
cls: Type[C],
|
|
211
|
+
name: Optional[str] = "config",
|
|
212
|
+
data: Optional[dict[str, Any]] = None,
|
|
213
|
+
) -> Type[C]:
|
|
214
|
+
"""Create new FastConfig subclass from a dictionary.
|
|
215
|
+
|
|
216
|
+
Each key becomes a field; its type is inferred via `type(value)`,
|
|
217
|
+
and its default is `value`.
|
|
218
|
+
|
|
219
|
+
Arguments:
|
|
220
|
+
name: The name of the new configuration class.
|
|
221
|
+
data: Key/value pairs to turn into fields.
|
|
222
|
+
|
|
223
|
+
For example:
|
|
224
|
+
|
|
225
|
+
>>> from symprog import FastConfig
|
|
226
|
+
>>> config = FastConfig.from_dict("new", {"a" : 10})()
|
|
227
|
+
>>> config.a
|
|
228
|
+
10
|
|
229
|
+
"""
|
|
230
|
+
if data is None:
|
|
231
|
+
data = {}
|
|
232
|
+
annotations: dict[str, type] = {}
|
|
233
|
+
namespace: dict[str, Any] = {}
|
|
234
|
+
|
|
235
|
+
for key, value in data.items():
|
|
236
|
+
annotations[key] = type(value)
|
|
237
|
+
namespace[key] = value
|
|
238
|
+
|
|
239
|
+
namespace["__annotations__"] = annotations
|
|
240
|
+
|
|
241
|
+
meta = type(cls)
|
|
242
|
+
DynamicConfig = meta(name, (cls,), namespace)
|
|
243
|
+
return DynamicConfig
|
symprog/lang/__init__.py
ADDED