DanielSwift commited on
Commit
2249e80
·
0 Parent(s):

Initial SFOSR system with Gradio interface

Browse files
README.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: SFOSR
3
+ emoji: 🏃
4
+ colorFrom: yellow
5
+ colorTo: gray
6
+ sdk: gradio
7
+ sdk_version: 5.24.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ short_description: SFOSR System
12
+ ---
13
+
14
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
15
+
16
+ # SFOSR: Система Формальной Оценки Смысла и Верификации
17
+
18
+ This project implements core components of the SFOSR theory, including semantic analysis, contract verification, and proof construction using both input data and a knowledge base.
19
+
20
+ ## Project Structure
21
+
22
+ - `sfosr_core/`: Contains the main system logic (`integrated_sfosr.py`, `sfosr_database.py`).
23
+ - `tests/`: Contains unit tests (`test_*.py`).
24
+ - `docs/`: Contains documentation and theoretical papers related to SFOSR.
25
+ - `archive/`: Contains archived materials (e.g., old databases).
26
+ - `sfosr.db`: The main SQLite database containing concepts, vectors, rules, etc.
27
+ - `requirements.txt`: Project dependencies.
28
+ - `README.md`: This file.
29
+
30
+ ## Installation
31
+
32
+ (Currently, no external dependencies are required beyond standard Python libraries.)
33
+
34
+ ```bash
35
+ # It's recommended to use a virtual environment
36
+ python -m venv venv
37
+ source venv/bin/activate # On Windows use `venv\\Scripts\\activate`
38
+
39
+ # Install dependencies (if any added later)
40
+ pip install -r requirements.txt
41
+ ```
42
+
43
+ ## Running Tests
44
+
45
+ To run all tests, execute the following command from the project root directory:
46
+
47
+ ```bash
48
+ python -m unittest discover tests -v
49
+ ```
50
+
51
+ ## Current Capabilities
52
+
53
+ - Analyzes SFOSR structures for syntactic validity.
54
+ - Verifies vectors against database concepts and predefined contracts.
55
+ - Constructs proofs based on input vectors, prioritizing them first.
56
+ - Integrates knowledge from the `sfosr.db` database into the proof process if input vectors are insufficient.
57
+ - Supports inference rules: `chain_rule`, `causality_transfer`, `implication_causality_chain`, `part_of_transitivity`.
58
+ - Correctly handles cyclic dependencies in proof paths.
59
+
60
+ ## Known Limitations / Future Work
61
+
62
+ Запуск `python integrated_sfosr.py` демонстрирует обработку примера с построением доказательства и выводом **оценок достоверности**.
63
+
64
+ ## Вклад в проект
65
+
66
+ Приглашаем заинтересованных исследователей и разработчиков присоединиться к развитию SFOSR.
67
+
68
+ ## Лицензия
69
+
70
+ Проект SFOSR распространяется под лицензией MIT.
app.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio application for SFOSR (Semantic Formal Ontology Structure Representation) System.
3
+
4
+ Provides an interface to analyze, verify, and construct proofs using the SFOSR framework.
5
+ """
6
+
7
+ import gradio as gr
8
+ import json
9
+ import os
10
+ from sfosr_core.sfosr_system import SFOSRSystem # Assuming sfosr_system.py is in sfosr_core
11
+
12
+ # --- Configuration ---
13
+ DB_PATH = "sfosr.db" # Assumes the database is in the root directory
14
+
15
+ # Check if DB exists, otherwise handle appropriately (e.g., log an error)
16
+ if not os.path.exists(DB_PATH):
17
+ print(f"Error: Database file not found at {DB_PATH}. Please ensure it exists.")
18
+ # In a real scenario, you might want to exit or provide a way to upload/create it.
19
+ sfosr_instance = None # Indicate that the system is not ready
20
+ else:
21
+ # --- Initialize SFOSR System ---
22
+ try:
23
+ sfosr_instance = SFOSRSystem(db_path=DB_PATH)
24
+ print("SFOSR System initialized successfully.")
25
+ except Exception as e:
26
+ print(f"Error initializing SFOSR System: {e}")
27
+ sfosr_instance = None # Indicate that the system failed to initialize
28
+
29
+ # --- Helper Functions for Gradio ---
30
+
31
+ def handle_system_unavailable():
32
+ """Returns an error message if the SFOSR system isn't ready."""
33
+ return "SFOSR System is not available. Please check server logs.", "", "", ""
34
+
35
+ def process_input_json(json_string):
36
+ """Safely parse input JSON string."""
37
+ if not json_string:
38
+ return None, "Input JSON cannot be empty."
39
+ try:
40
+ data = json.loads(json_string)
41
+ if not isinstance(data, dict):
42
+ return None, "Input must be a valid JSON object (dictionary)."
43
+ if "vectors" not in data or not isinstance(data["vectors"], list):
44
+ return None, "Input JSON must contain a 'vectors' key with a list of vectors."
45
+ # Add more validation as needed (e.g., text key)
46
+ return data, None
47
+ except json.JSONDecodeError as e:
48
+ return None, f"Invalid JSON format: {e}"
49
+ except Exception as e:
50
+ return None, f"Error processing input: {e}"
51
+
52
+ def run_analysis_verification(input_json_str):
53
+ """Gradio function to run analysis and verification."""
54
+ if sfosr_instance is None:
55
+ return handle_system_unavailable()
56
+
57
+ input_data, error = process_input_json(input_json_str)
58
+ if error:
59
+ return f"Input Error: {error}", "", "", ""
60
+
61
+ try:
62
+ # Use the main process method which handles both analysis and verification
63
+ result = sfosr_instance.process(input_data)
64
+
65
+ # Format the output
66
+ analysis_summary = f"**Analysis Status:** {result.get('analysis', {}).get('status', 'N/A')}\n" \
67
+ f"**Compilable:** {result.get('analysis', {}).get('is_compilable', 'N/A')}\n" \
68
+ f"**Graph Metrics:** {result.get('analysis', {}).get('graph_metrics', {})}"
69
+
70
+ verification_summary = f"**Total Vectors Processed:** {result.get('verification', {}).get('total_vectors', 0)}\n" \
71
+ f"**Valid Vectors:** {result.get('verification', {}).get('valid_count', 0)}\n" \
72
+ f"**Compliance Rate:** {result.get('verification', {}).get('compliance_rate', 0.0):.2f}"
73
+
74
+ vector_details = result.get('verification', {}).get('vectors_data', {})
75
+
76
+ # Optional: Add graph visualization logic here later
77
+ graph_output = "Graph visualization placeholder"
78
+
79
+ return analysis_summary, verification_summary, vector_details, graph_output
80
+
81
+ except Exception as e:
82
+ print(f"Error during SFOSR processing: {e}") # Log for debugging
83
+ return f"An error occurred: {e}", "", "", ""
84
+
85
+ def run_proof_construction(input_json_str, source_concept, target_concept):
86
+ """Gradio function to run proof construction."""
87
+ if sfosr_instance is None:
88
+ return handle_system_unavailable()[:1] + ("",) # Only return one value for proof status
89
+
90
+ input_data, error = process_input_json(input_json_str)
91
+ if error:
92
+ return f"Input Error: {error}", ""
93
+
94
+ if not source_concept or not target_concept:
95
+ return "Source and Target concepts cannot be empty.", ""
96
+
97
+ # Add the proof query to the input data
98
+ input_data["proof_query"] = {
99
+ "source": source_concept,
100
+ "target": target_concept
101
+ }
102
+
103
+ try:
104
+ # Use the main process method - it includes proof if query exists
105
+ result = sfosr_instance.process(input_data)
106
+
107
+ proof_result = result.get("proof")
108
+
109
+ if not proof_result:
110
+ # This might happen if the process function structure changes or proof wasn't run
111
+ return "Proof was not attempted or failed silently.", ""
112
+
113
+ proof_status = f"**Proof Status:** {proof_result.get('status', 'N/A')}\n" \
114
+ f"**Is Valid:** {proof_result.get('is_valid', 'N/A')}"
115
+ if proof_result.get('reason'):
116
+ proof_status += f"\n**Reason:** {proof_result.get('reason')}"
117
+
118
+ proof_details = proof_result # Return the whole proof structure for now
119
+
120
+ return proof_status, proof_details
121
+
122
+ except Exception as e:
123
+ print(f"Error during SFOSR proof: {e}") # Log for debugging
124
+ return f"An error occurred: {e}", ""
125
+
126
+
127
+ # --- Gradio Interface Definition ---
128
+
129
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
130
+ gr.Markdown(
131
+ """
132
+ # SFOSR: Semantic Formal Ontology Structure Representation
133
+ Interact with the SFOSR system to analyze, verify semantic structures,
134
+ and construct formal proofs based on input vectors.
135
+ Provide input data in the specified JSON format.
136
+ [Link to GitHub Repo - Placeholder]
137
+ """
138
+ )
139
+
140
+ with gr.Tabs():
141
+ with gr.TabItem("Analyze & Verify"):
142
+ with gr.Row():
143
+ with gr.Column(scale=1):
144
+ gr.Markdown("### Input Data (JSON)")
145
+ input_json_av = gr.Textbox(
146
+ lines=15,
147
+ label="SFOSR JSON Input",
148
+ info="Paste the JSON containing 'text' (optional) and 'vectors' (required list).",
149
+ placeholder='{\n "text": "Example context...",\n "vectors": [\n {\n "id": "V1",\n "source": "ConceptA",\n "target": "ConceptB",\n "type": "Causality",\n "axis": "relationship",\n "justification": "A causes B based on evidence X."\n }\n // ... more vectors\n ],\n "instance_definitions": {\n "Inst1": {"is_a": "ConceptA", "label": "My Instance"}\n }\n}'
150
+ )
151
+ av_button = gr.Button("Run Analysis & Verification", variant="primary")
152
+ with gr.Column(scale=1):
153
+ gr.Markdown("### Analysis Summary")
154
+ analysis_output = gr.Markdown()
155
+ gr.Markdown("### Verification Summary")
156
+ verification_output = gr.Markdown()
157
+ gr.Markdown("### Vector Verification Details")
158
+ vector_details_output = gr.JSON(label="Vector Details")
159
+ gr.Markdown("### Concept Graph (Placeholder)")
160
+ graph_placeholder_output = gr.Textbox(label="Graph Info") # Placeholder
161
+
162
+ # Add examples later using gr.Examples
163
+ # gr.Examples(
164
+ # examples=[
165
+ # [sample_json_1],
166
+ # [sample_json_2]
167
+ # ],
168
+ # inputs=input_json_av
169
+ # )
170
+
171
+ with gr.TabItem("Construct Proof"):
172
+ with gr.Row():
173
+ with gr.Column(scale=1):
174
+ gr.Markdown("### Input Data & Query (JSON)")
175
+ input_json_p = gr.Textbox(
176
+ lines=10,
177
+ label="SFOSR JSON Context",
178
+ info="Paste the JSON containing 'vectors' to be used as premises.",
179
+ placeholder='{\n "vectors": [\n {\n "id": "V1", "source": "A", "target": "B", "type": "Implication", "axis": "logic", "is_valid": true \n },\n {\n "id": "V2", "source": "B", "target": "C", "type": "Implication", "axis": "logic", "is_valid": true \n }\n ]\n}'
180
+ )
181
+ source_concept_input = gr.Textbox(label="Source Concept", info="The starting concept for the proof.")
182
+ target_concept_input = gr.Textbox(label="Target Concept", info="The concept to prove reachability for.")
183
+ p_button = gr.Button("Find Proof", variant="primary")
184
+ with gr.Column(scale=1):
185
+ gr.Markdown("### Proof Result")
186
+ proof_status_output = gr.Markdown()
187
+ gr.Markdown("### Proof Details / Path")
188
+ proof_details_output = gr.JSON(label="Proof Structure")
189
+
190
+ # Add examples later using gr.Examples
191
+
192
+ # --- Event Handlers ---
193
+ if sfosr_instance: # Only wire up buttons if the system initialized
194
+ av_button.click(
195
+ fn=run_analysis_verification,
196
+ inputs=[input_json_av],
197
+ outputs=[analysis_output, verification_output, vector_details_output, graph_placeholder_output]
198
+ )
199
+
200
+ p_button.click(
201
+ fn=run_proof_construction,
202
+ inputs=[input_json_p, source_concept_input, target_concept_input],
203
+ outputs=[proof_status_output, proof_details_output]
204
+ )
205
+ else:
206
+ # Display a persistent error if the system couldn't load
207
+ gr.Markdown("**Error: SFOSR System failed to initialize. Cannot run operations. Check logs.**")
208
+
209
+
210
+ # --- Launch the App ---
211
+ if __name__ == "__main__":
212
+ demo.launch() # Share=True for public link if needed
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Add project dependencies here
2
+ # e.g., streamlit
3
+ streamlit
4
+ gradio
sfosr_core/__init__.py ADDED
File without changes
sfosr_core/sfosr_database.py ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SFOSR Database Module
3
+
4
+ Обеспечивает взаимодействие с базой данных SFOSR.
5
+ Предоставляет методы для извлечения аксиом, правил вывода,
6
+ концептов и их свойств, необходимых для работы системы.
7
+ """
8
+
9
+ import sqlite3
10
+ import json
11
+ from typing import Dict, List, Any, Optional, Tuple, Union, Set
12
+
13
+ class SFOSRDatabase:
14
+ """
15
+ Класс для работы с базой данных SFOSR
16
+
17
+ Предоставляет интерфейс для:
18
+ - Получения аксиом и правил вывода
19
+ - Извлечения информации о концептах
20
+ - Получения векторных связей между концептами
21
+ - Добавления новых данных в базу знаний
22
+ """
23
+
24
+ def __init__(self, db_path="sfosr.db"):
25
+ """Инициализация соединения с БД"""
26
+ self.db_path = db_path
27
+ self.connection = None
28
+
29
+ def connect(self):
30
+ """Подключение к БД"""
31
+ # Возвращаем новое соединение каждый раз
32
+ # row_factory установим здесь же
33
+ connection = sqlite3.connect(self.db_path)
34
+ connection.row_factory = sqlite3.Row
35
+ return connection
36
+
37
+ # Добавляем контекстный менеджер
38
+ def __enter__(self):
39
+ self.connection = self.connect()
40
+ return self.connection
41
+
42
+ def __exit__(self, exc_type, exc_val, exc_tb):
43
+ if self.connection:
44
+ self.connection.close()
45
+ self.connection = None # Сбрасываем соединение
46
+
47
+ def get_axioms(self) -> List[Dict]:
48
+ """Получение всех аксиом из БД"""
49
+ with self as conn: # Используем with
50
+ cursor = conn.cursor()
51
+ cursor.execute("SELECT * FROM axioms")
52
+ axioms = [dict(row) for row in cursor.fetchall()]
53
+ return axioms
54
+
55
+ def get_inference_rules(self) -> List[Dict]:
56
+ """Получение всех правил вывода из БД"""
57
+ with self as conn:
58
+ cursor = conn.cursor()
59
+ cursor.execute("SELECT id, name, description, pattern, premise_types, conclusion_types, domain FROM inference_rules")
60
+ rules = [dict(row) for row in cursor.fetchall()]
61
+ return rules
62
+
63
+ def get_concept_by_name(self, name: str) -> Optional[Dict]:
64
+ """Поиск концепта по имени"""
65
+ with self as conn:
66
+ cursor = conn.cursor()
67
+ cursor.execute("SELECT id, name, description, domain, level FROM concepts WHERE name=?", (name,))
68
+ concept = cursor.fetchone()
69
+ return dict(concept) if concept else None
70
+
71
+ def get_all_concepts(self) -> List[Dict]:
72
+ """Получение всех концептов из БД"""
73
+ with self as conn:
74
+ cursor = conn.cursor()
75
+ cursor.execute("SELECT id, name, description, domain, level FROM concepts")
76
+ concepts = [dict(row) for row in cursor.fetchall()]
77
+ return concepts
78
+
79
+ def get_all_concept_names(self) -> Set[str]:
80
+ """Получение имен всех концептов из БД"""
81
+ with self as conn:
82
+ cursor = conn.cursor()
83
+ cursor.execute("SELECT name FROM concepts")
84
+ names = {row['name'] for row in cursor.fetchall()}
85
+ return names
86
+
87
+ def get_vectors_for_concept(self, concept_id: int) -> List[Dict]:
88
+ """
89
+ Получение всех векторов, связанных с концептом
90
+
91
+ Args:
92
+ concept_id: ID концепта
93
+
94
+ Returns:
95
+ Список векторов с именами источника и цели
96
+ """
97
+ with self as conn:
98
+ cursor = conn.cursor()
99
+ cursor.execute("""
100
+ SELECT v.id, v.source_id, v.target_id, v.vector_type, v.axis, v.justification,
101
+ c1.name as source_name, c2.name as target_name
102
+ FROM vectors v
103
+ JOIN concepts c1 ON v.source_id = c1.id
104
+ JOIN concepts c2 ON v.target_id = c2.id
105
+ WHERE v.source_id=? OR v.target_id=?
106
+ """, (concept_id, concept_id))
107
+ vectors = [dict(row) for row in cursor.fetchall()]
108
+ return vectors
109
+
110
+ def get_concept_properties(self, concept_id: int) -> Dict[str, Any]:
111
+ """
112
+ Получение всех свойств концепта
113
+
114
+ Args:
115
+ concept_id: ID концепта
116
+
117
+ Returns:
118
+ Словарь свойств в формате {имя_свойства: значение}
119
+ """
120
+ with self as conn:
121
+ cursor = conn.cursor()
122
+ cursor.execute("""
123
+ SELECT property_name, property_value
124
+ FROM concept_properties
125
+ WHERE concept_id=?
126
+ """, (concept_id,))
127
+ properties = {}
128
+ for row in cursor.fetchall():
129
+ prop_name = row['property_name']
130
+ prop_value = row['property_value']
131
+ try:
132
+ if isinstance(prop_value, str) and (prop_value.startswith('[') or prop_value.startswith('{')):
133
+ prop_value = json.loads(prop_value)
134
+ except (json.JSONDecodeError, TypeError):
135
+ pass
136
+ properties[prop_name] = prop_value
137
+ return properties
138
+
139
+ def get_complete_concept_info(self, concept_name: str) -> Optional[Dict]:
140
+ """
141
+ Получение полной информации о концепте
142
+
143
+ Args:
144
+ concept_name: Имя концепта
145
+
146
+ Returns:
147
+ Словарь с информацией о концепте, его свойствах и связях
148
+ """
149
+ concept = self.get_concept_by_name(concept_name)
150
+ if not concept:
151
+ return None
152
+
153
+ concept_id = concept['id']
154
+ properties = self.get_concept_properties(concept_id)
155
+ vectors = self.get_vectors_for_concept(concept_id)
156
+
157
+ return {
158
+ "concept": concept,
159
+ "properties": properties,
160
+ "vectors": vectors
161
+ }
162
+
163
+ def get_related_concepts(self, concept_id: int, depth: int = 1) -> List[Dict]:
164
+ """
165
+ Получение связанных концептов с заданной глубиной
166
+
167
+ Args:
168
+ concept_id: ID исходного концепта
169
+ depth: Глубина поиска связей (1 = только прямые связи)
170
+
171
+ Returns:
172
+ Список связанных концептов
173
+ """
174
+ if depth <= 0:
175
+ return []
176
+
177
+ # Получаем прямые связи
178
+ vectors = self.get_vectors_for_concept(concept_id)
179
+ related_ids = set()
180
+
181
+ for vector in vectors:
182
+ if vector['source_id'] != concept_id:
183
+ related_ids.add(vector['source_id'])
184
+ if vector['target_id'] != concept_id:
185
+ related_ids.add(vector['target_id'])
186
+
187
+ # Рекурсивно получаем связи с заданной глубиной
188
+ all_related = []
189
+ for related_id in related_ids:
190
+ with self as conn:
191
+ cursor = conn.cursor()
192
+ cursor.execute("SELECT * FROM concepts WHERE id=?", (related_id,))
193
+ concept = cursor.fetchone()
194
+
195
+ if concept:
196
+ related_concept = dict(concept)
197
+ all_related.append(related_concept)
198
+
199
+ # Если нужна большая глубина, рекурсивно получаем связанные концепты
200
+ if depth > 1:
201
+ deeper_related = self.get_related_concepts(related_id, depth - 1)
202
+ all_related.extend(deeper_related)
203
+
204
+ return all_related
205
+
206
+ def find_path_between_concepts(self, source_name: str, target_name: str, max_depth: int = 3) -> List[Dict]:
207
+ """
208
+ Поиск пути между двумя концептами
209
+
210
+ Args:
211
+ source_name: Имя исходного концепта
212
+ target_name: Имя целевого концепта
213
+ max_depth: Максимальная глубина поиска
214
+
215
+ Returns:
216
+ Список векторов, образующих путь между концептами
217
+ """
218
+ source = self.get_concept_by_name(source_name)
219
+ target = self.get_concept_by_name(target_name)
220
+
221
+ if not source or not target:
222
+ return []
223
+
224
+ # Поиск в ширину для нахождения пути
225
+ visited_concepts = {source['id']} # Храним ID концептов, чтобы не зацикливаться
226
+ queue = [(source['id'], [])] # (id_концепта, path_из_векторов_до_него)
227
+
228
+ # Ограничиваем глубину поиска
229
+ current_depth = 0
230
+ nodes_at_current_depth = 1
231
+ nodes_at_next_depth = 0
232
+
233
+ while queue and current_depth < max_depth:
234
+ if nodes_at_current_depth == 0:
235
+ current_depth += 1
236
+ nodes_at_current_depth = nodes_at_next_depth
237
+ nodes_at_next_depth = 0
238
+ if current_depth >= max_depth: # Проверка после инкремента глубины
239
+ break
240
+
241
+ current_id, path = queue.pop(0)
242
+ nodes_at_current_depth -= 1
243
+
244
+ # Проверка, не достигли ли мы цели
245
+ if current_id == target['id']:
246
+ return path # Возвращаем список векторов
247
+
248
+ # Получаем связанные векторы для текущего концепта
249
+ # Используем get_vectors_for_concept, так как он возвращает нужные данные
250
+ connected_vectors = self.get_vectors_for_concept(current_id)
251
+
252
+ for vector in connected_vectors:
253
+ next_id = None
254
+
255
+ # Определяем следующий концепт в пути
256
+ if vector['source_id'] == current_id and vector['target_id'] not in visited_concepts:
257
+ next_id = vector['target_id']
258
+ elif vector['target_id'] == current_id and vector['source_id'] not in visited_concepts:
259
+ next_id = vector['source_id']
260
+
261
+ if next_id:
262
+ visited_concepts.add(next_id)
263
+ # Добавляем сам вектор (как словарь) в путь
264
+ new_path = path + [vector]
265
+ queue.append((next_id, new_path))
266
+ nodes_at_next_depth += 1
267
+
268
+ return [] # Путь не найден в пределах max_depth
269
+
270
+ def convert_db_vector_to_system_format(self, db_vector: Dict) -> Dict:
271
+ """
272
+ Преобразование вектора из формата БД в формат системы SFOSR
273
+
274
+ Args:
275
+ db_vector: Вектор в формате БД
276
+
277
+ Returns:
278
+ Вектор в формате системы SFOSR
279
+ """
280
+ return {
281
+ "id": f"V{db_vector['id']}",
282
+ "source": db_vector['source_name'],
283
+ "target": db_vector['target_name'],
284
+ "type": db_vector['vector_type'],
285
+ "axis": db_vector['axis'],
286
+ "justification": db_vector['justification']
287
+ }
288
+
289
+ # Методы для обновления БД
290
+
291
+ def add_concept(self, name: str, description: str, domain: str, level: str) -> int:
292
+ """
293
+ Добавление нового концепта в БД
294
+
295
+ Args:
296
+ name: Имя концепта
297
+ description: Описание концепта
298
+ domain: Домен (область знаний)
299
+ level: Уровень абстракции
300
+
301
+ Returns:
302
+ ID добавленного концепта
303
+ """
304
+ with self as conn:
305
+ cursor = conn.cursor()
306
+ cursor.execute("""
307
+ INSERT INTO concepts (name, description, domain, level)
308
+ VALUES (?, ?, ?, ?)
309
+ """, (name, description, domain, level))
310
+ new_id = cursor.lastrowid
311
+ conn.commit()
312
+ return new_id
313
+
314
+ def add_concept_property(self, concept_id: int, property_name: str, property_value: Union[str, List, Dict]) -> int:
315
+ """
316
+ Добавление свойства концепта
317
+
318
+ Args:
319
+ concept_id: ID концепта
320
+ property_name: Имя свойства
321
+ property_value: Значение свойства (строка или JSON)
322
+
323
+ Returns:
324
+ ID добавленного свойства
325
+ """
326
+ # Если значение не строка, преобразуем в JSON
327
+ if not isinstance(property_value, str):
328
+ property_value = json.dumps(property_value)
329
+
330
+ with self as conn:
331
+ cursor = conn.cursor()
332
+ cursor.execute("""
333
+ INSERT INTO concept_properties (concept_id, property_name, property_value)
334
+ VALUES (?, ?, ?)
335
+ """, (concept_id, property_name, property_value))
336
+ new_id = cursor.lastrowid
337
+ conn.commit()
338
+ return new_id
339
+
340
+ def add_vector(self, source_id: int, target_id: int, vector_type: str,
341
+ axis: str, justification: Optional[str] = None) -> int:
342
+ """
343
+ Добавление нового вектора (связи между концептами)
344
+
345
+ Args:
346
+ source_id: ID исходного концепта
347
+ target_id: ID целевого концепта
348
+ vector_type: Тип вектора
349
+ axis: Семантическая ось
350
+ justification: Обоснование связи
351
+
352
+ Returns:
353
+ ID добавленного вектора
354
+ """
355
+ with self as conn:
356
+ cursor = conn.cursor()
357
+ cursor.execute("""
358
+ INSERT INTO vectors (source_id, target_id, vector_type, axis, justification)
359
+ VALUES (?, ?, ?, ?, ?)
360
+ """, (source_id, target_id, vector_type, axis, justification))
361
+ new_id = cursor.lastrowid
362
+ conn.commit()
363
+ return new_id
364
+
365
+ def add_axiom(self, name: str, description: str, formulation: str, domain: str) -> int:
366
+ """
367
+ Добавление новой аксиомы
368
+
369
+ Args:
370
+ name: Имя аксиомы
371
+ description: Описание аксиомы
372
+ formulation: Формальная формулировка
373
+ domain: Домен (область применения)
374
+
375
+ Returns:
376
+ ID добавленной аксиомы
377
+ """
378
+ with self as conn:
379
+ cursor = conn.cursor()
380
+ cursor.execute("""
381
+ INSERT INTO axioms (name, description, formulation, domain)
382
+ VALUES (?, ?, ?, ?)
383
+ """, (name, description, formulation, domain))
384
+ new_id = cursor.lastrowid
385
+ conn.commit()
386
+ return new_id
387
+
388
+ def add_inference_rule(self, name: str, description: str, pattern: str,
389
+ premise_types: str, conclusion_types: str, domain: str) -> int:
390
+ """
391
+ Добавление нового правила вывода
392
+
393
+ Args:
394
+ name: Имя правила
395
+ description: Описание правила
396
+ pattern: Паттерн вывода
397
+ premise_types: Типы посылок
398
+ conclusion_types: Типы выводов
399
+ domain: Домен (область применения)
400
+
401
+ Returns:
402
+ ID добавленного правила
403
+ """
404
+ with self as conn:
405
+ cursor = conn.cursor()
406
+ cursor.execute("""
407
+ INSERT INTO inference_rules (name, description, pattern, premise_types,
408
+ conclusion_types, domain)
409
+ VALUES (?, ?, ?, ?, ?, ?)
410
+ """, (name, description, pattern, premise_types, conclusion_types, domain))
411
+ new_id = cursor.lastrowid
412
+ conn.commit()
413
+ return new_id
414
+
415
+ def get_all_vectors(self):
416
+ """Получить все векторы из базы данных"""
417
+ query = """
418
+ SELECT
419
+ v.id,
420
+ v.source_id,
421
+ v.target_id,
422
+ v.vector_type,
423
+ v.axis,
424
+ v.justification,
425
+ s.name as source_name,
426
+ t.name as target_name,
427
+ v.is_valid
428
+ FROM vectors v
429
+ JOIN concepts s ON v.source_id = s.id
430
+ JOIN concepts t ON v.target_id = t.id
431
+ WHERE v.is_valid = 1
432
+ """
433
+
434
+ with self as conn:
435
+ cursor = conn.cursor()
436
+ cursor.execute(query)
437
+ rows = cursor.fetchall()
438
+
439
+ vectors = []
440
+ for row in rows:
441
+ vector = {
442
+ "id": f"V{row[0]}", # Добавляем префикс V к ID
443
+ "source_name": row[6],
444
+ "target_name": row[7],
445
+ "type": row[3], # vector_type из БД становится type в объекте
446
+ "axis": row[4],
447
+ "justification": row[5],
448
+ "is_valid": bool(row[8])
449
+ }
450
+ vectors.append(vector)
451
+
452
+ return vectors
sfosr_core/sfosr_system.py ADDED
@@ -0,0 +1,1403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SFOSR Integrated System (with Binary Validity)
3
+
4
+ Этот модуль объединяет основные компоненты системы SFOSR:
5
+ - Анализатор структуры (`SFOSRAnalyzer`)
6
+ - Верификатор контрактов (`ContractVerifier`)
7
+ - Систему построения доказательств (`ProofSystem`)
8
+
9
+ для обеспечения комплексной обработки и формальной оценки
10
+ смысловых структур на основе бинарной валидности.
11
+ """
12
+
13
+ import json
14
+ import subprocess
15
+ import os
16
+ from typing import Dict, List, Any, Tuple, Optional, Set, Union
17
+ from .sfosr_database import SFOSRDatabase # Use relative import within the package
18
+
19
+ # Конфигурация системы
20
+ SFOSR_CONFIG = {
21
+ "version": "0.4.0",
22
+ "description": "Integrated SFOSR System",
23
+ "components": ["analyzer", "verifier", "proof_system"],
24
+ "debug_mode": False,
25
+ "auto_update_plausibility": True # Автоматическое обновление plausibility
26
+ }
27
+
28
+ # Общие типы векторов
29
+ VECTOR_TYPES = {
30
+ "Causality": {"weight": 2.0, "requires_justification": True, "description": "Причинно-следственная связь"},
31
+ "Implication": {"weight": 1.8, "requires_justification": True, "description": "Логическое следование (если-то)"},
32
+ "Transformation": {"weight": 1.5, "requires_justification": False, "description": "Превращение из одного состояния в другое"},
33
+ "Goal": {"weight": 1.3, "requires_justification": False, "description": "Целеполагание, намерение"},
34
+ "Prevention": {"weight": 1.3, "requires_justification": False, "description": "Предотвращение нежелательного исхода"},
35
+ "Contrast": {"weight": 1.2, "requires_justification": False, "description": "Противопоставление"},
36
+ "Comparison": {"weight": 1.0, "requires_justification": False, "description": "Сравнение элементов"},
37
+ "Inclusion": {"weight": 0.8, "requires_justification": False, "description": "Отношение часть-целое"},
38
+ "Attribution": {"weight": 0.7, "requires_justification": False, "description": "Приписывание свойства объекту"},
39
+ "Temporal": {"weight": 1.1, "requires_justification": False, "description": "Временная последовательность"},
40
+ "Qualification": {"weight": 0.6, "requires_justification": False, "description": "Ограничение или уточнение"},
41
+ "Definition": {"weight": 1.4, "requires_justification": False, "description": "Определение понятия"},
42
+ "PartOf": {"constraints": [], "requires_justification": False},
43
+ "Mechanism": {"constraints": [], "requires_justification": True},
44
+ "Example": {"constraints": [], "requires_justification": False},
45
+ "Requirement": {"constraints": [], "requires_justification": True},
46
+ "Action": {"constraints": [], "requires_justification": False},
47
+ "Capability": {"constraints": [], "requires_justification": False},
48
+ "PropertyOf": {"constraints": [], "requires_justification": False},
49
+ "Purpose": {"constraints": [], "requires_justification": False},
50
+ "Governs": {"constraints": [], "requires_justification": False},
51
+ "Contains": {"constraints": [], "requires_justification": False},
52
+ "Represents": {"constraints": [], "requires_justification": False},
53
+ "Context": {"constraints": [], "requires_justification": False},
54
+ "IsA": {"constraints": [], "requires_justification": False},
55
+ # "ActsOn": {"constraints": [], "requires_justification": False}, # Removed as it's not used now
56
+ "Dependency": {"constraints": [], "requires_justification": True}
57
+ }
58
+
59
+ # Интерфейс для интеграции компонентов
60
+ class SFOSRSystem:
61
+ """
62
+ Основной класс интегрированной системы SFOSR (на бинарной валидности)
63
+
64
+ Объединяет:
65
+ - Анализ структуры векторов
66
+ - Проверку контрактов и определение валидности
67
+ - Построение валидных доказательств
68
+ """
69
+
70
+ def __init__(self, db_path="sfosr.db", debug=False):
71
+ self.db_path = db_path
72
+ self.db = SFOSRDatabase(db_path) # Database connection
73
+ self._analyzer = SFOSRAnalyzer()
74
+
75
+ # Prepare data for ContractVerifier
76
+ all_concepts = self.db.get_all_concepts()
77
+ known_concepts_names = {c['name'] for c in all_concepts}
78
+ concepts_data_map = {c['name']: c for c in all_concepts}
79
+
80
+ self._verifier = ContractVerifier(known_concepts=known_concepts_names, concepts_data=concepts_data_map)
81
+ self._proof_system = ProofSystem(db_conn=self.db) # Pass db connection
82
+ self.concept_graph = None
83
+ self.debug = debug
84
+
85
+ # Load inference rules from DB
86
+ try:
87
+ db_rules = self.db.get_inference_rules()
88
+ if db_rules:
89
+ self._proof_system.load_rules(db_rules)
90
+ except Exception as e:
91
+ if self.debug:
92
+ print(f"Error loading inference rules from database: {str(e)}")
93
+
94
+ # Графы
95
+ self.concept_graph = {}
96
+
97
+ def process(self, input_data):
98
+ """
99
+ Основной метод обработки входных данных (с бинарной валидностью)
100
+
101
+ Последовательно выполняет:
102
+ 1. Анализ структуры
103
+ 2. Проверку контрактов (определение is_valid)
104
+ 3. Построение доказательств (если применимо, определение is_valid)
105
+ (Использует только валидированные векторы из input_data, без обогащения из БД)
106
+
107
+ Args:
108
+ input_data: Словарь с текстом и векторами SFOSR
109
+
110
+ Returns:
111
+ Dict: Результаты обработки (с полями is_valid)
112
+ """
113
+ # Шаг 1: Анализ структуры
114
+ analysis_result = self._analyzer.analyze(input_data)
115
+ self.concept_graph = analysis_result["concept_graph"]
116
+
117
+ # Если анализ не прошел
118
+ if analysis_result["analysis_status"] != "Completed":
119
+ return {
120
+ "status": "Error",
121
+ "message": f"Analysis failed: {analysis_result['analysis_status']}",
122
+ "details": {
123
+ "validation_issues": analysis_result["validation_issues"]
124
+ }
125
+ }
126
+
127
+ # Получаем инстансы из контекста, если они есть
128
+ instance_definitions = input_data.get("instance_definitions", {})
129
+
130
+ # Шаг 2: Проверка контрактов
131
+ vectors_to_verify = analysis_result["vectors_analyzed"]
132
+ # Передаем инстансы в верификатор
133
+ verification_result = self._verifier.verify_all(vectors_to_verify, instance_definitions)
134
+
135
+ # Собираем только валидные векторы для системы доказательств
136
+ valid_input_vectors = []
137
+ vectors_data = verification_result["vectors_data"]
138
+ for vector in vectors_to_verify:
139
+ v_id = vector.get("id")
140
+ # Используем get для безопасного доступа и проверяем наличие ключа 'vector'
141
+ vector_dict = vectors_data.get(v_id, {}).get('vector')
142
+ if vector_dict and vectors_data[v_id].get("is_valid", False):
143
+ valid_input_vectors.append(vector_dict) # Добавляем исходный вектор
144
+
145
+ # --- Генерация временных IsA векторов ---
146
+ temporary_isa_vectors = []
147
+ for instance_id, definition in instance_definitions.items():
148
+ general_type = definition.get('is_a')
149
+ instance_label = definition.get('label', instance_id) # Use label or ID
150
+ if general_type:
151
+ # Проверяем, существует ли общий тип в БД
152
+ if self.db.get_concept_by_name(general_type):
153
+ temporary_isa_vectors.append({
154
+ "id": f"isa_{instance_id}", # Уникальный временный ID
155
+ "source": instance_id, # Используем временный ID
156
+ "target": general_type, # Ссылка на общий тип в БД
157
+ "type": "IsA",
158
+ "axis": "classification",
159
+ "justification": f"Instance '{instance_label}' defined as type '{general_type}' in input context.",
160
+ "is_valid": True # Считаем эти связи априори валидными для доказательства
161
+ })
162
+ else:
163
+ print(f"Warning: General type '{general_type}' for instance '{instance_id}' not found in DB. Skipping IsA vector generation.")
164
+ # ----------------------------------------
165
+
166
+ # Базовый результат обработки
167
+ result = {
168
+ "status": "Success",
169
+ "input_text": input_data.get("text", ""),
170
+ "analysis": {
171
+ "status": analysis_result["analysis_status"],
172
+ "is_compilable": analysis_result["is_compilable"],
173
+ "graph_metrics": analysis_result["graph_metrics"]
174
+ },
175
+ "verification": {
176
+ "total_vectors": verification_result["total_vectors_processed"],
177
+ "valid_count": verification_result["valid_count"],
178
+ "compliance_rate": verification_result["compliance_rate"],
179
+ "vectors_data": verification_result["vectors_data"]
180
+ }
181
+ }
182
+
183
+ # Шаг 3: Построение доказательств
184
+ vectors_for_proof = valid_input_vectors + temporary_isa_vectors
185
+ # Запускаем, если есть запрос И есть ХОТЬ КАКИЕ-ТО векторы (входные или IsA)
186
+ if "proof_query" in input_data and vectors_for_proof:
187
+ query = input_data["proof_query"]
188
+ source = query.get("source")
189
+ target = query.get("target")
190
+
191
+ if source and target:
192
+ proof_result = self._proof_system.construct_proof(
193
+ vectors_for_proof, source, target
194
+ )
195
+ result["proof"] = proof_result
196
+ else: # Нет source/target
197
+ result["proof"] = {"status": "Failed", "reason": "Missing source or target in proof query", "is_valid": False}
198
+ # else: # Нет proof_query или нет векторов - proof не создается
199
+ # pass
200
+
201
+ return result
202
+
203
+ def analyze(self, input_data):
204
+ """Удобный метод для выполнения только анализа"""
205
+ return self._analyzer.analyze(input_data)
206
+
207
+ def verify(self, input_data):
208
+ """Удобный метод для выполнения только верификации"""
209
+ vectors = input_data.get("vectors", [])
210
+ # Сначала базовый анализ для получения структурно валидных векторов
211
+ analysis_result = self._analyzer.analyze(input_data)
212
+ # Передаем пустой словарь instance_definitions, т.к. verify не работает с контекстом
213
+ return self._verifier.verify_all(analysis_result["vectors_analyzed"], instance_definitions={})
214
+
215
+ def prove(self, input_data, source, target):
216
+ """Удобный метод для построения доказательства.
217
+
218
+ Анализирует, верифицирует и строит доказательство, используя только
219
+ валидированные векторы из input_data (без обогащения из БД).
220
+ """
221
+ # Сначала анализ
222
+ analysis_res = self.analyze(input_data)
223
+ if analysis_res["analysis_status"] != "Completed":
224
+ return {"status": "Failed", "reason": "Analysis failed"}
225
+
226
+ # Получаем список векторов, прошедших анализ
227
+ vectors_analyzed = analysis_res.get("vectors_analyzed", [])
228
+ if not vectors_analyzed:
229
+ return {"status": "Failed", "reason": "No vectors passed analysis"}
230
+
231
+ # Затем верификация этих векторов
232
+ # Передаем пустой instance_definitions, т.к. prove работает с готовым input_data
233
+ # Хотя, возможно, стоило бы передавать реальный instance_definitions из input_data?
234
+ # Пока оставим пустым для совместимости.
235
+ verification_res = self._verifier.verify_all(vectors_analyzed, instance_definitions={})
236
+ vectors_data = verification_res.get("vectors_data", {})
237
+
238
+ # Извлекаем валидные векторы, ИТЕРРИРУЯ ПО ИСХОДНОМУ СПИСКУ
239
+ valid_vectors = [
240
+ vector # Берем исходный вектор
241
+ for vector in vectors_analyzed # Итерируем по результатам анализа
242
+ if vectors_data.get(vector.get("id", ""), {}).get("is_valid", False) # Проверяем валидность в результатах верификации
243
+ ]
244
+
245
+ if not valid_vectors:
246
+ return {"status": "Failed", "reason": "No valid vectors after verification"}
247
+
248
+ # Enrichment is disabled, use valid_vectors directly
249
+ vectors_for_proof = valid_vectors
250
+
251
+ return self._proof_system.construct_proof(vectors_for_proof, source, target)
252
+
253
+ def get_concept_info(self, concept_name):
254
+ """
255
+ Получение информации о концепте из БД
256
+
257
+ Args:
258
+ concept_name: Имя концепта
259
+
260
+ Returns:
261
+ Dict: Информация о концепте или None
262
+ """
263
+ return self.db.get_complete_concept_info(concept_name)
264
+
265
+ def find_related_concepts(self, concept_name, depth=1):
266
+ """
267
+ Поиск связанных концептов
268
+
269
+ Args:
270
+ concept_name: Имя концепта
271
+ depth: Глубина поиска
272
+
273
+ Returns:
274
+ List: Список связанных концептов
275
+ """
276
+ concept = self.db.get_concept_by_name(concept_name)
277
+ if not concept:
278
+ return []
279
+
280
+ return self.db.get_related_concepts(concept["id"], depth)
281
+
282
+ def add_concept_to_db(self, name, description, domain, level):
283
+ """
284
+ Добавление нового концепта в БД
285
+
286
+ Args:
287
+ name: Имя концепта
288
+ description: Описание
289
+ domain: Домен (область знаний)
290
+ level: Уровень абстракции
291
+
292
+ Returns:
293
+ int: ID добавленного концепта
294
+ """
295
+ return self.db.add_concept(name, description, domain, level)
296
+
297
+ def add_vector_to_db(self, source_name, target_name, vector_type, axis, justification=None):
298
+ """
299
+ Добавление нового вектора в БД
300
+
301
+ Args:
302
+ source_name: Имя исходного концепта
303
+ target_name: Имя целевого концепта
304
+ vector_type: Тип вектора
305
+ axis: Ось
306
+ justification: Обоснование
307
+
308
+ Returns:
309
+ int: ID добавленного вектора или None в случае ошибки
310
+ """
311
+ source = self.db.get_concept_by_name(source_name)
312
+ target = self.db.get_concept_by_name(target_name)
313
+
314
+ if not source or not target:
315
+ return None
316
+
317
+ return self.db.add_vector(source["id"], target["id"], vector_type, axis, justification)
318
+
319
+ # Реализация компонентов системы
320
+
321
+ class SFOSRAnalyzer:
322
+ """
323
+ Анализатор структуры векторов SFOSR
324
+
325
+ Отвечает за:
326
+ - Проверку синтаксиса и базовой структуры векторов
327
+ - Проверку компилируемости (наличие необходимых полей)
328
+ - Построение графа концептов
329
+ """
330
+
331
+ def __init__(self, vector_types=None):
332
+ """Инициализация анализатора"""
333
+ self.vector_types = vector_types or VECTOR_TYPES
334
+
335
+ def build_concept_graph(self, vectors):
336
+ """
337
+ Строит граф концептов и связей между ними
338
+
339
+ Args:
340
+ vectors: Список векторов SFOSR
341
+
342
+ Returns:
343
+ Dict: Структура графа с узлами и связями
344
+ """
345
+ # Структура для хранения графа
346
+ graph = {
347
+ "nodes": set(), # уникальные концепты
348
+ "edges": [], # связи (кортежи source, target, vector_id)
349
+ "adjacency": {}, # словарь смежности для быстрого доступа
350
+ }
351
+
352
+ # Собираем все уникальные концепты и ребра
353
+ all_nodes = set()
354
+ for vector in vectors:
355
+ source = vector.get("source")
356
+ target = vector.get("target")
357
+ vector_id = vector.get("id")
358
+
359
+ if source:
360
+ all_nodes.add(source)
361
+ if source not in graph["adjacency"]:
362
+ graph["adjacency"][source] = {"out": [], "in": []}
363
+
364
+ if target:
365
+ all_nodes.add(target)
366
+ if target not in graph["adjacency"]:
367
+ graph["adjacency"][target] = {"out": [], "in": []}
368
+
369
+ if source and target and vector_id:
370
+ edge = (source, target, vector_id)
371
+ graph["edges"].append(edge)
372
+ graph["adjacency"][source]["out"].append((target, vector_id))
373
+ graph["adjacency"][target]["in"].append((source, vector_id))
374
+
375
+ graph["nodes"] = all_nodes
376
+
377
+ return graph
378
+
379
+ def validate_vector_structure(self, vector):
380
+ """
381
+ Проверяет структуру вектора на соответствие базовым требованиям
382
+
383
+ Args:
384
+ vector: Словарь с данными вектора
385
+
386
+ Returns:
387
+ Tuple[bool, Optional[str]]: (валидность, сообщение об ошибке)
388
+ """
389
+ required_keys = ["id", "source", "target", "type", "axis"]
390
+ missing_keys = [key for key in required_keys if key not in vector or not vector[key]]
391
+
392
+ if missing_keys:
393
+ return False, f"Vector {vector.get('id', 'Unknown')} missing keys: {', '.join(missing_keys)}"
394
+
395
+ # Проверяем, существует ли указанный тип вектора
396
+ vector_type = vector.get("type")
397
+ if vector_type not in self.vector_types:
398
+ return False, f"Vector {vector.get('id', 'Unknown')} has invalid type: {vector_type}"
399
+
400
+ return True, None
401
+
402
+ def validate_compilability(self, vector):
403
+ """
404
+ Проверяет на компилируемость (достаточность данных)
405
+
406
+ Args:
407
+ vector: Словарь с данными вектора
408
+
409
+ Returns:
410
+ Tuple[bool, Optional[str]]: (компилируемость, сообщение об ошибке)
411
+ """
412
+ vector_type = vector.get("type")
413
+
414
+ # Проверяем требования обоснования в зависимости от типа
415
+ if (vector_type in self.vector_types and
416
+ self.vector_types[vector_type]["requires_justification"]):
417
+ if not vector.get("justification"):
418
+ return False, f"Vector {vector.get('id', 'Unknown')} requires justification for type {vector_type}."
419
+
420
+ return True, None
421
+
422
+ def analyze(self, input_data):
423
+ """
424
+ Главная функция анализа структуры SFOSR (упрощенная)
425
+
426
+ Args:
427
+ input_data: Словарь с текстом и векторами
428
+
429
+ Returns:
430
+ Dict: Результаты анализа (валидация и граф)
431
+ """
432
+ input_text = input_data.get("text", "N/A")
433
+ vectors = input_data.get("vectors", [])
434
+
435
+ valid_vectors = []
436
+ validation_issues = []
437
+ analysis_status = "Completed"
438
+
439
+ # 1. Валидация структуры и компилируемости каждого вектора
440
+ for vector in vectors:
441
+ is_struct_valid, struct_error = self.validate_vector_structure(vector)
442
+ if not is_struct_valid:
443
+ validation_issues.append(struct_error)
444
+ analysis_status = "Validation Error"
445
+ continue # Невалидную структуру дальше не проверяем
446
+
447
+ is_comp_valid, comp_error = self.validate_compilability(vector)
448
+ if not is_comp_valid:
449
+ validation_issues.append(comp_error)
450
+ # Продолжаем анализ, но помечаем проблему
451
+
452
+ # Собираем только структурно валидные векторы
453
+ valid_vectors.append(vector)
454
+
455
+ # Определяем компилируемость по наличию проблем
456
+ is_compilable = len(validation_issues) == 0
457
+ if not is_compilable and analysis_status == "Completed":
458
+ analysis_status = "Compilability Error" # Если были только проблемы компилируемости
459
+
460
+ # 2. Построение графа концептов (только из структурно валидных векторов)
461
+ graph = self.build_concept_graph(valid_vectors)
462
+
463
+ # 3. Формирование упрощенного результата
464
+ result = {
465
+ "input_text": input_text,
466
+ "analysis_status": analysis_status,
467
+ "is_compilable": is_compilable,
468
+ "validation_issues": validation_issues,
469
+ "graph_metrics": { # Упрощенные метрики графа
470
+ "concepts_count": len(graph["nodes"]),
471
+ "connections_count": len(graph["edges"]),
472
+ },
473
+ "vectors_analyzed": valid_vectors, # Содержит только структурно валидные
474
+ "concept_graph": graph
475
+ }
476
+
477
+ return result
478
+
479
+ class ContractVerifier:
480
+ """
481
+ Верификатор контрактов векторов SFOSR
482
+
483
+ Проверяет соответствие векторов формальным контрактам,
484
+ определяет бинарную валидность (`is_valid`) вектора
485
+ и собирает метаданные.
486
+ """
487
+
488
+ def __init__(self, contract_types=None, known_concepts: Optional[Set[str]] = None, concepts_data: Optional[Dict[str, Dict]] = None):
489
+ """Инициализация верификатора с известными концептами и их данными (уровнями)."""
490
+ self.contract_types = contract_types or set(VECTOR_TYPES.keys())
491
+ self.known_concepts = known_concepts or set()
492
+ # --- Сохраняем данные об уровнях ---
493
+ self.concepts_data = concepts_data or {}
494
+ # ----------------------------------
495
+ self.axis_registry = set()
496
+
497
+ def verify_vector_contract(self, vector: Dict[str, Any], instance_definitions: Dict[str, Dict]) -> Tuple[bool, List[str], Dict[str, Any]]:
498
+ """Проверяет отдельный вектор на ��оответствие контрактам"""
499
+ issues = []
500
+ metadata = {}
501
+ is_valid = True # Начинаем с предположения о валидности
502
+
503
+ # --- Проверка существования концептов и их типов ---
504
+ source_name = vector.get("source")
505
+ target_name = vector.get("target")
506
+ vector_type = vector.get("type")
507
+ vector_id = vector.get("id", "Unknown")
508
+
509
+ # --- Получаем реальные ТИПЫ концептов для проверки ---
510
+ source_type_name = source_name
511
+ target_type_name = target_name
512
+ is_source_instance = False
513
+ is_target_instance = False
514
+
515
+ if source_name in instance_definitions:
516
+ source_type_name = instance_definitions[source_name].get("is_a")
517
+ is_source_instance = True
518
+ if not source_type_name:
519
+ issues.append(f"Instance '{source_name}' in vector {vector_id} has no 'is_a' type defined in context.")
520
+ is_valid = False
521
+ source_type_name = None # Не можем проверить дальше
522
+
523
+ if target_name in instance_definitions:
524
+ target_type_name = instance_definitions[target_name].get("is_a")
525
+ is_target_instance = True
526
+ if not target_type_name:
527
+ issues.append(f"Instance '{target_name}' in vector {vector_id} has no 'is_a' type defined in context.")
528
+ is_valid = False
529
+ target_type_name = None # Не можем проверить дальше
530
+ # ----------------------------------------------------
531
+
532
+ source_concept_data = None
533
+ target_concept_data = None
534
+
535
+ if source_type_name and is_valid:
536
+ source_concept_data = self.concepts_data.get(source_type_name)
537
+ if not source_concept_data:
538
+ issues.append(f"Source concept/type '{source_type_name}' (for '{source_name}') not found in known concepts for vector {vector_id}.")
539
+ is_valid = False
540
+
541
+ if target_type_name and is_valid:
542
+ target_concept_data = self.concepts_data.get(target_type_name)
543
+ if not target_concept_data:
544
+ issues.append(f"Target concept/type '{target_type_name}' (for '{target_name}') not found in known concepts for vector {vector_id}.")
545
+ is_valid = False
546
+
547
+ # --- Проверка контрактов типа Transformation ---
548
+ if is_valid and vector_type == "Transformation":
549
+ if source_name == target_name:
550
+ issues.append(f"Transformation vector {vector_id} cannot have the same source and target ('{source_name}').")
551
+ is_valid = False
552
+
553
+ # --- Проверка контракта для Causality (разные уровни) ---
554
+ if is_valid and vector_type == "Causality" and "level" in vector.get("axis", ""):
555
+ if source_concept_data and target_concept_data:
556
+ source_level = source_concept_data.get('level')
557
+ target_level = target_concept_data.get('level')
558
+ if source_level and target_level and source_level == target_level:
559
+ issues.append(f"Causality vector {vector_id} ('{source_type_name}' -> '{target_type_name}') links concepts on the same level '{source_level}' with axis containing 'level'.")
560
+ is_valid = False
561
+
562
+ # --- Добавить другие специфичные для типов векторов проверки ---
563
+ # Например, для ActsOn: source должен быть подтипом Action, target - подтипом Object?
564
+ # Это потребует иерархии в БД или более сложной логики.
565
+
566
+ # Регистрация осей остается
567
+ if vector.get("axis") and vector["axis"] not in self.axis_registry:
568
+ self.axis_registry.add(vector["axis"])
569
+
570
+ # Добавляем сами данные вектора в метаданные для использования в `prove`
571
+ # metadata['vector'] = vector # Убрали - теперь prove получает исходный список
572
+
573
+ return is_valid, issues, metadata
574
+
575
+ def verify_all(self, vectors: List[Dict[str, Any]], instance_definitions: Dict[str, Dict]) -> Dict[str, Any]:
576
+ """Проверка всех векторов, агрегация валидности и метаданных"""
577
+ vectors_data = {}
578
+ valid_count = 0
579
+ processed_count = 0
580
+
581
+ for vector in vectors:
582
+ processed_count += 1
583
+ vector_id = vector.get("id", f"unknown_{processed_count}")
584
+ # Передаем instance_definitions в проверку контракта
585
+ is_valid, issues, metadata = self.verify_vector_contract(vector, instance_definitions)
586
+
587
+ vectors_data[vector_id] = {
588
+ "vector": vector,
589
+ "is_valid": is_valid,
590
+ "issues": issues,
591
+ "metadata": metadata
592
+ }
593
+
594
+ if is_valid:
595
+ valid_count += 1
596
+
597
+ # Формируем отчет
598
+ report = {
599
+ "total_vectors_processed": processed_count,
600
+ "valid_count": valid_count,
601
+ "compliance_rate": round(valid_count / processed_count, 3) if processed_count > 0 else 0.0,
602
+ "vectors_data": vectors_data # Основные данные теперь здесь
603
+ }
604
+
605
+ return report
606
+
607
+ class ProofSystem:
608
+ """
609
+ Система построения доказательств SFOSR
610
+
611
+ Отвечает за:
612
+ - Построение доказательств на основе ВАЛИДНЫХ векторов (и данных из БД)
613
+ - Проверку итоговой валидности (`is_valid`) доказательств
614
+ - Поиск путей доказательства между концептами (с использованием БД)
615
+ """
616
+
617
+ def __init__(self, db_conn):
618
+ """Инициализация системы доказательств.
619
+
620
+ Args:
621
+ db_conn: Экземпляр SFOSRDatabase для доступа к БД.
622
+ """
623
+ self.db_conn = db_conn # Store the database connection
624
+ # Базовые правила вывода (с бинарной валидностью)
625
+ self.inference_rules = {
626
+ "chain_rule": {
627
+ "pattern": "A → B, B → C ⊢ A → C",
628
+ "premise_types": ["Implication", "Implication"],
629
+ "conclusion_type": "Implication",
630
+ "domain": "logical_inference"
631
+ },
632
+ "causality_transfer": {
633
+ "pattern": "A → B (Causality), B → C (Causality) ⊢ A → C (Causality)",
634
+ "premise_types": ["Causality", "Causality"],
635
+ "conclusion_type": "Causality",
636
+ "domain": "causal_inference"
637
+ },
638
+ "implication_causality_chain": {
639
+ "pattern": "A → B (Implication), B → C (Causality) ⊢ A → C (Causality)",
640
+ "premise_types": ["Implication", "Causality"],
641
+ "conclusion_type": "Causality",
642
+ "domain": "mixed_inference"
643
+ },
644
+ # --- New Rule ---
645
+ "part_of_transitivity": {
646
+ "pattern": "A PartOf B, B PartOf C ⊢ A PartOf C",
647
+ "premise_types": ["PartOf", "PartOf"],
648
+ "conclusion_type": "PartOf",
649
+ "domain": "mereology"
650
+ },
651
+ # --- НОВОЕ ПРАВИЛО ---
652
+ "action_causality_chain": {
653
+ "pattern": "A -> B (Action), B -> C (Causality) |- A -> C (Causality)",
654
+ "premise_types": ["Action", "Causality"],
655
+ "conclusion_type": "Causality",
656
+ "domain": "action_inference"
657
+ },
658
+ # --- ЕЩЕ ОДНО НОВОЕ ПРАВИЛО ---
659
+ "action_isa_generalization": {
660
+ "pattern": "A -> B_inst (Action), B_inst IsA B_type |- A -> B_type (Action)",
661
+ "premise_types": ["Action", "IsA"],
662
+ "conclusion_type": "Action", # Результат - обобщенное действие
663
+ "domain": "inheritance_inference"
664
+ }
665
+ }
666
+
667
+ # Кэш для хранения построенных доказательств (только структура вывода)
668
+ self.proof_cache = {}
669
+
670
+ def load_rules(self, db_rules):
671
+ """
672
+ Загрузка правил вывода из БД (игнорируя любые старые данные plausibility)
673
+
674
+ Args:
675
+ db_rules: Словарь с правилами вывода из БД
676
+ """
677
+ for name, rule_data in db_rules.items():
678
+ rule_data.pop('plausibility', None) # Убеждаемся, что plausibility удалено
679
+ self.inference_rules[name] = rule_data
680
+
681
+ # --- Helper for Input-Only BFS ---
682
+ def _find_path_using_input_graph(self, input_graph, source_concept, target_concept) -> Dict[str, Any]:
683
+ """BFS using only the input graph."""
684
+ # print(f"DEBUG _find_path_using_input_graph: Start {source_concept} -> {target_concept}") # UNCOMMENTED
685
+ if source_concept not in input_graph["nodes"]:
686
+ # --- DEBUG PRINT ---
687
+ # print(f"DEBUG construct_proof: Source '{source_concept}' not in input graph nodes: {input_graph['nodes']}")
688
+ # --- END DEBUG PRINT ---
689
+ return {"status": "Source node not found"}
690
+
691
+ visited = {source_concept}
692
+ queue: List[Tuple[str, List[Tuple[str, str, str, str]]]] = [(source_concept, [])]
693
+
694
+ while queue:
695
+ current_concept, path = queue.pop(0)
696
+ # print(f"DEBUG _find_path_using_input_graph: Dequeue '{current_concept}'") # UNCOMMENTED
697
+
698
+ if current_concept in input_graph["adjacency"]:
699
+ for next_concept_input, vector_id_input in input_graph["adjacency"][current_concept].get("out", []):
700
+ # --- DEBUG PRINT ---
701
+ # print(f"DEBUG construct_proof (Input BFS): Edge {current_concept} -> {next_concept_input} via {vector_id_input}") # UNCOMMENTED
702
+ # --- END DEBUG PRINT ---
703
+ if next_concept_input == target_concept:
704
+ final_path = path + [(current_concept, next_concept_input, vector_id_input, 'input')]
705
+ # print(f"DEBUG _find_path_using_input_graph: Target reached. Path: {final_path}") # UNCOMMENTED
706
+ # --- DEBUG PRINT ---
707
+ # print(f"DEBUG construct_proof (Input BFS): Target '{target_concept}' reached. Path: {final_path}")
708
+ # --- END DEBUG PRINT ---
709
+ return {"status": "Path found", "path": final_path, "db_vectors_used": []}
710
+
711
+ if next_concept_input not in visited:
712
+ visited.add(next_concept_input)
713
+ new_path = path + [(current_concept, next_concept_input, vector_id_input, 'input')]
714
+ queue.append((next_concept_input, new_path))
715
+ # print(f"DEBUG _find_path_using_input_graph: Enqueue '{next_concept_input}'") # UNCOMMENTED
716
+
717
+ # print(f"DEBUG _find_path_using_input_graph: Path not found.") # UNCOMMENTED
718
+ # --- DEBUG PRINT ---
719
+ # print(f"DEBUG construct_proof (Input BFS): Path not found from '{source_concept}' to '{target_concept}'")
720
+ # --- END DEBUG PRINT ---
721
+ return {"status": "Path not found (input only)"}
722
+
723
+ # --- Helper for Combined BFS ---
724
+ def _find_path_using_combined_graph(self, input_graph, source_concept, target_concept) -> Dict[str, Any]:
725
+ """BFS using input graph AND database lookups."""
726
+ # print(f"\\nDEBUG _find_path_using_combined_graph: Start {source_concept} -> {target_concept}")
727
+ # --- DEBUG PRINT ---
728
+ # print(f"DEBUG construct_proof (Combined BFS): Start {source_concept} -> {target_concept}")
729
+ # --- END DEBUG PRINT ---
730
+ if source_concept not in input_graph["nodes"]:
731
+ if not self.db_conn.get_concept_by_name(source_concept):
732
+ # --- DEBUG PRINT ---
733
+ # print(f"DEBUG construct_proof (Combined BFS): Source '{source_concept}' not in input graph or DB.")
734
+ # --- END DEBUG PRINT ---
735
+ return {"status": "Source node not found"}
736
+
737
+ # visited теперь словарь: {concept_name: origin ('input' или 'db')}
738
+ visited: Dict[str, str] = {source_concept: 'start'}
739
+ queue: List[Tuple[str, List[Tuple[str, str, str, str]], Set[int]]] = [(source_concept, [], set())]
740
+ used_db_vector_ids = set()
741
+ db_vector_cache = {}
742
+
743
+ while queue:
744
+ current_concept, path, current_used_db_ids = queue.pop(0)
745
+ # --- DEBUG PRINT ---
746
+ # print(f"DEBUG construct_proof (Combined BFS): Dequeue '{current_concept}'")
747
+ # --- END DEBUG PRINT ---
748
+
749
+ # --- Шаг 1: Входной граф ---
750
+ if current_concept in input_graph["adjacency"]:
751
+ for next_concept_input, vector_id_input in input_graph["adjacency"][current_concept].get("out", []):
752
+ # --- DEBUG PRINT ---
753
+ # print(f"DEBUG construct_proof (Combined BFS): Input Edge {current_concept} -> {next_concept_input} via {vector_id_input}")
754
+ # --- END DEBUG PRINT ---
755
+ if next_concept_input == target_concept:
756
+ final_path = path + [(current_concept, next_concept_input, vector_id_input, 'input')]
757
+ final_db_vectors_list = [ self.db_conn.convert_db_vector_to_system_format(db_vector_cache[vid]) for vid in current_used_db_ids if vid in db_vector_cache ]
758
+ # --- DEBUG PRINT ---
759
+ # print(f"DEBUG construct_proof (Combined BFS): Target '{target_concept}' reached via input edge. Path: {final_path}")
760
+ # --- END DEBUG PRINT ---
761
+ return {"status": "Path found", "path": final_path, "db_vectors_used": final_db_vectors_list}
762
+
763
+ if next_concept_input not in visited:
764
+ visited[next_concept_input] = 'input' # Помечаем как посещенный через input
765
+ new_path = path + [(current_concept, next_concept_input, vector_id_input, 'input')]
766
+ queue.append((next_concept_input, new_path, current_used_db_ids))
767
+
768
+ # --- Шаг 2: База Данных ---
769
+ try:
770
+ current_concept_info = self.db_conn.get_concept_by_name(current_concept)
771
+ if not current_concept_info:
772
+ # --- DEBUG PRINT ---
773
+ # print(f"DEBUG construct_proof (Combined BFS): Concept '{current_concept}' not found in DB for DB search.")
774
+ # --- END DEBUG PRINT ---
775
+ continue
776
+ current_concept_id = current_concept_info['id']
777
+ # --- MORE DEBUG ---
778
+ # print(f"DEBUG _find_path_using_combined_graph: Querying DB vectors for concept '{current_concept}' (ID: {current_concept_id})")
779
+ # --- END MORE DEBUG ---
780
+ db_vectors_raw = self.db_conn.get_vectors_for_concept(current_concept_id)
781
+ # --- MORE DEBUG ---
782
+ # print(f"DEBUG _find_path_using_combined_graph: Received {len(db_vectors_raw)} vectors from DB for ID {current_concept_id}:")
783
+ # for dbv in db_vectors_raw:
784
+ # print(f" - ID: V{dbv.get('id')}, Type: {dbv.get('vector_type')}, Source: {dbv.get('source_name')}, Target: {dbv.get('target_name')}")
785
+ # --- END MORE DEBUG ---
786
+
787
+ for db_vector in db_vectors_raw:
788
+ if db_vector['source_id'] == current_concept_id:
789
+ next_concept_db = db_vector['target_name']
790
+ db_vector_actual_id = db_vector['id']
791
+ db_vector_system_id = f"V{db_vector_actual_id}"
792
+ # --- DEBUG PRINT ---
793
+ # print(f"DEBUG construct_proof (Combined BFS): DB Edge {current_concept} -> {next_concept_db} via {db_vector_system_id}")
794
+ # --- END DEBUG PRINT ---
795
+
796
+ if db_vector_actual_id not in db_vector_cache:
797
+ db_vector_cache[db_vector_actual_id] = db_vector
798
+
799
+ new_used_db_ids = current_used_db_ids.union({db_vector_actual_id})
800
+
801
+ if next_concept_db == target_concept:
802
+ final_path = path + [(current_concept, next_concept_db, db_vector_system_id, 'db')]
803
+ final_db_vectors_list = [ self.db_conn.convert_db_vector_to_system_format(db_vector_cache[vid]) for vid in new_used_db_ids if vid in db_vector_cache ]
804
+ # --- DEBUG PRINT ---
805
+ # print(f"DEBUG construct_proof (Combined BFS): Target '{target_concept}' reached via DB edge. Path: {final_path}")
806
+ # --- END DEBUG PRINT ---
807
+ return {"status": "Path found", "path": final_path, "db_vectors_used": final_db_vectors_list}
808
+
809
+ # Проверяем, был ли узел посещен и откуда
810
+ current_visit_status = visited.get(next_concept_db)
811
+ # Добавляем в очередь, ТОЛЬКО если не посещен через input
812
+ if current_visit_status != 'input':
813
+ # Если еще не посещался или посещался через db, обновляем/добавляем
814
+ if current_visit_status is None or current_visit_status == 'db':
815
+ visited[next_concept_db] = 'db' # Помечаем как посещенный через db
816
+ new_path = path + [(current_concept, next_concept_db, db_vector_system_id, 'db')]
817
+ queue.append((next_concept_db, new_path, new_used_db_ids))
818
+
819
+ except Exception as e:
820
+ print(f"DB Error during path finding in combined search: {e}")
821
+ return {"status": "DB error", "reason": str(e)}
822
+
823
+ return {"status": "Path not found (combined)"}
824
+
825
+ # --- Orchestrator Method ---
826
+ def find_proof_path(self, input_graph, source_concept, target_concept) -> Dict[str, Any]:
827
+ """
828
+ Ищет путь доказательства: сначала только по входным данным, затем с БД.
829
+
830
+ Args:
831
+ input_graph: Граф, построенный ТОЛЬКО из валидных входных векторов.
832
+ source_concept: Имя исходного концепта.
833
+ target_concept: Имя целевого концепта.
834
+
835
+ Returns:
836
+ Dict: Результат поиска пути (статус, путь, db_vectors_used).
837
+ """
838
+ # Phase 1: Input vectors only
839
+ # print("DEBUG find_proof_path: Starting Phase 1 (Input Only)")
840
+ input_path_info = self._find_path_using_input_graph(input_graph, source_concept, target_concept)
841
+
842
+ if input_path_info["status"] == "Path found":
843
+ # print("DEBUG find_proof_path: Path found in Phase 1. Returning.")
844
+ return input_path_info
845
+
846
+ # Phase 2: Combined input and DB vectors
847
+ # print("DEBUG find_proof_path: Path not found in Phase 1. Starting Phase 2 (Combined Input+DB)")
848
+ combined_path_info = self._find_path_using_combined_graph(input_graph, source_concept, target_concept)
849
+
850
+ if combined_path_info["status"] == "Path not found (combined)":
851
+ combined_path_info["status"] = "Path not found"
852
+
853
+ # print(f"DEBUG find_proof_path: Phase 2 finished with status: {combined_path_info['status']}")
854
+ return combined_path_info
855
+
856
+ def _apply_chain_rule(self, premises: List[Dict]) -> Tuple[Optional[Dict], bool]:
857
+ """Логика для правила chain_rule. Возвращает (вывод, валидность_шага)."""
858
+ if len(premises) != 2:
859
+ return None, False
860
+ v1, v2 = premises
861
+
862
+ # Проверяем соответствие паттерну правила
863
+ if v1["target"] == v2["source"] and \
864
+ v1["type"] == "Implication" and \
865
+ v2["type"] == "Implication": # Вторая посылка должна быть Implication
866
+
867
+ # Определяем тип вывода (просто берем из правила)
868
+ conclusion_type = self.inference_rules["chain_rule"]["conclusion_type"]
869
+
870
+ # Формируем вывод
871
+ conclusion = {
872
+ "id": f"S{len(self.proof_cache) + 1}", # Генерируем ID для шага
873
+ "source": v1["source"],
874
+ "target": v2["target"],
875
+ "type": conclusion_type,
876
+ "axis": v1["axis"], # Берем ось из первой посылки (можно уточнить)
877
+ "justification": f"Derived by chain_rule from {v1['id']} and {v2['id']}",
878
+ "derived": True # Помечаем, что вектор выведен
879
+ }
880
+ return conclusion, True # Шаг валиден
881
+
882
+ return None, False # Правило неприменимо
883
+
884
+ def _apply_causality_transfer(self, premises: List[Dict]) -> Tuple[Optional[Dict], bool]:
885
+ """Логика для правила causality_transfer. Возвращает (вывод, валидность_шага)."""
886
+ if len(premises) != 2:
887
+ return None, False
888
+ v1, v2 = premises
889
+
890
+ # Проверяем соответствие паттерну правила
891
+ if v1["target"] == v2["source"] and \
892
+ v1["type"] == "Causality" and \
893
+ v2["type"] == "Causality": # Вторая посылка должна быть Causality
894
+
895
+ conclusion_type = self.inference_rules["causality_transfer"]["conclusion_type"]
896
+
897
+ conclusion = {
898
+ "id": f"S{len(self.proof_cache) + 1}",
899
+ "source": v1["source"],
900
+ "target": v2["target"],
901
+ "type": conclusion_type,
902
+ "axis": v1["axis"],
903
+ "justification": f"Derived by causality_transfer from {v1['id']} and {v2['id']}",
904
+ "derived": True
905
+ }
906
+ return conclusion, True
907
+
908
+ return None, False
909
+
910
+ def _apply_implication_causality_chain(self, premises: List[Dict]) -> Tuple[Optional[Dict], bool]:
911
+ """Логика для правила implication_causality_chain. Возвращает (вывод, валидность_шага)."""
912
+ if len(premises) != 2:
913
+ return None, False
914
+ v1, v2 = premises
915
+
916
+ # Проверяем соответствие паттерну
917
+ if v1["target"] == v2["source"] and \
918
+ v1["type"] == "Implication" and \
919
+ v2["type"] == "Causality":
920
+
921
+ conclusion_type = self.inference_rules["implication_causality_chain"]["conclusion_type"]
922
+
923
+ conclusion = {
924
+ "id": f"S{len(self.proof_cache) + 1}",
925
+ "source": v1["source"],
926
+ "target": v2["target"],
927
+ "type": conclusion_type,
928
+ "axis": v1["axis"], # Берем ось из первой посылки
929
+ "justification": f"Derived by implication_causality_chain from {v1['id']} and {v2['id']}",
930
+ "derived": True
931
+ }
932
+ return conclusion, True
933
+
934
+ return None, False
935
+
936
+ # --- New Method for PartOf Rule ---
937
+ def _apply_part_of_transitivity(self, premises: List[Dict]) -> Tuple[Optional[Dict], bool]:
938
+ """Логика для правила part_of_transitivity."""
939
+ if len(premises) != 2:
940
+ return None, False
941
+ v1, v2 = premises
942
+
943
+ # Проверяем типы посылок и связь
944
+ if v1["target"] == v2["source"] and \
945
+ v1.get("type") == "PartOf" and \
946
+ v2.get("type") == "PartOf":
947
+
948
+ conclusion_type = self.inference_rules["part_of_transitivity"]["conclusion_type"]
949
+
950
+ conclusion = {
951
+ "id": f"S{len(self.proof_cache) + 1}",
952
+ "source": v1["source"],
953
+ "target": v2["target"],
954
+ "type": conclusion_type,
955
+ "axis": v1.get("axis", "partonomy"), # Use axis from v1 or default
956
+ "justification": f"Derived by part_of_transitivity from {v1.get('id', '?')} and {v2.get('id', '?')}",
957
+ "derived": True
958
+ }
959
+ return conclusion, True
960
+
961
+ return None, False
962
+
963
+ # --- Новая логика для правила Action -> Causality ---
964
+ def _apply_action_causality_chain(self, premises: List[Dict]) -> Tuple[Optional[Dict], bool]:
965
+ """Логика для правила action_causality_chain. Возвращает (вывод, валидность_шага)."""
966
+ if len(premises) != 2:
967
+ return None, False
968
+ v1, v2 = premises
969
+
970
+ # Проверяем соответствие паттерну правила: A->B (Action), B->C (Causality)
971
+ if v1["target"] == v2["source"] and \
972
+ v1.get("type") == "Action" and \
973
+ v2.get("type") == "Causality":
974
+
975
+ conclusion_type = self.inference_rules["action_causality_chain"]["conclusion_type"]
976
+
977
+ conclusion = {
978
+ "id": f"S{len(self.proof_cache) + 1}",
979
+ "source": v1["source"],
980
+ "target": v2["target"],
981
+ "type": conclusion_type,
982
+ "axis": v1.get("axis", v2.get("axis")), # Ось можно взять из Action или Causality
983
+ "justification": f"Derived by action_causality_chain from {v1.get('id', '?')} and {v2.get('id', '?')}",
984
+ "derived": True
985
+ }
986
+ return conclusion, True # Шаг считаем валидным, если правило применилось
987
+
988
+ return None, False
989
+
990
+ # --- Логика для правила Action -> IsA ---
991
+ def _apply_action_isa_generalization(self, premises: List[Dict]) -> Tuple[Optional[Dict], bool]:
992
+ """Логика для правила action_isa_generalization."""
993
+ if len(premises) != 2:
994
+ return None, False
995
+ v_action, v_isa = premises # Ожидаем Action, затем IsA
996
+
997
+ # Проверяем типы и связь: A -> B_inst (Action), B_inst IsA B_type
998
+ if v_action.get("type") == "Action" and \
999
+ v_isa.get("type") == "IsA" and \
1000
+ v_action.get("target") == v_isa.get("source"): # Target(Action) == Source(IsA)
1001
+
1002
+ conclusion_type = self.inference_rules["action_isa_generalization"]["conclusion_type"]
1003
+ source_a = v_action.get("source")
1004
+ target_b_type = v_isa.get("target") # Берем тип из IsA
1005
+
1006
+ conclusion = {
1007
+ "id": f"S{len(self.proof_cache) + 1}",
1008
+ "source": source_a,
1009
+ "target": target_b_type,
1010
+ "type": conclusion_type, # Тип сохраняется как Action
1011
+ "axis": v_action.get("axis"), # Ось берем из Action
1012
+ "justification": f"Derived by action_isa_generalization from {v_action.get('id', '?')} and {v_isa.get('id', '?')}",
1013
+ "derived": True
1014
+ }
1015
+ # print(f"DEBUG _apply_action_isa_generalization: Applied. Conclusion: {conclusion}") # Временный дебаг
1016
+ return conclusion, True
1017
+
1018
+ # print(f"DEBUG _apply_action_isa_generalization: Rule not applicable. v_action type: {v_action.get('type')}, v_isa type: {v_isa.get('type')}, link: {v_action.get('target')} == {v_isa.get('source')}") # Временный дебаг
1019
+ return None, False
1020
+
1021
+ def apply_inference_rule(self, rule_name: str, premises: List[Dict]) -> Tuple[Optional[Dict], bool]:
1022
+ """Применяет правило вывода, возвращая (вывод, валидность_шага)."""
1023
+ rule_functions = {
1024
+ "chain_rule": self._apply_chain_rule,
1025
+ "causality_transfer": self._apply_causality_transfer,
1026
+ "implication_causality_chain": self._apply_implication_causality_chain,
1027
+ "part_of_transitivity": self._apply_part_of_transitivity,
1028
+ "action_causality_chain": self._apply_action_causality_chain, # Добавляем новое правило
1029
+ "action_isa_generalization": self._apply_action_isa_generalization # Добавляем еще одно новое правило
1030
+ }
1031
+
1032
+ conclusion, is_step_valid = None, False
1033
+ if rule_name in rule_functions:
1034
+ # Предполагаем, что в premises УЖЕ только валидные векторы
1035
+ conclusion, is_step_valid = rule_functions[rule_name](premises)
1036
+
1037
+ # --- DEBUG PRINT ---
1038
+ # print(f"DEBUG apply_inference_rule: Rule='{rule_name}', Premises={[p.get('id', '?') for p in premises]}, Conclusion='{conclusion.get('id', None) if conclusion else None}', StepValid={is_step_valid}")
1039
+ # --- END DEBUG PRINT ---
1040
+
1041
+ if conclusion:
1042
+ # Кэшируем только структуру успешного вывода
1043
+ self.proof_cache[conclusion["id"]] = conclusion
1044
+
1045
+ return conclusion, is_step_valid
1046
+
1047
+ def construct_proof(self, vectors_for_proof: List[Dict], source_concept: str, target_concept: str) -> Dict:
1048
+ """Построение доказательства от source_concept к target_concept.
1049
+ Использует граф из предоставленных векторов (входных + временных IsA)
1050
+ и динамически подгружает векторы из БД.
1051
+ """
1052
+ # --- DEBUG PRINT ---
1053
+ # print(f"\\nDEBUG construct_proof: Start. Query: {source_concept} -> {target_concept}")
1054
+ # print(f"DEBUG construct_proof: Input vectors count: {len(vectors_for_proof)}")
1055
+ # --- END DEBUG PRINT ---
1056
+
1057
+ # Убираем instance_definitions из параметров
1058
+ valid_vectors_input = vectors_for_proof # Переименуем для консистентности с кодом ниже
1059
+
1060
+ if not valid_vectors_input and not self.db_conn.get_concept_by_name(source_concept):
1061
+ # --- DEBUG PRINT ---
1062
+ # print("DEBUG construct_proof: Failed - No input vectors and source concept not found in DB.")
1063
+ # --- END DEBUG PRINT ---
1064
+ return {"status": "Failed", "reason": "No input vectors and source concept not found in DB", "is_valid": False}
1065
+
1066
+ # --- Теперь строим граф и vector_map из ВСЕХ предоставленных векторов ---
1067
+ input_graph = self._build_proof_graph(valid_vectors_input)
1068
+ vector_map = {v["id"]: v for v in valid_vectors_input}
1069
+ # --- DEBUG PRINT ---
1070
+ # print(f"DEBUG construct_proof: Built input graph with {len(input_graph['nodes'])} nodes and {len(input_graph['edges'])} edges.")
1071
+ # print(f"DEBUG construct_proof: Vector map keys: {list(vector_map.keys())}")
1072
+ # --- MORE DEBUG ---
1073
+ import pprint
1074
+ # print(f"DEBUG construct_proof: Input Graph Adjacency:\n{pprint.pformat(input_graph.get('adjacency', {}))}")
1075
+ # --- END MORE DEBUG ---
1076
+ # --- END DEBUG PRINT ---
1077
+ # ---------------------------------------------------------------------
1078
+
1079
+ # Ищем путь: сначала только входные, потом с БД
1080
+ path_info = self.find_proof_path(input_graph, source_concept, target_concept)
1081
+ # --- DEBUG PRINT ---
1082
+ # print(f"DEBUG construct_proof: Path finding result: Status='{path_info.get('status')}', Path length={len(path_info.get('path', []))}")
1083
+ # --- END DEBUG PRINT ---
1084
+
1085
+ if path_info.get("status") != "Path found":
1086
+ # --- DEBUG PRINT ---
1087
+ # print(f"DEBUG construct_proof: Failed - Path not found. Reason: {path_info.get('status', 'Unknown')}")
1088
+ # --- END DEBUG PRINT ---
1089
+ return {"status": "Failed", "reason": f"Path not found: {path_info.get('status', 'Unknown')}", "is_valid": False}
1090
+
1091
+ path = path_info["path"]
1092
+ db_vectors_used = path_info.get("db_vectors_used", [])
1093
+
1094
+ # --- MORE DEBUG ---
1095
+ # print(f"DEBUG construct_proof: Found Path: {path}")
1096
+ # print(f"DEBUG construct_proof: DB Vectors Used: {[v.get('id') for v in db_vectors_used]}")
1097
+ # --- END MORE DEBUG ---
1098
+
1099
+ # Добавляем векторы из БД в vector_map
1100
+ for db_vec in db_vectors_used:
1101
+ if db_vec["id"] not in vector_map:
1102
+ vector_map[db_vec["id"]] = db_vec
1103
+
1104
+ # --- MORE DEBUG ---
1105
+ # print(f"DEBUG construct_proof: Vector Map Contents:")
1106
+ # for vid, vdata in vector_map.items():
1107
+ # print(f" - {vid}: Type={vdata.get('type')}, Source={vdata.get('source')}, Target={vdata.get('target')}")
1108
+ # --- END MORE DEBUG ---
1109
+
1110
+ # --- Проверка на прямой путь ---
1111
+ if len(path) == 1:
1112
+ direct_vector_id = path[0][2]
1113
+ direct_vector = vector_map.get(direct_vector_id)
1114
+ # --- DEBUG PRINT ---
1115
+ # print(f"DEBUG construct_proof: Path length is 1. Direct vector ID: {direct_vector_id}")
1116
+ # --- END DEBUG PRINT ---
1117
+ if direct_vector:
1118
+ # --- DEBUG PRINT ---
1119
+ # print(f"DEBUG construct_proof: Success - Direct proof found using vector {direct_vector_id}.")
1120
+ # --- END DEBUG PRINT ---
1121
+ return {
1122
+ "status": "Success",
1123
+ "source": source_concept,
1124
+ "target": target_concept,
1125
+ "steps": [], # Нет шагов для прямого доказательства
1126
+ "rule": "direct", # Указываем, что это прямой путь
1127
+ "direct_vector_id": direct_vector_id,
1128
+ "is_valid": True, # Прямой путь считается валидным
1129
+ "final_conclusion_type": direct_vector.get("type"),
1130
+ "metadata": {} # Пока без метаданных о цикле здесь
1131
+ }
1132
+ else:
1133
+ # --- DEBUG PRINT ---
1134
+ # print(f"DEBUG construct_proof: Failed - Direct vector {direct_vector_id} not found in map.")
1135
+ # --- END DEBUG PRINT ---
1136
+ return {"status": "Failed", "reason": f"Direct vector {direct_vector_id} not found", "is_valid": False}
1137
+
1138
+ # --- Построение доказательства по шагам ---
1139
+ steps = []
1140
+ current_premise = None # Будет содержать ВЕКТОР (словарь)
1141
+ overall_validity = True # Валидность всего доказательства
1142
+ cycle_warning = None
1143
+ visited_nodes_in_proof = {source_concept} # Для обнаружения циклов во время ПОСТРОЕНИЯ
1144
+
1145
+ # --- DEBUG PRINT ---
1146
+ # print("DEBUG construct_proof: Starting step-by-step construction...")
1147
+ # --- END DEBUG PRINT ---
1148
+ for i, (seg_source, seg_target, vector_id, origin) in enumerate(path):
1149
+ # --- DEBUG PRINT ---
1150
+ # print(f"DEBUG construct_proof: Processing segment {i+1}/{len(path)}: {seg_source} -> {seg_target} via {vector_id} (from {origin})")
1151
+ # --- END DEBUG PRINT ---
1152
+
1153
+ # Защита от отсутствия вектора в карте (на всякий случай)
1154
+ premise2 = vector_map.get(vector_id)
1155
+ if not premise2:
1156
+ # --- DEBUG PRINT ---
1157
+ # print(f"DEBUG construct_proof: Failed - Vector {vector_id} for segment {i+1} not found in map.")
1158
+ # --- END DEBUG PRINT ---
1159
+ overall_validity = False
1160
+ return {"status": "Failed", "reason": f"Vector {vector_id} not found during step construction", "is_valid": False}
1161
+
1162
+ premise2_source = origin # 'input' or 'db'
1163
+
1164
+ if current_premise is None:
1165
+ current_premise = premise2
1166
+ source1 = premise2_source # Источник первой посылки - сам этот вектор
1167
+ # --- DEBUG PRINT ---
1168
+ # print(f"DEBUG construct_proof: Segment {i+1}: Initial premise set to {current_premise.get('id')}")
1169
+ # --- MORE DEBUG ---
1170
+ # print(f"DEBUG construct_proof: Segment {i+1}: Initial premise set to: ID={current_premise.get('id', 'NO_ID')}, Type={current_premise.get('type', 'NO_TYPE')}, Source={current_premise.get('source')}, Target={current_premise.get('target')}")
1171
+ # --- END MORE DEBUG ---
1172
+ # --- END DEBUG PRINT ---
1173
+ else:
1174
+ premises = [current_premise, premise2]
1175
+ premise_ids = [p.get("id", "?") for p in premises]
1176
+ source1 = "derived" if current_premise.get("derived") else current_premise.get("origin", "input") # Откуда первая посылка?
1177
+
1178
+ conclusion = None
1179
+ is_step_valid = False
1180
+ rule_name = None
1181
+
1182
+ # --- DEBUG PRINT ---
1183
+ # print(f"DEBUG construct_proof: Segment {i+1}: Trying to apply rules. Premise1='{premises[0].get('id')}' ({source1}), Premise2='{premises[1].get('id')}' ({premise2_source})")
1184
+ # --- MORE DEBUG ---
1185
+ prem1_id = current_premise.get('id', 'NO_ID')
1186
+ prem1_type = current_premise.get('type', 'NO_TYPE')
1187
+ prem2_id = premise2.get('id', 'NO_ID')
1188
+ prem2_type = premise2.get('type', 'NO_TYPE')
1189
+ # print(f"DEBUG construct_proof: Applying rules. Premise1: ID={prem1_id}, Type={prem1_type} | Premise2: ID={prem2_id}, Type={prem2_type}")
1190
+ # --- MORE DEBUG ---
1191
+ # print(f" Premise1 Details: Source={current_premise.get('source')}, Target={current_premise.get('target')}")
1192
+ # print(f" Premise2 Details: Source={premise2.get('source')}, Target={premise2.get('target')}")
1193
+ # --- END MORE DEBUG ---
1194
+ # --- END DEBUG PRINT ---
1195
+
1196
+ # Применяем подходящее правило
1197
+ for key in self.inference_rules.keys():
1198
+ temp_conclusion, temp_valid = self.apply_inference_rule(key, premises)
1199
+ if temp_conclusion:
1200
+ conclusion = temp_conclusion
1201
+ is_step_valid = temp_valid
1202
+ rule_name = key
1203
+ # --- DEBUG PRINT ---
1204
+ # print(f"DEBUG construct_proof: Segment {i+1}: Applied rule '{rule_name}'. Conclusion='{conclusion.get('id')}', StepValid={is_step_valid}")
1205
+ # --- END DEBUG PRINT ---
1206
+ break # Нашли подходящее правило
1207
+
1208
+ if conclusion:
1209
+ conclusion["origin"] = "derived" # Помечаем, что вывод получен
1210
+ step_detail = {
1211
+ "id": conclusion["id"],
1212
+ "rule": rule_name,
1213
+ "premises": premise_ids,
1214
+ "conclusion": conclusion,
1215
+ "is_valid": is_step_valid,
1216
+ "premise1_source": source1,
1217
+ "premise2_source": premise2_source, # 'input' or 'db'
1218
+ # --- DEBUG PRINT ---
1219
+ # "debug_premise1": current_premise,
1220
+ # "debug_premise2": premise2
1221
+ # --- END DEBUG PRINT ---
1222
+ }
1223
+ steps.append(step_detail)
1224
+ current_premise = conclusion # Результат этого шага становится первой посылкой для следующего
1225
+
1226
+ if not is_step_valid:
1227
+ overall_validity = False
1228
+ # --- DEBUG PRINT ---
1229
+ # print(f"DEBUG construct_proof: Segment {i+1}: Step marked invalid, setting overall validity to False.")
1230
+ # --- END DEBUG PRINT ---
1231
+ # Можно прервать, если один шаг невалиден? Или достроить? Пока достраиваем.
1232
+
1233
+ # Проверка на цикл в построении
1234
+ target_node = conclusion.get("target")
1235
+ if target_node in visited_nodes_in_proof:
1236
+ cycle_warning = f"Cycle detected during proof construction: revisiting node '{target_node}'"
1237
+ # --- DEBUG PRINT ---
1238
+ # print(f"DEBUG construct_proof: Segment {i+1}: {cycle_warning}")
1239
+ # --- END DEBUG PRINT ---
1240
+ else:
1241
+ visited_nodes_in_proof.add(target_node)
1242
+
1243
+ else:
1244
+ # Не смогли применить правило - доказательство невалидно
1245
+ overall_validity = False
1246
+ # --- DEBUG PRINT ---
1247
+ # print(f"DEBUG construct_proof: Failed - No applicable rule found for premises {premise_ids} in segment {i+1}.")
1248
+ # --- END DEBUG PRINT ---
1249
+ return {"status": "Failed", "reason": f"No inference rule applicable for premises {premise_ids}", "is_valid": False}
1250
+
1251
+ # --- Финальное формирование результата ---
1252
+ final_conclusion = current_premise # Последний вывод - это и есть результат
1253
+
1254
+ # Проверка, что финальный вывод соответствует запросу
1255
+ if not final_conclusion or \
1256
+ final_conclusion.get("source") != source_concept or \
1257
+ final_conclusion.get("target") != target_concept:
1258
+ # --- DEBUG PRINT ---
1259
+ final_src = final_conclusion.get('source') if final_conclusion else 'None'
1260
+ final_tgt = final_conclusion.get('target') if final_conclusion else 'None'
1261
+ # print(f"DEBUG construct_proof: Failed - Final conclusion mismatch. Expected={source_concept}->{target_concept}, Got={final_src}->{final_tgt}")
1262
+ # --- END DEBUG PRINT ---
1263
+ overall_validity = False
1264
+ # Статус все еще может быть Success, но is_valid = False? Или статус Failed?
1265
+ # Сделаем статус Failed, если вывод не совпал.
1266
+ return {
1267
+ "status": "Failed",
1268
+ "reason": f"Final conclusion mismatch: expected {source_concept}->{target_concept}, got {final_conclusion.get('source') if final_conclusion else 'N/A'}->{final_conclusion.get('target') if final_conclusion else 'N/A'}",
1269
+ "is_valid": False,
1270
+ "source": source_concept,
1271
+ "target": target_concept,
1272
+ "steps": steps,
1273
+ "metadata": {"cycle_warning": cycle_warning} if cycle_warning else {}
1274
+ }
1275
+
1276
+ final_result = {
1277
+ "status": "Success", # Если дошли сюда, структура доказательства построена
1278
+ "source": source_concept,
1279
+ "target": target_concept,
1280
+ "steps": steps,
1281
+ "is_valid": overall_validity, # Валидность зависит от валидности всех шагов
1282
+ "final_conclusion_type": final_conclusion.get("type"),
1283
+ "metadata": {"cycle_warning": cycle_warning} if cycle_warning else {}
1284
+ }
1285
+ # --- DEBUG PRINT ---
1286
+ # print(f"DEBUG construct_proof: Finished successfully.")
1287
+ # print(f"DEBUG construct_proof: Final Result Status: {final_result['status']}")
1288
+ # print(f"DEBUG construct_proof: Final Result IsValid: {final_result['is_valid']}")
1289
+ # print(f"DEBUG construct_proof: Final Result Steps Count: {len(final_result['steps'])}")
1290
+ # if final_result['steps']:
1291
+ # for idx, step in enumerate(final_result['steps']):
1292
+ # print(f" Step {idx+1} ({step['id']}): Rule='{step['rule']}', Premises={step['premises']}, Valid={step['is_valid']}, Conc={step['conclusion']['source']}->{step['conclusion']['target']}")
1293
+ # print(f"DEBUG construct_proof: Final Conclusion Type: {final_result['final_conclusion_type']}")
1294
+ # print(f"DEBUG construct_proof: Metadata: {final_result['metadata']}")
1295
+ # --- END DEBUG PRINT ---
1296
+ return final_result
1297
+
1298
+
1299
+ def _build_proof_graph(self, vectors):
1300
+ """Вспомогательная функция для построения графа из векторов"""
1301
+ graph = {"nodes": set(), "edges": [], "adjacency": {}}
1302
+ for v in vectors:
1303
+ source, target, v_id = v["source"], v["target"], v["id"]
1304
+ graph["nodes"].add(source)
1305
+ graph["nodes"].add(target)
1306
+ graph["edges"].append((source, target, v_id))
1307
+
1308
+ # Обновление списка смежности
1309
+ if source not in graph["adjacency"]:
1310
+ graph["adjacency"][source] = {"out": [], "in": []}
1311
+ if target not in graph["adjacency"]:
1312
+ graph["adjacency"][target] = {"out": [], "in": []}
1313
+
1314
+ graph["adjacency"][source]["out"].append((target, v_id))
1315
+ graph["adjacency"][target]["in"].append((source, v_id))
1316
+
1317
+ return graph
1318
+
1319
+ if __name__ == "__main__":
1320
+ print(f"SFOSR Integrated System v{SFOSR_CONFIG['version']}")
1321
+ print("Готов к анализу смысловых структур.")
1322
+
1323
+ # Пример входных данных (используем концепты из БД для демонстрации)
1324
+ example = {
1325
+ "text": "Emergence leads to multi-level space, which causes cross-level causation, finally leading to regulatory flow.",
1326
+ "vectors": [
1327
+ {
1328
+ "id": "V_EC_MLS",
1329
+ "source": "emergent_complexity",
1330
+ "target": "multi_level_space",
1331
+ "type": "Implication",
1332
+ "axis": "structure <-> hierarchy",
1333
+ "justification": "Emergence creates levels"
1334
+ },
1335
+ {
1336
+ "id": "V_MLS_CLC",
1337
+ "source": "multi_level_space",
1338
+ "target": "cross_level_causation",
1339
+ "type": "Causality", # Тип Causality
1340
+ "axis": "level <-> interaction",
1341
+ "justification": "Levels imply cross-level effects"
1342
+ },
1343
+ {
1344
+ "id": "V_CLC_RF",
1345
+ "source": "cross_level_causation",
1346
+ "target": "regulatory_flow",
1347
+ "type": "Causality", # Тип Causality
1348
+ "axis": "cause <-> regulation",
1349
+ "justification": "Cross-level effects drive regulation"
1350
+ }
1351
+ ],
1352
+ "proof_query": {
1353
+ "source": "multi_level_space",
1354
+ "target": "regulatory_flow"
1355
+ }
1356
+ }
1357
+
1358
+ # Создаем и тестируем интегрированную систему
1359
+ system = SFOSRSystem()
1360
+ result = system.process(example)
1361
+
1362
+ # Выводим результаты (обновлено для бинарной валидности)
1363
+ print("\n--- Результаты Обработки ---")
1364
+ print(f"Статус: {result['status']}")
1365
+ if result['status'] == 'Success':
1366
+ print(f"Компилируемость: {result['analysis']['is_compilable']}")
1367
+ print("\n--- Верификация ---")
1368
+ print(f"Всего обработано векторов: {result['verification']['total_vectors_processed']}")
1369
+ print(f"Валидных векторов: {result['verification']['valid_count']}")
1370
+ print(f"Уровень соответствия: {result['verification']['compliance_rate'] * 100:.1f}%")
1371
+ print("Данные по Векторам:")
1372
+ for v_id, data in result['verification']['vectors_data'].items():
1373
+ valid_str = "Валиден" if data['is_valid'] else "Не валиден"
1374
+ issues_str = ', '.join(data['issues']) if data['issues'] else 'Нет'
1375
+ print(f" - {v_id}: Статус={valid_str}, Проблемы: {issues_str}")
1376
+
1377
+ if "proof" in result:
1378
+ print("\n--- Доказательство ---")
1379
+ proof = result['proof']
1380
+ print(f"Статус: {proof['status']}")
1381
+ valid_proof_str = "Валидно" if proof.get('is_valid', False) else "Не валидно"
1382
+ print(f"Общая Валидность: {valid_proof_str}")
1383
+ if proof['status'] == 'Success':
1384
+ print(f"Доказательство: {proof['source']} → {proof['target']}")
1385
+ print(f"Тип финального вывода: {proof.get('final_conclusion_type', 'N/A')}")
1386
+ print("Шаги доказательства:")
1387
+ if proof['steps']:
1388
+ for step in proof['steps']:
1389
+ step_valid_str = "Валиден" if step['is_valid'] else "Не валиден"
1390
+ print(f" - {step['id']}: Правило={step['rule']}, Посылки=({', '.join(step['premises'])}) | Статус={step_valid_str}")
1391
+ if step['conclusion']:
1392
+ print(f" Вывод: {step['conclusion']['source']} → {step['conclusion']['target']} ({step['conclusion']['type']})")
1393
+ else:
1394
+ print(f" Вывод: None (Ошибка: {step.get('reason', '')})")
1395
+ else:
1396
+ print(" (Нет шагов)")
1397
+ elif 'reason' in proof:
1398
+ print(f"Причина неудачи: {proof['reason']}")
1399
+
1400
+ elif 'details' in result and 'validation_issues' in result['details']:
1401
+ print("\n--- Ошибки Анализа ---")
1402
+ for issue in result['details']['validation_issues']:
1403
+ print(f" - {issue}")