Skip to content

Commit f522da7

Browse files
committed
feat(compiler): add error handling for invalid rounding
1 parent 4f33878 commit f522da7

File tree

3 files changed

+44
-7
lines changed

3 files changed

+44
-7
lines changed

core/compiler/src/compile.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,18 @@ impl<'a> ExecPass<'a> {
22692269
kind: ExecErrorKind::InconsistentConstraint(constraint),
22702270
});
22712271
}
2272+
for var in self
2273+
.cell_state_mut(cell_id)
2274+
.solver
2275+
.invalid_rounding()
2276+
.clone()
2277+
{
2278+
self.errors.push(ExecError {
2279+
span: None,
2280+
cell: cell_id,
2281+
kind: ExecErrorKind::InvalidRounding(var),
2282+
});
2283+
}
22722284

22732285
self.partial_cells
22742286
.pop_back()
@@ -4217,6 +4229,9 @@ pub enum ExecErrorKind {
42174229
/// Inconsistent constraint.
42184230
#[error("inconsistent constraint")]
42194231
InconsistentConstraint(ConstraintId),
4232+
/// Invalid rounding (e.g. solved value is not sufficiently close to a rounding step).
4233+
#[error("invalid rounding")]
4234+
InvalidRounding(Var),
42204235
/// A cell or instance had an empty bounding box.
42214236
#[error("empty bbox")]
42224237
EmptyBbox,

core/compiler/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ mod tests {
1818
};
1919
use approx::assert_relative_eq;
2020
use const_format::concatcp;
21+
use gds21::GdsUnits;
2122

22-
use crate::compile::{compile, CellArg, CompileInput};
23+
use crate::compile::{CellArg, CompileInput, compile};
2324
const EPSILON: f64 = 1e-10;
2425

2526
const EXAMPLES_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../examples");
@@ -439,6 +440,7 @@ mod tests {
439440
cells
440441
.to_gds(
441442
GdsMap::from_lyp(SKY130_LYP).expect("failed to create GDS map"),
443+
GdsUnits::new(1e-3, 1e-9),
442444
work_dir.join("layout.gds"),
443445
)
444446
.expect("Failed to write to GDS");
@@ -506,7 +508,7 @@ mod tests {
506508
assert_eq!(cells.errors.len(), 1);
507509
assert!(matches!(
508510
cells.errors.first().unwrap().kind,
509-
ExecErrorKind::InconsistentConstraint(_)
511+
ExecErrorKind::InvalidRounding(_)
510512
));
511513
}
512514

@@ -681,6 +683,7 @@ mod tests {
681683
cells
682684
.to_gds(
683685
GdsMap::from_lyp(SKY130_LYP).expect("failed to create GDS map"),
686+
GdsUnits::new(1e-3, 1e-9),
684687
work_dir.join("layout.gds"),
685688
)
686689
.expect("Failed to write to GDS");

core/compiler/src/solver.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use approx::relative_eq;
1+
use approx::{relative_eq, relative_ne};
22
use indexmap::{IndexMap, IndexSet};
33
use itertools::{Either, Itertools};
44
use nalgebra::{DMatrix, DVector};
@@ -19,6 +19,7 @@ pub struct Solver {
1919
var_to_constraints: IndexMap<Var, IndexSet<ConstraintId>>,
2020
solved_vars: IndexMap<Var, f64>,
2121
inconsistent_constraints: IndexSet<ConstraintId>,
22+
invalid_rounding: IndexSet<Var>,
2223
}
2324

2425
fn round(x: f64) -> f64 {
@@ -57,6 +58,11 @@ impl Solver {
5758
&self.inconsistent_constraints
5859
}
5960

61+
#[inline]
62+
pub fn invalid_rounding(&self) -> &IndexSet<Var> {
63+
&self.invalid_rounding
64+
}
65+
6066
pub fn unsolved_vars(&self) -> IndexSet<Var> {
6167
IndexSet::from_iter((0..self.next_var).map(Var).filter(|&v| !self.is_solved(v)))
6268
}
@@ -95,11 +101,15 @@ impl Solver {
95101
let (coeff, var) = constraint.coeffs[0];
96102
let val = -constraint.constant / coeff;
97103
if let Some(old_val) = self.solved_vars.get(&var) {
98-
if !relative_eq!(*old_val, val, epsilon = EPSILON) {
104+
if relative_ne!(*old_val, val, epsilon = EPSILON) {
99105
self.inconsistent_constraints.insert(constraint_id);
100106
}
101107
} else {
102-
self.solved_vars.insert(var, val);
108+
let rounded_val = round(val);
109+
if relative_ne!(val, rounded_val, epsilon = EPSILON) {
110+
self.invalid_rounding.insert(var);
111+
}
112+
self.solved_vars.insert(var, rounded_val);
103113
}
104114
self.constraints.swap_remove(&constraint_id);
105115
for constraint in self
@@ -146,8 +156,7 @@ impl Solver {
146156
if !self.solved_vars.contains_key(&Var(i))
147157
&& relative_eq!(recons, 1., epsilon = EPSILON)
148158
{
149-
let val = round(sol[(i as usize, 0)]);
150-
self.solved_vars.insert(Var(i), val);
159+
self.solved_vars.insert(Var(i), sol[(i as usize, 0)]);
151160
}
152161
}
153162
for (id, constraint) in self.constraints.iter_mut() {
@@ -158,6 +167,16 @@ impl Solver {
158167
self.inconsistent_constraints.insert(*id);
159168
}
160169
}
170+
for i in 0..self.next_var {
171+
if let Some(val) = self.solved_vars.get_mut(&Var(i)) {
172+
let rounded_val = round(*val);
173+
if relative_ne!(*val, rounded_val, epsilon = EPSILON) {
174+
self.invalid_rounding.insert(Var(i));
175+
} else {
176+
*val = rounded_val;
177+
}
178+
}
179+
}
161180
self.constraints
162181
.retain(|_, constraint| !constraint.coeffs.is_empty());
163182
}

0 commit comments

Comments
 (0)