quantized-lab 0.9.0__tar.gz → 0.10.0__tar.gz
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.
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/PKG-INFO +1 -1
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/pyproject.toml +1 -1
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/__init__.py +1 -1
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure.py +17 -5
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_break.py +10 -4
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_labels.py +46 -14
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_overrides.py +1 -1
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_page.py +4 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_scale.py +4 -1
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_ticks.py +31 -2
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_bumps.py +30 -4
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_scan.py +2 -7
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fitting.py +17 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/curve_style_color.py +13 -8
- quantized_lab-0.10.0/src/quantized/io/origin_project/fidelity.py +192 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/figure_layers.py +2 -1
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/figures.py +13 -3
- quantized_lab-0.10.0/src/quantized/io/origin_project/graph_preview.py +156 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opj_curves.py +87 -22
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/templates.py +5 -3
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/export_figures.py +6 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/export_page.py +2 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/fitting.py +20 -3
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/parsers.py +31 -6
- quantized_lab-0.9.0/src/quantized/web/assets/index-BiZzN7J6.css → quantized_lab-0.10.0/src/quantized/web/assets/index-DkU851uO.css +1 -1
- quantized_lab-0.10.0/src/quantized/web/assets/index-N-btoovo.js +27 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/web/index.html +2 -2
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/tools/origin_compare/README.md +1 -1
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/tools/visual/README.md +44 -5
- quantized_lab-0.9.0/src/quantized/web/assets/index-D8RNGjHO.js +0 -27
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/.gitignore +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/LICENSE +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/NOTICE +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/README.md +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/__main__.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/api.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/app.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/__init__.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/_clipfit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/_natural_neighbor.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/aggregate.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/backgrounds.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/baseline.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/batch_fit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/constants.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/corrections.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/crystallography.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/diffusion.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/electrical.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/electrochemistry.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/element_data.json +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/element_data.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/errors.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_categorical.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_corner.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_facets.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_field.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_hitmap.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_map.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_shapes.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_statplots.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_styles.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/figure_ternary.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_autoguess.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_bootstrap.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_constraints.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_equation.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_findxy.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_models.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_models_special.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_odr.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/fit_stats.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/formula.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/global_curve_fit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/global_fit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/interp2d.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/linecut.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/magnetic.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/magnetometry.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/map.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/mcmc.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/optics.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/pawley.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/peak_batch.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/peak_fit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/peak_integrate.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/peak_multifit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/peak_track.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/peaks.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/peakshapes.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/plotting.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/processing.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/qspace.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/reductions.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/reductions_fft.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/refl_sld_presets.json +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/reflectivity.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/registry.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/relaxation.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/report.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/report_emit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/resample.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/rsm.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/rsm_analyze.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/semiconductor.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/sld.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/sld_formula.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/spectral.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/statplots.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_anova2.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_anova_ext.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_dist.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_glm.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_multivar.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_roc.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_survival.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/stats_tests.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/substrates.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/superconductor.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/surface_fit.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/surface_models.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/thermal.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/thin_film.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/unit_convert.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/units.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/vacuum.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/calc/xray.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/cli.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/datastruct.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/__init__.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/_hdf5_layout.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/_jcamp_asdf.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/_xrdml_scan.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/base.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/bruker_brml.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/bruker_raw.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/cif.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/consolidated.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/delimited.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/excel.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/hdf5.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/import_filters.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/import_preview.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/jcamp.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/lakeshore.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/ncnr.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/netcdf.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/opus.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_com.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/__init__.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/annotation_marks.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/container.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/figure_geometry.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/figure_text.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/figures_opju.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/notes.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opj.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opj_shapes.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_axis_real_form.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_axis_specimen_form.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_codec.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_curves.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_curves_allcols.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_figure_curves.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_figure_text.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/opju_reports.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/origin_richtext.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/preview.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/tree.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/tree_opju.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/windows.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/windows_opju.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/writer.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/writer_blocks.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/qd.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/refl1d.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/registry.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/report_export.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/rigaku.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/sims.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/spc.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/xrd_csv.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/xrdml.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/jobs.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/plugins/__init__.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/plugins/contract.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/plugins/loader.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/plugins/steps.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/__init__.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/_bookcache.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/_export_common.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/_payload.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/_uploadcache.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/aggregate.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/baseline.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/books.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/calc.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/corrections.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/crystallography.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/diffusion.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/electrical.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/electrochemistry.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/export.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/export_facets.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/fitting_bumps.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/import_template.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/import_wizard.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/jobs_api.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/magnetic.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/magnetometry.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/optics.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/peaks.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/plot.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/reductions.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/reference.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/reflectivity.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/report_export.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/rsm.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/samples.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/semiconductor.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/sld.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/spectral.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/statplots.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/stats.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/stats_design.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/substrates.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/superconductor.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/thermal.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/thin_film.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/vacuum.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/routes/xray.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/samples/demo_vsm.csv +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/server_launch.py +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/web/assets/JetBrainsMono-Bold-CUogYd9I.woff2 +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/web/assets/JetBrainsMono-Regular-CA-Os4ii.woff2 +0 -0
- {quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/web/loading.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantized-lab
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Plot, analyze, and publish lab data - magnetometry, XRD, reflectometry - with OriginPro file interop, golden-verified analysis, and reproducible pipelines.
|
|
5
5
|
Project-URL: Homepage, https://github.com/pquarterman17/quantized
|
|
6
6
|
Project-URL: Documentation, https://github.com/pquarterman17/quantized/wiki
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "quantized-lab"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.0"
|
|
4
4
|
description = "Plot, analyze, and publish lab data - magnetometry, XRD, reflectometry - with OriginPro file interop, golden-verified analysis, and reproducible pipelines."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -27,7 +27,7 @@ from quantized.calc.figure_labels import safe_mathtext_label # noqa: E402
|
|
|
27
27
|
from quantized.calc.figure_overrides import _apply_overrides, _validate_overrides # noqa: E402
|
|
28
28
|
from quantized.calc.figure_scale import apply_axis_scale, resolve_axis_scale # noqa: E402
|
|
29
29
|
from quantized.calc.figure_styles import FigureStyle, figure_style # noqa: E402
|
|
30
|
-
from quantized.calc.figure_ticks import apply_tick_formats # noqa: E402
|
|
30
|
+
from quantized.calc.figure_ticks import apply_tick_formats, apply_tick_steps # noqa: E402
|
|
31
31
|
|
|
32
32
|
__all__ = ["draw_series_axes", "render_figure", "style_rc"]
|
|
33
33
|
|
|
@@ -177,6 +177,8 @@ def draw_series_axes(
|
|
|
177
177
|
series_styles: Sequence[Mapping[str, Any] | None] | None = None,
|
|
178
178
|
x_fmt: Mapping[str, Any] | None = None,
|
|
179
179
|
y_fmt: Mapping[str, Any] | None = None,
|
|
180
|
+
x_step: float | None = None,
|
|
181
|
+
y_step: float | None = None,
|
|
180
182
|
) -> list[Any]:
|
|
181
183
|
"""Plot ``series`` into an EXISTING Axes: lines, scales, labels, spines,
|
|
182
184
|
legend, grid, and the per-figure override sweep (:func:`_apply_overrides`).
|
|
@@ -221,8 +223,11 @@ def draw_series_axes(
|
|
|
221
223
|
(line,) = ax.plot(xv, yv, label=label, **kw)
|
|
222
224
|
_apply_fill(ax, xv, yv, series, i, spec, line.get_color())
|
|
223
225
|
artists.append(line)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
+
resolved_x_scale = resolve_axis_scale(x_scale, x_log)
|
|
227
|
+
resolved_y_scale = resolve_axis_scale(y_scale, y_log)
|
|
228
|
+
apply_axis_scale(ax, "x", resolved_x_scale)
|
|
229
|
+
apply_axis_scale(ax, "y", resolved_y_scale)
|
|
230
|
+
apply_tick_steps(ax, x_step, y_step, resolved_x_scale, resolved_y_scale)
|
|
226
231
|
apply_tick_formats(ax, x_fmt, y_fmt)
|
|
227
232
|
if title:
|
|
228
233
|
ax.set_title(title)
|
|
@@ -242,9 +247,10 @@ def draw_series_axes(
|
|
|
242
247
|
loc=st.legend_location,
|
|
243
248
|
)
|
|
244
249
|
if st.grid_alpha > 0:
|
|
245
|
-
ax.grid(True, alpha=st.grid_alpha)
|
|
250
|
+
ax.grid(True, which="major", alpha=st.grid_alpha)
|
|
251
|
+
ax.grid(True, which="minor", alpha=st.grid_alpha * 0.4)
|
|
246
252
|
else:
|
|
247
|
-
ax.grid(False)
|
|
253
|
+
ax.grid(False, which="both")
|
|
248
254
|
_apply_overrides(fig, ax, st, ov, n_series=len(series))
|
|
249
255
|
return artists
|
|
250
256
|
|
|
@@ -270,6 +276,8 @@ def _render_impl(
|
|
|
270
276
|
collect_map: bool = False,
|
|
271
277
|
x_fmt: Mapping[str, Any] | None = None,
|
|
272
278
|
y_fmt: Mapping[str, Any] | None = None,
|
|
279
|
+
x_step: float | None = None,
|
|
280
|
+
y_step: float | None = None,
|
|
273
281
|
) -> bytes | dict[str, Any]:
|
|
274
282
|
"""Render ``series`` (each ``(label, y)``) against ``x`` to image bytes.
|
|
275
283
|
|
|
@@ -352,6 +360,8 @@ def _render_impl(
|
|
|
352
360
|
series_styles=series_styles,
|
|
353
361
|
x_fmt=x_fmt,
|
|
354
362
|
y_fmt=y_fmt,
|
|
363
|
+
x_step=x_step,
|
|
364
|
+
y_step=y_step,
|
|
355
365
|
)
|
|
356
366
|
fig, ax = plt.subplots(figsize=figsize)
|
|
357
367
|
try:
|
|
@@ -372,6 +382,8 @@ def _render_impl(
|
|
|
372
382
|
series_styles=series_styles,
|
|
373
383
|
x_fmt=x_fmt,
|
|
374
384
|
y_fmt=y_fmt,
|
|
385
|
+
x_step=x_step,
|
|
386
|
+
y_step=y_step,
|
|
375
387
|
)
|
|
376
388
|
if not ov.get("margins"):
|
|
377
389
|
fig.tight_layout()
|
|
@@ -40,7 +40,7 @@ from numpy.typing import ArrayLike, NDArray # noqa: E402
|
|
|
40
40
|
|
|
41
41
|
from quantized.calc.figure import _plot_kwargs # noqa: E402
|
|
42
42
|
from quantized.calc.figure_scale import apply_axis_scale, resolve_axis_scale # noqa: E402
|
|
43
|
-
from quantized.calc.figure_ticks import apply_tick_formats # noqa: E402
|
|
43
|
+
from quantized.calc.figure_ticks import apply_tick_formats, apply_tick_steps # noqa: E402
|
|
44
44
|
|
|
45
45
|
__all__ = ["render_breaks_impl"]
|
|
46
46
|
|
|
@@ -65,6 +65,8 @@ def render_breaks_impl(
|
|
|
65
65
|
series_styles: Sequence[Mapping[str, Any] | None] | None,
|
|
66
66
|
x_fmt: Mapping[str, Any] | None = None,
|
|
67
67
|
y_fmt: Mapping[str, Any] | None = None,
|
|
68
|
+
x_step: float | None = None,
|
|
69
|
+
y_step: float | None = None,
|
|
68
70
|
) -> bytes:
|
|
69
71
|
"""Render ``series`` against ``x`` with the x-axis elided over each
|
|
70
72
|
``[lo, hi]`` pair in ``breaks`` (already sorted/validated non-overlapping
|
|
@@ -100,8 +102,11 @@ def render_breaks_impl(
|
|
|
100
102
|
ax.plot(x, np.asarray(y, dtype=float), label=label, **kw)
|
|
101
103
|
lo, hi = bounds[i]
|
|
102
104
|
ax.set_xlim(lo, hi)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
resolved_x_scale = resolve_axis_scale(x_scale, x_log)
|
|
106
|
+
resolved_y_scale = resolve_axis_scale(y_scale, y_log)
|
|
107
|
+
apply_axis_scale(ax, "x", resolved_x_scale)
|
|
108
|
+
apply_axis_scale(ax, "y", resolved_y_scale)
|
|
109
|
+
apply_tick_steps(ax, x_step, y_step, resolved_x_scale, resolved_y_scale)
|
|
105
110
|
apply_tick_formats(ax, x_fmt, y_fmt)
|
|
106
111
|
if i == 0:
|
|
107
112
|
handles, labels_out = ax.get_legend_handles_labels()
|
|
@@ -113,7 +118,8 @@ def render_breaks_impl(
|
|
|
113
118
|
if not st.box_on:
|
|
114
119
|
ax.spines["top"].set_visible(False)
|
|
115
120
|
if st.grid_alpha > 0:
|
|
116
|
-
ax.grid(True, alpha=st.grid_alpha)
|
|
121
|
+
ax.grid(True, which="major", alpha=st.grid_alpha)
|
|
122
|
+
ax.grid(True, which="minor", alpha=st.grid_alpha * 0.4)
|
|
117
123
|
|
|
118
124
|
# Diagonal break glyphs (matplotlib's standard broken-axis recipe):
|
|
119
125
|
# short strokes angled across each seam, on both the outgoing panel's
|
|
@@ -13,17 +13,21 @@ error an export.
|
|
|
13
13
|
|
|
14
14
|
Screen/export WYSIWYG (bug-hunt fix): the frontend's parser
|
|
15
15
|
(``frontend/src/lib/richtext.ts``) only accepts a STRICT SUBSET of
|
|
16
|
-
mathtext -- Greek letters,
|
|
17
|
-
\\
|
|
18
|
-
|
|
16
|
+
mathtext -- Greek letters, the symbol/relation/arrow commands in
|
|
17
|
+
``SUPPORTED_MATHTEXT_COMMANDS`` (``\\AA \\times \\leq \\approx \\infty
|
|
18
|
+
\\rightarrow`` ...), the structural commands ``\\frac \\sqrt`` (MAIN #28),
|
|
19
|
+
``\\, \\mathrm \\mathit``, and literal Unicode -- and rejects everything else
|
|
20
|
+
(e.g. ``\\hat``, ``\\vec``, ``\\overline``) as invalid markup, rendering it
|
|
19
21
|
literally on screen. Raw matplotlib accepts a much larger command set, so
|
|
20
|
-
without this gate a label like ``$\\
|
|
21
|
-
("Invalid markup") on screen but an actual
|
|
22
|
-
a WYSIWYG violation. `_uses_only_supported_commands`
|
|
23
|
-
command set the frontend parser accepts and rejects
|
|
24
|
-
any ``$...$`` region using a command outside it,
|
|
25
|
-
parse below ever runs. Keep
|
|
26
|
-
richtext.ts's
|
|
22
|
+
without this gate a label like ``$\\vec{v}$`` would render literal text
|
|
23
|
+
("Invalid markup") on screen but an actual arrow-accented vector in the
|
|
24
|
+
PDF/SVG export -- a WYSIWYG violation. `_uses_only_supported_commands`
|
|
25
|
+
enumerates the SAME command set the frontend parser accepts and rejects
|
|
26
|
+
(falls back to literal) any ``$...$`` region using a command outside it,
|
|
27
|
+
BEFORE the matplotlib trial parse below ever runs. Keep
|
|
28
|
+
`SUPPORTED_MATHTEXT_COMMANDS` in sync with richtext.ts's
|
|
29
|
+
``GREEK``/``SYMBOLS``/``BIGOPS`` tables + ``mathrm``/``mathit``/``frac``/
|
|
30
|
+
``sqrt`` handling.
|
|
27
31
|
|
|
28
32
|
Pure layer: string in -> string out. matplotlib is imported lazily (same
|
|
29
33
|
convention as the figure modules -- the heavy import is paid only on export).
|
|
@@ -60,12 +64,40 @@ _GREEK_COMMANDS = frozenset(
|
|
|
60
64
|
"Phi", "Psi", "Omega",
|
|
61
65
|
}
|
|
62
66
|
)
|
|
63
|
-
# Mirrors richtext.ts's `SYMBOLS` table (non-Greek symbol commands)
|
|
64
|
-
|
|
67
|
+
# Mirrors richtext.ts's `SYMBOLS` table (non-Greek symbol commands): base
|
|
68
|
+
# symbols + relations, analysis/geometry glyphs, and arrows. Each is verified
|
|
69
|
+
# to parse in matplotlib mathtext (tools probe), so a label using them renders
|
|
70
|
+
# identically on screen (richtext.ts glyph) and in export (mathtext glyph).
|
|
71
|
+
_SYMBOL_COMMANDS = frozenset(
|
|
72
|
+
{
|
|
73
|
+
"AA", "circ", "times", "cdot", "pm", "mp", "div", "prime",
|
|
74
|
+
# relations
|
|
75
|
+
"leq", "geq", "neq", "approx", "equiv", "sim", "propto", "ll", "gg",
|
|
76
|
+
# analysis / geometry
|
|
77
|
+
"infty", "partial", "nabla", "perp", "parallel", "angle",
|
|
78
|
+
"cdots", "ldots", "dots",
|
|
79
|
+
# arrows
|
|
80
|
+
"rightarrow", "to", "leftarrow", "leftrightarrow", "Rightarrow",
|
|
81
|
+
}
|
|
82
|
+
)
|
|
65
83
|
# Mirrors richtext.ts's `\mathrm{...}` / `\mathit{...}` style-group handling.
|
|
66
84
|
_STYLE_COMMANDS = frozenset({"mathrm", "mathit"})
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
# Mirrors richtext.ts's structural nodes: `\frac{a}{b}`, `\sqrt{x}`,
|
|
86
|
+
# `\sqrt[n]{x}` (MAIN #28). matplotlib renders these natively; the frontend
|
|
87
|
+
# canvas + DOM renderers draw the matching stacked/radical layout.
|
|
88
|
+
_STRUCTURE_COMMANDS = frozenset({"frac", "sqrt"})
|
|
89
|
+
# Mirrors richtext.ts's `BIGOPS` table (large operators). matplotlib stacks
|
|
90
|
+
# \sum/\prod limits over/under and side-scripts \int/\oint inline -- the
|
|
91
|
+
# frontend canvas renderer matches that limits placement.
|
|
92
|
+
_BIGOP_COMMANDS = frozenset({"sum", "prod", "int", "oint"})
|
|
93
|
+
|
|
94
|
+
SUPPORTED_MATHTEXT_COMMANDS = (
|
|
95
|
+
_GREEK_COMMANDS
|
|
96
|
+
| _SYMBOL_COMMANDS
|
|
97
|
+
| _STYLE_COMMANDS
|
|
98
|
+
| _STRUCTURE_COMMANDS
|
|
99
|
+
| _BIGOP_COMMANDS
|
|
100
|
+
)
|
|
69
101
|
|
|
70
102
|
|
|
71
103
|
def _uses_only_supported_commands(label: str) -> bool:
|
|
@@ -118,7 +118,7 @@ def _apply_overrides(
|
|
|
118
118
|
)
|
|
119
119
|
|
|
120
120
|
if "grid" in ov:
|
|
121
|
-
ax.grid(bool(ov["grid"]), alpha=st.grid_alpha or 0.3)
|
|
121
|
+
ax.grid(bool(ov["grid"]), which="both", alpha=st.grid_alpha or 0.3)
|
|
122
122
|
|
|
123
123
|
for ann in ov.get("annotations", []):
|
|
124
124
|
# MAIN #18: a per-annotation `size` (the pointer tool's corner-handle
|
|
@@ -115,6 +115,8 @@ class PagePanel:
|
|
|
115
115
|
# (a figure page composes several independently-configured plot views).
|
|
116
116
|
x_fmt: Mapping[str, Any] | None = None
|
|
117
117
|
y_fmt: Mapping[str, Any] | None = None
|
|
118
|
+
x_step: float | None = None
|
|
119
|
+
y_step: float | None = None
|
|
118
120
|
series_styles: Sequence[Mapping[str, Any] | None] | None = None
|
|
119
121
|
overrides: Mapping[str, Any] | None = None
|
|
120
122
|
label: str | None = None
|
|
@@ -256,6 +258,8 @@ def render_figure_page(
|
|
|
256
258
|
series_styles=p.series_styles,
|
|
257
259
|
x_fmt=p.x_fmt,
|
|
258
260
|
y_fmt=p.y_fmt,
|
|
261
|
+
x_step=p.x_step,
|
|
262
|
+
y_step=p.y_step,
|
|
259
263
|
)
|
|
260
264
|
text = p.label if p.label is not None else panel_label(idx, label_format)
|
|
261
265
|
_place_label(ax, safe_mathtext_label(text), label_pos, st)
|
|
@@ -21,7 +21,7 @@ from __future__ import annotations
|
|
|
21
21
|
from typing import Any
|
|
22
22
|
|
|
23
23
|
import numpy as np
|
|
24
|
-
from matplotlib.ticker import Locator
|
|
24
|
+
from matplotlib.ticker import Locator, LogLocator, NullFormatter
|
|
25
25
|
from numpy.typing import NDArray
|
|
26
26
|
|
|
27
27
|
__all__ = ["apply_axis_scale", "reciprocal_tick_values", "resolve_axis_scale"]
|
|
@@ -115,6 +115,9 @@ def apply_axis_scale(ax: Any, axis: str, scale: str) -> None:
|
|
|
115
115
|
set_scale = ax.set_xscale if axis == "x" else ax.set_yscale
|
|
116
116
|
if scale == "log":
|
|
117
117
|
set_scale("log")
|
|
118
|
+
target = ax.xaxis if axis == "x" else ax.yaxis
|
|
119
|
+
target.set_minor_locator(LogLocator(base=10.0, subs=tuple(range(2, 10))))
|
|
120
|
+
target.set_minor_formatter(NullFormatter())
|
|
118
121
|
return
|
|
119
122
|
if scale == "reciprocal":
|
|
120
123
|
set_scale("function", functions=(_reciprocal, _reciprocal))
|
|
@@ -36,9 +36,9 @@ import math
|
|
|
36
36
|
from collections.abc import Mapping
|
|
37
37
|
from typing import Any
|
|
38
38
|
|
|
39
|
-
from matplotlib.ticker import Formatter
|
|
39
|
+
from matplotlib.ticker import Formatter, MultipleLocator
|
|
40
40
|
|
|
41
|
-
__all__ = ["apply_tick_formats", "axis_tick_formatter"]
|
|
41
|
+
__all__ = ["apply_tick_formats", "apply_tick_steps", "axis_tick_formatter"]
|
|
42
42
|
|
|
43
43
|
_MODES = ("fixed", "sci", "eng")
|
|
44
44
|
|
|
@@ -215,3 +215,32 @@ def apply_tick_formats(
|
|
|
215
215
|
yf = axis_tick_formatter(y_fmt)
|
|
216
216
|
if yf is not None:
|
|
217
217
|
ax.yaxis.set_major_formatter(yf)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def apply_tick_steps(
|
|
221
|
+
ax: Any,
|
|
222
|
+
x_step: float | None,
|
|
223
|
+
y_step: float | None,
|
|
224
|
+
x_scale: str,
|
|
225
|
+
y_scale: str,
|
|
226
|
+
) -> None:
|
|
227
|
+
"""Apply saved major-tick increments to linear axes only.
|
|
228
|
+
|
|
229
|
+
Origin's decoded ``from/to/step`` triples carry a linear increment. Log
|
|
230
|
+
axes retain their scale-specific locator; reciprocal axes have their own
|
|
231
|
+
locator. Invalid or absent increments leave matplotlib's locator intact.
|
|
232
|
+
``MultipleLocator`` anchors ticks at integer multiples of the step, the
|
|
233
|
+
same convention as the interactive ``fixedLinearAxisSplits`` helper.
|
|
234
|
+
"""
|
|
235
|
+
def apply_one(axis: Any, limits: tuple[float, float], step: float | None) -> None:
|
|
236
|
+
if step is None or not math.isfinite(step) or step <= 0:
|
|
237
|
+
return
|
|
238
|
+
span = abs(float(limits[1]) - float(limits[0]))
|
|
239
|
+
if not math.isfinite(span) or span / step > 1000:
|
|
240
|
+
return
|
|
241
|
+
axis.set_major_locator(MultipleLocator(float(step)))
|
|
242
|
+
|
|
243
|
+
if x_scale == "linear":
|
|
244
|
+
apply_one(ax.xaxis, ax.get_xlim(), x_step)
|
|
245
|
+
if y_scale == "linear":
|
|
246
|
+
apply_one(ax.yaxis, ax.get_ylim(), y_step)
|
|
@@ -39,6 +39,12 @@ AbortFn = Callable[[], bool]
|
|
|
39
39
|
#: Supported bumps engine ids (bumps.fitters FITTERS ids).
|
|
40
40
|
BUMPS_ENGINES = ("amoeba", "lm", "de", "dream")
|
|
41
41
|
|
|
42
|
+
#: Gelman-Rubin R-hat at/above which DREAM chains are considered NOT converged
|
|
43
|
+
#: (the widely-used 1.1 threshold; < ~1.01 is "well mixed"). Surfaced so the UI
|
|
44
|
+
#: never presents posterior intervals as trustworthy when the chains haven't
|
|
45
|
+
#: mixed (audit P1 #2).
|
|
46
|
+
RHAT_THRESHOLD = 1.1
|
|
47
|
+
|
|
42
48
|
_INSTALL_HINT = (
|
|
43
49
|
"bumps is not installed - the optional fit engine needs "
|
|
44
50
|
"'pip install quantized[bumps]' (or: uv sync --extra bumps)"
|
|
@@ -154,9 +160,11 @@ def fit_bumps(
|
|
|
154
160
|
saved equation-builder models) or a raw ``f(x, p)`` callable. ``engine``
|
|
155
161
|
is one of ``BUMPS_ENGINES``: amoeba / lm / de report Hessian-derived
|
|
156
162
|
uncertainties (``uncertainty_kind='hessian'``); dream samples the
|
|
157
|
-
posterior (``uncertainty_kind='posterior'``) and additionally returns
|
|
158
|
-
per-parameter medians
|
|
159
|
-
|
|
163
|
+
posterior (``uncertainty_kind='posterior'``) and additionally returns, in
|
|
164
|
+
``result['posterior']``, per-parameter medians, central 68% intervals,
|
|
165
|
+
``n_draws``, ``nChains``, and the convergence diagnostics ``rHat`` (per
|
|
166
|
+
parameter), ``rHatMax``, and ``converged`` (Gelman-Rubin, audit P1 #2) —
|
|
167
|
+
plus the raw draw matrix when ``return_samples`` (corner-plot food).
|
|
160
168
|
|
|
161
169
|
``samples`` / ``burn`` / ``pop`` tune dream only. ``progress_callback``
|
|
162
170
|
(fraction in [0, 1)) and ``abort_check`` (True -> stop sampling early)
|
|
@@ -243,15 +251,33 @@ def fit_bumps(
|
|
|
243
251
|
}
|
|
244
252
|
|
|
245
253
|
if engine == "dream":
|
|
246
|
-
|
|
254
|
+
state = driver.fitter.state
|
|
255
|
+
draw = state.draw()
|
|
247
256
|
pts = np.asarray(draw.points, dtype=float)[:, order]
|
|
248
257
|
lo68, med, hi68 = (
|
|
249
258
|
np.asarray(np.percentile(pts, q, axis=0), dtype=float) for q in (16.0, 50.0, 84.0)
|
|
250
259
|
)
|
|
260
|
+
# Convergence diagnostics (audit P1 #2): the Gelman-Rubin potential
|
|
261
|
+
# scale reduction factor (R-hat) per parameter, so posterior intervals
|
|
262
|
+
# are never presented as trustworthy when the chains haven't mixed.
|
|
263
|
+
# (bumps also warns "Did not converge!" via a separate K-S test; R-hat
|
|
264
|
+
# is the standard, interpretable metric. bumps exposes no clean
|
|
265
|
+
# effective-sample-size, so ESS isn't faked from cross-chain draws.)
|
|
266
|
+
try:
|
|
267
|
+
rhat_all = np.asarray(state.gelman(), dtype=float)
|
|
268
|
+
rhat = [float(rhat_all[i]) for i in order]
|
|
269
|
+
rhat_max = float(np.max(rhat_all)) if rhat_all.size else float("nan")
|
|
270
|
+
except Exception: # noqa: BLE001 — aborted/degenerate state (cf. stderr above)
|
|
271
|
+
rhat = [float("nan")] * n_params
|
|
272
|
+
rhat_max = float("nan")
|
|
251
273
|
result["posterior"] = {
|
|
252
274
|
"medians": [float(v) for v in med],
|
|
253
275
|
"interval68": [[float(a), float(b)] for a, b in zip(lo68, hi68, strict=True)],
|
|
254
276
|
"n_draws": int(pts.shape[0]),
|
|
277
|
+
"rHat": rhat,
|
|
278
|
+
"rHatMax": rhat_max,
|
|
279
|
+
"converged": bool(math.isfinite(rhat_max) and rhat_max < RHAT_THRESHOLD),
|
|
280
|
+
"nChains": int(getattr(state, "Npop", pop * n_params)),
|
|
255
281
|
}
|
|
256
282
|
if return_samples:
|
|
257
283
|
result["samples"] = pts
|
|
@@ -31,7 +31,7 @@ from numpy.typing import ArrayLike, NDArray
|
|
|
31
31
|
from quantized.calc.fit_autoguess import auto_guess
|
|
32
32
|
from quantized.calc.fit_equation import default_guesses, equation_model
|
|
33
33
|
from quantized.calc.fit_models import FIT_MODELS
|
|
34
|
-
from quantized.calc.fitting import curve_fit
|
|
34
|
+
from quantized.calc.fitting import curve_fit, weights_from_dy
|
|
35
35
|
|
|
36
36
|
__all__ = ["aicc_from_aic", "default_candidates", "scan_models"]
|
|
37
37
|
|
|
@@ -210,12 +210,7 @@ def scan_models(
|
|
|
210
210
|
|
|
211
211
|
weights: NDArray[np.float64] | None = None
|
|
212
212
|
if dy is not None:
|
|
213
|
-
|
|
214
|
-
if dyv.size != n:
|
|
215
|
-
raise ValueError("dy must have the same length as x")
|
|
216
|
-
if not bool(np.all(np.isfinite(dyv))) or bool(np.any(dyv <= 0)):
|
|
217
|
-
raise ValueError("dy entries must be finite and > 0")
|
|
218
|
-
weights = np.asarray(1.0 / dyv**2, dtype=float)
|
|
213
|
+
weights = weights_from_dy(dy, n)
|
|
219
214
|
|
|
220
215
|
names = list(models) if models is not None else default_candidates(n)
|
|
221
216
|
entries: list[dict[str, Any]] = []
|
|
@@ -83,6 +83,23 @@ def _numerical_hessian(
|
|
|
83
83
|
return hmat
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def weights_from_dy(dy: ArrayLike, n: int) -> NDArray[np.float64]:
|
|
87
|
+
"""Per-point 1-sigma errors ``dy`` -> least-squares weights ``1/dy**2``.
|
|
88
|
+
|
|
89
|
+
The single canonical error->weight convention shared by every fit entry
|
|
90
|
+
point (``/fit``, ``/equation/fit``, ``/scan``) so "use the plotted error
|
|
91
|
+
column" means the same thing everywhere. Validates length and that every
|
|
92
|
+
entry is finite and strictly positive (a zero error would demand infinite
|
|
93
|
+
weight).
|
|
94
|
+
"""
|
|
95
|
+
dyv = np.asarray(dy, dtype=float).ravel()
|
|
96
|
+
if dyv.size != n:
|
|
97
|
+
raise ValueError("dy must have the same length as x")
|
|
98
|
+
if not bool(np.all(np.isfinite(dyv))) or bool(np.any(dyv <= 0)):
|
|
99
|
+
raise ValueError("dy entries must be finite and > 0")
|
|
100
|
+
return np.asarray(1.0 / dyv**2, dtype=float)
|
|
101
|
+
|
|
102
|
+
|
|
86
103
|
def curve_fit(
|
|
87
104
|
x: ArrayLike,
|
|
88
105
|
y: ArrayLike,
|
{quantized_lab-0.9.0 → quantized_lab-0.10.0}/src/quantized/io/origin_project/curve_style_color.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Per-curve visual style (color / symbol kind /
|
|
1
|
+
"""Per-curve visual style (color / symbol kind / plot type) for Origin
|
|
2
2
|
graph curves -- shared by BOTH containers (solved 2026-07-06 against the
|
|
3
3
|
4-stem ``curve_style.json`` oracle: ``hc2convert`` (.opj), ``Hc2 data``,
|
|
4
4
|
``RockingCurve``, ``UnpolPlots`` (.opju)).
|
|
@@ -33,11 +33,13 @@ variants -- the 4-byte size difference sits past offset 492):
|
|
|
33
33
|
|
|
34
34
|
* offset 23 -- **symbol kind** (Origin's symbol-gallery index; 0 = none).
|
|
35
35
|
Oracle-verified 49/49 (.opj) + 43/43 (.opju).
|
|
36
|
-
* offset 76 -- the **
|
|
36
|
+
* offset 76 -- the **plot-type byte** (``0xc8`` line / ``0xc9``
|
|
37
37
|
scatter -- the very byte ``opju_codec.curve_plot_style``'s
|
|
38
38
|
``8f 01 <style> 83`` tag writes: that tag is this record's sparse chunk
|
|
39
|
-
for offset 76). ``0xca``
|
|
40
|
-
|
|
39
|
+
for offset 76). ``0xca`` is line + symbol: OriginLab's official ``plotxy``
|
|
40
|
+
reference maps plot ids 200/201/202 to line/scatter/line+symbol, matching
|
|
41
|
+
these bytes exactly. ``0xe7``/``0xe9`` (PNR corpus) remain unmapped --
|
|
42
|
+
omitted, never guessed.
|
|
41
43
|
* offsets 302-305 -- a constant ``0xFFFFFFF7`` "auto" sentinel u32.
|
|
42
44
|
* offsets 306-309 -- the **symbol color** (ocolor u32 LE), terminator
|
|
43
45
|
``0xff`` at 310 (the validity gate: hc2convert's 24 non-oracle anchors
|
|
@@ -160,9 +162,11 @@ _SYMBOL_SHAPES = {
|
|
|
160
162
|
8: "star",
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
# Same byte table opju_codec._STYLE_BYTES validated (fig_pairs oracle)
|
|
164
|
-
# 0xca
|
|
165
|
-
|
|
165
|
+
# Same byte table opju_codec._STYLE_BYTES validated (fig_pairs oracle).
|
|
166
|
+
# 0xca = Origin plot:=202, officially documented as line + symbol:
|
|
167
|
+
# https://docs.originlab.com/x-function/ref/plotxy/
|
|
168
|
+
# 0xe7/0xe9 remain unmapped -- never guessed.
|
|
169
|
+
_CONNECT_STYLE = {0xC8: "line", 0xC9: "scatter", 0xCA: "line_symbol"}
|
|
166
170
|
|
|
167
171
|
# ── auto/increment colours (2026-07-06, §13.2 #2) ────────────────────────────
|
|
168
172
|
#
|
|
@@ -241,7 +245,8 @@ def style_fields(record: bytes) -> dict[str, str | float]:
|
|
|
241
245
|
"""Decoded per-curve style keys from one curve-anchor record (raw ``.opj``
|
|
242
246
|
payload or :func:`opju_style_record` reconstruction): any of ``color``
|
|
243
247
|
(``"#RRGGBB"``), ``symbol`` (marker shape name), ``style``
|
|
244
|
-
(``"line"``/``"scatter"``), ``lineWidth`` /
|
|
248
|
+
(``"line"``/``"scatter"``/``"line_symbol"``), ``lineWidth`` /
|
|
249
|
+
``symbolSize`` (points, the
|
|
245
250
|
1/500-pt u16 fields at offsets 21/25 — 92/92 oracle-exact). Undecodable
|
|
246
251
|
or implausible fields are simply absent, never defaulted."""
|
|
247
252
|
out: dict[str, str | float] = {}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Loss-aware fidelity assessment for decoded Origin graph records.
|
|
2
|
+
|
|
3
|
+
The binary decoders intentionally return every record they can identify. This
|
|
4
|
+
module builds the presentation contract separately: actionable figures gain a
|
|
5
|
+
conservative per-figure assessment, while filtered/internal records survive as
|
|
6
|
+
diagnostic summaries in a versioned project manifest. Nothing here mutates or
|
|
7
|
+
reduces the pure decoder output.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Iterable
|
|
13
|
+
from typing import Any, Literal, TypedDict
|
|
14
|
+
|
|
15
|
+
from quantized.io.origin_project.graph_preview import PreviewDiagnostic
|
|
16
|
+
|
|
17
|
+
__all__ = ["assess_origin_figures", "origin_figure_decode_failure"]
|
|
18
|
+
|
|
19
|
+
FidelityStatus = Literal["exact", "best_effort", "reference_only", "unresolved"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FigureFidelity(TypedDict):
|
|
23
|
+
status: FidelityStatus
|
|
24
|
+
recovered: list[str]
|
|
25
|
+
omissions: list[str]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FilteredFigure(TypedDict):
|
|
29
|
+
index: int
|
|
30
|
+
name: str
|
|
31
|
+
layer: int | None
|
|
32
|
+
reason: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class OriginFidelityManifest(TypedDict):
|
|
36
|
+
version: int
|
|
37
|
+
container: Literal["opj", "opju"]
|
|
38
|
+
status: FidelityStatus
|
|
39
|
+
graph_records_total: int
|
|
40
|
+
graph_records_actionable: int
|
|
41
|
+
graph_records_filtered: int
|
|
42
|
+
omissions: list[str]
|
|
43
|
+
filtered_figures: list[FilteredFigure]
|
|
44
|
+
preview_diagnostics: list[PreviewDiagnostic]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def origin_figure_decode_failure(
|
|
48
|
+
*, container: Literal["opj", "opju"]
|
|
49
|
+
) -> OriginFidelityManifest:
|
|
50
|
+
"""Manifest used when optional graph decode fails but workbooks survive."""
|
|
51
|
+
return {
|
|
52
|
+
"version": 1,
|
|
53
|
+
"container": container,
|
|
54
|
+
"status": "unresolved",
|
|
55
|
+
"graph_records_total": 0,
|
|
56
|
+
"graph_records_actionable": 0,
|
|
57
|
+
"graph_records_filtered": 0,
|
|
58
|
+
"omissions": ["figure_decode_error"],
|
|
59
|
+
"filtered_figures": [],
|
|
60
|
+
"preview_diagnostics": [],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
_ALWAYS_OMITTED = [
|
|
65
|
+
"graphic_objects",
|
|
66
|
+
"rich_text_run_formatting",
|
|
67
|
+
"advanced_axis_types",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _normalized_sources(source_names: Iterable[str]) -> tuple[str, ...]:
|
|
72
|
+
return tuple(s.strip().lower() for s in source_names if s.strip())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _source_resolves(fig: dict[str, Any], sources: tuple[str, ...]) -> bool:
|
|
76
|
+
hint = str(fig.get("source_hint") or "").strip().lower()
|
|
77
|
+
return bool(hint) and any(hint == s or hint in s or s in hint for s in sources)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _filter_reason(fig: dict[str, Any], sources: tuple[str, ...]) -> str | None:
|
|
81
|
+
if fig.get("curves") or _source_resolves(fig, sources):
|
|
82
|
+
return None
|
|
83
|
+
hint = str(fig.get("source_hint") or "").strip()
|
|
84
|
+
return (
|
|
85
|
+
f'source hint "{hint}" did not match an imported workbook'
|
|
86
|
+
if hint
|
|
87
|
+
else "no bound curves or source hint"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _recovered_groups(fig: dict[str, Any]) -> list[str]:
|
|
92
|
+
recovered = ["axis_ranges", "axis_scales"]
|
|
93
|
+
if fig.get("saved_preview"):
|
|
94
|
+
recovered.append("saved_graph_preview")
|
|
95
|
+
if fig.get("curves"):
|
|
96
|
+
recovered.extend(("curve_bindings", "curve_order"))
|
|
97
|
+
if fig.get("x_title") is not None or fig.get("y_title") is not None:
|
|
98
|
+
recovered.append("axis_titles")
|
|
99
|
+
if fig.get("legend_labels"):
|
|
100
|
+
recovered.append("legend_labels")
|
|
101
|
+
if fig.get("annotation_marks"):
|
|
102
|
+
recovered.append("positioned_annotations")
|
|
103
|
+
if fig.get("region_shades"):
|
|
104
|
+
recovered.append("region_shades")
|
|
105
|
+
if fig.get("frame") and fig.get("page"):
|
|
106
|
+
recovered.append("layer_geometry")
|
|
107
|
+
curves = fig.get("curves")
|
|
108
|
+
if isinstance(curves, list) and curves:
|
|
109
|
+
if any(isinstance(c, dict) and c.get("style") for c in curves):
|
|
110
|
+
recovered.append("curve_style")
|
|
111
|
+
if any(isinstance(c, dict) and c.get("color") for c in curves):
|
|
112
|
+
recovered.append("curve_color")
|
|
113
|
+
if any(isinstance(c, dict) and c.get("symbol") for c in curves):
|
|
114
|
+
recovered.append("marker_shape")
|
|
115
|
+
if any(isinstance(c, dict) and c.get("lineWidth") for c in curves):
|
|
116
|
+
recovered.append("line_width")
|
|
117
|
+
if any(isinstance(c, dict) and c.get("symbolSize") for c in curves):
|
|
118
|
+
recovered.append("symbol_size")
|
|
119
|
+
if any(isinstance(c, dict) and c.get("style") == "line_symbol" for c in curves):
|
|
120
|
+
recovered.append("line_symbol_mode")
|
|
121
|
+
return recovered
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _figure_fidelity(fig: dict[str, Any]) -> FigureFidelity:
|
|
125
|
+
omissions = list(_ALWAYS_OMITTED)
|
|
126
|
+
if not fig.get("saved_preview"):
|
|
127
|
+
omissions.append("saved_graph_preview")
|
|
128
|
+
curves = fig.get("curves")
|
|
129
|
+
if not curves:
|
|
130
|
+
omissions.append("exact_curve_bindings")
|
|
131
|
+
elif isinstance(curves, list):
|
|
132
|
+
if any(not isinstance(c, dict) or not c.get("style") for c in curves):
|
|
133
|
+
omissions.append("some_curve_styles")
|
|
134
|
+
if any(not isinstance(c, dict) or not c.get("color") for c in curves):
|
|
135
|
+
omissions.append("some_curve_colors")
|
|
136
|
+
if not (fig.get("frame") and fig.get("page")):
|
|
137
|
+
omissions.append("layer_geometry")
|
|
138
|
+
return {
|
|
139
|
+
# No current decoder proves every visual property, so "exact" is
|
|
140
|
+
# deliberately reserved for a future preview/oracle comparison gate.
|
|
141
|
+
"status": "best_effort",
|
|
142
|
+
"recovered": _recovered_groups(fig),
|
|
143
|
+
"omissions": omissions,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def assess_origin_figures(
|
|
148
|
+
figures: list[dict[str, Any]],
|
|
149
|
+
*,
|
|
150
|
+
container: Literal["opj", "opju"],
|
|
151
|
+
source_names: Iterable[str] = (),
|
|
152
|
+
preview_diagnostics: list[PreviewDiagnostic] | None = None,
|
|
153
|
+
) -> tuple[list[dict[str, Any]], OriginFidelityManifest]:
|
|
154
|
+
"""Return actionable annotated figures plus a loss-aware project manifest."""
|
|
155
|
+
actionable: list[dict[str, Any]] = []
|
|
156
|
+
filtered: list[FilteredFigure] = []
|
|
157
|
+
project_omissions: set[str] = set()
|
|
158
|
+
sources = _normalized_sources(source_names)
|
|
159
|
+
for index, fig in enumerate(figures):
|
|
160
|
+
reason = _filter_reason(fig, sources)
|
|
161
|
+
if reason is not None:
|
|
162
|
+
layer = fig.get("layer")
|
|
163
|
+
filtered.append(
|
|
164
|
+
{
|
|
165
|
+
"index": index,
|
|
166
|
+
"name": str(fig.get("name") or "SYSTEM"),
|
|
167
|
+
"layer": int(layer) if isinstance(layer, int) else None,
|
|
168
|
+
"reason": reason,
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
continue
|
|
172
|
+
fidelity = _figure_fidelity(fig)
|
|
173
|
+
project_omissions.update(fidelity["omissions"])
|
|
174
|
+
actionable.append({**fig, "fidelity": fidelity})
|
|
175
|
+
|
|
176
|
+
if not figures:
|
|
177
|
+
project_omissions.add("no_graph_records")
|
|
178
|
+
elif filtered:
|
|
179
|
+
project_omissions.add("filtered_internal_graph_records")
|
|
180
|
+
status: FidelityStatus = "best_effort" if actionable else "unresolved"
|
|
181
|
+
manifest: OriginFidelityManifest = {
|
|
182
|
+
"version": 1,
|
|
183
|
+
"container": container,
|
|
184
|
+
"status": status,
|
|
185
|
+
"graph_records_total": len(figures),
|
|
186
|
+
"graph_records_actionable": len(actionable),
|
|
187
|
+
"graph_records_filtered": len(filtered),
|
|
188
|
+
"omissions": sorted(project_omissions),
|
|
189
|
+
"filtered_figures": filtered,
|
|
190
|
+
"preview_diagnostics": preview_diagnostics or [],
|
|
191
|
+
}
|
|
192
|
+
return actionable, manifest
|