Compare commits

..

17 Commits

Author SHA1 Message Date
93a6d8683f updated readme with infocalc info 2026-03-11 20:29:55 +00:00
bd93399700 Ignore generated files and emacs tempfiles 2026-03-11 20:25:01 +00:00
0887a9effa cleanup 2026-03-11 20:23:07 +00:00
16327adb58 Studnet t-distribution is more accurate small samples 2026-03-11 20:20:32 +00:00
f4711640da cleaning up old attempts 2026-03-11 19:48:18 +00:00
6efdb3e241 Updated tests to new arguments 2026-03-11 19:45:14 +00:00
dda8587dd8 Single limit/tolerance option now working 2026-02-27 20:06:52 +01:00
3823372449 options to control the legend and font 2026-02-27 18:33:19 +01:00
2f7924e580 Option to have stats on graph or not 2026-02-27 17:55:12 +01:00
35ca52fd84 quick tests that the modes are working 2026-02-27 17:51:59 +01:00
878ca95eed SIM and DATA mode now 2026-02-27 17:51:56 +01:00
e0a75514b5 command line args and option to normalize y 2026-02-27 17:22:08 +01:00
04c1dd53da quick testdata in csv 2026-02-27 16:40:02 +01:00
4f92b12704 Merge branch 'main' of ssh://gitea.cs.ru.is:2222/AxiomaticDesign/ad-calc 2026-02-27 16:34:30 +01:00
882b590865 Fixed the y-scaling problem 2026-02-27 16:34:08 +01:00
74518e7daf Created some SADDL files to go with the toy example in the paper 2026-02-26 11:04:32 +00:00
2281ab1b27 bits unit for Info 2025-12-12 14:20:03 +00:00
10 changed files with 234 additions and 80 deletions

4
.gitignore vendored
View File

@@ -1 +1,5 @@
*~
*.log
flycheck_*.py
\#*#
test.pdf

View File

@@ -1,3 +1,5 @@
# ad-calc
Tools to help calculating values for Axiomatic Design analysis
`infocalc.py` calculates information content based upon a csv file or statistical parameters and upper/lower limits

180
infocalc.py Executable file
View File

@@ -0,0 +1,180 @@
#!/usr/bin/env python
## Axiomatic Design Information Calculator (and plotter)
## Author: Joseph Timothy foley <foley AT RU.IS>
## Start Date: 2026-02-27
## Input: data in csv file
## Output: information calculation and PDF for report/presentation
import os
import logging
import argparse
from pathlib import PurePath##https://docs.python.org/3/library/pathlib.html#module-pathlib
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.stats import norm,t
import scipy.stats
import pandas as pd
#Main program loop
print("""Axiomatic Design Information Calculator by Joseph. T. Foley<foley AT ru DOT is>
From https://gitea.cs.ru.is/AxiomaticDesign/adcalc/""")
parser = argparse.ArgumentParser(
description="Axiomatic Design Information Calculator.")
subparsers = parser.add_subparsers(dest='mode')
subparsers.required = True
### MODE DATA
parser_data = subparsers.add_parser("DATA")
parser_data.add_argument('csvfile',
help="CSV file with data and headers")
parser_data.add_argument('column',
help='Which column header to take data from')
## MODE SIM
parser_sim = subparsers.add_parser("SIM")
parser_sim.add_argument('samplesize', type=int,
help="sample size")
parser_sim.add_argument('mean', type=float,
help="mean(average) value")
parser_sim.add_argument('stddev', type=float,
help="sample standard deviation")
## General Arguments
parser.add_argument('--lowerbound', type=float,
help='Tolerance low limit')
parser.add_argument('--upperbound', type=float,
help='Tolerance high limit')
parser.add_argument('--normalizey', action="store_true",
help='Set y-axis to normalized probability density')
parser.add_argument('--log', default="INFO",
help='Console log level: Number or DEBUG, INFO, WARNING, ERROR')
parser.add_argument('--legend', action="store_true",
help='Put legend on the PDF graph')
parser.add_argument('--graphinfo', action="store_true",
help='Put information on the PDF graph')
parser.add_argument('--xlabel',
help='X-axis label, if needed')
parser.add_argument('--outfile',
help="output graph to PDF file")
parser.add_argument('--fontsize', default=14, type=int,
help="Adjust font size")
args = parser.parse_args()
## Set up logging
numeric_level = getattr(logging, args.log.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError(f'Invalid log level: {args.log}')
#print(f"Log level: {numeric_level}")
logger = logging.getLogger("app")
logger.setLevel(numeric_level)
# log everything to file
logpath = "infocalc.log"
fh = logging.FileHandler(logpath)
fh.setLevel(logging.DEBUG)
# log to console
ch = logging.StreamHandler()
ch.setLevel(numeric_level)
# create formatter and add to handlers
consoleformatter = logging.Formatter('%(message)s')
ch.setFormatter(consoleformatter)
spamformatter = logging.Formatter('%(asctime)s %(name)s[%(levelname)s] %(message)s')
fh.setFormatter(spamformatter)
# add the handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)
logger.debug("Creating infocalc log file %s", logpath)
# seed values for variable scoping
mean = 0
stddev = 1
samplesize =1
if args.mode == "DATA":
# filename pre-processing for output
inpath = PurePath(args.csvfile)
print(f"Input: {inpath}")
# grab the data and process
data = np.array(pd.read_csv(inpath)[args.column])
mean = data.mean()
stddev = data.std(ddof=1)
# Delta Degrees of Freedom: ddof=0 for population, ddof=1 for sample std dev
samplesize = len(data)
elif args.mode == "SIM":
mean = args.mean
stddev = args.stddev
samplesize = args.samplesize
df = samplesize - 1
prob = 0
if args.upperbound and args.lowerbound:
prob = t.cdf(df,args.upperbound, mean, stddev) - t.cdf(df,args.lowerbound, mean, stddev)
elif args.upperbound:
prob = t.cdf(df,args.upperbound, mean, stddev)
elif args.lowerbound:
prob = 1 - t.cdf(df,args.lowerbound, mean, stddev)
else:
prob = 1# no bounds set!
#print("probability: %f", prob)
info = -np.emath.log2(prob)
#print("information content: %f bits", info)
## set default fontsize
matplotlib.rcParams['font.size']=args.fontsize
## place text on plot: https://matplotlib.org/3.3.4/gallery/recipes/placing_text_boxes.html
fig, ax = plt.subplots()
if args.graphinfo:#put info on corner of graph
textstr = '\n'.join((
r'$n=%d$' % (samplesize),
r'$\mu=%.2f$' % (mean, ),
r'$\sigma=%.2f$' % (stddev, ),
r'$P=%.2f$' % (prob, ),
r'$I=%.2f$ bits' % (info, )))
# these are matplotlib.patch.Patch properties
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
# place a text box in upper left in axes coords
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=args.fontsize,
verticalalignment='top', bbox=props)
xgraphlimits = {"min": mean-3*stddev, "max": mean+3*stddev}
if args.lowerbound and xgraphlimits["min"] > args.lowerbound:
xgraphlimits["min"] = args.lowerbound
if args.upperbound and xgraphlimits["max"] < args.upperbound:
xgraphlimits["max"] = args.upperbound
x = np.linspace(xgraphlimits["min"], xgraphlimits["max"], 500)
y = norm.pdf(x, loc=mean, scale=stddev)
if args.normalizey:
y = y * stddev#rescale back to unity area
plt.axvline(x=mean, color="green", linestyle="dashed", label="mean")
if args.lowerbound:
plt.axvline(args.lowerbound, color="red")
if args.upperbound:
plt.axvline(args.upperbound, color="red")
plt.plot(x, y, 'b-', label='Normal distribution')
#yt = scipy.stats.t.pdf(x, len(data)-1, mean, stddev)
#plt.plot(x, yt, 'g-', label='T Distribution')
# Filter for which region to fill
coloredregion = x#default fill all
if args.lowerbound and args.upperbound:
coloredregion = (x >= args.lowerbound) & ( x <= args.upperbound )
elif args.upperbound:
coloredregion = x <= args.upperbound
elif args.lowerbound:
coloredregion = x >= args.lowerbound
plt.fill_between(x, 0, y, where=coloredregion, color="grey", alpha=0.5, label="Design range",)
if args.xlabel:
plt.xlabel(args.xlabel)
plt.ylabel('Probability density')
if args.legend:
plt.legend()
#plt.grid(True)
top = plt.ylim()[1]
if args.outfile:
logger.info(f"Graph output to {args.outfile}")
plt.savefig(args.outfile,bbox_inches='tight')
else:
plt.show()

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
## Data goes here for now --foley
data = np.array([1, 1.1, 0.9, 1, 1, 0.9, 0.9])
lowerbound = 0.9
upperbound = 1.0
mean = data.mean()
stddev = data.std(ddof=1)
# Delta Degrees of Freedom: ddof=0 for population, ddof=1 for sample std dev
prob = norm.cdf(upperbound, mean, stddev) - norm.cdf(lowerbound, mean, stddev)
#print("probability: %f", prob)
info = -np.emath.log2(prob)
#print("information content: %f bits", info)
## place text on plot: https://matplotlib.org/3.3.4/gallery/recipes/placing_text_boxes.html
fig, ax = plt.subplots()
textstr = '\n'.join((
r'$n=%d$' % (len(data)),
r'$\mu=%.2f$' % (mean, ),
r'$P=%.2f$' % (prob, ),
r'$I=%.2f$' % (info, )))
# these are matplotlib.patch.Patch properties
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
# place a text box in upper left in axes coords
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14,
verticalalignment='top', bbox=props)
x = np.linspace(mean-3*stddev, mean+3*stddev, 500)
y = norm.pdf(x, mean, stddev)
plt.axvline(x=mean, color="green", linestyle="dashed", label="mean")
plt.axvline(lowerbound, color="red")
plt.axvline(upperbound, color="red")
plt.plot(x, y, 'b-', label='Normal distribution')
#yt = scipy.stats.t.pdf(x, len(data)-1, mean, stddev)
#plt.plot(x, yt, 'g-', label='T Distribution')
coloredregion = (x >= lowerbound) & ( x <= upperbound ) #select x values
plt.fill_between(x, 0, y, where=coloredregion, color="grey", alpha=0.5, label="Design range")
plt.xlabel('X')
plt.ylabel('Probability density')
plt.legend()
plt.grid(True)
top = plt.ylim()[1]
plt.show()
# annotate values on X after drawing the graphs

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env python
import pandas as pd
df = pd.DataFrame(
{
"Name": [
"Braund, Mr. Owen Harris",
"Allen, Mr. William Hentry",
"Bonnell, Miss. Elizabeth",
],
"Age": [22, 35, 58],
"Sex": ["male", "male", "female"],
}
)
print(df.describe())
titanic = pd.read_csv("titanic.csv")
print(titanic.head(8))
titanic.to_excel("titanic.xlsx", sheet_name="passengers", index=False)
titanic_xltest = pd.read_excel("titanic.xlsx", sheet_name="passengers")
print("INFO")
print(titanic.info())

8
testdata.csv Normal file
View File

@@ -0,0 +1,8 @@
data1,data2,
1.0,1.1,
1.1,1.2,
0.9,1.3,
1.0,1.4,
1.0,1.5,
0.9,1.6,
0.9,1.7,
1 data1 data2
2 1.0 1.1
3 1.1 1.2
4 0.9 1.3
5 1.0 1.4
6 1.0 1.5
7 0.9 1.6
8 0.9 1.7

6
tests.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
# Get infocalc.py from https://gitea.cs.ru.is/AxiomaticDesign/adcalc
echo "Loading data from file"
./infocalc.py --lowerbound 0.9 --upperbound 1.1 --graphinfo DATA testdata.csv data1
echo "Creating simulated curve from parameters"
./infocalc.py --lowerbound 0.9 --upperbound 1.1 SIM 8 1.0 0.5

View File

@@ -0,0 +1,11 @@
DEFINE CN1 as 8-year-old child can operate
DEFINE CN2 as Flies 3 m up
DEFINE CN3 as Flies at least 5 m from 1 m starting height
DEFINE CN4 as Doesn't injur people
DEFINE CN5 as Doesn't shatter glass windows
DEFINE CN6 as Fuel is easily available in common households
DEFINE FR1 as When shot up vertically, the "rocket" flies at least 3m
DEFINE FR2 as When shot horizontally from an elevation of 1 m, the "rocket" travels at least 5 m
DEFINE FR3 as The maximum kinetic energy at launch is less than 0.5 J
DEFINE FR4 as The "rocket's" impact area is at least 30 mm²
DEFINE DP1 as Hybrid launcher with rocket non-propelled

View File

@@ -0,0 +1,11 @@
DEFINE CN1 as 8-year-old child can operate
DEFINE CN2 as Flies 3 m up
DEFINE CN3 as Flies at least 5 m from 1 m starting height
DEFINE CN4 as Doesn't injur people
DEFINE CN5 as Doesn't shatter glass windows
DEFINE CN6 as Fuel is easily available in common households
DEFINE FR1 as When shot up vertically, the "rocket" flies at least 3m
DEFINE FR2 as When shot horizontally from an elevation of 1 m, the "rocket" travels at least 5 m
DEFINE FR3 as The maximum kinetic energy at launch is less than 0.5 J
DEFINE FR4 as The "rocket's" impact area is at least 30 mm²
DEFINE DP1 as Kinetic-reaction self-launch

View File

@@ -0,0 +1,11 @@
DEFINE CN1 as 8-year-old child can operate
DEFINE CN2 as Flies 3 m up
DEFINE CN3 as Flies at least 5 m from 1 m starting height
DEFINE CN4 as Doesn't injur people
DEFINE CN5 as Doesn't shatter glass windows
DEFINE CN6 as Fuel is easily available in common households
DEFINE FR1 as When shot up vertically, the "rocket" flies at least 3m
DEFINE FR2 as When shot horizontally from an elevation of 1 m, the "rocket" travels at least 5 m
DEFINE FR3 as The maximum kinetic energy at launch is less than 0.5 J
DEFINE FR4 as The "rocket's" impact area is at least 30 mm²
DEFINE DP1 as Slingshot launcher