Skip to content

Commit a12c30f

Browse files
authored
Merge pull request #28 from ModECI/development
To v0.3.0; not casting floats like 2.0 to int in serialization
2 parents 4385312 + 927e36d commit a12c30f

File tree

10 files changed

+103
-44
lines changed

10 files changed

+103
-44
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Continuous builds
22

33
on:
44
push:
5-
branches: [ main, development, experimental ]
5+
branches: [ main, development, experimental, test* ]
66
pull_request:
7-
branches: [ main, development, experimental ]
7+
branches: [ main, development, experimental, test* ]
88

99
jobs:
1010

examples/document.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ A model for documents.
1313
<tr>
1414
<td><b>title</b></td>
1515
<td>str</td>
16-
<td><i>Document title</i></td>
16+
<td><i>The document title</i></td>
1717
</tr>
1818

1919

examples/document.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Document(Base):
4040
4141
Args:
4242
id: The unique id of the document
43-
title: Document title
43+
title: The document title
4444
ISBN: International Standard Book Number
4545
sections: The sections of the document
4646
"""
@@ -65,24 +65,29 @@ class Document(Base):
6565

6666
print(doc)
6767
print(doc.sections[0].paragraphs[0].contents)
68-
print(doc.sections[0].paragraphs[0].__getattribute__("contents"))
68+
print(doc.sections[0].paragraphs[1].__getattribute__("contents"))
6969

7070
doc.to_json_file("document.json")
7171
doc.to_yaml_file("document.yaml")
7272
doc.to_bson_file("document.bson")
7373

74+
print(" >> Full document details in YAML format:\n")
75+
76+
print(doc.to_yaml())
77+
7478
doc_md = doc.generate_documentation(format="markdown")
7579

7680
with open("document.md", "w") as d:
7781
d.write(doc_md)
7882

83+
7984
doc_rst = doc.generate_documentation(format="rst")
8085

8186
with open("document.rst", "w") as d:
8287
d.write(doc_rst)
8388

8489

85-
print("\nGenerating specification in dict form")
90+
print("\n >> Generating specification in dict form...")
8691
doc_dict = doc.generate_documentation(format="dict")
8792

8893
import json
@@ -92,9 +97,12 @@ class Document(Base):
9297
with open("document.specification.json", "w") as d:
9398
d.write(json.dumps(doc_dict, indent=4))
9499

95-
print("Generating specification in YAML")
100+
print(" >> Generating specification in YAML...\n")
101+
96102
with open("document.specification.yaml", "w") as d:
97-
d.write(yaml.dump(doc_dict, indent=4, sort_keys=False))
103+
yy = yaml.dump(doc_dict, indent=4, sort_keys=False)
104+
print(yy)
105+
d.write(yy)
98106

99107
with open("document.specification.bson", "wb") as d:
100108
d.write(bson.encode(doc_dict))

examples/document.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ A model for documents.
99
Allowed field Data Type Description
1010
=============== =========== ==================================
1111
**id** str The unique id of the document
12-
**title** str Document title
12+
**title** str The document title
1313
**ISBN** int International Standard Book Number
1414
=============== =========== ==================================
1515

examples/document.specification.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"title": {
1010
"type": "str",
11-
"description": "Document title"
11+
"description": "The document title"
1212
},
1313
"ISBN": {
1414
"type": "int",

examples/document.specification.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Document:
66
description: The unique id of the document
77
title:
88
type: str
9-
description: Document title
9+
description: The document title
1010
ISBN:
1111
type: int
1212
description: International Standard Book Number

src/modelspec/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.2.7"
1+
__version__ = "0.3.0"
22

33
from .base_types import Base, define, has, field, fields, optional, instance_of, in_
44

src/modelspec/base_types.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,13 @@ def to_yaml(self, include_metadata: bool = True) -> str:
205205
yaml_converter.unstructure(self.to_dict()), sort_keys=False, indent=4
206206
)
207207

208-
def to_yaml_file(self, filename: str, include_metadata: bool = True) -> str:
208+
def to_yaml_file(
209+
self, filename: Optional[str] = None, include_metadata: Optional[bool] = True
210+
) -> str:
209211
"""Convert modelspec format to yaml format
210212
211213
Args:
212-
filename: File in modelspec format (Filename extension: .yaml )
214+
filename: File in modelspec format (Filename extension: .yaml). If None, use :code:`f"{self.id}.yaml"`
213215
include_metadata: Contains contact information, citations, acknowledgements, pointers to sample data,
214216
benchmark results, and environments in which the specified model was originally implemented
215217
Returns:
@@ -584,7 +586,7 @@ def _cls_generate_documentation(cls, format: str = MARKDOWN_FORMAT):
584586
allowed_fields = cls._parse_allowed_fields()
585587
allowed_children = cls._parse_allowed_children()
586588

587-
print(f" - {cls.__name__} ({definition})")
589+
# print(f" - {cls.__name__} ({definition})")
588590

589591
rst_url_format = "`%s <%s>`__"
590592

@@ -693,7 +695,7 @@ def insert_links(text, format=MARKDOWN_FORMAT):
693695
type_, can_be_eval_expr=True, can_be_dict=True
694696
)
695697
type_str = Base._type_to_str(type_)
696-
print(" Allowed parameter: {} {}".format(f, (description, type_str)))
698+
# print(" Allowed parameter: {} {}".format(f, (description, type_str)))
697699

698700
if format == DICT_FORMAT:
699701
doc_dict[name]["allowed_parameters"][f] = {}
@@ -750,7 +752,7 @@ def insert_links(text, format=MARKDOWN_FORMAT):
750752

751753
for c, (description, type_) in allowed_children.items():
752754
type_str = Base._type_to_str(type_)
753-
print(" Allowed child: {} {}".format(c, (description, type_str)))
755+
# print(" Allowed child: {} {}".format(c, (description, type_str)))
754756

755757
referencable = not Base._is_base_type(type_, can_be_dict=True)
756758

src/modelspec/utils.py

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@
99
from modelspec.base_types import print_
1010
from modelspec.base_types import EvaluableExpression
1111

12+
from random import Random
13+
from typing import Union
14+
1215
verbose = False
1316

1417

15-
def load_json(filename):
18+
def load_json(filename: str):
1619
"""
1720
Load a generic JSON file
21+
22+
Args:
23+
filename: The name of the JSON file to load
1824
"""
1925

2026
with open(filename) as f:
@@ -23,19 +29,25 @@ def load_json(filename):
2329
return data
2430

2531

26-
def load_yaml(filename):
32+
def load_yaml(filename: str):
2733
"""
2834
Load a generic YAML file
35+
36+
Args:
37+
filename: The name of the YAML file to load
2938
"""
3039
with open(filename) as f:
3140
data = yaml.load(f, Loader=yaml.SafeLoader)
3241

3342
return data
3443

3544

36-
def load_bson(filename):
45+
def load_bson(filename: str):
3746
"""
3847
Load a generic BSON file
48+
49+
Args:
50+
filename: The name of the BSON file to load
3951
"""
4052
with open(filename, "rb") as infile:
4153
data_encoded = infile.read()
@@ -211,11 +223,26 @@ def _params_info(parameters, multiline=False):
211223
FORMAT_TENSORFLOW = "tensorflow"
212224

213225

214-
def evaluate(expr, parameters={}, rng=None, array_format=FORMAT_NUMPY, verbose=False):
226+
def evaluate(
227+
expr: Union[int, float, str, list, dict],
228+
parameters: dict = {},
229+
rng: Random = None,
230+
array_format: str = FORMAT_NUMPY,
231+
verbose: bool = False,
232+
cast_to_int: bool = False,
233+
):
215234
"""
216235
Evaluate a general string like expression (e.g. "2 * weight") using a dict
217236
of parameters (e.g. {'weight':10}). Returns floats, ints, etc. if that's what's
218237
given in expr
238+
239+
Args:
240+
expr: The expression to convert
241+
parameters: A dict of the parameters which can be substituted in to the expression
242+
rng: The random number generator to use
243+
array_format: numpy or tensorflow
244+
verbose: Print the calculations
245+
cast_to_int: return an int for float/string values if castable
219246
"""
220247

221248
if array_format == FORMAT_TENSORFLOW:
@@ -233,7 +260,7 @@ def evaluate(expr, parameters={}, rng=None, array_format=FORMAT_NUMPY, verbose=F
233260
expr
234261
] # replace with the value in parameters & check whether it's float/int...
235262
if verbose:
236-
print_("Using for that param: %s" % _val_info(expr), verbose)
263+
print_(" Using for that param: %s" % _val_info(expr), verbose)
237264

238265
if type(expr) == str:
239266
try:
@@ -242,26 +269,28 @@ def evaluate(expr, parameters={}, rng=None, array_format=FORMAT_NUMPY, verbose=F
242269
else:
243270
expr = int(expr)
244271
except:
245-
pass
246-
try:
247-
if array_format == FORMAT_TENSORFLOW:
248-
expr = tf.constant(float(expr))
249-
else:
250-
expr = float(expr)
251-
except:
252-
pass
272+
273+
try:
274+
if array_format == FORMAT_TENSORFLOW:
275+
expr = tf.constant(float(expr))
276+
else:
277+
expr = float(expr)
278+
except:
279+
pass
253280

254281
if type(expr) == list:
255282
if verbose:
256-
print_("Returning a list in format: %s" % array_format, verbose)
283+
print_(" Returning a list in format: %s" % array_format, verbose)
257284
if array_format == FORMAT_TENSORFLOW:
258285
return tf.constant(expr, dtype=tf.float64)
259286
else:
260287
return np.array(expr)
261288

262289
if type(expr) == np.ndarray:
263290
if verbose:
264-
print_("Returning a numpy array in format: %s" % array_format, verbose)
291+
print_(
292+
" Returning a numpy array in format: %s" % array_format, verbose
293+
)
265294
if array_format == FORMAT_TENSORFLOW:
266295
return tf.convert_to_tensor(expr, dtype=tf.float64)
267296
else:
@@ -270,22 +299,22 @@ def evaluate(expr, parameters={}, rng=None, array_format=FORMAT_NUMPY, verbose=F
270299
if "Tensor" in type(expr).__name__:
271300
if verbose:
272301
print_(
273-
"Returning a tensorflow Tensor in format: %s" % array_format,
302+
" Returning a tensorflow Tensor in format: %s" % array_format,
274303
verbose,
275304
)
276305
if array_format == FORMAT_NUMPY:
277306
return expr.numpy()
278307
else:
279308
return expr
280309

281-
if int(expr) == expr:
310+
if int(expr) == expr and cast_to_int:
282311
if verbose:
283-
print_("Returning int: %s" % int(expr), verbose)
312+
print_(" Returning int: %s" % int(expr), verbose)
284313
return int(expr)
285314
else: # will have failed if not number
286315
if verbose:
287-
print_("Returning float: %s" % expr, verbose)
288-
return float(expr)
316+
print_(" Returning {}: {}".format(type(expr), expr), verbose)
317+
return expr
289318
except:
290319
try:
291320
if rng:
@@ -299,7 +328,7 @@ def evaluate(expr, parameters={}, rng=None, array_format=FORMAT_NUMPY, verbose=F
299328

300329
if verbose:
301330
print_(
302-
"Trying to eval [%s] with Python using %s..."
331+
" Trying to eval [%s] with Python using %s..."
303332
% (expr, parameters.keys()),
304333
verbose,
305334
)
@@ -308,13 +337,14 @@ def evaluate(expr, parameters={}, rng=None, array_format=FORMAT_NUMPY, verbose=F
308337

309338
if verbose:
310339
print_(
311-
"Evaluated with Python: {} = {}".format(expr, _val_info(v)), verbose
340+
" Evaluated with Python: {} = {}".format(expr, _val_info(v)),
341+
verbose,
312342
)
313343

314344
if (type(v) == float or type(v) == str) and int(v) == v:
315345

316346
if verbose:
317-
print_("Returning int: %s" % int(v), verbose)
347+
print_(" Returning int: %s" % int(v), verbose)
318348

319349
if array_format == FORMAT_TENSORFLOW:
320350
return tf.constant(int(v))
@@ -323,7 +353,7 @@ def evaluate(expr, parameters={}, rng=None, array_format=FORMAT_NUMPY, verbose=F
323353
return v
324354
except Exception as e:
325355
if verbose:
326-
print_(f"Returning without altering: {expr} (error: {e})", verbose)
356+
print_(f" Returning without altering: {expr} (error: {e})", verbose)
327357
return expr
328358

329359

tests/test_utils.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ def test_evaluate(self):
1919
params = {"p": 33}
2020
assert evaluate("p+p", params, verbose=True) == 66
2121

22+
print("======")
2223
assert type(evaluate("33")) == int
23-
assert type(evaluate("33.0")) == int
24+
assert type(evaluate("33", cast_to_int=True)) == int
25+
assert type(evaluate("33.0")) == float
26+
assert type(evaluate("33.0", cast_to_int=True)) == int
27+
2428
assert type(evaluate("33.1")) == float
25-
assert type(evaluate("33.1a")) == str
29+
assert type(evaluate("33.1a", verbose=True)) == str
2630

27-
assert type(evaluate("33.1a")) == str
31+
assert type(evaluate("a")) == str
2832

2933
import random
3034

@@ -41,6 +45,21 @@ def test_evaluate(self):
4145

4246
assert evaluate("a+b", params, verbose=True)[2] == 3
4347

48+
params = {"a1": np.array([1]), "b": np.array([1, 1, 3])}
49+
50+
a1_b = evaluate("a1+b", params, verbose=True)
51+
assert a1_b[2] == 4
52+
53+
params = {"A": np.ones([2, 2]), "B": np.ones([2, 2])}
54+
55+
AplusB = evaluate("A+B", params, verbose=True)
56+
assert AplusB[0, 0] == 2
57+
assert AplusB.shape == (2, 2)
58+
59+
AtimesB = evaluate("A*B", params, verbose=True)
60+
assert AtimesB[0, 0] == 1
61+
assert AtimesB.shape == (2, 2)
62+
4463
def test_val_info_tuple(self):
4564
print(_val_info((1, 2)))
4665
print(_val_info((("test", 1), 2)))

0 commit comments

Comments
 (0)