scipopt / russcip

Rust interface for SCIP
https://crates.io/crates/russcip
Apache License 2.0
30 stars 10 forks source link

Any plan to add SCIPcreateExprPow ? #140

Open youngzhaozju opened 2 months ago

youngzhaozju commented 2 months ago

Hey, I want to use the function of "SCIPcreateExprPow". It is not available currently.

Therefore, I want to add it by my self.

pub(crate) fn create_pow_expr( &mut self, base: &Variable, exponent: f64, ) -> Result<Expr, Retcode> { let mut expr_ptr = MaybeUninit::uninit(); scip_call! { ffi::SCIPcreateExprPow( self.raw, expr_ptr.as_mutptr(), base.raw as *mut , exponent, None, std::ptr::null_mut(), ) };

    let expr_ptr = unsafe { expr_ptr.assume_init() };
    Ok(Expr { raw: expr_ptr })
}

pub fn add_pow_expr(&mut self, base: &Variable, exponent: f64) -> Rc { let exp = self .scip .create_pow_expr(base, exponent) .expect("Failed to create express power in state ProblemCreated"); let exp_rc = Rc::new(exp); return exp_rc; }

Here is an example to utilize it,

// Create model let mut model = Model::new() .hide_output() .include_default_plugins() .create_prob("test") .set_obj_sense(ObjSense::Maximize);

  // Add variables
  let x1 = model.add_var(0., f64::INFINITY, 3., "x1", VarType::Integer);
  let xx = model.add_pow_expr(&x1, 2.0);

  model.add_cons(
    vec![x1.clone(), **xx.clone()**],
    &[2., 1.],
    -f64::INFINITY,
    100.,
    "c1",
);

I found that I cannot utilize it in model.add_cons . Is any suggestion?

Thank you! Young

youngzhaozju commented 2 months ago

I solved it. Thanks!

mmghannam commented 2 months ago

@youngzhaozju Great! would you mind sharing how? and/or the code? to help others with the same issue and to help me understand what is needed to be added to the interface.

youngzhaozju commented 2 months ago

I found the most important interface for me is SCIPparseExpr. It allows to add complex expression. It helps me a lot to make my codes simple. Using the current version 0.3.4, I have to write a lot of codes to make constraints. Here are the coding for your consideration:

in scrip.rs, add following:

 fn add_Expression_by_txt(
        &mut self,
        content:&str
    )->Expr{
        let exp_rc = self.scip.crate_Expr_by_parse_string(content).expect(format!("Failed to prase txt in state ProblemCreated:{}", content).as_str());
        return exp_rc;
    }

 pub(crate) fn crate_Expr_by_parse_string(&mut self, content: &str) -> Result<Expr, Retcode> {
        let c_content = CString::new(content).expect("Failed to convert string to CString");
        let mut scip_Expr = MaybeUninit::uninit();

        let mut final_pos = ptr::null();

        scip_call! { ffi::SCIPparseExpr(
            self.raw,
            scip_Expr.as_mut_ptr(),
            c_content.as_ptr(),
            &mut final_pos,
            None,
            std::ptr::null_mut(),
        ) };

        let scip_Expr = unsafe { scip_Expr.assume_init() };

        Ok(Expr { raw: scip_Expr })
    }

Here is another important interface for me. it allows to use the above expression to make constraint directly:

            fn add_basic_nonlinear_cons(
                &mut self,
                express: Expr,
                lhs: f64,
                rhs: f64,
                name: &str,
            ) -> Rc<Constraint> {
                let cons = self
                    .scip.create_cons_basic_nonlinear(express.raw,lhs,rhs,name).expect("Failed to create basic nonlinear constraint in state ProblemCreated");
                let cons = Rc::new(cons);
                self.state.conss.borrow_mut().push(cons.clone());
                cons
            }

 fn add_Expression_text_to_eq_zero(
            &mut self,
            content:&str,
            name:&str,
        ) {
            let exp_rc = self.scip.crate_Expr_by_parse_string(content).expect(format!("Failed to prase txt in state ProblemCreated:{}", content).as_str());
             self
            .scip.create_cons_basic_nonlinear(exp_rc.raw,0.0,0.0,name).expect("Failed to create basic nonlinear constraint in state ProblemCreated");

        }

Here is an example:

 // Create model
    let mut model = Model::new()
        .hide_output()
        .include_default_plugins()
        .create_prob("test")
        .set_obj_sense(ObjSense::Minimize);

    // Add variables
    let x1 = model.add_var(-10., 4.0, 1., "x1", VarType::Continuous);

    let x2 = model.add_var(1.0, 5.0, 1.0, "x2", VarType::Continuous);

    let mut x1_expr = model.add_expr_var(&x1);

    let x2_expr = model.add_Expression_by_txt("<x2>*<x1> +  <x1>^2.0 - <x1>*<x2>*<x2>");

    model.add_basic_nonlinear_cons(x2_expr, 1.0, 1.0, "name");

    let solved_model = model.solve();

    let status = solved_model.status();
    println!("Solved with status {:?}", status);

    let obj_val = solved_model.obj_val();
    println!("Objective value: {}", obj_val);

    let sol = solved_model.best_sol().unwrap();
    let vars = solved_model.vars();

    for var in vars {
        println!("{} = {}", &var.name(), sol.val(var));
    }

    let model = solved_model.free_transform();

    println!("end");
mmghannam commented 2 months ago

Very cool! I could happily copy this code and add it to the repo, but maybe you want to open a PR? so that your contribution is counted in the commit history?

youngzhaozju commented 2 months ago

Thanks. I do not want to open a PR since I could not make continues contributions to this project. Anyway, thanks so much for your nice work!