Skip to content

Commit

Permalink
Getting cli compile working again
Browse files Browse the repository at this point in the history
  • Loading branch information
ciaranra committed Jan 23, 2025
1 parent 61afcba commit a37d959
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 134 deletions.
28 changes: 22 additions & 6 deletions crates/pecos-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use pecos_engines::{
engines::{classical::QirClassicalEngine, quantum::QuantumSimulator, HybridEngine},
};
use std::error::Error;
use std::path::{Path, PathBuf};

fn main() -> Result<(), Box<dyn Error>> {
env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();

let matches = Command::new("qir-runner")
.version("1.0")
Expand Down Expand Up @@ -45,23 +46,38 @@ fn main() -> Result<(), Box<dyn Error>> {
)
.get_matches();

// Get path to the input program
let program = matches.get_one::<String>("program").unwrap();
let program_path = if program.ends_with(".ll") {
program.to_string()
} else {
format!("{}.ll", program)
format!("{program}.ll")
};

let mode = matches.get_one::<String>("mode").unwrap();
let total_shots = *matches.get_one::<usize>("shots").unwrap();
let num_workers = *matches.get_one::<usize>("workers").unwrap();

// Create absolute paths
let current_dir = std::env::current_dir()?;
let build_dir = current_dir.join("build");
let program_path = current_dir.join(program_path);
debug!("Current directory: {}", current_dir.display());

// Convert relative path to absolute
let program_path = if Path::new(&program_path).is_absolute() {
PathBuf::from(&program_path)
} else {
current_dir.join(&program_path)
};

// Make sure program path exists
if !program_path.exists() {
return Err(format!("Program file not found: {}", program_path.display()).into());
}

let program_path = program_path.canonicalize()?;
debug!("Program path: {}", program_path.display());

// Create build directory next to the input file
let build_dir = program_path.parent().unwrap().join("build");
debug!("Build directory: {}", build_dir.display());

// Create build directory
Expand Down Expand Up @@ -99,7 +115,7 @@ fn main() -> Result<(), Box<dyn Error>> {
engine.print_statistics(&stats);
}
_ => {
println!("Unknown mode: {}", mode);
println!("Unknown mode: {mode}");
println!("Usage: qir-runner <compile|run> <program> [-s <shots>] [-w <workers>]");
std::process::exit(1);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/pecos-engines/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ keywords.workspace = true
categories.workspace = true

[lib]
crate-type = ["staticlib", "rlib"]
crate-type = ["cdylib", "staticlib", "rlib"]

[dependencies]
rand = "0.8"
Expand Down
13 changes: 7 additions & 6 deletions crates/pecos-engines/src/channels/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct StdioChannel {
}

impl StdioChannel {
#[must_use]
pub fn new(
reader: Box<dyn BufRead + Send + Sync>,
writer: Box<dyn Write + Send + Sync>,
Expand Down Expand Up @@ -45,14 +46,14 @@ impl CommandChannel for StdioChannel {
let mut writer = self
.writer
.lock()
.map_err(|e| QueueError::LockError(format!("Failed to lock writer: {}", e)))?;
.map_err(|e| QueueError::LockError(format!("Failed to lock writer: {e}")))?;

writeln!(*writer, "FLUSH_BEGIN")?;

for cmd in cmds {
trace!("Sending command: {:?}", cmd);
let cmd_str = format_command(&cmd)?;
writeln!(*writer, "CMD {}", cmd_str)?;
writeln!(*writer, "CMD {cmd_str}")?;
}

writeln!(*writer, "FLUSH_END")?;
Expand All @@ -64,7 +65,7 @@ impl CommandChannel for StdioChannel {
let mut writer = self
.writer
.lock()
.map_err(|e| QueueError::LockError(format!("Failed to lock writer: {}", e)))?;
.map_err(|e| QueueError::LockError(format!("Failed to lock writer: {e}")))?;
writer.flush()?;
Ok(())
}
Expand All @@ -75,23 +76,23 @@ impl MeasurementChannel for StdioChannel {
let mut reader = self
.reader
.lock()
.map_err(|e| QueueError::LockError(format!("Failed to lock reader: {}", e)))?;
.map_err(|e| QueueError::LockError(format!("Failed to lock reader: {e}")))?;

let mut line = String::new();
reader.read_line(&mut line)?;

let measurement = line
.trim()
.parse()
.map_err(|e| QueueError::OperationError(format!("Invalid measurement: {}", e)))?;
.map_err(|e| QueueError::OperationError(format!("Invalid measurement: {e}")))?;

trace!("Received measurement: {}", measurement);
Ok(measurement)
}
}

pub(crate) fn format_command(cmd: &QuantumCommand) -> Result<String, QueueError> {
use crate::types::GateType::*;
use crate::types::GateType::{Measure, RXY, RZ, ZZ};

match &cmd.gate {
RZ { theta } => Ok(format!("RZ {} {}", theta, cmd.qubits[0])),
Expand Down
159 changes: 67 additions & 92 deletions crates/pecos-engines/src/engines/classical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct QirClassicalEngine {
}

impl QirClassicalEngine {
#[must_use]
pub fn new(program_path: &Path, build_dir: &Path) -> Self {
let program_name = program_path
.file_stem()
Expand All @@ -36,27 +37,44 @@ impl QirClassicalEngine {
}
}

fn find_library_dir() -> Result<PathBuf, Box<dyn std::error::Error>> {
let exe_path = std::env::current_exe()?;
let exe_dir = exe_path.parent().ok_or("Cannot get executable directory")?;

// Development case
if let Some(target_dir) = exe_dir.parent() {
let lib = target_dir.join("debug/libpecos_engines.so");
if lib.exists() {
return Ok(target_dir.join("debug"));
}
}

// Installation case
if exe_dir.ends_with("bin") {
let lib_dir = exe_dir.parent().unwrap().join("lib");
return Ok(lib_dir);
}

Err("Could not find libpecos_engines.so".into())
}

pub fn compile(&self) -> Result<(), Box<dyn std::error::Error>> {
fs::create_dir_all(&self.build_dir)?;

let bc_path = self.build_dir.join(format!("{}.bc", self.program_name));
let obj_path = self.build_dir.join(format!("{}.o", self.program_name));
let exe_path = self.build_dir.join(&self.program_name);

// Ensure input path has .ll extension
let input_path = if self.program_path.extension().unwrap_or_default() == "ll" {
self.program_path.clone()
} else {
self.program_path.with_extension("ll")
};

debug!("Input file: {}", input_path.display());
debug!("Input file: {}", self.program_path.display());
debug!("Object file: {}", obj_path.display());
debug!("Executable: {}", exe_path.display());

let lib_dir = Self::find_library_dir()?;
debug!("Library directory: {}", lib_dir.display());

info!("Converting LLVM IR to bitcode...");
let status = Command::new("llvm-as-13")
.arg(&input_path)
.arg(&self.program_path)
.arg("-o")
.arg(&bc_path)
.status()?;
Expand All @@ -65,7 +83,6 @@ impl QirClassicalEngine {
return Err("Failed to convert LLVM IR to bitcode".into());
}

// Compile bitcode to object file
info!("Compiling to native code...");
let status = Command::new(&self.llc_path)
.arg(&bc_path)
Expand All @@ -78,22 +95,25 @@ impl QirClassicalEngine {
return Err("LLC compilation failed".into());
}

// Link with runtime
info!("Linking with runtime...");
let current_dir = std::env::current_dir()?;
let lib_path = current_dir.join("target/debug");

debug!("Library path: {}", lib_path.display());
let status = Command::new(&self.clang_path)
let output = Command::new(&self.clang_path)
.arg("-v")
.arg("-o")
.arg(&exe_path)
.arg(&obj_path)
.arg(format!("-L{}", lib_path.display()))
.arg("-lqir_black_box")
.status()?;

if !status.success() {
return Err("Linking failed".into());
.arg(format!("-L{}", lib_dir.display()))
.arg("-Wl,-rpath")
.arg(lib_dir)
.arg("-lpecos_engines")
.output()?;

if !output.status.success() {
return Err(format!(
"Linking failed:\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)
.into());
}

info!("Compilation successful: {}", exe_path.display());
Expand Down Expand Up @@ -122,7 +142,7 @@ impl ClassicalEngine for QirClassicalEngine {
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()
.map_err(|e| QueueError::ExecutionError(format!("Failed to spawn process: {}", e)))?;
.map_err(|e| QueueError::ExecutionError(format!("Failed to spawn process: {e}")))?;

let stdout = child
.stdout
Expand All @@ -139,32 +159,31 @@ impl ClassicalEngine for QirClassicalEngine {
break;
}

match line.trim() {
"FLUSH_BEGIN" => {
debug!("Processing commands block");
loop {
line.clear();
let len = reader.read_line(&mut line)?;
if len == 0 {
break;
}
if line.trim() == "FLUSH_BEGIN" {
debug!("Processing commands block");
loop {
line.clear();
let len = reader.read_line(&mut line)?;
if len == 0 {
break;
}

let trimmed = line.trim();
if trimmed == "FLUSH_END" {
debug!("End of commands block");
break;
}
let trimmed = line.trim();
if trimmed == "FLUSH_END" {
debug!("End of commands block");
break;
}

if let Some(cmd) = trimmed.strip_prefix("CMD ") {
debug!("Received command: {}", cmd);
if let Ok(quantum_cmd) = QuantumCommand::parse_from_str(cmd) {
commands.push(quantum_cmd);
}
if let Some(cmd) = trimmed.strip_prefix("CMD ") {
debug!("Received command: {}", cmd);
if let Ok(quantum_cmd) = QuantumCommand::parse_from_str(cmd) {
commands.push(quantum_cmd);
}
}
break;
}
_ => debug!("Skipping line: {}", line.trim()),
break;
} else {
debug!("Skipping line: {}", line.trim());
}
line.clear();
}
Expand All @@ -184,13 +203,13 @@ impl ClassicalEngine for QirClassicalEngine {
// Store measurement with result_id that matches the QIR program
self.current_results
.measurements
.insert(format!("measurement_{}", qubit_idx), measurement);
.insert(format!("measurement_{qubit_idx}"), measurement);

// Try to send back to process if still alive
if let Some(child) = &mut self.child_process {
if let Some(mut stdin) = child.stdin.take() {
match writeln!(stdin, "{}", measurement) {
Ok(_) => {
match writeln!(stdin, "{measurement}") {
Ok(()) => {
debug!(
"Successfully sent measurement {} to classical process",
measurement
Expand All @@ -212,51 +231,7 @@ impl ClassicalEngine for QirClassicalEngine {
}

fn compile(&self) -> Result<(), Box<dyn std::error::Error>> {
fs::create_dir_all(&self.build_dir)?;

let bc_path = self.build_dir.join(format!("{}.bc", self.program_name));
let obj_path = self.build_dir.join(format!("{}.o", self.program_name));
let exe_path = self.build_dir.join(&self.program_name);

info!("Converting LLVM IR to bitcode...");
let status = Command::new("llvm-as-13")
.arg(&self.program_path)
.arg("-o")
.arg(&bc_path)
.status()?;

if !status.success() {
return Err("Failed to convert LLVM IR to bitcode".into());
}

// Compile bitcode to object file
info!("Compiling to native code...");
let status = Command::new(&self.llc_path)
.arg(&bc_path)
.arg("-filetype=obj")
.arg("-o")
.arg(&obj_path)
.status()?;

if !status.success() {
return Err("LLC compilation failed".into());
}

// Link with runtime
let status = Command::new(&self.clang_path)
.arg("-o")
.arg(&exe_path)
.arg(&obj_path)
.arg("-L./target/debug/")
.arg("-lqir_black_box")
.status()?;

if !status.success() {
return Err("Linking failed".into());
}

info!("Compilation successful");
Ok(())
self.compile()
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/pecos-engines/src/engines/hybrid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ where
};
shot_result
.measurements
.insert(format!("measurement_{}", res_id), measurement);
.insert(format!("measurement_{res_id}"), measurement);
}
}
}
Expand Down Expand Up @@ -124,7 +124,7 @@ where

for key in keys {
let qstats = &stats.per_qubit_stats[key];
println!("\n{}:", key);
println!("\n{key}:");
println!(" Total measurements: {}", qstats.total_measurements());
println!(
" |0⟩: {} ({:.1}%)",
Expand Down
1 change: 1 addition & 0 deletions crates/pecos-engines/src/engines/quantum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ impl Default for QuantumSimulator {
}

impl QuantumSimulator {
#[must_use]
pub fn new() -> Self {
Self
}
Expand Down
Loading

0 comments on commit a37d959

Please sign in to comment.