pymemora 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
memora/__init__.py ADDED
@@ -0,0 +1,24 @@
1
+ """Memora: A memory-aware training optimization framework for PyTorch."""
2
+
3
+ from memora.config import MemoraConfig, OptimizationRecommendation
4
+ from memora.engine import Memora
5
+ from memora.hardware.gpu_detector import DeviceInfo, GPUDetector, HardwareReport
6
+ from memora.hardware.memory_monitor import MemoryMonitor, MemorySnapshot, MemoryTracker
7
+ from memora.reporting.report import RunReport, compare_runs
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ __all__ = [
12
+ "Memora",
13
+ "MemoraConfig",
14
+ "OptimizationRecommendation",
15
+ "GPUDetector",
16
+ "DeviceInfo",
17
+ "HardwareReport",
18
+ "MemoryMonitor",
19
+ "MemorySnapshot",
20
+ "MemoryTracker",
21
+ "RunReport",
22
+ "compare_runs",
23
+ ]
24
+
memora/cli.py ADDED
@@ -0,0 +1,104 @@
1
+ """Command line interface for Memora using Typer and Rich."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import typer
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ from rich.table import Table
10
+
11
+ from memora import __version__
12
+ from memora.hardware.gpu_detector import GPUDetector
13
+ from memora.utils.common import format_bytes
14
+
15
+ app = typer.Typer(help="Memora: PyTorch Memory-Aware Optimization CLI")
16
+ console = Console()
17
+
18
+
19
+ @app.command()
20
+ def hardware() -> None:
21
+ """Detect and display system hardware, CPU RAM, and GPU memory."""
22
+ report = GPUDetector.get_hardware_report()
23
+
24
+ console.print(Panel.fit(
25
+ f"[bold cyan]Memora Hardware Discovery[/bold cyan]\n"
26
+ f"OS: {report.platform_system} ({report.platform_release})\n"
27
+ f"Python: {report.python_version} | PyTorch: {report.torch_version}\n"
28
+ f"CUDA Available: [{'green' if report.cuda_available else 'yellow'}]{report.cuda_available}[/]"
29
+ + (f" (v{report.cuda_version})" if report.cuda_version else "")
30
+ + f"\nSystem RAM: {report.to_dict()['system_ram_available_str']} free / {report.to_dict()['system_ram_total_str']} total",
31
+ title="System Environment",
32
+ ))
33
+
34
+ table = Table(title="Detected Compute Devices", header_style="bold magenta")
35
+ table.add_column("Index", style="cyan")
36
+ table.add_column("Type", style="bold")
37
+ table.add_column("Device Name", style="white")
38
+ table.add_column("Total Memory", style="green")
39
+ table.add_column("Compute Cap.", style="yellow")
40
+ table.add_column("FP16 / BF16", style="blue")
41
+
42
+ for dev in report.devices:
43
+ half_support = f"FP16: {dev.supports_fp16} | BF16: {dev.supports_bf16}"
44
+ table.add_row(
45
+ str(dev.device_index),
46
+ dev.device_type.upper(),
47
+ dev.name,
48
+ format_bytes(dev.total_memory),
49
+ dev.compute_capability or "N/A",
50
+ half_support,
51
+ )
52
+
53
+ console.print(table)
54
+
55
+
56
+ @app.command()
57
+ def report(file_path: str = typer.Argument(..., help="Path to Memora report JSON file")) -> None:
58
+ """Read and format a Memora training run report."""
59
+ try:
60
+ with open(file_path, "r", encoding="utf-8") as f:
61
+ data = json.load(f)
62
+
63
+ console.print(Panel(
64
+ f"[bold]Run ID:[/bold] {data.get('run_id')}\n"
65
+ f"[bold]Timestamp:[/bold] {data.get('timestamp')}\n"
66
+ f"[bold]Device:[/bold] {data.get('hardware_info', {}).get('default_device', {}).get('name')}",
67
+ title="[bold green]Report Summary[/bold green]",
68
+ ))
69
+
70
+ opts = data.get("optimization_decisions", {})
71
+ opt_table = Table(title="Optimization Decisions", header_style="bold cyan")
72
+ opt_table.add_column("Parameter", style="bold")
73
+ opt_table.add_column("Value", style="green")
74
+
75
+ for k, v in opts.items():
76
+ if k != "reasons":
77
+ opt_table.add_row(k, str(v))
78
+ console.print(opt_table)
79
+
80
+ prof = data.get("profiler_summary", {})
81
+ if prof:
82
+ prof_table = Table(title="Performance Metrics", header_style="bold yellow")
83
+ prof_table.add_column("Metric", style="bold")
84
+ prof_table.add_column("Value", style="cyan")
85
+ for k, v in prof.items():
86
+ prof_table.add_row(k, str(v))
87
+ console.print(prof_table)
88
+
89
+ except Exception as e:
90
+ console.print(f"[bold red]Error loading report:[/bold red] {e}")
91
+ raise typer.Exit(code=1)
92
+
93
+
94
+ @app.command()
95
+ def version() -> None:
96
+ """Show Memora version."""
97
+ import torch
98
+ console.print(f"Memora version: [bold green]{__version__}[/bold green]")
99
+ console.print(f"PyTorch version: [bold cyan]{torch.__version__}[/bold cyan]")
100
+
101
+
102
+ if __name__ == "__main__":
103
+ app()
104
+
memora/config.py ADDED
@@ -0,0 +1,126 @@
1
+ """Configuration models and schemas for Memora."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Literal, Optional, Union
6
+ from pydantic import BaseModel, Field, field_validator
7
+
8
+
9
+ PrecisionType = Literal["auto", "fp32", "fp16", "bf16"]
10
+
11
+
12
+ class MemoraConfig(BaseModel):
13
+ """Configuration for Memora optimization and training run."""
14
+
15
+ # Device & resource management
16
+ device: str = Field(
17
+ default="auto",
18
+ description="Target compute device: 'auto', 'cuda', 'cuda:0', 'mps', or 'cpu'.",
19
+ )
20
+ gpu_memory_limit: Union[float, int, Literal["auto"]] = Field(
21
+ default="auto",
22
+ description="VRAM memory limit. If float <= 1.0, fraction of total VRAM. If int > 1, bytes limit. 'auto' uses default safety headroom.",
23
+ )
24
+ safety_margin: float = Field(
25
+ default=0.15,
26
+ ge=0.01,
27
+ le=0.50,
28
+ description="Fraction of memory reserved as a safety buffer against unexpected memory spikes (default: 15%).",
29
+ )
30
+
31
+ # Batch search parameters
32
+ batch_size: Union[int, Literal["auto"]] = Field(
33
+ default="auto",
34
+ description="Micro-batch size for training. 'auto' benchmarks and selects optimal size.",
35
+ )
36
+ min_batch_size: int = Field(
37
+ default=1,
38
+ ge=1,
39
+ description="Minimum batch size allowed during search.",
40
+ )
41
+ max_batch_size: int = Field(
42
+ default=512,
43
+ ge=1,
44
+ description="Maximum batch size allowed during search.",
45
+ )
46
+
47
+ # Gradient accumulation parameters
48
+ effective_batch_size: Optional[int] = Field(
49
+ default=None,
50
+ ge=1,
51
+ description="Target effective batch size. When provided with small batch_size, gradient accumulation is automatically configured.",
52
+ )
53
+ gradient_accumulation_steps: Union[int, Literal["auto"]] = Field(
54
+ default="auto",
55
+ description="Gradient accumulation steps. 'auto' computes steps to match effective_batch_size or 1.",
56
+ )
57
+
58
+ # Precision
59
+ precision: PrecisionType = Field(
60
+ default="auto",
61
+ description="Numerical precision: 'auto', 'fp32', 'fp16', or 'bf16'.",
62
+ )
63
+
64
+ # Activation checkpointing
65
+ activation_checkpointing: Union[bool, Literal["auto"]] = Field(
66
+ default="auto",
67
+ description="Activation checkpointing toggle: 'auto' enables it when VRAM is tight, or explicit boolean.",
68
+ )
69
+
70
+ # Profiling & reporting
71
+ enable_profiling: bool = Field(
72
+ default=True,
73
+ description="Whether to monitor and profile memory and step timings.",
74
+ )
75
+ report_output_dir: Optional[str] = Field(
76
+ default="./memora_reports",
77
+ description="Directory to persist optimization reports.",
78
+ )
79
+ seed: int = Field(
80
+ default=42,
81
+ description="Random seed for deterministic runs.",
82
+ )
83
+
84
+ @field_validator("batch_size")
85
+ @classmethod
86
+ def validate_batch_size(cls, v: Any) -> Any:
87
+ if isinstance(v, str):
88
+ if v.lower() != "auto":
89
+ raise ValueError("batch_size string value must be 'auto'")
90
+ return "auto"
91
+ if isinstance(v, int):
92
+ if v < 1:
93
+ raise ValueError("batch_size must be >= 1")
94
+ return v
95
+ raise ValueError(f"Invalid batch_size type: {type(v)}")
96
+
97
+ @field_validator("gradient_accumulation_steps")
98
+ @classmethod
99
+ def validate_grad_accum(cls, v: Any) -> Any:
100
+ if isinstance(v, str):
101
+ if v.lower() != "auto":
102
+ raise ValueError("gradient_accumulation_steps string must be 'auto'")
103
+ return "auto"
104
+ if isinstance(v, int):
105
+ if v < 1:
106
+ raise ValueError("gradient_accumulation_steps must be >= 1")
107
+ return v
108
+ raise ValueError(f"Invalid gradient_accumulation_steps type: {type(v)}")
109
+
110
+ def to_dict(self) -> Dict[str, Any]:
111
+ """Convert config to dictionary."""
112
+ return self.model_dump()
113
+
114
+
115
+ class OptimizationRecommendation(BaseModel):
116
+ """Structured recommendation produced by the optimization engine."""
117
+
118
+ recommended_batch_size: int
119
+ recommended_precision: Literal["fp32", "fp16", "bf16"]
120
+ recommended_grad_accum_steps: int
121
+ recommended_checkpointing: bool
122
+ estimated_peak_memory_bytes: int
123
+ memory_limit_bytes: int
124
+ reasons: Dict[str, str] = Field(default_factory=dict)
125
+ probe_results: Dict[str, Any] = Field(default_factory=dict)
126
+