def test_invalid_constructor():
    """Test types for constructor."""
    with pytest.raises(ValueError):
        CategoricalBounds(categories="foo")

    with pytest.raises(ValueError):
        CategoricalBounds(categories={1, 2})
def test_no_data_objects():
    objs = [
        PropertyTemplate("pt", bounds=CategoricalBounds()),
        ConditionTemplate("ct", bounds=CategoricalBounds())
    ]
    templates, data_objects = split_templates_from_objects(objs)
    assert len(templates) == 2
    assert len(data_objects) == 0
Beispiel #3
0
def test_categories():
    """Test categories setter."""
    assert CategoricalBounds().categories == set()
    assert CategoricalBounds(categories={"foo", "bar"}).categories == {
        "foo", "bar"
    }
    assert CategoricalBounds(categories=["foo", "bar"]).categories == {
        "foo", "bar"
    }
Beispiel #4
0
def test_contains():
    """Test basic contains logic."""
    bounds = CategoricalBounds(categories={"spam", "eggs"})
    assert bounds.contains(CategoricalBounds(categories={"spam"}))
    assert not bounds.contains(CategoricalBounds(categories={"spam", "foo"}))
    assert not bounds.contains(RealBounds(0.0, 2.0, ''))
    assert not bounds.contains(None)
    with pytest.raises(TypeError):
        bounds.contains({"spam", "eggs"})
def test_both_present():
    objs = [
        ProcessSpec("ps"),
        PropertyTemplate("pt", bounds=CategoricalBounds()),
        MeasurementSpec("ms"),
        ConditionTemplate("ct", bounds=CategoricalBounds())
    ]
    templates, data_objects = split_templates_from_objects(objs)
    assert len(templates) == 2
    assert len(data_objects) == 2
def test_numpy():
    """Test that ndarrays, Series work as well."""
    assert len(array_like()) < 5  # In case we extend at some point

    if len(array_like()) > 2:  # Test numpy
        import numpy as np
        np_bounds = CategoricalBounds(np.array(["spam", "eggs"], dtype=object))
        np_copy = loads(dumps(np_bounds))
        assert np_copy == np_bounds

    if len(array_like()) > 3:  # Test numpy
        import pandas as pd
        pd_bounds = CategoricalBounds(pd.Series(["spam", "eggs"]))
        pd_copy = loads(dumps(pd_bounds))
        assert pd_copy == pd_bounds
def test_object_template_serde():
    """Test serde of an object template."""
    length_template = PropertyTemplate("Length", bounds=RealBounds(2.0, 3.5, 'cm'))
    sub_bounds = RealBounds(2.5, 3.0, 'cm')
    color_template = PropertyTemplate("Color", bounds=CategoricalBounds(["red", "green", "blue"]))
    # Properties are a mixture of property templates and [template, bounds], pairs
    block_template = MaterialTemplate("Block", properties=[[length_template, sub_bounds],
                                                           color_template])
    copy_template = MaterialTemplate.build(block_template.dump())
    assert copy_template == block_template

    # Tests below exercise similar code, but for measurement and process templates
    pressure_template = ConditionTemplate("pressure", bounds=RealBounds(0.1, 0.11, 'MPa'))
    index_template = ParameterTemplate("index", bounds=IntegerBounds(2, 10))
    meas_template = MeasurementTemplate("A measurement of length", properties=[length_template],
                                        conditions=[pressure_template], description="Description",
                                        parameters=[index_template], tags=["foo"])
    assert MeasurementTemplate.build(meas_template.dump()) == meas_template

    proc_template = ProcessTemplate("Make an object", parameters=[index_template],
                                    conditions=[pressure_template], allowed_labels=["Label"],
                                    allowed_names=["first sample", "second sample"])
    assert ProcessTemplate.build(proc_template.dump()) == proc_template

    # Check that serde still works if the template is a LinkByUID
    pressure_template.uids['id'] = '12345'  # uids['id'] not populated by default
    proc_template.conditions[0][0] = LinkByUID('id', pressure_template.uids['id'])
    assert ProcessTemplate.build(proc_template.dump()) == proc_template
def test_object_template_validation():
    """Test that attribute templates are validated against given bounds."""
    length_template = PropertyTemplate("Length", bounds=RealBounds(2.0, 3.5, 'cm'))
    dial_template = ConditionTemplate("dial", bounds=IntegerBounds(0, 5))
    color_template = ParameterTemplate("Color", bounds=CategoricalBounds(["red", "green", "blue"]))

    with pytest.raises(TypeError):
        MaterialTemplate()

    with pytest.raises(ValueError):
        MaterialTemplate("Block", properties=[[length_template, RealBounds(3.0, 4.0, 'cm')]])

    with pytest.raises(ValueError):
        ProcessTemplate("a process", conditions=[[color_template, CategoricalBounds(["zz"])]])
        
    with pytest.raises(ValueError):
        MeasurementTemplate("A measurement", parameters=[[dial_template, IntegerBounds(-3, -1)]])
Beispiel #9
0
def test_flatten_bounds():
    """Test that flatten works when the objects contain other objects."""
    bounds = CategoricalBounds(categories=["foo", "bar"])
    template = ProcessTemplate(
        "spam",
        conditions=[(ConditionTemplate(name="eggs", bounds=bounds), bounds)]
    )
    spec = ProcessSpec(name="spec", template=template)

    flat = flatten(spec, 'test-scope')
    assert len(flat) == 2, "Expected 2 flattened objects"
Beispiel #10
0
def test_repeated_objects():
    """Test that objects aren't double counted."""
    ct = ConditionTemplate(name="color",
                           bounds=CategoricalBounds(categories=["black", "white"]))
    pt = ProcessTemplate(name="painting", conditions=[ct])
    ps = ProcessSpec(name='painting',
                     template=pt,
                     conditions=Condition(name='Paint color',
                                          value=NominalCategorical("black"),
                                          template=ct
                                          )
                     )
    assert len(recursive_flatmap(ps, lambda x: [x])) == 3
def test_flatten():
    """Test that gemd utility methods can be applied to citrine-python objects.
    Citrine-python resources extend the gemd data model, so the gemd operations
    should work on them.
    """

    bounds = CategoricalBounds(categories=["foo", "bar"])
    template = ProcessTemplate(
        "spam",
        conditions=[(ConditionTemplate(name="eggs", bounds=bounds), bounds)]
    )
    spec = ProcessSpec(name="spec", template=template)

    flat = flatten(spec, scope='testing')
    assert len(flat) == 3, "Expected 3 flattened objects"
Beispiel #12
0
def test_fields_from_property():
    """Test that several fields of the attribute are derived from the property."""
    prop_template = PropertyTemplate(name="cookie eating template", bounds=IntegerBounds(0, 1000))
    cond_template = ConditionTemplate(name="Hunger template",
                                      bounds=CategoricalBounds(["hungry", "full", "peckish"]))
    prop = Property(name="number of cookies eaten",
                    template=prop_template,
                    origin='measured',
                    value=NominalInteger(27))
    cond = Condition(name="hunger level",
                     template=cond_template,
                     origin='specified',
                     value=NominalCategorical("hungry"))

    prop_and_conds = PropertyAndConditions(property=prop, conditions=[cond])
    assert prop_and_conds.name == prop.name
    assert prop_and_conds.template == prop.template
    assert prop_and_conds.origin == prop.origin
    assert prop_and_conds.value == prop.value
Beispiel #13
0
def make_templates(template_scope=DEMO_TEMPLATE_SCOPE):
    """Build all templates needed for the table."""
    tmpl = dict()

    # Attribute Templates
    attribute_feed = {
        "Formula": [PropertyTemplate,
                    CompositionBounds(components=EmpiricalFormula.all_elements())],
        "Crystallinity": [ConditionTemplate,
                          CategoricalBounds(
                              ['Amorphous', 'Polycrystalline', 'Single crystalline']
                          )],
        "Color": [PropertyTemplate,
                  CategoricalBounds(
                      ['Amber', 'Black', 'Blue', 'Bluish', 'Bronze', 'Brown', 'Brown-Black',
                       'Copper-Red', 'Dark Brown', 'Dark Gray', 'Dark Green', 'Dark Red', 'Gray',
                       'Light Gray', 'Ocher', 'Orange', 'Orange-Red', 'Pale Yellow', 'Red',
                       'Red-Yellow', 'Violet', 'White', 'Yellow', 'Yellow-Orange', 'Yellow-White']
                  )],
        "Band gap": [PropertyTemplate,
                     RealBounds(lower_bound=0.001, upper_bound=100, default_units='eV')],
        "Temperature": [ConditionTemplate,
                        RealBounds(lower_bound=1, upper_bound=1000, default_units='K')],
        "Temperature derivative of band gap": [PropertyTemplate,
                                               RealBounds(lower_bound=-0.01, upper_bound=0.01,
                                                          default_units='eV/K')],
        "Lasing": [PropertyTemplate,
                   CategoricalBounds(['True', 'False'])],
        "Cathodoluminescence": [PropertyTemplate,
                                CategoricalBounds(['True', 'False'])],
        "Mechanical luminescence": [PropertyTemplate,
                                    CategoricalBounds(['True', 'False'])],
        "Photoluminescence": [PropertyTemplate,
                              CategoricalBounds(['True', 'False'])],
        "Electroluminescence": [PropertyTemplate,
                                CategoricalBounds(['True', 'False'])],
        "Thermoluminescence": [PropertyTemplate,
                               CategoricalBounds(['True', 'False'])],
        "Morphology": [ConditionTemplate,
                       CategoricalBounds(['Thin film', 'Bulk'])],
        "Electric field polarization": [ConditionTemplate,
                                        CategoricalBounds(['Parallel to A axis',
                                                           'Parallel to B axis',
                                                           'Parallel to C axis',
                                                           'Perpendicular to B axis',
                                                           'Perpendicular to C axis'])],
        "Phase": [ConditionTemplate,
                  CategoricalBounds(['A', 'B', 'B1', 'B2', 'Fused quartz', 'Natural diamond',
                                     'Rutile', 'Sapphire', 'Synthetic quartz'])],
        "Crystal system": [ConditionTemplate,
                           CategoricalBounds(['Cubic', 'Hexagonal', 'Orthorhombic', 'Tetragonal',
                                              'Trigonal'])],
        "Transition": [ConditionTemplate,
                       CategoricalBounds(['Direct', 'Excitonic', 'Indirect'])],
        "Bands": [ConditionTemplate,
                  CategoricalBounds(['G1 to X1', 'G15 to G1', 'G15 to X1', 'G25 to G1',
                                     'G25 to G12', 'G25 to G15', 'G6 to G8', 'G8 to G6+',
                                     'L6+ to L6-'])]
    }
    for (name, (typ, bounds)) in attribute_feed.items():
        assert name not in tmpl
        tmpl[name] = typ(name=name,
                         bounds=bounds,
                         uids={template_scope: name},
                         tags=['citrine::demo::template::attribute']
                         )

    # Object Templates
    object_feed = {
        "Sample preparation": [
            ProcessTemplate,
            dict()
        ],
        "Chemical": [
            MaterialTemplate,
            {"properties": [tmpl["Formula"]]}
        ],
        "Band gap measurement": [
            MeasurementTemplate,
            {"properties": [tmpl["Band gap"],
                            tmpl["Temperature derivative of band gap"],
                            tmpl["Color"],
                            tmpl["Lasing"],
                            tmpl["Cathodoluminescence"],
                            tmpl["Mechanical luminescence"],
                            tmpl["Photoluminescence"],
                            tmpl["Electroluminescence"],
                            tmpl["Thermoluminescence"]
                            ],
             "conditions": [tmpl["Temperature"],
                            tmpl["Crystallinity"],
                            tmpl["Morphology"],
                            tmpl["Electric field polarization"],
                            tmpl["Phase"],
                            tmpl["Crystal system"],
                            tmpl["Transition"],
                            tmpl["Bands"]
                            ]
             }
        ],
    }
    for (name, (typ, kw_args)) in object_feed.items():
        assert name not in tmpl
        tmpl[name] = typ(name=name,
                         uids={template_scope: name},
                         tags=['citrine::demo::template::object'],
                         **kw_args)

    return tmpl
Beispiel #14
0
def test_property_template():
    """Test creation and serde of condition templates."""
    bounds = CategoricalBounds(['solid', 'liquid', 'gas'])
    template = PropertyTemplate("State", bounds=bounds, uids={'my_id': '0'})
    assert PropertyTemplate.build(template.dump()) == template
from gemd.entity.bounds.real_bounds import RealBounds
from gemd.entity.value.uniform_real import UniformReal
from gemd.entity.template.attribute_template import AttributeTemplate
from gemd.entity.template.property_template import PropertyTemplate
from gemd.entity.template.condition_template import ConditionTemplate
from gemd.entity.template.parameter_template import ParameterTemplate
from gemd.json import dumps, loads


class SampleAttributeTemplate(AttributeTemplate):
    """A class to flex the base attribute template."""

    typ = "sample_attribute_template"


cat_bounds = CategoricalBounds(categories={"a", "b", "c"})


def test_name_is_a_string():
    """Test that name is a string."""
    with pytest.raises(ValueError) as error:
        SampleAttributeTemplate(name=42, bounds=cat_bounds)

    assert "must be a string" in str(error.value)


def test_invalid_bounds():
    """Test that invalid bounds throw the appropriate error."""
    with pytest.raises(ValueError):
        SampleAttributeTemplate(name="name")  # Must have a bounds
    with pytest.raises(TypeError):
Beispiel #16
0
        name="kinematic viscosity",
        bounds=RealBounds(lower_bound=0.0, upper_bound=10.0**40, default_units="m^2 / s")
    )
}

known_conditions = {
    "temperature": ConditionTemplate(
        name="temperature",
        bounds=RealBounds(lower_bound=0.0, upper_bound=1000.0, default_units='K')
    )
}

known_parameters = {
    "knob_2_setting": ParameterTemplate(
        name="knob_2_setting",
        bounds=CategoricalBounds(categories={"low", "medium", "high"})
    )
}


def _parse_value(val):
    """Example field-parsing logic."""
    # If the string is complicated, split it up and try to get uncertainty and/or units
    if isinstance(val, str) and len(val.split()) > 1:
        toks = val.split()
        mean = float(toks[0])
        std = -1
        if toks[1] in {"+-", "+/-"}:
            std = float(toks[2])

        try:
def test_contains():
    """Test basic contains logic."""
    bounds = CategoricalBounds(categories={"spam", "eggs"})
    assert bounds.contains(CategoricalBounds(categories={"spam"}))
    assert not bounds.contains(CategoricalBounds(categories={"spam", "foo"}))
    assert not bounds.contains(RealBounds(0.0, 2.0, ''))
    assert not bounds.contains(None)
    with pytest.raises(TypeError):
        bounds.contains({"spam", "eggs"})

    from gemd.entity.value import NominalCategorical

    assert bounds.contains(NominalCategorical("spam"))
    assert not bounds.contains(NominalCategorical("foo"))
def test_json():
    """Test that serialization works (categories is encoded as a list)."""
    bounds = CategoricalBounds(categories={"spam", "eggs"})
    copy = loads(dumps(bounds))
    assert copy == bounds