sc_neurocore_engine/bindings/
optimizer.rs1use pyo3::prelude::*;
12use pyo3::types::PyDict;
13
14pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
16 module.add_function(wrap_pyfunction!(simulated_annealing_search, module)?)?;
17 module.add_function(wrap_pyfunction!(extract_pareto_frontier, module)?)?;
18 Ok(())
19}
20
21#[pyfunction(name = "py_opt_sa_search")]
23#[pyo3(signature = (mac_counts, weights, max_luts, max_power, max_latency=0, t_init=1.0, t_min=0.001, alpha=0.95, max_iter=2000, seed=42))]
24fn simulated_annealing_search<'py>(
25 py: Python<'py>,
26 mac_counts: Vec<i64>,
27 weights: Vec<f64>,
28 max_luts: i64,
29 max_power: f64,
30 max_latency: i64,
31 t_init: f64,
32 t_min: f64,
33 alpha: f64,
34 max_iter: usize,
35 seed: u64,
36) -> PyResult<Py<PyAny>> {
37 let candidates: Vec<Vec<crate::optimizer::Candidate>> = mac_counts
38 .iter()
39 .map(|&mac_count| crate::optimizer::generate_candidates(mac_count))
40 .collect();
41
42 let result = crate::optimizer::simulated_annealing(
43 &candidates,
44 &weights,
45 max_luts,
46 max_power,
47 max_latency,
48 t_init,
49 t_min,
50 alpha,
51 max_iter,
52 seed,
53 );
54
55 let dictionary = PyDict::new(py);
56 match result {
57 Some(result) => {
58 let mut layer_luts = Vec::new();
59 let mut layer_power = Vec::new();
60 let mut layer_accuracy = Vec::new();
61 for (layer_index, &candidate_index) in result.best_config.iter().enumerate() {
62 let candidate = &candidates[layer_index][candidate_index];
63 layer_luts.push(candidate.luts);
64 layer_power.push(candidate.power);
65 layer_accuracy.push(candidate.accuracy);
66 }
67
68 dictionary.set_item("best_config", result.best_config)?;
69 dictionary.set_item("best_score", result.best_score)?;
70 dictionary.set_item("pareto_luts", result.pareto_luts)?;
71 dictionary.set_item("pareto_power", result.pareto_power)?;
72 dictionary.set_item("pareto_score", result.pareto_score)?;
73 dictionary.set_item("feasible", true)?;
74 dictionary.set_item("layer_luts", layer_luts)?;
75 dictionary.set_item("layer_power", layer_power)?;
76 dictionary.set_item("layer_accuracy", layer_accuracy)?;
77 }
78 None => {
79 dictionary.set_item("feasible", false)?;
80 }
81 }
82 dictionary.set_item("backend", "rust")?;
83 Ok(dictionary.into_any().unbind())
84}
85
86#[pyfunction(name = "py_opt_extract_pareto")]
88fn extract_pareto_frontier<'py>(
89 py: Python<'py>,
90 luts: Vec<i64>,
91 power: Vec<f64>,
92 score: Vec<f64>,
93) -> PyResult<Py<PyAny>> {
94 let indices = crate::optimizer::extract_pareto(&luts, &power, &score);
95 let dictionary = PyDict::new(py);
96 let pareto_luts: Vec<i64> = indices.iter().map(|&index| luts[index]).collect();
97 let pareto_power: Vec<f64> = indices.iter().map(|&index| power[index]).collect();
98 let pareto_score: Vec<f64> = indices.iter().map(|&index| score[index]).collect();
99 dictionary.set_item("indices", indices)?;
100 dictionary.set_item("luts", pareto_luts)?;
101 dictionary.set_item("power", pareto_power)?;
102 dictionary.set_item("score", pareto_score)?;
103 dictionary.set_item("backend", "rust")?;
104 Ok(dictionary.into_any().unbind())
105}