"""Original response-shape comparison; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7. Run from the output directory; writes bioassay-parallelism.png to cwd. All concentrations and responses are stipulated mathematical examples. """ from pathlib import Path import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import numpy as np def main(): x = np.geomspace(0.1, 1000, 600) reference = x / (10 + x) parallel_test = x / (5 + x) different_slope = x**2 / (100 + x**2) fig, axes = plt.subplots(1, 2, figsize=(9, 3.8), dpi=120, facecolor='white') for ax in axes: ax.set_xscale('log') ax.set_xlim(0.1, 1000) ax.set_ylim(0, 1.03) ax.set_xlabel('Concentration (arbitrary units)') ax.set_ylabel('Normalized response') ax.plot(x, reference, color='#225ea8', label='Reference') ax.grid(alpha=0.2) axes[0].plot(x, parallel_test, color='#b44d2b', label='Test: shared shape') axes[0].axhline(0.5, color='grey', lw=0.8, ls='--') axes[0].scatter([5, 10], [0.5, 0.5], color=['#b44d2b', '#225ea8'], zorder=4) axes[0].annotate('Equal-effect concentration ratio = 2', xy=(7, 0.5), xytext=(0.05, 0.21), textcoords='axes fraction', fontsize=8.5, arrowprops={'arrowstyle': '->', 'color': '0.3'}) axes[0].set_title('Parallel on log concentration') axes[1].plot(x, different_slope, color='#b44d2b', label='Test: different slope') for response, ratio in [(0.2, 0.5), (0.8, 2)]: xr = 10 * response / (1 - response) xt = 10 * np.sqrt(response / (1 - response)) axes[1].hlines(response, min(xr, xt), max(xr, xt), color='0.35', lw=1.5) axes[1].scatter([xr, xt], [response, response], color=['#225ea8', '#b44d2b'], zorder=4) axes[1].text(0.13, response + 0.03, f'Equal-effect ratio = {ratio:g}', fontsize=8.5) axes[1].set_title('Different slopes: ratio depends on response') for ax in axes: ax.legend(loc='lower right', fontsize=8) fig.suptitle('Equal-effect ratios need a shared response shape', fontsize=12) fig.tight_layout(rect=(0, 0, 1, 0.95)) fig.savefig(Path('bioassay-parallelism.png'), facecolor='white', dpi=120) plt.close(fig) if __name__ == '__main__': main()