monata 0.1.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.
- monata-0.1.0/.gitignore +27 -0
- monata-0.1.0/LICENSE +21 -0
- monata-0.1.0/PKG-INFO +142 -0
- monata-0.1.0/README.md +110 -0
- monata-0.1.0/pyproject.toml +88 -0
- monata-0.1.0/src/monata/__init__.py +35 -0
- monata-0.1.0/src/monata/_config.py +105 -0
- monata-0.1.0/src/monata/_home.py +47 -0
- monata-0.1.0/src/monata/_json.py +57 -0
- monata-0.1.0/src/monata/_paths.py +73 -0
- monata-0.1.0/src/monata/_types.py +8 -0
- monata-0.1.0/src/monata/category.py +139 -0
- monata-0.1.0/src/monata/cell.py +179 -0
- monata-0.1.0/src/monata/circuits/__init__.py +29 -0
- monata-0.1.0/src/monata/circuits/cmos.py +102 -0
- monata-0.1.0/src/monata/circuits/source.py +70 -0
- monata-0.1.0/src/monata/corner.py +355 -0
- monata-0.1.0/src/monata/eda/__init__.py +35 -0
- monata-0.1.0/src/monata/eda/kicad.py +526 -0
- monata-0.1.0/src/monata/errors.py +33 -0
- monata-0.1.0/src/monata/examples.py +79 -0
- monata-0.1.0/src/monata/generation/__init__.py +6 -0
- monata-0.1.0/src/monata/generation/netlist.py +72 -0
- monata-0.1.0/src/monata/generation/symbol.py +26 -0
- monata-0.1.0/src/monata/library.py +323 -0
- monata-0.1.0/src/monata/measure/__init__.py +55 -0
- monata-0.1.0/src/monata/measure/calculus.py +330 -0
- monata-0.1.0/src/monata/measure/freq_domain.py +250 -0
- monata-0.1.0/src/monata/measure/result.py +107 -0
- monata-0.1.0/src/monata/measure/spec.py +385 -0
- monata-0.1.0/src/monata/measure/statistics.py +91 -0
- monata-0.1.0/src/monata/measure/summary.py +217 -0
- monata-0.1.0/src/monata/measure/time_domain.py +362 -0
- monata-0.1.0/src/monata/models/__init__.py +32 -0
- monata-0.1.0/src/monata/models/artifacts.py +104 -0
- monata-0.1.0/src/monata/models/cache.py +355 -0
- monata-0.1.0/src/monata/models/compiler.py +239 -0
- monata-0.1.0/src/monata/models/diagnostics.py +46 -0
- monata-0.1.0/src/monata/models/flow.py +243 -0
- monata-0.1.0/src/monata/models/manifest.py +438 -0
- monata-0.1.0/src/monata/models/registry.py +369 -0
- monata-0.1.0/src/monata/models/resolver.py +970 -0
- monata-0.1.0/src/monata/netlist/__init__.py +45 -0
- monata-0.1.0/src/monata/netlist/device_schema.py +394 -0
- monata-0.1.0/src/monata/netlist/ir.py +1026 -0
- monata-0.1.0/src/monata/netlist/mutation.py +173 -0
- monata-0.1.0/src/monata/netlist/ngspice.py +221 -0
- monata-0.1.0/src/monata/netlist/scope_api.py +1601 -0
- monata-0.1.0/src/monata/netlist/scope_state.py +137 -0
- monata-0.1.0/src/monata/netlist/topology.py +369 -0
- monata-0.1.0/src/monata/optim/__init__.py +13 -0
- monata-0.1.0/src/monata/optim/base.py +155 -0
- monata-0.1.0/src/monata/optim/bayesian.py +187 -0
- monata-0.1.0/src/monata/optim/circuit.py +166 -0
- monata-0.1.0/src/monata/optim/nsga2.py +272 -0
- monata-0.1.0/src/monata/parser/__init__.py +92 -0
- monata-0.1.0/src/monata/parser/analysis_import.py +1009 -0
- monata-0.1.0/src/monata/parser/commands.py +70 -0
- monata-0.1.0/src/monata/parser/deck.py +348 -0
- monata-0.1.0/src/monata/parser/errors.py +28 -0
- monata-0.1.0/src/monata/parser/expression.py +457 -0
- monata-0.1.0/src/monata/parser/import_plan.py +687 -0
- monata-0.1.0/src/monata/parser/importer.py +636 -0
- monata-0.1.0/src/monata/parser/source_subcircuit.py +53 -0
- monata-0.1.0/src/monata/physics.py +309 -0
- monata-0.1.0/src/monata/projection.py +238 -0
- monata-0.1.0/src/monata/py.typed +0 -0
- monata-0.1.0/src/monata/registry.py +59 -0
- monata-0.1.0/src/monata/sim/__init__.py +5 -0
- monata-0.1.0/src/monata/sim/_backend.py +28 -0
- monata-0.1.0/src/monata/sim/_digital_bits.py +42 -0
- monata-0.1.0/src/monata/sim/_frozen.py +124 -0
- monata-0.1.0/src/monata/sim/_vector_identity.py +93 -0
- monata-0.1.0/src/monata/sim/analysis_spec.py +310 -0
- monata-0.1.0/src/monata/sim/artifacts.py +150 -0
- monata-0.1.0/src/monata/sim/backends/__init__.py +41 -0
- monata-0.1.0/src/monata/sim/backends/base.py +335 -0
- monata-0.1.0/src/monata/sim/backends/ngspice.py +415 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_common.py +550 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_output.py +321 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_parse.py +377 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_plan.py +621 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_shared.py +344 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_shared_commands.py +65 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_shared_ffi.py +130 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_shared_session.py +621 -0
- monata-0.1.0/src/monata/sim/backends/ngspice_stdout.py +237 -0
- monata-0.1.0/src/monata/sim/cache.py +367 -0
- monata-0.1.0/src/monata/sim/capabilities.py +191 -0
- monata-0.1.0/src/monata/sim/core.py +77 -0
- monata-0.1.0/src/monata/sim/corner.py +261 -0
- monata-0.1.0/src/monata/sim/digital_circuits.py +199 -0
- monata-0.1.0/src/monata/sim/digital_claims.py +310 -0
- monata-0.1.0/src/monata/sim/digital_extract.py +402 -0
- monata-0.1.0/src/monata/sim/digital_plan.py +505 -0
- monata-0.1.0/src/monata/sim/digital_projection.py +111 -0
- monata-0.1.0/src/monata/sim/digital_results.py +186 -0
- monata-0.1.0/src/monata/sim/digital_spec.py +161 -0
- monata-0.1.0/src/monata/sim/digital_table.py +512 -0
- monata-0.1.0/src/monata/sim/digital_table_config.py +234 -0
- monata-0.1.0/src/monata/sim/digital_tasks.py +225 -0
- monata-0.1.0/src/monata/sim/digital_timing.py +109 -0
- monata-0.1.0/src/monata/sim/executor.py +100 -0
- monata-0.1.0/src/monata/sim/export.py +65 -0
- monata-0.1.0/src/monata/sim/export_hdf5.py +339 -0
- monata-0.1.0/src/monata/sim/export_payload.py +296 -0
- monata-0.1.0/src/monata/sim/montecarlo.py +251 -0
- monata-0.1.0/src/monata/sim/plot.py +156 -0
- monata-0.1.0/src/monata/sim/rawfile.py +403 -0
- monata-0.1.0/src/monata/sim/result_ops.py +140 -0
- monata-0.1.0/src/monata/sim/results.py +782 -0
- monata-0.1.0/src/monata/sim/session.py +382 -0
- monata-0.1.0/src/monata/sim/sweep.py +272 -0
- monata-0.1.0/src/monata/sim/task.py +152 -0
- monata-0.1.0/src/monata/sim/vector_names.py +203 -0
- monata-0.1.0/src/monata/sim/waveform.py +785 -0
- monata-0.1.0/src/monata/spice_library.py +775 -0
- monata-0.1.0/src/monata/techlib/__init__.py +5 -0
- monata-0.1.0/src/monata/techlib/parse.py +187 -0
- monata-0.1.0/src/monata/techlib/projection.py +168 -0
- monata-0.1.0/src/monata/techlib/registry.py +501 -0
- monata-0.1.0/src/monata/techlib/schema.py +130 -0
- monata-0.1.0/src/monata/units.py +1251 -0
- monata-0.1.0/src/monata/views/__init__.py +5 -0
- monata-0.1.0/src/monata/views/base.py +155 -0
- monata-0.1.0/src/monata/views/digital_truth_table.py +280 -0
- monata-0.1.0/src/monata/views/netlist.py +15 -0
- monata-0.1.0/src/monata/views/registry.py +332 -0
- monata-0.1.0/src/monata/views/schematic.py +10 -0
- monata-0.1.0/src/monata/views/simulation.py +312 -0
- monata-0.1.0/src/monata/views/symbol.py +64 -0
- monata-0.1.0/src/monata/views/testbench.py +16 -0
- monata-0.1.0/src/monata/workspace/__init__.py +6 -0
- monata-0.1.0/src/monata/workspace/experiment.py +93 -0
- monata-0.1.0/src/monata/workspace/project.py +230 -0
- monata-0.1.0/src/monata/workspace/result_store.py +177 -0
- monata-0.1.0/tests/conftest.py +26 -0
- monata-0.1.0/tests/support/README.md +26 -0
- monata-0.1.0/tests/support/__init__.py +1 -0
- monata-0.1.0/tests/support/assertions.py +15 -0
- monata-0.1.0/tests/support/backends.py +84 -0
- monata-0.1.0/tests/support/digital_cases.py +460 -0
- monata-0.1.0/tests/support/executors.py +60 -0
- monata-0.1.0/tests/support/model_cases.py +99 -0
- monata-0.1.0/tests/support/native_backend_cases.py +49 -0
- monata-0.1.0/tests/support/netlist_cases.py +14 -0
- monata-0.1.0/tests/support/ngspice.py +43 -0
- monata-0.1.0/tests/support/results.py +61 -0
- monata-0.1.0/tests/support/sim_results_cases.py +0 -0
- monata-0.1.0/tests/support/workspaces.py +20 -0
- monata-0.1.0/tests/test_cell.py +366 -0
- monata-0.1.0/tests/test_cir2py.py +167 -0
- monata-0.1.0/tests/test_circuit_construction.py +211 -0
- monata-0.1.0/tests/test_errors.py +43 -0
- monata-0.1.0/tests/test_examples.py +43 -0
- monata-0.1.0/tests/test_foundation_closed_loop.py +72 -0
- monata-0.1.0/tests/test_generation.py +277 -0
- monata-0.1.0/tests/test_integration.py +78 -0
- monata-0.1.0/tests/test_kicad_import.py +252 -0
- monata-0.1.0/tests/test_library.py +331 -0
- monata-0.1.0/tests/test_measure_calculus.py +195 -0
- monata-0.1.0/tests/test_measure_freq_domain.py +279 -0
- monata-0.1.0/tests/test_measure_result.py +144 -0
- monata-0.1.0/tests/test_measure_spec.py +370 -0
- monata-0.1.0/tests/test_measure_statistics.py +145 -0
- monata-0.1.0/tests/test_measure_summary.py +278 -0
- monata-0.1.0/tests/test_measure_time_domain.py +337 -0
- monata-0.1.0/tests/test_models_cache.py +368 -0
- monata-0.1.0/tests/test_models_compiler.py +241 -0
- monata-0.1.0/tests/test_models_external_artifacts.py +257 -0
- monata-0.1.0/tests/test_models_resolver.py +322 -0
- monata-0.1.0/tests/test_models_schema.py +259 -0
- monata-0.1.0/tests/test_netlist_construction.py +157 -0
- monata-0.1.0/tests/test_netlist_mutation.py +106 -0
- monata-0.1.0/tests/test_netlist_mutation_surface.py +355 -0
- monata-0.1.0/tests/test_netlist_rendering.py +897 -0
- monata-0.1.0/tests/test_netlist_roundtrip_parser.py +426 -0
- monata-0.1.0/tests/test_netlist_schema_topology.py +307 -0
- monata-0.1.0/tests/test_ngspice_rawfile.py +254 -0
- monata-0.1.0/tests/test_ngspice_shared.py +544 -0
- monata-0.1.0/tests/test_optim_base.py +98 -0
- monata-0.1.0/tests/test_optim_bayesian.py +118 -0
- monata-0.1.0/tests/test_optim_circuit.py +286 -0
- monata-0.1.0/tests/test_optim_nsga2.py +91 -0
- monata-0.1.0/tests/test_p4_workflow.py +51 -0
- monata-0.1.0/tests/test_paths.py +51 -0
- monata-0.1.0/tests/test_physics.py +142 -0
- monata-0.1.0/tests/test_public_api.py +756 -0
- monata-0.1.0/tests/test_registry.py +133 -0
- monata-0.1.0/tests/test_sim_analysis_spec.py +242 -0
- monata-0.1.0/tests/test_sim_artifacts.py +155 -0
- monata-0.1.0/tests/test_sim_backend_native_artifact_parsing.py +268 -0
- monata-0.1.0/tests/test_sim_backend_native_failure_handling.py +304 -0
- monata-0.1.0/tests/test_sim_backend_native_planning.py +344 -0
- monata-0.1.0/tests/test_sim_backend_native_subprocess.py +552 -0
- monata-0.1.0/tests/test_sim_backend_registry.py +312 -0
- monata-0.1.0/tests/test_sim_cache.py +134 -0
- monata-0.1.0/tests/test_sim_corner.py +539 -0
- monata-0.1.0/tests/test_sim_digital_result_extraction.py +205 -0
- monata-0.1.0/tests/test_sim_digital_task_construction.py +469 -0
- monata-0.1.0/tests/test_sim_digital_timing_claims.py +360 -0
- monata-0.1.0/tests/test_sim_digital_truth_table_spec.py +210 -0
- monata-0.1.0/tests/test_sim_executor.py +138 -0
- monata-0.1.0/tests/test_sim_export_hdf5.py +339 -0
- monata-0.1.0/tests/test_sim_montecarlo.py +355 -0
- monata-0.1.0/tests/test_sim_rawfile.py +458 -0
- monata-0.1.0/tests/test_sim_results_analysis.py +882 -0
- monata-0.1.0/tests/test_sim_results_core.py +689 -0
- monata-0.1.0/tests/test_sim_results_export_plot.py +563 -0
- monata-0.1.0/tests/test_sim_results_measurement.py +73 -0
- monata-0.1.0/tests/test_sim_session.py +425 -0
- monata-0.1.0/tests/test_sim_sweep.py +329 -0
- monata-0.1.0/tests/test_sim_task.py +282 -0
- monata-0.1.0/tests/test_sinomos_project_examples.py +181 -0
- monata-0.1.0/tests/test_spice_analysis_import.py +609 -0
- monata-0.1.0/tests/test_spice_expression.py +77 -0
- monata-0.1.0/tests/test_spice_import.py +694 -0
- monata-0.1.0/tests/test_spice_import_plan.py +278 -0
- monata-0.1.0/tests/test_spice_library.py +471 -0
- monata-0.1.0/tests/test_spice_parser.py +186 -0
- monata-0.1.0/tests/test_spice_roundtrip.py +92 -0
- monata-0.1.0/tests/test_suite_structure.py +144 -0
- monata-0.1.0/tests/test_techlib.py +879 -0
- monata-0.1.0/tests/test_units.py +829 -0
- monata-0.1.0/tests/test_views.py +901 -0
- monata-0.1.0/tests/test_workspace_experiment.py +352 -0
- monata-0.1.0/tests/test_workspace_project.py +486 -0
monata-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
*.egg
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.pyright/
|
|
13
|
+
.coverage
|
|
14
|
+
.coverage.*
|
|
15
|
+
htmlcov/
|
|
16
|
+
.tox/
|
|
17
|
+
.nox/
|
|
18
|
+
.hypothesis/
|
|
19
|
+
pip-wheel-metadata/
|
|
20
|
+
.venv/
|
|
21
|
+
.env
|
|
22
|
+
.env.*
|
|
23
|
+
!.env.example
|
|
24
|
+
|
|
25
|
+
# Documentation build
|
|
26
|
+
docs/_build/
|
|
27
|
+
docs/api/monata/
|
monata-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zhangmai Li
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
monata-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: monata
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight EDA framework for circuit design and SPICE simulation workflows
|
|
5
|
+
Project-URL: Homepage, https://github.com/lizhangmai/monata
|
|
6
|
+
Project-URL: Repository, https://github.com/lizhangmai/monata
|
|
7
|
+
Project-URL: Issues, https://github.com/lizhangmai/monata/issues
|
|
8
|
+
Author: lizhangmai
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: analog,circuit,cmos,digital,eda,simulation,spice
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: cffi
|
|
21
|
+
Requires-Dist: h5py
|
|
22
|
+
Requires-Dist: matplotlib
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: build; extra == 'dev'
|
|
26
|
+
Requires-Dist: pyright; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Monata
|
|
34
|
+
|
|
35
|
+
Monata is a Python toolkit for circuit workflow automation. It provides a
|
|
36
|
+
typed front end for organizing circuit projects, authoring SPICE-oriented
|
|
37
|
+
netlists, running simulator tasks, and working with measurement and result
|
|
38
|
+
data.
|
|
39
|
+
|
|
40
|
+
Monata is designed as a lightweight Python package, not as a simulator
|
|
41
|
+
distribution. It integrates with simulator and model-toolchain programs that
|
|
42
|
+
users install in their own environments.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- Library, cell, and view organization for circuit projects.
|
|
47
|
+
- Native circuit/netlist records with ngspice-compatible rendering.
|
|
48
|
+
- Backend-neutral simulation tasks, result objects, sweeps, corners, Monte
|
|
49
|
+
Carlo flows, and measurements.
|
|
50
|
+
- Built-in ngspice subprocess backend, plus a shared-library ngspice runner
|
|
51
|
+
for environments that provide `libngspice`.
|
|
52
|
+
- Plotting and HDF5 result import/export available from the default install.
|
|
53
|
+
- Optional technology-library packages, such as `monata-techlib`, for reusable
|
|
54
|
+
model metadata and redistributed model assets.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
python -m pip install monata
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Monata supports Python 3.11 and 3.12.
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
Create a Monata library and build a small circuit:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from monata import LibraryRegistry
|
|
70
|
+
from monata.netlist import Circuit
|
|
71
|
+
|
|
72
|
+
registry = LibraryRegistry()
|
|
73
|
+
library = registry.create_library(path="work/analog", name="analog")
|
|
74
|
+
cell = library.create_cell("rc_filter", description="RC low-pass filter")
|
|
75
|
+
|
|
76
|
+
circuit = Circuit("rc low-pass")
|
|
77
|
+
circuit.voltage("in", "vin", "0", "1")
|
|
78
|
+
circuit.resistor("load", "vin", "vout", "1k")
|
|
79
|
+
circuit.capacitor("hold", "vout", "0", "1n")
|
|
80
|
+
|
|
81
|
+
print(cell.name)
|
|
82
|
+
print(circuit.to_spice())
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Run a simulation when a supported backend is installed:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from monata.sim.core import DCSpec, LocalExecutor, SimTask
|
|
89
|
+
|
|
90
|
+
task = SimTask(
|
|
91
|
+
circuit=circuit,
|
|
92
|
+
analysis_spec=DCSpec(source="Vin", start=0, stop=1, step=0.25),
|
|
93
|
+
output_names=["vout"],
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
result = LocalExecutor(max_workers=1).submit(task).result()
|
|
97
|
+
if result.failed:
|
|
98
|
+
raise RuntimeError(result.error_message)
|
|
99
|
+
|
|
100
|
+
print(result.waveforms["vout"])
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The default backend is `ngspice-subprocess`, which runs a local `ngspice`
|
|
104
|
+
executable from `PATH` or `CONDA_PREFIX/bin`.
|
|
105
|
+
|
|
106
|
+
## External Tools
|
|
107
|
+
|
|
108
|
+
The public `monata` package does not ship simulator binaries, shared simulator
|
|
109
|
+
libraries, XSPICE code models, OpenVAF, Xyce, foundry PDKs, PTM model cards, or
|
|
110
|
+
compiled OSDI binaries.
|
|
111
|
+
|
|
112
|
+
Monata can use:
|
|
113
|
+
|
|
114
|
+
- a user-installed `ngspice` executable through `ngspice-subprocess`;
|
|
115
|
+
- a user-provided `libngspice` shared library through `ngspice-shared`;
|
|
116
|
+
- separately installed technology-library packages such as `monata-techlib`.
|
|
117
|
+
|
|
118
|
+
Users and downstream packagers remain responsible for installing external tools
|
|
119
|
+
and complying with their upstream licenses.
|
|
120
|
+
|
|
121
|
+
## Documentation
|
|
122
|
+
|
|
123
|
+
Long-form documentation lives outside this source package:
|
|
124
|
+
|
|
125
|
+
- Documentation repository: https://github.com/lizhangmai/monata-docs
|
|
126
|
+
- Getting started: https://github.com/lizhangmai/monata-docs/tree/main/docs/getting-started
|
|
127
|
+
- User guide: https://github.com/lizhangmai/monata-docs/tree/main/docs/user-guide
|
|
128
|
+
- API boundaries: https://github.com/lizhangmai/monata-docs/blob/main/docs/reference/api-boundaries.md
|
|
129
|
+
- Simulator and tool setup: https://github.com/lizhangmai/monata-docs/blob/main/docs/toolchain/external-tools.md
|
|
130
|
+
- Maintainer notes: https://github.com/lizhangmai/monata-docs/tree/main/docs/maintainers
|
|
131
|
+
|
|
132
|
+
## Security
|
|
133
|
+
|
|
134
|
+
Monata project views can be Python source files. Loading schematic and testbench
|
|
135
|
+
views executes that project code in the current Python process; view loading is
|
|
136
|
+
not sandboxed. Open, load, generate, and simulate only trusted libraries and
|
|
137
|
+
project workspaces.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
Monata's Python framework is licensed under the MIT License. See
|
|
142
|
+
[`LICENSE`](LICENSE).
|
monata-0.1.0/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Monata
|
|
2
|
+
|
|
3
|
+
Monata is a Python toolkit for circuit workflow automation. It provides a
|
|
4
|
+
typed front end for organizing circuit projects, authoring SPICE-oriented
|
|
5
|
+
netlists, running simulator tasks, and working with measurement and result
|
|
6
|
+
data.
|
|
7
|
+
|
|
8
|
+
Monata is designed as a lightweight Python package, not as a simulator
|
|
9
|
+
distribution. It integrates with simulator and model-toolchain programs that
|
|
10
|
+
users install in their own environments.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Library, cell, and view organization for circuit projects.
|
|
15
|
+
- Native circuit/netlist records with ngspice-compatible rendering.
|
|
16
|
+
- Backend-neutral simulation tasks, result objects, sweeps, corners, Monte
|
|
17
|
+
Carlo flows, and measurements.
|
|
18
|
+
- Built-in ngspice subprocess backend, plus a shared-library ngspice runner
|
|
19
|
+
for environments that provide `libngspice`.
|
|
20
|
+
- Plotting and HDF5 result import/export available from the default install.
|
|
21
|
+
- Optional technology-library packages, such as `monata-techlib`, for reusable
|
|
22
|
+
model metadata and redistributed model assets.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python -m pip install monata
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Monata supports Python 3.11 and 3.12.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
Create a Monata library and build a small circuit:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from monata import LibraryRegistry
|
|
38
|
+
from monata.netlist import Circuit
|
|
39
|
+
|
|
40
|
+
registry = LibraryRegistry()
|
|
41
|
+
library = registry.create_library(path="work/analog", name="analog")
|
|
42
|
+
cell = library.create_cell("rc_filter", description="RC low-pass filter")
|
|
43
|
+
|
|
44
|
+
circuit = Circuit("rc low-pass")
|
|
45
|
+
circuit.voltage("in", "vin", "0", "1")
|
|
46
|
+
circuit.resistor("load", "vin", "vout", "1k")
|
|
47
|
+
circuit.capacitor("hold", "vout", "0", "1n")
|
|
48
|
+
|
|
49
|
+
print(cell.name)
|
|
50
|
+
print(circuit.to_spice())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Run a simulation when a supported backend is installed:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from monata.sim.core import DCSpec, LocalExecutor, SimTask
|
|
57
|
+
|
|
58
|
+
task = SimTask(
|
|
59
|
+
circuit=circuit,
|
|
60
|
+
analysis_spec=DCSpec(source="Vin", start=0, stop=1, step=0.25),
|
|
61
|
+
output_names=["vout"],
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
result = LocalExecutor(max_workers=1).submit(task).result()
|
|
65
|
+
if result.failed:
|
|
66
|
+
raise RuntimeError(result.error_message)
|
|
67
|
+
|
|
68
|
+
print(result.waveforms["vout"])
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The default backend is `ngspice-subprocess`, which runs a local `ngspice`
|
|
72
|
+
executable from `PATH` or `CONDA_PREFIX/bin`.
|
|
73
|
+
|
|
74
|
+
## External Tools
|
|
75
|
+
|
|
76
|
+
The public `monata` package does not ship simulator binaries, shared simulator
|
|
77
|
+
libraries, XSPICE code models, OpenVAF, Xyce, foundry PDKs, PTM model cards, or
|
|
78
|
+
compiled OSDI binaries.
|
|
79
|
+
|
|
80
|
+
Monata can use:
|
|
81
|
+
|
|
82
|
+
- a user-installed `ngspice` executable through `ngspice-subprocess`;
|
|
83
|
+
- a user-provided `libngspice` shared library through `ngspice-shared`;
|
|
84
|
+
- separately installed technology-library packages such as `monata-techlib`.
|
|
85
|
+
|
|
86
|
+
Users and downstream packagers remain responsible for installing external tools
|
|
87
|
+
and complying with their upstream licenses.
|
|
88
|
+
|
|
89
|
+
## Documentation
|
|
90
|
+
|
|
91
|
+
Long-form documentation lives outside this source package:
|
|
92
|
+
|
|
93
|
+
- Documentation repository: https://github.com/lizhangmai/monata-docs
|
|
94
|
+
- Getting started: https://github.com/lizhangmai/monata-docs/tree/main/docs/getting-started
|
|
95
|
+
- User guide: https://github.com/lizhangmai/monata-docs/tree/main/docs/user-guide
|
|
96
|
+
- API boundaries: https://github.com/lizhangmai/monata-docs/blob/main/docs/reference/api-boundaries.md
|
|
97
|
+
- Simulator and tool setup: https://github.com/lizhangmai/monata-docs/blob/main/docs/toolchain/external-tools.md
|
|
98
|
+
- Maintainer notes: https://github.com/lizhangmai/monata-docs/tree/main/docs/maintainers
|
|
99
|
+
|
|
100
|
+
## Security
|
|
101
|
+
|
|
102
|
+
Monata project views can be Python source files. Loading schematic and testbench
|
|
103
|
+
views executes that project code in the current Python process; view loading is
|
|
104
|
+
not sandboxed. Open, load, generate, and simulate only trusted libraries and
|
|
105
|
+
project workspaces.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
Monata's Python framework is licensed under the MIT License. See
|
|
110
|
+
[`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "monata"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A lightweight EDA framework for circuit design and SPICE simulation workflows"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "lizhangmai" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["eda", "spice", "circuit", "simulation", "analog", "digital", "cmos"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"License :: OSI Approved :: MIT License",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"numpy",
|
|
28
|
+
"matplotlib",
|
|
29
|
+
"h5py",
|
|
30
|
+
"cffi",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest",
|
|
36
|
+
"pytest-cov",
|
|
37
|
+
"ruff",
|
|
38
|
+
"pyright",
|
|
39
|
+
"build",
|
|
40
|
+
"twine",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/lizhangmai/monata"
|
|
45
|
+
Repository = "https://github.com/lizhangmai/monata"
|
|
46
|
+
Issues = "https://github.com/lizhangmai/monata/issues"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
only-packages = true
|
|
50
|
+
packages = ["src/monata"]
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.sdist]
|
|
53
|
+
include = [
|
|
54
|
+
"src/monata/",
|
|
55
|
+
"tests/",
|
|
56
|
+
"LICENSE",
|
|
57
|
+
"README.md",
|
|
58
|
+
"pyproject.toml",
|
|
59
|
+
]
|
|
60
|
+
exclude = [
|
|
61
|
+
"data/",
|
|
62
|
+
"examples/",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.pytest.ini_options]
|
|
66
|
+
testpaths = ["tests"]
|
|
67
|
+
addopts = ["--strict-markers"]
|
|
68
|
+
markers = [
|
|
69
|
+
"fast: quick unit or contract tests suitable for the default development loop",
|
|
70
|
+
"integration: cross-module workflows, external fixtures, or end-to-end persistence paths",
|
|
71
|
+
"native: tests that execute or depend on native simulator backends or external simulator binaries",
|
|
72
|
+
"slow: broad or computationally heavier suites kept out of the fast development loop",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[tool.ruff]
|
|
76
|
+
line-length = 120
|
|
77
|
+
src = ["src", "tests"]
|
|
78
|
+
target-version = "py311"
|
|
79
|
+
|
|
80
|
+
[tool.pyright]
|
|
81
|
+
exclude = [
|
|
82
|
+
"**/__pycache__",
|
|
83
|
+
"build",
|
|
84
|
+
"dist",
|
|
85
|
+
]
|
|
86
|
+
extraPaths = ["src", "tests"]
|
|
87
|
+
include = ["src/monata", "tests"]
|
|
88
|
+
pythonVersion = "3.11"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Monata — a lightweight EDA framework for circuit design."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from monata.cell import Cell
|
|
6
|
+
from monata.category import Category
|
|
7
|
+
from monata.corner import OperatingCorner
|
|
8
|
+
from monata.errors import (
|
|
9
|
+
CellNotFoundError,
|
|
10
|
+
LibraryNotFoundError,
|
|
11
|
+
ViewAlreadyModifiedError,
|
|
12
|
+
ViewNotFoundError,
|
|
13
|
+
ViewNotGeneratedError,
|
|
14
|
+
)
|
|
15
|
+
from monata.library import Library
|
|
16
|
+
from monata.registry import LibraryRegistry
|
|
17
|
+
from monata.units import Quantity, Unit, UnitArray
|
|
18
|
+
from monata.views import View
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"Cell",
|
|
22
|
+
"CellNotFoundError",
|
|
23
|
+
"Category",
|
|
24
|
+
"Library",
|
|
25
|
+
"LibraryNotFoundError",
|
|
26
|
+
"LibraryRegistry",
|
|
27
|
+
"OperatingCorner",
|
|
28
|
+
"Quantity",
|
|
29
|
+
"Unit",
|
|
30
|
+
"UnitArray",
|
|
31
|
+
"View",
|
|
32
|
+
"ViewAlreadyModifiedError",
|
|
33
|
+
"ViewNotFoundError",
|
|
34
|
+
"ViewNotGeneratedError",
|
|
35
|
+
]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Internal TOML persistence helpers for Monata workspace metadata."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable, Mapping
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import tomllib
|
|
11
|
+
except ImportError:
|
|
12
|
+
import tomli as tomllib # type: ignore[no-redef]
|
|
13
|
+
|
|
14
|
+
from monata._paths import toml_string
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def read_toml(path: Path) -> dict[str, Any]:
|
|
18
|
+
with path.open("rb") as file:
|
|
19
|
+
return tomllib.load(file)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def reject_unknown_fields(
|
|
23
|
+
data: Mapping[str, Any], allowed: frozenset[str], subject: str
|
|
24
|
+
) -> None:
|
|
25
|
+
unknown = sorted(key for key in data if key not in allowed)
|
|
26
|
+
if unknown:
|
|
27
|
+
raise ValueError(f"{subject} has unknown fields: {', '.join(unknown)}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def cell_config(name: str, *, description: str = "") -> dict[str, Any]:
|
|
31
|
+
return {"cell": {"name": name, "description": description}, "views": {}}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def category_config(name: str, *, description: str = "") -> dict[str, Any]:
|
|
35
|
+
return {"category": {"name": name, "description": description}}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def write_category_config(path: Path, config: Mapping[str, Any]) -> None:
|
|
39
|
+
category = config["category"]
|
|
40
|
+
lines = [f'[category]\nname = "{toml_string(category["name"])}"\n']
|
|
41
|
+
if "description" in category:
|
|
42
|
+
lines.append(f'description = "{toml_string(category["description"])}"\n')
|
|
43
|
+
path.write_text("".join(lines))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def write_cell_config(path: Path, config: Mapping[str, Any]) -> None:
|
|
47
|
+
cell = config["cell"]
|
|
48
|
+
lines = [f'[cell]\nname = "{toml_string(cell["name"])}"\n']
|
|
49
|
+
if "description" in cell:
|
|
50
|
+
lines.append(f'description = "{toml_string(cell["description"])}"\n')
|
|
51
|
+
lines.append("\n[views]\n")
|
|
52
|
+
for view_type, view_config in config.get("views", {}).items():
|
|
53
|
+
parts = ", ".join(f"{key} = {toml_value(value)}" for key, value in view_config.items())
|
|
54
|
+
lines.append(f"{view_type} = {{ {parts} }}\n")
|
|
55
|
+
path.write_text("".join(lines))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def write_library_config(
|
|
59
|
+
path: Path,
|
|
60
|
+
*,
|
|
61
|
+
name: str,
|
|
62
|
+
tech_model_paths: Iterable[Any],
|
|
63
|
+
description: str = "",
|
|
64
|
+
techlib_attachments: Iterable[str] = (),
|
|
65
|
+
default_corner: str | None = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
model_paths = ", ".join(toml_value(model_path) for model_path in tech_model_paths)
|
|
68
|
+
lines = [
|
|
69
|
+
f'[library]\nname = "{toml_string(name)}"\n',
|
|
70
|
+
f'description = "{toml_string(description)}"\n\n',
|
|
71
|
+
f"[technology]\nmodel_paths = [{model_paths}]\n",
|
|
72
|
+
]
|
|
73
|
+
attachments = list(techlib_attachments)
|
|
74
|
+
if attachments or default_corner is not None:
|
|
75
|
+
techlibs = ", ".join(toml_value(attachment) for attachment in attachments)
|
|
76
|
+
lines.extend(["\n[attachments]\n", f"techlibs = [{techlibs}]\n"])
|
|
77
|
+
if default_corner is not None:
|
|
78
|
+
lines.append(f'default_corner = "{toml_string(default_corner)}"\n')
|
|
79
|
+
path.write_text("".join(lines))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def write_project_config(
|
|
83
|
+
path: Path,
|
|
84
|
+
*,
|
|
85
|
+
name: str,
|
|
86
|
+
libraries: Iterable[Mapping[str, str]] = (),
|
|
87
|
+
) -> None:
|
|
88
|
+
lines = [f'[project]\nname = "{toml_string(name)}"\n']
|
|
89
|
+
for entry in libraries:
|
|
90
|
+
lines.extend([
|
|
91
|
+
"\n[[libraries]]\n",
|
|
92
|
+
f'name = "{toml_string(entry["name"])}"\n',
|
|
93
|
+
f'path = "{toml_string(entry["path"])}"\n',
|
|
94
|
+
])
|
|
95
|
+
path.write_text("".join(lines))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def toml_value(value: Any) -> str:
|
|
99
|
+
if isinstance(value, bool):
|
|
100
|
+
return "true" if value else "false"
|
|
101
|
+
if isinstance(value, (int, float)):
|
|
102
|
+
return repr(value)
|
|
103
|
+
if isinstance(value, (list, tuple)):
|
|
104
|
+
return "[" + ", ".join(toml_value(item) for item in value) + "]"
|
|
105
|
+
return f'"{toml_string(value)}"'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Monata-managed mutable state locations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def monata_home(explicit: str | Path | None = None) -> Path | None:
|
|
10
|
+
"""Return the configured Monata home root, if one is configured."""
|
|
11
|
+
|
|
12
|
+
if explicit is not None:
|
|
13
|
+
return Path(explicit)
|
|
14
|
+
env = os.environ.get("MONATA_HOME")
|
|
15
|
+
return Path(env) if env else None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def default_user_cache_root() -> Path:
|
|
19
|
+
"""Return the platform-neutral user cache root used when MONATA_HOME is unset."""
|
|
20
|
+
|
|
21
|
+
xdg = os.environ.get("XDG_CACHE_HOME")
|
|
22
|
+
if xdg:
|
|
23
|
+
return Path(xdg)
|
|
24
|
+
return Path(os.path.expanduser("~/.cache"))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def monata_cache_dir(*, home: str | Path | None = None) -> Path:
|
|
28
|
+
"""Return the generic Monata cache directory for a configured home/default."""
|
|
29
|
+
|
|
30
|
+
root = monata_home(home)
|
|
31
|
+
if root is not None:
|
|
32
|
+
return root / "cache"
|
|
33
|
+
return default_user_cache_root() / "monata"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def monata_registry_dir(*, home: str | Path | None = None) -> Path | None:
|
|
37
|
+
"""Return the optional Monata registry directory when a home root exists."""
|
|
38
|
+
|
|
39
|
+
root = monata_home(home)
|
|
40
|
+
return root / "registry" if root is not None else None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def monata_logs_dir(*, home: str | Path | None = None) -> Path | None:
|
|
44
|
+
"""Return the optional Monata logs directory when a home root exists."""
|
|
45
|
+
|
|
46
|
+
root = monata_home(home)
|
|
47
|
+
return root / "logs" if root is not None else None
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Shared JSON-safe value coercion helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def json_safe(value: Any, *, strict: bool = False) -> Any:
|
|
14
|
+
"""Return a recursively JSON-compatible representation of common Monata values."""
|
|
15
|
+
|
|
16
|
+
result = _json_safe(value)
|
|
17
|
+
if strict:
|
|
18
|
+
json.dumps(result)
|
|
19
|
+
return result
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def json_safe_dict(values: Mapping[str, Any]) -> dict[str, Any]:
|
|
23
|
+
return {str(key): json_safe(value) for key, value in dict(values).items()}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _json_safe(value: Any) -> Any:
|
|
27
|
+
if hasattr(value, "to_dict"):
|
|
28
|
+
return json_safe(value.to_dict())
|
|
29
|
+
if value is None or isinstance(value, str | int | float | bool):
|
|
30
|
+
return value
|
|
31
|
+
if isinstance(value, Path):
|
|
32
|
+
return str(value)
|
|
33
|
+
if isinstance(value, np.generic):
|
|
34
|
+
return json_safe(value.item())
|
|
35
|
+
if isinstance(value, complex):
|
|
36
|
+
return {"real": value.real, "imag": value.imag}
|
|
37
|
+
if isinstance(value, np.ndarray):
|
|
38
|
+
if np.iscomplexobj(value):
|
|
39
|
+
return {
|
|
40
|
+
"real": np.real(value).tolist(),
|
|
41
|
+
"imag": np.imag(value).tolist(),
|
|
42
|
+
}
|
|
43
|
+
return json_safe(value.tolist())
|
|
44
|
+
if isinstance(value, Mapping):
|
|
45
|
+
return json_safe_dict(value)
|
|
46
|
+
if isinstance(value, list | tuple):
|
|
47
|
+
return [json_safe(item) for item in value]
|
|
48
|
+
if isinstance(value, set | frozenset):
|
|
49
|
+
return [json_safe(item) for item in sorted(value, key=str)]
|
|
50
|
+
if hasattr(value, "item"):
|
|
51
|
+
try:
|
|
52
|
+
return json_safe(value.item())
|
|
53
|
+
except ValueError:
|
|
54
|
+
pass
|
|
55
|
+
if hasattr(value, "tolist"):
|
|
56
|
+
return json_safe(value.tolist())
|
|
57
|
+
return value
|