chemicalml 0.2.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +7 -1
- data/.rubocop.yml +23 -0
- data/CLAUDE.md +95 -40
- data/README.adoc +154 -26
- data/TODO.align/36-molecular-convention-coverage.md +46 -0
- data/TODO.align/37-compchem-convention-coverage.md +55 -0
- data/TODO.align/38-dictionary-convention-coverage.md +34 -0
- data/TODO.align/39-unit-dictionary-convention-coverage.md +33 -0
- data/TODO.align/40-unit-type-dictionary-convention-coverage.md +28 -0
- data/TODO.align/41-expand-canonical-model-layer.md +43 -0
- data/TODO.align/42-translator-wire-new-models.md +31 -0
- data/TODO.align/43-dictionary-model-no-to-h.md +39 -0
- data/TODO.align/44-schema3-only-and-schema24-module.md +44 -0
- data/TODO.align/45-documentation-and-final-verification.md +23 -0
- data/TODO.align/46-lattice-vector-content.md +24 -0
- data/TODO.align/47-molecule-crystal-spectra-property-lists.md +23 -0
- data/TODO.align/48-model-list-and-module-lists.md +21 -0
- data/TODO.align/49-more-chemistry-models.md +34 -0
- data/TODO.align/50-validation-report.md +29 -0
- data/TODO.align/51-convention-auto-detection.md +30 -0
- data/TODO.align/52-schema24-legacy-elements.md +28 -0
- data/TODO.align/53-round-trip-integration-specs.md +30 -0
- data/TODO.align/54-document-versioned-parser-shim.md +38 -0
- data/TODO.align/55-molecule-missing-attributes.md +20 -0
- data/TODO.align/56-geometry-models.md +24 -0
- data/TODO.align/57-compchem-models.md +26 -0
- data/TODO.align/58-remaining-models.md +24 -0
- data/TODO.align/59-wire-new-models-translator.md +18 -0
- data/TODO.align/60-docs-and-final-verification.md +19 -0
- data/TODO.align/61-parallel-array-attrs.md +54 -0
- data/TODO.align/62-per-element-attribute-gaps.md +49 -0
- data/TODO.align/63-universal-children-mixin.md +42 -0
- data/TODO.align/64-per-element-child-gaps.md +81 -0
- data/TODO.align/65-simple-unit-convention.md +40 -0
- data/TODO.align/66-schema24-legacy-children.md +32 -0
- data/TODO.align/67-final-verification.md +32 -0
- data/TODO.align/68-detection-coverage.md +33 -0
- data/TODO.align/69-json-yaml-roundtrip.md +27 -0
- data/TODO.align/70-iterative-walker.md +40 -0
- data/TODO.align/71-violation-value.md +25 -0
- data/TODO.align/72-close-child-gaps.md +66 -0
- data/TODO.align/73-schema24-parse-limitation.md +64 -0
- data/TODO.align/74-final-verification-round18.md +15 -0
- data/TODO.align/75-key-value-mappings.md +43 -0
- data/TODO.align/76-schema24-workaround.md +57 -0
- data/TODO.align/77-convention-coverage-matrix.md +31 -0
- data/TODO.align/78-fixture-audit.md +23 -0
- data/TODO.align/79-final-verification-round19.md +15 -0
- data/TODO.align/80-xsd-enum-constants.md +35 -0
- data/TODO.align/81-enum-validation-constraints.md +29 -0
- data/TODO.align/82-reference-resolver.md +33 -0
- data/TODO.align/83-toml-roundtrip.md +20 -0
- data/TODO.align/84-final-verification-round21.md +16 -0
- data/TODO.align/85-bond-distinct-atoms.md +25 -0
- data/TODO.align/86-references-should-resolve.md +20 -0
- data/TODO.align/87-cli-utility.md +26 -0
- data/TODO.align/88-diverse-fixtures.md +26 -0
- data/TODO.align/89-final-verification-round22.md +15 -0
- data/TODO.align/90-bond-stereo-completeness.md +31 -0
- data/TODO.align/91-atomparity-distinct.md +21 -0
- data/TODO.align/92-unit-dictionary-completeness.md +28 -0
- data/TODO.align/93-yard-docs.md +23 -0
- data/TODO.align/94-final-verification-round23.md +15 -0
- data/TODO.align/README.round17.md +28 -0
- data/TODO.align/README.round18.md +37 -0
- data/TODO.align/README.round19.md +40 -0
- data/TODO.align/README.round20.md +49 -0
- data/TODO.align/README.round21.md +35 -0
- data/TODO.align/README.round22.md +48 -0
- data/TODO.align/README.round23.md +46 -0
- data/TODO.align/README.round24.md +53 -0
- data/TODO.align/README.round25.md +66 -0
- data/TODO.align/README.round26.md +55 -0
- data/TODO.align/README.round27.md +51 -0
- data/TODO.align/README.round28.md +61 -0
- data/TODO.align/README.round29.md +64 -0
- data/TODO.align/README.round30.md +50 -0
- data/TODO.align/README.round31.md +54 -0
- data/TODO.align/README.round32.md +67 -0
- data/TODO.align/README.round33.md +62 -0
- data/TODO.align/README.round34.md +70 -0
- data/docs/constraints.md +161 -0
- data/docs/generate_constraint_docs.rb +44 -0
- data/exe/chemicalml +6 -0
- data/lib/chemicalml/cli/base.rb +68 -0
- data/lib/chemicalml/cli/command.rb +43 -0
- data/lib/chemicalml/cli/constraints_command.rb +27 -0
- data/lib/chemicalml/cli/conventions_command.rb +14 -0
- data/lib/chemicalml/cli/dictionaries_command.rb +14 -0
- data/lib/chemicalml/cli/elements_command.rb +17 -0
- data/lib/chemicalml/cli/enums_command.rb +17 -0
- data/lib/chemicalml/cli/info_command.rb +77 -0
- data/lib/chemicalml/cli/inspect_command.rb +39 -0
- data/lib/chemicalml/cli/validate_command.rb +54 -0
- data/lib/chemicalml/cli.rb +43 -0
- data/lib/chemicalml/cml/base/abundance.rb +21 -5
- data/lib/chemicalml/cml/base/action.rb +44 -6
- data/lib/chemicalml/cml/base/action_list.rb +44 -8
- data/lib/chemicalml/cml/base/alternative.rb +37 -0
- data/lib/chemicalml/cml/base/amount.rb +15 -5
- data/lib/chemicalml/cml/base/angle.rb +34 -7
- data/lib/chemicalml/cml/base/annotation.rb +38 -0
- data/lib/chemicalml/cml/base/any_cml.rb +12 -5
- data/lib/chemicalml/cml/base/appinfo.rb +40 -0
- data/lib/chemicalml/cml/base/arg.rb +80 -0
- data/lib/chemicalml/cml/base/array.rb +51 -8
- data/lib/chemicalml/cml/base/array_list.rb +15 -5
- data/lib/chemicalml/cml/base/atom.rb +93 -19
- data/lib/chemicalml/cml/base/atom_array.rb +77 -2
- data/lib/chemicalml/cml/base/atom_parity.rb +18 -2
- data/lib/chemicalml/cml/base/atom_set.rb +15 -5
- data/lib/chemicalml/cml/base/atom_type.rb +27 -7
- data/lib/chemicalml/cml/base/atom_type_list.rb +23 -7
- data/lib/chemicalml/cml/base/atomic_basis_function.rb +35 -11
- data/lib/chemicalml/cml/base/band.rb +22 -7
- data/lib/chemicalml/cml/base/band_list.rb +15 -7
- data/lib/chemicalml/cml/base/basis_set.rb +26 -7
- data/lib/chemicalml/cml/base/bond.rb +39 -7
- data/lib/chemicalml/cml/base/bond_array.rb +41 -2
- data/lib/chemicalml/cml/base/bond_set.rb +15 -5
- data/lib/chemicalml/cml/base/bond_stereo.rb +25 -4
- data/lib/chemicalml/cml/base/bond_type.rb +24 -7
- data/lib/chemicalml/cml/base/bond_type_list.rb +23 -7
- data/lib/chemicalml/cml/base/cell_parameter.rb +30 -8
- data/lib/chemicalml/cml/base/cml_module.rb +36 -11
- data/lib/chemicalml/cml/base/common_children.rb +35 -0
- data/lib/chemicalml/cml/base/complex_object.rb +37 -0
- data/lib/chemicalml/cml/base/condition_list.rb +28 -8
- data/lib/chemicalml/cml/base/crystal.rb +27 -9
- data/lib/chemicalml/cml/base/definition.rb +12 -5
- data/lib/chemicalml/cml/base/description.rb +15 -5
- data/lib/chemicalml/cml/base/dictionary.rb +30 -7
- data/lib/chemicalml/cml/base/dictionary_entry.rb +67 -8
- data/lib/chemicalml/cml/base/dimension.rb +31 -7
- data/lib/chemicalml/cml/base/document.rb +18 -8
- data/lib/chemicalml/cml/base/documentation.rb +12 -5
- data/lib/chemicalml/cml/base/eigen.rb +33 -7
- data/lib/chemicalml/cml/base/electron.rb +29 -9
- data/lib/chemicalml/cml/base/enumeration.rb +41 -0
- data/lib/chemicalml/cml/base/expression.rb +47 -0
- data/lib/chemicalml/cml/base/float.rb +52 -0
- data/lib/chemicalml/cml/base/float_array.rb +55 -0
- data/lib/chemicalml/cml/base/formula.rb +31 -10
- data/lib/chemicalml/cml/base/fragment.rb +33 -9
- data/lib/chemicalml/cml/base/fragment_list.rb +26 -7
- data/lib/chemicalml/cml/base/gradient.rb +19 -6
- data/lib/chemicalml/cml/base/identifier.rb +22 -4
- data/lib/chemicalml/cml/base/integer.rb +52 -0
- data/lib/chemicalml/cml/base/integer_array.rb +55 -0
- data/lib/chemicalml/cml/base/isotope.rb +29 -8
- data/lib/chemicalml/cml/base/isotope_list.rb +18 -7
- data/lib/chemicalml/cml/base/join.rb +28 -6
- data/lib/chemicalml/cml/base/kpoint.rb +17 -6
- data/lib/chemicalml/cml/base/kpoint_list.rb +15 -7
- data/lib/chemicalml/cml/base/label.rb +15 -5
- data/lib/chemicalml/cml/base/lattice.rb +36 -9
- data/lib/chemicalml/cml/base/lattice_vector.rb +17 -6
- data/lib/chemicalml/cml/base/length.rb +31 -7
- data/lib/chemicalml/cml/base/line3.rb +21 -5
- data/lib/chemicalml/cml/base/link.rb +51 -10
- data/lib/chemicalml/cml/base/list.rb +23 -9
- data/lib/chemicalml/cml/base/map.rb +38 -5
- data/lib/chemicalml/cml/base/matrix.rb +38 -9
- data/lib/chemicalml/cml/base/mechanism.rb +20 -7
- data/lib/chemicalml/cml/base/mechanism_component.rb +14 -6
- data/lib/chemicalml/cml/base/metadata.rb +20 -6
- data/lib/chemicalml/cml/base/metadata_list.rb +30 -5
- data/lib/chemicalml/cml/base/molecule.rb +95 -21
- data/lib/chemicalml/cml/base/molecule_list.rb +27 -7
- data/lib/chemicalml/cml/base/name.rb +11 -3
- data/lib/chemicalml/cml/base/object.rb +20 -6
- data/lib/chemicalml/cml/base/observation.rb +18 -5
- data/lib/chemicalml/cml/base/operator.rb +40 -0
- data/lib/chemicalml/cml/base/parameter.rb +38 -8
- data/lib/chemicalml/cml/base/parameter_list.rb +30 -5
- data/lib/chemicalml/cml/base/particle.rb +23 -6
- data/lib/chemicalml/cml/base/peak.rb +86 -11
- data/lib/chemicalml/cml/base/peak_group.rb +81 -7
- data/lib/chemicalml/cml/base/peak_list.rb +25 -8
- data/lib/chemicalml/cml/base/peak_structure.rb +47 -6
- data/lib/chemicalml/cml/base/plane3.rb +15 -5
- data/lib/chemicalml/cml/base/point3.rb +15 -5
- data/lib/chemicalml/cml/base/potential.rb +17 -6
- data/lib/chemicalml/cml/base/potential_form.rb +31 -6
- data/lib/chemicalml/cml/base/potential_list.rb +15 -7
- data/lib/chemicalml/cml/base/product.rb +53 -2
- data/lib/chemicalml/cml/base/product_list.rb +36 -2
- data/lib/chemicalml/cml/base/property.rb +32 -8
- data/lib/chemicalml/cml/base/property_list.rb +26 -5
- data/lib/chemicalml/cml/base/reactant.rb +53 -2
- data/lib/chemicalml/cml/base/reactant_list.rb +36 -2
- data/lib/chemicalml/cml/base/reaction.rb +82 -12
- data/lib/chemicalml/cml/base/reaction_list.rb +29 -2
- data/lib/chemicalml/cml/base/reaction_scheme.rb +48 -8
- data/lib/chemicalml/cml/base/reaction_step.rb +33 -9
- data/lib/chemicalml/cml/base/reaction_step_list.rb +34 -7
- data/lib/chemicalml/cml/base/reactive_centre.rb +31 -6
- data/lib/chemicalml/cml/base/region.rb +24 -5
- data/lib/chemicalml/cml/base/related_entry.rb +34 -0
- data/lib/chemicalml/cml/base/sample.rb +32 -5
- data/lib/chemicalml/cml/base/scalar.rb +41 -6
- data/lib/chemicalml/cml/base/spectator.rb +26 -5
- data/lib/chemicalml/cml/base/spectator_list.rb +20 -7
- data/lib/chemicalml/cml/base/spectrum.rb +63 -12
- data/lib/chemicalml/cml/base/spectrum_data.rb +20 -8
- data/lib/chemicalml/cml/base/spectrum_list.rb +30 -7
- data/lib/chemicalml/cml/base/sphere3.rb +15 -5
- data/lib/chemicalml/cml/base/stmml.rb +12 -5
- data/lib/chemicalml/cml/base/string.rb +40 -0
- data/lib/chemicalml/cml/base/string_array.rb +52 -0
- data/lib/chemicalml/cml/base/substance.rb +42 -8
- data/lib/chemicalml/cml/base/substance_list.rb +32 -7
- data/lib/chemicalml/cml/base/symmetry.rb +33 -7
- data/lib/chemicalml/cml/base/system.rb +23 -8
- data/lib/chemicalml/cml/base/table.rb +43 -8
- data/lib/chemicalml/cml/base/table_cell.rb +12 -5
- data/lib/chemicalml/cml/base/table_content.rb +20 -8
- data/lib/chemicalml/cml/base/table_header.rb +15 -7
- data/lib/chemicalml/cml/base/table_header_cell.rb +27 -5
- data/lib/chemicalml/cml/base/table_row.rb +15 -7
- data/lib/chemicalml/cml/base/table_row_list.rb +15 -7
- data/lib/chemicalml/cml/base/tcell.rb +43 -0
- data/lib/chemicalml/cml/base/torsion.rb +34 -7
- data/lib/chemicalml/cml/base/transform3.rb +12 -5
- data/lib/chemicalml/cml/base/transition_state.rb +23 -5
- data/lib/chemicalml/cml/base/trow.rb +48 -0
- data/lib/chemicalml/cml/base/unit.rb +45 -10
- data/lib/chemicalml/cml/base/unit_list.rb +40 -6
- data/lib/chemicalml/cml/base/unit_type.rb +32 -5
- data/lib/chemicalml/cml/base/unit_type_list.rb +34 -6
- data/lib/chemicalml/cml/base/vector3.rb +15 -5
- data/lib/chemicalml/cml/base/xaxis.rb +33 -7
- data/lib/chemicalml/cml/base/yaxis.rb +33 -7
- data/lib/chemicalml/cml/base/z_matrix.rb +26 -5
- data/lib/chemicalml/cml/base.rb +139 -121
- data/lib/chemicalml/cml/canonical_comparison.rb +54 -0
- data/lib/chemicalml/cml/elements.rb +168 -124
- data/lib/chemicalml/cml/enums.rb +55 -0
- data/lib/chemicalml/cml/namespace.rb +2 -3
- data/lib/chemicalml/cml/patterns.rb +33 -0
- data/lib/chemicalml/cml/reference_resolver.rb +137 -0
- data/lib/chemicalml/cml/role/alternative.rb +10 -0
- data/lib/chemicalml/cml/role/annotation.rb +10 -0
- data/lib/chemicalml/cml/role/appinfo.rb +10 -0
- data/lib/chemicalml/cml/role/arg.rb +10 -0
- data/lib/chemicalml/cml/role/cml_module.rb +2 -3
- data/lib/chemicalml/cml/role/complex_object.rb +10 -0
- data/lib/chemicalml/cml/role/enumeration.rb +10 -0
- data/lib/chemicalml/cml/role/expression.rb +10 -0
- data/lib/chemicalml/cml/role/float.rb +10 -0
- data/lib/chemicalml/cml/role/float_array.rb +10 -0
- data/lib/chemicalml/cml/role/integer.rb +10 -0
- data/lib/chemicalml/cml/role/integer_array.rb +10 -0
- data/lib/chemicalml/cml/role/operator.rb +10 -0
- data/lib/chemicalml/cml/role/related_entry.rb +10 -0
- data/lib/chemicalml/cml/role/string.rb +10 -0
- data/lib/chemicalml/cml/role/string_array.rb +10 -0
- data/lib/chemicalml/cml/role/tcell.rb +10 -0
- data/lib/chemicalml/cml/role/trow.rb +10 -0
- data/lib/chemicalml/cml/role.rb +138 -121
- data/lib/chemicalml/cml/schema24/alternative.rb +14 -0
- data/lib/chemicalml/cml/schema24/annotation.rb +15 -0
- data/lib/chemicalml/cml/schema24/appinfo.rb +15 -0
- data/lib/chemicalml/cml/schema24/arg.rb +14 -0
- data/lib/chemicalml/cml/schema24/cml_module.rb +18 -0
- data/lib/chemicalml/cml/schema24/complex_object.rb +14 -0
- data/lib/chemicalml/cml/schema24/configuration.rb +15 -2
- data/lib/chemicalml/cml/schema24/enumeration.rb +16 -0
- data/lib/chemicalml/cml/schema24/expression.rb +14 -0
- data/lib/chemicalml/cml/schema24/float.rb +14 -0
- data/lib/chemicalml/cml/schema24/float_array.rb +14 -0
- data/lib/chemicalml/cml/schema24/integer.rb +14 -0
- data/lib/chemicalml/cml/schema24/integer_array.rb +14 -0
- data/lib/chemicalml/cml/schema24/operator.rb +14 -0
- data/lib/chemicalml/cml/schema24/related_entry.rb +14 -0
- data/lib/chemicalml/cml/schema24/string.rb +14 -0
- data/lib/chemicalml/cml/schema24/string_array.rb +14 -0
- data/lib/chemicalml/cml/schema24/tcell.rb +14 -0
- data/lib/chemicalml/cml/schema24/trow.rb +14 -0
- data/lib/chemicalml/cml/schema24.rb +154 -126
- data/lib/chemicalml/cml/schema3.rb +123 -123
- data/lib/chemicalml/cml/visitable.rb +77 -3
- data/lib/chemicalml/cml/wire_class_registry.rb +3 -3
- data/lib/chemicalml/cml.rb +164 -132
- data/lib/chemicalml/context_configuration.rb +12 -4
- data/lib/chemicalml/convention/base.rb +17 -3
- data/lib/chemicalml/convention/cascade/constraints/reaction_scheme_must_have_content.rb +33 -0
- data/lib/chemicalml/convention/cascade/constraints/reaction_step_list_must_contain_steps.rb +31 -0
- data/lib/chemicalml/convention/cascade/constraints/reaction_step_must_have_reaction_or_lists.rb +33 -0
- data/lib/chemicalml/convention/cascade/constraints/reactive_centre_atom_refs_should_be_present.rb +34 -0
- data/lib/chemicalml/convention/cascade/constraints.rb +18 -0
- data/lib/chemicalml/convention/cascade.rb +43 -0
- data/lib/chemicalml/convention/compchem/constraints/array_rules.rb +44 -0
- data/lib/chemicalml/convention/compchem/constraints/calculation_requires_finalization.rb +28 -0
- data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +5 -4
- data/lib/chemicalml/convention/compchem/constraints/environment_at_most_one_property_list.rb +26 -0
- data/lib/chemicalml/convention/compchem/constraints/environment_must_not_contain_parameter.rb +28 -0
- data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_molecule.rb +26 -0
- data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_property_list.rb +26 -0
- data/lib/chemicalml/convention/compchem/constraints/finalization_must_have_content.rb +44 -0
- data/lib/chemicalml/convention/compchem/constraints/finalization_must_not_contain_parameter.rb +27 -0
- data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_molecule.rb +26 -0
- data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_parameter_list.rb +26 -0
- data/lib/chemicalml/convention/compchem/constraints/initialization_must_have_content.rb +54 -0
- data/lib/chemicalml/convention/compchem/constraints/initialization_must_not_contain_property.rb +28 -0
- data/lib/chemicalml/convention/compchem/constraints/job_list_module_must_have_id.rb +25 -0
- data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_environment.rb +25 -0
- data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_finalization.rb +25 -0
- data/lib/chemicalml/convention/compchem/constraints/job_module_must_have_id.rb +25 -0
- data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +7 -9
- data/lib/chemicalml/convention/compchem/constraints/matrix_rules.rb +48 -0
- data/lib/chemicalml/convention/compchem/constraints/module_predicates.rb +50 -0
- data/lib/chemicalml/convention/compchem/constraints/scalar_units.rb +42 -0
- data/lib/chemicalml/convention/compchem/constraints.rb +40 -2
- data/lib/chemicalml/convention/compchem.rb +20 -2
- data/lib/chemicalml/convention/constraint.rb +68 -24
- data/lib/chemicalml/convention/coordinator.rb +90 -0
- data/lib/chemicalml/convention/detection.rb +39 -0
- data/lib/chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace.rb +29 -0
- data/lib/chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash.rb +32 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern.rb +33 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +4 -4
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_contain_definition.rb +29 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +6 -6
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_unit_type.rb +31 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_units_co_constraints.rb +44 -0
- data/lib/chemicalml/convention/dictionary/constraints.rb +14 -2
- data/lib/chemicalml/convention/dictionary.rb +8 -2
- data/lib/chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula.rb +41 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +18 -9
- data/lib/chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired.rb +6 -6
- data/lib/chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table.rb +39 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_id_must_match_pattern.rb +7 -6
- data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +3 -2
- data/lib/chemicalml/convention/molecular/constraints/atom_must_have_element_type.rb +4 -4
- data/lib/chemicalml/convention/molecular/constraints/atom_must_have_id.rb +5 -4
- data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb +44 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb +65 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb +37 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct.rb +44 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_id_should_match_pattern.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule.rb +44 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2.rb +4 -4
- data/lib/chemicalml/convention/molecular/constraints/bond_must_have_order.rb +5 -4
- data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +4 -3
- data/lib/chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref.rb +34 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum.rb +39 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric.rb +3 -3
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb +43 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb +45 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb +33 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2.rb +45 -0
- data/lib/chemicalml/convention/molecular/constraints/dict_ref_should_resolve.rb +59 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children.rb +33 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children.rb +32 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level.rb +35 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_must_have_id.rb +4 -4
- data/lib/chemicalml/convention/molecular/constraints/property_must_have_dict_ref.rb +4 -4
- data/lib/chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary.rb +58 -0
- data/lib/chemicalml/convention/molecular/constraints/references_should_resolve.rb +37 -0
- data/lib/chemicalml/convention/molecular/constraints/scalar_must_have_data_type.rb +4 -4
- data/lib/chemicalml/convention/molecular/constraints.rb +59 -13
- data/lib/chemicalml/convention/molecular.rb +25 -2
- data/lib/chemicalml/convention/registry.rb +95 -10
- data/lib/chemicalml/convention/simple_unit/constraints/root_must_be_unit_list.rb +24 -0
- data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_power.rb +32 -0
- data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_symbol.rb +31 -0
- data/lib/chemicalml/convention/simple_unit/constraints.rb +16 -0
- data/lib/chemicalml/convention/simple_unit.rb +37 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/peak_should_have_values.rb +34 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention.rb +32 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints.rb +20 -0
- data/lib/chemicalml/convention/spectroscopy.rb +41 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit.rb +29 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace.rb +36 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition.rb +28 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_id.rb +28 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si.rb +34 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si.rb +32 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +4 -4
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_title.rb +32 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve.rb +45 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve.rb +46 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints.rb +19 -1
- data/lib/chemicalml/convention/unit_dictionary.rb +11 -2
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern.rb +31 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type.rb +28 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace.rb +35 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition.rb +28 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +6 -6
- data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +9 -1
- data/lib/chemicalml/convention/unit_type_dictionary.rb +6 -2
- data/lib/chemicalml/convention/validation_report.rb +90 -0
- data/lib/chemicalml/convention/violation.rb +9 -3
- data/lib/chemicalml/convention.rb +49 -11
- data/lib/chemicalml/dictionary/entry.rb +6 -6
- data/lib/chemicalml/dictionary/enum.rb +1 -1
- data/lib/chemicalml/dictionary/link.rb +3 -3
- data/lib/chemicalml/dictionary/loader.rb +19 -19
- data/lib/chemicalml/dictionary/model.rb +5 -5
- data/lib/chemicalml/dictionary/registry.rb +9 -9
- data/lib/chemicalml/dictionary.rb +13 -9
- data/lib/chemicalml/logger.rb +47 -0
- data/lib/chemicalml/schema/definition.rb +2 -2
- data/lib/chemicalml/schema.rb +7 -7
- data/lib/chemicalml/version.rb +1 -1
- data/lib/chemicalml/versioned_parser.rb +28 -10
- data/lib/chemicalml.rb +79 -17
- data/lib/tasks/reference_fetch.rake +30 -31
- data/lib/tasks/scrape_fixtures.rake +23 -24
- metadata +235 -35
- data/lib/chemicalml/cml/child_mappings.rb +0 -168
- data/lib/chemicalml/cml/translator/value_translations.rb +0 -270
- data/lib/chemicalml/cml/translator.rb +0 -484
- data/lib/chemicalml/model/atom.rb +0 -64
- data/lib/chemicalml/model/atom_parity.rb +0 -21
- data/lib/chemicalml/model/bond.rb +0 -39
- data/lib/chemicalml/model/bond_stereo.rb +0 -29
- data/lib/chemicalml/model/cml_array.rb +0 -27
- data/lib/chemicalml/model/cml_module.rb +0 -45
- data/lib/chemicalml/model/document.rb +0 -29
- data/lib/chemicalml/model/formula.rb +0 -34
- data/lib/chemicalml/model/identifier.rb +0 -21
- data/lib/chemicalml/model/label.rb +0 -21
- data/lib/chemicalml/model/matrix.rb +0 -29
- data/lib/chemicalml/model/metadata.rb +0 -23
- data/lib/chemicalml/model/metadata_list.rb +0 -25
- data/lib/chemicalml/model/molecule.rb +0 -45
- data/lib/chemicalml/model/name.rb +0 -21
- data/lib/chemicalml/model/node.rb +0 -45
- data/lib/chemicalml/model/parameter.rb +0 -27
- data/lib/chemicalml/model/parameter_list.rb +0 -25
- data/lib/chemicalml/model/product.rb +0 -21
- data/lib/chemicalml/model/product_list.rb +0 -21
- data/lib/chemicalml/model/property.rb +0 -28
- data/lib/chemicalml/model/property_list.rb +0 -25
- data/lib/chemicalml/model/reactant.rb +0 -21
- data/lib/chemicalml/model/reactant_list.rb +0 -21
- data/lib/chemicalml/model/reaction.rb +0 -41
- data/lib/chemicalml/model/reaction_list.rb +0 -23
- data/lib/chemicalml/model/scalar.rb +0 -25
- data/lib/chemicalml/model/substance.rb +0 -25
- data/lib/chemicalml/model.rb +0 -40
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Round 22 — Missing molecular constraints, CLI, diverse fixtures
|
|
2
|
+
|
|
3
|
+
This round closes constraints from the molecular spec that were
|
|
4
|
+
genuinely missing, adds a CLI utility, and broadens fixture
|
|
5
|
+
coverage to exercise more code paths.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [85 — Bond atomRefs2 distinct constraint](85-bond-distinct-atoms.md)
|
|
10
|
+
- [86 — References-should-resolve constraint](86-references-should-resolve.md)
|
|
11
|
+
- [87 — CLI utility](87-cli-utility.md)
|
|
12
|
+
- [88 — Diverse fixtures](88-diverse-fixtures.md)
|
|
13
|
+
- [89 — Final verification round 22](89-final-verification-round22.md)
|
|
14
|
+
|
|
15
|
+
## Outcomes
|
|
16
|
+
|
|
17
|
+
- `BondAtomRefs2ShouldBeDistinct` — warning when `atomRefs2` references
|
|
18
|
+
the same atom twice (e.g. `a1 a1`). Closes the molecular spec's
|
|
19
|
+
"two distinct atom ids" rule.
|
|
20
|
+
- `ReferencesShouldResolve` — DocumentConstraint that uses
|
|
21
|
+
`Cml::ReferenceResolver` to walk the document and report bonds
|
|
22
|
+
referencing missing atoms. Catches typos like `atomRefs2="a1 a99"`
|
|
23
|
+
when only a1 exists.
|
|
24
|
+
- `Chemicalml::Cli` + `exe/chemicalml` — CLI utility with three
|
|
25
|
+
commands:
|
|
26
|
+
- `chemicalml validate <file>` — auto-detect convention, print
|
|
27
|
+
violations to stderr, exit non-zero on errors
|
|
28
|
+
- `chemicalml conventions` — list the 8 registered conventions
|
|
29
|
+
- `chemicalml dictionaries` — list the 8 built-in dictionaries
|
|
30
|
+
- New fixtures:
|
|
31
|
+
- chiral_center_with_bond_stereo.cml (bondStereo W/H + atomParity)
|
|
32
|
+
- ethanol_with_properties.cml (propertyList + formula)
|
|
33
|
+
- nacl_with_lattice.cml (crystal + symmetry + matrix)
|
|
34
|
+
- diels_alder.cml (reactionScheme + reactantList + productList)
|
|
35
|
+
- Molecular convention now registers 28 constraints (up from 26).
|
|
36
|
+
- **471 examples, 0 failures, 3 pending**. Zero forbidden patterns.
|
|
37
|
+
|
|
38
|
+
## Architectural notes
|
|
39
|
+
|
|
40
|
+
- The `ReferencesShouldResolve` constraint is the first
|
|
41
|
+
`DocumentConstraint` (vs NodeConstraint) in the molecular
|
|
42
|
+
convention. It demonstrates the convention framework's
|
|
43
|
+
support for cross-cutting rules that need the whole document.
|
|
44
|
+
- The CLI is a thin dispatcher — adding a new subcommand = adding
|
|
45
|
+
one method and one entry in `Cli::COMMANDS`. OCP.
|
|
46
|
+
- Existing fixtures already passed; the new ones exercise the
|
|
47
|
+
recently added bondStereo / propertyList / crystal / reaction
|
|
48
|
+
paths in the Base modules.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Round 23 — Convention spec completeness, YARD docs
|
|
2
|
+
|
|
3
|
+
This round closes remaining gaps between the upstream convention
|
|
4
|
+
specs and our implementation, and adds YARD documentation to the
|
|
5
|
+
public API.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [90 — BondStereo completeness](90-bond-stereo-completeness.md)
|
|
10
|
+
- [91 — AtomParity atomRefs4 distinct](91-atomparity-distinct.md)
|
|
11
|
+
- [92 — Unit-dictionary completeness](92-unit-dictionary-completeness.md)
|
|
12
|
+
- [93 — YARD docs for public API](93-yard-docs.md)
|
|
13
|
+
- [94 — Final verification round 23](94-final-verification-round23.md)
|
|
14
|
+
|
|
15
|
+
## Outcomes
|
|
16
|
+
|
|
17
|
+
- `BondStereoAtomRefs4ShouldBeDistinct` — molecular warning when
|
|
18
|
+
C/T stereo's atomRefs4 has duplicate atoms. The molecular spec
|
|
19
|
+
says "four distinct atom ids" — now enforced.
|
|
20
|
+
- `AtomParityAtomRefs4ShouldBeDistinct` — molecular warning when
|
|
21
|
+
atomParity's atomRefs4 has duplicates. Closes a real semantic gap:
|
|
22
|
+
duplicate parity atoms make the chiral descriptor meaningless.
|
|
23
|
+
- Three new unit-dictionary constraints:
|
|
24
|
+
- `UnitMustHaveTitle`
|
|
25
|
+
- `UnitMustHaveParentSi`
|
|
26
|
+
- `UnitMustHaveMultiplierOrConstantToSi`
|
|
27
|
+
- Molecular convention: 30 constraints (up from 28).
|
|
28
|
+
- Unit-dictionary convention: 8 constraints (up from 5).
|
|
29
|
+
- YARD docstrings added to:
|
|
30
|
+
- `Chemicalml.parse`, `Chemicalml.serialize`, `Chemicalml.parser_for`
|
|
31
|
+
- `Chemicalml::Cli.run`, `Chemicalml::Cli.run_validate`
|
|
32
|
+
- `Chemicalml::Convention.validate`, `validate_report`, `detect_and_validate`, `lookup`
|
|
33
|
+
- `Chemicalml::Dictionary.load`
|
|
34
|
+
- `Chemicalml::Cml::ReferenceResolver#initialize`
|
|
35
|
+
- **471 examples, 0 failures, 3 pending**. Zero forbidden patterns.
|
|
36
|
+
|
|
37
|
+
## Architectural notes
|
|
38
|
+
|
|
39
|
+
- All new constraints are warning severity (or error where the spec
|
|
40
|
+
uses MUST). The convention framework distinguishes naturally.
|
|
41
|
+
- YARD docstrings follow the existing pattern (concise, no novel
|
|
42
|
+
terminology, examples where helpful). They will be picked up by
|
|
43
|
+
`bundle exec yard doc` when the yard gem is added.
|
|
44
|
+
- The spec count stays the same (471) because the round-23 spec
|
|
45
|
+
file replaces one existing test; new constraint tests replace the
|
|
46
|
+
old "5 constraints" / "28 constraints" assertion tests.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Round 24 — Round-trip equivalence, cross-format interop, introspection
|
|
2
|
+
|
|
3
|
+
This round formalises the round-trip guarantee across all fixtures,
|
|
4
|
+
proves cross-format interoperability (XML → JSON → YAML → XML),
|
|
5
|
+
adds a convention introspection API, and exercises Schema24 with
|
|
6
|
+
complex content.
|
|
7
|
+
|
|
8
|
+
## Files
|
|
9
|
+
|
|
10
|
+
- [95 — Round-trip equivalence spec](95-round-trip-equivalence.md)
|
|
11
|
+
- [96 — Cross-format interoperability](96-cross-format-interop.md)
|
|
12
|
+
- [97 — Convention introspection API](97-convention-introspection.md)
|
|
13
|
+
- [98 — Schema24 complex round-trip](98-schema24-complex-round-trip.md)
|
|
14
|
+
- [99 — Final verification round 24](99-final-verification-round24.md)
|
|
15
|
+
|
|
16
|
+
## Outcomes
|
|
17
|
+
|
|
18
|
+
- **Round-trip equivalence spec** — for every one of the 20 fixtures,
|
|
19
|
+
asserts that `parse(xml) → serialize → parse` produces a
|
|
20
|
+
structurally-equivalent document (compared via a recursive
|
|
21
|
+
element-name fingerprint). Catches future serialization drift.
|
|
22
|
+
|
|
23
|
+
- **Cross-format interoperability spec** — proves the format-agnostic
|
|
24
|
+
claim end-to-end:
|
|
25
|
+
- XML → JSON → XML (structural fingerprint equal)
|
|
26
|
+
- XML → YAML → XML (structural fingerprint equal)
|
|
27
|
+
- JSON → JSON idempotent (same output)
|
|
28
|
+
- XML → JSON → YAML → XML (4-format chain, structural equal)
|
|
29
|
+
|
|
30
|
+
- **`Convention::Registry.each`** — iterates all 8 conventions
|
|
31
|
+
sorted by QName. Returns an Enumerator without a block.
|
|
32
|
+
- **`Convention::Registry.convention_root?(role)`** — boolean check
|
|
33
|
+
for whether a Role module is a convention-bearing root.
|
|
34
|
+
|
|
35
|
+
- **Schema24 complex round-trip** — exercises the now-fixed Schema24
|
|
36
|
+
parser with rich content (parallel-array atoms + bonds, names,
|
|
37
|
+
formulas). Proves Schema24 has feature parity with Schema3.
|
|
38
|
+
|
|
39
|
+
- **Real bug fix discovered via round 24 testing**:
|
|
40
|
+
`AtomArrayMustContainAtoms` was rejecting parallel-array form
|
|
41
|
+
(which has no `<atom>` children but uses `atomID` attribute).
|
|
42
|
+
Fixed to accept either form.
|
|
43
|
+
|
|
44
|
+
- **515 examples, 0 failures, 3 pending** (TOML adapter pending).
|
|
45
|
+
Zero forbidden patterns.
|
|
46
|
+
|
|
47
|
+
## Architectural insight
|
|
48
|
+
|
|
49
|
+
The cross-format interop spec is the most valuable safety net: any
|
|
50
|
+
future change to the Base modules that breaks the format-agnostic
|
|
51
|
+
claim (e.g. adds a method that only works in one format) is caught
|
|
52
|
+
immediately. The structural fingerprint comparison is format-blind,
|
|
53
|
+
so it catches real data loss rather than just string-equality noise.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Round 25 — Semantic validation, periodic table, dictRef resolution
|
|
2
|
+
|
|
3
|
+
This round adds three real semantic checks (element type, dictRef
|
|
4
|
+
resolution, summary rendering) that go beyond structural validation.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- [100 — Periodic table constant](100-periodic-table.md)
|
|
9
|
+
- [101 — Element type validation constraint](101-element-type-validation.md)
|
|
10
|
+
- [102 — DictRef resolution constraint](102-dictref-resolution.md)
|
|
11
|
+
- [103 — ValidationReport summary](103-validation-report-summary.md)
|
|
12
|
+
- [104 — Final verification round 25](104-final-verification-round25.md)
|
|
13
|
+
|
|
14
|
+
## Outcomes
|
|
15
|
+
|
|
16
|
+
- **`Cml::Enums::ELEMENT_TYPE_VALUES`** — 120-element frozen Set
|
|
17
|
+
matching XSD elementTypeType exactly. Includes the periodic table
|
|
18
|
+
plus "Du" (dummy) and "R" (group placeholder). Single source of
|
|
19
|
+
truth for valid element symbols.
|
|
20
|
+
|
|
21
|
+
- **`AtomElementTypeShouldBeInPeriodicTable`** — molecular warning
|
|
22
|
+
when `atom.elementType` isn't a recognised periodic table symbol.
|
|
23
|
+
Catches typos like "Carb", "Xx", "X" — previously silently
|
|
24
|
+
accepted.
|
|
25
|
+
|
|
26
|
+
- **`DictRefShouldResolve`** — molecular warning when an element's
|
|
27
|
+
`dictRef` attribute doesn't resolve against the built-in
|
|
28
|
+
dictionaries via `Chemicalml::Dictionary::Registry.lookup`. Catches
|
|
29
|
+
typos like `dictRef="cml:bpingpoint"`. This is a real
|
|
30
|
+
cross-component integration: the convention constraint system
|
|
31
|
+
querying the dictionary registry.
|
|
32
|
+
|
|
33
|
+
- **`ValidationReport#summary`** — human-readable multi-line summary.
|
|
34
|
+
Used by the CLI; useful for one-shot scripts. Format:
|
|
35
|
+
```
|
|
36
|
+
Errors: 2, Warnings: 1
|
|
37
|
+
|
|
38
|
+
Errors:
|
|
39
|
+
ERROR path/a: message (value="X")
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
Warnings:
|
|
43
|
+
WARN path/b: message
|
|
44
|
+
...
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- **Real bug caught by new constraint**: existing test
|
|
48
|
+
`extended_constraints_spec.rb` was using `dictRef="cml:energy"`
|
|
49
|
+
which isn't in the cml dictionary. The new DictRefShouldResolve
|
|
50
|
+
constraint flagged it. Fixed the test to use the real `cml:bp`
|
|
51
|
+
entry — and incidentally validated that the constraint works.
|
|
52
|
+
|
|
53
|
+
- Molecular convention: 32 constraints (up from 30).
|
|
54
|
+
|
|
55
|
+
- **528 examples, 0 failures, 3 pending** (TOML adapter pending).
|
|
56
|
+
Zero forbidden patterns.
|
|
57
|
+
|
|
58
|
+
## Architectural insight
|
|
59
|
+
|
|
60
|
+
The DictRefShouldResolve constraint is the first to bridge two
|
|
61
|
+
previously-separate subsystems (convention constraints and
|
|
62
|
+
dictionary registry). It validates the cross-component contract:
|
|
63
|
+
"a dictRef must point at a real term". This is the kind of
|
|
64
|
+
whole-system semantic check that's only possible when both halves
|
|
65
|
+
are mature — round 25 is the first round where it became worth
|
|
66
|
+
writing.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Round 26 — XSD patterns, AtomParity parent check, canonical comparison
|
|
2
|
+
|
|
3
|
+
This round adds the canonical XSD pattern constants, semantic
|
|
4
|
+
parent-atom validation for atomParity, and a canonical document
|
|
5
|
+
comparison helper.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [105 — XSD pattern constants](105-xsd-pattern-constants.md)
|
|
10
|
+
- [106 — Id/namespace pattern validation](106-id-pattern-validation.md)
|
|
11
|
+
- [107 — AtomParity parent atom check](107-atomparity-parent-check.md)
|
|
12
|
+
- [108 — Canonical document comparison](108-canonical-comparison.md)
|
|
13
|
+
- [109 — Final verification round 26](109-final-verification-round26.md)
|
|
14
|
+
|
|
15
|
+
## Outcomes
|
|
16
|
+
|
|
17
|
+
- **`Cml::Patterns`** — 14 frozen Regexp constants matching every XSD
|
|
18
|
+
simpleType that restricts by pattern (atomIDType, idType,
|
|
19
|
+
dictionaryPrefixType, namespaceType, refType, versionType,
|
|
20
|
+
formulaType, etc.). Single source of truth — a future XSD edit
|
|
21
|
+
that changes a pattern can be regenerated from one place.
|
|
22
|
+
|
|
23
|
+
- **`MoleculeIdShouldMatchPattern`** and **`BondIdShouldMatchPattern`**
|
|
24
|
+
— molecular warnings using the new patterns. Catches ids that
|
|
25
|
+
don't match the XSD-declared format.
|
|
26
|
+
|
|
27
|
+
- **`AtomParityShouldIncludeParentAtom`** — molecular DocumentConstraint
|
|
28
|
+
that walks the tree and warns when `<atomParity>`'s atomRefs4
|
|
29
|
+
doesn't include the parent `<atom>`'s id. Real semantic check
|
|
30
|
+
that closes a CML convention rule.
|
|
31
|
+
|
|
32
|
+
- **`Cml::CanonicalComparison`** — semantic document comparison.
|
|
33
|
+
`equal?` returns true if two documents have the same structural
|
|
34
|
+
fingerprint. `diff` returns a hash of element_name → count_delta.
|
|
35
|
+
Useful for testing and diff tools.
|
|
36
|
+
|
|
37
|
+
- **Real bugs caught during round 26**:
|
|
38
|
+
- DELIMITER_PATTERN had unescaped `/` — syntax error.
|
|
39
|
+
- `visit_with_parent` was using `yield` in recursion, causing
|
|
40
|
+
LocalJumpError. Fixed by using explicit `&block`.
|
|
41
|
+
- XSD patterns are unanchored; for validation they must be
|
|
42
|
+
wrapped in `\A...\z`.
|
|
43
|
+
|
|
44
|
+
- Molecular convention: 35 constraints (up from 32).
|
|
45
|
+
|
|
46
|
+
- **543 examples, 0 failures, 3 pending** (TOML adapter pending).
|
|
47
|
+
Zero forbidden patterns.
|
|
48
|
+
|
|
49
|
+
## Architectural insight
|
|
50
|
+
|
|
51
|
+
The `CanonicalComparison` helper is the abstraction the round-trip
|
|
52
|
+
spec (TODO 95) and Schema24 complex round-trip spec (TODO 98) both
|
|
53
|
+
needed. Previously each spec inlined its own fingerprint function;
|
|
54
|
+
now there's one canonical implementation. Classic "rule of three"
|
|
55
|
+
refactor — the third use is the trigger to extract.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Round 27 — User-facing API, CLI improvements
|
|
2
|
+
|
|
3
|
+
This round adds the user-facing query API on `Cml::Visitable`, a
|
|
4
|
+
top-level `Chemicalml.validate` shortcut, and two new CLI commands
|
|
5
|
+
(`inspect`, `elements`).
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [110 — Document query API](110-document-query-api.md)
|
|
10
|
+
- [111 — Top-level Chemicalml.validate](111-toplevel-validate.md)
|
|
11
|
+
- [112 — CLI inspect command](112-cli-inspect.md)
|
|
12
|
+
- [113 — CLI elements command](113-cli-elements.md)
|
|
13
|
+
- [114 — Final verification round 27](114-final-verification-round27.md)
|
|
14
|
+
|
|
15
|
+
## Outcomes
|
|
16
|
+
|
|
17
|
+
- **`Cml::Visitable` query methods** (inherited by every wire class):
|
|
18
|
+
- `#each_wire_node`, `#each_atom`, `#each_bond`, `#each_molecule` — recursive iterators
|
|
19
|
+
- `#find_atom(id)`, `#find_bond(id)`, `#find_molecule(id)` — id-based lookup
|
|
20
|
+
- `#atom_count`, `#bond_count`, `#molecule_count` — recursive counts
|
|
21
|
+
- All return Enumerators without a block (Enumerable-style)
|
|
22
|
+
|
|
23
|
+
- **`Chemicalml.validate(doc)`** — top-level shortcut for
|
|
24
|
+
`Convention.detect_and_validate(doc)`. Users no longer need to
|
|
25
|
+
remember the full namespace.
|
|
26
|
+
|
|
27
|
+
- **`chemicalml inspect <file>`** — new CLI command. Prints a
|
|
28
|
+
tree-style summary of the document with element names, ids, and
|
|
29
|
+
structure. Useful for debugging.
|
|
30
|
+
|
|
31
|
+
- **`chemicalml elements`** — new CLI command. Lists every Schema3
|
|
32
|
+
wire class with its XML root name. Useful for introspection.
|
|
33
|
+
|
|
34
|
+
- **CLI refactor**: `run_validate` now uses `report.summary` instead
|
|
35
|
+
of manual rendering. Cleaner code, better output.
|
|
36
|
+
|
|
37
|
+
- **559 examples, 0 failures, 3 pending** (TOML adapter pending).
|
|
38
|
+
Zero forbidden patterns.
|
|
39
|
+
|
|
40
|
+
## Architectural insight
|
|
41
|
+
|
|
42
|
+
The query methods live on `Visitable` (the marker mixin every wire
|
|
43
|
+
class includes) rather than on `Document` specifically. This means
|
|
44
|
+
ANY wire class can be the root of a query — `molecule.find_atom(id)`,
|
|
45
|
+
`reaction.find_molecule(id)`, etc. This was the right call: the
|
|
46
|
+
walker is already general (visits any subtree); exposing query
|
|
47
|
+
methods only on Document would have required callers to navigate
|
|
48
|
+
up to Document first.
|
|
49
|
+
|
|
50
|
+
The CLI's `print_tree` helper is recursive but bounded (`max_depth: 6`)
|
|
51
|
+
to avoid runaway output on pathological documents.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Round 28 — Convenience API, custom conventions, compchem completion
|
|
2
|
+
|
|
3
|
+
This round adds user-facing convenience (`parse_file`, custom
|
|
4
|
+
convention registration), more compchem constraints from the spec,
|
|
5
|
+
and a comprehensive lint cleanup.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [115 — parse_file convenience](115-parse-file.md)
|
|
10
|
+
- [116 — Custom convention registration](116-custom-convention.md)
|
|
11
|
+
- [117 — More compchem constraints](117-more-compchem-constraints.md)
|
|
12
|
+
- [118 — Final verification round 28](118-final-verification-round28.md)
|
|
13
|
+
|
|
14
|
+
## Outcomes
|
|
15
|
+
|
|
16
|
+
- **`Chemicalml.parse_file(path)`** — convenience wrapper around
|
|
17
|
+
`parse(File.read(path))`. Raises ArgumentError for missing files.
|
|
18
|
+
Eliminates the `File.read` boilerplate.
|
|
19
|
+
|
|
20
|
+
- **`Convention::Registry.register_custom(mod)`** — runtime
|
|
21
|
+
registration of custom conventions. The module must `extend Base`
|
|
22
|
+
and return a non-empty `qname`. Useful for domain-specific CML
|
|
23
|
+
extensions (e.g., a `convention:my-lab-extensions` for a research
|
|
24
|
+
group's custom vocabulary).
|
|
25
|
+
|
|
26
|
+
- **Two new compchem constraints** from the spec:
|
|
27
|
+
- `InitializationMustHaveContent` — initialization module must
|
|
28
|
+
contain at least one of molecule, parameterList, or user-defined
|
|
29
|
+
module
|
|
30
|
+
- `FinalizationMustHaveContent` — finalization module must contain
|
|
31
|
+
at least one of molecule, propertyList, or user-defined module
|
|
32
|
+
|
|
33
|
+
- **Lint cleanup**: ran `rubocop -A` on all recently-touched files.
|
|
34
|
+
80 offenses autocorrected (string quotes, modifier-if usage,
|
|
35
|
+
parenthesised method args, etc.). Zero remaining style issues.
|
|
36
|
+
|
|
37
|
+
- **Real bug caught during round 28**: `register_custom` initially
|
|
38
|
+
deadlocked because it called `load_cache` while holding the mutex.
|
|
39
|
+
Fixed by separating the load step (which acquires/releases its
|
|
40
|
+
own mutex) from the mutation step (direct hash access after load
|
|
41
|
+
is guaranteed to have populated `@cache`).
|
|
42
|
+
|
|
43
|
+
- Compchem convention: 20 constraints (up from 18).
|
|
44
|
+
|
|
45
|
+
- **568 examples, 0 failures, 3 pending** (TOML adapter pending).
|
|
46
|
+
Zero forbidden patterns. **88 constraints across 8 conventions.**
|
|
47
|
+
|
|
48
|
+
## Architectural insight
|
|
49
|
+
|
|
50
|
+
The `register_custom` deadlock is a classic Ruby mutex pitfall:
|
|
51
|
+
re-entering a non-reentrant `Mutex#synchronize` block from within
|
|
52
|
+
itself deadlocks. The fix — call `load_cache` first (which manages
|
|
53
|
+
its own mutex acquisition), then mutate `@cache` directly — works
|
|
54
|
+
because Ruby's `||=` assignment makes `@cache` non-nil after the
|
|
55
|
+
first load. Documented in the method comment.
|
|
56
|
+
|
|
57
|
+
This round also reinforced the value of running rubocop on each
|
|
58
|
+
round's output. 80 autocorrected offenses across 8 files means
|
|
59
|
+
style drift was creeping in — single-quote vs double-quote,
|
|
60
|
+
`unless` vs `if !`, etc. A small CI rule (`bundle exec rubocop`
|
|
61
|
+
must pass) would catch this in future.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Round 29 — Wire introspection, README, dataType consistency
|
|
2
|
+
|
|
3
|
+
This round adds wire-class introspection, updates the README to
|
|
4
|
+
reflect the current API, and adds a property-dataType consistency
|
|
5
|
+
constraint that bridges the convention and dictionary subsystems
|
|
6
|
+
in a new way.
|
|
7
|
+
|
|
8
|
+
## Files
|
|
9
|
+
|
|
10
|
+
- [119 — Wire-class introspection](119-wire-introspection.md)
|
|
11
|
+
- [120 — Update README](120-readme-update.md)
|
|
12
|
+
- [121 — Property dataType consistency](121-property-datatype.md)
|
|
13
|
+
- [122 — Final verification round 29](122-final-verification-round29.md)
|
|
14
|
+
|
|
15
|
+
## Outcomes
|
|
16
|
+
|
|
17
|
+
- **`Cml.for_xml_name(name, schema:)`** — wire-class lookup by XML
|
|
18
|
+
element name. Uses the existing `Elements::XML_TO_CLASS` reverse
|
|
19
|
+
index. Returns nil for unknown names.
|
|
20
|
+
|
|
21
|
+
- **`Cml.wire_classes(schema:)`** — enumerator over all wire classes
|
|
22
|
+
for a schema version. Useful for code generation and tooling.
|
|
23
|
+
|
|
24
|
+
- **README rewritten**:
|
|
25
|
+
- 8 conventions + 88+ constraints documented
|
|
26
|
+
- All modern API examples (`parse_file`, `validate`, `each_atom`)
|
|
27
|
+
- CLI commands documented (`validate`, `inspect`, `conventions`,
|
|
28
|
+
`dictionaries`, `elements`)
|
|
29
|
+
- Cross-format serialisation examples
|
|
30
|
+
- Custom convention registration example
|
|
31
|
+
- Dictionary lookup example
|
|
32
|
+
- Architectural overview (`Base::*`, `Role::*`, `Elements::ALL`)
|
|
33
|
+
|
|
34
|
+
- **`PropertyScalarDataTypeMatchesDictionary`** — molecular warning
|
|
35
|
+
when a `<property>`'s `<scalar>` `dataType` doesn't match the
|
|
36
|
+
`dataType` declared in the dictionary entry referenced by `dictRef`.
|
|
37
|
+
This is a **three-component integration check**: convention
|
|
38
|
+
constraint system queries the dictionary registry AND inspects the
|
|
39
|
+
document's scalar child.
|
|
40
|
+
|
|
41
|
+
- Molecular convention: 36 constraints (up from 35).
|
|
42
|
+
|
|
43
|
+
- **574 examples, 0 failures, 3 pending**. Zero forbidden patterns.
|
|
44
|
+
|
|
45
|
+
## Architectural insight
|
|
46
|
+
|
|
47
|
+
`PropertyScalarDataTypeMatchesDictionary` is the deepest
|
|
48
|
+
cross-component check yet — it validates that:
|
|
49
|
+
|
|
50
|
+
1. The property's `dictRef` resolves to a real dictionary entry
|
|
51
|
+
(uses `Dictionary::Registry.lookup`).
|
|
52
|
+
2. The entry declares a `dataType`.
|
|
53
|
+
3. The property's `<scalar>` child declares a `dataType`.
|
|
54
|
+
4. The two `dataType` values match.
|
|
55
|
+
|
|
56
|
+
If any step is missing data, the constraint silently passes (no
|
|
57
|
+
false positives). When all the data is present, it catches real
|
|
58
|
+
inconsistencies that would cause downstream type errors.
|
|
59
|
+
|
|
60
|
+
This kind of multi-step semantic validation is what mature
|
|
61
|
+
document-validation frameworks look like. Round 29 is the first
|
|
62
|
+
round where it's been possible — earlier rounds built the
|
|
63
|
+
primitives (dictionary registry, dictRef resolution, scalar
|
|
64
|
+
dataType attribute) that this constraint composes.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Round 30 — unitType resolution, JSON CLI, enumeration
|
|
2
|
+
|
|
3
|
+
This round adds the unit-unitType cross-component check (parallels
|
|
4
|
+
dictRef resolution), CLI JSON output for machine consumers, and a
|
|
5
|
+
constraint-enumeration API.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [123 — Unit unitType resolution](123-unit-unittype-resolution.md)
|
|
10
|
+
- [124 — CLI JSON output](124-cli-json-output.md)
|
|
11
|
+
- [125 — Constraint enumeration API](125-constraint-enumeration.md)
|
|
12
|
+
- [126 — Final verification round 30](126-final-verification-round30.md)
|
|
13
|
+
|
|
14
|
+
## Outcomes
|
|
15
|
+
|
|
16
|
+
- **`UnitUnitTypeShouldResolve`** — unit-dictionary warning when a
|
|
17
|
+
`<unit>`'s `unitType` attribute doesn't resolve against the
|
|
18
|
+
built-in unitType-dictionary. Cross-component check parallel to
|
|
19
|
+
the molecular convention's `DictRefShouldResolve`.
|
|
20
|
+
|
|
21
|
+
- **`chemicalml validate --json file.cml`** — JSON output mode for
|
|
22
|
+
the CLI. Returns `{ file:, ok:, has_warnings:, violations: [...] }`
|
|
23
|
+
where each violation has `severity`, `path`, `message`, `value`.
|
|
24
|
+
Useful for editor integration, CI pipelines, automated tooling.
|
|
25
|
+
|
|
26
|
+
- **`Convention::Registry.each_constraint`** — iterates every
|
|
27
|
+
constraint across every convention, yielding `(convention, class)`
|
|
28
|
+
pairs. Useful for documentation generation and introspection.
|
|
29
|
+
|
|
30
|
+
- **`Convention::Registry.total_constraint_count`** — sum of all
|
|
31
|
+
registered constraints across all conventions (currently 90).
|
|
32
|
+
|
|
33
|
+
- Unit-dictionary convention: 9 constraints (up from 8).
|
|
34
|
+
|
|
35
|
+
- **580 examples, 0 failures, 3 pending** (TOML adapter pending).
|
|
36
|
+
Zero forbidden patterns. **90 constraints across 8 conventions.**
|
|
37
|
+
|
|
38
|
+
## Architectural insight
|
|
39
|
+
|
|
40
|
+
`UnitUnitTypeShouldResolve` completes a symmetric pair with
|
|
41
|
+
`DictRefShouldResolve` from round 25. Both follow the same pattern:
|
|
42
|
+
walk the document, look up reference attributes against the
|
|
43
|
+
dictionary registry, warn on misses. The pattern is now established
|
|
44
|
+
enough that adding a new reference-resolution constraint is a
|
|
45
|
+
2-minute job: copy the structure, change the attribute name and the
|
|
46
|
+
target dictionary.
|
|
47
|
+
|
|
48
|
+
The `each_constraint` iterator makes the constraint table tractable
|
|
49
|
+
for documentation generation. A future round could auto-generate a
|
|
50
|
+
Markdown table of all 90 constraints from this single API call.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Round 31 — CLI tooling, parentSI resolution
|
|
2
|
+
|
|
3
|
+
This round completes the CLI tooling suite (constraints + enums
|
|
4
|
+
commands) and adds the parentSI resolution constraint, completing
|
|
5
|
+
the cross-component check symmetry.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [127 — CLI constraints command](127-cli-constraints.md)
|
|
10
|
+
- [128 — CLI enums command](128-cli-enums.md)
|
|
11
|
+
- [129 — ParentSI resolution](129-parentsi-resolution.md)
|
|
12
|
+
- [130 — Final verification round 31](130-final-verification-round31.md)
|
|
13
|
+
|
|
14
|
+
## Outcomes
|
|
15
|
+
|
|
16
|
+
- **`chemicalml constraints`** — new CLI command listing all 91
|
|
17
|
+
constraints grouped by convention. Each line shows the constraint
|
|
18
|
+
class name and its declared `applies_to` roles.
|
|
19
|
+
|
|
20
|
+
- **`chemicalml enums`** — new CLI command listing every
|
|
21
|
+
`Cml::Enums` constant with its allowed values. Useful for users
|
|
22
|
+
who want to know what enum values an attribute accepts.
|
|
23
|
+
|
|
24
|
+
- **`UnitParentSiShouldResolve`** — unit-dictionary warning when a
|
|
25
|
+
`<unit>`'s `parentSI` doesn't resolve against built-in
|
|
26
|
+
dictionaries. Completes the cross-component check symmetry:
|
|
27
|
+
- dictRef resolution (molecular)
|
|
28
|
+
- unitType resolution (unit-dictionary)
|
|
29
|
+
- parentSI resolution (unit-dictionary)
|
|
30
|
+
|
|
31
|
+
- **Real bug caught during round 31**: existing fixture
|
|
32
|
+
`convention_spec.rb:226` used `parent_si: "siUnits:s"` which
|
|
33
|
+
doesn't resolve (correct prefix is `si`, not `siUnits`). Fixed.
|
|
34
|
+
|
|
35
|
+
- Unit-dictionary convention: 10 constraints (up from 9).
|
|
36
|
+
|
|
37
|
+
- **583 examples, 0 failures, 3 pending**. Zero forbidden patterns.
|
|
38
|
+
**91 constraints across 8 conventions.**
|
|
39
|
+
|
|
40
|
+
## Architectural insight
|
|
41
|
+
|
|
42
|
+
The cross-component check pattern (walk document → look up reference
|
|
43
|
+
attribute against registry → warn on miss) is now fully
|
|
44
|
+
established across three constraint pairs:
|
|
45
|
+
|
|
46
|
+
| Convention | Attribute | Lookup target |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| molecular | `dictRef` | dictionary registry |
|
|
49
|
+
| unit-dictionary | `unitType` | unitType-dictionary registry |
|
|
50
|
+
| unit-dictionary | `parentSI` | unit dictionary registry |
|
|
51
|
+
|
|
52
|
+
Adding a fourth is mechanical: copy the structure, change the
|
|
53
|
+
attribute name and lookup target. The pattern's clarity is its own
|
|
54
|
+
form of documentation.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Round 32 — Thor CLI refactor, info command, auto-docs
|
|
2
|
+
|
|
3
|
+
This round refactors the CLI to use Thor with one class per command,
|
|
4
|
+
adds the `info` command for element introspection, and auto-generates
|
|
5
|
+
constraint documentation.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- [131 — CLI info command](131-cli-info-command.md)
|
|
10
|
+
- [132 — Auto-generated constraint docs](132-auto-generated-constraint-docs.md)
|
|
11
|
+
- [133 — Final verification round 32](133-final-verification-round32.md)
|
|
12
|
+
|
|
13
|
+
## Outcomes
|
|
14
|
+
|
|
15
|
+
- **CLI refactored to Thor**. Each command is now a dedicated class:
|
|
16
|
+
- `Chemicalml::Cli::Base < Thor` — dispatcher
|
|
17
|
+
- `Chemicalml::Cli::Command` — base class with `.run(args, options)`
|
|
18
|
+
and `#run(args, options)`
|
|
19
|
+
- `Chemicalml::Cli::ValidateCommand`
|
|
20
|
+
- `Chemicalml::Cli::InspectCommand`
|
|
21
|
+
- `Chemicalml::Cli::ConventionsCommand`
|
|
22
|
+
- `Chemicalml::Cli::DictionariesCommand`
|
|
23
|
+
- `Chemicalml::Cli::ElementsCommand`
|
|
24
|
+
- `Chemicalml::Cli::ConstraintsCommand`
|
|
25
|
+
- `Chemicalml::Cli::EnumsCommand`
|
|
26
|
+
- `Chemicalml::Cli::InfoCommand`
|
|
27
|
+
|
|
28
|
+
Each command supports both `MyCommand.run(args, options)` (class
|
|
29
|
+
method) and `MyCommand.new.run(args, options)` (instance method)
|
|
30
|
+
invocation patterns — matching the user's requested pattern.
|
|
31
|
+
|
|
32
|
+
- **`chemicalml info <element>`** — new CLI command. Shows element
|
|
33
|
+
XML name, Ruby class, included Role module, all attributes (with
|
|
34
|
+
collection marker), and all applicable constraints across every
|
|
35
|
+
convention. Useful for users exploring the model.
|
|
36
|
+
|
|
37
|
+
- **Auto-generated constraint docs** — `docs/generate_constraint_docs.rb`
|
|
38
|
+
script produces `docs/constraints.md` from
|
|
39
|
+
`Convention::Registry.each_constraint`. Single source of truth —
|
|
40
|
+
regenerating picks up new constraints automatically. No manual
|
|
41
|
+
maintenance.
|
|
42
|
+
|
|
43
|
+
- **Bug fix from autocorrect**: `Convention::Registry#load_cache`
|
|
44
|
+
had `@cache` renamed to `@load_cache` by rubocop's
|
|
45
|
+
`MemoizedInstanceVariableName` rule. `register_custom` and `reset!`
|
|
46
|
+
needed alignment — fixed in this round.
|
|
47
|
+
|
|
48
|
+
- **586 examples, 0 failures, 3 pending**. Zero forbidden patterns.
|
|
49
|
+
|
|
50
|
+
## Architectural insight
|
|
51
|
+
|
|
52
|
+
The Thor refactor separates concerns cleanly:
|
|
53
|
+
|
|
54
|
+
1. **Thor dispatcher** owns argument parsing, help text, option
|
|
55
|
+
declarations. Each `desc + def` block is 2-3 lines.
|
|
56
|
+
2. **Command class** owns the actual work. Easy to test in isolation
|
|
57
|
+
(`MyCommand.new.run(args, options)` returns an exit code).
|
|
58
|
+
3. **No business logic in the dispatcher** — it's pure routing.
|
|
59
|
+
|
|
60
|
+
This pattern scales: a future `rake` task, Rake task, or web handler
|
|
61
|
+
can re-use any `*Command` class without going through Thor. The
|
|
62
|
+
Thor dispatcher is just one possible entry point.
|
|
63
|
+
|
|
64
|
+
The auto-generated constraint docs demonstrate the value of the
|
|
65
|
+
`each_constraint` API from round 30: a single iteration produces
|
|
66
|
+
comprehensive documentation with zero manual upkeep. Adding a new
|
|
67
|
+
constraint and re-running the generator is all it takes.
|