Skip to content

Commit b6a4260

Browse files
authored
Merge pull request #230 from scipopt/add_cons_local
Add add_cons_local, add_cons_node
2 parents cb4d418 + b323541 commit b6a4260

File tree

5 files changed

+150
-25
lines changed

5 files changed

+150
-25
lines changed

Diff for: src/branchrule.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ impl SCIPBranchRule {
114114
mod tests {
115115
use super::*;
116116
use crate::model::ModelWithProblem;
117-
use crate::prelude::branchrule;
117+
use crate::prelude::{branchrule, cons};
118118
use crate::{model::Model, status::Status, Solving};
119119

120120
struct FirstChoosingBranchingRule {
@@ -220,7 +220,23 @@ mod tests {
220220
_branchrule: SCIPBranchRule,
221221
_candidates: Vec<BranchingCandidate>,
222222
) -> BranchingResult {
223-
model.create_child();
223+
let child1 = model.create_child();
224+
let child2 = model.create_child();
225+
226+
let vars = model.vars();
227+
model.add_cons_node(
228+
&child1,
229+
&cons().eq(0.0).coef(&vars[0], 1.).coef(&vars[1], -1.),
230+
);
231+
232+
model.add_cons_node(
233+
&child2,
234+
&cons().eq(1.0).coef(&vars[0], 1.).coef(&vars[1], 1.),
235+
);
236+
237+
assert_eq!(model.node_get_n_added_conss(&child1), 1);
238+
assert_eq!(model.node_get_n_added_conss(&child2), 1);
239+
224240
BranchingResult::CustomBranching
225241
}
226242
}
@@ -237,6 +253,8 @@ mod tests {
237253

238254
let br = CustomBranchingRule;
239255
model.add(branchrule(br));
256+
model.add_var(0., 1., 1., "x", crate::variable::VarType::Binary);
257+
model.add_var(0., 1., 1., "y", crate::variable::VarType::Binary);
240258
let solved = model.solve();
241259

242260
assert!(solved.n_nodes() > 1);

Diff for: src/builder/cons.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ use crate::{
66
/// A builder for creating constraints.
77
#[derive(Debug)]
88
pub struct ConsBuilder<'a> {
9-
lhs: f64,
10-
rhs: f64,
11-
name: Option<&'a str>,
12-
coefs: Vec<(&'a Variable, f64)>,
9+
/// Left-hand side of constraint
10+
pub(crate) lhs: f64,
11+
/// Right-hand side of constraint
12+
pub(crate) rhs: f64,
13+
/// (Optional) name of constraint
14+
pub(crate) name: Option<&'a str>,
15+
/// Coefficients of constraint
16+
pub(crate) coefs: Vec<(&'a Variable, f64)>,
1317
}
1418

1519
/// Creates a new default `ConsBuilder`.

Diff for: src/model.rs

+95-17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::builder::cons::ConsBuilder;
12
use crate::builder::CanBeAddedToModel;
23
use crate::constraint::Constraint;
34
use crate::eventhdlr::Eventhdlr;
@@ -161,7 +162,7 @@ impl Model<ProblemCreated> {
161162
///
162163
/// # Returns
163164
///
164-
/// A reference-counted pointer to the new variable.
165+
/// The created `Variable`
165166
///
166167
/// # Panics
167168
///
@@ -400,7 +401,7 @@ impl Model<Solving> {
400401
///
401402
/// # Returns
402403
///
403-
/// A reference-counted pointer to the new variable.
404+
/// The created `Variable`
404405
///
405406
/// # Panics
406407
///
@@ -425,10 +426,6 @@ impl Model<Solving> {
425426
}
426427

427428
/// Returns the current node of the model.
428-
///
429-
/// # Panics
430-
///
431-
/// This method panics if not called in the `Solving` state, it should only be used from plugins implementations.
432429
pub fn focus_node(&self) -> Node {
433430
let scip_node = self.scip.focus_node().expect("Failed to get focus node");
434431
Node {
@@ -438,10 +435,6 @@ impl Model<Solving> {
438435
}
439436

440437
/// Creates a new child node of the current node and returns it.
441-
///
442-
/// # Panics
443-
///
444-
/// This method panics if not called from plugins implementations.
445438
pub fn create_child(&mut self) -> Node {
446439
let node_ptr = self
447440
.scip
@@ -466,7 +459,7 @@ impl Model<Solving> {
466459
///
467460
/// # Returns
468461
///
469-
/// This function returns a reference-counted smart pointer (`Rc`) to the created `Variable` instance.
462+
/// The created `Variable`
470463
pub fn add_priced_var(
471464
&mut self,
472465
lb: f64,
@@ -486,13 +479,98 @@ impl Model<Solving> {
486479
}
487480
}
488481

482+
/// Locally adds a constraint to the current node and its subnodes.
483+
///
484+
/// # Arguments
485+
///
486+
/// * `cons` - The constraint to add (can be built by calling the cons() function).
487+
///
488+
/// # Returns
489+
///
490+
/// The new constraint
491+
///
492+
/// # Panics
493+
///
494+
/// This method panics if the constraint cannot be created in the current state.
495+
pub fn add_cons_local(&mut self, cons: &ConsBuilder) -> Constraint {
496+
let vars: Vec<&Variable> = cons.coefs.iter().map(|(var, _)| *var).collect();
497+
let coefs: Vec<f64> = cons.coefs.iter().map(|(_, coef)| *coef).collect();
498+
499+
let cons = self
500+
.scip
501+
.create_cons(
502+
None,
503+
vars,
504+
&coefs,
505+
cons.lhs,
506+
cons.rhs,
507+
cons.name.unwrap_or(""),
508+
true,
509+
)
510+
.expect("Failed to create constraint in state Solving");
511+
Constraint {
512+
raw: cons,
513+
scip: self.scip.clone(),
514+
}
515+
}
516+
517+
/// Locally adds a constraint to a given node and its children.
518+
///
519+
/// # Arguments
520+
///
521+
/// * `node` - The node to which the constraint should be added.
522+
/// * `cons` - The constraint to add.
523+
///
524+
/// # Returns
525+
///
526+
/// The created `Constraint`.
527+
///
528+
/// # Panics
529+
///
530+
/// This method panics if the constraint cannot be created in the current state.
531+
pub fn add_cons_node(&mut self, node: &Node, cons: &ConsBuilder) -> Constraint {
532+
let vars: Vec<&Variable> = cons.coefs.iter().map(|(var, _)| *var).collect();
533+
let coefs: Vec<f64> = cons.coefs.iter().map(|(_, coef)| *coef).collect();
534+
535+
let cons = self
536+
.scip
537+
.create_cons(
538+
Some(node),
539+
vars,
540+
&coefs,
541+
cons.lhs,
542+
cons.rhs,
543+
cons.name.unwrap_or(""),
544+
true,
545+
)
546+
.expect("Failed to create constraint in state ProblemCreated");
547+
548+
Constraint {
549+
raw: cons,
550+
scip: self.scip.clone(),
551+
}
552+
}
553+
554+
/// Returns the number of added constraints to the given nodes
555+
///
556+
/// # Arguments
557+
///
558+
/// * `node` - The node to which the constraints were added.
559+
///
560+
/// # Returns
561+
///
562+
/// The number of added constraints.
563+
pub fn node_get_n_added_conss(&mut self, node: &Node) -> usize {
564+
self.scip.node_get_n_added_conss(node)
565+
}
566+
489567
/// Gets the variable in current problem given its index (in the problem).
490568
///
491569
/// # Arguments
492570
/// * `var_prob_id` - The index of the variable in the problem.
493571
///
494572
/// # Returns
495-
/// A reference-counted pointer to the variable.
573+
/// The `Variable` if it exists, otherwise `None`.
496574
pub fn var_in_prob(&self, var_prob_id: usize) -> Option<Variable> {
497575
unsafe {
498576
ScipPtr::var_from_id(self.scip.raw, var_prob_id).map(|v| Variable {
@@ -1023,7 +1101,7 @@ impl<S: ModelStageProblemOrSolving> ProblemOrSolving for Model<S> {
10231101
assert_eq!(vars.len(), coefs.len());
10241102
let cons = self
10251103
.scip
1026-
.create_cons(vars, coefs, lhs, rhs, name)
1104+
.create_cons(None, vars, coefs, lhs, rhs, name, false)
10271105
.expect("Failed to create constraint in state ProblemCreated");
10281106

10291107
Constraint {
@@ -1068,7 +1146,7 @@ impl<S: ModelStageProblemOrSolving> ProblemOrSolving for Model<S> {
10681146
///
10691147
/// # Returns
10701148
///
1071-
/// A reference-counted pointer to the new constraint.
1149+
/// The new `Constraint`.
10721150
///
10731151
/// # Panics
10741152
///
@@ -1095,7 +1173,7 @@ impl<S: ModelStageProblemOrSolving> ProblemOrSolving for Model<S> {
10951173
///
10961174
/// # Returns
10971175
///
1098-
/// A reference-counted pointer to the new constraint.
1176+
/// The created `Constraint`
10991177
///
11001178
/// # Panics
11011179
///
@@ -1123,7 +1201,7 @@ impl<S: ModelStageProblemOrSolving> ProblemOrSolving for Model<S> {
11231201
///
11241202
/// # Returns
11251203
///
1126-
/// A reference-counted pointer to the new constraint.
1204+
/// The created `Constraint`
11271205
///
11281206
/// # Panics
11291207
///
@@ -1157,7 +1235,7 @@ impl<S: ModelStageProblemOrSolving> ProblemOrSolving for Model<S> {
11571235
///
11581236
/// # Returns
11591237
///
1160-
/// A reference-counted pointer to the new constraint.
1238+
/// The created `Constraint`
11611239
///
11621240
/// # Panics
11631241
///

Diff for: src/scip.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::branchrule::{BranchRule, BranchingCandidate};
2+
use crate::node::Node;
23
use crate::pricer::{Pricer, PricerResultState};
34
use crate::{
45
ffi, scip_call_panic, BranchingResult, Conshdlr, Constraint, Event, Eventhdlr, HeurResult,
@@ -353,11 +354,13 @@ impl ScipPtr {
353354

354355
pub(crate) fn create_cons(
355356
&self,
357+
node: Option<&Node>,
356358
vars: Vec<&Variable>,
357359
coefs: &[f64],
358360
lhs: f64,
359361
rhs: f64,
360362
name: &str,
363+
local: bool,
361364
) -> Result<*mut SCIP_Cons, Retcode> {
362365
assert_eq!(vars.len(), coefs.len());
363366
let c_name = CString::new(name).unwrap();
@@ -376,7 +379,18 @@ impl ScipPtr {
376379
for (i, var) in vars.iter().enumerate() {
377380
scip_call! { ffi::SCIPaddCoefLinear(self.raw, scip_cons, var.raw, coefs[i]) };
378381
}
379-
scip_call! { ffi::SCIPaddCons(self.raw, scip_cons) };
382+
if local {
383+
if node.is_none() {
384+
// adding to current node
385+
scip_call! { ffi::SCIPaddConsLocal(self.raw, scip_cons, std::ptr::null_mut()) };
386+
} else {
387+
// adding to given node
388+
scip_call! { ffi::SCIPaddConsNode(self.raw, node.unwrap().raw, scip_cons, std::ptr::null_mut()) };
389+
}
390+
} else {
391+
scip_call! { ffi::SCIPaddCons(self.raw, scip_cons) };
392+
}
393+
380394
let stage = unsafe { ffi::SCIPgetStage(self.raw) };
381395
if stage == ffi::SCIP_Stage_SCIP_STAGE_SOLVING {
382396
scip_call! { ffi::SCIPreleaseCons(self.raw, &mut scip_cons) };
@@ -535,6 +549,11 @@ impl ScipPtr {
535549
Ok(scip_cons)
536550
}
537551

552+
/// Get number of constraints added in node
553+
pub(crate) fn node_get_n_added_conss(&self, node: &Node) -> usize {
554+
unsafe { ffi::SCIPnodeGetNAddedConss(node.raw) as usize }
555+
}
556+
538557
pub(crate) unsafe fn var_from_id(scip: *mut Scip, var_prob_id: usize) -> Option<*mut SCIP_Var> {
539558
let n_vars = ffi::SCIPgetNVars(scip) as usize;
540559
let var = *ffi::SCIPgetVars(scip).add(var_prob_id);
@@ -544,6 +563,8 @@ impl ScipPtr {
544563
Some(var)
545564
}
546565
}
566+
567+
/// Create indicator constraint
547568
pub(crate) fn create_cons_indicator(
548569
&self,
549570
bin_var: &Variable,

Diff for: src/separator.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,14 @@ mod tests {
286286
let mut row = sepa
287287
.create_empty_row(&model, "test", 5.0, 5.0, true, false, false)
288288
.unwrap();
289-
for var in model.vars() {
289+
let vars = model.vars();
290+
for var in vars.clone() {
290291
row.set_coeff(&var, 1.0);
291292
}
292293
model.add_cut(row, true);
294+
let n_conss_before = model.n_conss();
295+
model.add_cons_local(&cons().ge(7.0).coef(&(vars[0]), 2.).coef(&(vars[1]), 1.));
296+
assert_eq!(model.n_conss(), n_conss_before + 1);
293297

294298
SeparationResult::Separated
295299
}

0 commit comments

Comments
 (0)