1use mosekcomodel::domain::{QuadraticCone, VectorDomainType};
5use mosekcomodel::utils::Permutation;
6use mosekcomodel::*;
7use mosekcomodel::model::{IntSolutionManager, ModelWithIntSolutionCallback, ModelWithLogCallback};
8use mosekcomodel::utils::iter::{Chunkation, ChunksByIterExt, PermuteByEx, PermuteByMutEx};
9use itertools::{iproduct, izip, Permutations};
10use std::collections::HashMap;
11use std::fs::File;
12use std::io::{BufReader,BufRead,Read};
13use std::path::Path;
14
15mod json;
17mod bio;
18mod msto;
19
20pub type Model = ModelAPI<Backend>;
21
22
23#[derive(Clone,Copy)]
24struct ConItem {
25 block_index : usize,
26 offset : usize,
27}
28
29#[derive(Clone,Copy)]
30enum VarItem {
31 Linear,
33 LinearLowerBound,
36 LinearUpperBound,
39 Conic{conidx : usize}
41}
42
43#[derive(Clone)]
44enum VecCone {
45 Zero{dim : usize},
46 NonNegative{dim : usize},
47 NonPositive{dim : usize},
48 Unbounded{dim : usize},
49 Quadratic{dim : usize},
50 RotatedQuadratic{dim : usize},
51 PrimalPower{dim : usize, alpha : Vec<f64>},
52 DualPower{dim : usize, alpha : Vec<f64>},
53 PrimalExp,
54 DualExp,
55 PrimalGeometricMean{dim : usize},
56 DualGeometricMean{dim : usize},
57 ScaledVectorizedPSD{dim : usize},
58}
59impl VecCone {
60 pub fn dim(&self) -> usize {
61 use VecCone::*;
62 match self {
63 Zero{dim} => *dim,
64 NonNegative{dim} => *dim,
65 NonPositive{dim} => *dim,
66 Unbounded{dim} => *dim,
67 Quadratic{dim} => *dim,
68 RotatedQuadratic{dim} => *dim,
69 PrimalPower{dim, ..} => *dim,
70 DualPower{dim, ..} => *dim,
71 PrimalExp => 3,
72 DualExp => 3,
73 PrimalGeometricMean{dim} => *dim,
74 DualGeometricMean{dim} => *dim,
75 ScaledVectorizedPSD{dim} => *dim,
76 }
77 }
78}
79
80#[derive(Default)]
83pub struct Backend {
84 name : Option<String>,
85
86 log_cb : Option<Box<dyn Fn(&str)>>,
88 sol_cb : Option<Box<dyn FnMut(&IntSolutionManager)>>,
90
91 var_lb : Vec<f64>,
93 var_ub : Vec<f64>,
95
96 var_int : Vec<bool>,
98 var_names : Vec<Option<String>>,
100 var_idx : Vec<usize>,
102 vars : Vec<VarItem>,
105
106 mx : msto::MatrixStore,
108
109 cons : Vec<ConItem>,
112 con_mx_row : Vec<usize>,
113 con_rhs : Vec<f64>,
114 con_block_ptr : Vec<usize>,
115 con_block_dom : Vec<VecCone>,
116 con_names : Vec<Option<String>>,
117
118 sense_max : bool,
119 c_subj : Vec<usize>,
120 c_cof : Vec<f64>,
121
122 address : Option<reqwest::Url>,
123 dpar : HashMap<String,f64>,
124 ipar : HashMap<String,i32>,
125}
126
127impl BaseModelTrait for Backend {
128 fn new(name : Option<&str>) -> Self {
129 Backend{
130 name : name.map(|v| v.to_string()),
131 vars : vec![VarItem::Linear],
132 var_idx : vec![usize::MAX],
133 ..Default::default()
134 }
135 }
136
137 fn free_variable<const N : usize>
138 (&mut self,
139 name : Option<&str>,
140 shape : &[usize;N]) -> Result<<LinearDomain<N> as VarDomainTrait<Self>>::Result, String> where Self : Sized
141 {
142 let n = shape.iter().product::<usize>();
143
144 let lb = vec![f64::NEG_INFINITY;n];
145 let ub = vec![f64::INFINITY;n];
146 let idxs = self.native_linear_variable(lb.as_slice(),ub.as_slice(),false);
147
148
149 let first = self.vars.len();
150 let last = first + n;
151
152 if let Some(name) = name {
153 self.linear_names(idxs.start,idxs.end, shape, None, name);
154 }
155
156 Ok(Variable::new((first..last).collect::<Vec<usize>>(), None, shape))
157 }
158
159 fn linear_variable<const N : usize,R>
160 (&mut self,
161 name : Option<&str>,
162 dom : LinearDomain<N>) -> Result<<LinearDomain<N> as VarDomainTrait<Self>>::Result,String>
163 where
164 Self : Sized
165 {
166 let (dt,b,sp,shape,is_integer) = dom.dissolve();
167 let n = sp.as_ref().map(|v| v.len()).unwrap_or(shape.iter().product::<usize>());
168
169 let (idxs,vit) =
170 match dt {
171 LinearDomainType::Free => { (self.native_linear_variable(vec![f64::NEG_INFINITY;n].as_slice(), vec![f64::INFINITY; n].as_slice(), is_integer),VarItem::Linear) },
172 LinearDomainType::Zero => { (self.native_linear_variable(b.as_slice(),b.as_slice(), is_integer), VarItem::Linear) },
173 LinearDomainType::NonNegative => { (self.native_linear_variable(b.as_slice(), vec![f64::INFINITY; n].as_slice(), is_integer),VarItem::LinearLowerBound) },
174 LinearDomainType::NonPositive => { (self.native_linear_variable(vec![f64::NEG_INFINITY;n].as_slice(), b.as_slice(), is_integer),VarItem::LinearUpperBound) },
175 };
176 let (first,last) = (self.var_idx.len(),self.vars.len() + n);
177
178 self.var_idx.reserve(n); for i in idxs.clone() { self.var_idx.push(i); }
179 self.vars.resize(last, vit);
180
181 if let Some(name) = name {
182 self.linear_names(idxs.start, idxs.end, &shape, sp.as_deref(), name);
183 }
184
185 Ok(Variable::new((first..last).collect::<Vec<usize>>(), sp, &shape))
186 }
187
188 fn ranged_variable<const N : usize,R>(&mut self, name : Option<&str>,dom : LinearRangeDomain<N>) -> Result<<LinearRangeDomain<N> as VarDomainTrait<Self>>::Result,String>
189 where
190 Self : Sized
191 {
192 let (shape,bl,bu,sp,is_integer) = dom.dissolve();
193 let n = sp.as_ref().map(|v| v.len()).unwrap_or(shape.iter().product::<usize>());
194
195 let idxs = self.native_linear_variable(bl.as_slice(),bu.as_slice(), is_integer);
196 let first = self.var_idx.len();
197 self.var_idx.reserve(n*2);
198 for i in idxs.clone() { self.var_idx.push(i); }
199 for i in idxs.clone() { self.var_idx.push(i); }
200 self.vars.reserve(n*2);
201 self.vars.resize(first+n, VarItem::LinearLowerBound);
202 self.vars.resize(first+n*2, VarItem::LinearUpperBound);
203
204 if let Some(name) = name {
205 self.linear_names(idxs.start, idxs.end, &shape, sp.as_deref(), name);
206 }
207
208 Ok((Variable::new((first..first+n).collect::<Vec<usize>>(), sp.clone(), &shape),
209 Variable::new((first+n..first+2*n).collect::<Vec<usize>>(), sp, &shape)))
210 }
211
212 fn linear_constraint<const N : usize>
213 (& mut self,
214 name : Option<&str>,
215 dom : LinearDomain<N>,
216 _eshape : &[usize],
217 ptr : &[usize],
218 subj : &[usize],
219 cof : &[f64]) -> Result<<LinearDomain<N> as ConstraintDomain<N,Self>>::Result,String>
220 {
221 let (dt,rhs,sp,shape,_is_integer) = dom.dissolve();
222
223 if sp.is_some() { return Err("Sparse domain on costraint not allowd".to_string()); }
224 assert_eq!(rhs.len(),ptr.len()-1);
225 assert_eq!(ptr.len(),shape.iter().product::<usize>()+1);
226 let n = rhs.len();
227
228 let first = self.con_rhs.len();
229
230 let ptrchunks = Chunkation::new(ptr).unwrap();
231 let rowidxs = izip!(ptrchunks.chunks(subj).unwrap(),
232 ptrchunks.chunks(cof).unwrap())
233 .map(|(subj,cof)| {
234 if let (Some(j),Some(c)) = (subj.first(),cof.first()) {
236 if *j == 0 {
237 self.mx.append_row(&subj[1..], &cof[..cof.len()-1], *c)
238 }
239 else {
240 self.mx.append_row(subj, cof, 0.0)
241 }
242 }
243 else {
244 self.mx.append_row(subj, cof, 0.0)
245 }
246 });
247
248 let block_index = self.con_block_ptr.len();
249 self.con_block_ptr.push(first);
250
251 match dt {
252 LinearDomainType::Free => self.con_block_dom.push(VecCone::Unbounded { dim: n }),
253 LinearDomainType::Zero => self.con_block_dom.push(VecCone::Zero { dim: n }),
254 LinearDomainType::NonNegative => self.con_block_dom.push(VecCone::NonNegative { dim: n }),
255 LinearDomainType::NonPositive => self.con_block_dom.push(VecCone::NonPositive { dim: n }),
256 }
257 self.con_mx_row.extend(rowidxs);
258 self.con_rhs.extend_from_slice(rhs.as_slice());
259
260 self.cons.extend((0..n).map(|offset| ConItem{block_index, offset}));
261
262 if let Some(name) = name {
263 let mut name_index_buf = [1usize; N];
264 for _ in 0..n {
265 name_index_buf.iter_mut().zip(shape.iter()).rev().fold(1,|c,(i,&d)| { *i += c; if *i > d { *i = 1; 1 } else { 0 } });
266 self.con_names.push(Some(format!("{}{:?}", name, name_index_buf)));
267 }
268 }
269 else {
270 self.con_names.resize(self.con_names.len()+n,None);
271 }
272
273 Ok(Constraint::new((first..first+n).collect::<Vec<usize>>(), &shape))
274 }
275
276 fn ranged_constraint<const N : usize>
277 (& mut self,
278 name : Option<&str>,
279 dom : LinearRangeDomain<N>,
280 _eshape : &[usize],
281 ptr : &[usize],
282 subj : &[usize],
283 cof : &[f64]) -> Result<<LinearRangeDomain<N> as ConstraintDomain<N,Self>>::Result,String>
284 {
285 let (shape,bl,bu,sp,_) = dom.dissolve();
286 if sp.is_some() { return Err("Sparse domain on costraint not allowd".to_string()); }
287 assert_eq!(bl.len(),ptr.len()-1);
288 assert_eq!(bu.len(),ptr.len()-1);
289 assert_eq!(ptr.len(),shape.iter().product::<usize>()+1);
290
291 let n = bl.len();
292
293 let first0 = self.con_rhs.len();
294 let last0 = first0+n;
295 let first1 = last0;
296 let last1 = first1+n*2;
297
298 let ptrchunks = Chunkation::new(ptr).unwrap();
299 let rowidxs : Vec<usize> = izip!(ptrchunks.chunks(subj).unwrap(), ptrchunks.chunks(cof).unwrap())
300 .map(|(subj,cof)| {
301 if let (Some(j),Some(c)) = (subj.first(),cof.first()) {
302 if *j == 0 {
304 self.mx.append_row(&subj[1..], &cof[..cof.len()-1], *c)
305 }
306 else {
307 self.mx.append_row(subj, cof, 0.0)
308 }
309 }
310 else {
311 self.mx.append_row(subj, cof, 0.0)
312 }
313 })
314 .collect();
315 let block_index = self.con_block_ptr.len();
316
317 self.con_block_ptr.push(self.con_rhs.len());
318 self.con_block_ptr.push(self.con_rhs.len()+n);
319
320 self.con_block_dom.push(VecCone::NonNegative { dim: n });
321 self.con_block_dom.push(VecCone::NonPositive { dim: n });
322
323 self.con_mx_row.extend_from_slice(rowidxs.as_slice());
324 self.con_mx_row.extend_from_slice(rowidxs.as_slice());
325 self.con_rhs.extend_from_slice(bl.as_slice());
326 self.con_rhs.extend_from_slice(bu.as_slice());
327
328 self.cons.extend((0..n).map(|offset| ConItem{block_index, offset} ));
329 self.cons.extend((0..n).map(|offset| ConItem{block_index : block_index+1, offset} ));
330
331 if let Some(name) = name {
332 let mut name_index_buf = [1usize; N];
333 for _ in 0..n {
334 name_index_buf.iter_mut().zip(shape.iter()).rev().fold(1,|c,(i,&d)| { *i += c; if *i > d { *i = 1; 1 } else { 0 } });
335 self.con_names.push(Some(format!("{}{:?}", name, name_index_buf)));
336 }
337 }
338 else {
339 for _ in 0..n {
340 self.con_names.push(None);
341 }
342 }
343 Ok((Constraint::new((first0..last0).collect::<Vec<usize>>(), &shape),
344 Constraint::new((first1..last1).collect::<Vec<usize>>(), &shape)))
345 }
346
347 fn update(& mut self, rowidxs : &[usize], shape : &[usize], ptr : &[usize], subj : &[usize], cof : &[f64]) -> Result<(),String>
348 {
349 if shape.iter().product::<usize>() != rowidxs.len() { return Err("Mismatching constraint and experssion sizes".to_string()); }
350
351 if let Some(&i) = rowidxs.iter().max() {
352 if i >= self.cons.len() {
353 return Err("Constraint index out of bounds".to_string());
354 }
355 }
356
357 let ptrchunker = Chunkation::new(ptr).unwrap();
358 for (i,subj,cof) in izip!(rowidxs.iter(),
359 ptrchunker.chunks(subj).unwrap(),
360 ptrchunker.chunks(cof).unwrap()) {
361 if let Some((j,c)) = subj.first().zip(cof.first()) {
362 if *j == 0 {
363 self.mx.replace_row(*i, &subj[1..], &cof[1..], *c);
364 }
365 else {
366 self.mx.replace_row(*i, subj, cof, 0.0);
367 }
368 }
369 else {
370 self.mx.replace_row(*i, subj, cof, 0.0);
371 }
372 }
373
374 Ok(())
375 }
376
377 fn write_problem<P>(&self, filename : P) -> Result<(),String> where P : AsRef<Path>
378 {
379 let p = filename.as_ref();
380 if let Some(ext) = p.extension().and_then(|ext| ext.to_str()) {
381 match ext {
382 "json"|"jtask" => {
383 self.write_jtask(&mut File::create(p).map_err(|e| e.to_string())?).map_err(|e| e.to_string())
384 },
385 _ => Err("Writing problem not supported".to_string())
386 }
387 }
388 else {
389 Err("Writing problem not supported".to_string())
390 }
391 }
392
393
394
395 fn solve(& mut self, sol_bas : & mut Solution, sol_itr : &mut Solution, sol_itg : &mut Solution) -> Result<(),String>
396 {
397 if let Some(url) = &self.address {
398 let mut url = url.clone();
399 url.set_path("/api/v1/submit+solve");
400
401 let (req_r,mut req_w) = std::io::pipe().map_err(|e| e.to_string())?;
402 let (mut resp_r,mut resp_w) = std::io::pipe().map_err(|e| e.to_string())?;
403
404 let t = std::thread::spawn(move || {
405 let client = reqwest::blocking::Client::new();
406 client.post(url)
407 .header("Content-Type", "application/x-mosek-jtask")
408 .header("Accept", "application/x-mosek-multiplex")
409 .header("X-Mosek-Callback", "values")
410 .header("X-Mosek-Stream", "log")
411 .body(reqwest::blocking::Body::new(req_r))
412 .send()
413 .map_err(|e| e.to_string())
414 });
415
416 self.write_jtask(&mut req_w).map_err(|e| e.to_string())?;
417 drop(req_w);
418
419 let mut resp = t.join().unwrap()?;
420
421 if ! resp.status().is_success() {
422 return Err(format!("OptServer responded with code {}",resp.status()));
423 }
424
425 let mut content_type = None;
426 for (k,v) in resp.headers().iter() {
427 if k == "content-type" {
428 content_type = Some(v);
429 }
430 }
431 if let Some(ct) = content_type {
432 if ct != "application/x-mosek-multiplex" {
433 return Err(format!("Unexpected response format: {:?}",ct));
434 }
435 }
436 else {
437 return Err("Unexpected response format: Missing".to_string());
438 }
439
440 let t = std::thread::spawn(move || {
441 resp.copy_to(&mut resp_w).map_err(|e| e.to_string())
442 });
443
444 let res = self.parse_multistream(&mut resp_r,sol_bas,sol_itr,sol_itg);
445 t.join().unwrap().and_then(|_| res)?;
446 }
447 else {
448 return Err("No optserver address given".to_string());
449 }
450
451 Ok(())
452
453 }
454
455 fn objective(&mut self, _name : Option<&str>, sense : Sense, subj : &[usize],cof : &[f64]) -> Result<(),String>
456 {
457 self.sense_max = match sense { Sense::Maximize => true, Sense::Minimize => false };
458 self.c_subj.resize(subj.len(),0); self.c_subj.copy_from_slice(subj);
459 self.c_cof.resize(cof.len(),0.0); self.c_cof.copy_from_slice(cof);
460 Ok(())
461 }
462}
463
464
465pub trait OptserverDomainTrait : VectorDomainTrait {
466
467}
468
469impl<D> VectorConeModelTrait<D> for Backend where D : VectorDomainTrait+'static {
470 fn conic_variable<const N : usize>(&mut self, name : Option<&str>,dom : VectorDomain<N,D>) -> Result<Variable<N>,String> {
471 let (dt,rhs,shape,conedim,is_int) = dom.dissolve();
472 self.conic_variable(name,shape,conedim,dt.to_conic_domain_type(),rhs.as_slice(),is_int)
473
474 }
475 fn conic_constraint<const N : usize>(& mut self, name : Option<&str>, dom : VectorDomain<N,D>, _shape : &[usize], ptr : &[usize], subj : &[usize], cof : &[f64]) -> Result<Constraint<N>,String> {
476 let (dt,rhs,shape,conedim,_is_int) = dom.dissolve();
477
478 self.conic_constraint(name, ptr, subj, cof, shape, conedim, dt.to_conic_domain_type(), rhs.as_slice())
479 }
480}
481
482
483impl ModelWithIntSolutionCallback for Backend {
484 fn set_solution_callback<F>(&mut self, func : F) where F : 'static+FnMut(&IntSolutionManager) {
485 self.sol_cb = Some(Box::new(func))
486 }
487}
488
489
490
491
492mod msgread {
493 use std::io::Read;
494
495 pub struct MessageReader<'a,R> where R : Read {
496 eof : bool,
497 frame_remains : usize,
498 final_frame : bool,
499 s : & 'a mut R
500
501 }
502 impl<'a,R> MessageReader<'a,R> where R : Read {
503 pub fn new(s : &'a mut R) -> MessageReader<'a,R> { MessageReader { eof: false, frame_remains: 0, s , final_frame : false}}
504 #[allow(unused)]
505 pub fn skip(&mut self) -> std::io::Result<()> {
506 let mut buf = [0;4096];
507 while 0 < self.read(&mut buf)? {}
508 Ok(())
509 }
510 }
511
512 impl<'a,R> Read for MessageReader<'a,R> where R : Read {
513 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
514 if self.eof {
516 Ok(0)
517 }
518 else {
519 if self.frame_remains == 0 {
520 if self.final_frame {
521 return Ok(0);
522 }
523 let mut buf = [0;2];
524 self.s.read_exact(&mut buf).map_err(|e| std::io::Error::other("Message stream error: Failed to read message header"))?;
526
527 self.final_frame = buf[0] > 127;
528 self.frame_remains = (((buf[0] & 0x7f) as usize) << 8) | (buf[1] as usize);
529 }
531
532 let n = buf.len().min(self.frame_remains);
533 let nr = self.s.read(&mut buf[..n])?;
535 self.frame_remains -= nr;
536 Ok(nr)
537 }
538 }
539 }
540}
541
542use msgread::*;
543pub struct SolverAddress(pub String);
544
545impl SolverParameterValue<Backend> for SolverAddress {
546 type Key = ();
547 fn set(self,_parname : Self::Key, model : & mut Backend) -> Result<(),String> {
548 model.address = Some(reqwest::Url::parse(self.0.as_str())
549 .map_err(|_| "Invalid SolverAddress value".to_string())
550 .and_then(|mut url|
551 if url.scheme() == "" {
552 url.set_scheme("http").unwrap();
553 Ok(url)
554 }
555 else if url.scheme().eq_ignore_ascii_case("http") || url.scheme().eq_ignore_ascii_case("https") {
556 Ok(url)
557 } else {
558 Err(format!("Invalid url scheme: {}",url.as_str()))
559 })?);
560 Ok(())
561 }
562}
563
564impl SolverParameterValue<Backend> for f64 {
565 type Key = &'static str;
566 fn set(self,parname : Self::Key, model : & mut Backend) -> Result<(),String> {
567 _ = model.dpar.insert(parname.to_string(), self);
568 Ok(())
569 }
570}
571impl SolverParameterValue<Backend> for i32 {
572 type Key = &'static str;
573 fn set(self,parname : Self::Key, model : & mut Backend) -> Result<(),String> {
574 _ = model.ipar.insert(parname.to_string(), self);
575 Ok(())
576 }
577}
578
579fn bnd_to_bk(lb : f64, ub : f64) -> &'static str {
580 match (lb.is_finite(),ub.is_finite()) {
581 (false,false) => "fr",
582 (false,true) => "up",
583 (true,false) => "lo",
584 (true,true) => if lb < ub { "ra" } else { "fx" }
585 }
586}
587impl Backend {
588 fn parse_multistream<R>(&mut self, r : &mut R, sol_bas : & mut Solution, sol_itr : &mut Solution, sol_itg : &mut Solution) -> Result<(),String> where R : Read {
589 let mut buf = Vec::new();
594 let mut head = String::new();
595
596 'outer: loop { head.clear();
599 {
600 let mut mr = BufReader::new(MessageReader::new(r));
601 mr.read_line(&mut head).map_err(|e| e.to_string())?;
602
603
604 loop {
605 let n = mr.read_line(&mut head).map_err(|e| e.to_string())?;
606 if n <= 1 { break; }
607 }
608 let head = head.trim_ascii_end();
611 let mut lines = head.as_bytes().chunk_by(|&a,_| a != b'\n');
612 let hd = lines.next().ok_or_else(|| "Invalid response format A".to_string())?.trim_ascii_end();
613 let headers = lines
614 .map(|s| s.trim_ascii_end())
615 .map(|s| { if let Some(p) = subseq_location(s, b":") { (&s[..p],&s[p+1..]) } else { (&s[..0],s) } });
616
617 match hd {
618 b"log" => {
619 if let Some(f) = &self.log_cb {
620 buf.clear();
621 mr.read_to_end(&mut buf).map_err(|e| e.to_string())?;
622 if let Ok(s) = std::str::from_utf8(buf.as_slice()) {
623 f(s)
624 }
625 }
626 },
627 b"msg" => {},
628 b"wrn" => {},
629 b"err" => {},
630 b"cbmap" => {},
631 b"cbinfo" => {},
632 b"cbcode" => {},
633 b"cbwarn" => {},
634 b"cb-intsol" => {
635 if self.sol_cb.is_some() {
636 let mut xxbytes = Vec::new();
637 mr.read_to_end(&mut xxbytes).map_err(|e| e.to_string())?;
638 let n = xxbytes.len()/size_of::<f64>();
639 if n == self.var_lb.len() {
640 let mut xx : Vec<f64> = Vec::new(); xx.resize(n,0.0);
641 unsafe{xx.align_to_mut().1}.copy_from_slice(&xxbytes[..n*size_of::<f64>()]);
642
643 let mut solxx = Vec::new(); solxx.resize(self.vars.len(),0.0);
644
645 for (d,v) in solxx.iter_mut().zip( xx.permute_by(self.var_idx.as_slice())) { *d = *v; }
646
647 let obj : f64 = self.c_cof.iter().zip(solxx.permute_by(self.c_subj.as_slice())).map(|(c,x)| *c * *x).sum();
648
649 if let Some(cb) = & mut self.sol_cb {
650 cb(&IntSolutionManager::new(obj,solxx));
651 }
652 }
653 }
654 },
655 b"ok" => {
656 let mut _trm = None;
657 let mut content_type = None;
658 for (k,v) in headers {
659 match k {
660 b"trm" => _trm = Some(v),
661 b"content-type" => {
662 content_type = Some(v)
663 }
664 _ => {},
665 }
666 }
667
668 match content_type {
669 Some(b"application/x-mosek-b") => break 'outer self.read_bsolution(sol_bas,sol_itr,sol_itg,&mut mr).map_err(|e| e.to_string()),
670 Some(b"application/x-mosek-b+zstd") => {
671 let mut unzstd = zstd::Decoder::new(mr).map_err(|e| e.to_string())?;
672 break 'outer self.read_bsolution(sol_bas,sol_itr,sol_itg,&mut unzstd).map_err(|e| e.to_string())
673 },
674 Some(content_type) => break 'outer Err(format!("Unexpected solution format: {}",std::str::from_utf8(content_type).unwrap_or("<?>"))),
675 None => break 'outer Err(format!("Missing solution format"))
676 }
677
678 },
679 b"fail" => {
680 let mut res = None;
681 for (k,v) in headers {
682 if k == b"res" { res = Some(v); }
683 }
684
685 buf.clear();
686 mr.read_to_end(&mut buf).map_err(|e| e.to_string())?;
687 let message = std::str::from_utf8(buf.as_slice()).unwrap_or("");
688 break 'outer Err(format!("Solve failed ({}): {}",
689 std::str::from_utf8(res.unwrap_or(b"?")).unwrap_or("?"),
690 message));
691 },
692 _ => {},
693 }
694
695 let mut flushbuf = [0u8;4096];
696 while 0 < mr.read(&mut flushbuf).map_err(|e| e.to_string())? {}
697 }
698 }
699 }
700
701 fn copy_solution(&self,
702 psta : SolutionStatus,
703 dsta : SolutionStatus,
704 numvar : usize,
705 pobj : f64,
707 dobj : f64,
708 xx : Option<Vec<f64>>,
710 sx : Option<(Vec<f64>,Vec<f64>)>,
711 sc : Option<Vec<f64>>,
714 sol : & mut Solution) -> std::io::Result<()>
715 {
716 let pdef = if let SolutionStatus::Undefined = psta { false } else { true };
717 let ddef = if let SolutionStatus::Undefined = dsta { false } else { true };
718
719 sol.primal.status = psta;
720 sol.dual.status = dsta;
721
722 if pdef {
723 sol.primal.obj = pobj;
724 sol.primal.con.clear();
725 if let Some(xx) = xx {
726 sol.primal.var.resize(self.var_idx.len(),0.0);
727 sol.primal.var[0] = 1.0;
728 xx.permute_by(&self.var_idx[1..])
729 .zip(&mut sol.primal.var[1..])
730 .for_each(|(&src,dst)| *dst = src);
731
732 self.mx.eval_into(sol.primal.var.as_slice(),&mut sol.primal.con).unwrap();
733 }
734 else if ! self.vars.is_empty() {
735 return Err(std::io::Error::other("Missing solution section sol/var/primal"));
736 }
737 else {
738 sol.primal.con.resize(self.con_rhs.len(),0.0);
739 }
740 }
741
742 if ddef {
743 if let Some(sc) = sc.as_ref() {
744 if self.con_mx_row.len() != sc.len() {
745 return Err(std::io::Error::other("Incorrect solution dimension in sol/acc/primal"));
746 }
747 }
748 sol.dual.obj = dobj;
749 if let Some(((sl,su),sc)) = sx.as_ref().zip(sc.as_ref()) {
750 if sl.len() != numvar || su.len() != numvar { return Err(std::io::Error::other("Incorrect solution dimension in sol/var/dual")); }
751 sol.dual.var.resize(self.var_idx.len(),0.0);
752 for (solx,index,e) in izip!(sol.dual.var[1..].iter_mut(),self.var_idx[1..].iter(),self.vars.iter()) {
753 *solx =
754 match e {
755 VarItem::Linear => sl[*index]-su[*index],
756 VarItem::LinearLowerBound => -su[*index],
757 VarItem::LinearUpperBound => sl[*index],
758 VarItem::Conic { conidx } => sc[*conidx],
759 };
760 }
761 }
762 else if ! self.vars.is_empty() {
763 return Err(std::io::Error::other("Missing solution section sol/var/primal"));
764 }
765
766 if let Some(sc) = sc.as_ref() {
767 sol.dual.con.copy_from_slice(sc.as_slice());
768 }
769 else if ! self.cons.is_empty() {
770 return Err(std::io::Error::other("Missing solution section sol/var/primal"));
771 }
772 }
773
774 Ok(())
775 }
776
777 fn read_bsolution<R>(&self, sol_bas : & mut Solution, sol_itr : &mut Solution, sol_itg : &mut Solution,r : &mut R) -> std::io::Result<()> where R : Read
861 {
862 let mut r = bio::Des::new(r)?;
863
864 let mut note : Vec<u8> = Vec::new();
865 r.expect(b"MSKSOLN",b"[B")?
866 .read_into(&mut note)?;
867
868 let version =
869 {
870 let mut entry = r.expect(b"version",b"III")?;
871 (entry.next_value::<u32>()?,
872 entry.next_value::<u32>()?,
873 entry.next_value::<u32>()?)
874 };
875
876 if version.0 != 10 || version.1 != 2 {
877 return Err(std::io::Error::other(format!("Unsupported solution format version: {}.{}.{}",version.0,version.1,version.2)));
878 }
879
880 sol_bas.primal.status = Undefined;
881 sol_bas.dual.status = Undefined;
882 sol_itr.primal.status = Undefined;
883 sol_itr.dual.status = Undefined;
884 sol_itg.primal.status = Undefined;
885 sol_itg.dual.status = Undefined;
886
887 let numvar = r.expect(b"numvar",b"I")?.next_value::<u32>()? as usize;
888 let numbarvar = r.expect(b"numbarvar",b"I")?.next_value::<u32>()?;
889 let numcon = r.expect(b"numcon",b"I")?.next_value::<u32>()? as usize;
890 let numcone = r.expect(b"numcone",b"I")?.next_value::<u32>()?;
891 let numacc = r.expect(b"numacc",b"L")?.next_value::<u64>()? as usize;
892
893 if self.var_lb.len() != numvar { return Err(std::io::Error::other("Invalid solution dimension")); }
894 if self.var_ub.len() != numvar { return Err(std::io::Error::other("Invalid solution dimension")); }
895 if 0 != numcone { return Err(std::io::Error::other("Invalid solution dimension")); }
896 if self.con_block_ptr.len() != numacc { return Err(std::io::Error::other("Invalid solution dimension")); }
897 if 0 != numbarvar { return Err(std::io::Error::other("Invalid solution dimension")); }
898
899 let mut acc_pattern : Vec<u64> = Vec::new();
900 if let Some((b"acc/pattern",b"[L")) = r.peek()? {
901 r.next_entry()?.unwrap().read_into(&mut acc_pattern)?;
902 }
903 use SolutionStatus::*;
906
907 if let Some((b"sol/interior",b"[B[B")) = r.peek()? {
908 sol_itr.primal.var.resize(self.vars.len(),0.0);
909 sol_itr.dual.var.resize(self.vars.len(),0.0);
910 sol_itr.primal.con.resize(self.cons.len(),0.0);
911 sol_itr.dual.con.resize(self.cons.len(),0.0);
912
913 let sta = r.expect(b"sol/interior",b"[B[B").and_then(|mut entry| { entry.skip_field()?; Ok(str_to_pdsolsta(entry.read::<u8>()?.as_slice())?) })?;
914 let pdef = !matches!(sta.0,Undefined);
915 let ddef = !matches!(sta.1,Undefined);
916 let pobj = if pdef { r.expect(b"sol/interior/pobj",b"d").and_then(|mut entry| entry.next_value::<f64>())? } else { 0.0 };
917 let dobj = if ddef { r.expect(b"sol/interior/dobj",b"d").and_then(|mut entry| entry.next_value::<f64>())? } else { 0.0 };
918 let _varsta = r.expect(b"sol/interior/var/sta",b"[B").and_then(|mut entry| Ok(entry.read::<u8>()?))?;
919 let xx = if pdef { Some(r.expect(b"sol/interior/var/primal",b"[d").and_then(|mut entry| Ok(entry.read::<f64>()?))?) } else { None };
920 let sx = if ddef { Some(r.expect(b"sol/interior/var/dual",b"[d[d").and_then(|mut entry| Ok((entry.read::<f64>()?,entry.read::<f64>()?)))?) } else { None };
921 let sc = if numcon > 0 && ddef { Some(r.expect(b"sol/interior/con/dual",b"[d[d[d").and_then(|mut entry| Ok((entry.read::<f64>()?,entry.read::<f64>()?,entry.read::<f64>()?)))?) } else { None };
924 let sn = if numacc > 0 && ddef { Some(r.expect(b"sol/interior/acc/dual",b"[d").and_then(|mut entry| Ok(entry.read::<f64>()?))?) } else { None };
925
926 self.copy_solution(sta.0,sta.1,
927 numvar,
928 pobj,dobj,
929 xx,sx,
931 sn,
934 sol_itr)?;
935 }
936
937 if let Some((b"sol/basic",b"[B[B")) = r.peek()? {
938 let sta = r.expect(b"sol/basic",b"[B[B").and_then(|mut entry| { entry.skip_field()?; Ok(str_to_pdsolsta(entry.read::<u8>()?.as_slice())?) })?;
939 let pdef = !matches!(sta.0,Undefined);
940 let ddef = !matches!(sta.1,Undefined);
941 let pobj = if pdef { r.expect(b"sol/basic/pobj",b"d").and_then(|mut entry| entry.next_value::<f64>())? } else { 0.0 };
942 let dobj = if ddef { r.expect(b"sol/basic/dobj",b"d").and_then(|mut entry| entry.next_value::<f64>())? } else { 0.0 };
943 let varsta = r.expect(b"sol/basic/var/sta",b"[B").and_then(|mut entry| Ok(entry.read::<u8>()?))?;
944 let xx = if pdef { Some(r.expect(b"sol/basic/var/primal",b"[d").and_then(|mut entry| Ok(entry.read::<f64>()?))?) } else { None };
945 let sx = if ddef { Some(r.expect(b"sol/basic/var/dual",b"[d[d").and_then(|mut entry| Ok((entry.read::<f64>()?,entry.read::<f64>()?)))?) } else { None };
946 let _xc = if numcon > 0 && pdef { Some(r.expect(b"sol/basic/con/primal",b"[d").and_then(|mut entry| Ok(entry.read::<f64>()?))?) } else { None };
948 let sc = if numcon > 0 && ddef { Some(r.expect(b"sol/basic/con/dual",b"[d[d[d").and_then(|mut entry| Ok((entry.read::<f64>()?,entry.read::<f64>()?,entry.read::<f64>()?)))?) } else { None };
949 let sn = if numacc > 0 && ddef { Some(r.expect(b"sol/basic/acc/dual",b"[d").and_then(|mut entry| Ok(entry.read::<f64>()?))?) } else { None };
950
951 self.copy_solution(sta.0,sta.1,
952 numvar,
953 pobj,dobj,
954 xx,sx,
956 sn,
959 sol_bas)?;
960 }
961
962 if let Some((b"sol/integer",b"[B[B")) = r.peek()? {
963 let sta = r.expect(b"sol/integer",b"[B[B").and_then(|mut entry| { entry.skip_field()?; Ok(str_to_pdsolsta(entry.read::<u8>()?.as_slice())?) })?;
964 let pdef = !matches!(sta.0,Undefined);
965 let pobj = if pdef { r.expect(b"sol/integer/pobj",b"d").and_then(|mut entry| entry.next_value::<f64>())? } else { 0.0 };
966 let varsta = r.expect(b"sol/integer/var/sta",b"[B").and_then(|mut entry| Ok(entry.read::<u8>()?))?;
967 let xx = if pdef { Some(r.expect(b"sol/integer/var/primal",b"[d").and_then(|mut entry| Ok(entry.read::<f64>()?))?) } else { None };
968 let _xc = if numcon > 0 && pdef { Some(r.expect(b"sol/integer/con/primal",b"[d").and_then(|mut entry| Ok(entry.read::<f64>()?))?) } else { None };
970
971 self.copy_solution(sta.0,sta.1,
972 numvar,
973 pobj,0.0,
974 xx,None,
976 None,
979 sol_itg)?;
980 }
981
982 r.expect(b"name/var",b"[I[B")?.skip_all()?;
983 r.expect(b"name/barvar", b"[I[B")?.skip_all()?;
984 r.expect(b"name/con", b"[I[B")?.skip_all()?;
985 r.expect(b"name/cone", b"[I[B")?.skip_all()?;
986 r.expect(b"name/acc", b"[I[B")?.skip_all()?;
987 r.expect(b"name/problem", b"[B")?.skip_all()?;
988 r.expect(b"name/objective", b"[B")?.skip_all()?;
989
990 r.expect(b"inf/f64", b"[B[B[d")?.skip_all()?;
991 r.expect(b"inf/i32", b"[B[B[i")?.skip_all()?;
992 r.expect(b"inf/i64", b"[B[B[l")?.skip_all()?;
993
994 Ok(())
995 }
996
997 fn write_jtask<S>(&self, strm : &mut S) -> std::io::Result<()>
1088 where
1089 S : std::io::Write
1090 {
1091 use json::JSON;
1092
1093 let mut doc = json::Dict::new();
1094 doc.append("$schema",JSON::String("http://mosek.com/json/schema#".to_string()));
1095
1096 if let Some(name) = &self.name {
1097 doc.append("Task/name", name.clone());
1098 }
1099
1100 doc.append(
1101 "Task/info",
1102 json::Dict::from(|taskinfo| {
1103 taskinfo.append("numvar",self.vars.len() as i64);
1104 taskinfo.append("numcon",0);
1105 taskinfo.append("numafe",self.mx.num_row() as i64);
1106 taskinfo.append("numacc",self.con_block_dom.len() as i64);
1107 }));
1108
1109 doc.append(
1110 "Task/data",
1111 json::Dict::from(|taskdata| {
1112 {
1113 let bk = self.var_lb.iter().zip(self.var_ub.iter()).map(|(lb,ub)| bnd_to_bk(*lb, *ub).to_string()).collect();
1114
1115 taskdata.append("var",json::Dict::from(|d| {
1116 d.append("bk", JSON::StringArray(bk));
1117 d.append("bl", JSON::FloatArray(self.var_lb.clone()));
1118 d.append("bu", JSON::FloatArray(self.var_ub.clone()));
1119 d.append("type",JSON::StringArray(self.var_int.iter().map(|&e| if e { "int".into() } else { "cont".into() }).collect()));
1120 }));
1121 }
1122 {
1123 taskdata.append("con",json::Dict::from(|d| {
1124 d.append("bk", JSON::StringArray(Vec::new()));
1125 d.append("bl", JSON::FloatArray(Vec::new()));
1126 d.append("bu", JSON::FloatArray(Vec::new()));
1127 }));
1128 }
1129 taskdata.append(
1130 "objective",
1131 json::Dict::from(|d| {
1132 d.append("sense", if self.sense_max { "max" } else { "min" });
1133 d.append("cfix",0.0f64);
1134 d.append("c", json::Dict::from(|d2| {
1135 d2.append("subj",JSON::IntArray(self.var_idx.permute_by(self.c_subj.as_slice()).map(|&i| i as i64).collect()));
1136 d2.append("val", self.c_cof.as_slice());
1137 }));
1138 }));
1139 taskdata.append(
1140 "A",
1141 json::Dict::from(|d| {
1142 d.append("subi",JSON::IntArray(Vec::new()));
1143 d.append("subj",JSON::IntArray(Vec::new()));
1144 d.append("val", JSON::FloatArray(Vec::new()));
1145 }));
1146
1147 taskdata.append(
1148 "AFE",
1149 json::Dict::from(|d| {
1150 let nnz = self.mx.num_nonzeros();
1151 let mut subi = Vec::with_capacity(nnz);
1152 let mut subj = Vec::with_capacity(nnz);
1153 let mut val = Vec::with_capacity(nnz);
1154 let mut gs = Vec::with_capacity(self.mx.num_row());
1155
1156 self.mx.row_iter()
1157 .enumerate()
1158 .for_each(|(i,(jj,cc,g))| {
1159 let base = subi.len();
1160 let n = jj.len();
1161 subi.resize(base+n, i as i64);
1162 subj.extend(self.var_idx.permute_by(jj).map(|&i| i as i64));
1163 val.extend_from_slice(cc);
1164 gs.push(g);
1165 });
1166 d.append("numafe", JSON::Int( self.mx.num_row() as i64));
1167
1168 d.append("F", json::Dict::from(|d| {
1169 d.append("subi",JSON::IntArray(subi));
1170 d.append("subj",JSON::IntArray(subj));
1171 d.append("val", JSON::FloatArray(val));
1172 }));
1173 d.append(
1175 "g",
1176 json::Dict::from(|d| {
1177 d.append("subi",JSON::IntArray((0..self.mx.num_row() as i64).collect()));
1178 d.append("val", JSON::FloatArray(self.mx.row_iter().map(|(_,_,g)| g).collect()));
1179 }));
1180 }));
1181
1182 taskdata.append(
1183 "domains",
1184 JSON::Dict(
1185 json::Dict::from(|d|
1186 d.append(
1187 "type",
1188 JSON::List(self.con_block_dom.iter().map(|c| dom2json(c)).collect::<Vec<JSON>>())))));
1189 taskdata.append(
1190 "ACC",
1191 json::Dict::from(|d| {
1192 d.append("domain",JSON::IntArray((0..self.con_block_dom.len()).map(|i| i as i64).collect()));
1193 let mut acc_afe_idxs : Vec<Vec<i64>> = self.con_block_dom.iter().map(|d| vec![0i64; d.dim()]).collect();
1194 acc_afe_idxs.iter_mut().flat_map(|r| r.iter_mut()).zip(self.con_mx_row.iter()).for_each(|(d,&s)| *d = s as i64);
1195 let mut acc_b : Vec<Vec<f64>> = self.con_block_dom.iter().map(|dom| vec![0f64; dom.dim()]).collect();
1196 acc_b.iter_mut().flat_map(|row| row.iter_mut()).zip(self.con_rhs.iter()).for_each(|(d,&s)| *d = s);
1197 d.append("afeidx",JSON::List( acc_afe_idxs.into_iter().map(|row| JSON::IntArray(row)).collect() ));
1198 d.append("b", JSON::List( acc_b.into_iter().map(|row| JSON::FloatArray(row)).collect() ));
1199 }));
1200 }));
1201 doc.append(
1202 "Task/parameters",
1203 json::Dict::from(|d| {
1204 if ! self.dpar.is_empty() {
1205 d.append(
1206 "dparam",
1207 json::Dict::from(|d| for (k,v) in self.dpar.iter() { d.append(k.as_str(), JSON::Float(*v)); }))
1208 }
1209 if ! self.ipar.is_empty() {
1210 d.append(
1211 "iparam",
1212 json::Dict::from(|d| for (k,v) in self.ipar.iter() { d.append(k.as_str(), JSON::Int(*v as i64)); }))
1213 }
1214 }));
1215 JSON::Dict(doc).write(strm)
1216 }
1217
1218
1219 fn linear_names<const N : usize>(&mut self, first : usize, last : usize, shape : &[usize;N], sp : Option<&[usize]>, name : &str) {
1220 let mut name_index_buf = [1usize; N];
1221 let mut strides = [0;N];
1222 strides.iter_mut().zip(shape.iter()).rev().fold(1,|s,(st,&d)| { *st = s; d * s });
1223 if let Some(sp) = &sp {
1224 for (&i,n) in sp.iter().zip(self.var_names.iter_mut()) {
1225 name_index_buf.iter_mut().zip(strides.iter()).fold(i,|i,(ni,&st)| { *ni = i/st; i%st });
1226 *n= Some(format!("{}{:?}", name, name_index_buf));
1227 }
1228 }
1229 else {
1230 for n in self.var_names.iter_mut() {
1231 name_index_buf.iter_mut().zip(shape.iter()).rev().fold(1,|c,(i,&d)| { *i += c; if *i > d { *i = 1; 1 } else { 0 } });
1232 *n = Some(format!("{}{:?}", name, name_index_buf));
1233 }
1234 }
1235 }
1236
1237
1238 fn native_linear_variable(&mut self, lb : &[f64], ub : &[f64], is_int : bool) -> std::ops::Range<usize> {
1241 assert_eq!(lb.len(),ub.len());
1242 let n = lb.len();
1243 let first = self.var_lb.len();
1244 let last = first + n;
1245
1246 let first_nvar = self.var_lb.len();
1247 let last_nvar = first_nvar+n;
1248
1249 self.var_lb.extend_from_slice(lb);
1250 self.var_ub.extend_from_slice(ub);
1251 self.var_int.resize(last_nvar, is_int);
1252 self.var_names.resize(last_nvar, None);
1253
1254 first..last
1255 }
1256
1257
1258
1259 fn conic_variable<const N : usize>(
1260 &mut self,
1261 name : Option<&str>,
1262 shape : [usize;N],
1264 conedim : usize,
1265 dt : VectorDomainType,
1266 offset : &[f64],
1267 is_int : bool) -> Result<Variable<N>,String>
1268 {
1269 use VecCone::*;
1270
1271 let n = shape.iter().product();
1272 let dim = shape[conedim];
1273 let numcone = n/dim;
1274
1275 assert_eq!(offset.len(),n);
1276
1277
1278 let ct =
1279 match dt {
1280 VectorDomainType::QuadraticCone => Quadratic{dim},
1281 VectorDomainType::RotatedQuadraticCone => RotatedQuadratic{dim},
1282 VectorDomainType::SVecPSDCone => ScaledVectorizedPSD{dim},
1283 VectorDomainType::GeometricMeanCone => PrimalGeometricMean {dim},
1284 VectorDomainType::DualGeometricMeanCone => DualGeometricMean{dim},
1285 VectorDomainType::ExponentialCone => PrimalExp,
1286 VectorDomainType::DualExponentialCone => DualExp,
1287 VectorDomainType::PrimalPowerCone(alpha) => PrimalPower{dim,alpha},
1288 VectorDomainType::DualPowerCone(alpha) => DualPower{dim,alpha},
1289 VectorDomainType::NonNegative => NonNegative{dim},
1291 VectorDomainType::NonPositive => NonPositive{dim},
1292 VectorDomainType::Zero => Zero{dim},
1293 VectorDomainType::Free => Unbounded {dim},
1294 };
1295
1296 let base = self.var_lb.len();
1297 self.var_lb.resize(base+n,f64::NEG_INFINITY);
1298 self.var_ub.resize(base+n,f64::INFINITY);
1299 self.var_int.resize(base+n,is_int);
1300 self.var_names.resize(base+n,None);
1301
1302 let firstvar = self.var_idx.len();
1303 let lastvar = firstvar + n;
1304
1305 let con_base = self.con_rhs.len();
1306 self.con_rhs.extend_from_slice(offset);
1307 self.con_names.resize(con_base+n, None);
1308
1309 self.con_mx_row.extend( (firstvar..lastvar).map(|i| self.mx.append_row(&[i], &[1.0], 0.0)) );
1310 self.con_block_dom.extend((0..numcone).map(|_| ct.clone()));
1311 self.con_block_ptr.extend((0..numcone).map(|i| base+i*dim ));
1312
1313 self.var_idx.extend(base..base+n);
1314 self.vars.extend((con_base..con_base+dim).map(|conidx| VarItem::Conic { conidx }));
1315
1316
1317 let shape3 = [ shape[..conedim].iter().product(),dim,shape[conedim+1..].iter().product()];
1318 let strides3 = [ shape3[1]*shape[2], 1, shape3[2]];
1319
1320 let perm : Vec::<usize> = iproduct!(0..shape3[0],0..shape3[1],0..shape3[2]).map(|(i0,i1,i2)| strides3[0]*i0 + strides3[1]*i1 + strides3[2]*i2).collect();
1321
1322 let varidxs = perm.iter().map(|i| firstvar + i).collect();
1323
1324 self.var_names.resize(base+n,None);
1325
1326 if let Some(name) = name {
1327 let mut idx = [1; N];
1328
1329 self.var_names[base..].permute_by_mut(perm.as_slice())
1330 .for_each(|n| {
1331 *n = Some(format!("{}{:?}",name,idx));
1332 _ = idx.iter_mut().zip(shape.iter()).rev().fold(1,|c,(i,&d)| { *i += c; if *i > d { *i = 1; 1 } else { 0 } });
1333 });
1334 }
1335
1336 Ok(Variable::new(varidxs, None, &shape))
1337 }
1338
1339 fn conic_constraint<const N : usize>(
1340 &mut self,
1341 name : Option<&str>,
1342 ptr : &[usize],
1344 subj : &[usize],
1345 cof : &[f64],
1346 shape : [usize;N],
1348 conedim : usize,
1349 dt : VectorDomainType,
1350 offset : &[f64]) -> Result<Constraint<N>,String>
1351 {
1352 use VecCone::*;
1353 let n = shape.iter().product();
1354 let dim = shape[conedim];
1355 let numcone = n/dim;
1356
1357 assert_eq!(offset.len(),n);
1358
1359 let ct =
1360 match dt {
1361 VectorDomainType::QuadraticCone => Quadratic{dim},
1362 VectorDomainType::RotatedQuadraticCone => RotatedQuadratic{dim},
1363 VectorDomainType::SVecPSDCone => ScaledVectorizedPSD{dim},
1364 VectorDomainType::GeometricMeanCone => PrimalGeometricMean {dim},
1365 VectorDomainType::DualGeometricMeanCone => DualGeometricMean{dim},
1366 VectorDomainType::ExponentialCone => PrimalExp,
1367 VectorDomainType::DualExponentialCone => DualExp,
1368 VectorDomainType::PrimalPowerCone(alpha) => PrimalPower{dim,alpha},
1369 VectorDomainType::DualPowerCone(alpha) => DualPower{dim,alpha},
1370 VectorDomainType::NonNegative => NonNegative{dim},
1372 VectorDomainType::NonPositive => NonPositive{dim},
1373 VectorDomainType::Zero => Zero{dim},
1374 VectorDomainType::Free => Unbounded {dim},
1375 };
1376
1377 let firstcon = self.con_rhs.len();
1378
1379 let ptr_perm = Chunkation::new(ptr).unwrap();
1380 self.con_mx_row.extend(
1381 izip!(ptr_perm.chunks(subj).unwrap(),
1382 ptr_perm.chunks(cof).unwrap())
1383 .map(|(subj,cof)| {
1384 if let (Some(j),Some(c)) = (subj.first(),cof.first()) {
1386 if *j == 0 {
1387 self.mx.append_row(&subj[1..], &cof[..cof.len()-1], *c)
1388 }
1389 else {
1390 self.mx.append_row(subj, cof, 0.0)
1391 }
1392 }
1393 else {
1394 self.mx.append_row(subj, cof, 0.0)
1395 }}));
1396 self.con_rhs.extend_from_slice(offset);
1397 self.con_names.resize(firstcon+n,None);
1398 let firstblock = self.con_block_dom.len();
1399 self.con_block_ptr.extend( (firstcon..firstcon+n).step_by(dim) );
1400 self.con_block_dom.extend( (0..numcone).map(|i| ct.clone() ));
1401
1402 let shape3 = [ shape[..conedim].iter().product(),dim,shape[conedim+1..].iter().product()];
1403 let strides3 = [ shape3[1]*shape3[2], 1, shape3[2]];
1404
1405 let perm : Vec::<usize> = iproduct!(0..shape3[0],0..shape3[1],0..shape3[2]).map(|(i0,i1,i2)| strides3[0]*i0 + strides3[1]*i1 + strides3[2]*i2).collect();
1406 let rescon0 = self.cons.len();
1407
1408 self.cons.extend( iproduct!(firstblock..firstblock+numcone,0..dim).map(|(block_index,offset)| ConItem{ block_index, offset}));
1409
1410 if let Some(name) = name {
1411 let mut idx = [1; N];
1412
1413 self.con_names[firstcon..]
1414 .permute_by_mut(perm.as_slice())
1415 .for_each(|n| {
1416 *n = Some(format!("{}{:?}",name,idx));
1417 _ = idx.iter_mut().zip(shape.iter()).rev().fold(1,|c,(i,&d)| { *i += c; if *i > d { *i = 1; 1 } else { 0 } });
1418 });
1419 }
1420
1421 Ok(Constraint::new(perm.iter().map(|&i| rescon0+i).collect(), &shape))
1422 }
1423}
1424
1425
1426
1427
1428
1429impl ModelWithLogCallback for Backend {
1430 fn set_log_handler<F>(& mut self, func : F) where F : 'static+Fn(&str) {
1431 self.log_cb = Some(Box::new(func));
1432 }
1433}
1434
1435fn subseq_location_from<T>(pos : usize, src : &[T], seq : &[T]) -> Option<usize> where T : Eq {
1436 if pos+seq.len() > src.len() {
1437 return None;
1438 }
1439
1440 for i in pos..src.len()-seq.len()+1 {
1441 if unsafe{ *src.get_unchecked(i) == *seq.get_unchecked(0) } {
1442 let mut found = true;
1443 for (j0,j1) in (i..i+seq.len()).enumerate() {
1444 if unsafe{ *src.get_unchecked(j1) != *seq.get_unchecked(j0) } {
1445 found = false;
1446 break;
1447 }
1448 }
1449 if found {
1450 return Some(i);
1451 }
1452 }
1453 }
1454 None
1455}
1456fn subseq_location<T>(src : &[T], seq : &[T]) -> Option<usize> where T : Eq {
1457 subseq_location_from(0,src, seq)
1458}
1459
1460fn str_to_pdsolsta(solsta : &[u8]) -> std::io::Result<(SolutionStatus,SolutionStatus)> {
1462 match solsta {
1463 b"UNKNOWN" => Ok((SolutionStatus::Unknown,SolutionStatus::Unknown)),
1464 b"OPTIMAL" => Ok((SolutionStatus::Optimal,SolutionStatus::Optimal)),
1465 b"PRIM_FEAS" => Ok((SolutionStatus::Feasible,SolutionStatus::Unknown)),
1466 b"DUAL_FEAS" => Ok((SolutionStatus::Unknown,SolutionStatus::Feasible)),
1467 b"PRIM_AND_DUAL_FEAS" => Ok((SolutionStatus::Feasible,SolutionStatus::Feasible)),
1468 b"PRIM_INFEAS_CER" => Ok((SolutionStatus::Undefined,SolutionStatus::CertInfeas)),
1469 b"DUAL_INFEAS_CER" => Ok((SolutionStatus::CertInfeas,SolutionStatus::Undefined)),
1470 b"PRIM_ILLPOSED_CER" => Ok((SolutionStatus::Undefined,SolutionStatus::CertIllposed)),
1471 b"DUAL_ILLPOSED_CER" => Ok((SolutionStatus::CertIllposed,SolutionStatus::Undefined)),
1472 b"INTEGER_OPTIMAL" => Ok((SolutionStatus::Optimal,SolutionStatus::Undefined)),
1473 _ => Err(std::io::Error::other(format!("Invalid solution format: {}",std::str::from_utf8(solsta).unwrap_or("<invalid utf-8>"))))
1474 }
1475}
1476
1477
1478fn dom2json(c : &VecCone) -> json::JSON {
1479 use json::JSON;
1480 use VecCone::*;
1481 match c {
1482 Zero{dim} => JSON::List(vec![ JSON::String("rzero".to_string()), JSON::Int(*dim as i64)]),
1483 NonNegative{dim} => JSON::List(vec![ JSON::String("rplus".to_string()), JSON::Int(*dim as i64)]),
1484 NonPositive{dim} => JSON::List(vec![ JSON::String("rminus".to_string()), JSON::Int(*dim as i64)]),
1485 Unbounded{dim} => JSON::List(vec![ JSON::String("r".to_string()), JSON::Int(*dim as i64)]),
1486 Quadratic{dim} => JSON::List(vec![ JSON::String("quad".to_string()), JSON::Int(*dim as i64)]),
1487 RotatedQuadratic{dim} => JSON::List(vec![ JSON::String("rquad".to_string()), JSON::Int(*dim as i64)]),
1488 PrimalGeometricMean{dim} => JSON::List(vec![ JSON::String("pgmean".to_string()), JSON::Int(*dim as i64)]),
1489 DualGeometricMean{dim} => JSON::List(vec![ JSON::String("dgmean".to_string()), JSON::Int(*dim as i64)]),
1490 PrimalExp => JSON::List(vec![ JSON::String("pexp".to_string())]),
1491 DualExp => JSON::List(vec![ JSON::String("dexp".to_string())]),
1492 PrimalPower { dim, alpha } => JSON::List(vec![ JSON::String("ppow".to_string()), JSON::Int(*dim as i64), JSON::FloatArray(alpha.clone())]),
1493 DualPower { dim, alpha } => JSON::List(vec![ JSON::String("dpow".to_string()), JSON::Int(*dim as i64), JSON::FloatArray(alpha.clone())]),
1494 ScaledVectorizedPSD { dim } => JSON::List(vec![ JSON::String("svecpsd".to_string()), JSON::Int(*dim as i64)]),
1495 }
1496}
1497
1498#[cfg(test)]
1499mod test {
1500 use super::*;
1501 #[test]
1502 fn test_optserver() {
1503 let addr = "http://solve.mosek.com:30080".to_string();
1504 let mut m = Model::new(Some("SuperModel"));
1505 m.set_parameter((), SolverAddress(addr));
1506
1507 let a0 : &[f64] = &[ 3.0, 1.0, 2.0, 0.0 ];
1508 let a1 : &[f64] = &[ 2.0, 1.0, 3.0, 1.0 ];
1509 let a2 : &[f64] = &[ 0.0, 2.0, 0.0, 3.0 ];
1510 let c : &[f64] = &[ 3.0, 1.0, 5.0, 1.0 ];
1511
1512 let x = m.variable(Some("x0"), nonnegative().with_shape(&[4]));
1514
1515 let _ = m.constraint(None, x.index(1), less_than(10.0));
1517 let _ = m.constraint(Some("c1"), x.dot(a0), equal_to(30.0));
1518 let _ = m.constraint(Some("c2"), x.dot(a1), greater_than(15.0));
1519 let _ = m.constraint(Some("c3"), x.dot(a2), less_than(25.0));
1520
1521 m.objective(Some("obj"), Sense::Maximize, x.dot(c));
1523
1524 m.write_problem("lo1-nosol.jtask");
1525
1526 m.set_log_handler(|msg| print!("{}",msg));
1527 m.solve();
1528
1529 let (psta,dsta) = m.solution_status(SolutionType::Default);
1531 println!("Status = {:?}/{:?}",psta,dsta);
1532 let xx = m.primal_solution(SolutionType::Default,&x);
1533 println!("x = {:?}", xx);
1534 }
1535}