cdxcore 0.1.6__py3-none-any.whl → 0.1.10__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.

Potentially problematic release.


This version of cdxcore might be problematic. Click here for more details.

@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.4
2
+ Name: cdxcore
3
+ Version: 0.1.10
4
+ Summary: Basic Python Tools; upgraded cdxbasics
5
+ Author-email: Hans Buehler <github@buehler.london>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hansbuehler/cdxcore
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: numpy
14
+ Requires-Dist: pandas
15
+ Requires-Dist: matplotlib
16
+ Requires-Dist: sortedcontainers
17
+ Requires-Dist: psutil
18
+ Requires-Dist: jsonpickle
19
+ Requires-Dist: numba
20
+ Requires-Dist: joblib
21
+ Requires-Dist: blosc
22
+ Requires-Dist: mkdocstrings[python]>=0.18
23
+ Dynamic: license-file
24
+
25
+ # cdxcore
26
+ Under construction.
27
+
@@ -0,0 +1,35 @@
1
+ cdxcore/__init__.py,sha256=DzMRN-6SAKCjFPYC72fGXBql03834TWP2QnjtPSb_vc,127
2
+ cdxcore/config.py,sha256=YnIEJVFtMZ5EHlwzaB2JsSkCPhWPvEw9QSpe6mof_V4,98472
3
+ cdxcore/crman.py,sha256=jOw40Bh6PkmEiabA7OS9NsqF-9viam3CBiKzPwxVkVw,5693
4
+ cdxcore/err.py,sha256=SIJMBXKXYI_hiysv5iSPRy0w_BbxyTDPbNEs56Y94Rk,14541
5
+ cdxcore/jcpool.py,sha256=OzoXWBJKWaDgJm1OeUj9ERz9skvwslGOwNq0wbKtcFM,17222
6
+ cdxcore/pretty.py,sha256=iUpgUCwmI8yb5O-WZFJEk3QvNYcj_GIFHUgZ5lK8F2I,17082
7
+ cdxcore/pretty.py_bak.py,sha256=JgWr5044HzCNGG0wKSAWlWiPRs7-bNzkwiKH0T3n0to,28658
8
+ cdxcore/subdir.py,sha256=ceL-Od5NAlJX3f4mYgwki5EMzbs-LpG5M2cikC8eFIE,173868
9
+ cdxcore/uniquehash.py,sha256=g-D8pqPIppSdRq5QfdE5aP3paZ-NkXWHfnn-uNB7fmg,50648
10
+ cdxcore/util.py,sha256=0fp0EzeZvnje1Q7SUcgB_JtKpsYgGTfvlHVfq0mE_ug,31930
11
+ cdxcore/verbose.py,sha256=nKNoZQwl3eF1zBf-JZwPC-lL9d_o5mJsDsSUMixTMLw,29882
12
+ cdxcore/version.py,sha256=m30oI2Ortg44dKSim-sIoeh9PioD1FWsSfVEP5rubhk,27173
13
+ cdxcore-0.1.10.dist-info/licenses/LICENSE,sha256=M-cisgK9kb1bqVRJ7vrCxHcMQQfDxdY3c2YFJJWfNQg,1090
14
+ docs/source/conf.py,sha256=Owctibh5XcSpSNcrpOr3ROIDjoklmFVrMhu8cOSe50o,4180
15
+ tests/test_config.py,sha256=0U9vFIKDex0Il-7Vc_C4saAuXoHIsdQ8YhhS8AO7FQI,15950
16
+ tests/test_crman.py,sha256=jYDxqF__iq3fEjaZQoq66CNChWRoR79Ntyng5mr3sIA,1698
17
+ tests/test_err.py,sha256=VbVmbaB6o49G-n3t7yuJ4M0d9pyUQyJuVDqK-xRrLo8,3458
18
+ tests/test_pretty.py,sha256=5TmF7c1TRDSN-YR5yo04SiLJiW3bZaxpXHJ-4ZEO8hg,11952
19
+ tests/test_subdir.py,sha256=tO-zoOIKQtZEMpQM-tsrisyLRmMH8txCSOzh6jPRhYY,11721
20
+ tests/test_uniquehash.py,sha256=ldoQLT77R7odMAok4Yo3jmiUIH3VPHKoSiSLKbbM_mo,24907
21
+ tests/test_util.py,sha256=DZ6AlPFDNNlkqP5MlM1BUwmBJvEj4WCFqZcdh_Isflw,19955
22
+ tests/test_verbose.py,sha256=7JGCLKHU1HovO6UYSLLcJQxjaZxYJejS1fl8O3Sgk9w,5037
23
+ tests/test_version.py,sha256=eq7bNT0GefbUkwEzo658UUxgBCiR7CDhuIxAmnnI-qQ,4658
24
+ tmp/deferred.py,sha256=TirvzxYXWnZkOWuEMB7qsf6auilfy4VD_zPknYCjrnw,9401
25
+ tmp/dynaplot.py,sha256=kwrH_WccpJcfS7n_gzdAr4QxQobvIZZrrxgdsKLKfj0,48552
26
+ tmp/filelock.py,sha256=HqnHZhSCESaOA3ClrdWPW_GZpyo7c3VRSEofAV-khKM,18137
27
+ tmp/np.py,sha256=2MynhiaTfmx984Gz7TwfZH3t7GCmCAQiyeWzDDCL6_k,47686
28
+ tmp/npio.py,sha256=4Kwp5H4MgKHkOEhu4UJ5CcwpM7Pm8UFkaoL5FvOEFRI,10310
29
+ tmp/sharedarray.py,sha256=JuHuSlxA0evD0a-bEZgTFrfdlVPMgzfQNgfSjr1212w,11484
30
+ up/git_message.py,sha256=EfSH7Pit3ZoCiRqSMwRCUN_QyuwreU4LTIyGSutBlm4,123
31
+ up/pip_modify_setup.py,sha256=Esaml4yA9tFsqxLhk5bWSwvKCURONjQqfyChgFV2TSY,1584
32
+ cdxcore-0.1.10.dist-info/METADATA,sha256=CR4ZGaCL36hx08u3KnBdB5TWKM8Sknk2W_KsCptAv4g,754
33
+ cdxcore-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ cdxcore-0.1.10.dist-info/top_level.txt,sha256=phNSwCyJFe7UP2YMoi8o6ykhotatlIbJHjTp9EHM51k,26
35
+ cdxcore-0.1.10.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  cdxcore
2
- conda
2
+ docs
3
3
  tests
4
+ tmp
4
5
  up
docs/source/conf.py ADDED
@@ -0,0 +1,123 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # For the full list of built-in configuration values, see the documentation:
4
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
5
+
6
+ # -- Project information -----------------------------------------------------
7
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
+
9
+ # conf.py
10
+ def set_path(source = "cdxcore"):
11
+ import os, sys
12
+ root_path = os.path.split(
13
+ os.path.split(
14
+ os.path.split( __file__ )[0] # 'source
15
+ )[0] # 'docs'
16
+ )[0] # 'packag
17
+ assert root_path[-len(source):] == source, f"Conf.py '{__file__}': invalid source path '{root_path}'. Call 'make html' from the docs directory"
18
+ sys.path.insert(0, root_path) # so your package is importable
19
+
20
+ project = 'cdxcore'
21
+ copyright = '2025, Hans Buehler'
22
+ author = 'Hans Buehler'
23
+
24
+ set_path(project)
25
+
26
+ # -- General configuration ---------------------------------------------------
27
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
28
+
29
+ extensions = [
30
+ "sphinx.ext.autodoc",
31
+ "sphinx.ext.autosummary",
32
+ "sphinx.ext.intersphinx",
33
+ "sphinx.ext.viewcode",
34
+ #"sphinx_autodoc_typehints",
35
+ "numpydoc",
36
+ "sphinx_automodapi.automodapi",
37
+ "sphinx_copybutton",
38
+ "sphinx_design",
39
+ "myst_parser", # pip install myst-parser
40
+ ]
41
+ templates_path = ['_templates']
42
+ exclude_patterns = []
43
+
44
+ # -- Options for HTML output -------------------------------------------------
45
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
46
+
47
+ html_theme = "pydata_sphinx_theme"
48
+ html_theme_options = {
49
+ "navigation_depth": 4,
50
+ "show_prev_next": False,
51
+ "github_url": "https://github.com/hansbuehler/cdxcore", # optional
52
+ "show_toc_level": 2,
53
+ "secondary_sidebar_items": ["page-toc", "sourcelink"],
54
+ }
55
+ html_theme_options = {
56
+ }
57
+
58
+ html_static_path = ['_static']
59
+ html_css_files = ["custom.css"]
60
+
61
+ # Autodoc / autosummary: NumPy-like API pages
62
+ autosummary_generate = True
63
+ autodoc_default_options = {
64
+ "members": True,
65
+ "inherited-members": False,
66
+ "undoc-members": False,
67
+ "show-inheritance": False,
68
+ "special-members": "__call__"
69
+ }
70
+ autodoc_typehints = 'signature' # types shown in the doc body, like NumPy
71
+ #numpydoc_show_class_members = True
72
+
73
+ # numpydoc tweaks (keeps class doc at top, avoids member spam)
74
+ numpydoc_show_class_members = True
75
+ numpydoc_class_members_toctree = True
76
+ # Optional validation during build:
77
+ # numpydoc_validation_checks = {"all"} # or a subset like {"GL06","PR01",...}
78
+
79
+ # Cross-link to external projects (like NumPy, SciPy, pandas)
80
+ intersphinx_mapping = {
81
+ "python": ("https://docs.python.org/3", None),
82
+ "numpy": ("https://numpy.org/doc/stable/", None),
83
+ "scipy": ("https://docs.scipy.org/doc/scipy/", None),
84
+ "pandas": ("https://pandas.pydata.org/docs/", None),
85
+ }
86
+
87
+ myst_enable_extensions = [
88
+ "colon_fence", # allow ::: fenced blocks
89
+ "deflist", # definition lists
90
+ "dollarmath", # $math$ and $$math$$
91
+ "amsmath", # AMS math environments
92
+ "linkify", # auto-detect bare URLs
93
+ ]
94
+
95
+ source_suffix = {
96
+ ".rst": "restructuredtext",
97
+ ".md": "markdown",
98
+ }
99
+
100
+ from docutils import nodes
101
+ from sphinx.roles import XRefRole
102
+
103
+ def decorator_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
104
+ """Custom role to display decorators with @ prefix."""
105
+ # Create a pending_xref node like other Sphinx cross-references
106
+ node = nodes.literal(text, f"@{text}", classes=["xref", "dec"])
107
+ node["reftype"] = "func" # decorators are functions
108
+ node["reftarget"] = text
109
+ node["refdomain"] = "py" # Python domain
110
+ return [node], []
111
+
112
+ from sphinx.domains.python import PyXRefRole
113
+
114
+ def setup(app):
115
+ # Make :dec: behave exactly like :py:func:
116
+ app.add_role_to_domain("py", "dec", PyXRefRole("func"))
117
+ return {"parallel_read_safe": True}
118
+
119
+ #def setup(app):
120
+ # app.add_role("dec", decorator_role)
121
+ # return {"parallel_read_safe": True}
122
+
123
+
tests/test_config.py ADDED
@@ -0,0 +1,500 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Tue Apr 14 21:24:52 2020
4
+ @author: hansb
5
+ """
6
+
7
+ import unittest as unittest
8
+ import dataclasses as dataclasses
9
+ import sys as sys
10
+ import os as os
11
+ import pickle as pickle
12
+ import tempfile as tempfile
13
+ import shutil as shutil
14
+
15
+ def import_local():
16
+ """
17
+ In order to be able to run our tests manually from the 'tests' directory
18
+ we force import from the local package.
19
+ We also force reloading all modules to make sure we are not running old code.
20
+ """
21
+ me = "cdxcore"
22
+ import os
23
+ import sys
24
+ cwd = os.getcwd()
25
+ if cwd[-len(me):] == me:
26
+ return
27
+ assert cwd[-5:] == "tests",("Expected current working directory to be in a 'tests' directory", cwd[-5:], "from", cwd)
28
+ assert cwd[-6] in ['/', '\\'],("Expected current working directory 'tests' to be lead by a '\\' or '/'", cwd[-6:], "from", cwd)
29
+ sys.path.insert( 0, cwd[:-6] )
30
+
31
+ # reload modules
32
+ import importlib as imp
33
+ modules = sys.modules.copy()
34
+ for name, mdata in modules.items():
35
+ if name[:len(me)] == me:
36
+ imp.reload(mdata)
37
+ print("Reloaded", name)
38
+ #import_local()
39
+
40
+ from cdxcore.config import Config, Int, Float
41
+ from cdxcore.pretty import PrettyObject as pdct
42
+ from cdxcore.uniquehash import unique_hash16
43
+
44
+ class Test(unittest.TestCase):
45
+
46
+ def test_config(self):
47
+
48
+ config = Config(x=0., z=-1.)
49
+ x = config("x", 10., float, "test x")
50
+ self.assertEqual( x, 0. )
51
+ y = config("y", 10., float, "test y")
52
+ self.assertEqual( y, 10. )
53
+
54
+ with self.assertRaises(Exception):
55
+ # 'z' was not read
56
+ config.done()
57
+
58
+ # calling twice with different values
59
+ config = Config(x=0.)
60
+ x = config("x", 1., float, "test x")
61
+ x = config("x", 1., float, "test x") # ok: same parameters
62
+ with self.assertRaises(Exception):
63
+ x = config("x", 1., Float<0.5, "test x") # not ok: Float condition
64
+ with self.assertRaises(Exception):
65
+ x = config("x", 2., float, "test x") # not ok: different default
66
+ config.done()
67
+
68
+ # test usage per access method:
69
+ # __call__('a')
70
+ # get('a')
71
+ # get_default('a', ...)
72
+ # all register usage;
73
+ # get_raw('a')
74
+ # ['a']
75
+ # do not.
76
+ config = Config(a=1)
77
+ _ = config.get("a")
78
+ self.assertTrue( not 'a' in config.not_done )
79
+ config = Config(a=1)
80
+ _ = config.get_default("a", 0)
81
+ self.assertTrue( not 'a' in config.not_done )
82
+ config = Config(a=1)
83
+ _ = config("a")
84
+ self.assertTrue( not 'a' in config.not_done )
85
+ config = Config(a=1)
86
+ _ = config("a", 0)
87
+ self.assertTrue( not 'a' in config.not_done )
88
+
89
+ config = Config(a=1)
90
+ _ = config.get_raw('a')
91
+ self.assertTrue( 'a' in config.not_done )
92
+ config = Config(a=1)
93
+ _ = config['a']
94
+ self.assertTrue( 'a' in config.not_done )
95
+
96
+ # test sub configs
97
+ config = Config()
98
+ config.x = 1
99
+ config.a = "a"
100
+ config.sub.x = 2.
101
+
102
+ self.assertEqual(1., config("x", 0., float, "x"))
103
+ self.assertEqual("a", config("a", None, str, "a"))
104
+ self.assertEqual(2, config.sub("x", 0, int, "x"))
105
+ self.assertTrue( isinstance( config.sub, Config ) )
106
+ config.done()
107
+
108
+ # test detach
109
+ config = Config()
110
+ config.sub.x = 1
111
+ with self.assertRaises(Exception):
112
+ config.done() # 'sub.x' not read
113
+
114
+ config = Config()
115
+ config.sub.x = 1
116
+ sub = config.sub.detach()
117
+ config.done() # ok
118
+ _ = sub("x", 1)
119
+ config.done() # fine now
120
+
121
+ # test list (_Enum)
122
+ config = Config(t="a", q="q")
123
+ _ = config("t", "b", ['a', 'b', 'c'] )
124
+ self.assertEqual(_, 'a')
125
+ with self.assertRaises(Exception):
126
+ _ = config("q", "b", ['a', 'b', 'c'] ) # exception: 'q' not in set
127
+
128
+ # test tuple (_Alt)
129
+ config = Config(t="a")
130
+ _ = config("t", "b", (None, str) )
131
+ self.assertEqual(_, 'a')
132
+ config = Config(t=None)
133
+ _ = config("t", "b", (None, str) )
134
+ self.assertEqual(_, None)
135
+ with self.assertRaises(Exception):
136
+ config = Config(t="a")
137
+ _ = config("t", 1, (None, int) )
138
+ self.assertEqual(_, None)
139
+ config = Config()
140
+ _ = config("t", "b", (None, ['a','b']) )
141
+ self.assertEqual(_, 'b')
142
+ config = Config(t=2)
143
+ _ = config("t", 1, (Int>=1, Int<=1) )
144
+ self.assertEqual(_, 2)
145
+ config = Config()
146
+ _ = config("t", 3, (Int>=1, Int<=1) )
147
+ self.assertEqual(_, 3)
148
+ with self.assertRaises(Exception):
149
+ config = Config()
150
+ _ = config("t", 0, (Int>=1, Int<=-1) )
151
+ with self.assertRaises(Exception):
152
+ config = Config(t=0)
153
+ _ = config("t", 3, (Int>=1, Int<=-1) )
154
+
155
+ # combined conditons
156
+ config = Config(x=1., y=1.)
157
+
158
+ x = config("x", 1., ( Float>=0.) & (Float<=1.), "test x")
159
+ with self.assertRaises(Exception):
160
+ # test that violated condition is caught
161
+ y = config("y", 1., ( Float>=0.) & (Float<1.), "test y")
162
+
163
+ config = Config(x=1., y=1.)
164
+ with self.assertRaises(NotImplementedError):
165
+ # left hand must be > or >=
166
+ y = config("y", 1., ( Float<=0.) & (Float<1.), "test x")
167
+ config = Config(x=1., y=1.)
168
+ with self.assertRaises(NotImplementedError):
169
+ # right hand must be < or <=
170
+ y = config("y", 1., ( Float>=0.) & (Float>1.), "test x")
171
+
172
+ # test int
173
+ config = Config(x=1)
174
+ x = config("x", 0, ( Int>=0 ) & ( Int<=1), "int test")
175
+ config = Config(x=1)
176
+ with self.assertRaises(NotImplementedError):
177
+ # cannot mix types
178
+ x = config("x", 1., ( Float>=0.) & (Int<=1), "test x")
179
+
180
+ # test deleting children
181
+ config = Config()
182
+ config.a.x = 1
183
+ config.b.x = 2
184
+ config.c.x = 3
185
+ config.delete_children( 'a' )
186
+ l = sorted( config.children )
187
+ self.assertEqual(l, ['b', 'c'])
188
+ config = Config()
189
+ config.a.x = 1
190
+ config.b.x = 2
191
+ config.c.x = 3
192
+ config.delete_children( ['a','b'] )
193
+ l = sorted( config.children )
194
+ self.assertEqual(l, ['c'])
195
+
196
+ # test conversion to dictionaries
197
+ config = Config()
198
+ config.x = 1
199
+ config.y = 2
200
+ config.sub.x = 10
201
+ config.sub.y = 20
202
+ inp_dict = config.input_dict()
203
+
204
+ test = pdct()
205
+ test.x = 1
206
+ test.y = 2
207
+ test.sub = pdct()
208
+ test.sub.x = 10
209
+ test.sub.y = 20
210
+
211
+ self.assertEqual( test, inp_dict)
212
+
213
+ # clean_copy
214
+ config = Config()
215
+ config.gym.user_version = 1
216
+ config.gym.world_character_id = 2
217
+ config.gym.vol_model.type = "decoder"
218
+ _ = config.clean_copy()
219
+
220
+ """
221
+ test = PrettyDict()
222
+ test.x = config("x", 1)
223
+ test.y = config("y", 22)
224
+ test.z = config("z", 33)
225
+ test.sub = PrettyDict()
226
+ test.sub.x = config.sub("x", 10)
227
+ test.sub.y = config.sub("y", 222)
228
+ test.sub.z = config.sub("z", 333)
229
+ usd_dict = config.usage_dict()
230
+ self.assertEqual( usd_dict, test )
231
+ """
232
+
233
+ # test keys()
234
+
235
+ config = Config()
236
+ config.a = 1
237
+ config.x.b = 2
238
+ keys = list(config)
239
+ sorted(keys)
240
+ self.assertEqual( keys, ['a'])
241
+ keys = list(config.keys())
242
+ sorted(keys)
243
+ self.assertEqual( keys, ['a'])
244
+
245
+ # test update
246
+
247
+ config = Config()
248
+ config.a = 1
249
+ config.x.a = 1
250
+ config.z.a =1
251
+
252
+ config2 = Config()
253
+ config2.b = 2
254
+ config2.x.a = 2
255
+ config2.x.b = 2
256
+ config2.y.b = 2
257
+ config2.z = 2
258
+ config.update( config2 )
259
+ ur1 = config.input_report()
260
+
261
+ econfig = Config()
262
+ econfig.a = 1
263
+ econfig.b = 2
264
+ econfig.x.a = 2
265
+ econfig.x.b = 2
266
+ econfig.y.b = 2
267
+ econfig.z = 2
268
+ ur2 = econfig.input_report()
269
+ self.assertEqual( ur1, ur2 )
270
+
271
+ config = Config()
272
+ config.a = 1
273
+ config.x.a = 1
274
+
275
+ d = dict(b=2,x=dict(a=2,b=2),y=dict(b=2),z=2)
276
+ config.update(d)
277
+ ur2 = econfig.input_report()
278
+ self.assertEqual( ur1, ur2 )
279
+
280
+ # test str and repr
281
+
282
+ config = Config()
283
+ config.x = 1
284
+ config.y = 2
285
+ config.sub.x = 10
286
+ config.sub.y = 20
287
+
288
+ self.assertEqual( str(config), "config{'x': 1, 'y': 2, 'sub': {'x': 10, 'y': 20}}")
289
+ self.assertEqual( repr(config), "Config( **{'x': 1, 'y': 2, 'sub': {'x': 10, 'y': 20}}, config_name='config' )")
290
+
291
+ # test recorded usage
292
+
293
+ config = Config()
294
+ config.x = 1
295
+ config.sub.a = 1
296
+ config.det.o = 1
297
+
298
+ _ = config("x", 11)
299
+ _ = config("y", 22)
300
+ _ = config.sub("a", 11)
301
+ _ = config.sub("b", 22)
302
+ det = config.det.detach() # shares the same recorder !
303
+ _ = det("o", 11)
304
+ _ = det("p", 22)
305
+
306
+ self.assertEqual( config.get_recorded("x"), 1)
307
+ self.assertEqual( config.get_recorded("y"), 22)
308
+ self.assertEqual( config.sub.get_recorded("a"), 1)
309
+ self.assertEqual( config.sub.get_recorded("b"), 22)
310
+ self.assertEqual( config.det.get_recorded("o"), 1)
311
+ self.assertEqual( config.det.get_recorded("p"), 22)
312
+
313
+ # unique ID
314
+
315
+ config = Config()
316
+ # world
317
+ config.world.samples = 10000
318
+ config.world.steps = 20
319
+ config.world.black_scholes = True
320
+ config.world.rvol = 0.2 # 20% volatility
321
+ config.world.drift = 0. # real life drift
322
+ config.world.cost_s = 0.
323
+ # gym
324
+ config.gym.objective.utility = "cvar"
325
+ config.gym.objective.lmbda = 1.
326
+ config.gym.agent.network.depth = 6
327
+ config.gym.agent.network.width = 40
328
+ config.gym.agent.network.activation = "softplus"
329
+ # trainer
330
+ config.trainer.train.optimizer = "adam"
331
+ config.trainer.train.batch_size = None
332
+ config.trainer.train.epochs = 400
333
+ config.trainer.caching.epoch_freq = 10
334
+ config.trainer.caching.mode = "on"
335
+ config.trainer.visual.epoch_refresh = 1
336
+ config.trainer.visual.time_refresh = 10
337
+ config.trainer.visual.confidence_pcnt_lo = 0.25
338
+ config.trainer.visual.confidence_pcnt_hi = 0.75
339
+
340
+ id1 = config.unique_hash()
341
+
342
+ config = Config()
343
+ # world
344
+ config.world.samples = 10000
345
+ config.world.steps = 20
346
+ config.world.black_scholes = True
347
+ config.world.rvol = 0.2 # 20% volatility
348
+ config.world.drift = 0. # real life drift
349
+ config.world.cost_s = 0.
350
+ # gym
351
+ config.gym.objective.utility = "cvar"
352
+ config.gym.objective.lmbda = 1.
353
+ config.gym.agent.network.depth = 5 # <====== changed this
354
+ config.gym.agent.network.width = 40
355
+ config.gym.agent.network.activation = "softplus"
356
+ # trainer
357
+ config.trainer.train.optimizer = "adam"
358
+ config.trainer.train.batch_size = None
359
+ config.trainer.train.epochs = 400
360
+ config.trainer.caching.epoch_freq = 10
361
+ config.trainer.caching.mode = "on"
362
+ config.trainer.visual.epoch_refresh = 1
363
+ config.trainer.visual.time_refresh = 10
364
+ config.trainer.visual.confidence_pcnt_lo = 0.25
365
+ config.trainer.visual.confidence_pcnt_hi = 0.75
366
+
367
+ id2 = config.unique_hash()
368
+ self.assertNotEqual(id1,id2)
369
+ self.assertEqual(id2,"cfef59b69770d0a973342ad68f38fba2")
370
+
371
+ _ = config.nothing("get_nothing", 0) # this triggered a new ID in old versions
372
+
373
+ id3 = config.unique_hash()
374
+ self.assertEqual(id2,id3)
375
+
376
+ idempty = Config().unique_hash()
377
+ self.assertEqual(idempty,"64550d6ffe2c0a01a14aba1eade0200c")
378
+ self.assertNotEqual(idempty,id3)
379
+
380
+ # pickle test
381
+
382
+ binary = pickle.dumps(config)
383
+ restored = pickle.loads(binary)
384
+ idrest = restored.unique_hash()
385
+ self.assertEqual(idrest,id2)
386
+
387
+ # unique ID test
388
+
389
+ config1 = Config()
390
+ config1.x = 1
391
+ config1.sub.y = 2
392
+ config2 = Config()
393
+ config2.x = 1
394
+ config2.sub.y = 3
395
+ self.assertNotEqual( unique_hash16(config1), unique_hash16(config2) )
396
+
397
+ config1 = Config()
398
+ config1.x = 1
399
+ config1.sub.y = 2
400
+ config2 = Config()
401
+ config2.x = 2
402
+ config2.sub.y = 2
403
+ self.assertNotEqual( unique_hash16(config1), unique_hash16(config2) )
404
+
405
+ config1 = Config()
406
+ config1.x = 1
407
+ config1.sub.y = 2
408
+ config2 = Config()
409
+ config2.x = 1
410
+ config2.sub.y = 2
411
+ self.assertEqual( unique_hash16(config1), unique_hash16(config2) )
412
+
413
+ # unique_hash16() ignores protected and private members
414
+ config1 = Config()
415
+ config1.x = 1
416
+ config1.sub._y = 2
417
+ config2 = Config()
418
+ config2.x = 1
419
+ config2.sub._y = 3
420
+ self.assertEqual( unique_hash16(config1), unique_hash16(config2) )
421
+
422
+ def test_detach(self):
423
+ """ testing detach/copy/clean_cooy """
424
+
425
+ config = Config(a=1,b=2)
426
+ config.child.x = 1
427
+ _ = config("a", 2)
428
+ c1 = config.detach()
429
+ with self.assertRaises(Exception):
430
+ _ = c1("a", 1) # different default
431
+ _ = c1("b", 3)
432
+ with self.assertRaises(Exception):
433
+ _ = config("b", 2) # different default
434
+
435
+ config = Config(a=1,b=2)
436
+ config.child.x = 1
437
+ _ = config("a", 2)
438
+ c1 = config.copy()
439
+ with self.assertRaises(Exception):
440
+ _ = c1("a", 1) # different default
441
+ _ = c1("b", 3)
442
+ _ = config("b", 2) # different default - ok
443
+
444
+ config = Config(a=1,b=2)
445
+ config.child.x = 1
446
+ _ = config("a", 2)
447
+ c1 = config.clean_copy()
448
+ _ = c1("a", 1) # different default - ok
449
+ _ = c1("b", 3)
450
+ _ = config("b", 2) # different default - ok
451
+
452
+ def test_dataclass(self):
453
+
454
+ @dataclasses.dataclass
455
+ class A:
456
+ i : int = 0
457
+ config : Config = Config().as_field()
458
+
459
+ def f(self):
460
+ return self.config("a", 1, int, "Test")
461
+
462
+ a = A()
463
+ self.assertEqual(a.f(),1)
464
+ c = Config()
465
+ a = A(i=2,config=Config(c))
466
+ self.assertEqual(a.f(),1)
467
+ c = Config(a=2)
468
+ a = A(i=2,config=Config(c))
469
+ self.assertEqual(a.f(),2)
470
+ a = A(i=2,config=Config(a=2))
471
+ self.assertEqual(a.f(),2)
472
+
473
+ def test_io(self):
474
+
475
+ config = Config(x=1)
476
+ config.child.y = 2
477
+ config._test = 33
478
+
479
+ try:
480
+ tmp_dir = tempfile.mkdtemp()
481
+ self.assertNotEqual(tmp_dir[-1],"/")
482
+ self.assertNotEqual(tmp_dir[-1],"\\")
483
+ tmp_file = tmp_dir + "/test_pretty_object.pck"
484
+
485
+ with open(tmp_file, "wb") as f:
486
+ pickle.dump(config,f)
487
+
488
+ with open(tmp_file, "rb") as f:
489
+ config2 = pickle.load(f)
490
+ self.assertEqual(config,config2)
491
+
492
+ os.remove(tmp_file)
493
+ finally:
494
+ shutil.rmtree(tmp_dir)
495
+
496
+
497
+ if __name__ == '__main__':
498
+ unittest.main()
499
+
500
+