dandelion_chart
Version:
A library for visualizing quantum state probabilities and amplitudes in a geometrical and interactive way.
477 lines (298 loc) • 12.4 kB
JavaScript
import * as d3 from 'd3'
import {state_dark, state_light, state_Light} from "./color_scheme";
/*
* 1. 自动根据state vector生成states
* 2. 自动根据points个数循环color
* 3. system中的对应修改,md文件对应修改
* 4. 部署到npm
* -----
* 去改Encoder的系统
* */
function dandelion_chart(state_vectors_,
[bundle_g_, size_arr_, position_arr_, point_radius_=8],
[theta_=1, theta_point_=1]
){
////////////////////////// 检查参数 ////////////////////////////////
// // 判断 svg 是否真实存在
// if(typeof svg_element !== 'object'){
// console.log('Dandelion chart svg not valid')
//
// return
// }else if(svg_element.attr('class') !== 'view_svg'){
// console.log('Dandelion chart parameter `svg` not valid')
//
// return
// }
// 判断 size_arr_, position_arr_ 是否合法
if(typeof state_vectors_ !== 'object'){
console.log('Dandelion chart parameter `states` not valid')
return
}
// 判断 size_arr_, position_arr_ 是否合法
if(typeof size_arr_ !== 'object' || size_arr_.length !== 2){
console.log('Dandelion chart parameter `size_arr_` not valid')
return
}
if(typeof position_arr_ !== 'object' || position_arr_.length !== 2){
console.log('Dandelion chart parameter `position_arr_` not valid')
return
}
// 定义变量
let state_vector = state_vectors_ || 'parameter error'
// let all_possible_states = states || 'parameter error'
let bundle_g = bundle_g_ || 'parameter error'
let size_arr = size_arr_ || 'parameter error'
let position_arr = position_arr_ || 'parameter error'
////////////////////////// 开始画图 ////////////////////////////////
// 定义长宽
const [container_width, container_height] = size_arr
const view_margin_left = 0, view_margin_top = 0
const view_padding_left = 10, view_padding_top = 10
// 定义位置
const [container_x, container_y] = position_arr
// 实际的圆心关于 state 点的偏置
let theta = theta_ || 1// theta是缩小半径 r 的参数
let theta_point = theta_point_ || 1// theta是缩小半径 r 的参数
let opacity = 0.8
let point_radius = point_radius_
let radius_reduce_value = point_radius*(3/8)
let view = bundle_g.append('g') // 外面套的边框是 黑色的border
.attr('class', function(){
return `dandelion_container dandelion_container_index_${d3.selectAll('.dandelion_container').size()}`
})
.attr('transform', `translate(${container_x+view_margin_left},${container_y+view_margin_top})`)
// 画container的边框
let border = view.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', container_width)
.attr('height', container_height)
.attr('rx', 0)
.attr('stroke', '#000000')
.attr('stroke-width', '1px')
.attr('fill', '#ffffff')
let content_width = container_width - 2*view_padding_left
let content_height = container_height - 2*view_padding_top
// 画内部的content所有元素
let content_g = view.append('g')
.attr('transform', `translate(${view_padding_left}, ${view_padding_top})`)
.attr('class', `content_g`)
//////////////////// 添加 横竖 两个坐标轴 ////////////////////////
let scale_x = d3.scaleLinear()
.domain([-1,1])
.range([0, content_width])
let scale_y = d3.scaleLinear()
.domain([-1,1])
.range([content_height, 0])
let all_possible_states = [];
let qubit_n = Math.log2(state_vector.length)
// Convert to decimal
let maxDecimal = parseInt("1".repeat(qubit_n),2);
// For every number between 0->decimal
for(let i = 0; i <= maxDecimal; i++){
// Convert to binary, pad with 0, and add to final results
all_possible_states.push(i.toString(2).padStart(qubit_n,'0'));
}
// 判断qubit_n是不是为整数,如果不是,报错"qubit number is not an integer"
if(!Number.isInteger(qubit_n)){
console.log('Error: qubit number is not an integer')
return
}
const view_data_ = all_possible_states.map((d,i)=>{
let obj = {}
obj['name'] = d
obj['state_vector'] = state_vector[i]
return obj
})
//处理 数据源 对每一个元素生成point—radius
let view_data_copy = view_data_
let view_data = view_data_.map(d=>{
// 这个函数的作用就是统计已经有多少个同位置的元素被渲染了, 生成一个像是同心圆的东西
let number = 0
view_data_copy.every(ele=>{
if(ele.state_vector.toString()==d.state_vector.toString() && ele.name != d.name){
number = number + 1
return true
}
if(ele.name == d.name){
return false
}
return true
})
// console.log('number', number);
// console.log('r', point_r - number*2);
if(d['state_vector'][0]==0 && d['state_vector'][1]==0){
return {
...d,
point_radius: 0
}
}
// console.log(number);
return {
...d,
point_radius: (point_radius-number*radius_reduce_value)*theta_point
}
})
// console.log(view_data);
let scale_new_x = d3.scaleLinear()
.domain([-1, 1])
.range([-content_width/2, content_width/2])
let scale_new_y = d3.scaleLinear()
.domain([-1, 1])
.range([content_width/2, -content_width/2])
let exp = 1
let scale_new_x_pow = d3.scalePow()
.domain([-1, 1])
.range([-content_width/2, content_width/2])
.exponent(exp)
let scale_new_y_pow = d3.scalePow()
.domain([-1, 1])
.range([content_width/2, -content_width/2])
.exponent(exp)
//////////////////////// 画 原点到 state 的 g (包括state的点,到原点的连线,还有两根到坐标轴的线)
let state_g0 = content_g.selectAll('.null')
.data(view_data)
.join('g')
.attr('class', d=>`${d['name']}_g`)
.attr('id', 'state_g')
.attr('transform', d=>`translate(${scale_x(d['state_vector'][0])}, ${scale_y(d['state_vector'][1])})`)
let state_g = content_g.selectAll('.null')
.data(view_data)
.join('g')
.attr('class', d=>`${d['name']}_g`)
.attr('transform', d=>`translate(${scale_x(d['state_vector'][0])}, ${scale_y(d['state_vector'][1])})`)
// 画state_g 代表 prob 的圆
state_g0.append('circle')
.attr('id', 'dandelion_circle')
.attr("r", d=>Math.sqrt(Math.pow(-scale_new_x_pow(d['state_vector'][0]) * theta, 2) + Math.pow(-scale_new_y_pow(d['state_vector'][1]) * theta, 2)))
.attr("cx", d=>-scale_new_x_pow(d['state_vector'][0]) * theta)
.attr("cy", d=>-scale_new_y_pow(d['state_vector'][1]) * theta)
.style("stroke", (d,i)=>Object.values(state_dark)[i % Object.values(state_dark).length])
.style("stroke-width", 1)
.style("fill", (d,i)=>Object.values(state_Light)[i % Object.values(state_Light).length])
.style('opacity', opacity)
// 添加 x-axis
content_g.append('g')
.attr('class', 'view_axis_x')
.attr('transform', `translate(${0}, ${content_height/2})`)
.call(d3.axisTop(scale_x)
.tickValues([-1, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1])
.tickFormat(function (d){
return [-1, -0.6, -0.2, 0.2, 0.6, 1].includes(d)? d: null
})
.tickSize(6)
)
.append("text")
.attr("class", "x-label")
.attr("text-anchor", "end")
.attr("x", content_width)
.attr("y", 13)
.style('font-size', '1.2em')
.text("Real")
.attr('fill', '#000000')
// 添加 y-axis
content_g.append('g')
.attr('class', 'view_axis_y')
.attr('transform', `translate(${content_width/2}, ${0})`)
.call(d3.axisRight(scale_y)
.tickValues([-1, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1])
.tickFormat(function (d){
return [-1, -0.6, -0.2, 0.2, 0.6, 1].includes(d)? d: null
})
.tickSize(6)
)
.append("text")
.attr("class", "y-label")
.attr("text-anchor", "end")
.attr("x", 0)
.attr("y", -3)
.style('font-size', '1.2em')
.text("Imag.")
.attr('fill', '#000000')
.attr('transform', 'rotate(-90)')
// 画 state_g 到原点的线
state_g.append('line')
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', d=>-scale_new_x(d['state_vector'][0]))
.attr('y2', d=>-scale_new_y(d['state_vector'][1]))
.style('stroke', '#262626')
.style('stroke-width', 1)
// 画state_g 到两个坐标轴的线
// 实部----x-----到y轴
state_g.append('line')
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', d=>-scale_new_x(d['state_vector'][0]))
.attr('y2', 0)
.style('stroke', '#005a89')
.style('stroke-width', 1)
.attr('stroke-dasharray', 3)
// 实部----y-----到x轴
state_g.append('line')
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', 0)
.attr('y2', d=>-scale_new_y(d['state_vector'][1]))
.style('stroke', '#790077')
.style('stroke-width', 1)
.attr('stroke-dasharray', 4)
// console.log(view_data);
// 代表 state 的 point
let points = state_g.append('circle')
.attr('id', `view3_point`)
.attr("r", d=>{
// console.log(d)
//
// // 这个函数的作用就是统计已经有多少个同位置的元素被渲染了, 生成一个像是同心圆的东西
// let number = 0
//
// view_data.every(ele=>{
// if(ele.state_vector.toString()==d.state_vector.toString() && ele.name != d.name){
// number = number + 1
// return true
// }
//
//
// if(ele.name == d.name){
// return false
// }
//
// return true
// })
// console.log('number', number);
// console.log('r', point_r - number*2);
// if(d['state_vector'][0]==0 && d['state_vector'][1]==0){
// return 0
// }
// return point_r - number*3
return d['point_radius']
})
.attr("cx", 0)
.attr("cy", 0)
.style("fill", (d,i)=>Object.values(state_dark)[i % Object.values(state_dark).length])
.style('stroke', '#fff')
.style('stroke-width', 1)
// .attr('class', d=>`${d['state_vector'][0]}_${d['state_vector'][1]}`)
// .each(d=>{
// console.log(d);
// })
// 画 每个state的 label
// state_g2.append('text')
// .html(d=>`|${ d['name']}⟩`)
// .attr('transform', d=>`translate(${scale_new(d['state_vector'][0])/3}, ${scale_new(d['state_vector'][1])/3})`)
// .style('font-size', '1.2em')
// .style('fill', '#1d1d1d')
// 添加每个state的title
state_g0
.append("title")
.attr("text-anchor", "middle")
.style("font-size", "16px")
.html(d=>`|${ d['name']}⟩: ${d['state_vector']}`)
points
.append("title")
.attr("text-anchor", "middle")
.style("font-size", "16px")
.html(d=>`|${ d['name']}⟩: ${d['state_vector']}`)
}
export default dandelion_chart