Skip to main content

A high-performance compartment modelling library in Rust and Python.

Project description

Commol

A high-performance compartment modelling library for mathematical modeling using difference equations. Commol provides a clean Python API backed by a fast Rust engine for numerical computations.

⚠️ Alpha Stage Warning: Commol is currently in alpha development. The API is not yet stable and may change between versions without backward compatibility guarantees. Use in production at your own risk.

Features

  • Intuitive Model Building: Fluent API for constructing compartment models
  • Mathematical Expressions: Support for complex mathematical formulas in transition rates (sin, cos, exp, log, etc.)
  • Unit Checking: Automatic dimensional analysis to catch unit errors before simulation
  • High Performance: Rust-powered simulation engine for fast computations
  • Flexible Architecture: Support for stratified populations and conditional transitions
  • Type Safety: Comprehensive validation using Pydantic models
  • Multiple Output Formats: Get results as dictionaries or lists for easy analysis

Installation

# Install from PyPI (once published)
pip install commol

# Or install from source
git clone https://github.com/MUNQU/commol.git
cd commol/py-commol
pip install maturin

# If using a virtual environment, activate it first
# source venv/bin/activate  # On Linux/macOS
# venv\Scripts\activate     # On Windows

maturin develop --release

⚠️ Important: The project directory path must not contain tildes (~) or spaces. Maturin may fail with paths like ~/projects/commol or /home/my projects/commol. Use full paths like /home/username/projects/commol instead.

Quick Start

from commol import ModelBuilder, Simulation

# Build a simple SIR model
model = (
    ModelBuilder(name="Basic SIR", version="1.0")
    .add_bin(id="S", name="Susceptible")
    .add_bin(id="I", name="Infected")
    .add_bin(id="R", name="Recovered")
    .add_parameter(id="beta", value=0.3)
    .add_parameter(id="gamma", value=0.1)
    .add_transition(
        id="infection",
        source=["S"],
        target=["I"],
        rate="beta * S * I / N"
    )
    .add_transition(
        id="recovery",
        source=["I"],
        target=["R"],
        rate="gamma"
    )
    .set_initial_conditions(
        population_size=1000,
        bin_fractions=[
            {"bin": "S", "fraction": 0.99},
            {"bin": "I", "fraction": 0.01},
            {"bin": "R", "fraction": 0.0}
        ]
    )
    .build(typology="DifferenceEquations")
)

# Run simulation
simulation = Simulation(model)
results = simulation.run(num_steps=100)

# Display results
print(f"Final infected: {results['I'][-1]:.0f}")

# Visualize results
from commol import SimulationPlotter

plotter = SimulationPlotter(simulation, results)
plotter.plot_series(output_file="sir_model.png")

Using $compartment Placeholder for Multiple Transitions

When you need to apply the same transition to multiple compartments (like death rates), use the $compartment placeholder instead of writing repetitive code:

model = (
    ModelBuilder(name="SLIR with Deaths", version="1.0")
    .add_bin(id="S", name="Susceptible")
    .add_bin(id="L", name="Latent")
    .add_bin(id="I", name="Infected")
    .add_bin(id="R", name="Recovered")
    .add_parameter(id="beta", value=0.3, unit="1/day")
    .add_parameter(id="gamma", value=0.2, unit="1/day")
    .add_parameter(id="delta", value=0.1, unit="1/day")
    .add_parameter(id="d", value=0.01, unit="1/day")  # Death rate
    .add_transition(
        id="infection",
        source=["S"],
        target=["L"],
        rate="beta * S * I / N"
    )
    .add_transition(
        id="progression",
        source=["L"],
        target=["I"],
        rate="gamma * L"
    )
    .add_transition(
        id="recovery",
        source=["I"],
        target=["R"],
        rate="delta * I"
    )
    # Single transition automatically expands to 4 separate death transitions
    .add_transition(
        id="death",
        source=["S", "L", "I", "R"],
        target=[],
        rate="d * $compartment"  # Expands to: d*S, d*L, d*I, d*R
    )
    .set_initial_conditions(
        population_size=1000,
        bin_fractions=[
            {"bin": "S", "fraction": 0.99},
            {"bin": "L", "fraction": 0.005},
            {"bin": "I", "fraction": 0.005},
            {"bin": "R", "fraction": 0.0}
        ]
    )
    .build(typology="DifferenceEquations")
)

The $compartment placeholder:

  • Automatically expands to multiple transitions (one per source compartment)
  • Replaces $compartment with the actual compartment name in the rate formula
  • Works with stratified rates for age-structured or location-based models
  • Reduces code duplication and improves maintainability

Example with stratified rates:

.add_transition(
    id="death",
    source=["S", "I", "R"],
    target=[],
    rate="d_base * $compartment",  # Fallback rate
    stratified_rates=[
        {
            "conditions": [{"stratification": "age", "category": "young"}],
            "rate": "d_young * $compartment"  # Lower death rate for young
        },
        {
            "conditions": [{"stratification": "age", "category": "old"}],
            "rate": "d_old * $compartment"  # Higher death rate for old
        }
    ]
)

With Unit Checking

Add units to parameters and bins for automatic dimensional validation and annotated equation display:

model = (
    ModelBuilder(name="SIR with Units", version="1.0", bin_unit="person")
    .add_bin(id="S", name="Susceptible")
    .add_bin(id="I", name="Infected")
    .add_bin(id="R", name="Recovered")
    .add_parameter(id="beta", value=0.5, unit="1/day")  # Rate with units
    .add_parameter(id="gamma", value=0.1, unit="1/day")
    .add_transition(
        id="infection",
        source=["S"],
        target=["I"],
        rate="beta * S * I / N"
    )
    .add_transition(
        id="recovery",
        source=["I"],
        target=["R"],
        rate="gamma * I"
    )
    .set_initial_conditions(
        population_size=1000,
        bin_fractions=[
            {"bin": "S", "fraction": 0.99},
            {"bin": "I", "fraction": 0.01},
            {"bin": "R", "fraction": 0.0}
        ]
    )
    .build(typology="DifferenceEquations")
)

# Validate dimensional consistency
model.check_unit_consistency()  # Ensures all equations have correct units

# Print equations with unit annotations
model.print_equations()
# Output shows:
#   S -> I: beta(1/day) * S(person) * I(person) / N(person) [person/day]
#   I -> R: gamma(1/day) * I(person) [person/day]

# Export equations in LaTeX format for publications
model.print_equations(format="latex")
# Output: \[\frac{dS}{dt} = - (\beta \cdot S \cdot I / N)\]

Note: Units must be defined for ALL parameters and bins, or for NONE. Partial unit definitions will raise a ValueError to prevent inconsistent models.

Model Calibration

Fit model parameters to observed data using optimization algorithms. Parameters to be calibrated should be set to None:

from commol import (
    ModelBuilder,
    Simulation,
    Calibrator,
    CalibrationProblem,
    CalibrationParameter,
    ObservedDataPoint,
    ParticleSwarmConfig,
)

# Build model with unknown parameters
model = (
    ModelBuilder(name="SIR Model", version="1.0")
    .add_bin(id="S", name="Susceptible")
    .add_bin(id="I", name="Infected")
    .add_bin(id="R", name="Recovered")
    .add_parameter(id="beta", value=None)   # To be calibrated
    .add_parameter(id="gamma", value=None)  # To be calibrated
    .add_transition(
        id="infection",
        source=["S"],
        target=["I"],
        rate="beta * S * I / N"
    )
    .add_transition(
        id="recovery",
        source=["I"],
        target=["R"],
        rate="gamma * I"
    )
    .set_initial_conditions(
        population_size=1000,
        bin_fractions=[
            {"bin": "S", "fraction": 0.99},
            {"bin": "I", "fraction": 0.01},
            {"bin": "R", "fraction": 0.0}
        ]
    )
    .build(typology="DifferenceEquations")
)

# Define observed data from real outbreak
observed_data = [
    ObservedDataPoint(step=10, compartment="I", value=45.2),
    ObservedDataPoint(step=20, compartment="I", value=78.5),
    ObservedDataPoint(step=30, compartment="I", value=62.3),
]

# Simulation can be created with None values for calibration
simulation = Simulation(model)

# Specify parameters to calibrate with bounds and initial guesses
parameters = [
    CalibrationParameter(
        id="beta",
        parameter_type="parameter",
        min_bound=0.0,
        max_bound=1.0,
        initial_guess=0.3  # Starting point
    ),
    CalibrationParameter(
        id="gamma",
        parameter_type="parameter",
        min_bound=0.0,
        max_bound=1.0,
    ),
]

# Configure optimization algorithm (config type determines the algorithm)
pso_config = ParticleSwarmConfig(
    num_particles=40,
    max_iterations=300,
    verbose=True
)

# Configure calibration problem
problem = CalibrationProblem(
    observed_data=observed_data,
    parameters=parameters,
    loss_function="sse",
    optimization_config=pso_config,  # ParticleSwarmConfig or NelderMeadConfig
)

# Run calibration
calibrator = Calibrator(simulation, problem)
result = calibrator.run()

print(f"Calibrated beta: {result.best_parameters['beta']:.4f}")
print(f"Calibrated gamma: {result.best_parameters['gamma']:.4f}")

# Update model with calibrated parameters
model.update_parameters(result.best_parameters)

# Create new simulation with calibrated model
calibrated_simulation = Simulation(model)
calibrated_results = calibrated_simulation.run(num_steps=100)

Calibrating with Scale Parameters:

When observed data is underreported, use scale parameters to estimate the reporting rate:

# Reported cases (potentially underreported)
reported_cases = [10, 15, 25, 40, 60, 75, 85, 70, 50, 30]

# Link observed data to scale parameter
observed_data = [
    ObservedDataPoint(
        step=idx,
        compartment="I",
        value=cases,
        scale_id="reporting_rate"  # Links to scale parameter
    )
    for idx, cases in enumerate(reported_cases)
]

parameters = [
    CalibrationParameter(
        id="beta",
        parameter_type="parameter",
        min_bound=0.1,
        max_bound=1.0
    ),
    CalibrationParameter(
        id="gamma",
        parameter_type="parameter",
        min_bound=0.05,
        max_bound=0.5
    ),
    CalibrationParameter(
        id="reporting_rate",
        parameter_type="scale",
        min_bound=0.01,
        max_bound=1.0
    ),
]

# Run calibration
result = calibrator.run()

# Separate parameters by type
scale_values = {
    param.id: result.best_parameters[param.id]
    for param in problem.parameters
    if param.parameter_type == "scale"
}

print(f"Calibrated reporting rate: {scale_values['reporting_rate']:.2%}")

# Visualize with scale_values for correct display
plotter.plot_series(observed_data=observed_data, scale_values=scale_values)

Constraining Parameters:

Apply constraints to enforce biological knowledge during calibration.

from commol import CalibrationConstraint

# Add constraint: beta/gamma <= 5 (written as 5 - beta/gamma >= 0)
constraints = [
    CalibrationConstraint(
        id="r0_bound",
        expression="5.0 - beta/gamma",
        description="R0 <= 5",
    )
]

problem = CalibrationProblem(
    observed_data=observed_data,
    parameters=parameters,
    constraints=constraints,  # Include constraints
    loss_function="sse",
    optimization_config=pso_config,
)

result = calibrator.run()

Probabilistic Calibration:

For uncertainty quantification, use probabilistic calibration to get an ensemble of parameter sets:

from commol import ProbabilisticCalibrationConfig

# Configure probabilistic calibration
prob_config = ProbabilisticCalibrationConfig(
    n_runs=20,  # Number of calibration runs
    confidence_level=0.95
)

problem = CalibrationProblem(
    observed_data=observed_data,
    parameters=parameters,
    loss_function="sse",
    optimization_config=pso_config,
    probabilistic_config=prob_config,  # Enable probabilistic mode
)

# Run probabilistic calibration
calibrator = Calibrator(simulation, problem)
prob_result = calibrator.run_probabilistic()

Documentation

Full Documentation

Development

For contributors and developers:

Local Development

# Clone repository
git clone https://github.com/MUNQU/commol.git
cd commol

# Create and activate virtual environment
python -m venv venv

source venv/bin/activate  # On Linux/macOS
venv\Scripts\activate   # On Windows

# Install Python dependencies
cd py-commol
pip install -e ".[dev,docs]"

# Build Rust workspace
cd ..
cargo build --workspace

# Build Python extension (with virtual environment activated)
cd py-commol
maturin develop --release

# Run tests
pytest
cd ..
cargo test --workspace

# Build documentation locally
cd py-commol
mkdocs serve

⚠️ Path Requirements: Ensure the project path contains no tildes (~) or spaces. Maturin may fail otherwise.

💡 Tip: Make sure your virtual environment is activated before running maturin develop.

License

Commol is licensed under the MIT License. See LICENSE for details.

Authors

Citation

If you use Commol in your research, please cite:

@software{commol2025,
  title = {Commol: A High-Performance Compartment Modelling Library},
  author = {
    Villanueva Micó, Rafael J.
    and Andreu Vilarroig, Carlos
    and Martínez Rodríguez, David
  },
  year = {2025},
  url = {https://github.com/MUNQU/commol}
}

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

commol-0.1.0b1.tar.gz (298.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

commol-0.1.0b1-cp313-cp313-win_amd64.whl (2.2 MB view details)

Uploaded CPython 3.13Windows x86-64

commol-0.1.0b1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

commol-0.1.0b1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.2 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

commol-0.1.0b1-cp313-cp313-macosx_11_0_arm64.whl (2.0 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

commol-0.1.0b1-cp313-cp313-macosx_10_12_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

commol-0.1.0b1-cp312-cp312-win_amd64.whl (2.2 MB view details)

Uploaded CPython 3.12Windows x86-64

commol-0.1.0b1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

commol-0.1.0b1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.2 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

commol-0.1.0b1-cp312-cp312-macosx_11_0_arm64.whl (2.0 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

commol-0.1.0b1-cp312-cp312-macosx_10_12_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

File details

Details for the file commol-0.1.0b1.tar.gz.

File metadata

  • Download URL: commol-0.1.0b1.tar.gz
  • Upload date:
  • Size: 298.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for commol-0.1.0b1.tar.gz
Algorithm Hash digest
SHA256 ed0db0175eb2bd7784024825403585f36f30c9b20e88d371eba9bd0213f97b6e
MD5 418dd4230e5b3b9f27d20aa99206f50b
BLAKE2b-256 202cbc2839c7f79367632c97990a91d9372e7f6c535bf5821795486c84b50462

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1.tar.gz:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: commol-0.1.0b1-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 2.2 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for commol-0.1.0b1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 e4ac4bfa620c3e89d6df69e4e528a188e008d4314ea714e90ccc43dc96d8967d
MD5 7d116fab962e1545ccab9d7edd6bfe28
BLAKE2b-256 41dc5e1e54339556593b627e1e61738b348ae095502a8cd92beb6f58809618ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp313-cp313-win_amd64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 bea05f52b2dfde611de615956c4ad102eefafb58bfbb02b300626c5ecaaaffaa
MD5 c47c36376ace5d4e2357574231a001f3
BLAKE2b-256 2e8efbd9c6d0e28c01ff576ee6e0fcaa4a9d9b51a5dfc25b17d1c58cab0e2a43

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 f9d43b720cc177ecac60206bf2fe3d10b2642cb4abef8157190b8d3691b04191
MD5 9c8b02076b0514c23fbf39e96d50ea7b
BLAKE2b-256 70cba0c04ee3ed3f4da7155055588f0f511c5f221e45e9b38ea03a2099453343

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 03f4ba6e45048f8123a30c6ea5027ef40c2015c0bda93064a5395a61d05c8000
MD5 31f372babbc7414403f739c575e9923c
BLAKE2b-256 6d728100312cf94c55e6ff3045deac2552bef740e12aa44b2ed3814c06468f89

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 3c04e7a46d69d80710399f4b580e54da06f8007dc1fa78e27dddac41c5ef3e5d
MD5 b1de34ed40b5505df2950851fe6bcbf0
BLAKE2b-256 f5a3d3505ed4956de618b1776a2a7ff20c8ee15b4cedb7351b234c9590e408ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp313-cp313-macosx_10_12_x86_64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: commol-0.1.0b1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 2.2 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for commol-0.1.0b1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 6679c912a4546d46b601b0fecad34bf1221edb9edcaac369120a9885ba36b492
MD5 d6ab16cbaafa8e733b07850443808c68
BLAKE2b-256 c6f37b60828070636179b045e3c57fcae939abcecdd76af0933eb6f983006ceb

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp312-cp312-win_amd64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e57ceddae2516ab855805cc4d19485be8349892f332ba4671e81d90f314a56c9
MD5 c7b5e5c066213d3d11cbe23f56ee36aa
BLAKE2b-256 4c7aa08b11f9591d9d6c1892eaed407fdbd2852961de9ea919d90c52c56b3826

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9b6e870182eb800f651b9fac5fb8e8adc54cbc318b9aef501966461c9fe39b4c
MD5 6805c756492202967343753e0156556c
BLAKE2b-256 a66c85fe52ac2fec69314b18e0d44641f19f68ad07f4ecd59ce03d8f15d1e8fe

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c1165df44a16e28115b4f620ec9d1107243c97861aa8f6528152803103eedc6e
MD5 6da4e36fe09afafdda230e71be34b45c
BLAKE2b-256 f52bb40d71955036ed58fd3448ce8a1eeb1df531c521d027b7d9e99219ed9668

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file commol-0.1.0b1-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for commol-0.1.0b1-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 40371f64f0cb237a039943eae0eb0899d7b0429d3855f4a8a1c28c1532166226
MD5 02f4172f0b702b5e126b285cfc69a32a
BLAKE2b-256 73fd4b79a1e8b9f4bdb5842c291a548b5006af4483b2af618ecef7ced834de47

See more details on using hashes here.

Provenance

The following attestation bundles were made for commol-0.1.0b1-cp312-cp312-macosx_10_12_x86_64.whl:

Publisher: release.yaml on MUNQU/commol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page