Skip to content

Added JSON serialisation for Lambda functions#1695

Open
bennthomsen wants to merge 10 commits intoQCoDeS:mainfrom
bennthomsen:JSONserialisation
Open

Added JSON serialisation for Lambda functions#1695
bennthomsen wants to merge 10 commits intoQCoDeS:mainfrom
bennthomsen:JSONserialisation

Conversation

@bennthomsen
Copy link

Pull Request Summary

Title: Added JSON serialization for Lambda functions

Description

This PR adds support for serializing and deserializing BluePrint objects that contain arbitrary functions (arb_func), including lambda functions and named functions, to/from JSON format.

Changes

Files Modified:

  • src/broadbean/blueprint.py (+131 lines) - Core serialization/deserialization logic
  • tests/test_arb_func_serialization.py (+263 lines) - New comprehensive test suite
  • tests/test_blueprint.py (+1 line) - Updated existing test

Key Features

  1. Lambda Function Serialization: Lambda functions used with PulseAtoms.arb_func can now be serialized to JSON by storing their source code, extracted using inspect.getsource() with regex parsing.

  2. Named Function Serialization: Regular named functions are serialized with both their name and full source code, allowing reconstruction even in different Python sessions.

  3. Graceful Fallbacks: When functions cannot be reconstructed (e.g., missing dependencies or invalid source), the system falls back to a zero function with appropriate warnings.

  4. Sample Rate Persistence: The SR (sample rate) property is now included in the blueprint description and restored during deserialization.

  5. Backward Compatibility: Legacy JSON formats without the new func_type field are still supported.

Usage Example

import broadbean as bb
import numpy as np

# Create a blueprint with a lambda function using arb_func
bp = bb.BluePrint()

# Add a segment using arb_func with a lambda function
my_lambda = lambda t, **kwargs: np.sin(2 * np.pi * kwargs.get('freq', 1) * t)
bp.insertSegment(
    0, 
    bb.PulseAtoms.arb_func, 
    args=(my_lambda, {'freq': 10}),  # (function, kwargs_dict)
    name='sine_wave',
    dur=1e-6
)
bp.setSR(1e9)

# Serialize to JSON file
bp.write_to_json('my_blueprint.json')

# Later, reconstruct the blueprint from JSON
bp_restored = bb.BluePrint.init_from_json('my_blueprint.json')

# The lambda function is reconstructed and works as expected
print(bp_restored.description)

Example with a named function:

import broadbean as bb
import numpy as np

# Define a custom waveform function
def gaussian_pulse(t, **kwargs):
    amplitude = kwargs.get('amplitude', 1.0)
    sigma = kwargs.get('sigma', 1e-7)
    center = kwargs.get('center', 5e-7)
    return amplitude * np.exp(-((t - center) ** 2) / (2 * sigma ** 2))

# Create blueprint
bp = bb.BluePrint()
bp.insertSegment(
    0,
    bb.PulseAtoms.arb_func,
    args=(gaussian_pulse, {'amplitude': 0.5, 'sigma': 2e-7, 'center': 5e-7}),
    name='gaussian',
    dur=1e-6
)
bp.setSR(1e9)

# Save and restore
bp.write_to_json('gaussian_blueprint.json')
bp_restored = bb.BluePrint.init_from_json('gaussian_blueprint.json')

Technical Details

Serialization (description property):

  • Detects arb_func segments and extracts function information
  • For lambdas: extracts source code using regex to isolate the lambda expression
  • For named functions: stores both function name and complete source code
  • Stores associated kwargs dictionary

Deserialization (blueprint_from_description):

  • Reconstructs lambda functions using eval()
  • Reconstructs named functions by executing source code in isolated namespace
  • Falls back to global namespace lookup or zero function if reconstruction fails

Testing

Added comprehensive test suite (test_arb_func_serialization.py) covering:

  • Lambda function serialization round-trip
  • Named function serialization round-trip
  • Complex expressions with numpy operations
  • Kwargs preservation
  • JSON file write/read operations
  • Error handling and fallbacks
  • Integration with existing blueprint functionality

Breaking Changes

None - existing functionality and JSON format remains compatible.

@codecov
Copy link

codecov bot commented Feb 3, 2026

Codecov Report

❌ Patch coverage is 42.42424% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.16%. Comparing base (ee22118) to head (93d64c2).

Files with missing lines Patch % Lines
src/broadbean/blueprint.py 42.42% 38 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1695      +/-   ##
==========================================
- Coverage   72.12%   71.16%   -0.97%     
==========================================
  Files           9        9              
  Lines        1539     1602      +63     
==========================================
+ Hits         1110     1140      +30     
- Misses        429      462      +33     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are there two duplicated(?) plots instead of one in this example? or is it github mis-rendering?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be working fine in VScode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants