vishal-1344 commited on
Commit
6ba1ba5
·
verified ·
1 Parent(s): 62c1e1d

Initial SCI framework upload (v1)

Browse files
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vishal Joshua Meesala
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.
README.md CHANGED
@@ -1,3 +1,133 @@
1
- ---
2
- license: mit
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SCI: Surgical Cognitive Interpreter
2
+ A Metacognitive Control Layer for Signal Dynamics
3
+
4
+ This repository contains the reference implementation of the Surgical Cognitive Interpreter (SCI), a closed-loop metacognitive controller that wraps existing models and turns prediction into a regulated process rather than a one-shot function evaluation.
5
+
6
+ SCI is introduced in:
7
+
8
+ Vishal Joshua Meesala
9
+ SCI: A Metacognitive Control for Signal Dynamics.
10
+ arXiv:2511.12240, 2025
11
+ https://arxiv.org/abs/2511.12240
12
+
13
+ The paper formalizes interpretability as a feedback-regulated state: SCI monitors a scalar interpretive signal SP(t), defined over reliability-weighted, multi-scale features, and adaptively adjusts an interpreter’s parameters to reduce interpretive error
14
+
15
+ ΔSP(t) = SP*(t) − SP(t)
16
+
17
+ under Lyapunov-style stability constraints.
18
+
19
+ 1. Motivation
20
+ Most neural networks are deployed as open-loop function approximators: they map inputs to outputs in a single forward pass, with no explicit mechanism to regulate how much computation, explanation quality, or clarification is applied to a given case. In safety–critical domains (medicine, industrial monitoring, environmental sensing), this is brittle:
21
+
22
+ Easy and ambiguous inputs receive the same computational budget.
23
+ Explanations are static, post hoc, and do not adapt under drift.
24
+ There is no explicit notion of “interpretive error” that can be monitored and controlled.
25
+ SCI addresses this by introducing a closed-loop metacognitive layer that:
26
+
27
+ Monitors a scalar interpretive state SP(t) ∈ [0, 1] over time.
28
+ Computes interpretive error ΔSP = SP* − SP relative to a target clarity level SP*.
29
+ Updates interpreter parameters Θ according to a Lyapunov-inspired rule with safeguards.
30
+ Allocates more inference steps and adaptation to ambiguous or unstable inputs.
31
+ Exposes ΔSP as a safety signal for abstention, escalation, or human-in-the-loop review.
32
+ Empirically, SCI:
33
+
34
+ Allocates roughly 3.6–3.8× more computation to misclassified inputs than to correct ones.
35
+ Produces a scalar safety signal ΔSP with AUROC ≈ 0.70–0.86 for detecting errors across vision, medical, and industrial benchmarks.
36
+ 2. Conceptual Overview
37
+ SCI is a modular architecture with the following core components.
38
+
39
+ 2.1 Decomposition Π
40
+ A multi-scale, multimodal feature bank P(t, s) that organizes raw signals X(t) into interpretable blocks:
41
+
42
+ Rhythmic components (frequency bands, oscillatory structure)
43
+ Trend components (low-frequency baselines, drifts)
44
+ Spatial / structural components (sensor topology, modes)
45
+ Cross-modal interactions (coherence, cross-correlation, causal couplings)
46
+ Compact but auditable latent composites Π*
47
+ Each feature is associated with a reliability weight w_f(t), derived from quantities such as:
48
+
49
+ Signal-to-noise ratio (SNR)
50
+ Temporal persistence
51
+ Multi-sensor or cross-modal coherence
52
+ These weights allow SCI to emphasize trustworthy features and down-weight degraded sensors or spurious patterns.
53
+
54
+ 2.2 Interpreter ψΘ
55
+ A knowledge-guided interpreter that maps the reliability-weighted feature bank into:
56
+
57
+ Markers m_k: human-meaningful states or concepts
58
+ Confidences p_k(t): calibrated probabilities
59
+ Rationales r_k(t): sparse feature-level attributions and/or templated text
60
+ The interpreter can be instantiated as a modest neural head (e.g., linear layer or shallow MLP) on top of P(t, s), optionally constrained by ontologies or domain rules.
61
+
62
+ 2.3 Surgical Precision (SP)
63
+ A scalar interpretive signal SP(t) ∈ [0, 1] that aggregates calibrated components such as:
64
+
65
+ Clarity / selectivity
66
+ Pattern strength
67
+ Domain consistency
68
+ Predictive alignment
69
+ In the minimal implementation, SP is instantiated as normalized entropy of a marker distribution or predictive distribution: high SP corresponds to focused, confident internal usage of markers; low SP indicates diffuse or ambiguous internal state.
70
+
71
+ 2.4 Closed-Loop Controller
72
+ A controller monitors ΔSP(t) and updates Θ accordingly. At a high level:
73
+
74
+ Compute ΔSP(t) = SP*(t) − SP(t) relative to a target SP*(t).
75
+
76
+ If |ΔSP(t)| exceeds a threshold, update parameters:
77
+
78
+ Θ_{t+1} = Proj_C [ Θ_t + η_t ( ΔSP(t) · ∇_Θ SP(t) + λ_h · u_h(t) ) ]
79
+
80
+ where:
81
+
82
+ η_t is a step-size schedule,
83
+ λ_h is a human-gain budget,
84
+ u_h(t) is a bounded human feedback signal (optional),
85
+ Proj_C enforces constraints (e.g., trust region, sparsity, or parameter bounds).
86
+ Lyapunov-style analysis shows that, under suitable conditions on η_t and λ_h, the “interpretive energy”
87
+
88
+ V(t) = ½ · (ΔSP(t))²
89
+
90
+ decreases monotonically up to bounded noise, so explanations become more stable and consistent over time.
91
+
92
+ This yields a reactive interpretability layer that not only explains but also stabilizes explanations under drift, feedback, and evolving conditions.
93
+
94
+ 3. Repository Structure
95
+ The repository is organized as follows:
96
+
97
+ sci/ # Core library
98
+ __init__.py
99
+ controller.py # SCIController: closed-loop update over Θ using ΔSP
100
+ interpreter.py # Interpreter / marker head and SP computation
101
+ sp_evaluator.py # SP and component metrics, calibration, logging
102
+ decomposition.py # Decomposition Π and reliability-weighted feature bank
103
+ reliability.py # Reliability scores (SNR, persistence, coherence)
104
+ utils.py # Shared utilities and helper functions
105
+
106
+ configs/ # Example configuration files
107
+ mnist.yaml
108
+ mitbih.yaml
109
+ bearings.yaml
110
+
111
+ examples/ # Jupyter notebooks (to be populated)
112
+ mnist_sci_demo.ipynb
113
+ ecg_sci_demo.ipynb
114
+ bearings_sci_demo.ipynb
115
+
116
+ experiments/ # Experiment scripts, logs, and analysis
117
+
118
+ scripts/ # Training utilities, Hub utilities, etc.
119
+ push_to_hub.py
120
+
121
+ run_sci_mitbih_fixed_k.py
122
+ run_sci_bearings.py
123
+ run_sci_signal_v2.py # Signal-domain SCI experiments
124
+
125
+ plot_metacognition_hero.py # Plotting script for metacognitive behavior
126
+ sc_arxiv.pdf # Paper PDF (for convenience)
127
+ sci_latex.tex # LaTeX source of the paper
128
+
129
+ pyproject.toml
130
+ setup.cfg
131
+ LICENSE
132
+ README.md
133
+
configs/bearings.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ dataset: bearings
2
+ feature_dim: 128
3
+ num_classes: 3
4
+ num_markers: 8
configs/mitbih.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ dataset: mitbih
2
+ feature_dim: 128
3
+ num_classes: 5
4
+ num_markers: 8
configs/mnist.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ dataset: mnist
2
+ feature_dim: 128
3
+ num_classes: 10
4
+ num_markers: 8
examples/bearings_demo.ipynb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [],
3
+ "metadata": {
4
+ "language_info": {
5
+ "name": "python"
6
+ }
7
+ },
8
+ "nbformat": 4,
9
+ "nbformat_minor": 5
10
+ }
examples/ecg_demo.ipynb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [],
3
+ "metadata": {
4
+ "language_info": {
5
+ "name": "python"
6
+ }
7
+ },
8
+ "nbformat": 4,
9
+ "nbformat_minor": 5
10
+ }
examples/mnist_demo.ipynb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [],
3
+ "metadata": {
4
+ "language_info": {
5
+ "name": "python"
6
+ }
7
+ },
8
+ "nbformat": 4,
9
+ "nbformat_minor": 5
10
+ }
experiments/mitbih_fixed_k/per_example.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
experiments/mitbih_sci_v2/per_example.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
experiments/mitbih_sci_v2/summary.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "acc_base": 0.8715,
4
+ "acc_sci": 0.879,
5
+ "mean_steps": 14.6775,
6
+ "err_reduction": 7.323214740265229,
7
+ "seed": 42
8
+ },
9
+ {
10
+ "acc_base": 0.8795,
11
+ "acc_sci": 0.885,
12
+ "mean_steps": 14.405,
13
+ "err_reduction": 6.982341868782045,
14
+ "seed": 100
15
+ },
16
+ {
17
+ "acc_base": 0.818,
18
+ "acc_sci": 0.8395,
19
+ "mean_steps": 15.8745,
20
+ "err_reduction": 3.616387450617109,
21
+ "seed": 2024
22
+ }
23
+ ]
experiments/mnist_sci_v2/per_example.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
plot_metacognition_hero.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import matplotlib.pyplot as plt
3
+ import numpy as np
4
+ import os
5
+
6
+ DOMAINS = [
7
+ ("experiments/mnist_sci_v2", "MNIST (Vision)"),
8
+ ("experiments/mitbih_sci_v2", "MIT-BIH (Medical)")
9
+ ]
10
+
11
+ def plot():
12
+ plt.figure(figsize=(10, 4))
13
+
14
+ for i, (path, name) in enumerate(DOMAINS):
15
+ log = os.path.join(path, "per_example.jsonl")
16
+ if not os.path.exists(log): continue
17
+
18
+ data = []
19
+ with open(log, 'r') as f:
20
+ for l in f: data.append(json.loads(l))
21
+
22
+ corr = [d['steps'] for d in data if d['correct_sci']]
23
+ wrong = [d['steps'] for d in data if not d['correct_sci']]
24
+
25
+ plt.subplot(1, 2, i+1)
26
+ plt.hist(corr, bins=np.arange(1, 26)-0.5, alpha=0.6, density=True, label='Correct', color='green')
27
+ plt.hist(wrong, bins=np.arange(1, 26)-0.5, alpha=0.6, density=True, label='Incorrect', color='red')
28
+ plt.title(f"{name}: Adaptive Compute")
29
+ plt.xlabel("Inference Steps")
30
+ plt.ylabel("Density")
31
+ plt.legend()
32
+
33
+ plt.tight_layout()
34
+ plt.savefig("metacognition_hero.png")
35
+ print("Saved metacognition_hero.png")
36
+
37
+ if __name__ == "__main__":
38
+ plot()
pyproject.toml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sci"
7
+ version = "0.0.0"
8
+ description = "Surgical Cognitive Interpreter (SCI) minimal prototype"
9
+ authors = [ { name = "Vishal Joshua Meesala" } ]
10
+ requires-python = ">=3.8"
run_sci_bearings.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ import torch.optim as optim
5
+ from torch.utils.data import Dataset, DataLoader
6
+ import numpy as np
7
+ import pandas as pd
8
+ import json
9
+ import os
10
+ import random
11
+ from sklearn.metrics import roc_auc_score
12
+
13
+ # --- CONFIGURATION ---
14
+ SEEDS = [42, 100, 2024]
15
+ BATCH_SIZE = 64
16
+ EPOCHS = 10
17
+ SP_TARGET = 0.85
18
+ MAX_STEPS = 25
19
+ PATIENCE = 3
20
+ TEMPERATURE = 0.5
21
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
22
+
23
+ # --- DATASET: PHYSICS-BASED BEARINGS ---
24
+ class SyntheticBearings(Dataset):
25
+ """
26
+ Simulates rotating machinery.
27
+ Class 0: Healthy (Sine Wave + Noise)
28
+ Class 1: Inner Race Fault (Impulses at specific frequencies)
29
+ """
30
+ def __init__(self, n_samples):
31
+ self.data = []
32
+ self.targets = []
33
+ t = np.linspace(0, 1, 200) # 200 time steps (0.2s at 1kHz)
34
+
35
+ for _ in range(n_samples):
36
+ # Base Carrier (Shaft Rotation 30Hz)
37
+ signal = 0.5 * np.sin(2 * np.pi * 30 * t)
38
+ label = 0
39
+
40
+ # Fault Injection (50% chance)
41
+ if np.random.rand() > 0.5:
42
+ label = 1
43
+ # Fault: High freq impulses (120Hz) decaying exponentially
44
+ fault_sig = 0.8 * np.sin(2 * np.pi * 120 * t) * np.exp(-5*t)
45
+ signal += fault_sig
46
+
47
+ # Industrial Noise
48
+ signal += np.random.normal(0, 0.4, size=len(t))
49
+
50
+ self.data.append(torch.tensor(signal, dtype=torch.float32).unsqueeze(0))
51
+ self.targets.append(label)
52
+
53
+ def __len__(self):
54
+ return len(self.data)
55
+
56
+ def __getitem__(self, idx):
57
+ return self.data[idx], self.targets[idx]
58
+
59
+ # --- MODEL ---
60
+ class BearingCNN(nn.Module):
61
+ def __init__(self):
62
+ super().__init__()
63
+ self.conv1 = nn.Conv1d(1, 16, 5)
64
+ self.conv2 = nn.Conv1d(16, 32, 5)
65
+ self.dropout = nn.Dropout(0.3)
66
+ self.pool = nn.MaxPool1d(2)
67
+ self.fc = nn.Linear(32 * 47, 2)
68
+
69
+ def forward(self, x):
70
+ x = self.pool(F.relu(self.conv1(x)))
71
+ x = self.pool(F.relu(self.conv2(x)))
72
+ x = self.dropout(x)
73
+ x = x.view(x.size(0), -1)
74
+ x = self.fc(x)
75
+ return x
76
+
77
+ # --- UTILS ---
78
+ def compute_sp(probs):
79
+ probs = torch.clamp(probs, min=1e-9)
80
+ entropy = -torch.sum(probs * torch.log(probs), dim=1)
81
+ sp = 1.0 - (entropy / np.log(2))
82
+ return sp
83
+
84
+ # --- RUNNER ---
85
+ def run_experiment(seed):
86
+ print(f"Running Bearings Seed {seed}...")
87
+ torch.manual_seed(seed)
88
+ np.random.seed(seed)
89
+
90
+ train_ds = SyntheticBearings(2000)
91
+ test_ds = SyntheticBearings(500)
92
+ train_loader = DataLoader(train_ds, batch_size=64, shuffle=True)
93
+ test_loader = DataLoader(test_ds, batch_size=1, shuffle=False)
94
+
95
+ model = BearingCNN().to(DEVICE)
96
+ opt = optim.Adam(model.parameters(), lr=0.001)
97
+
98
+ model.train()
99
+ for _ in range(EPOCHS):
100
+ for x, y in train_loader:
101
+ x, y = x.to(DEVICE), y.to(DEVICE)
102
+ opt.zero_grad()
103
+ out = model(x)
104
+ loss = F.cross_entropy(out, y)
105
+ loss.backward()
106
+ opt.step()
107
+
108
+ # Eval SCI
109
+ logs = []
110
+ with torch.no_grad():
111
+ for x, y in test_loader:
112
+ x = x.to(DEVICE)
113
+ accum = model(x)
114
+ steps = 1
115
+ sp_hist = []
116
+
117
+ while steps < MAX_STEPS:
118
+ new_logits = model(x)
119
+ accum += new_logits
120
+ steps += 1
121
+
122
+ curr_prob = F.softmax(accum/steps, dim=1)
123
+ curr_sp = compute_sp(curr_prob).item()
124
+ sp_hist.append(curr_sp)
125
+
126
+ # Convergence Check
127
+ if len(sp_hist) >= PATIENCE:
128
+ if abs(sp_hist[-1] - sp_hist[-PATIENCE]) < 0.005 and curr_sp > 0.8:
129
+ break
130
+
131
+ final_prob = F.softmax(accum/steps, dim=1)
132
+ pred = final_prob.argmax().item()
133
+ correct = (pred == y.item())
134
+ delta = abs(SP_TARGET - curr_sp)
135
+
136
+ logs.append({
137
+ "correct": int(correct),
138
+ "delta": delta,
139
+ "steps": steps
140
+ })
141
+
142
+ return logs
143
+
144
+ def analyze(logs):
145
+ df = pd.DataFrame(logs)
146
+ errors = 1 - df['correct']
147
+
148
+ # Safety Analysis
149
+ auc = roc_auc_score(errors, df['delta'])
150
+ steps_correct = df[df['correct']==1]['steps'].mean()
151
+ steps_wrong = df[df['correct']==0]['steps'].mean()
152
+
153
+ print("\n" + "="*40)
154
+ print("BEARINGS (INDUSTRIAL) RESULTS")
155
+ print("="*40)
156
+ print(f"Error Rate: {errors.mean()*100:.2f}%")
157
+ print(f"Safety AUROC: {auc:.4f}")
158
+ print("-" * 40)
159
+ print("Metacognition (Avg Steps):")
160
+ print(f"Correct: {steps_correct:.2f}")
161
+ print(f"Wrong: {steps_wrong:.2f}")
162
+ print("="*40)
163
+
164
+ if __name__ == "__main__":
165
+ all_logs = []
166
+ for s in SEEDS:
167
+ all_logs.extend(run_experiment(s))
168
+ analyze(all_logs)
run_sci_mitbih_fixed_k.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ import torch.optim as optim
5
+ from torch.utils.data import Dataset, DataLoader
6
+ import numpy as np
7
+ import pandas as pd
8
+ import json
9
+ import os
10
+ import random
11
+
12
+ # --- CONFIGURATION (COMPUTE MATCHED BASELINE) ---
13
+ # We match the ~15.6 steps from SCI v10
14
+ FIXED_K = 16
15
+ SEEDS = [42, 100, 2024]
16
+ BATCH_SIZE = 64
17
+ EPOCHS = 10
18
+ TEMPERATURE = 0.5
19
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
20
+ OUT_DIR = "experiments/mitbih_fixed_k"
21
+
22
+ # --- UTILS ---
23
+ def set_seed(seed):
24
+ torch.manual_seed(seed)
25
+ np.random.seed(seed)
26
+ random.seed(seed)
27
+ if torch.cuda.is_available():
28
+ torch.cuda.manual_seed(seed)
29
+
30
+ def compute_sp(probs):
31
+ probs = torch.clamp(probs, min=1e-9)
32
+ entropy = -torch.sum(probs * torch.log(probs), dim=1)
33
+ max_entropy = np.log(2)
34
+ sp = 1.0 - (entropy / max_entropy)
35
+ return sp
36
+
37
+ # --- DATASET ---
38
+ class RealMITBIH(Dataset):
39
+ def __init__(self, csv_file, limit=None):
40
+ df = pd.read_csv(csv_file, header=None)
41
+ df.iloc[:, 187] = df.iloc[:, 187].apply(lambda x: 0 if x == 0 else 1)
42
+ if limit:
43
+ df = df.sample(n=limit, random_state=42).reset_index(drop=True)
44
+ self.y = df.iloc[:, 187].values.astype(int)
45
+ self.X = df.iloc[:, :187].values.astype(np.float32)
46
+ self.X = np.expand_dims(self.X, axis=1)
47
+ num_neg = (self.y == 0).sum()
48
+ num_pos = (self.y == 1).sum()
49
+ self.pos_weight = num_neg / (num_pos + 1e-6)
50
+
51
+ def __len__(self):
52
+ return len(self.y)
53
+
54
+ def __getitem__(self, idx):
55
+ return torch.tensor(self.X[idx]), torch.tensor(self.y[idx])
56
+
57
+ # --- MODEL ---
58
+ class ECGCNN(nn.Module):
59
+ def __init__(self):
60
+ super(ECGCNN, self).__init__()
61
+ self.conv1 = nn.Conv1d(1, 32, 5)
62
+ self.conv2 = nn.Conv1d(32, 64, 5)
63
+ self.dropout1 = nn.Dropout(0.3)
64
+ self.dropout2 = nn.Dropout(0.5)
65
+ self.pool = nn.MaxPool1d(2)
66
+ self.global_pool = nn.AdaptiveAvgPool1d(1)
67
+ self.fc1 = nn.Linear(64, 64)
68
+ self.fc2 = nn.Linear(64, 2)
69
+
70
+ def forward(self, x):
71
+ x = self.pool(F.relu(self.conv1(x)))
72
+ x = self.pool(F.relu(self.conv2(x)))
73
+ x = self.dropout1(x)
74
+ x = self.global_pool(x)
75
+ x = x.view(x.size(0), -1)
76
+ x = F.relu(self.fc1(x))
77
+ x = self.dropout2(x)
78
+ x = self.fc2(x)
79
+ return x
80
+
81
+ # --- RUNNER ---
82
+ def run_experiment(seed):
83
+ print(f"\n>>> Running Fixed-K Baseline (K={FIXED_K}), Seed {seed}...")
84
+ set_seed(seed)
85
+
86
+ train_ds = RealMITBIH("mitbih_train.csv", limit=12000)
87
+ test_ds = RealMITBIH("mitbih_test.csv", limit=2000)
88
+ train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)
89
+ test_loader = DataLoader(test_ds, batch_size=1, shuffle=False)
90
+
91
+ model = ECGCNN().to(DEVICE)
92
+ optimizer = optim.Adam(model.parameters(), lr=0.001)
93
+ weight = torch.tensor([1.0, train_ds.pos_weight], dtype=torch.float32).to(DEVICE)
94
+ criterion = nn.CrossEntropyLoss(weight=weight)
95
+
96
+ model.train()
97
+ for epoch in range(EPOCHS):
98
+ for data, target in train_loader:
99
+ data, target = data.to(DEVICE), target.to(DEVICE)
100
+ optimizer.zero_grad()
101
+ output = model(data)
102
+ loss = criterion(output, target)
103
+ loss.backward()
104
+ optimizer.step()
105
+
106
+ per_example = []
107
+
108
+ with torch.no_grad():
109
+ for i, (data, target) in enumerate(test_loader):
110
+ data, target = data.to(DEVICE), target.to(DEVICE)
111
+
112
+ # FIXED K ENSEMBLE
113
+ accum_logits = model(data)
114
+
115
+ # Already did 1, do K-1 more
116
+ for _ in range(FIXED_K - 1):
117
+ accum_logits += model(data)
118
+
119
+ final_mean_logits = accum_logits / FIXED_K
120
+ probs = F.softmax(final_mean_logits / TEMPERATURE, dim=1)
121
+ sp = compute_sp(probs).item()
122
+ pred = probs.argmax(dim=1).item()
123
+ correct = (pred == target.item())
124
+
125
+ per_example.append({
126
+ "seed": seed,
127
+ "y_true": target.item(),
128
+ "correct": bool(correct),
129
+ "sp": sp,
130
+ "steps": FIXED_K
131
+ })
132
+
133
+ # Basic stats for print
134
+ acc = np.mean([1 if x['correct'] else 0 for x in per_example])
135
+ return {"acc": acc}, per_example
136
+
137
+ def main():
138
+ if not os.path.exists(OUT_DIR):
139
+ os.makedirs(OUT_DIR)
140
+
141
+ all_metrics = []
142
+ all_examples = []
143
+
144
+ for seed in SEEDS:
145
+ m, ex = run_experiment(seed)
146
+ all_metrics.append(m)
147
+ all_examples.extend(ex)
148
+ print(f"Seed {seed} Fixed-K Accuracy: {m['acc']:.4f}")
149
+
150
+ with open(f"{OUT_DIR}/per_example.jsonl", "w") as f:
151
+ for e in all_examples:
152
+ f.write(json.dumps(e) + "\n")
153
+
154
+ print(f"\nDone. Logs saved to {OUT_DIR}")
155
+
156
+ if __name__ == "__main__":
157
+ main()
run_sci_signal_v2.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ import torch.optim as optim
5
+ from torchvision import datasets, transforms
6
+ from torch.utils.data import DataLoader, Subset
7
+ import numpy as np
8
+
9
+ # --- CONFIGURATION v3 ---
10
+ BATCH_SIZE = 64
11
+ TRAIN_SIZE = 4000 # Increased for stability
12
+ TEST_SIZE = 1000
13
+ EPOCHS = 5 # Increased for better convergence
14
+ SP_TARGET = 0.85 # Realistically calibrated target (was 0.95)
15
+ MAX_STEPS = 15 # Give controller room to work
16
+ TEMPERATURE = 0.5 # Temperature scaling (sharpening)
17
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
18
+
19
+ # --- 1. MODEL DEFINITION ---
20
+ class SimpleCNN(nn.Module):
21
+ def __init__(self):
22
+ super(SimpleCNN, self).__init__()
23
+ self.conv1 = nn.Conv2d(1, 32, 3, 1) # Larger filters
24
+ self.conv2 = nn.Conv2d(32, 64, 3, 1)
25
+ self.dropout1 = nn.Dropout(0.25)
26
+ self.dropout2 = nn.Dropout(0.5)
27
+ self.fc1 = nn.Linear(9216, 128)
28
+ self.fc2 = nn.Linear(128, 10)
29
+
30
+ def forward(self, x):
31
+ x = self.conv1(x)
32
+ x = F.relu(x)
33
+ x = self.conv2(x)
34
+ x = F.relu(x)
35
+ x = F.max_pool2d(x, 2)
36
+ x = self.dropout1(x)
37
+ x = torch.flatten(x, 1)
38
+ x = self.fc1(x)
39
+ x = F.relu(x)
40
+ x = self.dropout2(x)
41
+ x = self.fc2(x)
42
+ return x # Returns logits
43
+
44
+ # --- 2. UTILS: SP CALCULATION ---
45
+ def compute_sp(probs):
46
+ """SP = 1 - (Entropy / MaxEntropy)"""
47
+ probs = torch.clamp(probs, min=1e-9)
48
+ entropy = -torch.sum(probs * torch.log(probs), dim=1)
49
+ max_entropy = np.log(10)
50
+ sp = 1.0 - (entropy / max_entropy)
51
+ return sp
52
+
53
+ # --- 3. TRAINING ---
54
+ def train_model():
55
+ print(f"Loading MNIST (Train: {TRAIN_SIZE}, Test: {TEST_SIZE})...")
56
+ transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
57
+
58
+ full_train = datasets.MNIST('./data', train=True, download=True, transform=transform)
59
+ train_loader = DataLoader(Subset(full_train, range(TRAIN_SIZE)), batch_size=BATCH_SIZE, shuffle=True)
60
+
61
+ model = SimpleCNN().to(DEVICE)
62
+ optimizer = optim.Adam(model.parameters(), lr=0.001)
63
+
64
+ model.train()
65
+ print(f"Training for {EPOCHS} epochs...")
66
+ for epoch in range(EPOCHS):
67
+ for data, target in train_loader:
68
+ data, target = data.to(DEVICE), target.to(DEVICE)
69
+ optimizer.zero_grad()
70
+ output = model(data)
71
+ loss = F.cross_entropy(output, target)
72
+ loss.backward()
73
+ optimizer.step()
74
+ return model
75
+
76
+ # --- 4. EVALUATION ---
77
+ def evaluate(model):
78
+ transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
79
+ test_loader = DataLoader(Subset(datasets.MNIST('./data', train=False, transform=transform), range(TEST_SIZE)), batch_size=1, shuffle=False)
80
+
81
+ base_acc, sci_acc = 0, 0
82
+ base_sp_list, sci_sp_list = [], []
83
+ sci_steps_list = []
84
+
85
+ model.train() # Stochastic mode ON
86
+
87
+ print(f"Running Inference (Target SP={SP_TARGET}, Temp={TEMPERATURE})...")
88
+
89
+ with torch.no_grad():
90
+ for i, (data, target) in enumerate(test_loader):
91
+ data, target = data.to(DEVICE), target.to(DEVICE)
92
+
93
+ # --- BASELINE (1 Pass) ---
94
+ logits = model(data)
95
+ # Apply Temperature Scaling to Baseline too for fair comparison
96
+ probs = F.softmax(logits / TEMPERATURE, dim=1)
97
+ sp = compute_sp(probs)
98
+ pred = probs.argmax(dim=1)
99
+
100
+ base_acc += pred.eq(target).sum().item()
101
+ base_sp_list.append(sp.item())
102
+
103
+ # --- SCI (Logit Averaging Controller) ---
104
+ accum_logits = logits.clone() # Start with first pass logits
105
+ steps = 1
106
+ current_sp = sp.item()
107
+
108
+ # Loop: while quality is low, compute more
109
+ while current_sp < SP_TARGET and steps < MAX_STEPS:
110
+ new_logits = model(data)
111
+ accum_logits += new_logits
112
+ steps += 1
113
+
114
+ # KEY CHANGE: Average Logits -> Softmax (Not Average Probs)
115
+ mean_logits = accum_logits / steps
116
+ current_probs = F.softmax(mean_logits / TEMPERATURE, dim=1)
117
+ current_sp = compute_sp(current_probs).item()
118
+
119
+ # Final Decision
120
+ final_mean_logits = accum_logits / steps
121
+ sci_probs = F.softmax(final_mean_logits / TEMPERATURE, dim=1)
122
+ sci_pred = sci_probs.argmax(dim=1)
123
+
124
+ sci_acc += sci_pred.eq(target).sum().item()
125
+ sci_sp_list.append(current_sp)
126
+ sci_steps_list.append(steps)
127
+
128
+ # --- 5. STATS ---
129
+ base_acc_pct = 100.0 * base_acc / TEST_SIZE
130
+ sci_acc_pct = 100.0 * sci_acc / TEST_SIZE
131
+ mean_base_sp = np.mean(base_sp_list)
132
+ mean_sci_sp = np.mean(sci_sp_list)
133
+
134
+ base_errors = [abs(SP_TARGET - sp) for sp in base_sp_list]
135
+ sci_errors = [abs(SP_TARGET - sp) for sp in sci_sp_list]
136
+
137
+ mean_base_error = np.mean(base_errors)
138
+ mean_sci_error = np.mean(sci_errors)
139
+ reduction = (mean_base_error - mean_sci_error) / mean_base_error * 100.0
140
+ avg_steps = np.mean(sci_steps_list)
141
+
142
+ print("\n" + "="*65)
143
+ print(f"RESULTS v3: SCI (Logit Avg + Temp Scaling) vs Baseline")
144
+ print("="*65)
145
+ print(f"{'Metric':<25} | {'Baseline':<10} | {'SCI (Adaptive)':<15}")
146
+ print("-" * 65)
147
+ print(f"{'Accuracy':<25} | {base_acc_pct:.2f}% | {sci_acc_pct:.2f}%")
148
+ print(f"{'Mean Surgical Precision':<25} | {mean_base_sp:.4f} | {mean_sci_sp:.4f}")
149
+ print(f"{'Mean Steps':<25} | {1.0:.2f} | {avg_steps:.2f}")
150
+ print("-" * 65)
151
+ print(f"{'Interpretive Error (dSP)':<25} | {mean_base_error:.4f} | {mean_sci_error:.4f}")
152
+ print(f"{'Error Reduction':<25} | - | {reduction:.2f}%")
153
+ print("="*65)
154
+
155
+ if __name__ == "__main__":
156
+ trained_model = train_model()
157
+ evaluate(trained_model)
sci/__init__.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SCI: Surgical Cognitive Interpreter
3
+ Metacognitive control for signal dynamics.
4
+
5
+ This package is structured to keep the top-level import lightweight:
6
+ - `import sci` does NOT import torch or heavy submodules immediately.
7
+ - Actual components are imported lazily when accessed.
8
+
9
+ Author: Vishal Joshua Meesala
10
+ """
11
+
12
+ from importlib import import_module
13
+ from typing import Any
14
+
15
+ __all__ = [
16
+ "SCIController",
17
+ "compute_sp",
18
+ "Interpreter",
19
+ "Decomposition",
20
+ "ReliabilityWeighting",
21
+ ]
22
+
23
+
24
+ def __getattr__(name: str) -> Any:
25
+ """
26
+ Lazy attribute access so that:
27
+
28
+ import sci
29
+ sci.SCIController
30
+
31
+ does not import torch until the attribute is actually used.
32
+ """
33
+ if name == "SCIController":
34
+ return import_module("sci.controller").SCIController
35
+ if name == "compute_sp":
36
+ return import_module("sci.sp").compute_sp
37
+ if name == "Interpreter":
38
+ return import_module("sci.interpreter").Interpreter
39
+ if name == "Decomposition":
40
+ return import_module("sci.decomposition").Decomposition
41
+ if name == "ReliabilityWeighting":
42
+ return import_module("sci.reliability").ReliabilityWeighting
43
+
44
+ raise AttributeError(f"module 'sci' has no attribute {name!r}")
45
+
sci/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (1.6 kB). View file
 
sci/__pycache__/controller.cpython-312.pyc ADDED
Binary file (3.32 kB). View file
 
sci/config.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Placeholder config module for SCI.
2
+
3
+ This file can be extended to expose default configuration
4
+ objects or helper loaders for YAML config files in `configs/`.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ DEFAULTS = {
10
+ "feature_dim": 128,
11
+ "num_markers": 8,
12
+ "num_classes": 10,
13
+ }
14
+
15
+ def load_yaml(path: str):
16
+ try:
17
+ import yaml
18
+ except Exception:
19
+ raise RuntimeError("PyYAML is required to load config files")
20
+ p = Path(path)
21
+ with p.open("r", encoding="utf-8") as f:
22
+ return yaml.safe_load(f)
sci/controller.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+
4
+
5
+ class SCIController(nn.Module):
6
+ """
7
+ Minimal SCI closed-loop controller.
8
+
9
+ It monitors a scalar interpretive state SP, compares it
10
+ to a target SP*, and performs a projected gradient-style
11
+ update on the interpreter parameters Θ based on ΔSP.
12
+
13
+ This is a simplified, minimal prototype to show the core idea.
14
+ """
15
+
16
+ def __init__(
17
+ self,
18
+ interpreter: nn.Module,
19
+ sp_target: float = 0.90,
20
+ eta: float = 0.01,
21
+ gamma: float = 0.10,
22
+ trust_region: float = 0.1,
23
+ ):
24
+ super().__init__()
25
+ self.interpreter = interpreter
26
+ self.sp_target = sp_target
27
+ self.eta = eta
28
+ self.gamma = gamma
29
+ self.trust_region = trust_region
30
+
31
+ @torch.no_grad()
32
+ def _project(self, theta: torch.Tensor, theta_old: torch.Tensor) -> torch.Tensor:
33
+ """Simple trust-region projection on parameter vector."""
34
+ delta = theta - theta_old
35
+ norm = delta.norm()
36
+ if norm > self.trust_region:
37
+ return theta_old + self.trust_region * delta / (norm + 1e-9)
38
+ return theta
39
+
40
+ def forward(self, x: torch.Tensor):
41
+ """
42
+ Run a single SCI control step.
43
+
44
+ Args:
45
+ x: input features (batch_size, feature_dim)
46
+
47
+ Returns:
48
+ pred: raw predictions (logits)
49
+ sp: scalar SP estimate (float tensor)
50
+ d_sp: SP* - SP
51
+ interpreter: the (possibly) updated interpreter module
52
+ """
53
+ sp, pred = self.interpreter.compute(x)
54
+ d_sp = self.sp_target - sp
55
+
56
+ # No-op zone: if |ΔSP| is small, do not update
57
+ if torch.abs(d_sp) < self.gamma:
58
+ return pred, sp, d_sp, self.interpreter
59
+
60
+ # Collect old parameters as a flat vector
61
+ theta_old = self.interpreter.parameters_vector().detach()
62
+
63
+ # Compute gradient of SP wrt parameters
64
+ grad = self.interpreter.grad_sp(x)
65
+
66
+ # Basic controller update: Θ_new = Θ_old + η * ΔSP * ∇Θ SP
67
+ theta_new = theta_old + self.eta * d_sp * grad
68
+
69
+ # Trust-region projection
70
+ theta_new = self._project(theta_new, theta_old)
71
+
72
+ # Push updated parameters back into the interpreter
73
+ self.interpreter.update_parameters(theta_new)
74
+
75
+ return pred, sp, d_sp, self.interpreter
sci/decomposition.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+
4
+ class Decomposition:
5
+ """
6
+ Placeholder semantic decomposition Π.
7
+
8
+ In the full SCI framework, this would include:
9
+ - Rhythmic features (FFT/STFT, wavelets, etc.)
10
+ - Trend features (detrending, SSA, etc.)
11
+ - Spatial / cross-modal features
12
+ Here we expose a simple identity mapping for now.
13
+ """
14
+
15
+ def __init__(self):
16
+ pass
17
+
18
+ def __call__(self, x: torch.Tensor) -> torch.Tensor:
19
+ # TODO: replace with real decomposition (e.g., STFT/wavelets)
20
+ return x
sci/interpreter.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from .sp import compute_sp
4
+
5
+
6
+ class Interpreter(nn.Module):
7
+ """
8
+ A lightweight SCI interpreter.
9
+
10
+ - Encodes input features into a hidden representation
11
+ - Emits marker logits (for SP)
12
+ - Emits task logits (for classification)
13
+
14
+ This is a minimal prototype; in practice you would replace
15
+ the feature encoder with a CNN/Transformer/etc.
16
+ """
17
+
18
+ def __init__(self, feature_dim: int = 128, num_markers: int = 8, num_classes: int = 10):
19
+ super().__init__()
20
+ self.encoder = nn.Linear(feature_dim, feature_dim)
21
+ self.marker_head = nn.Linear(feature_dim, num_markers)
22
+ self.classifier = nn.Linear(feature_dim, num_classes)
23
+
24
+ def encode(self, x: torch.Tensor) -> torch.Tensor:
25
+ h = torch.relu(self.encoder(x))
26
+ return h
27
+
28
+ def compute(self, x: torch.Tensor):
29
+ """
30
+ Compute SP and predictions for a batch of inputs.
31
+
32
+ Args:
33
+ x: tensor of shape (batch_size, feature_dim)
34
+
35
+ Returns:
36
+ sp_mean: scalar SP value (mean over batch)
37
+ logits: tensor of shape (batch_size, num_classes)
38
+ """
39
+ h = self.encode(x)
40
+ marker_logits = self.marker_head(h)
41
+ sp = compute_sp(marker_logits) # (batch_size,)
42
+ logits = self.classifier(h)
43
+ return sp.mean(), logits
44
+
45
+ def grad_sp(self, x: torch.Tensor) -> torch.Tensor:
46
+ """
47
+ Compute gradient of SP wrt parameters as a flat vector.
48
+ NOTE: This assumes gradients have been zeroed before calling.
49
+ """
50
+ self.zero_grad()
51
+ sp, _ = self.compute(x)
52
+ sp.backward()
53
+ grads = []
54
+ for p in self.parameters():
55
+ if p.grad is not None:
56
+ grads.append(p.grad.view(-1))
57
+ if not grads:
58
+ return torch.zeros(0)
59
+ return torch.cat(grads).detach()
60
+
61
+ @torch.no_grad()
62
+ def parameters_vector(self) -> torch.Tensor:
63
+ """Flatten all parameters into a single vector."""
64
+ return torch.cat([p.data.view(-1) for p in self.parameters()])
65
+
66
+ @torch.no_grad()
67
+ def update_parameters(self, new_theta: torch.Tensor) -> None:
68
+ """Load a flat parameter vector back into the module parameters."""
69
+ offset = 0
70
+ for p in self.parameters():
71
+ n = p.numel()
72
+ p.data.copy_(new_theta[offset : offset + n].view_as(p))
73
+ offset += n
sci/reliability.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+
4
+ class ReliabilityWeighting:
5
+ """
6
+ Placeholder reliability weighting.
7
+
8
+ In the full SCI framework this would:
9
+ - Estimate SNR, persistence, coherence for each feature
10
+ - Convert them to reliability scores z_f
11
+ - Normalize via a softmax to obtain weights w_f
12
+
13
+ For now, we return the input unchanged.
14
+ """
15
+
16
+ def __call__(self, features: torch.Tensor) -> torch.Tensor:
17
+ # TODO: implement reliability-based weighting
18
+ return features
sci/sp.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn.functional as F
3
+
4
+
5
+ def compute_sp(marker_logits: torch.Tensor) -> torch.Tensor:
6
+ """
7
+ Compute an entropy-based Surgical Precision (SP) score.
8
+
9
+ SP = 1 - H(q) / log(K), where:
10
+ - q = softmax(marker_logits)
11
+ - H(q) is Shannon entropy over markers
12
+ - K is the number of markers
13
+
14
+ Args:
15
+ marker_logits: tensor of shape (..., K)
16
+
17
+ Returns:
18
+ SP: tensor of shape (...,) with values in [0, 1].
19
+ """
20
+ q = F.softmax(marker_logits, dim=-1)
21
+ k = q.shape[-1]
22
+ entropy = -torch.sum(q * torch.log(q + 1e-9), dim=-1)
23
+ sp = 1.0 - entropy / torch.log(torch.tensor(float(k), device=marker_logits.device))
24
+ return sp
sci/utils.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+
4
+
5
+ def flatten_params(model: nn.Module) -> torch.Tensor:
6
+ """Flatten all parameters of a model into a single vector."""
7
+ return torch.cat([p.data.view(-1) for p in model.parameters()])
scripts/push_to_hub.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from huggingface_hub import HfApi, create_repo, upload_folder
2
+
3
+
4
+ def main():
5
+ repo_id = "vishal-1344/sci" # adjust if needed
6
+ api = HfApi()
7
+
8
+ # Create repo if it doesn't exist
9
+ create_repo(repo_id, exist_ok=True, repo_type="model")
10
+
11
+ # Upload entire project folder
12
+ upload_folder(
13
+ folder_path=".",
14
+ repo_id=repo_id,
15
+ repo_type="model",
16
+ commit_message="Initial SCI framework push",
17
+ )
18
+
19
+
20
+ if __name__ == "__main__":
21
+ main()
setup.cfg ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [metadata]
2
+ name = sci
3
+ version = 0.0.0
4
+ description = Surgical Cognitive Interpreter
5
+
6
+ [options]
7
+ packages = find:
8
+ install_requires =
9
+ torch
10
+ pyyaml