Skip to content

Commit

Permalink
Ver 0.34.6
Browse files Browse the repository at this point in the history
- Big updates for plot feature
  • Loading branch information
Axect committed Mar 1, 2024
2 parents d7a3b06 + cc7ee6d commit a72778b
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 33 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "peroxide"
version = "0.34.5"
version = "0.34.6"
authors = ["axect <axect@outlook.kr>"]
edition = "2018"
description = "Rust comprehensive scientific computation library contains linear algebra, numerical analysis, statistics and machine learning tools with farmiliar syntax"
Expand Down Expand Up @@ -30,7 +30,7 @@ peroxide-ad = "0.3"
peroxide-num = "0.1"
#num-complex = "0.3"
netcdf = { version = "0.7", optional = true, default_features = false }
pyo3 = { version = "0.19", optional = true }
pyo3 = { version = "0.20", optional = true, features = ["auto-initialize"] }
blas = { version = "0.22", optional = true }
lapack = { version = "0.19", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
Expand Down
13 changes: 13 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# Release 0.34.6 (2024-03-01)

## Big updates for `plot` feature

* Add `auto-initialize` flag for `pyo3`
* Add `scienceplots` support. Here are available styles.
* `PlotStyle::Default` : default matplotlib style - no scienceplots required
* `PlotStyle::Science` : scienceplots default style - scienceplots required
* `PlotStyle::Nature` : nature style - scienceplots required
* `PlotStyle::IEEE` : IEEE style - scienceplots required
* Implement `xscale, yscale, xlim, ylim` for `Plot2D`
* You can check these features in [Peroxide Gallery](https://github.com/Axect/Peroxide_Gallery/tree/master/Plot/plot_feature)

# Release 0.34.5 (2024-02-08)

* Derive `Serialize` and `Deserialize` for `CubicHermiteSpline`
Expand Down
3 changes: 3 additions & 0 deletions src/prelude/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,6 @@ pub use crate::statistics::stat::Metric::*;

#[cfg(feature="parquet")]
pub use simpler::SimpleParquet;

#[cfg(feature="plot")]
pub use crate::util::plot::*;
187 changes: 156 additions & 31 deletions src/util/plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//!
//! Let's see next ordinary code file.
//!
//! ```rust
//! ```no-run
//! #[macro_use]
//! extern crate peroxide;
//! use peroxide::fuga::*;
Expand Down Expand Up @@ -115,6 +115,26 @@ pub enum Grid {
Off,
}

/// Plot Style (`scienceplots` should be installed)
///
/// * Nature
/// * IEEE
/// * Default (Matplotlib default style)
/// * Science
#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
pub enum PlotStyle {
Nature,
IEEE,
Default,
Science,
}

#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
pub enum PlotScale {
Linear,
Log,
}

pub trait Plot {
fn set_domain(&mut self, x: Vec<f64>) -> &mut Self;
fn insert_image(&mut self, y: Vec<f64>) -> &mut Self;
Expand All @@ -123,12 +143,18 @@ pub trait Plot {
fn set_xlabel(&mut self, xlabel: &str) -> &mut Self;
fn set_ylabel(&mut self, ylabel: &str) -> &mut Self;
fn set_zlabel(&mut self, zlabel: &str) -> &mut Self;
fn set_xscale(&mut self, xscale: PlotScale) -> &mut Self;
fn set_yscale(&mut self, yscale: PlotScale) -> &mut Self;
fn set_xlim(&mut self, xlim: (f64, f64)) -> &mut Self;
fn set_ylim(&mut self, ylim: (f64, f64)) -> &mut Self;
fn set_legend(&mut self, legends: Vec<&str>) -> &mut Self;
fn set_path(&mut self, path: &str) -> &mut Self;
fn set_fig_size(&mut self, fig_size: (usize, usize)) -> &mut Self;
fn set_dpi(&mut self, dpi: usize) -> &mut Self;
fn grid(&mut self, grid: Grid) -> &mut Self;
fn set_marker(&mut self, styles: Vec<Markers>) -> &mut Self;
fn set_style(&mut self, style: PlotStyle) -> &mut Self;
fn tight_layout(&mut self) -> &mut Self;
fn savefig(&self) -> PyResult<()>;
}

Expand All @@ -137,15 +163,21 @@ pub struct Plot2D {
domain: Vector,
images: Vec<Vector>,
pairs: Vec<(Vector, Vector)>,
title: String,
xlabel: String,
ylabel: String,
title: Option<String>,
xlabel: Option<String>,
ylabel: Option<String>,
xscale: PlotScale,
yscale: PlotScale,
xlim: Option<(f64, f64)>,
ylim: Option<(f64, f64)>,
legends: Vec<String>,
markers: Vec<Markers>,
path: String,
fig_size: (usize, usize),
fig_size: Option<(usize, usize)>,
dpi: usize,
grid: Grid,
style: PlotStyle,
tight: bool,
options: HashMap<PlotOptions, bool>,
}

Expand All @@ -162,15 +194,21 @@ impl Plot2D {
domain: vec![],
images: vec![],
pairs: vec![],
title: "Title".to_string(),
xlabel: "$x$".to_string(),
ylabel: "$y$".to_string(),
title: None,
xlabel: None,
ylabel: None,
xscale: PlotScale::Linear,
yscale: PlotScale::Linear,
xlim: None,
ylim: None,
legends: vec![],
markers: vec![],
path: "".to_string(),
fig_size: (10, 6),
fig_size: None,
dpi: 300,
grid: On,
style: PlotStyle::Default,
tight: false,
options: default_options,
}
}
Expand Down Expand Up @@ -202,24 +240,44 @@ impl Plot for Plot2D {
}

fn set_title(&mut self, title: &str) -> &mut Self {
self.title = title.to_owned();
self.title = Some(title.to_owned());
self
}

fn set_xlabel(&mut self, xlabel: &str) -> &mut Self {
self.xlabel = xlabel.to_owned();
self.xlabel = Some(xlabel.to_owned());
self
}

fn set_ylabel(&mut self, ylabel: &str) -> &mut Self {
self.ylabel = ylabel.to_owned();
self.ylabel = Some(ylabel.to_owned());
self
}

fn set_zlabel(&mut self, _zlabel: &str) -> &mut Self {
unimplemented!()
}

fn set_xscale(&mut self, xscale: PlotScale) -> &mut Self {
self.xscale = xscale;
self
}

fn set_yscale(&mut self, yscale: PlotScale) -> &mut Self {
self.yscale = yscale;
self
}

fn set_xlim(&mut self, xlim: (f64, f64)) -> &mut Self {
self.xlim = Some(xlim);
self
}

fn set_ylim(&mut self, ylim: (f64, f64)) -> &mut Self {
self.ylim = Some(ylim);
self
}

fn set_legend(&mut self, legends: Vec<&str>) -> &mut Self {
if let Some(x) = self.options.get_mut(&Legends) {
*x = true
Expand All @@ -240,7 +298,7 @@ impl Plot for Plot2D {
}

fn set_fig_size(&mut self, fig_size: (usize, usize)) -> &mut Self {
self.fig_size = fig_size;
self.fig_size = Some(fig_size);
self
}

Expand All @@ -259,6 +317,16 @@ impl Plot for Plot2D {
self
}

fn set_style(&mut self, style: PlotStyle) -> &mut Self {
self.style = style;
self
}

fn tight_layout(&mut self) -> &mut Self {
self.tight = true;
self
}

fn savefig(&self) -> PyResult<()> {
// Check domain
match self.options.get(&Domain) {
Expand Down Expand Up @@ -298,8 +366,9 @@ impl Plot for Plot2D {
match self.options.get(&Legends) {
Some(x) => {
assert!(*x, "Legends are not defined");
assert!(
self.images.len() + self.pairs.len() == self.legends.len(),
assert_eq!(
self.images.len() + self.pairs.len(),
self.legends.len(),
"Legends are not matched with images"
);
}
Expand All @@ -323,6 +392,12 @@ impl Plot for Plot2D {
On => true,
Off => false,
};
let style = match self.style {
PlotStyle::Nature => "nature",
PlotStyle::IEEE => "ieee",
PlotStyle::Default => "default",
PlotStyle::Science => "science",
};
let xlabel = self.xlabel.clone();
let ylabel = self.ylabel.clone();
let legends = self.legends.clone();
Expand All @@ -335,24 +410,69 @@ impl Plot for Plot2D {
globals.set_item("pair", pairs)?;
globals.set_item("n", y_length)?;
globals.set_item("p", pair_length)?;
globals.set_item("fs", fig_size)?;
if let Some(fs) = fig_size {
globals.set_item("fs", fs)?;
}
globals.set_item("dp", dpi)?;
globals.set_item("gr", grid)?;
globals.set_item("pa", path)?;
if let Some(xl) = self.xlim {
globals.set_item("xl", xl)?;
}
if let Some(yl) = self.ylim {
globals.set_item("yl", yl)?;
}

// Plot Code
let mut plot_string = format!(
"\
plt.rc(\"text\", usetex=True)\n\
plt.rc(\"font\", family=\"serif\")\n\
plt.figure(figsize=fs, dpi=dp)\n\
plt.title(r\"{}\", fontsize=16)\n\
plt.xlabel(r\"{}\", fontsize=14)\n\
plt.ylabel(r\"{}\", fontsize=14)\n\
if gr:\n\
\tplt.grid()\n",
title, xlabel, ylabel
);
let mut plot_string = match self.style {
PlotStyle::Default => {
"\
plt.rc(\"text\", usetex=True)\n\
plt.rc(\"font\", family=\"serif\")\n".to_string()
}
PlotStyle::Science => {
"\
import scienceplots\n\
plt.style.use(\"science\")\n".to_string()
}
_ => format!(
"\
import scienceplots\n\
plt.style.use([\"science\", \"{}\"])\n",
style
),
};
if let Some(fs) = fig_size {
plot_string.push_str(&format!("plt.figure(figsize=fs, dpi=dp)\n")[..]);
} else {
plot_string.push_str(&format!("plt.figure()\n")[..]);
}
if self.tight {
plot_string.push_str(&format!("plt.autoscale(tight=True)\n")[..]);
}
if let Some(t) = title {
plot_string.push_str(&format!("plt.title(r\"{}\")\n", t)[..]);
}
if let Some(x) = xlabel {
plot_string.push_str(&format!("plt.xlabel(r\"{}\")\n", x)[..]);
}
if let Some(y) = ylabel {
plot_string.push_str(&format!("plt.ylabel(r\"{}\")\n", y)[..]);
}
match self.xscale {
PlotScale::Linear => plot_string.push_str(&format!("plt.xscale(\"linear\")\n")[..]),
PlotScale::Log => plot_string.push_str(&format!("plt.xscale(\"log\")\n")[..]),
}
match self.yscale {
PlotScale::Linear => plot_string.push_str(&format!("plt.yscale(\"linear\")\n")[..]),
PlotScale::Log => plot_string.push_str(&format!("plt.yscale(\"log\")\n")[..]),
}
if let Some(xl) = self.xlim {
plot_string.push_str(&format!("plt.xlim(xl)\n")[..]);
}
if let Some(yl) = self.ylim {
plot_string.push_str(&format!("plt.ylim(yl)\n")[..]);
}

if self.markers.len() == 0 {
for i in 0..y_length {
Expand All @@ -372,8 +492,9 @@ impl Plot for Plot2D {
} else {
for i in 0..y_length {
match self.markers[i] {
Line => plot_string
.push_str(&format!("plt.plot(x,y[{}],label=r\"{}\")\n", i, legends[i])[..]),
Line => plot_string.push_str(
&format!("plt.plot(x,y[{}],label=r\"{}\")\n", i, legends[i])[..],
),
Point => plot_string.push_str(
&format!("plt.plot(x,y[{}],\".\",label=r\"{}\")\n", i, legends[i])[..],
),
Expand Down Expand Up @@ -412,7 +533,11 @@ impl Plot for Plot2D {
}
}

plot_string.push_str("plt.legend(fontsize=12)\nplt.savefig(pa)");
if self.tight {
plot_string.push_str(&format!("plt.legend()\nplt.savefig(pa, dpi={}, bbox_inches='tight')", dpi)[..]);
} else {
plot_string.push_str(&format!("plt.legend()\nplt.savefig(pa, dpi={})", dpi)[..]);
}

py.run(&plot_string[..], Some(&globals), None)?;
Ok(())
Expand Down

0 comments on commit a72778b

Please sign in to comment.